Skip to content

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

Open
@jieyouxu

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

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