Skip to content

Commit ddab91a

Browse files
committed
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
2 parents 89e5a07 + af503be commit ddab91a

File tree

9 files changed

+653
-14
lines changed

9 files changed

+653
-14
lines changed

src/libcore/macros.rs

+35-7
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ macro_rules! panic {
1919
($msg:expr) => ({
2020
$crate::panicking::panic(&($msg, file!(), line!(), __rust_unstable_column!()))
2121
});
22-
($fmt:expr, $($arg:tt)*) => ({
22+
($msg:expr,) => (
23+
panic!($msg)
24+
);
25+
($fmt:expr, $($arg:tt)+) => ({
2326
$crate::panicking::panic_fmt(format_args!($fmt, $($arg)*),
2427
&(file!(), line!(), __rust_unstable_column!()))
2528
});
@@ -79,6 +82,9 @@ macro_rules! assert {
7982
panic!(concat!("assertion failed: ", stringify!($cond)))
8083
}
8184
);
85+
($cond:expr,) => (
86+
assert!($cond)
87+
);
8288
($cond:expr, $($arg:tt)+) => (
8389
if !$cond {
8490
panic!($($arg)+)
@@ -359,7 +365,8 @@ macro_rules! try {
359365
$crate::result::Result::Err(err) => {
360366
return $crate::result::Result::Err($crate::convert::From::from(err))
361367
}
362-
})
368+
});
369+
($expr:expr,) => (try!($expr));
363370
}
364371

365372
/// Write formatted data into a buffer.
@@ -456,6 +463,9 @@ macro_rules! writeln {
456463
($dst:expr) => (
457464
write!($dst, "\n")
458465
);
466+
($dst:expr,) => (
467+
writeln!($dst)
468+
);
459469
($dst:expr, $fmt:expr) => (
460470
write!($dst, concat!($fmt, "\n"))
461471
);
@@ -524,6 +534,9 @@ macro_rules! unreachable {
524534
($msg:expr) => ({
525535
unreachable!("{}", $msg)
526536
});
537+
($msg:expr,) => ({
538+
unreachable!($msg)
539+
});
527540
($fmt:expr, $($arg:tt)*) => ({
528541
panic!(concat!("internal error: entered unreachable code: ", $fmt), $($arg)*)
529542
});
@@ -603,7 +616,10 @@ mod builtin {
603616
#[stable(feature = "compile_error_macro", since = "1.20.0")]
604617
#[macro_export]
605618
#[cfg(dox)]
606-
macro_rules! compile_error { ($msg:expr) => ({ /* compiler built-in */ }) }
619+
macro_rules! compile_error {
620+
($msg:expr) => ({ /* compiler built-in */ });
621+
($msg:expr,) => ({ /* compiler built-in */ });
622+
}
607623

608624
/// The core macro for formatted string creation & output.
609625
///
@@ -639,7 +655,10 @@ mod builtin {
639655
#[stable(feature = "rust1", since = "1.0.0")]
640656
#[macro_export]
641657
#[cfg(dox)]
642-
macro_rules! option_env { ($name:expr) => ({ /* compiler built-in */ }) }
658+
macro_rules! option_env {
659+
($name:expr) => ({ /* compiler built-in */ });
660+
($name:expr,) => ({ /* compiler built-in */ });
661+
}
643662

644663
/// Concatenate identifiers into one identifier.
645664
///
@@ -715,7 +734,10 @@ mod builtin {
715734
#[stable(feature = "rust1", since = "1.0.0")]
716735
#[macro_export]
717736
#[cfg(dox)]
718-
macro_rules! include_str { ($file:expr) => ({ /* compiler built-in */ }) }
737+
macro_rules! include_str {
738+
($file:expr) => ({ /* compiler built-in */ });
739+
($file:expr,) => ({ /* compiler built-in */ });
740+
}
719741

720742
/// Includes a file as a reference to a byte array.
721743
///
@@ -725,7 +747,10 @@ mod builtin {
725747
#[stable(feature = "rust1", since = "1.0.0")]
726748
#[macro_export]
727749
#[cfg(dox)]
728-
macro_rules! include_bytes { ($file:expr) => ({ /* compiler built-in */ }) }
750+
macro_rules! include_bytes {
751+
($file:expr) => ({ /* compiler built-in */ });
752+
($file:expr,) => ({ /* compiler built-in */ });
753+
}
729754

730755
/// Expands to a string that represents the current module path.
731756
///
@@ -755,5 +780,8 @@ mod builtin {
755780
#[stable(feature = "rust1", since = "1.0.0")]
756781
#[macro_export]
757782
#[cfg(dox)]
758-
macro_rules! include { ($file:expr) => ({ /* compiler built-in */ }) }
783+
macro_rules! include {
784+
($file:expr) => ({ /* compiler built-in */ });
785+
($file:expr,) => ({ /* compiler built-in */ });
786+
}
759787
}

src/libstd/macros.rs

+23-5
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ macro_rules! panic {
6868
($msg:expr) => ({
6969
$crate::rt::begin_panic($msg, &(file!(), line!(), __rust_unstable_column!()))
7070
});
71+
($msg:expr,) => ({
72+
panic!($msg)
73+
});
7174
($fmt:expr, $($arg:tt)+) => ({
7275
$crate::rt::begin_panic_fmt(&format_args!($fmt, $($arg)+),
7376
&(file!(), line!(), __rust_unstable_column!()))
@@ -312,7 +315,10 @@ pub mod builtin {
312315
/// ```
313316
#[stable(feature = "compile_error_macro", since = "1.20.0")]
314317
#[macro_export]
315-
macro_rules! compile_error { ($msg:expr) => ({ /* compiler built-in */ }) }
318+
macro_rules! compile_error {
319+
($msg:expr) => ({ /* compiler built-in */ });
320+
($msg:expr,) => ({ /* compiler built-in */ });
321+
}
316322

317323
/// The core macro for formatted string creation & output.
318324
///
@@ -400,7 +406,10 @@ pub mod builtin {
400406
/// ```
401407
#[stable(feature = "rust1", since = "1.0.0")]
402408
#[macro_export]
403-
macro_rules! option_env { ($name:expr) => ({ /* compiler built-in */ }) }
409+
macro_rules! option_env {
410+
($name:expr) => ({ /* compiler built-in */ });
411+
($name:expr,) => ({ /* compiler built-in */ });
412+
}
404413

405414
/// Concatenate identifiers into one identifier.
406415
///
@@ -580,7 +589,10 @@ pub mod builtin {
580589
/// Compiling 'main.rs' and running the resulting binary will print "adiós".
581590
#[stable(feature = "rust1", since = "1.0.0")]
582591
#[macro_export]
583-
macro_rules! include_str { ($file:expr) => ({ /* compiler built-in */ }) }
592+
macro_rules! include_str {
593+
($file:expr) => ({ /* compiler built-in */ });
594+
($file:expr,) => ({ /* compiler built-in */ });
595+
}
584596

585597
/// Includes a file as a reference to a byte array.
586598
///
@@ -614,7 +626,10 @@ pub mod builtin {
614626
/// Compiling 'main.rs' and running the resulting binary will print "adiós".
615627
#[stable(feature = "rust1", since = "1.0.0")]
616628
#[macro_export]
617-
macro_rules! include_bytes { ($file:expr) => ({ /* compiler built-in */ }) }
629+
macro_rules! include_bytes {
630+
($file:expr) => ({ /* compiler built-in */ });
631+
($file:expr,) => ({ /* compiler built-in */ });
632+
}
618633

619634
/// Expands to a string that represents the current module path.
620635
///
@@ -700,7 +715,10 @@ pub mod builtin {
700715
/// "🙈🙊🙉🙈🙊🙉".
701716
#[stable(feature = "rust1", since = "1.0.0")]
702717
#[macro_export]
703-
macro_rules! include { ($file:expr) => ({ /* compiler built-in */ }) }
718+
macro_rules! include {
719+
($file:expr) => ({ /* compiler built-in */ });
720+
($file:expr,) => ({ /* compiler built-in */ });
721+
}
704722
}
705723

706724
/// A macro for defining #[cfg] if-else statements.

src/libsyntax/ext/base.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -890,8 +890,8 @@ pub fn check_zero_tts(cx: &ExtCtxt,
890890
}
891891
}
892892

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

src/libsyntax_ext/cfg.rs

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ pub fn expand_cfg<'cx>(cx: &mut ExtCtxt,
2828
let mut p = cx.new_parser_from_tts(tts);
2929
let cfg = panictry!(p.parse_meta_item());
3030

31+
let _ = p.eat(&token::Comma);
32+
3133
if !p.eat(&token::Eof) {
3234
cx.span_err(sp, "expected 1 cfg-pattern");
3335
return DummyResult::expr(sp);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Companion test to the similarly-named file in run-pass.
12+
13+
// compile-flags: -C debug_assertions=yes
14+
// revisions: std core
15+
16+
#![cfg_attr(core, no_std)]
17+
18+
#[cfg(std)] use std::fmt;
19+
#[cfg(core)] use core::fmt;
20+
21+
// (see documentation of the similarly-named test in run-pass)
22+
fn to_format_or_not_to_format() {
23+
let falsum = || false;
24+
25+
// assert!(true, "{}",); // see run-pass
26+
27+
assert_eq!(1, 1, "{}",);
28+
//[core]~^ ERROR no arguments
29+
//[std]~^^ ERROR no arguments
30+
assert_ne!(1, 2, "{}",);
31+
//[core]~^ ERROR no arguments
32+
//[std]~^^ ERROR no arguments
33+
34+
// debug_assert!(true, "{}",); // see run-pass
35+
36+
debug_assert_eq!(1, 1, "{}",);
37+
//[core]~^ ERROR no arguments
38+
//[std]~^^ ERROR no arguments
39+
debug_assert_ne!(1, 2, "{}",);
40+
//[core]~^ ERROR no arguments
41+
//[std]~^^ ERROR no arguments
42+
43+
#[cfg(std)] {
44+
eprint!("{}",);
45+
//[std]~^ ERROR no arguments
46+
}
47+
48+
#[cfg(std)] {
49+
// FIXME: compile-fail says "expected error not found" even though
50+
// rustc does emit an error
51+
// eprintln!("{}",);
52+
// <DISABLED> [std]~^ ERROR no arguments
53+
}
54+
55+
#[cfg(std)] {
56+
format!("{}",);
57+
//[std]~^ ERROR no arguments
58+
}
59+
60+
format_args!("{}",);
61+
//[core]~^ ERROR no arguments
62+
//[std]~^^ ERROR no arguments
63+
64+
// if falsum() { panic!("{}",); } // see run-pass
65+
66+
#[cfg(std)] {
67+
print!("{}",);
68+
//[std]~^ ERROR no arguments
69+
}
70+
71+
#[cfg(std)] {
72+
// FIXME: compile-fail says "expected error not found" even though
73+
// rustc does emit an error
74+
// println!("{}",);
75+
// <DISABLED> [std]~^ ERROR no arguments
76+
}
77+
78+
unimplemented!("{}",);
79+
//[core]~^ ERROR no arguments
80+
//[std]~^^ ERROR no arguments
81+
82+
// if falsum() { unreachable!("{}",); } // see run-pass
83+
84+
struct S;
85+
impl fmt::Display for S {
86+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
87+
write!(f, "{}",)?;
88+
//[core]~^ ERROR no arguments
89+
//[std]~^^ ERROR no arguments
90+
91+
// FIXME: compile-fail says "expected error not found" even though
92+
// rustc does emit an error
93+
// writeln!(f, "{}",)?;
94+
// <DISABLED> [core]~^ ERROR no arguments
95+
// <DISABLED> [std]~^^ ERROR no arguments
96+
Ok(())
97+
}
98+
}
99+
}
100+
101+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// This is a companion to the similarly-named test in run-pass.
12+
//
13+
// It tests macros that unavoidably produce compile errors.
14+
15+
fn compile_error() {
16+
compile_error!("lel"); //~ ERROR lel
17+
compile_error!("lel",); //~ ERROR lel
18+
}
19+
20+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
()

0 commit comments

Comments
 (0)