Skip to content

Commit 78d2389

Browse files
committed
print raw lifetime idents with r#
1 parent e1b9081 commit 78d2389

File tree

7 files changed

+166
-48
lines changed

7 files changed

+166
-48
lines changed

compiler/rustc_ast/src/token.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub use NtPatKind::*;
77
pub use TokenKind::*;
88
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
99
use rustc_span::edition::Edition;
10+
use rustc_span::symbol::IdentPrintMode;
1011
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, kw, sym};
1112
#[allow(clippy::useless_attribute)] // FIXME: following use of `hidden_glob_reexports` incorrectly triggers `useless_attribute` lint.
1213
#[allow(hidden_glob_reexports)]
@@ -344,15 +345,24 @@ pub enum IdentIsRaw {
344345
Yes,
345346
}
346347

347-
impl From<bool> for IdentIsRaw {
348-
fn from(b: bool) -> Self {
349-
if b { Self::Yes } else { Self::No }
348+
impl IdentIsRaw {
349+
pub fn to_print_mode_ident(self) -> IdentPrintMode {
350+
match self {
351+
IdentIsRaw::No => IdentPrintMode::Normal,
352+
IdentIsRaw::Yes => IdentPrintMode::RawIdent,
353+
}
354+
}
355+
pub fn to_print_mode_lifetime(self) -> IdentPrintMode {
356+
match self {
357+
IdentIsRaw::No => IdentPrintMode::Normal,
358+
IdentIsRaw::Yes => IdentPrintMode::RawLifetime,
359+
}
350360
}
351361
}
352362

353-
impl From<IdentIsRaw> for bool {
354-
fn from(is_raw: IdentIsRaw) -> bool {
355-
matches!(is_raw, IdentIsRaw::Yes)
363+
impl From<bool> for IdentIsRaw {
364+
fn from(b: bool) -> Self {
365+
if b { Self::Yes } else { Self::No }
356366
}
357367
}
358368

compiler/rustc_ast_pretty/src/pprust/state.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::sync::Arc;
1111

1212
use rustc_ast::attr::AttrIdGenerator;
1313
use rustc_ast::ptr::P;
14-
use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Token, TokenKind};
14+
use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind};
1515
use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree};
1616
use rustc_ast::util::classify;
1717
use rustc_ast::util::comments::{Comment, CommentStyle};
@@ -442,7 +442,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
442442
fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool);
443443

444444
fn print_ident(&mut self, ident: Ident) {
445-
self.word(IdentPrinter::for_ast_ident(ident, ident.is_raw_guess()).to_string());
445+
self.word(IdentPrinter::for_ast_ident(ident, ident.guess_print_mode()).to_string());
446446
self.ann_post(ident)
447447
}
448448

@@ -1016,17 +1016,16 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
10161016

10171017
/* Name components */
10181018
token::Ident(name, is_raw) => {
1019-
IdentPrinter::new(name, is_raw.into(), convert_dollar_crate).to_string().into()
1019+
IdentPrinter::new(name, is_raw.to_print_mode_ident(), convert_dollar_crate)
1020+
.to_string()
1021+
.into()
10201022
}
10211023
token::NtIdent(ident, is_raw) => {
1022-
IdentPrinter::for_ast_ident(ident, is_raw.into()).to_string().into()
1024+
IdentPrinter::for_ast_ident(ident, is_raw.to_print_mode_ident()).to_string().into()
10231025
}
10241026

1025-
token::Lifetime(name, IdentIsRaw::No)
1026-
| token::NtLifetime(Ident { name, .. }, IdentIsRaw::No) => name.to_string().into(),
1027-
token::Lifetime(name, IdentIsRaw::Yes)
1028-
| token::NtLifetime(Ident { name, .. }, IdentIsRaw::Yes) => {
1029-
format!("'r#{}", &name.as_str()[1..]).into()
1027+
token::Lifetime(name, is_raw) | token::NtLifetime(Ident { name, .. }, is_raw) => {
1028+
IdentPrinter::new(name, is_raw.to_print_mode_lifetime(), None).to_string().into()
10301029
}
10311030

10321031
/* Other */

compiler/rustc_expand/src/proc_macro_server.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -250,20 +250,26 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre
250250
Question => op("?"),
251251
SingleQuote => op("'"),
252252

253-
Ident(sym, is_raw) => {
254-
trees.push(TokenTree::Ident(Ident { sym, is_raw: is_raw.into(), span }))
255-
}
253+
Ident(sym, is_raw) => trees.push(TokenTree::Ident(Ident {
254+
sym,
255+
is_raw: matches!(is_raw, IdentIsRaw::Yes),
256+
span,
257+
})),
256258
NtIdent(ident, is_raw) => trees.push(TokenTree::Ident(Ident {
257259
sym: ident.name,
258-
is_raw: is_raw.into(),
260+
is_raw: matches!(is_raw, IdentIsRaw::Yes),
259261
span: ident.span,
260262
})),
261263

262264
Lifetime(name, is_raw) => {
263265
let ident = rustc_span::Ident::new(name, span).without_first_quote();
264266
trees.extend([
265267
TokenTree::Punct(Punct { ch: b'\'', joint: true, span }),
266-
TokenTree::Ident(Ident { sym: ident.name, is_raw: is_raw.into(), span }),
268+
TokenTree::Ident(Ident {
269+
sym: ident.name,
270+
is_raw: matches!(is_raw, IdentIsRaw::Yes),
271+
span,
272+
}),
267273
]);
268274
}
269275
NtLifetime(ident, is_raw) => {

compiler/rustc_resolve/src/late/diagnostics.rs

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3112,7 +3112,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
31123112
} else {
31133113
self.suggest_introducing_lifetime(
31143114
&mut err,
3115-
Some(lifetime_ref.ident.name.as_str()),
3115+
Some(lifetime_ref.ident),
31163116
|err, _, span, message, suggestion, span_suggs| {
31173117
err.multipart_suggestion_verbose(
31183118
message,
@@ -3130,7 +3130,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
31303130
fn suggest_introducing_lifetime(
31313131
&self,
31323132
err: &mut Diag<'_>,
3133-
name: Option<&str>,
3133+
name: Option<Ident>,
31343134
suggest: impl Fn(
31353135
&mut Diag<'_>,
31363136
bool,
@@ -3177,7 +3177,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
31773177
let mut rm_inner_binders: FxIndexSet<Span> = Default::default();
31783178
let (span, sugg) = if span.is_empty() {
31793179
let mut binder_idents: FxIndexSet<Ident> = Default::default();
3180-
binder_idents.insert(Ident::from_str(name.unwrap_or("'a")));
3180+
binder_idents.insert(name.unwrap_or(Ident::from_str("'a")));
31813181

31823182
// We need to special case binders in the following situation:
31833183
// Change `T: for<'a> Trait<T> + 'b` to `for<'a, 'b> T: Trait<T> + 'b`
@@ -3207,16 +3207,11 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
32073207
}
32083208
}
32093209

3210-
let binders_sugg = binder_idents.into_iter().enumerate().fold(
3211-
"".to_string(),
3212-
|mut binders, (i, x)| {
3213-
if i != 0 {
3214-
binders += ", ";
3215-
}
3216-
binders += x.as_str();
3217-
binders
3218-
},
3219-
);
3210+
let binders_sugg: String = binder_idents
3211+
.into_iter()
3212+
.map(|ident| ident.to_string())
3213+
.intersperse(", ".to_owned())
3214+
.collect();
32203215
let sugg = format!(
32213216
"{}<{}>{}",
32223217
if higher_ranked { "for" } else { "" },
@@ -3232,15 +3227,16 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
32323227
.source_map()
32333228
.span_through_char(span, '<')
32343229
.shrink_to_hi();
3235-
let sugg = format!("{}, ", name.unwrap_or("'a"));
3230+
let sugg =
3231+
format!("{}, ", name.map(|i| i.to_string()).as_deref().unwrap_or("'a"));
32363232
(span, sugg)
32373233
};
32383234

32393235
if higher_ranked {
32403236
let message = Cow::from(format!(
32413237
"consider making the {} lifetime-generic with a new `{}` lifetime",
32423238
kind.descr(),
3243-
name.unwrap_or("'a"),
3239+
name.map(|i| i.to_string()).as_deref().unwrap_or("'a"),
32443240
));
32453241
should_continue = suggest(
32463242
err,

compiler/rustc_span/src/symbol.rs

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2505,10 +2505,16 @@ impl fmt::Debug for Ident {
25052505
/// except that AST identifiers don't keep the rawness flag, so we have to guess it.
25062506
impl fmt::Display for Ident {
25072507
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2508-
fmt::Display::fmt(&IdentPrinter::new(self.name, self.is_raw_guess(), None), f)
2508+
fmt::Display::fmt(&IdentPrinter::new(self.name, self.guess_print_mode(), None), f)
25092509
}
25102510
}
25112511

2512+
pub enum IdentPrintMode {
2513+
Normal,
2514+
RawIdent,
2515+
RawLifetime,
2516+
}
2517+
25122518
/// The most general type to print identifiers.
25132519
///
25142520
/// AST pretty-printer is used as a fallback for turning AST structures into token streams for
@@ -2524,40 +2530,59 @@ impl fmt::Display for Ident {
25242530
/// done for a token stream or a single token.
25252531
pub struct IdentPrinter {
25262532
symbol: Symbol,
2527-
is_raw: bool,
2533+
mode: IdentPrintMode,
25282534
/// Span used for retrieving the crate name to which `$crate` refers to,
25292535
/// if this field is `None` then the `$crate` conversion doesn't happen.
25302536
convert_dollar_crate: Option<Span>,
25312537
}
25322538

25332539
impl IdentPrinter {
25342540
/// The most general `IdentPrinter` constructor. Do not use this.
2535-
pub fn new(symbol: Symbol, is_raw: bool, convert_dollar_crate: Option<Span>) -> IdentPrinter {
2536-
IdentPrinter { symbol, is_raw, convert_dollar_crate }
2541+
pub fn new(
2542+
symbol: Symbol,
2543+
mode: IdentPrintMode,
2544+
convert_dollar_crate: Option<Span>,
2545+
) -> IdentPrinter {
2546+
IdentPrinter { symbol, mode, convert_dollar_crate }
25372547
}
25382548

25392549
/// This implementation is supposed to be used when printing identifiers
25402550
/// as a part of pretty-printing for larger AST pieces.
25412551
/// Do not use this either.
2542-
pub fn for_ast_ident(ident: Ident, is_raw: bool) -> IdentPrinter {
2543-
IdentPrinter::new(ident.name, is_raw, Some(ident.span))
2552+
pub fn for_ast_ident(ident: Ident, mode: IdentPrintMode) -> IdentPrinter {
2553+
IdentPrinter::new(ident.name, mode, Some(ident.span))
25442554
}
25452555
}
25462556

25472557
impl fmt::Display for IdentPrinter {
25482558
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2549-
if self.is_raw {
2550-
f.write_str("r#")?;
2551-
} else if self.symbol == kw::DollarCrate {
2552-
if let Some(span) = self.convert_dollar_crate {
2559+
let s = match self.mode {
2560+
IdentPrintMode::Normal
2561+
if self.symbol == kw::DollarCrate
2562+
&& let Some(span) = self.convert_dollar_crate =>
2563+
{
25532564
let converted = span.ctxt().dollar_crate_name();
25542565
if !converted.is_path_segment_keyword() {
25552566
f.write_str("::")?;
25562567
}
2557-
return fmt::Display::fmt(&converted, f);
2568+
converted
25582569
}
2559-
}
2560-
fmt::Display::fmt(&self.symbol, f)
2570+
IdentPrintMode::Normal => self.symbol,
2571+
IdentPrintMode::RawIdent => {
2572+
f.write_str("r#")?;
2573+
self.symbol
2574+
}
2575+
IdentPrintMode::RawLifetime => {
2576+
f.write_str("'r#")?;
2577+
let s = self
2578+
.symbol
2579+
.as_str()
2580+
.strip_prefix("'")
2581+
.expect("only lifetime idents should be passed with RawLifetime mode");
2582+
Symbol::intern(s)
2583+
}
2584+
};
2585+
s.fmt(f)
25612586
}
25622587
}
25632588

@@ -2949,6 +2974,25 @@ impl Ident {
29492974
self.name.can_be_raw() && self.is_reserved()
29502975
}
29512976

2977+
pub fn is_raw_lifetime_guess(self) -> bool {
2978+
// this should be kept consistent with `Parser::expect_lifetime` found under
2979+
// compiler/rustc_parse/src/parser/ty.rs
2980+
let name_without_apostrophe = self.without_first_quote();
2981+
name_without_apostrophe.name != self.name
2982+
&& ![kw::UnderscoreLifetime, kw::StaticLifetime].contains(&self.name)
2983+
&& name_without_apostrophe.is_raw_guess()
2984+
}
2985+
2986+
pub fn guess_print_mode(self) -> IdentPrintMode {
2987+
if self.is_raw_lifetime_guess() {
2988+
IdentPrintMode::RawLifetime
2989+
} else if self.is_raw_guess() {
2990+
IdentPrintMode::RawIdent
2991+
} else {
2992+
IdentPrintMode::Normal
2993+
}
2994+
}
2995+
29522996
/// Whether this would be the identifier for a tuple field like `self.0`, as
29532997
/// opposed to a named field like `self.thing`.
29542998
pub fn is_numeric(self) -> bool {
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Check that we properly suggest `r#fn` if we use it undeclared.
2+
// https://github.com/rust-lang/rust/issues/143150
3+
//
4+
//@ edition: 2021
5+
6+
fn a(_: dyn Trait + 'r#fn) {
7+
//~^ ERROR use of undeclared lifetime name `'r#fn` [E0261]
8+
}
9+
10+
trait Trait {}
11+
12+
struct Test {
13+
a: &'r#fn str,
14+
//~^ ERROR use of undeclared lifetime name `'r#fn` [E0261]
15+
}
16+
17+
trait Trait1<T>
18+
where T: for<'a> Trait1<T> + 'r#fn { }
19+
//~^ ERROR use of undeclared lifetime name `'r#fn` [E0261]
20+
21+
fn main() {}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
error[E0261]: use of undeclared lifetime name `'r#fn`
2+
--> $DIR/use-of-undeclared-raw-lifetimes.rs:6:21
3+
|
4+
LL | fn a(_: dyn Trait + 'r#fn) {
5+
| ^^^^^ undeclared lifetime
6+
|
7+
help: consider introducing lifetime `'r#fn` here
8+
|
9+
LL | fn a<'r#fn>(_: dyn Trait + 'r#fn) {
10+
| +++++++
11+
12+
error[E0261]: use of undeclared lifetime name `'r#fn`
13+
--> $DIR/use-of-undeclared-raw-lifetimes.rs:13:9
14+
|
15+
LL | a: &'r#fn str,
16+
| ^^^^^ undeclared lifetime
17+
|
18+
help: consider introducing lifetime `'r#fn` here
19+
|
20+
LL | struct Test<'r#fn> {
21+
| +++++++
22+
23+
error[E0261]: use of undeclared lifetime name `'r#fn`
24+
--> $DIR/use-of-undeclared-raw-lifetimes.rs:18:32
25+
|
26+
LL | where T: for<'a> Trait1<T> + 'r#fn { }
27+
| ^^^^^ undeclared lifetime
28+
|
29+
= note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
30+
help: consider making the bound lifetime-generic with a new `'r#fn` lifetime
31+
|
32+
LL - where T: for<'a> Trait1<T> + 'r#fn { }
33+
LL + where for<'r#fn, 'a> T: Trait1<T> + 'r#fn { }
34+
|
35+
help: consider introducing lifetime `'r#fn` here
36+
|
37+
LL | trait Trait1<'r#fn, T>
38+
| ++++++
39+
40+
error: aborting due to 3 previous errors
41+
42+
For more information about this error, try `rustc --explain E0261`.

0 commit comments

Comments
 (0)