Skip to content

Instantly share code, notes, and snippets.

@ExpHP
Created February 7, 2018 18:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ExpHP/6fc40e82f3d73267c4e590a9a94966f1 to your computer and use it in GitHub Desktop.
Save ExpHP/6fc40e82f3d73267c4e590a9a94966f1 to your computer and use it in GitHub Desktop.
macro-comma-support tests with double comma tests
// 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.
()
// 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