Skip to content

Confusing error when using non-XID_Start characters in lifetime names #141081

Description

@jieyouxu

Code

fn bad_lifetime_name<'🐛>(_: &'🐛 ()) {}

Current output

error: character literal may only contain one codepoint
 --> src/lib.rs:1:22
  |
1 | fn bad_lifetime_name<'🐛>(_: &'🐛 ()) {}
  |                      ^^^^^^^^^^
  |
help: if you meant to write a string literal, use double quotes
  |
1 - fn bad_lifetime_name<'🐛>(_: &'🐛 ()) {}
1 + fn bad_lifetime_name<"🐛>(_: &"🐛 ()) {}
  |

error: unexpected closing delimiter: `)`
 --> src/lib.rs:1:35
  |
1 | fn bad_lifetime_name<'🐛>(_: &'🐛 ()) {}
  |                                     ^ unexpected closing delimiter

Desired output

error: a lifetime name cannot begin with a character that is neither `_` nor a `XID_Start` character

 --> src/lib.rs:1:22
  |
1 | fn bad_lifetime_name<'🐛>(_: &'🐛 ()) {}
  |                       ^--
  |                        help: `\u{1F41B}` is neither `_` nor in `XID_Start`

Rationale and extra context

See also https://doc.rust-lang.org/reference/identifiers.html.
Initially reported at #general > Compiler isn't helpful with invalid lifetimes.

Looks like this recovery path(?) is hit when starting a lifetime name (not including the ') with a non-XID_Start character fails both is_id_start check and ASCII digit check hitting the !can_be_a_lifetime codepath.

let can_be_a_lifetime = if self.second() == '\'' {
// It's surely not a lifetime.
false
} else {
// If the first symbol is valid for identifier, it can be a lifetime.
// Also check if it's a number for a better error reporting (so '0 will
// be reported as invalid lifetime and not as unterminated char literal).
is_id_start(self.first()) || self.first().is_ascii_digit()
};
if !can_be_a_lifetime {
let terminated = self.single_quoted_string();
let suffix_start = self.pos_within_token();
if terminated {
self.eat_literal_suffix();
}
let kind = Char { terminated };
return Literal { kind, suffix_start };
}

Other cases

Marginally related is loop labels, this complains about "unterminated character literal".

fn main() {
    '🐛: {
        todo!();  
    };
}

Rust Version

$ rustc --version --verbose
rustc 1.87.0 (17067e9ac 2025-05-09)
binary: rustc
commit-hash: 17067e9ac6d7ecb70e50f92c1944e545188d2359
commit-date: 2025-05-09
host: aarch64-unknown-linux-gnu
release: 1.87.0
LLVM version: 20.1.1

Anything else?

No response

Activity

  1. added
    A-diagnosticsArea: Messages for errors, warnings, and lints
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on May 16, 2025
  2. mu001999 commented on May 22, 2025

    @mu001999
    Member

    @rustbot claim

  3. mu001999 commented on Jun 9, 2025

    @mu001999
    Member

    @rustbot release-assignment

  4. added
    D-confusingDiagnostics: Confusing error or lint that should be reworked.
    on Jun 9, 2025
  5. estebank commented on Mar 15, 2026

    @estebank
    Contributor

    #153893 introduces a barebones error for these cases. I'd like to keep the ticket open for tracking adding more information to it.

  6. added a commit that references this issue on Mar 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsA-lexerArea: lexerD-confusingDiagnostics: Confusing error or lint that should be reworked.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions