Skip to content

Commit

Permalink
Auto merge of #57155 - petrochenkov:dcrate3, r=dtolnay
Browse files Browse the repository at this point in the history
Resolve `$crate`s for pretty-printing at more appropriate time

Doing it in `BuildReducedGraphVisitor` wasn't a good idea, identifiers wasn't actually visited half of the time.
As a result some `$crate`s weren't resolved and were therefore pretty-printed as `$crate` literally, which turns into two tokens during re-parsing of the pretty-printed text.

Now we are visiting and resolving `$crate` identifiers in an item right before sending that item to a proc macro attribute or derive.

Fixes #57089
  • Loading branch information
bors committed Dec 28, 2018
2 parents f8caa32 + e40d7d9 commit e8ca35e
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 13 deletions.
11 changes: 0 additions & 11 deletions src/librustc_resolve/build_reduced_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
25 changes: 23 additions & 2 deletions src/librustc_resolve/macros.rs
Original file line number Diff line number Diff line change
@@ -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::*;
Expand All @@ -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;
Expand Down Expand Up @@ -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];
Expand Down
14 changes: 14 additions & 0 deletions src/libsyntax/ext/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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<SyntaxExtension>);
Expand Down Expand Up @@ -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<SyntaxExtension>) {}
Expand Down
4 changes: 4 additions & 0 deletions src/libsyntax/ext/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions src/test/ui/proc-macro/auxiliary/dollar-crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
26 changes: 26 additions & 0 deletions src/test/ui/proc-macro/dollar-crate-issue-57089.rs
Original file line number Diff line number Diff line change
@@ -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() {}
80 changes: 80 additions & 0 deletions src/test/ui/proc-macro/dollar-crate-issue-57089.stdout
Original file line number Diff line number Diff line change
@@ -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)
}
]

0 comments on commit e8ca35e

Please sign in to comment.