Add backwards-compat hack for certain '$name' tokens

See issue #74616
This commit is contained in:
Aaron Hill 2020-07-25 05:30:32 -04:00
parent cd24aee8e6
commit 0fcad9cd29
No known key found for this signature in database
GPG key ID: B4087E510E98B164
8 changed files with 103 additions and 8 deletions

View file

@ -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 {

View file

@ -173,13 +173,19 @@ impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec<Self>)>
}
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!(),

View file

@ -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,

View file

@ -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
}

View file

@ -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);
}

View file

@ -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) }]

View file

@ -0,0 +1,7 @@
// ignore-test this is not a test
macro_rules! arrays {
($name:ident) => {
#[my_macro] struct Two($name);
}
}

View file

@ -0,0 +1,7 @@
// ignore-test this is not a test
macro_rules! impl_macros {
($name:ident) => {
#[my_macro] struct One($name);
}
}