mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-05 11:48:30 +00:00
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) => ({
|
($msg:expr) => ({
|
||||||
$crate::panicking::panic(&($msg, file!(), line!(), __rust_unstable_column!()))
|
$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)*),
|
$crate::panicking::panic_fmt(format_args!($fmt, $($arg)*),
|
||||||
&(file!(), line!(), __rust_unstable_column!()))
|
&(file!(), line!(), __rust_unstable_column!()))
|
||||||
});
|
});
|
||||||
@ -79,6 +82,9 @@ macro_rules! assert {
|
|||||||
panic!(concat!("assertion failed: ", stringify!($cond)))
|
panic!(concat!("assertion failed: ", stringify!($cond)))
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
($cond:expr,) => (
|
||||||
|
assert!($cond)
|
||||||
|
);
|
||||||
($cond:expr, $($arg:tt)+) => (
|
($cond:expr, $($arg:tt)+) => (
|
||||||
if !$cond {
|
if !$cond {
|
||||||
panic!($($arg)+)
|
panic!($($arg)+)
|
||||||
@ -359,7 +365,8 @@ macro_rules! try {
|
|||||||
$crate::result::Result::Err(err) => {
|
$crate::result::Result::Err(err) => {
|
||||||
return $crate::result::Result::Err($crate::convert::From::from(err))
|
return $crate::result::Result::Err($crate::convert::From::from(err))
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
($expr:expr,) => (try!($expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write formatted data into a buffer.
|
/// Write formatted data into a buffer.
|
||||||
@ -456,6 +463,9 @@ macro_rules! writeln {
|
|||||||
($dst:expr) => (
|
($dst:expr) => (
|
||||||
write!($dst, "\n")
|
write!($dst, "\n")
|
||||||
);
|
);
|
||||||
|
($dst:expr,) => (
|
||||||
|
writeln!($dst)
|
||||||
|
);
|
||||||
($dst:expr, $fmt:expr) => (
|
($dst:expr, $fmt:expr) => (
|
||||||
write!($dst, concat!($fmt, "\n"))
|
write!($dst, concat!($fmt, "\n"))
|
||||||
);
|
);
|
||||||
@ -524,6 +534,9 @@ macro_rules! unreachable {
|
|||||||
($msg:expr) => ({
|
($msg:expr) => ({
|
||||||
unreachable!("{}", $msg)
|
unreachable!("{}", $msg)
|
||||||
});
|
});
|
||||||
|
($msg:expr,) => ({
|
||||||
|
unreachable!($msg)
|
||||||
|
});
|
||||||
($fmt:expr, $($arg:tt)*) => ({
|
($fmt:expr, $($arg:tt)*) => ({
|
||||||
panic!(concat!("internal error: entered unreachable code: ", $fmt), $($arg)*)
|
panic!(concat!("internal error: entered unreachable code: ", $fmt), $($arg)*)
|
||||||
});
|
});
|
||||||
@ -603,7 +616,10 @@ mod builtin {
|
|||||||
#[stable(feature = "compile_error_macro", since = "1.20.0")]
|
#[stable(feature = "compile_error_macro", since = "1.20.0")]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[cfg(dox)]
|
#[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.
|
/// The core macro for formatted string creation & output.
|
||||||
///
|
///
|
||||||
@ -639,7 +655,10 @@ mod builtin {
|
|||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[cfg(dox)]
|
#[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.
|
/// Concatenate identifiers into one identifier.
|
||||||
///
|
///
|
||||||
@ -715,7 +734,10 @@ mod builtin {
|
|||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[cfg(dox)]
|
#[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.
|
/// Includes a file as a reference to a byte array.
|
||||||
///
|
///
|
||||||
@ -725,7 +747,10 @@ mod builtin {
|
|||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[cfg(dox)]
|
#[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.
|
/// Expands to a string that represents the current module path.
|
||||||
///
|
///
|
||||||
@ -755,5 +780,8 @@ mod builtin {
|
|||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[cfg(dox)]
|
#[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) => ({
|
($msg:expr) => ({
|
||||||
$crate::rt::begin_panic($msg, &(file!(), line!(), __rust_unstable_column!()))
|
$crate::rt::begin_panic($msg, &(file!(), line!(), __rust_unstable_column!()))
|
||||||
});
|
});
|
||||||
|
($msg:expr,) => ({
|
||||||
|
panic!($msg)
|
||||||
|
});
|
||||||
($fmt:expr, $($arg:tt)+) => ({
|
($fmt:expr, $($arg:tt)+) => ({
|
||||||
$crate::rt::begin_panic_fmt(&format_args!($fmt, $($arg)+),
|
$crate::rt::begin_panic_fmt(&format_args!($fmt, $($arg)+),
|
||||||
&(file!(), line!(), __rust_unstable_column!()))
|
&(file!(), line!(), __rust_unstable_column!()))
|
||||||
@ -312,7 +315,10 @@ pub mod builtin {
|
|||||||
/// ```
|
/// ```
|
||||||
#[stable(feature = "compile_error_macro", since = "1.20.0")]
|
#[stable(feature = "compile_error_macro", since = "1.20.0")]
|
||||||
#[macro_export]
|
#[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.
|
/// The core macro for formatted string creation & output.
|
||||||
///
|
///
|
||||||
@ -400,7 +406,10 @@ pub mod builtin {
|
|||||||
/// ```
|
/// ```
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[macro_export]
|
#[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.
|
/// Concatenate identifiers into one identifier.
|
||||||
///
|
///
|
||||||
@ -580,7 +589,10 @@ pub mod builtin {
|
|||||||
/// Compiling 'main.rs' and running the resulting binary will print "adiós".
|
/// Compiling 'main.rs' and running the resulting binary will print "adiós".
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[macro_export]
|
#[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.
|
/// 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".
|
/// Compiling 'main.rs' and running the resulting binary will print "adiós".
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[macro_export]
|
#[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.
|
/// Expands to a string that represents the current module path.
|
||||||
///
|
///
|
||||||
@ -700,7 +715,10 @@ pub mod builtin {
|
|||||||
/// "🙈🙊🙉🙈🙊🙉".
|
/// "🙈🙊🙉🙈🙊🙉".
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[macro_export]
|
#[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.
|
/// 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
|
/// Interpreting `tts` as a comma-separated sequence of expressions,
|
||||||
/// is not a string literal, emit an error and return None.
|
/// expect exactly one string literal, or emit an error and return None.
|
||||||
pub fn get_single_str_from_tts(cx: &mut ExtCtxt,
|
pub fn get_single_str_from_tts(cx: &mut ExtCtxt,
|
||||||
sp: Span,
|
sp: Span,
|
||||||
tts: &[tokenstream::TokenTree],
|
tts: &[tokenstream::TokenTree],
|
||||||
@ -903,6 +903,8 @@ pub fn get_single_str_from_tts(cx: &mut ExtCtxt,
|
|||||||
return None
|
return None
|
||||||
}
|
}
|
||||||
let ret = panictry!(p.parse_expr());
|
let ret = panictry!(p.parse_expr());
|
||||||
|
let _ = p.eat(&token::Comma);
|
||||||
|
|
||||||
if p.token != token::Eof {
|
if p.token != token::Eof {
|
||||||
cx.span_err(sp, &format!("{} takes 1 argument", name));
|
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 mut p = cx.new_parser_from_tts(tts);
|
||||||
let cfg = panictry!(p.parse_meta_item());
|
let cfg = panictry!(p.parse_meta_item());
|
||||||
|
|
||||||
|
let _ = p.eat(&token::Comma);
|
||||||
|
|
||||||
if !p.eat(&token::Eof) {
|
if !p.eat(&token::Eof) {
|
||||||
cx.span_err(sp, "expected 1 cfg-pattern");
|
cx.span_err(sp, "expected 1 cfg-pattern");
|
||||||
return DummyResult::expr(sp);
|
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