about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJacob Pratt <jacob@jhpratt.dev>2025-08-22 22:00:47 -0400
committerGitHub <noreply@github.com>2025-08-22 22:00:47 -0400
commit2dbd411d229ee382b1cf2ec3af84cd581d56d521 (patch)
tree6b1323a95a2e0d5e1cf13638ca3ed674906e25fb
parent2bd39222cdb9b6bb182c4de44cde99048942683b (diff)
parent4970127c33a2cfd7b7035daedba7cac512a2e201 (diff)
downloadrust-2dbd411d229ee382b1cf2ec3af84cd581d56d521.tar.gz
rust-2dbd411d229ee382b1cf2ec3af84cd581d56d521.zip
Rollup merge of #144897 - fee1-dead-contrib:raw_lifetimes_printing, r=fmease
print raw lifetime idents with r#

This replaces rust-lang/rust#143185 and fixes rust-lang/rust#143150

cc ``@fmease``
-rw-r--r--compiler/rustc_ast/src/token.rs22
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs17
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs16
-rw-r--r--compiler/rustc_parse/messages.ftl5
-rw-r--r--compiler/rustc_parse/src/errors.rs5
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs2
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs3
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs26
-rw-r--r--compiler/rustc_span/src/symbol.rs74
-rw-r--r--tests/ui/closures/issue-52437.rs2
-rw-r--r--tests/ui/closures/issue-52437.stderr2
-rw-r--r--tests/ui/issues/issue-46311.rs2
-rw-r--r--tests/ui/issues/issue-46311.stderr2
-rw-r--r--tests/ui/label/label-static.rs4
-rw-r--r--tests/ui/label/label-static.stderr4
-rw-r--r--tests/ui/label/label-underscore.rs4
-rw-r--r--tests/ui/label/label-underscore.stderr4
-rw-r--r--tests/ui/lifetimes/raw/use-of-undeclared-raw-lifetimes.rs21
-rw-r--r--tests/ui/lifetimes/raw/use-of-undeclared-raw-lifetimes.stderr42
-rw-r--r--tests/ui/parser/require-parens-for-chained-comparison.rs4
-rw-r--r--tests/ui/parser/require-parens-for-chained-comparison.stderr4
21 files changed, 192 insertions, 73 deletions
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index fc816f2cb79..ea98bebd305 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -7,6 +7,7 @@ pub use NtPatKind::*;
 pub use TokenKind::*;
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
 use rustc_span::edition::Edition;
+use rustc_span::symbol::IdentPrintMode;
 use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, kw, sym};
 #[allow(clippy::useless_attribute)] // FIXME: following use of `hidden_glob_reexports` incorrectly triggers `useless_attribute` lint.
 #[allow(hidden_glob_reexports)]
@@ -344,15 +345,24 @@ pub enum IdentIsRaw {
     Yes,
 }
 
-impl From<bool> for IdentIsRaw {
-    fn from(b: bool) -> Self {
-        if b { Self::Yes } else { Self::No }
+impl IdentIsRaw {
+    pub fn to_print_mode_ident(self) -> IdentPrintMode {
+        match self {
+            IdentIsRaw::No => IdentPrintMode::Normal,
+            IdentIsRaw::Yes => IdentPrintMode::RawIdent,
+        }
+    }
+    pub fn to_print_mode_lifetime(self) -> IdentPrintMode {
+        match self {
+            IdentIsRaw::No => IdentPrintMode::Normal,
+            IdentIsRaw::Yes => IdentPrintMode::RawLifetime,
+        }
     }
 }
 
-impl From<IdentIsRaw> for bool {
-    fn from(is_raw: IdentIsRaw) -> bool {
-        matches!(is_raw, IdentIsRaw::Yes)
+impl From<bool> for IdentIsRaw {
+    fn from(b: bool) -> Self {
+        if b { Self::Yes } else { Self::No }
     }
 }
 
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 85f76036df7..a056ce3e29d 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -10,7 +10,7 @@ use std::borrow::Cow;
 use std::sync::Arc;
 
 use rustc_ast::attr::AttrIdGenerator;
-use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Token, TokenKind};
+use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind};
 use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree};
 use rustc_ast::util::classify;
 use rustc_ast::util::comments::{Comment, CommentStyle};
@@ -441,7 +441,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
     fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool);
 
     fn print_ident(&mut self, ident: Ident) {
-        self.word(IdentPrinter::for_ast_ident(ident, ident.is_raw_guess()).to_string());
+        self.word(IdentPrinter::for_ast_ident(ident, ident.guess_print_mode()).to_string());
         self.ann_post(ident)
     }
 
@@ -1015,17 +1015,16 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
 
             /* Name components */
             token::Ident(name, is_raw) => {
-                IdentPrinter::new(name, is_raw.into(), convert_dollar_crate).to_string().into()
+                IdentPrinter::new(name, is_raw.to_print_mode_ident(), convert_dollar_crate)
+                    .to_string()
+                    .into()
             }
             token::NtIdent(ident, is_raw) => {
-                IdentPrinter::for_ast_ident(ident, is_raw.into()).to_string().into()
+                IdentPrinter::for_ast_ident(ident, is_raw.to_print_mode_ident()).to_string().into()
             }
 
-            token::Lifetime(name, IdentIsRaw::No)
-            | token::NtLifetime(Ident { name, .. }, IdentIsRaw::No) => name.to_string().into(),
-            token::Lifetime(name, IdentIsRaw::Yes)
-            | token::NtLifetime(Ident { name, .. }, IdentIsRaw::Yes) => {
-                format!("'r#{}", &name.as_str()[1..]).into()
+            token::Lifetime(name, is_raw) | token::NtLifetime(Ident { name, .. }, is_raw) => {
+                IdentPrinter::new(name, is_raw.to_print_mode_lifetime(), None).to_string().into()
             }
 
             /* Other */
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index fd71f2ce948..5b1d3d6d35b 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -250,12 +250,14 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre
                 Question => op("?"),
                 SingleQuote => op("'"),
 
-                Ident(sym, is_raw) => {
-                    trees.push(TokenTree::Ident(Ident { sym, is_raw: is_raw.into(), span }))
-                }
+                Ident(sym, is_raw) => trees.push(TokenTree::Ident(Ident {
+                    sym,
+                    is_raw: matches!(is_raw, IdentIsRaw::Yes),
+                    span,
+                })),
                 NtIdent(ident, is_raw) => trees.push(TokenTree::Ident(Ident {
                     sym: ident.name,
-                    is_raw: is_raw.into(),
+                    is_raw: matches!(is_raw, IdentIsRaw::Yes),
                     span: ident.span,
                 })),
 
@@ -263,7 +265,11 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre
                     let ident = rustc_span::Ident::new(name, span).without_first_quote();
                     trees.extend([
                         TokenTree::Punct(Punct { ch: b'\'', joint: true, span }),
-                        TokenTree::Ident(Ident { sym: ident.name, is_raw: is_raw.into(), span }),
+                        TokenTree::Ident(Ident {
+                            sym: ident.name,
+                            is_raw: matches!(is_raw, IdentIsRaw::Yes),
+                            span,
+                        }),
                     ]);
                 }
                 NtLifetime(ident, is_raw) => {
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 1c09cab53b8..958bf1fde77 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -463,9 +463,6 @@ parse_invalid_dyn_keyword = invalid `dyn` keyword
 parse_invalid_expression_in_let_else = a `{$operator}` expression cannot be directly assigned in `let...else`
 parse_invalid_identifier_with_leading_number = identifiers cannot start with a number
 
-parse_invalid_label =
-    invalid label name `{$name}`
-
 parse_invalid_literal_suffix_on_tuple_index = suffixes on a tuple index are invalid
     .label = invalid suffix `{$suffix}`
     .tuple_exception_line_1 = `{$suffix}` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases
@@ -495,6 +492,8 @@ parse_invalid_unicode_escape = invalid unicode character escape
 parse_invalid_variable_declaration =
     invalid variable declaration
 
+parse_keyword_label = labels cannot use keyword names
+
 parse_keyword_lifetime =
     lifetimes cannot use keyword names
 
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index dfd4f38cf03..7f32f1cf030 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -2228,11 +2228,10 @@ pub(crate) struct KeywordLifetime {
 }
 
 #[derive(Diagnostic)]
-#[diag(parse_invalid_label)]
-pub(crate) struct InvalidLabel {
+#[diag(parse_keyword_label)]
+pub(crate) struct KeywordLabel {
     #[primary_span]
     pub span: Span,
-    pub name: Symbol,
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 6aa6fc2c790..1499808966c 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -3094,7 +3094,7 @@ impl<'a> Parser<'a> {
         if let Some((ident, is_raw)) = self.token.lifetime() {
             // Disallow `'fn`, but with a better error message than `expect_lifetime`.
             if matches!(is_raw, IdentIsRaw::No) && ident.without_first_quote().is_reserved() {
-                self.dcx().emit_err(errors::InvalidLabel { span: ident.span, name: ident.name });
+                self.dcx().emit_err(errors::KeywordLabel { span: ident.span });
             }
 
             self.bump();
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 94f381ee051..6168647183f 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -1502,8 +1502,7 @@ impl<'a> Parser<'a> {
     pub(super) fn expect_lifetime(&mut self) -> Lifetime {
         if let Some((ident, is_raw)) = self.token.lifetime() {
             if matches!(is_raw, IdentIsRaw::No)
-                && ident.without_first_quote().is_reserved()
-                && ![kw::UnderscoreLifetime, kw::StaticLifetime].contains(&ident.name)
+                && ident.without_first_quote().is_reserved_lifetime()
             {
                 self.dcx().emit_err(errors::KeywordLifetime { span: ident.span });
             }
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 9b201e40d13..8ca77b0fa19 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -3094,7 +3094,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
         } else {
             self.suggest_introducing_lifetime(
                 &mut err,
-                Some(lifetime_ref.ident.name.as_str()),
+                Some(lifetime_ref.ident),
                 |err, _, span, message, suggestion, span_suggs| {
                     err.multipart_suggestion_verbose(
                         message,
@@ -3112,7 +3112,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
     fn suggest_introducing_lifetime(
         &self,
         err: &mut Diag<'_>,
-        name: Option<&str>,
+        name: Option<Ident>,
         suggest: impl Fn(
             &mut Diag<'_>,
             bool,
@@ -3159,7 +3159,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                     let mut rm_inner_binders: FxIndexSet<Span> = Default::default();
                     let (span, sugg) = if span.is_empty() {
                         let mut binder_idents: FxIndexSet<Ident> = Default::default();
-                        binder_idents.insert(Ident::from_str(name.unwrap_or("'a")));
+                        binder_idents.insert(name.unwrap_or(Ident::from_str("'a")));
 
                         // We need to special case binders in the following situation:
                         // Change `T: for<'a> Trait<T> + 'b` to `for<'a, 'b> T: Trait<T> + 'b`
@@ -3189,16 +3189,11 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                             }
                         }
 
-                        let binders_sugg = binder_idents.into_iter().enumerate().fold(
-                            "".to_string(),
-                            |mut binders, (i, x)| {
-                                if i != 0 {
-                                    binders += ", ";
-                                }
-                                binders += x.as_str();
-                                binders
-                            },
-                        );
+                        let binders_sugg: String = binder_idents
+                            .into_iter()
+                            .map(|ident| ident.to_string())
+                            .intersperse(", ".to_owned())
+                            .collect();
                         let sugg = format!(
                             "{}<{}>{}",
                             if higher_ranked { "for" } else { "" },
@@ -3214,7 +3209,8 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                             .source_map()
                             .span_through_char(span, '<')
                             .shrink_to_hi();
-                        let sugg = format!("{}, ", name.unwrap_or("'a"));
+                        let sugg =
+                            format!("{}, ", name.map(|i| i.to_string()).as_deref().unwrap_or("'a"));
                         (span, sugg)
                     };
 
@@ -3222,7 +3218,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                         let message = Cow::from(format!(
                             "consider making the {} lifetime-generic with a new `{}` lifetime",
                             kind.descr(),
-                            name.unwrap_or("'a"),
+                            name.map(|i| i.to_string()).as_deref().unwrap_or("'a"),
                         ));
                         should_continue = suggest(
                             err,
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index dcb1becc957..744a4771aae 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -2533,10 +2533,16 @@ impl fmt::Debug for Ident {
 /// except that AST identifiers don't keep the rawness flag, so we have to guess it.
 impl fmt::Display for Ident {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Display::fmt(&IdentPrinter::new(self.name, self.is_raw_guess(), None), f)
+        fmt::Display::fmt(&IdentPrinter::new(self.name, self.guess_print_mode(), None), f)
     }
 }
 
+pub enum IdentPrintMode {
+    Normal,
+    RawIdent,
+    RawLifetime,
+}
+
 /// The most general type to print identifiers.
 ///
 /// AST pretty-printer is used as a fallback for turning AST structures into token streams for
@@ -2552,7 +2558,7 @@ impl fmt::Display for Ident {
 /// done for a token stream or a single token.
 pub struct IdentPrinter {
     symbol: Symbol,
-    is_raw: bool,
+    mode: IdentPrintMode,
     /// Span used for retrieving the crate name to which `$crate` refers to,
     /// if this field is `None` then the `$crate` conversion doesn't happen.
     convert_dollar_crate: Option<Span>,
@@ -2560,32 +2566,51 @@ pub struct IdentPrinter {
 
 impl IdentPrinter {
     /// The most general `IdentPrinter` constructor. Do not use this.
-    pub fn new(symbol: Symbol, is_raw: bool, convert_dollar_crate: Option<Span>) -> IdentPrinter {
-        IdentPrinter { symbol, is_raw, convert_dollar_crate }
+    pub fn new(
+        symbol: Symbol,
+        mode: IdentPrintMode,
+        convert_dollar_crate: Option<Span>,
+    ) -> IdentPrinter {
+        IdentPrinter { symbol, mode, convert_dollar_crate }
     }
 
     /// This implementation is supposed to be used when printing identifiers
     /// as a part of pretty-printing for larger AST pieces.
     /// Do not use this either.
-    pub fn for_ast_ident(ident: Ident, is_raw: bool) -> IdentPrinter {
-        IdentPrinter::new(ident.name, is_raw, Some(ident.span))
+    pub fn for_ast_ident(ident: Ident, mode: IdentPrintMode) -> IdentPrinter {
+        IdentPrinter::new(ident.name, mode, Some(ident.span))
     }
 }
 
 impl fmt::Display for IdentPrinter {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        if self.is_raw {
-            f.write_str("r#")?;
-        } else if self.symbol == kw::DollarCrate {
-            if let Some(span) = self.convert_dollar_crate {
+        let s = match self.mode {
+            IdentPrintMode::Normal
+                if self.symbol == kw::DollarCrate
+                    && let Some(span) = self.convert_dollar_crate =>
+            {
                 let converted = span.ctxt().dollar_crate_name();
                 if !converted.is_path_segment_keyword() {
                     f.write_str("::")?;
                 }
-                return fmt::Display::fmt(&converted, f);
+                converted
             }
-        }
-        fmt::Display::fmt(&self.symbol, f)
+            IdentPrintMode::Normal => self.symbol,
+            IdentPrintMode::RawIdent => {
+                f.write_str("r#")?;
+                self.symbol
+            }
+            IdentPrintMode::RawLifetime => {
+                f.write_str("'r#")?;
+                let s = self
+                    .symbol
+                    .as_str()
+                    .strip_prefix("'")
+                    .expect("only lifetime idents should be passed with RawLifetime mode");
+                Symbol::intern(s)
+            }
+        };
+        s.fmt(f)
     }
 }
 
@@ -3020,6 +3045,29 @@ impl Ident {
         self.name.can_be_raw() && self.is_reserved()
     }
 
+    /// Given the name of a lifetime without the first quote (`'`),
+    /// returns whether the lifetime name is reserved (therefore invalid)
+    pub fn is_reserved_lifetime(self) -> bool {
+        self.is_reserved() && ![kw::Underscore, kw::Static].contains(&self.name)
+    }
+
+    pub fn is_raw_lifetime_guess(self) -> bool {
+        let name_without_apostrophe = self.without_first_quote();
+        name_without_apostrophe.name != self.name
+            && name_without_apostrophe.name.can_be_raw()
+            && name_without_apostrophe.is_reserved_lifetime()
+    }
+
+    pub fn guess_print_mode(self) -> IdentPrintMode {
+        if self.is_raw_lifetime_guess() {
+            IdentPrintMode::RawLifetime
+        } else if self.is_raw_guess() {
+            IdentPrintMode::RawIdent
+        } else {
+            IdentPrintMode::Normal
+        }
+    }
+
     /// Whether this would be the identifier for a tuple field like `self.0`, as
     /// opposed to a named field like `self.thing`.
     pub fn is_numeric(self) -> bool {
diff --git a/tests/ui/closures/issue-52437.rs b/tests/ui/closures/issue-52437.rs
index 6ac5380a5aa..98b04d179af 100644
--- a/tests/ui/closures/issue-52437.rs
+++ b/tests/ui/closures/issue-52437.rs
@@ -1,5 +1,5 @@
 fn main() {
     [(); &(&'static: loop { |x| {}; }) as *const _ as usize]
-    //~^ ERROR: invalid label name `'static`
+    //~^ ERROR: labels cannot use keyword names
     //~| ERROR: type annotations needed
 }
diff --git a/tests/ui/closures/issue-52437.stderr b/tests/ui/closures/issue-52437.stderr
index 9ba24c7a886..8c6fa097ec5 100644
--- a/tests/ui/closures/issue-52437.stderr
+++ b/tests/ui/closures/issue-52437.stderr
@@ -1,4 +1,4 @@
-error: invalid label name `'static`
+error: labels cannot use keyword names
   --> $DIR/issue-52437.rs:2:13
    |
 LL |     [(); &(&'static: loop { |x| {}; }) as *const _ as usize]
diff --git a/tests/ui/issues/issue-46311.rs b/tests/ui/issues/issue-46311.rs
index 1233a49c582..24435665501 100644
--- a/tests/ui/issues/issue-46311.rs
+++ b/tests/ui/issues/issue-46311.rs
@@ -1,4 +1,4 @@
 fn main() {
-    'break: loop { //~ ERROR invalid label name `'break`
+    'break: loop { //~ ERROR labels cannot use keyword names
     }
 }
diff --git a/tests/ui/issues/issue-46311.stderr b/tests/ui/issues/issue-46311.stderr
index 86a3602899a..f040ba2c026 100644
--- a/tests/ui/issues/issue-46311.stderr
+++ b/tests/ui/issues/issue-46311.stderr
@@ -1,4 +1,4 @@
-error: invalid label name `'break`
+error: labels cannot use keyword names
   --> $DIR/issue-46311.rs:2:5
    |
 LL |     'break: loop {
diff --git a/tests/ui/label/label-static.rs b/tests/ui/label/label-static.rs
index 95e764d0187..ef06658506a 100644
--- a/tests/ui/label/label-static.rs
+++ b/tests/ui/label/label-static.rs
@@ -1,5 +1,5 @@
 fn main() {
-    'static: loop { //~ ERROR invalid label name `'static`
-        break 'static //~ ERROR invalid label name `'static`
+    'static: loop { //~ ERROR labels cannot use keyword names
+        break 'static //~ ERROR labels cannot use keyword names
     }
 }
diff --git a/tests/ui/label/label-static.stderr b/tests/ui/label/label-static.stderr
index 1d3251d1b5f..f6ae8b44c08 100644
--- a/tests/ui/label/label-static.stderr
+++ b/tests/ui/label/label-static.stderr
@@ -1,10 +1,10 @@
-error: invalid label name `'static`
+error: labels cannot use keyword names
   --> $DIR/label-static.rs:2:5
    |
 LL |     'static: loop {
    |     ^^^^^^^
 
-error: invalid label name `'static`
+error: labels cannot use keyword names
   --> $DIR/label-static.rs:3:15
    |
 LL |         break 'static
diff --git a/tests/ui/label/label-underscore.rs b/tests/ui/label/label-underscore.rs
index de67f3d2c3e..8f1f90fe7c0 100644
--- a/tests/ui/label/label-underscore.rs
+++ b/tests/ui/label/label-underscore.rs
@@ -1,5 +1,5 @@
 fn main() {
-    '_: loop { //~ ERROR invalid label name `'_`
-        break '_ //~ ERROR invalid label name `'_`
+    '_: loop { //~ ERROR labels cannot use keyword names
+        break '_ //~ ERROR labels cannot use keyword names
     }
 }
diff --git a/tests/ui/label/label-underscore.stderr b/tests/ui/label/label-underscore.stderr
index 4558ec4cb41..c0eb178d0f0 100644
--- a/tests/ui/label/label-underscore.stderr
+++ b/tests/ui/label/label-underscore.stderr
@@ -1,10 +1,10 @@
-error: invalid label name `'_`
+error: labels cannot use keyword names
   --> $DIR/label-underscore.rs:2:5
    |
 LL |     '_: loop {
    |     ^^
 
-error: invalid label name `'_`
+error: labels cannot use keyword names
   --> $DIR/label-underscore.rs:3:15
    |
 LL |         break '_
diff --git a/tests/ui/lifetimes/raw/use-of-undeclared-raw-lifetimes.rs b/tests/ui/lifetimes/raw/use-of-undeclared-raw-lifetimes.rs
new file mode 100644
index 00000000000..69461cfb200
--- /dev/null
+++ b/tests/ui/lifetimes/raw/use-of-undeclared-raw-lifetimes.rs
@@ -0,0 +1,21 @@
+// Check that we properly suggest `r#fn` if we use it undeclared.
+// https://github.com/rust-lang/rust/issues/143150
+//
+//@ edition: 2021
+
+fn a(_: dyn Trait + 'r#fn) {
+    //~^ ERROR use of undeclared lifetime name `'r#fn` [E0261]
+}
+
+trait Trait {}
+
+struct Test {
+    a: &'r#fn str,
+    //~^ ERROR use of undeclared lifetime name `'r#fn` [E0261]
+}
+
+trait Trait1<T>
+  where T: for<'a> Trait1<T> + 'r#fn { }
+//~^ ERROR use of undeclared lifetime name `'r#fn` [E0261]
+
+fn main() {}
diff --git a/tests/ui/lifetimes/raw/use-of-undeclared-raw-lifetimes.stderr b/tests/ui/lifetimes/raw/use-of-undeclared-raw-lifetimes.stderr
new file mode 100644
index 00000000000..8a17ce53dcf
--- /dev/null
+++ b/tests/ui/lifetimes/raw/use-of-undeclared-raw-lifetimes.stderr
@@ -0,0 +1,42 @@
+error[E0261]: use of undeclared lifetime name `'r#fn`
+  --> $DIR/use-of-undeclared-raw-lifetimes.rs:6:21
+   |
+LL | fn a(_: dyn Trait + 'r#fn) {
+   |                     ^^^^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'r#fn` here
+   |
+LL | fn a<'r#fn>(_: dyn Trait + 'r#fn) {
+   |     +++++++
+
+error[E0261]: use of undeclared lifetime name `'r#fn`
+  --> $DIR/use-of-undeclared-raw-lifetimes.rs:13:9
+   |
+LL |     a: &'r#fn str,
+   |         ^^^^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'r#fn` here
+   |
+LL | struct Test<'r#fn> {
+   |            +++++++
+
+error[E0261]: use of undeclared lifetime name `'r#fn`
+  --> $DIR/use-of-undeclared-raw-lifetimes.rs:18:32
+   |
+LL |   where T: for<'a> Trait1<T> + 'r#fn { }
+   |                                ^^^^^ undeclared lifetime
+   |
+   = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
+help: consider making the bound lifetime-generic with a new `'r#fn` lifetime
+   |
+LL -   where T: for<'a> Trait1<T> + 'r#fn { }
+LL +   where for<'r#fn, 'a> T: Trait1<T> + 'r#fn { }
+   |
+help: consider introducing lifetime `'r#fn` here
+   |
+LL | trait Trait1<'r#fn, T>
+   |              ++++++
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0261`.
diff --git a/tests/ui/parser/require-parens-for-chained-comparison.rs b/tests/ui/parser/require-parens-for-chained-comparison.rs
index 21a908923f2..6152fff6c03 100644
--- a/tests/ui/parser/require-parens-for-chained-comparison.rs
+++ b/tests/ui/parser/require-parens-for-chained-comparison.rs
@@ -24,14 +24,14 @@ fn main() {
     //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
     //~| ERROR expected
     //~| HELP add `'` to close the char literal
-    //~| ERROR invalid label name
+    //~| ERROR labels cannot use keyword names
 
     f<'_>();
     //~^ ERROR comparison operators cannot be chained
     //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
     //~| ERROR expected
     //~| HELP add `'` to close the char literal
-    //~| ERROR invalid label name
+    //~| ERROR labels cannot use keyword names
 
     let _ = f<u8>;
     //~^ ERROR comparison operators cannot be chained
diff --git a/tests/ui/parser/require-parens-for-chained-comparison.stderr b/tests/ui/parser/require-parens-for-chained-comparison.stderr
index 857c4a55788..9edfae36250 100644
--- a/tests/ui/parser/require-parens-for-chained-comparison.stderr
+++ b/tests/ui/parser/require-parens-for-chained-comparison.stderr
@@ -53,7 +53,7 @@ help: use `::<...>` instead of `<...>` to specify lifetime, type, or const argum
 LL |     let _ = f::<u8, i8>();
    |              ++
 
-error: invalid label name `'_`
+error: labels cannot use keyword names
   --> $DIR/require-parens-for-chained-comparison.rs:22:15
    |
 LL |     let _ = f<'_, i8>();
@@ -81,7 +81,7 @@ help: use `::<...>` instead of `<...>` to specify lifetime, type, or const argum
 LL |     let _ = f::<'_, i8>();
    |              ++
 
-error: invalid label name `'_`
+error: labels cannot use keyword names
   --> $DIR/require-parens-for-chained-comparison.rs:29:7
    |
 LL |     f<'_>();