Created
February 7, 2018 18:02
-
-
Save ExpHP/6fc40e82f3d73267c4e590a9a94966f1 to your computer and use it in GitHub Desktop.
macro-comma-support tests with double comma tests
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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) | |
// for all macros in rust's standard library. | |
// | |
// Invocations with double trailing commas are included to make sure | |
// they fail to compile. The particular error message is not of | |
// interest, so in lieu of annotating them with an expected message, | |
// their specificity is demonstrated simply by placing them right next | |
// to single-comma tests that do not produce errors. (where possible) | |
// | |
// 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: -C debug_assertions=yes | |
// revisions: std core | |
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | |
// !! FIXME FIXME FIXME ------------------------ FIXME FIXME FIXME !! | |
// !! | |
// !! Turns out this idea of testing against double commas | |
// !! alongside tests for single commas doesn't pan out too well. | |
// !! This file stops emitting errors after the test case for | |
// !! 'assert!(true, "hello",,);' | |
// !! | |
// !! My guess is that when format_args! encounters an unexpected | |
// !! double-comma, it is a fatal error and compilation immediately | |
// !! ends. | |
// !! | |
// !! This could be worked around by creating an obscene number of | |
// !! separate test files, but then it becomes very difficult to | |
// !! review them and verify that they are comprehensive. | |
// !! | |
// !! FIXME FIXME FIXME ------------------------ FIXME FIXME FIXME !! | |
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | |
#![cfg_attr(core, no_std)] | |
#![feature(concat_idents)] | |
#[cfg(std)] use std::fmt; | |
#[cfg(core)] use core::fmt; | |
fn assert() { | |
assert!(true); | |
assert!(true,); | |
assert!(true,,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
assert!(true, "hello"); | |
assert!(true, "hello",); | |
assert!(true, "hello",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
assert!(true, "hello {}", "world"); | |
assert!(true, "hello {}", "world",); | |
assert!(true, "hello {}", "world",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
} | |
fn assert_eq() { | |
assert_eq!(1, 1); | |
assert_eq!(1, 1,); | |
assert_eq!(1, 1,,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
assert_eq!(1, 1, "hello"); | |
assert_eq!(1, 1, "hello",); | |
assert_eq!(1, 1, "hello",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
assert_eq!(1, 1, "hello {}", "world"); | |
assert_eq!(1, 1, "hello {}", "world",); | |
assert_eq!(1, 1, "hello {}", "world",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
} | |
fn assert_ne() { | |
assert_ne!(1, 2); | |
assert_ne!(1, 2,); | |
assert_ne!(1, 2,,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
assert_ne!(1, 2, "hello"); | |
assert_ne!(1, 2, "hello",); | |
assert_ne!(1, 2, "hello",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
assert_ne!(1, 2, "hello {}", "world"); | |
assert_ne!(1, 2, "hello {}", "world",); | |
assert_ne!(1, 2, "hello {}", "world",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
} | |
fn cfg() { | |
let _ = cfg!(pants); | |
let _ = cfg!(pants,); | |
let _ = cfg!(pants,,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
let _ = cfg!(pants = "pants"); | |
let _ = cfg!(pants = "pants",); | |
let _ = cfg!(pants = "pants",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
let _ = cfg!(all(pants)); | |
let _ = cfg!(all(pants),); | |
let _ = cfg!(all(pants),,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
let _ = cfg!(all(pants,)); | |
let _ = cfg!(all(pants,,)); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
} | |
fn column() { | |
let _ = column!(); | |
} | |
fn compile_error() { | |
compile_error!("lel"); | |
//[core]~^ ERROR lel | |
//[std]~^^ ERROR lel | |
compile_error!("lel",); | |
//[core]~^ ERROR lel | |
//[std]~^^ ERROR lel | |
compile_error!("lel",,); | |
//[core]~^ ERROR lel | |
//[std]~^^ ERROR lel | |
//[core]~^^^ ERROR expected | |
//[std]~^^^^ ERROR expected | |
} | |
fn concat() { | |
let _ = concat!(); | |
let _ = concat!("hello"); | |
let _ = concat!("hello",); | |
let _ = concat!("hello",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
let _ = concat!("hello", " world"); | |
let _ = concat!("hello", " world",); | |
let _ = concat!("hello", " world",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
} | |
fn concat_idents() { | |
fn foo() {} | |
fn foobar() {} | |
concat_idents!(foo)(); | |
concat_idents!(foo,)(); | |
concat_idents!(foo,,)(); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
concat_idents!(foo, bar)(); | |
concat_idents!(foo, bar,)(); | |
concat_idents!(foo, bar,,)(); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
} | |
fn debug_assert() { | |
debug_assert!(true); | |
debug_assert!(true,); | |
debug_assert!(true,,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
debug_assert!(true, "hello"); | |
debug_assert!(true, "hello",); | |
debug_assert!(true, "hello",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
debug_assert!(true, "hello {}", "world"); | |
debug_assert!(true, "hello {}", "world",); | |
debug_assert!(true, "hello {}", "world",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
} | |
fn debug_assert_eq() { | |
debug_assert_eq!(1, 1); | |
debug_assert_eq!(1, 1,); | |
debug_assert_eq!(1, 1,,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
debug_assert_eq!(1, 1, "hello"); | |
debug_assert_eq!(1, 1, "hello",); | |
debug_assert_eq!(1, 1, "hello",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
debug_assert_eq!(1, 1, "hello {}", "world"); | |
debug_assert_eq!(1, 1, "hello {}", "world",); | |
debug_assert_eq!(1, 1, "hello {}", "world",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
} | |
fn debug_assert_ne() { | |
debug_assert_ne!(1, 2); | |
debug_assert_ne!(1, 2,); | |
debug_assert_ne!(1, 2,,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
debug_assert_ne!(1, 2, "hello"); | |
debug_assert_ne!(1, 2, "hello",); | |
debug_assert_ne!(1, 2, "hello",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
debug_assert_ne!(1, 2, "hello {}", "world"); | |
debug_assert_ne!(1, 2, "hello {}", "world",); | |
debug_assert_ne!(1, 2, "hello {}", "world",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
} | |
fn env() { | |
let _ = env!("PATH"); | |
let _ = env!("PATH",); | |
let _ = env!("PATH",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
let _ = env!("PATH", "not found"); | |
let _ = env!("PATH", "not found",); | |
let _ = env!("PATH", "not found",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
} | |
#[cfg(std)] | |
fn eprint() { | |
eprint!("hello"); | |
eprint!("hello",); | |
eprint!("hello",,); | |
//[std]~^ ERROR | |
eprint!("hello {}", "world"); | |
eprint!("hello {}", "world",); | |
eprint!("hello {}", "world",,); | |
//[std]~^ ERROR | |
} | |
#[cfg(std)] | |
fn eprintln() { | |
eprintln!(); | |
eprintln!("hello"); | |
eprintln!("hello",); | |
eprintln!("hello",,); | |
//[std]~^ ERROR | |
eprintln!("hello {}", "world"); | |
eprintln!("hello {}", "world",); | |
eprintln!("hello {}", "world",,); | |
//[std]~^ ERROR | |
} | |
fn file() { | |
let _ = file!(); | |
} | |
#[cfg(std)] | |
fn format() { | |
let _ = format!("hello"); | |
let _ = format!("hello",); | |
let _ = format!("hello",,); | |
//[std]~^ ERROR | |
let _ = format!("hello {}", "world"); | |
let _ = format!("hello {}", "world",); | |
let _ = format!("hello {}", "world",,); | |
//[std]~^ ERROR | |
} | |
fn format_args() { | |
let _ = format_args!("hello"); | |
let _ = format_args!("hello",); | |
let _ = format_args!("hello",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
let _ = format_args!("hello {}", "world"); | |
let _ = format_args!("hello {}", "world",); | |
let _ = format_args!("hello {}", "world",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
} | |
fn include() { | |
let _ = include!("auxiliary/macro-comma-support.rs"); | |
let _ = include!("auxiliary/macro-comma-support.rs",); | |
let _ = include!("auxiliary/macro-comma-support.rs",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
} | |
fn include_bytes() { | |
let _ = include_bytes!("auxiliary/macro-comma-support.rs"); | |
let _ = include_bytes!("auxiliary/macro-comma-support.rs",); | |
let _ = include_bytes!("auxiliary/macro-comma-support.rs",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
} | |
fn include_str() { | |
let _ = include_str!("auxiliary/macro-comma-support.rs"); | |
let _ = include_str!("auxiliary/macro-comma-support.rs",); | |
let _ = include_str!("auxiliary/macro-comma-support.rs",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
} | |
fn line() { | |
let _ = line!(); | |
} | |
fn module_path() { | |
let _ = module_path!(); | |
} | |
fn option_env() { | |
let _ = option_env!("PATH"); | |
let _ = option_env!("PATH",); | |
let _ = option_env!("PATH",,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
} | |
fn panic() { | |
// prevent 'unreachable code' warnings | |
let falsum = || false; | |
if falsum() { panic!(); } | |
if falsum() { panic!("hello"); } | |
if falsum() { panic!("hello",); } | |
if falsum() { panic!("hello",,); } | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
if falsum() { panic!("hello {}", "world"); } | |
if falsum() { panic!("hello {}", "world",); } | |
if falsum() { panic!("hello {}", "world",,); } | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
} | |
#[cfg(std)] | |
fn print() { | |
print!("hello"); | |
print!("hello",); | |
print!("hello",,); | |
//[std]~^ ERROR | |
print!("hello {}", "world"); | |
print!("hello {}", "world",); | |
print!("hello {}", "world",,); | |
//[std]~^ ERROR | |
} | |
#[cfg(std)] | |
fn println() { | |
println!(); | |
println!("hello"); | |
println!("hello",); | |
println!("hello",,); | |
//[std]~^ ERROR | |
println!("hello {}", "world"); | |
println!("hello {}", "world",); | |
println!("hello {}", "world",,); | |
//[std]~^ ERROR | |
} | |
// 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)] | |
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: () = ();; | |
//[std]~^ ERROR | |
} | |
thread_local! { | |
#[allow(unused)] pub static AAAA: () = (); | |
#[allow(unused)] pub static AAAAA: () = () | |
} | |
thread_local! { | |
#[allow(unused)] pub static AAAAAA: () = (); | |
#[allow(unused)] pub static AAAAAAG: () = (); | |
} | |
thread_local! { | |
#[allow(unused)] pub static AAAAAAGH: () = (); | |
#[allow(unused)] pub static AAAAAAGHH: () = ();; | |
//[std]~^ ERROR | |
} | |
} | |
fn try() { | |
fn inner() -> Result<(), ()> { | |
try!(Ok(())); | |
try!(Ok(()),); | |
try!(Ok(()),,); | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
Ok(()) | |
} | |
inner().unwrap(); | |
} | |
fn unimplemented() { | |
// prevent 'unreachable code' warnings | |
let falsum = || false; | |
if falsum() { unimplemented!(); } | |
if falsum() { unimplemented!("hello"); } | |
if falsum() { unimplemented!("hello",); } | |
if falsum() { unimplemented!("hello",,); } | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
if falsum() { unimplemented!("hello {}", "world"); } | |
if falsum() { unimplemented!("hello {}", "world",); } | |
if falsum() { unimplemented!("hello {}", "world",,); } | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
} | |
fn unreachable() { | |
// prevent 'unreachable code' warnings | |
let falsum = || false; | |
if falsum() { unreachable!(); } | |
if falsum() { unreachable!("hello"); } | |
if falsum() { unreachable!("hello",); } | |
if falsum() { unreachable!("hello",,); } | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
if falsum() { unreachable!("hello {}", "world"); } | |
if falsum() { unreachable!("hello {}", "world",); } | |
if falsum() { unreachable!("hello {}", "world",,); } | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
} | |
#[cfg(std)] | |
fn vec() { | |
let _: Vec<()> = vec![]; | |
let _ = vec![0]; | |
let _ = vec![0,]; | |
let _ = vec![0,,]; | |
//[std]~^ ERROR | |
let _ = vec![0, 1]; | |
let _ = vec![0, 1,]; | |
let _ = vec![0, 1,,]; | |
//[std]~^ ERROR | |
} | |
fn write() { | |
// impl fmt::Display to get access to a Write object on #![no_std] | |
struct Struct; | |
impl fmt::Display for Struct { | |
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
write!(f, "hello")?; | |
write!(f, "hello",)?; | |
write!(f, "hello",,)?; | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
write!(f, "hello {}", "world")?; | |
write!(f, "hello {}", "world",)?; | |
write!(f, "hello {}", "world",,)?; | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
Ok(()) | |
} | |
} | |
assert!(true, "{}", Struct); | |
} | |
fn writeln() { | |
// impl fmt::Display to get access to a Write object on #![no_std] | |
struct Struct; | |
impl fmt::Display for Struct { | |
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
writeln!(f)?; | |
writeln!(f,)?; | |
writeln!(f,,)?; | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
writeln!(f, "hello")?; | |
writeln!(f, "hello",)?; | |
writeln!(f, "hello",,)?; | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
writeln!(f, "hello {}", "world")?; | |
writeln!(f, "hello {}", "world",)?; | |
writeln!(f, "hello {}", "world",,)?; | |
//[core]~^ ERROR | |
//[std]~^^ ERROR | |
Ok(()) | |
} | |
} | |
assert!(true, "{}", Struct); | |
} | |
fn main() {} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment