add tests for macro trailing commas

This commit is contained in:
Michael Lamparski 2018-02-06 23:03:14 -05:00
parent ca7d839088
commit 5fa97c35da
5 changed files with 601 additions and 0 deletions

View 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() {}

View 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() {}

View 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.
()

View 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_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
}

View File

@ -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",);
}
}