Skip to content

Commit 4344a90

Browse files
committed
Pretty-print $crate as crate/::my_crate in tokens
...but only if those tokens are printed from inside of AST pretty-printing.
1 parent 7aaf0de commit 4344a90

File tree

3 files changed

+70
-55
lines changed

3 files changed

+70
-55
lines changed

src/libsyntax/print/pprust.rs

+62-47
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::tokenstream::{self, TokenStream, TokenTree};
1818

1919
use rustc_target::spec::abi::{self, Abi};
2020
use syntax_pos::{self, BytePos};
21-
use syntax_pos::{DUMMY_SP, FileName};
21+
use syntax_pos::{DUMMY_SP, FileName, Span};
2222

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

184+
fn ident_to_string(ident: ast::Ident, is_raw: bool) -> String {
185+
ident_to_string_ext(ident.name, is_raw, Some(ident.span))
186+
}
187+
188+
// AST pretty-printer is used as a fallback for turning AST structures into token streams for
189+
// proc macros. Additionally, proc macros may stringify their input and expect it survive the
190+
// stringification (especially true for proc macro derives written between Rust 1.15 and 1.30).
191+
// So we need to somehow pretty-print `$crate` in a way preserving at least some of its
192+
// hygiene data, most importantly name of the crate it refers to.
193+
// As a result we print `$crate` as `crate` if it refers to the local crate
194+
// and as `::other_crate_name` if it refers to some other crate.
195+
// Note, that this is only done if the ident token is printed from inside of AST pretty-pringing,
196+
// but not otherwise. Pretty-printing is the only way for proc macros to discover token contents,
197+
// so we should not perform this lossy conversion if the top level call to the pretty-printer was
198+
// done for a token stream or a single token.
199+
fn ident_to_string_ext(
200+
name: ast::Name, is_raw: bool, convert_dollar_crate: Option<Span>
201+
) -> String {
202+
if is_raw {
203+
format!("r#{}", name)
204+
} else {
205+
if name == kw::DollarCrate {
206+
if let Some(span) = convert_dollar_crate {
207+
let converted = span.ctxt().dollar_crate_name();
208+
return if converted.is_path_segment_keyword() {
209+
converted.to_string()
210+
} else {
211+
format!("::{}", converted)
212+
}
213+
}
214+
}
215+
name.to_string()
216+
}
217+
}
218+
184219
pub fn token_kind_to_string(tok: &TokenKind) -> String {
220+
token_kind_to_string_ext(tok, None)
221+
}
222+
223+
fn token_kind_to_string_ext(tok: &TokenKind, convert_dollar_crate: Option<Span>) -> String {
185224
match *tok {
186225
token::Eq => "=".to_string(),
187226
token::Lt => "<".to_string(),
@@ -227,8 +266,7 @@ pub fn token_kind_to_string(tok: &TokenKind) -> String {
227266
token::Literal(lit) => literal_to_string(lit),
228267

229268
/* Name components */
230-
token::Ident(s, false) => s.to_string(),
231-
token::Ident(s, true) => format!("r#{}", s),
269+
token::Ident(s, is_raw) => ident_to_string_ext(s, is_raw, convert_dollar_crate),
232270
token::Lifetime(s) => s.to_string(),
233271

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

245283
pub fn token_to_string(token: &Token) -> String {
246-
token_kind_to_string(&token.kind)
284+
token_to_string_ext(token, false)
285+
}
286+
287+
fn token_to_string_ext(token: &Token, convert_dollar_crate: bool) -> String {
288+
let convert_dollar_crate = if convert_dollar_crate { Some(token.span) } else { None };
289+
token_kind_to_string_ext(&token.kind, convert_dollar_crate)
247290
}
248291

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

295337
pub fn tt_to_string(tt: tokenstream::TokenTree) -> String {
296-
to_string(|s| s.print_tt(tt))
338+
to_string(|s| s.print_tt(tt, false))
297339
}
298340

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

303345
pub fn tokens_to_string(tokens: TokenStream) -> String {
304-
to_string(|s| s.print_tts(tokens))
346+
to_string(|s| s.print_tts_ext(tokens, false))
305347
}
306348

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

347-
pub fn ident_to_string(id: ast::Ident) -> String {
348-
to_string(|s| s.print_ident(id))
349-
}
350-
351389
pub fn vis_to_string(v: &ast::Visibility) -> String {
352390
to_string(|s| s.print_visibility(v))
353391
}
@@ -629,11 +667,7 @@ pub trait PrintState<'a> {
629667
self.writer().word("::");
630668
}
631669
if segment.ident.name != kw::PathRoot {
632-
if segment.ident.name == kw::DollarCrate {
633-
self.print_dollar_crate(segment.ident);
634-
} else {
635-
self.writer().word(segment.ident.as_str().to_string());
636-
}
670+
self.writer().word(ident_to_string(segment.ident, segment.ident.is_raw_guess()));
637671
}
638672
}
639673
}
@@ -707,10 +741,10 @@ pub trait PrintState<'a> {
707741
/// appropriate macro, transcribe back into the grammar we just parsed from,
708742
/// and then pretty-print the resulting AST nodes (so, e.g., we print
709743
/// expression arguments as expressions). It can be done! I think.
710-
fn print_tt(&mut self, tt: tokenstream::TokenTree) {
744+
fn print_tt(&mut self, tt: tokenstream::TokenTree, convert_dollar_crate: bool) {
711745
match tt {
712746
TokenTree::Token(ref token) => {
713-
self.writer().word(token_to_string(&token));
747+
self.writer().word(token_to_string_ext(&token, convert_dollar_crate));
714748
match token.kind {
715749
token::DocComment(..) => {
716750
self.writer().hardbreak()
@@ -729,12 +763,16 @@ pub trait PrintState<'a> {
729763
}
730764

731765
fn print_tts(&mut self, tts: tokenstream::TokenStream) {
766+
self.print_tts_ext(tts, true)
767+
}
768+
769+
fn print_tts_ext(&mut self, tts: tokenstream::TokenStream, convert_dollar_crate: bool) {
732770
self.ibox(0);
733771
for (i, tt) in tts.into_trees().enumerate() {
734772
if i != 0 {
735773
self.writer().space();
736774
}
737-
self.print_tt(tt);
775+
self.print_tt(tt, convert_dollar_crate);
738776
}
739777
self.end();
740778
}
@@ -744,21 +782,6 @@ pub trait PrintState<'a> {
744782
}
745783

746784
fn nbsp(&mut self) { self.writer().word(" ") }
747-
748-
// AST pretty-printer is used as a fallback for turning AST structures into token streams for
749-
// proc macros. Additionally, proc macros may stringify their input and expect it survive the
750-
// stringification (especially true for proc macro derives written between Rust 1.15 and 1.30).
751-
// So we need to somehow pretty-print `$crate` in paths in a way preserving at least some of
752-
// its hygiene data, most importantly name of the crate it refers to.
753-
// As a result we print `$crate` as `crate` if it refers to the local crate
754-
// and as `::other_crate_name` if it refers to some other crate.
755-
fn print_dollar_crate(&mut self, ident: ast::Ident) {
756-
let name = ident.span.ctxt().dollar_crate_name();
757-
if !ast::Ident::with_empty_ctxt(name).is_path_segment_keyword() {
758-
self.writer().word("::");
759-
}
760-
self.writer().word(name.as_str().to_string())
761-
}
762785
}
763786

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

22892312
crate fn print_ident(&mut self, ident: ast::Ident) {
2290-
if ident.is_raw_guess() {
2291-
self.s.word(format!("r#{}", ident));
2292-
} else {
2293-
self.s.word(ident.as_str().to_string());
2294-
}
2313+
self.s.word(ident_to_string(ident, ident.is_raw_guess()));
22952314
self.ann.post(self, AnnNode::Ident(&ident))
22962315
}
22972316

@@ -2322,11 +2341,7 @@ impl<'a> State<'a> {
23222341
segment: &ast::PathSegment,
23232342
colons_before_params: bool) {
23242343
if segment.ident.name != kw::PathRoot {
2325-
if segment.ident.name == kw::DollarCrate {
2326-
self.print_dollar_crate(segment.ident);
2327-
} else {
2328-
self.print_ident(segment.ident);
2329-
}
2344+
self.print_ident(segment.ident);
23302345
if let Some(ref args) = segment.args {
23312346
self.print_generic_args(args, colons_before_params);
23322347
}

src/test/ui/proc-macro/dollar-crate-issue-57089.stdout

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
PRINT-BANG INPUT (DISPLAY): struct M ( $crate :: S ) ;
1+
PRINT-BANG INPUT (DISPLAY): struct M ( crate :: S ) ;
22
PRINT-BANG INPUT (DEBUG): TokenStream [
33
Ident {
44
ident: "struct",
@@ -39,7 +39,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
3939
},
4040
]
4141
PRINT-ATTR INPUT (DISPLAY): struct A(crate::S);
42-
PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ( $crate :: S ) ;
42+
PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ( crate :: S ) ;
4343
PRINT-ATTR INPUT (DEBUG): TokenStream [
4444
Ident {
4545
ident: "struct",

src/test/ui/proc-macro/dollar-crate.stdout

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
PRINT-BANG INPUT (DISPLAY): struct M ( $crate :: S ) ;
1+
PRINT-BANG INPUT (DISPLAY): struct M ( crate :: S ) ;
22
PRINT-BANG INPUT (DEBUG): TokenStream [
33
Ident {
44
ident: "struct",
@@ -39,7 +39,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
3939
},
4040
]
4141
PRINT-ATTR INPUT (DISPLAY): struct A(crate::S);
42-
PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ( $crate :: S ) ;
42+
PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ( crate :: S ) ;
4343
PRINT-ATTR INPUT (DEBUG): TokenStream [
4444
Ident {
4545
ident: "struct",
@@ -80,7 +80,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [
8080
},
8181
]
8282
PRINT-DERIVE INPUT (DISPLAY): struct D(crate::S);
83-
PRINT-DERIVE RE-COLLECTED (DISPLAY): struct D ( $crate :: S ) ;
83+
PRINT-DERIVE RE-COLLECTED (DISPLAY): struct D ( crate :: S ) ;
8484
PRINT-DERIVE INPUT (DEBUG): TokenStream [
8585
Ident {
8686
ident: "struct",
@@ -120,7 +120,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [
120120
span: #2 bytes(LO..HI),
121121
},
122122
]
123-
PRINT-BANG INPUT (DISPLAY): struct M ( $crate :: S ) ;
123+
PRINT-BANG INPUT (DISPLAY): struct M ( ::dollar_crate_external :: S ) ;
124124
PRINT-BANG INPUT (DEBUG): TokenStream [
125125
Ident {
126126
ident: "struct",
@@ -161,7 +161,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
161161
},
162162
]
163163
PRINT-ATTR INPUT (DISPLAY): struct A(::dollar_crate_external::S);
164-
PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ( $crate :: S ) ;
164+
PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ( ::dollar_crate_external :: S ) ;
165165
PRINT-ATTR INPUT (DEBUG): TokenStream [
166166
Ident {
167167
ident: "struct",
@@ -202,7 +202,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [
202202
},
203203
]
204204
PRINT-DERIVE INPUT (DISPLAY): struct D(::dollar_crate_external::S);
205-
PRINT-DERIVE RE-COLLECTED (DISPLAY): struct D ( $crate :: S ) ;
205+
PRINT-DERIVE RE-COLLECTED (DISPLAY): struct D ( ::dollar_crate_external :: S ) ;
206206
PRINT-DERIVE INPUT (DEBUG): TokenStream [
207207
Ident {
208208
ident: "struct",

0 commit comments

Comments
 (0)