diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 8f5d110655a68..21fb29974c880 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -1025,15 +1025,4 @@ 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); - } } diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 182dcfe4098bb..5f6ef934c1aa3 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -1,6 +1,6 @@ use {AmbiguityError, AmbiguityKind, AmbiguityErrorMisc}; use {CrateLint, Resolver, ResolutionError, ScopeSet, Weak}; -use {Module, NameBinding, NameBindingKind, PathResult, Segment, ToNameBinding}; +use {Module, ModuleKind, NameBinding, NameBindingKind, PathResult, Segment, ToNameBinding}; use {is_known_tool, resolve_error}; use ModuleOrUniformRoot; use Namespace::*; @@ -15,12 +15,13 @@ use syntax::ast::{self, Ident}; use syntax::attr; use syntax::errors::DiagnosticBuilder; use syntax::ext::base::{self, Determinacy}; -use syntax::ext::base::{MacroKind, SyntaxExtension}; +use syntax::ext::base::{Annotatable, MacroKind, SyntaxExtension}; 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::symbol::{Symbol, keywords}; +use syntax::visit::Visitor; use syntax::util::lev_distance::find_best_match_for_name; use syntax_pos::{Span, DUMMY_SP}; use errors::Applicability; @@ -126,6 +127,26 @@ impl<'a> base::Resolver for Resolver<'a> { mark } + fn resolve_dollar_crates(&mut self, annotatable: &Annotatable) { + pub struct ResolveDollarCrates<'a, 'b: 'a> { + pub resolver: &'a mut Resolver<'b>, + } + impl<'a> Visitor<'a> for ResolveDollarCrates<'a, '_> { + 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); + } + } + fn visit_mac(&mut self, _: &ast::Mac) {} + } + + annotatable.visit_with(&mut ResolveDollarCrates { resolver: self }); + } + fn visit_ast_fragment_with_placeholders(&mut self, mark: Mark, fragment: &AstFragment, derives: &[Mark]) { let invocation = self.invocations[&mark]; diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 1efe0b3478db7..7e8b7007b2289 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -14,6 +14,7 @@ use parse::token; use ptr::P; use smallvec::SmallVec; use symbol::{keywords, Ident, Symbol}; +use visit::Visitor; use ThinVec; use rustc_data_structures::fx::FxHashMap; @@ -135,6 +136,17 @@ impl Annotatable { _ => false, } } + + pub fn visit_with<'a, V: Visitor<'a>>(&'a self, visitor: &mut V) { + match self { + Annotatable::Item(item) => visitor.visit_item(item), + Annotatable::TraitItem(trait_item) => visitor.visit_trait_item(trait_item), + Annotatable::ImplItem(impl_item) => visitor.visit_impl_item(impl_item), + Annotatable::ForeignItem(foreign_item) => visitor.visit_foreign_item(foreign_item), + Annotatable::Stmt(stmt) => visitor.visit_stmt(stmt), + Annotatable::Expr(expr) => visitor.visit_expr(expr), + } + } } // A more flexible ItemDecorator. @@ -730,6 +742,7 @@ pub trait Resolver { fn next_node_id(&mut self) -> ast::NodeId; fn get_module_scope(&mut self, id: ast::NodeId) -> Mark; + fn resolve_dollar_crates(&mut self, annotatable: &Annotatable); fn visit_ast_fragment_with_placeholders(&mut self, mark: Mark, fragment: &AstFragment, derives: &[Mark]); fn add_builtin(&mut self, ident: ast::Ident, ext: Lrc); @@ -763,6 +776,7 @@ 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 resolve_dollar_crates(&mut self, _annotatable: &Annotatable) {} fn visit_ast_fragment_with_placeholders(&mut self, _invoc: Mark, _fragment: &AstFragment, _derives: &[Mark]) {} fn add_builtin(&mut self, _ident: ast::Ident, _ext: Lrc) {} diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 3863778fe72af..9369e66cf83da 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -574,6 +574,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { Some(invoc.fragment_kind.expect_from_annotatables(items)) } AttrProcMacro(ref mac, ..) => { + // Resolve `$crate`s in case we have to go though stringification. + self.cx.resolver.resolve_dollar_crates(&item); self.gate_proc_macro_attr_item(attr.span, &item); let item_tok = TokenTree::Token(DUMMY_SP, Token::interpolated(match item { Annotatable::Item(item) => token::NtItem(item), @@ -915,6 +917,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { match *ext { ProcMacroDerive(ref ext, ..) => { + // Resolve `$crate`s in case we have to go though stringification. + self.cx.resolver.resolve_dollar_crates(&item); invoc.expansion_data.mark.set_expn_info(expn_info); let span = span.with_ctxt(self.cx.backtrace()); let dummy = ast::MetaItem { // FIXME(jseyfried) avoid this diff --git a/src/test/ui/proc-macro/auxiliary/dollar-crate.rs b/src/test/ui/proc-macro/auxiliary/dollar-crate.rs index d0ea850d4e323..c5347d2e81a6b 100644 --- a/src/test/ui/proc-macro/auxiliary/dollar-crate.rs +++ b/src/test/ui/proc-macro/auxiliary/dollar-crate.rs @@ -6,6 +6,13 @@ extern crate proc_macro; use proc_macro::TokenStream; +#[proc_macro] +pub fn m_empty(input: TokenStream) -> TokenStream { + println!("PROC MACRO INPUT (PRETTY-PRINTED): {}", input); + println!("PROC MACRO INPUT: {:#?}", input); + TokenStream::new() +} + #[proc_macro] pub fn m(input: TokenStream) -> TokenStream { println!("PROC MACRO INPUT (PRETTY-PRINTED): {}", input); diff --git a/src/test/ui/proc-macro/dollar-crate-issue-57089.rs b/src/test/ui/proc-macro/dollar-crate-issue-57089.rs new file mode 100644 index 0000000000000..2d54c07ff9530 --- /dev/null +++ b/src/test/ui/proc-macro/dollar-crate-issue-57089.rs @@ -0,0 +1,26 @@ +// compile-pass +// edition:2018 +// aux-build:dollar-crate.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; + +type S = u8; + +macro_rules! m { + () => { + dollar_crate::m_empty! { + struct M($crate::S); + } + + #[dollar_crate::a] + struct A($crate::S); + }; +} + +m!(); + +fn main() {} diff --git a/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout b/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout new file mode 100644 index 0000000000000..09340988c8968 --- /dev/null +++ b/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout @@ -0,0 +1,80 @@ +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) + } +]