Skip to content

Commit 69c6628

Browse files
committed
Reintroduce special pretty-printing for $crate when it's necessary for proc macros
1 parent 2bc67da commit 69c6628

File tree

9 files changed

+403
-14
lines changed

9 files changed

+403
-14
lines changed

src/librustc_resolve/build_reduced_graph.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,4 +1035,15 @@ impl<'a, 'b> Visitor<'a> for BuildReducedGraphVisitor<'a, 'b> {
10351035
}
10361036
visit::walk_attribute(self, attr);
10371037
}
1038+
1039+
fn visit_ident(&mut self, ident: Ident) {
1040+
if ident.name == keywords::DollarCrate.name() {
1041+
let name = match self.resolver.resolve_crate_root(ident).kind {
1042+
ModuleKind::Def(_, name) if name != keywords::Invalid.name() => name,
1043+
_ => keywords::Crate.name(),
1044+
};
1045+
ident.span.ctxt().set_dollar_crate_name(name);
1046+
}
1047+
visit::walk_ident(self, ident);
1048+
}
10381049
}

src/libsyntax/print/pprust.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -724,7 +724,11 @@ pub trait PrintState<'a> {
724724
self.writer().word("::")?
725725
}
726726
if segment.ident.name != keywords::PathRoot.name() {
727-
self.writer().word(segment.ident.as_str().get())?;
727+
if segment.ident.name == keywords::DollarCrate.name() {
728+
self.print_dollar_crate(segment.ident)?;
729+
} else {
730+
self.writer().word(segment.ident.as_str().get())?;
731+
}
728732
}
729733
}
730734
Ok(())
@@ -837,6 +841,21 @@ pub trait PrintState<'a> {
837841
}
838842

839843
fn nbsp(&mut self) -> io::Result<()> { self.writer().word(" ") }
844+
845+
// AST pretty-printer is used as a fallback for turning AST structures into token streams for
846+
// proc macros. Additionally, proc macros may stringify their input and expect it survive the
847+
// stringification (especially true for proc macro derives written between Rust 1.15 and 1.30).
848+
// So we need to somehow pretty-print `$crate` in paths in a way preserving at least some of
849+
// its hygiene data, most importantly name of the crate it refers to.
850+
// As a result we print `$crate` as `crate` if it refers to the local crate
851+
// and as `::other_crate_name` if it refers to some other crate.
852+
fn print_dollar_crate(&mut self, ident: ast::Ident) -> io::Result<()> {
853+
let name = ident.span.ctxt().dollar_crate_name();
854+
if !ast::Ident::with_empty_ctxt(name).is_path_segment_keyword() {
855+
self.writer().word("::")?;
856+
}
857+
self.writer().word(name.as_str().get())
858+
}
840859
}
841860

842861
impl<'a> PrintState<'a> for State<'a> {
@@ -2446,7 +2465,11 @@ impl<'a> State<'a> {
24462465
-> io::Result<()>
24472466
{
24482467
if segment.ident.name != keywords::PathRoot.name() {
2449-
self.print_ident(segment.ident)?;
2468+
if segment.ident.name == keywords::DollarCrate.name() {
2469+
self.print_dollar_crate(segment.ident)?;
2470+
} else {
2471+
self.print_ident(segment.ident)?;
2472+
}
24502473
if let Some(ref args) = segment.args {
24512474
self.print_generic_args(args, colons_before_params)?;
24522475
}

src/libsyntax_pos/hygiene.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@
1818
use GLOBALS;
1919
use Span;
2020
use edition::{Edition, DEFAULT_EDITION};
21-
use symbol::Symbol;
21+
use symbol::{keywords, Symbol};
2222

2323
use serialize::{Encodable, Decodable, Encoder, Decoder};
2424
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
25-
use std::fmt;
25+
use std::{fmt, mem};
2626

2727
/// A SyntaxContext represents a chain of macro expansions (represented by marks).
2828
#[derive(Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord, Hash)]
@@ -37,6 +37,8 @@ struct SyntaxContextData {
3737
opaque: SyntaxContext,
3838
// This context, but with all transparent marks filtered away.
3939
opaque_and_semitransparent: SyntaxContext,
40+
// Name of the crate to which `$crate` with this context would resolve.
41+
dollar_crate_name: Symbol,
4042
}
4143

4244
/// A mark is a unique id associated with a macro expansion.
@@ -200,6 +202,7 @@ impl HygieneData {
200202
prev_ctxt: SyntaxContext(0),
201203
opaque: SyntaxContext(0),
202204
opaque_and_semitransparent: SyntaxContext(0),
205+
dollar_crate_name: keywords::DollarCrate.name(),
203206
}],
204207
markings: FxHashMap::default(),
205208
default_edition: DEFAULT_EDITION,
@@ -258,6 +261,7 @@ impl SyntaxContext {
258261
prev_ctxt: SyntaxContext::empty(),
259262
opaque: SyntaxContext::empty(),
260263
opaque_and_semitransparent: SyntaxContext::empty(),
264+
dollar_crate_name: keywords::DollarCrate.name(),
261265
});
262266
SyntaxContext(data.syntax_contexts.len() as u32 - 1)
263267
})
@@ -324,6 +328,7 @@ impl SyntaxContext {
324328
prev_ctxt,
325329
opaque: new_opaque,
326330
opaque_and_semitransparent: new_opaque,
331+
dollar_crate_name: keywords::DollarCrate.name(),
327332
});
328333
new_opaque
329334
});
@@ -341,6 +346,7 @@ impl SyntaxContext {
341346
prev_ctxt,
342347
opaque,
343348
opaque_and_semitransparent: new_opaque_and_semitransparent,
349+
dollar_crate_name: keywords::DollarCrate.name(),
344350
});
345351
new_opaque_and_semitransparent
346352
});
@@ -356,6 +362,7 @@ impl SyntaxContext {
356362
prev_ctxt,
357363
opaque,
358364
opaque_and_semitransparent,
365+
dollar_crate_name: keywords::DollarCrate.name(),
359366
});
360367
new_opaque_and_semitransparent_and_transparent
361368
})
@@ -510,6 +517,21 @@ impl SyntaxContext {
510517
pub fn outer(self) -> Mark {
511518
HygieneData::with(|data| data.syntax_contexts[self.0 as usize].outer_mark)
512519
}
520+
521+
pub fn dollar_crate_name(self) -> Symbol {
522+
HygieneData::with(|data| data.syntax_contexts[self.0 as usize].dollar_crate_name)
523+
}
524+
525+
pub fn set_dollar_crate_name(self, dollar_crate_name: Symbol) {
526+
HygieneData::with(|data| {
527+
let prev_dollar_crate_name = mem::replace(
528+
&mut data.syntax_contexts[self.0 as usize].dollar_crate_name, dollar_crate_name
529+
);
530+
assert!(dollar_crate_name == prev_dollar_crate_name ||
531+
prev_dollar_crate_name == keywords::DollarCrate.name(),
532+
"$crate name is reset for a syntax context");
533+
})
534+
}
513535
}
514536

515537
impl fmt::Debug for SyntaxContext {

src/test/pretty/issue-4264.pp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@
3939

4040

4141

42-
((::fmt::format as
43-
for<'r> fn(std::fmt::Arguments<'r>) -> std::string::String {std::fmt::format})(((<::fmt::Arguments>::new_v1
42+
(($crate::fmt::format as
43+
for<'r> fn(std::fmt::Arguments<'r>) -> std::string::String {std::fmt::format})(((<$crate::fmt::Arguments>::new_v1
4444
as
4545
fn(&[&str], &[std::fmt::ArgumentV1<'_>]) -> std::fmt::Arguments<'_> {std::fmt::Arguments<'_>::new_v1})((&([("test"
4646
as
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
pub type S = u8;
2+
3+
#[macro_export]
4+
macro_rules! external {
5+
() => {
6+
dollar_crate::m! {
7+
struct M($crate::S);
8+
}
9+
10+
#[dollar_crate::a]
11+
struct A($crate::S);
12+
13+
#[derive(dollar_crate::d)]
14+
struct D($crate::S);
15+
};
16+
}

src/test/ui/proc-macro/auxiliary/dollar-crate.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,22 @@ extern crate proc_macro;
77
use proc_macro::TokenStream;
88

99
#[proc_macro]
10-
pub fn normalize(input: TokenStream) -> TokenStream {
10+
pub fn m(input: TokenStream) -> TokenStream {
11+
println!("PROC MACRO INPUT (PRETTY-PRINTED): {}", input);
12+
println!("PROC MACRO INPUT: {:#?}", input);
13+
input.into_iter().collect()
14+
}
15+
16+
#[proc_macro_attribute]
17+
pub fn a(_args: TokenStream, input: TokenStream) -> TokenStream {
18+
println!("ATTRIBUTE INPUT (PRETTY-PRINTED): {}", input);
19+
println!("ATTRIBUTE INPUT: {:#?}", input);
20+
input.into_iter().collect()
21+
}
22+
23+
#[proc_macro_derive(d)]
24+
pub fn d(input: TokenStream) -> TokenStream {
25+
println!("DERIVE INPUT (PRETTY-PRINTED): {}", input);
26+
println!("DERIVE INPUT: {:#?}", input);
1127
input.into_iter().collect()
1228
}
Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,34 @@
1-
// compile-pass
1+
// edition:2018
2+
// compile-flags:--extern dollar_crate --extern dollar_crate_external
23
// aux-build:dollar-crate.rs
4+
// aux-build:dollar-crate-external.rs
35

4-
extern crate dollar_crate;
6+
// Anonymize unstable non-dummy spans while still showing dummy spans `0..0`.
7+
// normalize-stdout-test "bytes\([^0]\w*\.\.(\w+)\)" -> "bytes(LO..$1)"
8+
// normalize-stdout-test "bytes\((\w+)\.\.[^0]\w*\)" -> "bytes($1..HI)"
59

610
type S = u8;
711

8-
macro_rules! check { () => {
9-
dollar_crate::normalize! {
10-
type A = $crate::S;
12+
mod local {
13+
macro_rules! local {
14+
() => {
15+
dollar_crate::m! {
16+
struct M($crate::S);
17+
}
18+
19+
#[dollar_crate::a]
20+
struct A($crate::S);
21+
22+
#[derive(dollar_crate::d)]
23+
struct D($crate::S); //~ ERROR the name `D` is defined multiple times
24+
};
1125
}
12-
}}
1326

14-
check!();
27+
local!();
28+
}
29+
30+
mod external {
31+
dollar_crate_external::external!(); //~ ERROR the name `D` is defined multiple times
32+
}
1533

1634
fn main() {}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error[E0428]: the name `D` is defined multiple times
2+
--> $DIR/dollar-crate.rs:23:13
3+
|
4+
LL | struct D($crate::S); //~ ERROR the name `D` is defined multiple times
5+
| ^^^^^^^^^^^^^^^^^^^^ `D` redefined here
6+
...
7+
LL | local!();
8+
| --------- in this macro invocation
9+
|
10+
= note: `D` must be defined only once in the type namespace of this module
11+
12+
error[E0428]: the name `D` is defined multiple times
13+
--> $DIR/dollar-crate.rs:31:5
14+
|
15+
LL | dollar_crate_external::external!(); //~ ERROR the name `D` is defined multiple times
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `D` redefined here
17+
|
18+
= note: `D` must be defined only once in the type namespace of this module
19+
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
20+
21+
error: aborting due to 2 previous errors
22+
23+
For more information about this error, try `rustc --explain E0428`.

0 commit comments

Comments
 (0)