--- /dev/null
+// 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() {}
--- /dev/null
+// 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_2arg() {
+ use fmt::Write;
+
+ let mut s = String::new();
+ writeln!(&mut s, "hi",).unwrap();
+ assert_eq!(&s, "hi\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
+}
--- /dev/null
+// 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 comprehensive test of invocations with and 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).
+//
+// The expectation is for this to be updated as new macros are added,
+// or as functionality is added to existing macros.
+//
+// (FIXME: (please discuss in PR) is the above expectation reasonable?)
+
+// std and core are both tested because they may contain separate
+// implementations for some macro_rules! macros as an implementation
+// detail.
+
+// 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",);
+}
+
+// FIXME: select! (please discuss in PR)
+//
+// Test cases for select! are obnoxiously large, see here:
+//
+// https://github.com/ExpHP/rust-macro-comma-test/blob/0062e75e01ab/src/main.rs#L190-L250
+//
+// and due to other usability issues described there, it is unclear to me that it is
+// going anywhere in its current state. This is a job far too big for a macro_rules! macro,
+// and for as long as it exists in this form it will have many many problems far worse than
+// just lack of trailing comma support.
+
+// 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",);
+ }
+}