]> git.lizzy.rs Git - rust.git/commitdiff
add tests for macro trailing commas
authorMichael Lamparski <diagonaldevice@gmail.com>
Wed, 7 Feb 2018 04:03:14 +0000 (23:03 -0500)
committerMichael Lamparski <diagonaldevice@gmail.com>
Wed, 7 Feb 2018 14:29:37 +0000 (09:29 -0500)
src/test/compile-fail/macro-comma-behavior.rs [new file with mode: 0644]
src/test/compile-fail/macro-comma-support.rs [new file with mode: 0644]
src/test/run-pass/auxiliary/macro-comma-support.rs [new file with mode: 0644]
src/test/run-pass/macro-comma-behavior.rs [new file with mode: 0644]
src/test/run-pass/macro-comma-support.rs [new file with mode: 0644]

diff --git a/src/test/compile-fail/macro-comma-behavior.rs b/src/test/compile-fail/macro-comma-behavior.rs
new file mode 100644 (file)
index 0000000..620e57b
--- /dev/null
@@ -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() {}
diff --git a/src/test/compile-fail/macro-comma-support.rs b/src/test/compile-fail/macro-comma-support.rs
new file mode 100644 (file)
index 0000000..e5fe9b4
--- /dev/null
@@ -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() {}
diff --git a/src/test/run-pass/auxiliary/macro-comma-support.rs b/src/test/run-pass/auxiliary/macro-comma-support.rs
new file mode 100644 (file)
index 0000000..6eafd52
--- /dev/null
@@ -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.
+
+()
diff --git a/src/test/run-pass/macro-comma-behavior.rs b/src/test/run-pass/macro-comma-behavior.rs
new file mode 100644 (file)
index 0000000..f8065f0
--- /dev/null
@@ -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_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
+}
diff --git a/src/test/run-pass/macro-comma-support.rs b/src/test/run-pass/macro-comma-support.rs
new file mode 100644 (file)
index 0000000..f73dfb7
--- /dev/null
@@ -0,0 +1,371 @@
+// 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",);
+    }
+}