diff --git a/src/librustc_ast/token.rs b/src/librustc_ast/token.rs index 46c4be0a33bf7..4a8bf6b4f19b6 100644 --- a/src/librustc_ast/token.rs +++ b/src/librustc_ast/token.rs @@ -11,9 +11,11 @@ use crate::tokenstream::TokenTree; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; use rustc_macros::HashStable_Generic; +use rustc_span::hygiene::ExpnKind; +use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, sym}; use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::{self, Span, DUMMY_SP}; +use rustc_span::{self, FileName, RealFileName, Span, DUMMY_SP}; use std::borrow::Cow; use std::{fmt, mem}; @@ -808,6 +810,31 @@ impl Nonterminal { } false } + + // See issue #74616 for details + pub fn ident_name_compatibility_hack( + &self, + orig_span: Span, + source_map: &SourceMap, + ) -> Option<(Ident, bool)> { + if let NtIdent(ident, is_raw) = self { + if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind { + let filename = source_map.span_to_filename(orig_span); + if let FileName::Real(RealFileName::Named(path)) = filename { + if (path.ends_with("time-macros-impl/src/lib.rs") + && macro_name == sym::impl_macros) + || (path.ends_with("js-sys/src/lib.rs") && macro_name == sym::arrays) + { + let snippet = source_map.span_to_snippet(orig_span); + if snippet.as_deref() == Ok("$name") { + return Some((*ident, *is_raw)); + } + } + } + } + } + None + } } impl PartialEq for Nonterminal { diff --git a/src/librustc_expand/proc_macro_server.rs b/src/librustc_expand/proc_macro_server.rs index 33e35cd0404fb..409784812f58f 100644 --- a/src/librustc_expand/proc_macro_server.rs +++ b/src/librustc_expand/proc_macro_server.rs @@ -173,13 +173,19 @@ impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec)> } Interpolated(nt) => { - let stream = nt_to_tokenstream(&nt, sess, span); - TokenTree::Group(Group { - delimiter: Delimiter::None, - stream, - span: DelimSpan::from_single(span), - flatten: nt.pretty_printing_compatibility_hack(), - }) + if let Some((name, is_raw)) = + nt.ident_name_compatibility_hack(span, sess.source_map()) + { + TokenTree::Ident(Ident::new(sess, name.name, is_raw, name.span)) + } else { + let stream = nt_to_tokenstream(&nt, sess, span); + TokenTree::Group(Group { + delimiter: Delimiter::None, + stream, + span: DelimSpan::from_single(span), + flatten: nt.pretty_printing_compatibility_hack(), + }) + } } OpenDelim(..) | CloseDelim(..) => unreachable!(), diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs index 3883d86520fee..e8067ddc7786b 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -258,6 +258,7 @@ symbols! { arith_offset, arm_target_feature, array, + arrays, as_str, asm, assert, @@ -571,6 +572,7 @@ symbols! { ignore, impl_header_lifetime_elision, impl_lint_pass, + impl_macros, impl_trait_in_bindings, import_shadowing, in_band_lifetimes, diff --git a/src/test/ui/proc-macro/group-compat-hack/auxiliary/group-compat-hack.rs b/src/test/ui/proc-macro/group-compat-hack/auxiliary/group-compat-hack.rs new file mode 100644 index 0000000000000..5cd3b40a2e42a --- /dev/null +++ b/src/test/ui/proc-macro/group-compat-hack/auxiliary/group-compat-hack.rs @@ -0,0 +1,13 @@ +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro_attribute] +pub fn my_macro(_attr: TokenStream, input: TokenStream) -> TokenStream { + println!("Called proc_macro_hack with {:?}", input); + input +} diff --git a/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.rs b/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.rs new file mode 100644 index 0000000000000..35c101587de05 --- /dev/null +++ b/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.rs @@ -0,0 +1,30 @@ +// check-pass +// aux-build:group-compat-hack.rs +// compile-flags: -Z span-debug + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + +#[macro_use] extern crate group_compat_hack; + +// Tests the backwards compatibility hack added for certain macros +// When an attribute macro named `proc_macro_hack` or `wasm_bindgen` +// has an `NtIdent` named `$name`, we pass a plain `Ident` token in +// place of a `None`-delimited group. This allows us to maintain +// backwards compatibility for older versions of these crates. + +include!("js-sys/src/lib.rs"); +include!("time-macros-impl/src/lib.rs"); + +macro_rules! other { + ($name:ident) => { + #[my_macro] struct Three($name); + } +} + +fn main() { + struct Foo; + impl_macros!(Foo); + arrays!(Foo); + other!(Foo); +} diff --git a/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stdout b/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stdout new file mode 100644 index 0000000000000..d519daab1f287 --- /dev/null +++ b/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stdout @@ -0,0 +1,3 @@ +Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/time-macros-impl/src/lib.rs:5:21: 5:27 (#5) }, Ident { ident: "One", span: $DIR/time-macros-impl/src/lib.rs:5:28: 5:31 (#5) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:27:18: 27:21 (#0) }], span: $DIR/time-macros-impl/src/lib.rs:5:31: 5:38 (#5) }, Punct { ch: ';', spacing: Alone, span: $DIR/time-macros-impl/src/lib.rs:5:38: 5:39 (#5) }] +Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/js-sys/src/lib.rs:5:21: 5:27 (#9) }, Ident { ident: "Two", span: $DIR/js-sys/src/lib.rs:5:28: 5:31 (#9) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:28:13: 28:16 (#0) }], span: $DIR/js-sys/src/lib.rs:5:31: 5:38 (#9) }, Punct { ch: ';', spacing: Alone, span: $DIR/js-sys/src/lib.rs:5:38: 5:39 (#9) }] +Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/group-compat-hack.rs:21:21: 21:27 (#13) }, Ident { ident: "Three", span: $DIR/group-compat-hack.rs:21:28: 21:33 (#13) }, Group { delimiter: Parenthesis, stream: TokenStream [Group { delimiter: None, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:29:12: 29:15 (#0) }], span: $DIR/group-compat-hack.rs:21:34: 21:39 (#13) }], span: $DIR/group-compat-hack.rs:21:33: 21:40 (#13) }, Punct { ch: ';', spacing: Alone, span: $DIR/group-compat-hack.rs:21:40: 21:41 (#13) }] diff --git a/src/test/ui/proc-macro/group-compat-hack/js-sys/src/lib.rs b/src/test/ui/proc-macro/group-compat-hack/js-sys/src/lib.rs new file mode 100644 index 0000000000000..d1a66940ebf3c --- /dev/null +++ b/src/test/ui/proc-macro/group-compat-hack/js-sys/src/lib.rs @@ -0,0 +1,7 @@ +// ignore-test this is not a test + +macro_rules! arrays { + ($name:ident) => { + #[my_macro] struct Two($name); + } +} diff --git a/src/test/ui/proc-macro/group-compat-hack/time-macros-impl/src/lib.rs b/src/test/ui/proc-macro/group-compat-hack/time-macros-impl/src/lib.rs new file mode 100644 index 0000000000000..c94c357920974 --- /dev/null +++ b/src/test/ui/proc-macro/group-compat-hack/time-macros-impl/src/lib.rs @@ -0,0 +1,7 @@ +// ignore-test this is not a test + +macro_rules! impl_macros { + ($name:ident) => { + #[my_macro] struct One($name); + } +}