Auto merge of #48056 - ExpHP:macro-commas, r=dtolnay

Comprehensively support trailing commas in std/core macros

I carefully organized the changes into four commits:

* Test cases
* Fixes for `macro_rules!` macros
* Fixes for builtin macros
* Docs for builtins

**I can easily scale this back to just the first two commits for now if such is desired.**

### Breaking (?) changes

* This fixes #48042, which is a breaking change that I hope people can agree is just a bugfix for an extremely dark corner case.

* To fix five of the builtins, this changes `syntax::ext::base::get_single_str_from_tts` to accept a trailing comma, and revises the documentation so that this aspect is not surprising. **I made this change under the (hopefully correct) understanding that `libsyntax` is private rustc implementation detail.** After reviewing all call sites (which were, you guessed it, *precisely those five macros*), I believe the revised semantics are closer to the intended spirit of the function.

### Changes which may require concensus

Up until now, it could be argued that some or all the following macros did not conceptually take a comma-separated list, because they only took one argument:

  * **`cfg(unix,)`** (most notable since cfg! is unique in taking a meta tag)
  * **`include{,_bytes,_str}("file.rs",)`**  (in item form this might be written as "`include!{"file.rs",}`" which is even slightly more odd)
  * **`compile_error("message",);`**
  * **`option_env!("PATH",)`**
  * **`try!(Ok(()),)`**

So I think these particular changes may require some sort of consensus.  **All of the fixes for builtins are included this list, so if we want to defer these decisions to later then I can scale this PR back to just the first two commits.**

### Other notes/general requests for comment

* Do we have a big checklist somewhere of "things to do when adding macros?" My hope is for `run-pass/macro-comma-support.rs` to remain comprehensive.
* Originally I wanted the tests to also comprehensively forbid double trailing commas.  However, this didn't work out too well: [see this gist and the giant FIXME in it](https://gist.github.com/ExpHP/6fc40e82f3d73267c4e590a9a94966f1#file-compile-fail_macro-comma-support-rs-L33-L50)
* I did not touch `select!`. It appears to me to be a complete mess, and its trailing comma mishaps are only the tip of the iceberg.
* There are [some compile-fail test cases](https://github.com/ExpHP/rust/blob/5fa97c35da2f0ee/src/test/compile-fail/macro-comma-behavior.rs#L49-L52) that didn't seem to work (rustc emits errors, but compile-fail doesn't acknowledge them), so they are disabled. Any clues? (Possibly related: These happen to be precisely the set of errors which are tagged by rustc as "this error originates in a macro outside of the current crate".)

---

Fixes #48042
Closes #46241
This commit is contained in:
bors 2018-02-28 07:10:05 +00:00
commit ddab91a5de
9 changed files with 653 additions and 14 deletions

View file

@ -19,7 +19,10 @@ macro_rules! panic {
($msg:expr) => ({
$crate::panicking::panic(&($msg, file!(), line!(), __rust_unstable_column!()))
});
($fmt:expr, $($arg:tt)*) => ({
($msg:expr,) => (
panic!($msg)
);
($fmt:expr, $($arg:tt)+) => ({
$crate::panicking::panic_fmt(format_args!($fmt, $($arg)*),
&(file!(), line!(), __rust_unstable_column!()))
});
@ -79,6 +82,9 @@ macro_rules! assert {
panic!(concat!("assertion failed: ", stringify!($cond)))
}
);
($cond:expr,) => (
assert!($cond)
);
($cond:expr, $($arg:tt)+) => (
if !$cond {
panic!($($arg)+)
@ -359,7 +365,8 @@ macro_rules! try {
$crate::result::Result::Err(err) => {
return $crate::result::Result::Err($crate::convert::From::from(err))
}
})
});
($expr:expr,) => (try!($expr));
}
/// Write formatted data into a buffer.
@ -456,6 +463,9 @@ macro_rules! writeln {
($dst:expr) => (
write!($dst, "\n")
);
($dst:expr,) => (
writeln!($dst)
);
($dst:expr, $fmt:expr) => (
write!($dst, concat!($fmt, "\n"))
);
@ -524,6 +534,9 @@ macro_rules! unreachable {
($msg:expr) => ({
unreachable!("{}", $msg)
});
($msg:expr,) => ({
unreachable!($msg)
});
($fmt:expr, $($arg:tt)*) => ({
panic!(concat!("internal error: entered unreachable code: ", $fmt), $($arg)*)
});
@ -603,7 +616,10 @@ mod builtin {
#[stable(feature = "compile_error_macro", since = "1.20.0")]
#[macro_export]
#[cfg(dox)]
macro_rules! compile_error { ($msg:expr) => ({ /* compiler built-in */ }) }
macro_rules! compile_error {
($msg:expr) => ({ /* compiler built-in */ });
($msg:expr,) => ({ /* compiler built-in */ });
}
/// The core macro for formatted string creation & output.
///
@ -639,7 +655,10 @@ mod builtin {
#[stable(feature = "rust1", since = "1.0.0")]
#[macro_export]
#[cfg(dox)]
macro_rules! option_env { ($name:expr) => ({ /* compiler built-in */ }) }
macro_rules! option_env {
($name:expr) => ({ /* compiler built-in */ });
($name:expr,) => ({ /* compiler built-in */ });
}
/// Concatenate identifiers into one identifier.
///
@ -715,7 +734,10 @@ mod builtin {
#[stable(feature = "rust1", since = "1.0.0")]
#[macro_export]
#[cfg(dox)]
macro_rules! include_str { ($file:expr) => ({ /* compiler built-in */ }) }
macro_rules! include_str {
($file:expr) => ({ /* compiler built-in */ });
($file:expr,) => ({ /* compiler built-in */ });
}
/// Includes a file as a reference to a byte array.
///
@ -725,7 +747,10 @@ mod builtin {
#[stable(feature = "rust1", since = "1.0.0")]
#[macro_export]
#[cfg(dox)]
macro_rules! include_bytes { ($file:expr) => ({ /* compiler built-in */ }) }
macro_rules! include_bytes {
($file:expr) => ({ /* compiler built-in */ });
($file:expr,) => ({ /* compiler built-in */ });
}
/// Expands to a string that represents the current module path.
///
@ -755,5 +780,8 @@ mod builtin {
#[stable(feature = "rust1", since = "1.0.0")]
#[macro_export]
#[cfg(dox)]
macro_rules! include { ($file:expr) => ({ /* compiler built-in */ }) }
macro_rules! include {
($file:expr) => ({ /* compiler built-in */ });
($file:expr,) => ({ /* compiler built-in */ });
}
}

View file

@ -68,6 +68,9 @@ macro_rules! panic {
($msg:expr) => ({
$crate::rt::begin_panic($msg, &(file!(), line!(), __rust_unstable_column!()))
});
($msg:expr,) => ({
panic!($msg)
});
($fmt:expr, $($arg:tt)+) => ({
$crate::rt::begin_panic_fmt(&format_args!($fmt, $($arg)+),
&(file!(), line!(), __rust_unstable_column!()))
@ -312,7 +315,10 @@ pub mod builtin {
/// ```
#[stable(feature = "compile_error_macro", since = "1.20.0")]
#[macro_export]
macro_rules! compile_error { ($msg:expr) => ({ /* compiler built-in */ }) }
macro_rules! compile_error {
($msg:expr) => ({ /* compiler built-in */ });
($msg:expr,) => ({ /* compiler built-in */ });
}
/// The core macro for formatted string creation & output.
///
@ -400,7 +406,10 @@ pub mod builtin {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[macro_export]
macro_rules! option_env { ($name:expr) => ({ /* compiler built-in */ }) }
macro_rules! option_env {
($name:expr) => ({ /* compiler built-in */ });
($name:expr,) => ({ /* compiler built-in */ });
}
/// Concatenate identifiers into one identifier.
///
@ -580,7 +589,10 @@ pub mod builtin {
/// Compiling 'main.rs' and running the resulting binary will print "adiós".
#[stable(feature = "rust1", since = "1.0.0")]
#[macro_export]
macro_rules! include_str { ($file:expr) => ({ /* compiler built-in */ }) }
macro_rules! include_str {
($file:expr) => ({ /* compiler built-in */ });
($file:expr,) => ({ /* compiler built-in */ });
}
/// Includes a file as a reference to a byte array.
///
@ -614,7 +626,10 @@ pub mod builtin {
/// Compiling 'main.rs' and running the resulting binary will print "adiós".
#[stable(feature = "rust1", since = "1.0.0")]
#[macro_export]
macro_rules! include_bytes { ($file:expr) => ({ /* compiler built-in */ }) }
macro_rules! include_bytes {
($file:expr) => ({ /* compiler built-in */ });
($file:expr,) => ({ /* compiler built-in */ });
}
/// Expands to a string that represents the current module path.
///
@ -700,7 +715,10 @@ pub mod builtin {
/// "🙈🙊🙉🙈🙊🙉".
#[stable(feature = "rust1", since = "1.0.0")]
#[macro_export]
macro_rules! include { ($file:expr) => ({ /* compiler built-in */ }) }
macro_rules! include {
($file:expr) => ({ /* compiler built-in */ });
($file:expr,) => ({ /* compiler built-in */ });
}
}
/// A macro for defining #[cfg] if-else statements.

View file

@ -890,8 +890,8 @@ pub fn check_zero_tts(cx: &ExtCtxt,
}
}
/// Extract the string literal from the first token of `tts`. If this
/// is not a string literal, emit an error and return None.
/// Interpreting `tts` as a comma-separated sequence of expressions,
/// expect exactly one string literal, or emit an error and return None.
pub fn get_single_str_from_tts(cx: &mut ExtCtxt,
sp: Span,
tts: &[tokenstream::TokenTree],
@ -903,6 +903,8 @@ pub fn get_single_str_from_tts(cx: &mut ExtCtxt,
return None
}
let ret = panictry!(p.parse_expr());
let _ = p.eat(&token::Comma);
if p.token != token::Eof {
cx.span_err(sp, &format!("{} takes 1 argument", name));
}

View file

@ -28,6 +28,8 @@ pub fn expand_cfg<'cx>(cx: &mut ExtCtxt,
let mut p = cx.new_parser_from_tts(tts);
let cfg = panictry!(p.parse_meta_item());
let _ = p.eat(&token::Comma);
if !p.eat(&token::Eof) {
cx.span_err(sp, "expected 1 cfg-pattern");
return DummyResult::expr(sp);

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_1arg() {
use fmt::Write;
let mut s = String::new();
writeln!(&mut s,).unwrap();
assert_eq!(&s, "\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,359 @@
// 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 meant to be a comprehensive test of invocations with/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).
// std and core are both tested because they may contain separate
// implementations for some macro_rules! macros as an implementation
// detail.
// ignore-pretty issue #37195
// 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",);
}
// select! is too troublesome and unlikely to be stabilized
// 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",);
}
}