diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 166d36ce424..fe9ad58c9ac 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -34,6 +34,13 @@ impl ExpectOne for SmallVec { } pub trait MutVisitor: Sized { + /// Mutable token visiting only exists for the `macro_rules` token marker and should not be + /// used otherwise. Token visitor would be entirely separate from the regular visitor if + /// the marker didn't have to visit AST fragments in nonterminal tokens. + fn token_visiting_enabled(&self) -> bool { + false + } + // Methods in this trait have one of three forms: // // fn visit_t(&mut self, t: &mut T); // common @@ -246,22 +253,6 @@ pub trait MutVisitor: Sized { noop_flat_map_generic_param(param, self) } - fn visit_tt(&mut self, tt: &mut TokenTree) { - noop_visit_tt(tt, self); - } - - fn visit_tts(&mut self, tts: &mut TokenStream) { - noop_visit_tts(tts, self); - } - - fn visit_token(&mut self, t: &mut Token) { - noop_visit_token(t, self); - } - - fn visit_interpolated(&mut self, nt: &mut token::Nonterminal) { - noop_visit_interpolated(nt, self); - } - fn visit_param_bound(&mut self, tpb: &mut GenericBound) { noop_visit_param_bound(tpb, self); } @@ -375,11 +366,30 @@ pub fn visit_mac_args(args: &mut MacArgs, vis: &mut T) { MacArgs::Empty => {} MacArgs::Delimited(dspan, _delim, tokens) => { visit_delim_span(dspan, vis); - vis.visit_tts(tokens); + visit_tts(tokens, vis); } MacArgs::Eq(eq_span, tokens) => { vis.visit_span(eq_span); - vis.visit_tts(tokens); + visit_tts(tokens, vis); + // The value in `#[key = VALUE]` must be visited as an expression for backward + // compatibility, so that macros can be expanded in that position. + if !vis.token_visiting_enabled() { + if let Some(TokenTree::Token(token)) = tokens.trees_ref().next() { + if let token::Interpolated(..) = token.kind { + // ^^ Do not `make_mut` unless we have to. + match Lrc::make_mut(&mut tokens.0).get_mut(0) { + Some((TokenTree::Token(token), _spacing)) => match &mut token.kind { + token::Interpolated(nt) => match Lrc::make_mut(nt) { + token::NtExpr(expr) => vis.visit_expr(expr), + t => panic!("unexpected token in key-value attribute: {:?}", t), + }, + t => panic!("unexpected token in key-value attribute: {:?}", t), + }, + t => panic!("unexpected token in key-value attribute: {:?}", t), + } + } + } + } } } } @@ -626,28 +636,33 @@ pub fn noop_flat_map_param(mut param: Param, vis: &mut T) -> Smal smallvec![param] } -pub fn noop_visit_tt(tt: &mut TokenTree, vis: &mut T) { +// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. +pub fn visit_tt(tt: &mut TokenTree, vis: &mut T) { match tt { TokenTree::Token(token) => { - vis.visit_token(token); + visit_token(token, vis); } TokenTree::Delimited(DelimSpan { open, close }, _delim, tts) => { vis.visit_span(open); vis.visit_span(close); - vis.visit_tts(tts); + visit_tts(tts, vis); } } } -pub fn noop_visit_tts(TokenStream(tts): &mut TokenStream, vis: &mut T) { - let tts = Lrc::make_mut(tts); - visit_vec(tts, |(tree, _is_joint)| vis.visit_tt(tree)); +// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. +pub fn visit_tts(TokenStream(tts): &mut TokenStream, vis: &mut T) { + if vis.token_visiting_enabled() { + let tts = Lrc::make_mut(tts); + visit_vec(tts, |(tree, _is_joint)| visit_tt(tree, vis)); + } } +// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. // Applies ident visitor if it's an ident; applies other visits to interpolated nodes. // In practice the ident part is not actually used by specific visitors right now, // but there's a test below checking that it works. -pub fn noop_visit_token(t: &mut Token, vis: &mut T) { +pub fn visit_token(t: &mut Token, vis: &mut T) { let Token { kind, span } = t; match kind { token::Ident(name, _) | token::Lifetime(name) => { @@ -659,13 +674,14 @@ pub fn noop_visit_token(t: &mut Token, vis: &mut T) { } token::Interpolated(nt) => { let mut nt = Lrc::make_mut(nt); - vis.visit_interpolated(&mut nt); + visit_interpolated(&mut nt, vis); } _ => {} } vis.visit_span(span); } +// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. /// Applies the visitor to elements of interpolated nodes. // // N.B., this can occur only when applying a visitor to partially expanded @@ -689,7 +705,7 @@ pub fn noop_visit_token(t: &mut Token, vis: &mut T) { // contain multiple items, but decided against it when I looked at // `parse_item_or_view_item` and tried to figure out what I would do with // multiple items there.... -pub fn noop_visit_interpolated(nt: &mut token::Nonterminal, vis: &mut T) { +pub fn visit_interpolated(nt: &mut token::Nonterminal, vis: &mut T) { match nt { token::NtItem(item) => visit_clobber(item, |item| { // This is probably okay, because the only visitors likely to @@ -714,7 +730,7 @@ pub fn noop_visit_interpolated(nt: &mut token::Nonterminal, vis: visit_mac_args(args, vis); } token::NtPath(path) => vis.visit_path(path), - token::NtTT(tt) => vis.visit_tt(tt), + token::NtTT(tt) => visit_tt(tt, vis), token::NtVis(visib) => vis.visit_vis(visib), } } diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 98da20af8f6..1e7001c2b23 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -318,6 +318,10 @@ impl TokenStream { } } + pub fn trees_ref(&self) -> CursorRef<'_> { + CursorRef::new(self) + } + pub fn trees(&self) -> Cursor { self.clone().into_trees() } @@ -408,6 +412,36 @@ impl TokenStreamBuilder { } } +/// By-reference iterator over a `TokenStream`. +#[derive(Clone)] +pub struct CursorRef<'t> { + stream: &'t TokenStream, + index: usize, +} + +impl<'t> CursorRef<'t> { + fn new(stream: &TokenStream) -> CursorRef<'_> { + CursorRef { stream, index: 0 } + } + + fn next_with_spacing(&mut self) -> Option<&'t TreeAndSpacing> { + self.stream.0.get(self.index).map(|tree| { + self.index += 1; + tree + }) + } +} + +impl<'t> Iterator for CursorRef<'t> { + type Item = &'t TokenTree; + + fn next(&mut self) -> Option<&'t TokenTree> { + self.next_with_spacing().map(|(tree, _)| tree) + } +} + +/// Owning by-value iterator over a `TokenStream`. +/// FIXME: Many uses of this can be replaced with by-reference iterator to avoid clones. #[derive(Clone)] pub struct Cursor { pub stream: TokenStream, diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 507b49616ea..2ab6667ac3c 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -14,8 +14,8 @@ //! those that are created by the expansion of a macro. use crate::ast::*; -use crate::token::Token; -use crate::tokenstream::{TokenStream, TokenTree}; +use crate::token; +use crate::tokenstream::TokenTree; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::Span; @@ -208,14 +208,6 @@ pub trait Visitor<'ast>: Sized { fn visit_attribute(&mut self, attr: &'ast Attribute) { walk_attribute(self, attr) } - fn visit_tt(&mut self, tt: TokenTree) { - walk_tt(self, tt) - } - fn visit_tts(&mut self, tts: TokenStream) { - walk_tts(self, tts) - } - fn visit_token(&mut self, _t: Token) {} - // FIXME: add `visit_interpolated` and `walk_interpolated` fn visit_vis(&mut self, vis: &'ast Visibility) { walk_vis(self, vis) } @@ -902,20 +894,19 @@ pub fn walk_attribute<'a, V: Visitor<'a>>(visitor: &mut V, attr: &'a Attribute) pub fn walk_mac_args<'a, V: Visitor<'a>>(visitor: &mut V, args: &'a MacArgs) { match args { MacArgs::Empty => {} - MacArgs::Delimited(_dspan, _delim, tokens) => visitor.visit_tts(tokens.clone()), - MacArgs::Eq(_eq_span, tokens) => visitor.visit_tts(tokens.clone()), - } -} - -pub fn walk_tt<'a, V: Visitor<'a>>(visitor: &mut V, tt: TokenTree) { - match tt { - TokenTree::Token(token) => visitor.visit_token(token), - TokenTree::Delimited(_, _, tts) => visitor.visit_tts(tts), - } -} - -pub fn walk_tts<'a, V: Visitor<'a>>(visitor: &mut V, tts: TokenStream) { - for tt in tts.trees() { - visitor.visit_tt(tt); + MacArgs::Delimited(_dspan, _delim, _tokens) => {} + // The value in `#[key = VALUE]` must be visited as an expression for backward + // compatibility, so that macros can be expanded in that position. + MacArgs::Eq(_eq_span, tokens) => match tokens.trees_ref().next() { + Some(TokenTree::Token(token)) => match &token.kind { + token::Interpolated(nt) => match &**nt { + token::NtExpr(expr) => visitor.visit_expr(expr), + t => panic!("unexpected token in key-value attribute: {:?}", t), + }, + token::Literal(..) | token::Ident(..) => {} + t => panic!("unexpected token in key-value attribute: {:?}", t), + }, + t => panic!("unexpected token in key-value attribute: {:?}", t), + }, } } diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 3d8a4bbff18..629e0e702b6 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -20,6 +20,10 @@ use std::mem; struct Marker(ExpnId, Transparency); impl MutVisitor for Marker { + fn token_visiting_enabled(&self) -> bool { + true + } + fn visit_span(&mut self, span: &mut Span) { *span = span.apply_mark(self.0, self.1) } @@ -277,7 +281,7 @@ pub(super) fn transcribe<'a>( // preserve syntax context. mbe::TokenTree::Token(token) => { let mut tt = TokenTree::Token(token); - marker.visit_tt(&mut tt); + mut_visit::visit_tt(&mut tt, &mut marker); result.push(tt.into()); } diff --git a/compiler/rustc_expand/src/mut_visit/tests.rs b/compiler/rustc_expand/src/mut_visit/tests.rs index 38ff594b6e9..9e65fc2eca7 100644 --- a/compiler/rustc_expand/src/mut_visit/tests.rs +++ b/compiler/rustc_expand/src/mut_visit/tests.rs @@ -15,6 +15,9 @@ fn fake_print_crate(s: &mut pprust::State<'_>, krate: &ast::Crate) { struct ToZzIdentMutVisitor; impl MutVisitor for ToZzIdentMutVisitor { + fn token_visiting_enabled(&self) -> bool { + true + } fn visit_ident(&mut self, ident: &mut Ident) { *ident = Ident::from_str("zz"); } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index d2d2a79374e..83016ed36a7 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -15,7 +15,6 @@ use crate::{ }; use crate::{Module, ModuleData, ModuleKind, NameBinding, NameBindingKind, Segment, ToNameBinding}; -use rustc_ast::token::{self, Token}; use rustc_ast::visit::{self, AssocCtxt, Visitor}; use rustc_ast::{self as ast, Block, ForeignItem, ForeignItemKind, Item, ItemKind, NodeId}; use rustc_ast::{AssocItem, AssocItemKind, MetaItemKind, StmtKind}; @@ -1395,16 +1394,6 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> { visit::walk_assoc_item(self, item, ctxt); } - fn visit_token(&mut self, t: Token) { - if let token::Interpolated(nt) = t.kind { - if let token::NtExpr(ref expr) = *nt { - if let ast::ExprKind::MacCall(..) = expr.kind { - self.visit_invoc(expr.id); - } - } - } - } - fn visit_attribute(&mut self, attr: &'b ast::Attribute) { if !attr.is_doc_comment() && attr::is_builtin_attr(attr) { self.r diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index a4de4d500f5..69773ba1d72 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -1,5 +1,4 @@ use crate::Resolver; -use rustc_ast::token::{self, Token}; use rustc_ast::visit::{self, FnKind}; use rustc_ast::walk_list; use rustc_ast::*; @@ -256,16 +255,6 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { } } - fn visit_token(&mut self, t: Token) { - if let token::Interpolated(nt) = t.kind { - if let token::NtExpr(ref expr) = *nt { - if let ExprKind::MacCall(..) = expr.kind { - self.visit_macro_invoc(expr.id); - } - } - } - } - fn visit_arm(&mut self, arm: &'a Arm) { if arm.is_placeholder { self.visit_macro_invoc(arm.id) } else { visit::walk_arm(self, arm) } } diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 0c53b6ed54a..2edeb81011a 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -2275,64 +2275,56 @@ atomic_int! { "AtomicU128::new(0)", u128 AtomicU128 ATOMIC_U128_INIT } -#[cfg(target_has_atomic_load_store = "ptr")] -#[cfg(target_pointer_width = "16")] -macro_rules! ptr_width { - () => { - 2 - }; + +macro_rules! atomic_int_ptr_sized { + ( $($target_pointer_width:literal $align:literal)* ) => { $( + #[cfg(target_has_atomic_load_store = "ptr")] + #[cfg(target_pointer_width = $target_pointer_width)] + atomic_int! { + cfg(target_has_atomic = "ptr"), + cfg(target_has_atomic_equal_alignment = "ptr"), + stable(feature = "rust1", since = "1.0.0"), + stable(feature = "extended_compare_and_swap", since = "1.10.0"), + stable(feature = "atomic_debug", since = "1.3.0"), + stable(feature = "atomic_access", since = "1.15.0"), + stable(feature = "atomic_from", since = "1.23.0"), + stable(feature = "atomic_nand", since = "1.27.0"), + rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), + stable(feature = "rust1", since = "1.0.0"), + "isize", "../../../std/primitive.isize.html", + "", + atomic_min, atomic_max, + $align, + "AtomicIsize::new(0)", + isize AtomicIsize ATOMIC_ISIZE_INIT + } + #[cfg(target_has_atomic_load_store = "ptr")] + #[cfg(target_pointer_width = $target_pointer_width)] + atomic_int! { + cfg(target_has_atomic = "ptr"), + cfg(target_has_atomic_equal_alignment = "ptr"), + stable(feature = "rust1", since = "1.0.0"), + stable(feature = "extended_compare_and_swap", since = "1.10.0"), + stable(feature = "atomic_debug", since = "1.3.0"), + stable(feature = "atomic_access", since = "1.15.0"), + stable(feature = "atomic_from", since = "1.23.0"), + stable(feature = "atomic_nand", since = "1.27.0"), + rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), + stable(feature = "rust1", since = "1.0.0"), + "usize", "../../../std/primitive.usize.html", + "", + atomic_umin, atomic_umax, + $align, + "AtomicUsize::new(0)", + usize AtomicUsize ATOMIC_USIZE_INIT + } + )* }; } -#[cfg(target_has_atomic_load_store = "ptr")] -#[cfg(target_pointer_width = "32")] -macro_rules! ptr_width { - () => { - 4 - }; -} -#[cfg(target_has_atomic_load_store = "ptr")] -#[cfg(target_pointer_width = "64")] -macro_rules! ptr_width { - () => { - 8 - }; -} -#[cfg(target_has_atomic_load_store = "ptr")] -atomic_int! { - cfg(target_has_atomic = "ptr"), - cfg(target_has_atomic_equal_alignment = "ptr"), - stable(feature = "rust1", since = "1.0.0"), - stable(feature = "extended_compare_and_swap", since = "1.10.0"), - stable(feature = "atomic_debug", since = "1.3.0"), - stable(feature = "atomic_access", since = "1.15.0"), - stable(feature = "atomic_from", since = "1.23.0"), - stable(feature = "atomic_nand", since = "1.27.0"), - rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), - stable(feature = "rust1", since = "1.0.0"), - "isize", "../../../std/primitive.isize.html", - "", - atomic_min, atomic_max, - ptr_width!(), - "AtomicIsize::new(0)", - isize AtomicIsize ATOMIC_ISIZE_INIT -} -#[cfg(target_has_atomic_load_store = "ptr")] -atomic_int! { - cfg(target_has_atomic = "ptr"), - cfg(target_has_atomic_equal_alignment = "ptr"), - stable(feature = "rust1", since = "1.0.0"), - stable(feature = "extended_compare_and_swap", since = "1.10.0"), - stable(feature = "atomic_debug", since = "1.3.0"), - stable(feature = "atomic_access", since = "1.15.0"), - stable(feature = "atomic_from", since = "1.23.0"), - stable(feature = "atomic_nand", since = "1.27.0"), - rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), - stable(feature = "rust1", since = "1.0.0"), - "usize", "../../../std/primitive.usize.html", - "", - atomic_umin, atomic_umax, - ptr_width!(), - "AtomicUsize::new(0)", - usize AtomicUsize ATOMIC_USIZE_INIT + +atomic_int_ptr_sized! { + "16" 2 + "32" 4 + "64" 8 } #[inline] diff --git a/src/test/ui/attributes/auxiliary/key-value-expansion.rs b/src/test/ui/attributes/auxiliary/key-value-expansion.rs new file mode 100644 index 00000000000..b4eb80bb516 --- /dev/null +++ b/src/test/ui/attributes/auxiliary/key-value-expansion.rs @@ -0,0 +1,12 @@ +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::*; + +#[proc_macro_derive(EthabiContract, attributes(ethabi_contract_options))] +pub fn ethabi_derive(input: TokenStream) -> TokenStream { + Default::default() +} diff --git a/src/test/ui/attributes/key-value-expansion.rs b/src/test/ui/attributes/key-value-expansion.rs new file mode 100644 index 00000000000..831c8091f32 --- /dev/null +++ b/src/test/ui/attributes/key-value-expansion.rs @@ -0,0 +1,55 @@ +// Regression tests for issue #55414, expansion happens in the value of a key-value attribute, +// and the expanded expression is more complex than simply a macro call. + +// aux-build:key-value-expansion.rs + +#![feature(rustc_attrs)] + +extern crate key_value_expansion; + +// Minimized test case. + +macro_rules! bug { + ($expr:expr) => { + #[rustc_dummy = $expr] // Any key-value attribute, not necessarily `doc` + //~^ ERROR unexpected token: `(7u32)` + struct S; + }; +} + +// Any expressions containing macro call `X` that's more complex than `X` itself. +// Parentheses will work. +bug!((column!())); + +// Original test case. + +macro_rules! bug { + () => { + bug!("bug" + stringify!(found)); + }; + ($test:expr) => { + #[doc = $test] //~ ERROR unexpected token: `"bug" + "found"` + struct Test {} + }; +} + +bug!(); + +// Test case from #66804. + +macro_rules! doc_comment { + ($x:expr) => { + #[doc = $x] //~ ERROR unexpected token: `{ + extern {} + }; +} + +macro_rules! some_macro { + ($t1: ty) => { + doc_comment! {format!("{coor}", coor = stringify!($t1)).as_str()} + }; +} + +some_macro!(u8); + +fn main() {} diff --git a/src/test/ui/attributes/key-value-expansion.stderr b/src/test/ui/attributes/key-value-expansion.stderr new file mode 100644 index 00000000000..5da4557f0ae --- /dev/null +++ b/src/test/ui/attributes/key-value-expansion.stderr @@ -0,0 +1,44 @@ +error: unexpected token: `(7u32)` + --> $DIR/key-value-expansion.rs:14:25 + | +LL | #[rustc_dummy = $expr] // Any key-value attribute, not necessarily `doc` + | ^^^^^ +... +LL | bug!((column!())); + | ------------------ in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unexpected token: `"bug" + "found"` + --> $DIR/key-value-expansion.rs:31:17 + | +LL | #[doc = $test] + | ^^^^^ +... +LL | bug!(); + | ------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unexpected token: `{ + let res = + ::alloc::fmt::format(::core::fmt::Arguments::new_v1(&[""], + &match (&"u8",) { + (arg0,) => + [::core::fmt::ArgumentV1::new(arg0, + ::core::fmt::Display::fmt)], + })); + res +}.as_str()` + --> $DIR/key-value-expansion.rs:42:17 + | +LL | #[doc = $x] + | ^^ +... +LL | some_macro!(u8); + | ---------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/attributes/nonterminal-expansion.rs b/src/test/ui/attributes/nonterminal-expansion.rs new file mode 100644 index 00000000000..b4f523936a0 --- /dev/null +++ b/src/test/ui/attributes/nonterminal-expansion.rs @@ -0,0 +1,17 @@ +// Macros were previously expanded in `Expr` nonterminal tokens, now they are not. + +macro_rules! pass_nonterminal { + ($n:expr) => { + #[repr(align($n))] //~ ERROR expected unsuffixed literal or identifier, found `n!()` + //~| ERROR unrecognized representation hint + struct S; + }; +} + +macro_rules! n { + () => { 32 }; +} + +pass_nonterminal!(n!()); + +fn main() {} diff --git a/src/test/ui/attributes/nonterminal-expansion.stderr b/src/test/ui/attributes/nonterminal-expansion.stderr new file mode 100644 index 00000000000..9f7f185f947 --- /dev/null +++ b/src/test/ui/attributes/nonterminal-expansion.stderr @@ -0,0 +1,25 @@ +error: expected unsuffixed literal or identifier, found `n!()` + --> $DIR/nonterminal-expansion.rs:5:22 + | +LL | #[repr(align($n))] + | ^^ +... +LL | pass_nonterminal!(n!()); + | ------------------------ in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0552]: unrecognized representation hint + --> $DIR/nonterminal-expansion.rs:5:16 + | +LL | #[repr(align($n))] + | ^^^^^^^^^ +... +LL | pass_nonterminal!(n!()); + | ------------------------ in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0552`. diff --git a/src/test/ui/conditional-compilation/cfg-attr-syntax-validation.rs b/src/test/ui/conditional-compilation/cfg-attr-syntax-validation.rs index 7f0648b381d..408eaffccf7 100644 --- a/src/test/ui/conditional-compilation/cfg-attr-syntax-validation.rs +++ b/src/test/ui/conditional-compilation/cfg-attr-syntax-validation.rs @@ -29,6 +29,7 @@ macro_rules! generate_s10 { ($expr: expr) => { #[cfg(feature = $expr)] //~^ ERROR expected unsuffixed literal or identifier, found `concat!("nonexistent")` + //~| ERROR expected unsuffixed literal or identifier, found `concat!("nonexistent")` struct S10; } } diff --git a/src/test/ui/conditional-compilation/cfg-attr-syntax-validation.stderr b/src/test/ui/conditional-compilation/cfg-attr-syntax-validation.stderr index 44063dd1d65..8ae2ff16a2b 100644 --- a/src/test/ui/conditional-compilation/cfg-attr-syntax-validation.stderr +++ b/src/test/ui/conditional-compilation/cfg-attr-syntax-validation.stderr @@ -63,7 +63,18 @@ LL | generate_s10!(concat!("nonexistent")); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 10 previous errors +error: expected unsuffixed literal or identifier, found `concat!("nonexistent")` + --> $DIR/cfg-attr-syntax-validation.rs:30:25 + | +LL | #[cfg(feature = $expr)] + | ^^^^^ +... +LL | generate_s10!(concat!("nonexistent")); + | -------------------------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 11 previous errors Some errors have detailed explanations: E0537, E0565. For more information about an error, try `rustc --explain E0537`. diff --git a/src/test/ui/proc-macro/auxiliary/test-macros.rs b/src/test/ui/proc-macro/auxiliary/test-macros.rs index 8682ebdd109..57a7ffa39ef 100644 --- a/src/test/ui/proc-macro/auxiliary/test-macros.rs +++ b/src/test/ui/proc-macro/auxiliary/test-macros.rs @@ -112,6 +112,12 @@ pub fn print_attr(_: TokenStream, input: TokenStream) -> TokenStream { print_helper(input, "ATTR") } +#[proc_macro_attribute] +pub fn print_attr_args(args: TokenStream, input: TokenStream) -> TokenStream { + print_helper(args, "ATTR_ARGS"); + input +} + #[proc_macro_derive(Print, attributes(print_helper))] pub fn print_derive(input: TokenStream) -> TokenStream { print_helper(input, "DERIVE"); diff --git a/src/test/ui/proc-macro/nonterminal-expansion.rs b/src/test/ui/proc-macro/nonterminal-expansion.rs new file mode 100644 index 00000000000..e6215587153 --- /dev/null +++ b/src/test/ui/proc-macro/nonterminal-expansion.rs @@ -0,0 +1,37 @@ +// check-pass +// compile-flags: -Z span-debug +// aux-build:test-macros.rs + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + +#[macro_use] +extern crate test_macros; + +macro_rules! pass_nonterminal { + ($line:expr) => { + #[print_attr_args(a, $line, b)] + struct S; + }; +} + +// `line!()` is not expanded before it's passed to the proc macro. +pass_nonterminal!(line!()); + +// Test case from #43860. + +#[macro_export] +macro_rules! use_contract { + ($name: ident, $path: expr) => { + #[derive(Empty)] + #[empty_helper(path = $path)] // OK + pub struct $name { + api: T, + contract: C, + } + }; +} + +use_contract!(ContractName, file!()); + +fn main() {} diff --git a/src/test/ui/proc-macro/nonterminal-expansion.stdout b/src/test/ui/proc-macro/nonterminal-expansion.stdout new file mode 100644 index 00000000000..4522dc14b9f --- /dev/null +++ b/src/test/ui/proc-macro/nonterminal-expansion.stdout @@ -0,0 +1,42 @@ +PRINT-ATTR_ARGS INPUT (DISPLAY): a, line!(), b +PRINT-ATTR_ARGS RE-COLLECTED (DISPLAY): a, line ! (), b +PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ + Ident { + ident: "a", + span: $DIR/nonterminal-expansion.rs:13:27: 13:28 (#4), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/nonterminal-expansion.rs:13:28: 13:29 (#4), + }, + Group { + delimiter: None, + stream: TokenStream [ + Ident { + ident: "line", + span: $DIR/nonterminal-expansion.rs:19:19: 19:23 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/nonterminal-expansion.rs:19:23: 19:24 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/nonterminal-expansion.rs:19:24: 19:26 (#0), + }, + ], + span: $DIR/nonterminal-expansion.rs:13:30: 13:35 (#4), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/nonterminal-expansion.rs:13:35: 13:36 (#4), + }, + Ident { + ident: "b", + span: $DIR/nonterminal-expansion.rs:13:37: 13:38 (#4), + }, +]