]> git.lizzy.rs Git - rust.git/commitdiff
Expand `NtExpr` tokens only in key-value attributes
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Sun, 27 Sep 2020 13:52:51 +0000 (16:52 +0300)
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Mon, 2 Nov 2020 21:53:43 +0000 (00:53 +0300)
18 files changed:
compiler/rustc_ast/src/mut_visit.rs
compiler/rustc_ast/src/tokenstream.rs
compiler/rustc_ast/src/visit.rs
compiler/rustc_expand/src/mbe/transcribe.rs
compiler/rustc_expand/src/mut_visit/tests.rs
compiler/rustc_resolve/src/build_reduced_graph.rs
compiler/rustc_resolve/src/def_collector.rs
library/core/src/sync/atomic.rs
src/test/ui/attributes/auxiliary/key-value-expansion.rs [new file with mode: 0644]
src/test/ui/attributes/key-value-expansion.rs [new file with mode: 0644]
src/test/ui/attributes/key-value-expansion.stderr [new file with mode: 0644]
src/test/ui/attributes/nonterminal-expansion.rs [new file with mode: 0644]
src/test/ui/attributes/nonterminal-expansion.stderr [new file with mode: 0644]
src/test/ui/conditional-compilation/cfg-attr-syntax-validation.rs
src/test/ui/conditional-compilation/cfg-attr-syntax-validation.stderr
src/test/ui/proc-macro/auxiliary/test-macros.rs
src/test/ui/proc-macro/nonterminal-expansion.rs [new file with mode: 0644]
src/test/ui/proc-macro/nonterminal-expansion.stdout [new file with mode: 0644]

index 166d36ce4243305aa5f414c7843e9395e7ffc2d7..fe9ad58c9ac84d151b8a56cbfab60a6530af4e4a 100644 (file)
@@ -34,6 +34,13 @@ fn expect_one(self, err: &'static str) -> A::Item {
 }
 
 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 @@ fn visit_variant_data(&mut self, vdata: &mut VariantData) {
         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<T: MutVisitor>(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_visit_meta_item<T: MutVisitor>(mi: &mut MetaItem, vis: &mut T) {
     smallvec![param]
 }
 
-pub fn noop_visit_tt<T: MutVisitor>(tt: &mut TokenTree, vis: &mut T) {
+// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
+pub fn visit_tt<T: MutVisitor>(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<T: MutVisitor>(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<T: MutVisitor>(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: MutVisitor>(t: &mut Token, vis: &mut T) {
+pub fn visit_token<T: MutVisitor>(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: MutVisitor>(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: MutVisitor>(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<T: MutVisitor>(nt: &mut token::Nonterminal, vis: &mut T) {
+pub fn visit_interpolated<T: MutVisitor>(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<T: MutVisitor>(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),
     }
 }
index 98da20af8f691cb54c61b2de9f5d7492b7501386..1e7001c2b2353e1c1fa90b1854e8079f454c0737 100644 (file)
@@ -318,6 +318,10 @@ pub fn span(&self) -> Option<Span> {
         }
     }
 
+    pub fn trees_ref(&self) -> CursorRef<'_> {
+        CursorRef::new(self)
+    }
+
     pub fn trees(&self) -> Cursor {
         self.clone().into_trees()
     }
@@ -408,6 +412,36 @@ pub fn build(self) -> TokenStream {
     }
 }
 
+/// 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,
index 507b49616ea0ff5461f237bb646c178f2b440dd2..2ab6667ac3cf1210e8725aa8ec6e3d783b555c37 100644 (file)
@@ -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 @@ fn visit_assoc_ty_constraint(&mut self, constraint: &'ast AssocTyConstraint) {
     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),
+        },
     }
 }
index 3d8a4bbff18339b94d9c82f08f903d3efed94085..629e0e702b6541366a2d088921d35ac89c3913ef 100644 (file)
 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());
             }
 
index 38ff594b6e9c12482e9443bbd11b5877ede20ac1..9e65fc2eca7397efd6f49d8853007df63777c90a 100644 (file)
@@ -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");
     }
index d2d2a79374e22be272aecc66c694d7b990271bb1..83016ed36a7293ead6e8f0f163998371db29fcbc 100644 (file)
@@ -15,7 +15,6 @@
 };
 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 @@ fn visit_assoc_item(&mut self, item: &'b AssocItem, ctxt: AssocCtxt) {
         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
index a4de4d500f5e9f0d4865c7e0887e17495d50ac5e..69773ba1d72215b1139483c81a436ad840412524 100644 (file)
@@ -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 @@ fn visit_stmt(&mut self, stmt: &'a Stmt) {
         }
     }
 
-    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) }
     }
index 0c53b6ed54a849e8fb2c85088e661c57990127cc..2edeb81011af9fb78597b4d92e4a24bab286745c 100644 (file)
@@ -2275,64 +2275,56 @@ pub fn as_mut_ptr(&self) -> *mut $int_type {
     "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
-    };
-}
-#[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
+
+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")]
-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 (file)
index 0000000..b4eb80b
--- /dev/null
@@ -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 (file)
index 0000000..831c809
--- /dev/null
@@ -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 (file)
index 0000000..5da4557
--- /dev/null
@@ -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 (file)
index 0000000..b4f5239
--- /dev/null
@@ -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 (file)
index 0000000..9f7f185
--- /dev/null
@@ -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`.
index 7f0648b381dbd92372246288ba64fee9059c222e..408eaffccf7d9e403525a561c85af2c6fe330483 100644 (file)
@@ -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;
     }
 }
index 44063dd1d65d7efa4b7807ec55253c6bca96dc2a..8ae2ff16a2b4fcd829221de41257599b69021228 100644 (file)
@@ -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`.
index 8682ebdd109f02f8d7c2b405abcb7a258225d320..57a7ffa39ef006ce96316355ece52b3882c01341 100644 (file)
@@ -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 (file)
index 0000000..e621558
--- /dev/null
@@ -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<T, C> {
+            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 (file)
index 0000000..4522dc1
--- /dev/null
@@ -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),
+    },
+]