}
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
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);
}
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),
+ }
+ }
+ }
+ }
}
}
}
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) => {
}
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
// 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
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),
}
}
}
}
+ pub fn trees_ref(&self) -> CursorRef<'_> {
+ CursorRef::new(self)
+ }
+
pub fn trees(&self) -> Cursor {
self.clone().into_trees()
}
}
}
+/// 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,
//! 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;
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)
}
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),
+ },
}
}
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)
}
// 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());
}
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");
}
};
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};
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
use crate::Resolver;
-use rustc_ast::token::{self, Token};
use rustc_ast::visit::{self, FnKind};
use rustc_ast::walk_list;
use rustc_ast::*;
}
}
- 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) }
}
"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]
--- /dev/null
+// 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()
+}
--- /dev/null
+// 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() {}
--- /dev/null
+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
+
--- /dev/null
+// 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() {}
--- /dev/null
+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`.
($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;
}
}
|
= 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`.
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");
--- /dev/null
+// 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() {}
--- /dev/null
+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),
+ },
+]