Auto merge of #56647 - petrochenkov:dcrate2, r=alexcrichton
Rework treatment of `$crate` in procedural macros Important clarification: `$crate` below means "processed `$crate`" or "output `$crate`". In the input of a decl macro `$crate` is just two separate tokens, but in the *output of a decl macro* `$crate` is a single keyword identifier (https://github.com/rust-lang/rust/issues/55640#issuecomment-435692791). First of all, this PR removes the `eliminate_crate_var` hack. `$crate::foo` is no longer replaced with `::foo` or `::crate_name::foo` in the input of derive proc macros, it's passed to the macro instead with its precise span and hygiene data, and can be treated as any other path segment keyword (like `crate` or `self`) after that. (Note: `eliminate_crate_var` was never used for non-derive proc macros.) This creates an annoying problem - derive macros still may stringify their input before processing and expect `$crate` survive that stringification and refer to the same crate (the Rust 1.15-1.29 way of doing things). Moreover, the input of proc macro attributes and derives (but not fn-like proc macros) also effectively survives stringification before being passed to the macro (also for legacy implementation reasons). So we kind of resurrect the `eliminate_crate_var` hack in reduced form, but apply it only to AST pretty-printing. If an AST fragment is pretty-printed, the resulting *text* will have `$crate` replaced with `crate` or `::crate_name`. This should be enough to keep all the legacy cases working. Closes https://github.com/rust-lang/rust/issues/55640 Closes https://github.com/rust-lang/rust/issues/56622 r? @ghost
This commit is contained in:
commit
9622f9dc47
@ -729,11 +729,6 @@ impl Punct {
|
||||
/// which can be further configured with the `set_span` method below.
|
||||
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
|
||||
pub fn new(ch: char, spacing: Spacing) -> Punct {
|
||||
const LEGAL_CHARS: &[char] = &['=', '<', '>', '!', '~', '+', '-', '*', '/', '%', '^',
|
||||
'&', '|', '@', '.', ',', ';', ':', '#', '$', '?', '\''];
|
||||
if !LEGAL_CHARS.contains(&ch) {
|
||||
panic!("unsupported character `{:?}`", ch)
|
||||
}
|
||||
Punct(bridge::client::Punct::new(ch, spacing))
|
||||
}
|
||||
|
||||
@ -800,16 +795,6 @@ impl fmt::Debug for Punct {
|
||||
pub struct Ident(bridge::client::Ident);
|
||||
|
||||
impl Ident {
|
||||
fn is_valid(string: &str) -> bool {
|
||||
let mut chars = string.chars();
|
||||
if let Some(start) = chars.next() {
|
||||
(start == '_' || start.is_xid_start())
|
||||
&& chars.all(|cont| cont == '_' || cont.is_xid_continue())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `Ident` with the given `string` as well as the specified
|
||||
/// `span`.
|
||||
/// The `string` argument must be a valid identifier permitted by the
|
||||
@ -831,18 +816,12 @@ impl Ident {
|
||||
/// tokens, requires a `Span` to be specified at construction.
|
||||
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
|
||||
pub fn new(string: &str, span: Span) -> Ident {
|
||||
if !Ident::is_valid(string) {
|
||||
panic!("`{:?}` is not a valid identifier", string)
|
||||
}
|
||||
Ident(bridge::client::Ident::new(string, span.0, false))
|
||||
}
|
||||
|
||||
/// Same as `Ident::new`, but creates a raw identifier (`r#ident`).
|
||||
#[unstable(feature = "proc_macro_raw_ident", issue = "54723")]
|
||||
pub fn new_raw(string: &str, span: Span) -> Ident {
|
||||
if !Ident::is_valid(string) {
|
||||
panic!("`{:?}` is not a valid identifier", string)
|
||||
}
|
||||
Ident(bridge::client::Ident::new(string, span.0, true))
|
||||
}
|
||||
|
||||
|
@ -1622,8 +1622,7 @@ impl<'a> State<'a> {
|
||||
if i > 0 {
|
||||
self.s.word("::")?
|
||||
}
|
||||
if segment.ident.name != keywords::PathRoot.name() &&
|
||||
segment.ident.name != keywords::DollarCrate.name() {
|
||||
if segment.ident.name != keywords::PathRoot.name() {
|
||||
self.print_ident(segment.ident)?;
|
||||
segment.with_generic_args(|generic_args| {
|
||||
self.print_generic_args(generic_args, segment.infer_types,
|
||||
@ -1636,8 +1635,7 @@ impl<'a> State<'a> {
|
||||
}
|
||||
|
||||
pub fn print_path_segment(&mut self, segment: &hir::PathSegment) -> io::Result<()> {
|
||||
if segment.ident.name != keywords::PathRoot.name() &&
|
||||
segment.ident.name != keywords::DollarCrate.name() {
|
||||
if segment.ident.name != keywords::PathRoot.name() {
|
||||
self.print_ident(segment.ident)?;
|
||||
segment.with_generic_args(|generic_args| {
|
||||
self.print_generic_args(generic_args, segment.infer_types, false)
|
||||
@ -1664,8 +1662,7 @@ impl<'a> State<'a> {
|
||||
if i > 0 {
|
||||
self.s.word("::")?
|
||||
}
|
||||
if segment.ident.name != keywords::PathRoot.name() &&
|
||||
segment.ident.name != keywords::DollarCrate.name() {
|
||||
if segment.ident.name != keywords::PathRoot.name() {
|
||||
self.print_ident(segment.ident)?;
|
||||
segment.with_generic_args(|generic_args| {
|
||||
self.print_generic_args(generic_args,
|
||||
|
@ -1035,4 +1035,15 @@ impl<'a, 'b> Visitor<'a> for BuildReducedGraphVisitor<'a, 'b> {
|
||||
}
|
||||
visit::walk_attribute(self, attr);
|
||||
}
|
||||
|
||||
fn visit_ident(&mut self, ident: Ident) {
|
||||
if ident.name == keywords::DollarCrate.name() {
|
||||
let name = match self.resolver.resolve_crate_root(ident).kind {
|
||||
ModuleKind::Def(_, name) if name != keywords::Invalid.name() => name,
|
||||
_ => keywords::Crate.name(),
|
||||
};
|
||||
ident.span.ctxt().set_dollar_crate_name(name);
|
||||
}
|
||||
visit::walk_ident(self, ident);
|
||||
}
|
||||
}
|
||||
|
@ -1173,10 +1173,6 @@ impl<'a> ModuleData<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_local(&self) -> bool {
|
||||
self.normal_ancestor_id.is_local()
|
||||
}
|
||||
|
||||
fn nearest_item_scope(&'a self) -> Module<'a> {
|
||||
if self.is_trait() { self.parent.unwrap() } else { self }
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
use {AmbiguityError, AmbiguityKind, AmbiguityErrorMisc};
|
||||
use {CrateLint, Resolver, ResolutionError, ScopeSet, Weak};
|
||||
use {Module, ModuleKind, NameBinding, NameBindingKind, PathResult, Segment, ToNameBinding};
|
||||
use {Module, NameBinding, NameBindingKind, PathResult, Segment, ToNameBinding};
|
||||
use {is_known_tool, resolve_error};
|
||||
use ModuleOrUniformRoot;
|
||||
use Namespace::*;
|
||||
@ -30,8 +30,6 @@ use syntax::ext::expand::{AstFragment, Invocation, InvocationKind};
|
||||
use syntax::ext::hygiene::{self, Mark};
|
||||
use syntax::ext::tt::macro_rules;
|
||||
use syntax::feature_gate::{feature_err, is_builtin_attr_name, GateIssue};
|
||||
use syntax::fold::{self, Folder};
|
||||
use syntax::ptr::P;
|
||||
use syntax::symbol::{Symbol, keywords};
|
||||
use syntax::util::lev_distance::find_best_match_for_name;
|
||||
use syntax_pos::{Span, DUMMY_SP};
|
||||
@ -138,58 +136,6 @@ impl<'a> base::Resolver for Resolver<'a> {
|
||||
mark
|
||||
}
|
||||
|
||||
fn eliminate_crate_var(&mut self, item: P<ast::Item>) -> P<ast::Item> {
|
||||
struct EliminateCrateVar<'b, 'a: 'b>(
|
||||
&'b mut Resolver<'a>, Span
|
||||
);
|
||||
|
||||
impl<'a, 'b> Folder for EliminateCrateVar<'a, 'b> {
|
||||
fn fold_path(&mut self, path: ast::Path) -> ast::Path {
|
||||
match self.fold_qpath(None, path) {
|
||||
(None, path) => path,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_qpath(&mut self, mut qself: Option<ast::QSelf>, mut path: ast::Path)
|
||||
-> (Option<ast::QSelf>, ast::Path) {
|
||||
qself = qself.map(|ast::QSelf { ty, path_span, position }| {
|
||||
ast::QSelf {
|
||||
ty: self.fold_ty(ty),
|
||||
path_span: self.new_span(path_span),
|
||||
position,
|
||||
}
|
||||
});
|
||||
|
||||
if path.segments[0].ident.name == keywords::DollarCrate.name() {
|
||||
let module = self.0.resolve_crate_root(path.segments[0].ident);
|
||||
path.segments[0].ident.name = keywords::PathRoot.name();
|
||||
if !module.is_local() {
|
||||
let span = path.segments[0].ident.span;
|
||||
path.segments.insert(1, match module.kind {
|
||||
ModuleKind::Def(_, name) => ast::PathSegment::from_ident(
|
||||
ast::Ident::with_empty_ctxt(name).with_span_pos(span)
|
||||
),
|
||||
_ => unreachable!(),
|
||||
});
|
||||
if let Some(qself) = &mut qself {
|
||||
qself.position += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
(qself, path)
|
||||
}
|
||||
|
||||
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
|
||||
fold::noop_fold_mac(mac, self)
|
||||
}
|
||||
}
|
||||
|
||||
let ret = EliminateCrateVar(self, item.span).fold_item(item);
|
||||
assert!(ret.len() == 1);
|
||||
ret.into_iter().next().unwrap()
|
||||
}
|
||||
|
||||
fn visit_ast_fragment_with_placeholders(&mut self, mark: Mark, fragment: &AstFragment,
|
||||
derives: &[Mark]) {
|
||||
let invocation = self.invocations[&mark];
|
||||
@ -259,7 +205,6 @@ impl<'a> base::Resolver for Resolver<'a> {
|
||||
self.definitions.add_parent_module_of_macro_def(invoc.expansion_data.mark,
|
||||
normal_module_def_id);
|
||||
invoc.expansion_data.mark.set_default_transparency(ext.default_transparency());
|
||||
invoc.expansion_data.mark.set_is_builtin(def_id.krate == CrateNum::BuiltinMacros);
|
||||
}
|
||||
|
||||
Ok(Some(ext))
|
||||
|
@ -732,7 +732,6 @@ pub type NamedSyntaxExtension = (Name, SyntaxExtension);
|
||||
pub trait Resolver {
|
||||
fn next_node_id(&mut self) -> ast::NodeId;
|
||||
fn get_module_scope(&mut self, id: ast::NodeId) -> Mark;
|
||||
fn eliminate_crate_var(&mut self, item: P<ast::Item>) -> P<ast::Item>;
|
||||
|
||||
fn visit_ast_fragment_with_placeholders(&mut self, mark: Mark, fragment: &AstFragment,
|
||||
derives: &[Mark]);
|
||||
@ -766,7 +765,6 @@ pub struct DummyResolver;
|
||||
impl Resolver for DummyResolver {
|
||||
fn next_node_id(&mut self) -> ast::NodeId { ast::DUMMY_NODE_ID }
|
||||
fn get_module_scope(&mut self, _id: ast::NodeId) -> Mark { Mark::root() }
|
||||
fn eliminate_crate_var(&mut self, item: P<ast::Item>) -> P<ast::Item> { item }
|
||||
|
||||
fn visit_ast_fragment_with_placeholders(&mut self, _invoc: Mark, _fragment: &AstFragment,
|
||||
_derives: &[Mark]) {}
|
||||
|
@ -203,10 +203,7 @@ fn macro_bang_format(path: &ast::Path) -> ExpnFormat {
|
||||
if i != 0 {
|
||||
path_str.push_str("::");
|
||||
}
|
||||
|
||||
if segment.ident.name != keywords::PathRoot.name() &&
|
||||
segment.ident.name != keywords::DollarCrate.name()
|
||||
{
|
||||
if segment.ident.name != keywords::PathRoot.name() {
|
||||
path_str.push_str(&segment.ident.as_str())
|
||||
}
|
||||
}
|
||||
|
@ -633,7 +633,9 @@ impl Token {
|
||||
(&Shebang(a), &Shebang(b)) => a == b,
|
||||
|
||||
(&Lifetime(a), &Lifetime(b)) => a.name == b.name,
|
||||
(&Ident(a, b), &Ident(c, d)) => a.name == c.name && b == d,
|
||||
(&Ident(a, b), &Ident(c, d)) => b == d && (a.name == c.name ||
|
||||
a.name == keywords::DollarCrate.name() ||
|
||||
c.name == keywords::DollarCrate.name()),
|
||||
|
||||
(&Literal(ref a, b), &Literal(ref c, d)) => {
|
||||
b == d && a.probably_equal_for_proc_macro(c)
|
||||
|
@ -16,7 +16,6 @@ use util::parser::{self, AssocOp, Fixity};
|
||||
use attr;
|
||||
use source_map::{self, SourceMap, Spanned};
|
||||
use syntax_pos::{self, BytePos};
|
||||
use syntax_pos::hygiene::{Mark, SyntaxContext};
|
||||
use parse::token::{self, BinOpToken, Token};
|
||||
use parse::lexer::comments;
|
||||
use parse::{self, ParseSess};
|
||||
@ -724,12 +723,12 @@ pub trait PrintState<'a> {
|
||||
if i > 0 {
|
||||
self.writer().word("::")?
|
||||
}
|
||||
if segment.ident.name != keywords::PathRoot.name() &&
|
||||
segment.ident.name != keywords::DollarCrate.name()
|
||||
{
|
||||
self.writer().word(segment.ident.as_str().get())?;
|
||||
} else if segment.ident.name == keywords::DollarCrate.name() {
|
||||
self.print_dollar_crate(segment.ident.span.ctxt())?;
|
||||
if segment.ident.name != keywords::PathRoot.name() {
|
||||
if segment.ident.name == keywords::DollarCrate.name() {
|
||||
self.print_dollar_crate(segment.ident)?;
|
||||
} else {
|
||||
self.writer().word(segment.ident.as_str().get())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -843,17 +842,19 @@ pub trait PrintState<'a> {
|
||||
|
||||
fn nbsp(&mut self) -> io::Result<()> { self.writer().word(" ") }
|
||||
|
||||
fn print_dollar_crate(&mut self, mut ctxt: SyntaxContext) -> io::Result<()> {
|
||||
if let Some(mark) = ctxt.adjust(Mark::root()) {
|
||||
// Make a best effort to print something that complies
|
||||
if mark.is_builtin() {
|
||||
if let Some(name) = std_inject::injected_crate_name() {
|
||||
self.writer().word("::")?;
|
||||
self.writer().word(name)?;
|
||||
}
|
||||
}
|
||||
// AST pretty-printer is used as a fallback for turning AST structures into token streams for
|
||||
// proc macros. Additionally, proc macros may stringify their input and expect it survive the
|
||||
// stringification (especially true for proc macro derives written between Rust 1.15 and 1.30).
|
||||
// So we need to somehow pretty-print `$crate` in paths in a way preserving at least some of
|
||||
// its hygiene data, most importantly name of the crate it refers to.
|
||||
// As a result we print `$crate` as `crate` if it refers to the local crate
|
||||
// and as `::other_crate_name` if it refers to some other crate.
|
||||
fn print_dollar_crate(&mut self, ident: ast::Ident) -> io::Result<()> {
|
||||
let name = ident.span.ctxt().dollar_crate_name();
|
||||
if !ast::Ident::with_empty_ctxt(name).is_path_segment_keyword() {
|
||||
self.writer().word("::")?;
|
||||
}
|
||||
Ok(())
|
||||
self.writer().word(name.as_str().get())
|
||||
}
|
||||
}
|
||||
|
||||
@ -2463,14 +2464,15 @@ impl<'a> State<'a> {
|
||||
colons_before_params: bool)
|
||||
-> io::Result<()>
|
||||
{
|
||||
if segment.ident.name != keywords::PathRoot.name() &&
|
||||
segment.ident.name != keywords::DollarCrate.name() {
|
||||
self.print_ident(segment.ident)?;
|
||||
if segment.ident.name != keywords::PathRoot.name() {
|
||||
if segment.ident.name == keywords::DollarCrate.name() {
|
||||
self.print_dollar_crate(segment.ident)?;
|
||||
} else {
|
||||
self.print_ident(segment.ident)?;
|
||||
}
|
||||
if let Some(ref args) = segment.args {
|
||||
self.print_generic_args(args, colons_before_params)?;
|
||||
}
|
||||
} else if segment.ident.name == keywords::DollarCrate.name() {
|
||||
self.print_dollar_crate(segment.ident.span.ctxt())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -348,7 +348,9 @@ impl TokenStream {
|
||||
| TokenTree::Token(_, Token::Semi)
|
||||
// The pretty printer collapses whitespace arbitrarily and can
|
||||
// introduce whitespace from `NoDelim`.
|
||||
| TokenTree::Token(_, Token::Whitespace) => false,
|
||||
| TokenTree::Token(_, Token::Whitespace)
|
||||
// The pretty printer can turn `$crate` into `::crate_name`
|
||||
| TokenTree::Token(_, Token::ModSep) => false,
|
||||
_ => true
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,6 @@ impl MultiItemModifier for ProcMacroDerive {
|
||||
// Mark attributes as known, and used.
|
||||
MarkAttrs(&self.attrs).visit_item(&item);
|
||||
|
||||
let item = ecx.resolver.eliminate_crate_var(item);
|
||||
let token = Token::interpolated(token::NtItem(item));
|
||||
let input = tokenstream::TokenTree::Token(DUMMY_SP, token).into();
|
||||
|
||||
|
@ -81,29 +81,23 @@ impl FromInternal<(TokenStream, &'_ ParseSess, &'_ mut Vec<Self>)>
|
||||
$($field $(: $value)*,)*
|
||||
span,
|
||||
})
|
||||
)
|
||||
);
|
||||
($ty:ident::$method:ident($($value:expr),*)) => (
|
||||
TokenTree::$ty(self::$ty::$method($($value,)* span))
|
||||
);
|
||||
}
|
||||
macro_rules! op {
|
||||
($a:expr) => {
|
||||
tt!(Punct { ch: $a, joint })
|
||||
tt!(Punct::new($a, joint))
|
||||
};
|
||||
($a:expr, $b:expr) => {{
|
||||
stack.push(tt!(Punct { ch: $b, joint }));
|
||||
tt!(Punct {
|
||||
ch: $a,
|
||||
joint: true
|
||||
})
|
||||
stack.push(tt!(Punct::new($b, joint)));
|
||||
tt!(Punct::new($a, true))
|
||||
}};
|
||||
($a:expr, $b:expr, $c:expr) => {{
|
||||
stack.push(tt!(Punct { ch: $c, joint }));
|
||||
stack.push(tt!(Punct {
|
||||
ch: $b,
|
||||
joint: true
|
||||
}));
|
||||
tt!(Punct {
|
||||
ch: $a,
|
||||
joint: true
|
||||
})
|
||||
stack.push(tt!(Punct::new($c, joint)));
|
||||
stack.push(tt!(Punct::new($b, true)));
|
||||
tt!(Punct::new($a, true))
|
||||
}};
|
||||
}
|
||||
|
||||
@ -156,20 +150,13 @@ impl FromInternal<(TokenStream, &'_ ParseSess, &'_ mut Vec<Self>)>
|
||||
Question => op!('?'),
|
||||
SingleQuote => op!('\''),
|
||||
|
||||
Ident(ident, is_raw) => tt!(Ident {
|
||||
sym: ident.name,
|
||||
is_raw
|
||||
}),
|
||||
Ident(ident, false) if ident.name == keywords::DollarCrate.name() =>
|
||||
tt!(Ident::dollar_crate()),
|
||||
Ident(ident, is_raw) => tt!(Ident::new(ident.name, is_raw)),
|
||||
Lifetime(ident) => {
|
||||
let ident = ident.without_first_quote();
|
||||
stack.push(tt!(Ident {
|
||||
sym: ident.name,
|
||||
is_raw: false
|
||||
}));
|
||||
tt!(Punct {
|
||||
ch: '\'',
|
||||
joint: true
|
||||
})
|
||||
stack.push(tt!(Ident::new(ident.name, false)));
|
||||
tt!(Punct::new('\'', true))
|
||||
}
|
||||
Literal(lit, suffix) => tt!(Literal { lit, suffix }),
|
||||
DocComment(c) => {
|
||||
@ -193,15 +180,9 @@ impl FromInternal<(TokenStream, &'_ ParseSess, &'_ mut Vec<Self>)>
|
||||
span: DelimSpan::from_single(span),
|
||||
}));
|
||||
if style == ast::AttrStyle::Inner {
|
||||
stack.push(tt!(Punct {
|
||||
ch: '!',
|
||||
joint: false
|
||||
}));
|
||||
stack.push(tt!(Punct::new('!', false)));
|
||||
}
|
||||
tt!(Punct {
|
||||
ch: '#',
|
||||
joint: false
|
||||
})
|
||||
tt!(Punct::new('#', false))
|
||||
}
|
||||
|
||||
Interpolated(_) => {
|
||||
@ -237,7 +218,7 @@ impl ToInternal<TokenStream> for TokenTree<Group, Punct, Ident, Literal> {
|
||||
)
|
||||
.into();
|
||||
}
|
||||
TokenTree::Ident(self::Ident { sym, span, is_raw }) => {
|
||||
TokenTree::Ident(self::Ident { sym, is_raw, span }) => {
|
||||
let token = Ident(ast::Ident::new(sym, span), is_raw);
|
||||
return tokenstream::TokenTree::Token(span, token).into();
|
||||
}
|
||||
@ -338,11 +319,52 @@ pub struct Punct {
|
||||
span: Span,
|
||||
}
|
||||
|
||||
impl Punct {
|
||||
fn new(ch: char, joint: bool, span: Span) -> Punct {
|
||||
const LEGAL_CHARS: &[char] = &['=', '<', '>', '!', '~', '+', '-', '*', '/', '%', '^',
|
||||
'&', '|', '@', '.', ',', ';', ':', '#', '$', '?', '\''];
|
||||
if !LEGAL_CHARS.contains(&ch) {
|
||||
panic!("unsupported character `{:?}`", ch)
|
||||
}
|
||||
Punct { ch, joint, span }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Ident {
|
||||
sym: Symbol,
|
||||
span: Span,
|
||||
is_raw: bool,
|
||||
span: Span,
|
||||
}
|
||||
|
||||
impl Ident {
|
||||
fn is_valid(string: &str) -> bool {
|
||||
let mut chars = string.chars();
|
||||
if let Some(start) = chars.next() {
|
||||
(start == '_' || start.is_xid_start())
|
||||
&& chars.all(|cont| cont == '_' || cont.is_xid_continue())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
fn new(sym: Symbol, is_raw: bool, span: Span) -> Ident {
|
||||
let string = sym.as_str().get();
|
||||
if !Self::is_valid(string) {
|
||||
panic!("`{:?}` is not a valid identifier", string)
|
||||
}
|
||||
if is_raw {
|
||||
let normalized_sym = Symbol::intern(string);
|
||||
if normalized_sym == keywords::Underscore.name() ||
|
||||
ast::Ident::with_empty_ctxt(normalized_sym).is_path_segment_keyword() {
|
||||
panic!("`{:?}` is not a valid raw identifier", string)
|
||||
}
|
||||
}
|
||||
Ident { sym, is_raw, span }
|
||||
}
|
||||
fn dollar_crate(span: Span) -> Ident {
|
||||
// `$crate` is accepted as an ident only if it comes from the compiler.
|
||||
Ident { sym: keywords::DollarCrate.name(), is_raw: false, span }
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(eddyb) `Literal` should not expose internal `Debug` impls.
|
||||
@ -492,11 +514,7 @@ impl server::Group for Rustc<'_> {
|
||||
|
||||
impl server::Punct for Rustc<'_> {
|
||||
fn new(&mut self, ch: char, spacing: Spacing) -> Self::Punct {
|
||||
Punct {
|
||||
ch,
|
||||
joint: spacing == Spacing::Joint,
|
||||
span: server::Span::call_site(self),
|
||||
}
|
||||
Punct::new(ch, spacing == Spacing::Joint, server::Span::call_site(self))
|
||||
}
|
||||
fn as_char(&mut self, punct: Self::Punct) -> char {
|
||||
punct.ch
|
||||
@ -518,14 +536,7 @@ impl server::Punct for Rustc<'_> {
|
||||
|
||||
impl server::Ident for Rustc<'_> {
|
||||
fn new(&mut self, string: &str, span: Self::Span, is_raw: bool) -> Self::Ident {
|
||||
let sym = Symbol::intern(string);
|
||||
if is_raw
|
||||
&& (sym == keywords::Underscore.name()
|
||||
|| ast::Ident::with_empty_ctxt(sym).is_path_segment_keyword())
|
||||
{
|
||||
panic!("`{:?}` is not a valid raw identifier", string)
|
||||
}
|
||||
Ident { sym, span, is_raw }
|
||||
Ident::new(Symbol::intern(string), is_raw, span)
|
||||
}
|
||||
fn span(&mut self, ident: Self::Ident) -> Self::Span {
|
||||
ident.span
|
||||
|
@ -18,11 +18,11 @@
|
||||
use GLOBALS;
|
||||
use Span;
|
||||
use edition::{Edition, DEFAULT_EDITION};
|
||||
use symbol::Symbol;
|
||||
use symbol::{keywords, Symbol};
|
||||
|
||||
use serialize::{Encodable, Decodable, Encoder, Decoder};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use std::fmt;
|
||||
use std::{fmt, mem};
|
||||
|
||||
/// A SyntaxContext represents a chain of macro expansions (represented by marks).
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord, Hash)]
|
||||
@ -37,6 +37,8 @@ struct SyntaxContextData {
|
||||
opaque: SyntaxContext,
|
||||
// This context, but with all transparent marks filtered away.
|
||||
opaque_and_semitransparent: SyntaxContext,
|
||||
// Name of the crate to which `$crate` with this context would resolve.
|
||||
dollar_crate_name: Symbol,
|
||||
}
|
||||
|
||||
/// A mark is a unique id associated with a macro expansion.
|
||||
@ -47,7 +49,6 @@ pub struct Mark(u32);
|
||||
struct MarkData {
|
||||
parent: Mark,
|
||||
default_transparency: Transparency,
|
||||
is_builtin: bool,
|
||||
expn_info: Option<ExpnInfo>,
|
||||
}
|
||||
|
||||
@ -77,7 +78,6 @@ impl Mark {
|
||||
parent,
|
||||
// By default expansions behave like `macro_rules`.
|
||||
default_transparency: Transparency::SemiTransparent,
|
||||
is_builtin: false,
|
||||
expn_info: None,
|
||||
});
|
||||
Mark(data.marks.len() as u32 - 1)
|
||||
@ -121,18 +121,6 @@ impl Mark {
|
||||
HygieneData::with(|data| data.marks[self.0 as usize].default_transparency = transparency)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_builtin(self) -> bool {
|
||||
assert_ne!(self, Mark::root());
|
||||
HygieneData::with(|data| data.marks[self.0 as usize].is_builtin)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_is_builtin(self, is_builtin: bool) {
|
||||
assert_ne!(self, Mark::root());
|
||||
HygieneData::with(|data| data.marks[self.0 as usize].is_builtin = is_builtin)
|
||||
}
|
||||
|
||||
pub fn is_descendant_of(mut self, ancestor: Mark) -> bool {
|
||||
HygieneData::with(|data| {
|
||||
while self != ancestor {
|
||||
@ -206,7 +194,6 @@ impl HygieneData {
|
||||
// If the root is opaque, then loops searching for an opaque mark
|
||||
// will automatically stop after reaching it.
|
||||
default_transparency: Transparency::Opaque,
|
||||
is_builtin: true,
|
||||
expn_info: None,
|
||||
}],
|
||||
syntax_contexts: vec![SyntaxContextData {
|
||||
@ -215,6 +202,7 @@ impl HygieneData {
|
||||
prev_ctxt: SyntaxContext(0),
|
||||
opaque: SyntaxContext(0),
|
||||
opaque_and_semitransparent: SyntaxContext(0),
|
||||
dollar_crate_name: keywords::DollarCrate.name(),
|
||||
}],
|
||||
markings: FxHashMap::default(),
|
||||
default_edition: DEFAULT_EDITION,
|
||||
@ -262,7 +250,6 @@ impl SyntaxContext {
|
||||
data.marks.push(MarkData {
|
||||
parent: Mark::root(),
|
||||
default_transparency: Transparency::SemiTransparent,
|
||||
is_builtin: false,
|
||||
expn_info: Some(expansion_info),
|
||||
});
|
||||
|
||||
@ -274,6 +261,7 @@ impl SyntaxContext {
|
||||
prev_ctxt: SyntaxContext::empty(),
|
||||
opaque: SyntaxContext::empty(),
|
||||
opaque_and_semitransparent: SyntaxContext::empty(),
|
||||
dollar_crate_name: keywords::DollarCrate.name(),
|
||||
});
|
||||
SyntaxContext(data.syntax_contexts.len() as u32 - 1)
|
||||
})
|
||||
@ -340,6 +328,7 @@ impl SyntaxContext {
|
||||
prev_ctxt,
|
||||
opaque: new_opaque,
|
||||
opaque_and_semitransparent: new_opaque,
|
||||
dollar_crate_name: keywords::DollarCrate.name(),
|
||||
});
|
||||
new_opaque
|
||||
});
|
||||
@ -357,6 +346,7 @@ impl SyntaxContext {
|
||||
prev_ctxt,
|
||||
opaque,
|
||||
opaque_and_semitransparent: new_opaque_and_semitransparent,
|
||||
dollar_crate_name: keywords::DollarCrate.name(),
|
||||
});
|
||||
new_opaque_and_semitransparent
|
||||
});
|
||||
@ -372,6 +362,7 @@ impl SyntaxContext {
|
||||
prev_ctxt,
|
||||
opaque,
|
||||
opaque_and_semitransparent,
|
||||
dollar_crate_name: keywords::DollarCrate.name(),
|
||||
});
|
||||
new_opaque_and_semitransparent_and_transparent
|
||||
})
|
||||
@ -526,6 +517,21 @@ impl SyntaxContext {
|
||||
pub fn outer(self) -> Mark {
|
||||
HygieneData::with(|data| data.syntax_contexts[self.0 as usize].outer_mark)
|
||||
}
|
||||
|
||||
pub fn dollar_crate_name(self) -> Symbol {
|
||||
HygieneData::with(|data| data.syntax_contexts[self.0 as usize].dollar_crate_name)
|
||||
}
|
||||
|
||||
pub fn set_dollar_crate_name(self, dollar_crate_name: Symbol) {
|
||||
HygieneData::with(|data| {
|
||||
let prev_dollar_crate_name = mem::replace(
|
||||
&mut data.syntax_contexts[self.0 as usize].dollar_crate_name, dollar_crate_name
|
||||
);
|
||||
assert!(dollar_crate_name == prev_dollar_crate_name ||
|
||||
prev_dollar_crate_name == keywords::DollarCrate.name(),
|
||||
"$crate name is reset for a syntax context");
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SyntaxContext {
|
||||
|
@ -39,8 +39,8 @@ pub fn bar() ({
|
||||
|
||||
|
||||
|
||||
((::fmt::format as
|
||||
for<'r> fn(std::fmt::Arguments<'r>) -> std::string::String {std::fmt::format})(((<::fmt::Arguments>::new_v1
|
||||
(($crate::fmt::format as
|
||||
for<'r> fn(std::fmt::Arguments<'r>) -> std::string::String {std::fmt::format})(((<$crate::fmt::Arguments>::new_v1
|
||||
as
|
||||
fn(&[&str], &[std::fmt::ArgumentV1<'_>]) -> std::fmt::Arguments<'_> {std::fmt::Arguments<'_>::new_v1})((&([("test"
|
||||
as
|
||||
|
16
src/test/ui/proc-macro/auxiliary/dollar-crate-external.rs
Normal file
16
src/test/ui/proc-macro/auxiliary/dollar-crate-external.rs
Normal file
@ -0,0 +1,16 @@
|
||||
pub type S = u8;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! external {
|
||||
() => {
|
||||
dollar_crate::m! {
|
||||
struct M($crate::S);
|
||||
}
|
||||
|
||||
#[dollar_crate::a]
|
||||
struct A($crate::S);
|
||||
|
||||
#[derive(dollar_crate::d)]
|
||||
struct D($crate::S);
|
||||
};
|
||||
}
|
28
src/test/ui/proc-macro/auxiliary/dollar-crate.rs
Normal file
28
src/test/ui/proc-macro/auxiliary/dollar-crate.rs
Normal file
@ -0,0 +1,28 @@
|
||||
// force-host
|
||||
// no-prefer-dynamic
|
||||
|
||||
#![crate_type = "proc-macro"]
|
||||
|
||||
extern crate proc_macro;
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
#[proc_macro]
|
||||
pub fn m(input: TokenStream) -> TokenStream {
|
||||
println!("PROC MACRO INPUT (PRETTY-PRINTED): {}", input);
|
||||
println!("PROC MACRO INPUT: {:#?}", input);
|
||||
input.into_iter().collect()
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn a(_args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
println!("ATTRIBUTE INPUT (PRETTY-PRINTED): {}", input);
|
||||
println!("ATTRIBUTE INPUT: {:#?}", input);
|
||||
input.into_iter().collect()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(d)]
|
||||
pub fn d(input: TokenStream) -> TokenStream {
|
||||
println!("DERIVE INPUT (PRETTY-PRINTED): {}", input);
|
||||
println!("DERIVE INPUT: {:#?}", input);
|
||||
input.into_iter().collect()
|
||||
}
|
40
src/test/ui/proc-macro/dollar-crate.rs
Normal file
40
src/test/ui/proc-macro/dollar-crate.rs
Normal file
@ -0,0 +1,40 @@
|
||||
// edition:2018
|
||||
// aux-build:dollar-crate.rs
|
||||
// aux-build:dollar-crate-external.rs
|
||||
|
||||
// Anonymize unstable non-dummy spans while still showing dummy spans `0..0`.
|
||||
// normalize-stdout-test "bytes\([^0]\w*\.\.(\w+)\)" -> "bytes(LO..$1)"
|
||||
// normalize-stdout-test "bytes\((\w+)\.\.[^0]\w*\)" -> "bytes($1..HI)"
|
||||
|
||||
extern crate dollar_crate;
|
||||
extern crate dollar_crate_external;
|
||||
|
||||
type S = u8;
|
||||
|
||||
mod local {
|
||||
use crate::dollar_crate;
|
||||
|
||||
macro_rules! local {
|
||||
() => {
|
||||
dollar_crate::m! {
|
||||
struct M($crate::S);
|
||||
}
|
||||
|
||||
#[dollar_crate::a]
|
||||
struct A($crate::S);
|
||||
|
||||
#[derive(dollar_crate::d)]
|
||||
struct D($crate::S); //~ ERROR the name `D` is defined multiple times
|
||||
};
|
||||
}
|
||||
|
||||
local!();
|
||||
}
|
||||
|
||||
mod external {
|
||||
use crate::dollar_crate_external;
|
||||
|
||||
dollar_crate_external::external!(); //~ ERROR the name `D` is defined multiple times
|
||||
}
|
||||
|
||||
fn main() {}
|
29
src/test/ui/proc-macro/dollar-crate.stderr
Normal file
29
src/test/ui/proc-macro/dollar-crate.stderr
Normal file
@ -0,0 +1,29 @@
|
||||
error[E0428]: the name `D` is defined multiple times
|
||||
--> $DIR/dollar-crate.rs:27:13
|
||||
|
|
||||
LL | struct D($crate::S); //~ ERROR the name `D` is defined multiple times
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| `D` redefined here
|
||||
| previous definition of the type `D` here
|
||||
...
|
||||
LL | local!();
|
||||
| --------- in this macro invocation
|
||||
|
|
||||
= note: `D` must be defined only once in the type namespace of this module
|
||||
|
||||
error[E0428]: the name `D` is defined multiple times
|
||||
--> $DIR/dollar-crate.rs:37:5
|
||||
|
|
||||
LL | dollar_crate_external::external!(); //~ ERROR the name `D` is defined multiple times
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| `D` redefined here
|
||||
| previous definition of the type `D` here
|
||||
|
|
||||
= note: `D` must be defined only once in the type namespace of this module
|
||||
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0428`.
|
240
src/test/ui/proc-macro/dollar-crate.stdout
Normal file
240
src/test/ui/proc-macro/dollar-crate.stdout
Normal file
@ -0,0 +1,240 @@
|
||||
PROC MACRO INPUT (PRETTY-PRINTED): struct M ( $crate :: S ) ;
|
||||
PROC MACRO INPUT: TokenStream [
|
||||
Ident {
|
||||
ident: "struct",
|
||||
span: #2 bytes(LO..HI)
|
||||
},
|
||||
Ident {
|
||||
ident: "M",
|
||||
span: #2 bytes(LO..HI)
|
||||
},
|
||||
Group {
|
||||
delimiter: Parenthesis,
|
||||
stream: TokenStream [
|
||||
Ident {
|
||||
ident: "$crate",
|
||||
span: #2 bytes(LO..HI)
|
||||
},
|
||||
Punct {
|
||||
ch: ':',
|
||||
spacing: Joint,
|
||||
span: #2 bytes(LO..HI)
|
||||
},
|
||||
Punct {
|
||||
ch: ':',
|
||||
spacing: Alone,
|
||||
span: #2 bytes(LO..HI)
|
||||
},
|
||||
Ident {
|
||||
ident: "S",
|
||||
span: #2 bytes(LO..HI)
|
||||
}
|
||||
],
|
||||
span: #2 bytes(LO..HI)
|
||||
},
|
||||
Punct {
|
||||
ch: ';',
|
||||
spacing: Alone,
|
||||
span: #2 bytes(LO..HI)
|
||||
}
|
||||
]
|
||||
ATTRIBUTE INPUT (PRETTY-PRINTED): struct A(crate::S);
|
||||
ATTRIBUTE INPUT: TokenStream [
|
||||
Ident {
|
||||
ident: "struct",
|
||||
span: #2 bytes(LO..HI)
|
||||
},
|
||||
Ident {
|
||||
ident: "A",
|
||||
span: #2 bytes(LO..HI)
|
||||
},
|
||||
Group {
|
||||
delimiter: Parenthesis,
|
||||
stream: TokenStream [
|
||||
Ident {
|
||||
ident: "$crate",
|
||||
span: #2 bytes(LO..HI)
|
||||
},
|
||||
Punct {
|
||||
ch: ':',
|
||||
spacing: Joint,
|
||||
span: #2 bytes(LO..HI)
|
||||
},
|
||||
Punct {
|
||||
ch: ':',
|
||||
spacing: Alone,
|
||||
span: #2 bytes(LO..HI)
|
||||
},
|
||||
Ident {
|
||||
ident: "S",
|
||||
span: #2 bytes(LO..HI)
|
||||
}
|
||||
],
|
||||
span: #2 bytes(LO..HI)
|
||||
},
|
||||
Punct {
|
||||
ch: ';',
|
||||
spacing: Alone,
|
||||
span: #2 bytes(LO..HI)
|
||||
}
|
||||
]
|
||||
DERIVE INPUT (PRETTY-PRINTED): struct D(crate::S);
|
||||
DERIVE INPUT: TokenStream [
|
||||
Ident {
|
||||
ident: "struct",
|
||||
span: #2 bytes(LO..HI)
|
||||
},
|
||||
Ident {
|
||||
ident: "D",
|
||||
span: #2 bytes(LO..HI)
|
||||
},
|
||||
Group {
|
||||
delimiter: Parenthesis,
|
||||
stream: TokenStream [
|
||||
Ident {
|
||||
ident: "$crate",
|
||||
span: #2 bytes(LO..HI)
|
||||
},
|
||||
Punct {
|
||||
ch: ':',
|
||||
spacing: Joint,
|
||||
span: #2 bytes(LO..HI)
|
||||
},
|
||||
Punct {
|
||||
ch: ':',
|
||||
spacing: Alone,
|
||||
span: #2 bytes(LO..HI)
|
||||
},
|
||||
Ident {
|
||||
ident: "S",
|
||||
span: #2 bytes(LO..HI)
|
||||
}
|
||||
],
|
||||
span: #2 bytes(LO..HI)
|
||||
},
|
||||
Punct {
|
||||
ch: ';',
|
||||
spacing: Alone,
|
||||
span: #2 bytes(LO..HI)
|
||||
}
|
||||
]
|
||||
PROC MACRO INPUT (PRETTY-PRINTED): struct M ( $crate :: S ) ;
|
||||
PROC MACRO INPUT: TokenStream [
|
||||
Ident {
|
||||
ident: "struct",
|
||||
span: #10 bytes(LO..HI)
|
||||
},
|
||||
Ident {
|
||||
ident: "M",
|
||||
span: #10 bytes(LO..HI)
|
||||
},
|
||||
Group {
|
||||
delimiter: Parenthesis,
|
||||
stream: TokenStream [
|
||||
Ident {
|
||||
ident: "$crate",
|
||||
span: #10 bytes(LO..HI)
|
||||
},
|
||||
Punct {
|
||||
ch: ':',
|
||||
spacing: Joint,
|
||||
span: #10 bytes(LO..HI)
|
||||
},
|
||||
Punct {
|
||||
ch: ':',
|
||||
spacing: Alone,
|
||||
span: #10 bytes(LO..HI)
|
||||
},
|
||||
Ident {
|
||||
ident: "S",
|
||||
span: #10 bytes(LO..HI)
|
||||
}
|
||||
],
|
||||
span: #10 bytes(LO..HI)
|
||||
},
|
||||
Punct {
|
||||
ch: ';',
|
||||
spacing: Alone,
|
||||
span: #10 bytes(LO..HI)
|
||||
}
|
||||
]
|
||||
ATTRIBUTE INPUT (PRETTY-PRINTED): struct A(::dollar_crate_external::S);
|
||||
ATTRIBUTE INPUT: TokenStream [
|
||||
Ident {
|
||||
ident: "struct",
|
||||
span: #10 bytes(LO..HI)
|
||||
},
|
||||
Ident {
|
||||
ident: "A",
|
||||
span: #10 bytes(LO..HI)
|
||||
},
|
||||
Group {
|
||||
delimiter: Parenthesis,
|
||||
stream: TokenStream [
|
||||
Ident {
|
||||
ident: "$crate",
|
||||
span: #10 bytes(LO..HI)
|
||||
},
|
||||
Punct {
|
||||
ch: ':',
|
||||
spacing: Joint,
|
||||
span: #10 bytes(LO..HI)
|
||||
},
|
||||
Punct {
|
||||
ch: ':',
|
||||
spacing: Alone,
|
||||
span: #10 bytes(LO..HI)
|
||||
},
|
||||
Ident {
|
||||
ident: "S",
|
||||
span: #10 bytes(LO..HI)
|
||||
}
|
||||
],
|
||||
span: #10 bytes(LO..HI)
|
||||
},
|
||||
Punct {
|
||||
ch: ';',
|
||||
spacing: Alone,
|
||||
span: #10 bytes(LO..HI)
|
||||
}
|
||||
]
|
||||
DERIVE INPUT (PRETTY-PRINTED): struct D(::dollar_crate_external::S);
|
||||
DERIVE INPUT: TokenStream [
|
||||
Ident {
|
||||
ident: "struct",
|
||||
span: #10 bytes(LO..HI)
|
||||
},
|
||||
Ident {
|
||||
ident: "D",
|
||||
span: #10 bytes(LO..HI)
|
||||
},
|
||||
Group {
|
||||
delimiter: Parenthesis,
|
||||
stream: TokenStream [
|
||||
Ident {
|
||||
ident: "$crate",
|
||||
span: #10 bytes(LO..HI)
|
||||
},
|
||||
Punct {
|
||||
ch: ':',
|
||||
spacing: Joint,
|
||||
span: #10 bytes(LO..HI)
|
||||
},
|
||||
Punct {
|
||||
ch: ':',
|
||||
spacing: Alone,
|
||||
span: #10 bytes(LO..HI)
|
||||
},
|
||||
Ident {
|
||||
ident: "S",
|
||||
span: #10 bytes(LO..HI)
|
||||
}
|
||||
],
|
||||
span: #10 bytes(LO..HI)
|
||||
},
|
||||
Punct {
|
||||
ch: ';',
|
||||
spacing: Alone,
|
||||
span: #10 bytes(LO..HI)
|
||||
}
|
||||
]
|
Loading…
Reference in New Issue
Block a user