Reintroduce special pretty-printing for $crate when it's necessary for proc macros

This commit is contained in:
Vadim Petrochenkov 2018-12-09 17:46:12 +03:00
parent 2bc67da378
commit 69c66286a9
9 changed files with 404 additions and 15 deletions

View file

@ -1035,4 +1035,15 @@ fn visit_attribute(&mut self, attr: &'a ast::Attribute) {
}
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);
}
}

View file

@ -724,7 +724,11 @@ fn print_attribute_path(&mut self, path: &ast::Path) -> io::Result<()> {
self.writer().word("::")?
}
if segment.ident.name != keywords::PathRoot.name() {
self.writer().word(segment.ident.as_str().get())?;
if segment.ident.name == keywords::DollarCrate.name() {
self.print_dollar_crate(segment.ident)?;
} else {
self.writer().word(segment.ident.as_str().get())?;
}
}
}
Ok(())
@ -837,6 +841,21 @@ fn space_if_not_bol(&mut self) -> io::Result<()> {
}
fn nbsp(&mut self) -> io::Result<()> { 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) -> io::Result<()> {
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().get())
}
}
impl<'a> PrintState<'a> for State<'a> {
@ -2446,7 +2465,11 @@ fn print_path_segment(&mut self,
-> io::Result<()>
{
if segment.ident.name != keywords::PathRoot.name() {
self.print_ident(segment.ident)?;
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)?;
}

View file

@ -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.
@ -200,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,
@ -258,6 +261,7 @@ pub fn allocate_directly(expansion_info: ExpnInfo) -> Self {
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)
})
@ -324,6 +328,7 @@ fn apply_mark_internal(self, mark: Mark, transparency: Transparency) -> SyntaxCo
prev_ctxt,
opaque: new_opaque,
opaque_and_semitransparent: new_opaque,
dollar_crate_name: keywords::DollarCrate.name(),
});
new_opaque
});
@ -341,6 +346,7 @@ fn apply_mark_internal(self, mark: Mark, transparency: Transparency) -> SyntaxCo
prev_ctxt,
opaque,
opaque_and_semitransparent: new_opaque_and_semitransparent,
dollar_crate_name: keywords::DollarCrate.name(),
});
new_opaque_and_semitransparent
});
@ -356,6 +362,7 @@ fn apply_mark_internal(self, mark: Mark, transparency: Transparency) -> SyntaxCo
prev_ctxt,
opaque,
opaque_and_semitransparent,
dollar_crate_name: keywords::DollarCrate.name(),
});
new_opaque_and_semitransparent_and_transparent
})
@ -510,6 +517,21 @@ pub fn modern_and_legacy(self) -> 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 {

View file

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

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

View file

@ -7,6 +7,22 @@
use proc_macro::TokenStream;
#[proc_macro]
pub fn normalize(input: TokenStream) -> TokenStream {
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()
}

View file

@ -1,16 +1,34 @@
// compile-pass
// edition:2018
// compile-flags:--extern dollar_crate --extern dollar_crate_external
// aux-build:dollar-crate.rs
// aux-build:dollar-crate-external.rs
extern crate dollar_crate;
// 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)"
type S = u8;
macro_rules! check { () => {
dollar_crate::normalize! {
type A = $crate::S;
}
}}
mod local {
macro_rules! local {
() => {
dollar_crate::m! {
struct M($crate::S);
}
check!();
#[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 {
dollar_crate_external::external!(); //~ ERROR the name `D` is defined multiple times
}
fn main() {}

View file

@ -0,0 +1,23 @@
error[E0428]: the name `D` is defined multiple times
--> $DIR/dollar-crate.rs:23:13
|
LL | struct D($crate::S); //~ ERROR the name `D` is defined multiple times
| ^^^^^^^^^^^^^^^^^^^^ `D` redefined 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:31:5
|
LL | dollar_crate_external::external!(); //~ ERROR the name `D` is defined multiple times
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `D` redefined 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`.

View file

@ -0,0 +1,260 @@
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: #0 bytes(0..0)
},
Ident {
ident: "A",
span: #0 bytes(0..0)
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "crate",
span: #0 bytes(0..0)
},
Punct {
ch: ':',
spacing: Joint,
span: #0 bytes(0..0)
},
Punct {
ch: ':',
spacing: Alone,
span: #0 bytes(0..0)
},
Ident {
ident: "S",
span: #0 bytes(0..0)
}
],
span: #0 bytes(0..0)
},
Punct {
ch: ';',
spacing: Alone,
span: #0 bytes(0..0)
}
]
DERIVE INPUT (PRETTY-PRINTED): struct D(crate::S);
DERIVE INPUT: TokenStream [
Ident {
ident: "struct",
span: #0 bytes(0..0)
},
Ident {
ident: "D",
span: #0 bytes(0..0)
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "crate",
span: #0 bytes(0..0)
},
Punct {
ch: ':',
spacing: Joint,
span: #0 bytes(0..0)
},
Punct {
ch: ':',
spacing: Alone,
span: #0 bytes(0..0)
},
Ident {
ident: "S",
span: #0 bytes(0..0)
}
],
span: #0 bytes(0..0)
},
Punct {
ch: ';',
spacing: Alone,
span: #0 bytes(0..0)
}
]
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: #0 bytes(0..0)
},
Ident {
ident: "A",
span: #0 bytes(0..0)
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Punct {
ch: ':',
spacing: Joint,
span: #0 bytes(0..0)
},
Punct {
ch: ':',
spacing: Alone,
span: #0 bytes(0..0)
},
Ident {
ident: "dollar_crate_external",
span: #0 bytes(0..0)
},
Punct {
ch: ':',
spacing: Joint,
span: #0 bytes(0..0)
},
Punct {
ch: ':',
spacing: Alone,
span: #0 bytes(0..0)
},
Ident {
ident: "S",
span: #0 bytes(0..0)
}
],
span: #0 bytes(0..0)
},
Punct {
ch: ';',
spacing: Alone,
span: #0 bytes(0..0)
}
]
DERIVE INPUT (PRETTY-PRINTED): struct D(::dollar_crate_external::S);
DERIVE INPUT: TokenStream [
Ident {
ident: "struct",
span: #0 bytes(0..0)
},
Ident {
ident: "D",
span: #0 bytes(0..0)
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Punct {
ch: ':',
spacing: Joint,
span: #0 bytes(0..0)
},
Punct {
ch: ':',
spacing: Alone,
span: #0 bytes(0..0)
},
Ident {
ident: "dollar_crate_external",
span: #0 bytes(0..0)
},
Punct {
ch: ':',
spacing: Joint,
span: #0 bytes(0..0)
},
Punct {
ch: ':',
spacing: Alone,
span: #0 bytes(0..0)
},
Ident {
ident: "S",
span: #0 bytes(0..0)
}
],
span: #0 bytes(0..0)
},
Punct {
ch: ';',
spacing: Alone,
span: #0 bytes(0..0)
}
]