Auto merge of #48056 - ExpHP:macro-commas, r=dtolnay
Comprehensively support trailing commas in std/core macros I carefully organized the changes into four commits: * Test cases * Fixes for `macro_rules!` macros * Fixes for builtin macros * Docs for builtins **I can easily scale this back to just the first two commits for now if such is desired.** ### Breaking (?) changes * This fixes #48042, which is a breaking change that I hope people can agree is just a bugfix for an extremely dark corner case. * To fix five of the builtins, this changes `syntax::ext::base::get_single_str_from_tts` to accept a trailing comma, and revises the documentation so that this aspect is not surprising. **I made this change under the (hopefully correct) understanding that `libsyntax` is private rustc implementation detail.** After reviewing all call sites (which were, you guessed it, *precisely those five macros*), I believe the revised semantics are closer to the intended spirit of the function. ### Changes which may require concensus Up until now, it could be argued that some or all the following macros did not conceptually take a comma-separated list, because they only took one argument: * **`cfg(unix,)`** (most notable since cfg! is unique in taking a meta tag) * **`include{,_bytes,_str}("file.rs",)`** (in item form this might be written as "`include!{"file.rs",}`" which is even slightly more odd) * **`compile_error("message",);`** * **`option_env!("PATH",)`** * **`try!(Ok(()),)`** So I think these particular changes may require some sort of consensus. **All of the fixes for builtins are included this list, so if we want to defer these decisions to later then I can scale this PR back to just the first two commits.** ### Other notes/general requests for comment * Do we have a big checklist somewhere of "things to do when adding macros?" My hope is for `run-pass/macro-comma-support.rs` to remain comprehensive. * Originally I wanted the tests to also comprehensively forbid double trailing commas. However, this didn't work out too well: [see this gist and the giant FIXME in it](https://gist.github.com/ExpHP/6fc40e82f3d73267c4e590a9a94966f1#file-compile-fail_macro-comma-support-rs-L33-L50) * I did not touch `select!`. It appears to me to be a complete mess, and its trailing comma mishaps are only the tip of the iceberg. * There are [some compile-fail test cases](https://github.com/ExpHP/rust/blob/5fa97c35da2f0ee/src/test/compile-fail/macro-comma-behavior.rs#L49-L52) that didn't seem to work (rustc emits errors, but compile-fail doesn't acknowledge them), so they are disabled. Any clues? (Possibly related: These happen to be precisely the set of errors which are tagged by rustc as "this error originates in a macro outside of the current crate".) --- Fixes #48042 Closes #46241
This commit is contained in:
commit
ddab91a5de
@ -19,7 +19,10 @@ macro_rules! panic {
|
||||
($msg:expr) => ({
|
||||
$crate::panicking::panic(&($msg, file!(), line!(), __rust_unstable_column!()))
|
||||
});
|
||||
($fmt:expr, $($arg:tt)*) => ({
|
||||
($msg:expr,) => (
|
||||
panic!($msg)
|
||||
);
|
||||
($fmt:expr, $($arg:tt)+) => ({
|
||||
$crate::panicking::panic_fmt(format_args!($fmt, $($arg)*),
|
||||
&(file!(), line!(), __rust_unstable_column!()))
|
||||
});
|
||||
@ -79,6 +82,9 @@ macro_rules! assert {
|
||||
panic!(concat!("assertion failed: ", stringify!($cond)))
|
||||
}
|
||||
);
|
||||
($cond:expr,) => (
|
||||
assert!($cond)
|
||||
);
|
||||
($cond:expr, $($arg:tt)+) => (
|
||||
if !$cond {
|
||||
panic!($($arg)+)
|
||||
@ -359,7 +365,8 @@ macro_rules! try {
|
||||
$crate::result::Result::Err(err) => {
|
||||
return $crate::result::Result::Err($crate::convert::From::from(err))
|
||||
}
|
||||
})
|
||||
});
|
||||
($expr:expr,) => (try!($expr));
|
||||
}
|
||||
|
||||
/// Write formatted data into a buffer.
|
||||
@ -456,6 +463,9 @@ macro_rules! writeln {
|
||||
($dst:expr) => (
|
||||
write!($dst, "\n")
|
||||
);
|
||||
($dst:expr,) => (
|
||||
writeln!($dst)
|
||||
);
|
||||
($dst:expr, $fmt:expr) => (
|
||||
write!($dst, concat!($fmt, "\n"))
|
||||
);
|
||||
@ -524,6 +534,9 @@ macro_rules! unreachable {
|
||||
($msg:expr) => ({
|
||||
unreachable!("{}", $msg)
|
||||
});
|
||||
($msg:expr,) => ({
|
||||
unreachable!($msg)
|
||||
});
|
||||
($fmt:expr, $($arg:tt)*) => ({
|
||||
panic!(concat!("internal error: entered unreachable code: ", $fmt), $($arg)*)
|
||||
});
|
||||
@ -603,7 +616,10 @@ mod builtin {
|
||||
#[stable(feature = "compile_error_macro", since = "1.20.0")]
|
||||
#[macro_export]
|
||||
#[cfg(dox)]
|
||||
macro_rules! compile_error { ($msg:expr) => ({ /* compiler built-in */ }) }
|
||||
macro_rules! compile_error {
|
||||
($msg:expr) => ({ /* compiler built-in */ });
|
||||
($msg:expr,) => ({ /* compiler built-in */ });
|
||||
}
|
||||
|
||||
/// The core macro for formatted string creation & output.
|
||||
///
|
||||
@ -639,7 +655,10 @@ mod builtin {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[macro_export]
|
||||
#[cfg(dox)]
|
||||
macro_rules! option_env { ($name:expr) => ({ /* compiler built-in */ }) }
|
||||
macro_rules! option_env {
|
||||
($name:expr) => ({ /* compiler built-in */ });
|
||||
($name:expr,) => ({ /* compiler built-in */ });
|
||||
}
|
||||
|
||||
/// Concatenate identifiers into one identifier.
|
||||
///
|
||||
@ -715,7 +734,10 @@ mod builtin {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[macro_export]
|
||||
#[cfg(dox)]
|
||||
macro_rules! include_str { ($file:expr) => ({ /* compiler built-in */ }) }
|
||||
macro_rules! include_str {
|
||||
($file:expr) => ({ /* compiler built-in */ });
|
||||
($file:expr,) => ({ /* compiler built-in */ });
|
||||
}
|
||||
|
||||
/// Includes a file as a reference to a byte array.
|
||||
///
|
||||
@ -725,7 +747,10 @@ mod builtin {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[macro_export]
|
||||
#[cfg(dox)]
|
||||
macro_rules! include_bytes { ($file:expr) => ({ /* compiler built-in */ }) }
|
||||
macro_rules! include_bytes {
|
||||
($file:expr) => ({ /* compiler built-in */ });
|
||||
($file:expr,) => ({ /* compiler built-in */ });
|
||||
}
|
||||
|
||||
/// Expands to a string that represents the current module path.
|
||||
///
|
||||
@ -755,5 +780,8 @@ mod builtin {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[macro_export]
|
||||
#[cfg(dox)]
|
||||
macro_rules! include { ($file:expr) => ({ /* compiler built-in */ }) }
|
||||
macro_rules! include {
|
||||
($file:expr) => ({ /* compiler built-in */ });
|
||||
($file:expr,) => ({ /* compiler built-in */ });
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +68,9 @@ macro_rules! panic {
|
||||
($msg:expr) => ({
|
||||
$crate::rt::begin_panic($msg, &(file!(), line!(), __rust_unstable_column!()))
|
||||
});
|
||||
($msg:expr,) => ({
|
||||
panic!($msg)
|
||||
});
|
||||
($fmt:expr, $($arg:tt)+) => ({
|
||||
$crate::rt::begin_panic_fmt(&format_args!($fmt, $($arg)+),
|
||||
&(file!(), line!(), __rust_unstable_column!()))
|
||||
@ -312,7 +315,10 @@ pub mod builtin {
|
||||
/// ```
|
||||
#[stable(feature = "compile_error_macro", since = "1.20.0")]
|
||||
#[macro_export]
|
||||
macro_rules! compile_error { ($msg:expr) => ({ /* compiler built-in */ }) }
|
||||
macro_rules! compile_error {
|
||||
($msg:expr) => ({ /* compiler built-in */ });
|
||||
($msg:expr,) => ({ /* compiler built-in */ });
|
||||
}
|
||||
|
||||
/// The core macro for formatted string creation & output.
|
||||
///
|
||||
@ -400,7 +406,10 @@ pub mod builtin {
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[macro_export]
|
||||
macro_rules! option_env { ($name:expr) => ({ /* compiler built-in */ }) }
|
||||
macro_rules! option_env {
|
||||
($name:expr) => ({ /* compiler built-in */ });
|
||||
($name:expr,) => ({ /* compiler built-in */ });
|
||||
}
|
||||
|
||||
/// Concatenate identifiers into one identifier.
|
||||
///
|
||||
@ -580,7 +589,10 @@ pub mod builtin {
|
||||
/// Compiling 'main.rs' and running the resulting binary will print "adiós".
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[macro_export]
|
||||
macro_rules! include_str { ($file:expr) => ({ /* compiler built-in */ }) }
|
||||
macro_rules! include_str {
|
||||
($file:expr) => ({ /* compiler built-in */ });
|
||||
($file:expr,) => ({ /* compiler built-in */ });
|
||||
}
|
||||
|
||||
/// Includes a file as a reference to a byte array.
|
||||
///
|
||||
@ -614,7 +626,10 @@ pub mod builtin {
|
||||
/// Compiling 'main.rs' and running the resulting binary will print "adiós".
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[macro_export]
|
||||
macro_rules! include_bytes { ($file:expr) => ({ /* compiler built-in */ }) }
|
||||
macro_rules! include_bytes {
|
||||
($file:expr) => ({ /* compiler built-in */ });
|
||||
($file:expr,) => ({ /* compiler built-in */ });
|
||||
}
|
||||
|
||||
/// Expands to a string that represents the current module path.
|
||||
///
|
||||
@ -700,7 +715,10 @@ pub mod builtin {
|
||||
/// "🙈🙊🙉🙈🙊🙉".
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[macro_export]
|
||||
macro_rules! include { ($file:expr) => ({ /* compiler built-in */ }) }
|
||||
macro_rules! include {
|
||||
($file:expr) => ({ /* compiler built-in */ });
|
||||
($file:expr,) => ({ /* compiler built-in */ });
|
||||
}
|
||||
}
|
||||
|
||||
/// A macro for defining #[cfg] if-else statements.
|
||||
|
@ -890,8 +890,8 @@ pub fn check_zero_tts(cx: &ExtCtxt,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract the string literal from the first token of `tts`. If this
|
||||
/// is not a string literal, emit an error and return None.
|
||||
/// Interpreting `tts` as a comma-separated sequence of expressions,
|
||||
/// expect exactly one string literal, or emit an error and return None.
|
||||
pub fn get_single_str_from_tts(cx: &mut ExtCtxt,
|
||||
sp: Span,
|
||||
tts: &[tokenstream::TokenTree],
|
||||
@ -903,6 +903,8 @@ pub fn get_single_str_from_tts(cx: &mut ExtCtxt,
|
||||
return None
|
||||
}
|
||||
let ret = panictry!(p.parse_expr());
|
||||
let _ = p.eat(&token::Comma);
|
||||
|
||||
if p.token != token::Eof {
|
||||
cx.span_err(sp, &format!("{} takes 1 argument", name));
|
||||
}
|
||||
|
@ -28,6 +28,8 @@ pub fn expand_cfg<'cx>(cx: &mut ExtCtxt,
|
||||
let mut p = cx.new_parser_from_tts(tts);
|
||||
let cfg = panictry!(p.parse_meta_item());
|
||||
|
||||
let _ = p.eat(&token::Comma);
|
||||
|
||||
if !p.eat(&token::Eof) {
|
||||
cx.span_err(sp, "expected 1 cfg-pattern");
|
||||
return DummyResult::expr(sp);
|
||||
|
101
src/test/compile-fail/macro-comma-behavior.rs
Normal file
101
src/test/compile-fail/macro-comma-behavior.rs
Normal file
@ -0,0 +1,101 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Companion test to the similarly-named file in run-pass.
|
||||
|
||||
// compile-flags: -C debug_assertions=yes
|
||||
// revisions: std core
|
||||
|
||||
#![cfg_attr(core, no_std)]
|
||||
|
||||
#[cfg(std)] use std::fmt;
|
||||
#[cfg(core)] use core::fmt;
|
||||
|
||||
// (see documentation of the similarly-named test in run-pass)
|
||||
fn to_format_or_not_to_format() {
|
||||
let falsum = || false;
|
||||
|
||||
// assert!(true, "{}",); // see run-pass
|
||||
|
||||
assert_eq!(1, 1, "{}",);
|
||||
//[core]~^ ERROR no arguments
|
||||
//[std]~^^ ERROR no arguments
|
||||
assert_ne!(1, 2, "{}",);
|
||||
//[core]~^ ERROR no arguments
|
||||
//[std]~^^ ERROR no arguments
|
||||
|
||||
// debug_assert!(true, "{}",); // see run-pass
|
||||
|
||||
debug_assert_eq!(1, 1, "{}",);
|
||||
//[core]~^ ERROR no arguments
|
||||
//[std]~^^ ERROR no arguments
|
||||
debug_assert_ne!(1, 2, "{}",);
|
||||
//[core]~^ ERROR no arguments
|
||||
//[std]~^^ ERROR no arguments
|
||||
|
||||
#[cfg(std)] {
|
||||
eprint!("{}",);
|
||||
//[std]~^ ERROR no arguments
|
||||
}
|
||||
|
||||
#[cfg(std)] {
|
||||
// FIXME: compile-fail says "expected error not found" even though
|
||||
// rustc does emit an error
|
||||
// eprintln!("{}",);
|
||||
// <DISABLED> [std]~^ ERROR no arguments
|
||||
}
|
||||
|
||||
#[cfg(std)] {
|
||||
format!("{}",);
|
||||
//[std]~^ ERROR no arguments
|
||||
}
|
||||
|
||||
format_args!("{}",);
|
||||
//[core]~^ ERROR no arguments
|
||||
//[std]~^^ ERROR no arguments
|
||||
|
||||
// if falsum() { panic!("{}",); } // see run-pass
|
||||
|
||||
#[cfg(std)] {
|
||||
print!("{}",);
|
||||
//[std]~^ ERROR no arguments
|
||||
}
|
||||
|
||||
#[cfg(std)] {
|
||||
// FIXME: compile-fail says "expected error not found" even though
|
||||
// rustc does emit an error
|
||||
// println!("{}",);
|
||||
// <DISABLED> [std]~^ ERROR no arguments
|
||||
}
|
||||
|
||||
unimplemented!("{}",);
|
||||
//[core]~^ ERROR no arguments
|
||||
//[std]~^^ ERROR no arguments
|
||||
|
||||
// if falsum() { unreachable!("{}",); } // see run-pass
|
||||
|
||||
struct S;
|
||||
impl fmt::Display for S {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}",)?;
|
||||
//[core]~^ ERROR no arguments
|
||||
//[std]~^^ ERROR no arguments
|
||||
|
||||
// FIXME: compile-fail says "expected error not found" even though
|
||||
// rustc does emit an error
|
||||
// writeln!(f, "{}",)?;
|
||||
// <DISABLED> [core]~^ ERROR no arguments
|
||||
// <DISABLED> [std]~^^ ERROR no arguments
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
20
src/test/compile-fail/macro-comma-support.rs
Normal file
20
src/test/compile-fail/macro-comma-support.rs
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// This is a companion to the similarly-named test in run-pass.
|
||||
//
|
||||
// It tests macros that unavoidably produce compile errors.
|
||||
|
||||
fn compile_error() {
|
||||
compile_error!("lel"); //~ ERROR lel
|
||||
compile_error!("lel",); //~ ERROR lel
|
||||
}
|
||||
|
||||
fn main() {}
|
11
src/test/run-pass/auxiliary/macro-comma-support.rs
Normal file
11
src/test/run-pass/auxiliary/macro-comma-support.rs
Normal file
@ -0,0 +1,11 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
()
|
98
src/test/run-pass/macro-comma-behavior.rs
Normal file
98
src/test/run-pass/macro-comma-behavior.rs
Normal file
@ -0,0 +1,98 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Ideally, any macro call with a trailing comma should behave
|
||||
// identically to a call without the comma.
|
||||
//
|
||||
// This checks the behavior of macros with trailing commas in key
|
||||
// places where regressions in behavior seem highly possible (due
|
||||
// to it being e.g. a place where the addition of an argument
|
||||
// causes it to go down a code path with subtly different behavior).
|
||||
//
|
||||
// There is a companion test in compile-fail.
|
||||
|
||||
// compile-flags: --test -C debug_assertions=yes
|
||||
// revisions: std core
|
||||
|
||||
#![cfg_attr(core, no_std)]
|
||||
|
||||
#[cfg(std)] use std::fmt;
|
||||
#[cfg(core)] use core::fmt;
|
||||
|
||||
// an easy mistake in the implementation of 'assert!'
|
||||
// would cause this to say "explicit panic"
|
||||
#[test]
|
||||
#[should_panic(expected = "assertion failed")]
|
||||
fn assert_1arg() {
|
||||
assert!(false,);
|
||||
}
|
||||
|
||||
// same as 'assert_1arg'
|
||||
#[test]
|
||||
#[should_panic(expected = "assertion failed")]
|
||||
fn debug_assert_1arg() {
|
||||
debug_assert!(false,);
|
||||
}
|
||||
|
||||
// make sure we don't accidentally forward to `write!("text")`
|
||||
#[cfg(std)]
|
||||
#[test]
|
||||
fn writeln_1arg() {
|
||||
use fmt::Write;
|
||||
|
||||
let mut s = String::new();
|
||||
writeln!(&mut s,).unwrap();
|
||||
assert_eq!(&s, "\n");
|
||||
}
|
||||
|
||||
// A number of format_args-like macros have special-case treatment
|
||||
// for a single message string, which is not formatted.
|
||||
//
|
||||
// This test ensures that the addition of a trailing comma does not
|
||||
// suddenly cause these strings to get formatted when they otherwise
|
||||
// would not be. This is an easy mistake to make by having such a macro
|
||||
// accept ", $($tok:tt)*" instead of ", $($tok:tt)+" after its minimal
|
||||
// set of arguments.
|
||||
//
|
||||
// (Example: Issue #48042)
|
||||
#[test]
|
||||
fn to_format_or_not_to_format() {
|
||||
// ("{}" is the easiest string to test because if this gets
|
||||
// sent to format_args!, it'll simply fail to compile.
|
||||
// "{{}}" is an example of an input that could compile and
|
||||
// produce an incorrect program, but testing the panics
|
||||
// would be burdensome.)
|
||||
let falsum = || false;
|
||||
|
||||
assert!(true, "{}",);
|
||||
|
||||
// assert_eq!(1, 1, "{}",); // see compile-fail
|
||||
// assert_ne!(1, 2, "{}",); // see compile-fail
|
||||
|
||||
debug_assert!(true, "{}",);
|
||||
|
||||
// debug_assert_eq!(1, 1, "{}",); // see compile-fail
|
||||
// debug_assert_ne!(1, 2, "{}",); // see compile-fail
|
||||
// eprint!("{}",); // see compile-fail
|
||||
// eprintln!("{}",); // see compile-fail
|
||||
// format!("{}",); // see compile-fail
|
||||
// format_args!("{}",); // see compile-fail
|
||||
|
||||
if falsum() { panic!("{}",); }
|
||||
|
||||
// print!("{}",); // see compile-fail
|
||||
// println!("{}",); // see compile-fail
|
||||
// unimplemented!("{}",); // see compile-fail
|
||||
|
||||
if falsum() { unreachable!("{}",); }
|
||||
|
||||
// write!(&mut stdout, "{}",); // see compile-fail
|
||||
// writeln!(&mut stdout, "{}",); // see compile-fail
|
||||
}
|
359
src/test/run-pass/macro-comma-support.rs
Normal file
359
src/test/run-pass/macro-comma-support.rs
Normal file
@ -0,0 +1,359 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// This is meant to be a comprehensive test of invocations with/without
|
||||
// trailing commas (or other, similar optionally-trailing separators).
|
||||
// Every macro is accounted for, even those not tested in this file.
|
||||
// (There will be a note indicating why).
|
||||
|
||||
// std and core are both tested because they may contain separate
|
||||
// implementations for some macro_rules! macros as an implementation
|
||||
// detail.
|
||||
|
||||
// ignore-pretty issue #37195
|
||||
|
||||
// compile-flags: --test -C debug_assertions=yes
|
||||
// revisions: std core
|
||||
|
||||
#![cfg_attr(core, no_std)]
|
||||
|
||||
#![feature(concat_idents)]
|
||||
|
||||
#[cfg(std)] use std::fmt;
|
||||
#[cfg(core)] use core::fmt;
|
||||
|
||||
#[test]
|
||||
fn assert() {
|
||||
assert!(true);
|
||||
assert!(true,);
|
||||
assert!(true, "hello");
|
||||
assert!(true, "hello",);
|
||||
assert!(true, "hello {}", "world");
|
||||
assert!(true, "hello {}", "world",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assert_eq() {
|
||||
assert_eq!(1, 1);
|
||||
assert_eq!(1, 1,);
|
||||
assert_eq!(1, 1, "hello");
|
||||
assert_eq!(1, 1, "hello",);
|
||||
assert_eq!(1, 1, "hello {}", "world");
|
||||
assert_eq!(1, 1, "hello {}", "world",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assert_ne() {
|
||||
assert_ne!(1, 2);
|
||||
assert_ne!(1, 2,);
|
||||
assert_ne!(1, 2, "hello");
|
||||
assert_ne!(1, 2, "hello",);
|
||||
assert_ne!(1, 2, "hello {}", "world");
|
||||
assert_ne!(1, 2, "hello {}", "world",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cfg() {
|
||||
let _ = cfg!(pants);
|
||||
let _ = cfg!(pants,);
|
||||
let _ = cfg!(pants = "pants");
|
||||
let _ = cfg!(pants = "pants",);
|
||||
let _ = cfg!(all(pants));
|
||||
let _ = cfg!(all(pants),);
|
||||
let _ = cfg!(all(pants,));
|
||||
let _ = cfg!(all(pants,),);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn column() {
|
||||
let _ = column!();
|
||||
}
|
||||
|
||||
// compile_error! is in a companion to this test in compile-fail
|
||||
|
||||
#[test]
|
||||
fn concat() {
|
||||
let _ = concat!();
|
||||
let _ = concat!("hello");
|
||||
let _ = concat!("hello",);
|
||||
let _ = concat!("hello", " world");
|
||||
let _ = concat!("hello", " world",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn concat_idents() {
|
||||
fn foo() {}
|
||||
fn foobar() {}
|
||||
|
||||
concat_idents!(foo)();
|
||||
concat_idents!(foo,)();
|
||||
concat_idents!(foo, bar)();
|
||||
concat_idents!(foo, bar,)();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn debug_assert() {
|
||||
debug_assert!(true);
|
||||
debug_assert!(true, );
|
||||
debug_assert!(true, "hello");
|
||||
debug_assert!(true, "hello",);
|
||||
debug_assert!(true, "hello {}", "world");
|
||||
debug_assert!(true, "hello {}", "world",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn debug_assert_eq() {
|
||||
debug_assert_eq!(1, 1);
|
||||
debug_assert_eq!(1, 1,);
|
||||
debug_assert_eq!(1, 1, "hello");
|
||||
debug_assert_eq!(1, 1, "hello",);
|
||||
debug_assert_eq!(1, 1, "hello {}", "world");
|
||||
debug_assert_eq!(1, 1, "hello {}", "world",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn debug_assert_ne() {
|
||||
debug_assert_ne!(1, 2);
|
||||
debug_assert_ne!(1, 2,);
|
||||
debug_assert_ne!(1, 2, "hello");
|
||||
debug_assert_ne!(1, 2, "hello",);
|
||||
debug_assert_ne!(1, 2, "hello {}", "world");
|
||||
debug_assert_ne!(1, 2, "hello {}", "world",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn env() {
|
||||
let _ = env!("PATH");
|
||||
let _ = env!("PATH",);
|
||||
let _ = env!("PATH", "not found");
|
||||
let _ = env!("PATH", "not found",);
|
||||
}
|
||||
|
||||
#[cfg(std)]
|
||||
#[test]
|
||||
fn eprint() {
|
||||
eprint!("hello");
|
||||
eprint!("hello",);
|
||||
eprint!("hello {}", "world");
|
||||
eprint!("hello {}", "world",);
|
||||
}
|
||||
|
||||
#[cfg(std)]
|
||||
#[test]
|
||||
fn eprintln() {
|
||||
eprintln!();
|
||||
eprintln!("hello");
|
||||
eprintln!("hello",);
|
||||
eprintln!("hello {}", "world");
|
||||
eprintln!("hello {}", "world",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file() {
|
||||
let _ = file!();
|
||||
}
|
||||
|
||||
#[cfg(std)]
|
||||
#[test]
|
||||
fn format() {
|
||||
let _ = format!("hello");
|
||||
let _ = format!("hello",);
|
||||
let _ = format!("hello {}", "world");
|
||||
let _ = format!("hello {}", "world",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_args() {
|
||||
let _ = format_args!("hello");
|
||||
let _ = format_args!("hello",);
|
||||
let _ = format_args!("hello {}", "world");
|
||||
let _ = format_args!("hello {}", "world",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn include() {
|
||||
let _ = include!("auxiliary/macro-comma-support.rs");
|
||||
let _ = include!("auxiliary/macro-comma-support.rs",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn include_bytes() {
|
||||
let _ = include_bytes!("auxiliary/macro-comma-support.rs");
|
||||
let _ = include_bytes!("auxiliary/macro-comma-support.rs",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn include_str() {
|
||||
let _ = include_str!("auxiliary/macro-comma-support.rs");
|
||||
let _ = include_str!("auxiliary/macro-comma-support.rs",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn line() {
|
||||
let _ = line!();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn module_path() {
|
||||
let _ = module_path!();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn option_env() {
|
||||
let _ = option_env!("PATH");
|
||||
let _ = option_env!("PATH",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panic() {
|
||||
// prevent 'unreachable code' warnings
|
||||
let falsum = || false;
|
||||
|
||||
if falsum() { panic!(); }
|
||||
if falsum() { panic!("hello"); }
|
||||
if falsum() { panic!("hello",); }
|
||||
if falsum() { panic!("hello {}", "world"); }
|
||||
if falsum() { panic!("hello {}", "world",); }
|
||||
}
|
||||
|
||||
#[cfg(std)]
|
||||
#[test]
|
||||
fn print() {
|
||||
print!("hello");
|
||||
print!("hello",);
|
||||
print!("hello {}", "world");
|
||||
print!("hello {}", "world",);
|
||||
}
|
||||
|
||||
#[cfg(std)]
|
||||
#[test]
|
||||
fn println() {
|
||||
println!();
|
||||
println!("hello");
|
||||
println!("hello",);
|
||||
println!("hello {}", "world");
|
||||
println!("hello {}", "world",);
|
||||
}
|
||||
|
||||
// select! is too troublesome and unlikely to be stabilized
|
||||
|
||||
// stringify! is N/A
|
||||
|
||||
#[cfg(std)]
|
||||
#[test]
|
||||
fn thread_local() {
|
||||
// this has an optional trailing *semicolon*
|
||||
thread_local! {
|
||||
#[allow(unused)] pub static A: () = ()
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
#[allow(unused)] pub static AA: () = ();
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
#[allow(unused)] pub static AAA: () = ();
|
||||
#[allow(unused)] pub static AAAA: () = ()
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
#[allow(unused)] pub static AAAAG: () = ();
|
||||
#[allow(unused)] pub static AAAAGH: () = ();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try() {
|
||||
fn inner() -> Result<(), ()> {
|
||||
try!(Ok(()));
|
||||
try!(Ok(()),);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
inner().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unimplemented() {
|
||||
// prevent 'unreachable code' warnings
|
||||
let falsum = || false;
|
||||
|
||||
if falsum() { unimplemented!(); }
|
||||
if falsum() { unimplemented!("hello"); }
|
||||
if falsum() { unimplemented!("hello",); }
|
||||
if falsum() { unimplemented!("hello {}", "world"); }
|
||||
if falsum() { unimplemented!("hello {}", "world",); }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unreachable() {
|
||||
// prevent 'unreachable code' warnings
|
||||
let falsum = || false;
|
||||
|
||||
if falsum() { unreachable!(); }
|
||||
if falsum() { unreachable!("hello"); }
|
||||
if falsum() { unreachable!("hello",); }
|
||||
if falsum() { unreachable!("hello {}", "world"); }
|
||||
if falsum() { unreachable!("hello {}", "world",); }
|
||||
}
|
||||
|
||||
#[cfg(std)]
|
||||
#[test]
|
||||
fn vec() {
|
||||
let _: Vec<()> = vec![];
|
||||
let _ = vec![0];
|
||||
let _ = vec![0,];
|
||||
let _ = vec![0, 1];
|
||||
let _ = vec![0, 1,];
|
||||
}
|
||||
|
||||
// give a test body access to a fmt::Formatter, which seems
|
||||
// to be the easiest way to use 'write!' on core.
|
||||
macro_rules! test_with_formatter {
|
||||
(
|
||||
#[test]
|
||||
fn $fname:ident($f:ident: &mut fmt::Formatter) $block:block
|
||||
) => {
|
||||
#[test]
|
||||
fn $fname() {
|
||||
struct Struct;
|
||||
impl fmt::Display for Struct {
|
||||
fn fmt(&self, $f: &mut fmt::Formatter) -> fmt::Result {
|
||||
Ok($block)
|
||||
}
|
||||
}
|
||||
|
||||
// suppress "unused"
|
||||
assert!(true, "{}", Struct);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test_with_formatter! {
|
||||
#[test]
|
||||
fn write(f: &mut fmt::Formatter) {
|
||||
let _ = write!(f, "hello");
|
||||
let _ = write!(f, "hello",);
|
||||
let _ = write!(f, "hello {}", "world");
|
||||
let _ = write!(f, "hello {}", "world",);
|
||||
}
|
||||
}
|
||||
|
||||
test_with_formatter! {
|
||||
#[test]
|
||||
fn writeln(f: &mut fmt::Formatter) {
|
||||
let _ = writeln!(f);
|
||||
let _ = writeln!(f,);
|
||||
let _ = writeln!(f, "hello");
|
||||
let _ = writeln!(f, "hello",);
|
||||
let _ = writeln!(f, "hello {}", "world");
|
||||
let _ = writeln!(f, "hello {}", "world",);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user