Skip to content

Fix pretty-printing of $crate (take 4) #62393

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 10, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/librustc_resolve/build_reduced_graph.rs
Original file line number Diff line number Diff line change
@@ -758,7 +758,10 @@ impl<'a> Resolver<'a> {
}

pub fn macro_def_scope(&mut self, expansion: Mark) -> Module<'a> {
let def_id = self.macro_defs[&expansion];
let def_id = match self.macro_defs.get(&expansion) {
Some(def_id) => *def_id,
None => return self.graph_root,
};
if let Some(id) = self.definitions.as_local_node_id(def_id) {
self.local_macro_def_scopes[&id]
} else if def_id.krate == CrateNum::BuiltinMacros {
27 changes: 8 additions & 19 deletions src/librustc_resolve/macros.rs
Original file line number Diff line number Diff line change
@@ -17,12 +17,11 @@ use syntax::errors::DiagnosticBuilder;
use syntax::ext::base::{self, Determinacy};
use syntax::ext::base::{MacroKind, SyntaxExtension};
use syntax::ext::expand::{AstFragment, Invocation, InvocationKind};
use syntax::ext::hygiene::Mark;
use syntax::ext::hygiene::{self, Mark};
use syntax::ext::tt::macro_rules;
use syntax::feature_gate::{feature_err, emit_feature_err, is_builtin_attr_name};
use syntax::feature_gate::{AttributeGate, GateIssue, Stability, BUILTIN_ATTRIBUTES};
use syntax::symbol::{Symbol, kw, sym};
use syntax::visit::Visitor;
use syntax::util::lev_distance::find_best_match_for_name;
use syntax_pos::{Span, DUMMY_SP};
use errors::Applicability;
@@ -146,24 +145,14 @@ impl<'a> base::Resolver for Resolver<'a> {
mark
}

fn resolve_dollar_crates(&mut self, fragment: &AstFragment) {
struct ResolveDollarCrates<'a, 'b> {
resolver: &'a mut Resolver<'b>
}
impl<'a> Visitor<'a> for ResolveDollarCrates<'a, '_> {
fn visit_ident(&mut self, ident: Ident) {
if ident.name == kw::DollarCrate {
let name = match self.resolver.resolve_crate_root(ident).kind {
ModuleKind::Def(.., name) if name != kw::Invalid => name,
_ => kw::Crate,
};
ident.span.ctxt().set_dollar_crate_name(name);
}
fn resolve_dollar_crates(&mut self) {
hygiene::update_dollar_crate_names(|ctxt| {
let ident = Ident::new(kw::DollarCrate, DUMMY_SP.with_ctxt(ctxt));
match self.resolve_crate_root(ident).kind {
ModuleKind::Def(.., name) if name != kw::Invalid => name,
_ => kw::Crate,
}
fn visit_mac(&mut self, _: &ast::Mac) {}
}

fragment.visit_with(&mut ResolveDollarCrates { resolver: self });
});
}

fn visit_ast_fragment_with_placeholders(&mut self, mark: Mark, fragment: &AstFragment,
2 changes: 1 addition & 1 deletion src/libsyntax/ext/base.rs
Original file line number Diff line number Diff line change
@@ -701,7 +701,7 @@ pub trait Resolver {

fn get_module_scope(&mut self, id: ast::NodeId) -> Mark;

fn resolve_dollar_crates(&mut self, fragment: &AstFragment);
fn resolve_dollar_crates(&mut self);
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>);
2 changes: 1 addition & 1 deletion src/libsyntax/ext/expand.rs
Original file line number Diff line number Diff line change
@@ -429,7 +429,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
fn collect_invocations(&mut self, mut fragment: AstFragment, derives: &[Mark])
-> (AstFragment, Vec<Invocation>) {
// Resolve `$crate`s in the fragment for pretty-printing.
self.cx.resolver.resolve_dollar_crates(&fragment);
self.cx.resolver.resolve_dollar_crates();

let invocations = {
let mut collector = InvocationCollector {
109 changes: 62 additions & 47 deletions src/libsyntax/print/pprust.rs
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ use crate::tokenstream::{self, TokenStream, TokenTree};

use rustc_target::spec::abi::{self, Abi};
use syntax_pos::{self, BytePos};
use syntax_pos::{DUMMY_SP, FileName};
use syntax_pos::{DUMMY_SP, FileName, Span};

use std::borrow::Cow;
use std::io::Read;
@@ -181,7 +181,46 @@ pub fn literal_to_string(lit: token::Lit) -> String {
out
}

fn ident_to_string(ident: ast::Ident, is_raw: bool) -> String {
ident_to_string_ext(ident.name, is_raw, Some(ident.span))
}

// 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 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.
// Note, that this is only done if the ident token is printed from inside of AST pretty-pringing,
// but not otherwise. Pretty-printing is the only way for proc macros to discover token contents,
// so we should not perform this lossy conversion if the top level call to the pretty-printer was
// done for a token stream or a single token.
fn ident_to_string_ext(
name: ast::Name, is_raw: bool, convert_dollar_crate: Option<Span>
) -> String {
if is_raw {
format!("r#{}", name)
} else {
if name == kw::DollarCrate {
if let Some(span) = convert_dollar_crate {
let converted = span.ctxt().dollar_crate_name();
return if converted.is_path_segment_keyword() {
converted.to_string()
} else {
format!("::{}", converted)
}
}
}
name.to_string()
}
}

pub fn token_kind_to_string(tok: &TokenKind) -> String {
token_kind_to_string_ext(tok, None)
}

fn token_kind_to_string_ext(tok: &TokenKind, convert_dollar_crate: Option<Span>) -> String {
match *tok {
token::Eq => "=".to_string(),
token::Lt => "<".to_string(),
@@ -227,8 +266,7 @@ pub fn token_kind_to_string(tok: &TokenKind) -> String {
token::Literal(lit) => literal_to_string(lit),

/* Name components */
token::Ident(s, false) => s.to_string(),
token::Ident(s, true) => format!("r#{}", s),
token::Ident(s, is_raw) => ident_to_string_ext(s, is_raw, convert_dollar_crate),
token::Lifetime(s) => s.to_string(),

/* Other */
@@ -243,7 +281,12 @@ pub fn token_kind_to_string(tok: &TokenKind) -> String {
}

pub fn token_to_string(token: &Token) -> String {
token_kind_to_string(&token.kind)
token_to_string_ext(token, false)
}

fn token_to_string_ext(token: &Token, convert_dollar_crate: bool) -> String {
let convert_dollar_crate = if convert_dollar_crate { Some(token.span) } else { None };
token_kind_to_string_ext(&token.kind, convert_dollar_crate)
}

crate fn nonterminal_to_string(nt: &Nonterminal) -> String {
@@ -256,9 +299,8 @@ crate fn nonterminal_to_string(nt: &Nonterminal) -> String {
token::NtBlock(ref e) => block_to_string(e),
token::NtStmt(ref e) => stmt_to_string(e),
token::NtPat(ref e) => pat_to_string(e),
token::NtIdent(e, false) => ident_to_string(e),
token::NtIdent(e, true) => format!("r#{}", ident_to_string(e)),
token::NtLifetime(e) => ident_to_string(e),
token::NtIdent(e, is_raw) => ident_to_string(e, is_raw),
token::NtLifetime(e) => e.to_string(),
token::NtLiteral(ref e) => expr_to_string(e),
token::NtTT(ref tree) => tt_to_string(tree.clone()),
token::NtImplItem(ref e) => impl_item_to_string(e),
@@ -293,15 +335,15 @@ pub fn lifetime_to_string(lt: &ast::Lifetime) -> String {
}

pub fn tt_to_string(tt: tokenstream::TokenTree) -> String {
to_string(|s| s.print_tt(tt))
to_string(|s| s.print_tt(tt, false))
}

pub fn tts_to_string(tts: &[tokenstream::TokenTree]) -> String {
to_string(|s| s.print_tts(tts.iter().cloned().collect()))
tokens_to_string(tts.iter().cloned().collect())
}

pub fn tokens_to_string(tokens: TokenStream) -> String {
to_string(|s| s.print_tts(tokens))
to_string(|s| s.print_tts_ext(tokens, false))
}

pub fn stmt_to_string(stmt: &ast::Stmt) -> String {
@@ -344,10 +386,6 @@ pub fn path_segment_to_string(p: &ast::PathSegment) -> String {
to_string(|s| s.print_path_segment(p, false))
}

pub fn ident_to_string(id: ast::Ident) -> String {
to_string(|s| s.print_ident(id))
}

pub fn vis_to_string(v: &ast::Visibility) -> String {
to_string(|s| s.print_visibility(v))
}
@@ -629,11 +667,7 @@ pub trait PrintState<'a> {
self.writer().word("::");
}
if segment.ident.name != kw::PathRoot {
if segment.ident.name == kw::DollarCrate {
self.print_dollar_crate(segment.ident);
} else {
self.writer().word(segment.ident.as_str().to_string());
}
self.writer().word(ident_to_string(segment.ident, segment.ident.is_raw_guess()));
}
}
}
@@ -707,10 +741,10 @@ pub trait PrintState<'a> {
/// appropriate macro, transcribe back into the grammar we just parsed from,
/// and then pretty-print the resulting AST nodes (so, e.g., we print
/// expression arguments as expressions). It can be done! I think.
fn print_tt(&mut self, tt: tokenstream::TokenTree) {
fn print_tt(&mut self, tt: tokenstream::TokenTree, convert_dollar_crate: bool) {
match tt {
TokenTree::Token(ref token) => {
self.writer().word(token_to_string(&token));
self.writer().word(token_to_string_ext(&token, convert_dollar_crate));
match token.kind {
token::DocComment(..) => {
self.writer().hardbreak()
@@ -729,12 +763,16 @@ pub trait PrintState<'a> {
}

fn print_tts(&mut self, tts: tokenstream::TokenStream) {
self.print_tts_ext(tts, true)
}

fn print_tts_ext(&mut self, tts: tokenstream::TokenStream, convert_dollar_crate: bool) {
self.ibox(0);
for (i, tt) in tts.into_trees().enumerate() {
if i != 0 {
self.writer().space();
}
self.print_tt(tt);
self.print_tt(tt, convert_dollar_crate);
}
self.end();
}
@@ -744,21 +782,6 @@ pub trait PrintState<'a> {
}

fn nbsp(&mut self) { self.writer().word(" ") }

// 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) {
let name = ident.span.ctxt().dollar_crate_name();
if !ast::Ident::with_empty_ctxt(name).is_path_segment_keyword() {
self.writer().word("::");
}
self.writer().word(name.as_str().to_string())
}
}

impl<'a> PrintState<'a> for State<'a> {
@@ -2287,11 +2310,7 @@ impl<'a> State<'a> {
}

crate fn print_ident(&mut self, ident: ast::Ident) {
if ident.is_raw_guess() {
self.s.word(format!("r#{}", ident));
} else {
self.s.word(ident.as_str().to_string());
}
self.s.word(ident_to_string(ident, ident.is_raw_guess()));
self.ann.post(self, AnnNode::Ident(&ident))
}

@@ -2322,11 +2341,7 @@ impl<'a> State<'a> {
segment: &ast::PathSegment,
colons_before_params: bool) {
if segment.ident.name != kw::PathRoot {
if segment.ident.name == kw::DollarCrate {
self.print_dollar_crate(segment.ident);
} else {
self.print_ident(segment.ident);
}
self.print_ident(segment.ident);
if let Some(ref args) = segment.args {
self.print_generic_args(args, colons_before_params);
}
30 changes: 18 additions & 12 deletions src/libsyntax_pos/hygiene.rs
Original file line number Diff line number Diff line change
@@ -33,7 +33,7 @@ use crate::symbol::{kw, Symbol};
use serialize::{Encodable, Decodable, Encoder, Decoder};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::sync::Lrc;
use std::{fmt, mem};
use std::fmt;

/// A SyntaxContext represents a chain of macro expansions (represented by marks).
#[derive(Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord, Hash)]
@@ -387,6 +387,23 @@ pub fn walk_chain(span: Span, to: SyntaxContext) -> Span {
HygieneData::with(|data| data.walk_chain(span, to))
}

pub fn update_dollar_crate_names(mut get_name: impl FnMut(SyntaxContext) -> Symbol) {
// The new contexts that need updating are at the end of the list and have `$crate` as a name.
let (len, to_update) = HygieneData::with(|data| (
data.syntax_contexts.len(),
data.syntax_contexts.iter().rev()
.take_while(|scdata| scdata.dollar_crate_name == kw::DollarCrate).count()
));
// The callback must be called from outside of the `HygieneData` lock,
// since it will try to acquire it too.
let range_to_update = len - to_update .. len;
let names: Vec<_> =
range_to_update.clone().map(|idx| get_name(SyntaxContext::from_u32(idx as u32))).collect();
HygieneData::with(|data| range_to_update.zip(names.into_iter()).for_each(|(idx, name)| {
data.syntax_contexts[idx].dollar_crate_name = name;
}))
}

impl SyntaxContext {
#[inline]
pub const fn empty() -> Self {
@@ -614,17 +631,6 @@ impl SyntaxContext {
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 == kw::DollarCrate,
"$crate name is reset for a syntax context");
})
}
}

impl fmt::Debug for SyntaxContext {
6 changes: 6 additions & 0 deletions src/test/ui/proc-macro/auxiliary/dollar-crate-external.rs
Original file line number Diff line number Diff line change
@@ -14,3 +14,9 @@ macro_rules! external {
struct D($crate::S);
};
}

#[macro_export]
macro_rules! issue_62325 { () => {
#[print_attr]
struct B(identity!($crate::S));
}}
4 changes: 2 additions & 2 deletions src/test/ui/proc-macro/dollar-crate-issue-57089.stdout
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
PRINT-BANG INPUT (DISPLAY): struct M ( $crate :: S ) ;
PRINT-BANG INPUT (DISPLAY): struct M ( crate :: S ) ;
PRINT-BANG INPUT (DEBUG): TokenStream [
Ident {
ident: "struct",
@@ -39,7 +39,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
},
]
PRINT-ATTR INPUT (DISPLAY): struct A(crate::S);
PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ( $crate :: S ) ;
PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ( crate :: S ) ;
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "struct",
27 changes: 27 additions & 0 deletions src/test/ui/proc-macro/dollar-crate-issue-62325.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// check-pass
// edition:2018
// aux-build:test-macros.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)"

#![feature(proc_macro_hygiene)]

#[macro_use]
extern crate test_macros;
extern crate dollar_crate_external;

type S = u8;

macro_rules! m { () => {
#[print_attr]
struct A(identity!($crate::S));
}}

m!();

dollar_crate_external::issue_62325!();

fn main() {}
112 changes: 112 additions & 0 deletions src/test/ui/proc-macro/dollar-crate-issue-62325.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
PRINT-ATTR INPUT (DISPLAY): struct A(identity!(crate :: S));
PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ( identity ! ( crate :: S ) ) ;
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "struct",
span: #2 bytes(LO..HI),
},
Ident {
ident: "A",
span: #2 bytes(LO..HI),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "identity",
span: #2 bytes(LO..HI),
},
Punct {
ch: '!',
spacing: Alone,
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),
},
],
span: #2 bytes(LO..HI),
},
Punct {
ch: ';',
spacing: Alone,
span: #2 bytes(LO..HI),
},
]
PRINT-ATTR INPUT (DISPLAY): struct B(identity!(::dollar_crate_external :: S));
PRINT-ATTR RE-COLLECTED (DISPLAY): struct B ( identity ! ( ::dollar_crate_external :: S ) ) ;
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "struct",
span: #7 bytes(LO..HI),
},
Ident {
ident: "B",
span: #7 bytes(LO..HI),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "identity",
span: #7 bytes(LO..HI),
},
Punct {
ch: '!',
spacing: Alone,
span: #7 bytes(LO..HI),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "$crate",
span: #7 bytes(LO..HI),
},
Punct {
ch: ':',
spacing: Joint,
span: #7 bytes(LO..HI),
},
Punct {
ch: ':',
spacing: Alone,
span: #7 bytes(LO..HI),
},
Ident {
ident: "S",
span: #7 bytes(LO..HI),
},
],
span: #7 bytes(LO..HI),
},
],
span: #7 bytes(LO..HI),
},
Punct {
ch: ';',
spacing: Alone,
span: #7 bytes(LO..HI),
},
]
12 changes: 6 additions & 6 deletions src/test/ui/proc-macro/dollar-crate.stdout
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
PRINT-BANG INPUT (DISPLAY): struct M ( $crate :: S ) ;
PRINT-BANG INPUT (DISPLAY): struct M ( crate :: S ) ;
PRINT-BANG INPUT (DEBUG): TokenStream [
Ident {
ident: "struct",
@@ -39,7 +39,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
},
]
PRINT-ATTR INPUT (DISPLAY): struct A(crate::S);
PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ( $crate :: S ) ;
PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ( crate :: S ) ;
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "struct",
@@ -80,7 +80,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [
},
]
PRINT-DERIVE INPUT (DISPLAY): struct D(crate::S);
PRINT-DERIVE RE-COLLECTED (DISPLAY): struct D ( $crate :: S ) ;
PRINT-DERIVE RE-COLLECTED (DISPLAY): struct D ( crate :: S ) ;
PRINT-DERIVE INPUT (DEBUG): TokenStream [
Ident {
ident: "struct",
@@ -120,7 +120,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [
span: #2 bytes(LO..HI),
},
]
PRINT-BANG INPUT (DISPLAY): struct M ( $crate :: S ) ;
PRINT-BANG INPUT (DISPLAY): struct M ( ::dollar_crate_external :: S ) ;
PRINT-BANG INPUT (DEBUG): TokenStream [
Ident {
ident: "struct",
@@ -161,7 +161,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
},
]
PRINT-ATTR INPUT (DISPLAY): struct A(::dollar_crate_external::S);
PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ( $crate :: S ) ;
PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ( ::dollar_crate_external :: S ) ;
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "struct",
@@ -202,7 +202,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [
},
]
PRINT-DERIVE INPUT (DISPLAY): struct D(::dollar_crate_external::S);
PRINT-DERIVE RE-COLLECTED (DISPLAY): struct D ( $crate :: S ) ;
PRINT-DERIVE RE-COLLECTED (DISPLAY): struct D ( ::dollar_crate_external :: S ) ;
PRINT-DERIVE INPUT (DEBUG): TokenStream [
Ident {
ident: "struct",