diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 083e04730bc37..35786d8932878 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2463,9 +2463,10 @@ pub const STDLIB_STABLE_CRATES: &[Symbol] = &[sym::std, sym::core, sym::alloc, s #[derive(Copy, Clone, Eq, HashStable_Generic, Encodable, Decodable)] pub struct Ident { - // `name` should never be the empty symbol. If you are considering that, - // you are probably conflating "empty identifier with "no identifier" and - // you should use `Option` instead. + /// `name` should never be the empty symbol. If you are considering that, + /// you are probably conflating "empty identifier with "no identifier" and + /// you should use `Option` instead. + /// Trying to construct an `Ident` with an empty name will trigger debug assertions. pub name: Symbol, pub span: Span, } @@ -2508,6 +2509,8 @@ impl Ident { Ident::new(self.name, span.with_ctxt(self.span.ctxt())) } + /// Creates a new ident with the same span and name with leading quote removed, if any. + /// If called on an empty ident, or with name just a single quote, returns an empty ident which is invalid. pub fn without_first_quote(self) -> Ident { Ident::new(Symbol::intern(self.as_str().trim_start_matches('\'')), self.span) } @@ -3095,10 +3098,15 @@ impl Ident { } 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() + // Check that the name isn't just a single quote. + // `self.without_first_quote()` would return empty ident, which triggers debug assert. + if self.name.as_str() == "'" { + return false; + } + let ident_without_apostrophe = self.without_first_quote(); + ident_without_apostrophe.name != self.name + && ident_without_apostrophe.name.can_be_raw() + && ident_without_apostrophe.is_reserved_lifetime() } pub fn guess_print_mode(self) -> IdentPrintMode { diff --git a/tests/ui/extern/extern-single-quote-issue-147365.rs b/tests/ui/extern/extern-single-quote-issue-147365.rs new file mode 100644 index 0000000000000..b50016bd65f31 --- /dev/null +++ b/tests/ui/extern/extern-single-quote-issue-147365.rs @@ -0,0 +1,9 @@ +//@ needs-rustc-debug-assertions + +// https://github.com/rust-lang/rust/issues/147365 +// Ensures we don't trigger debug assert by creating an empty Ident when determining whether +// the single quote is a raw lifetime. + +extern "'" {} //~ ERROR invalid ABI: found `'` + +fn main() {} diff --git a/tests/ui/extern/extern-single-quote-issue-147365.stderr b/tests/ui/extern/extern-single-quote-issue-147365.stderr new file mode 100644 index 0000000000000..d761bc3ebf323 --- /dev/null +++ b/tests/ui/extern/extern-single-quote-issue-147365.stderr @@ -0,0 +1,16 @@ +error[E0703]: invalid ABI: found `'` + --> $DIR/extern-single-quote-issue-147365.rs:7:8 + | +LL | extern "'" {} + | ^^^ invalid ABI + | + = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions +help: there's a similarly named valid ABI `C` + | +LL - extern "'" {} +LL + extern "C" {} + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0703`.