Skip to content

Commit

Permalink
Unrolled build for #126452
Browse files Browse the repository at this point in the history
Rollup merge of #126452 - compiler-errors:raw-lifetimes, r=spastorino

Implement raw lifetimes and labels (`'r#ident`)

This PR does two things:
1. Reserve lifetime prefixes, e.g. `'prefix#lt` in edition 2021.
2. Implements raw lifetimes, e.g. `'r#async` in edition 2021.

This PR additionally extends the `keyword_idents_2024` lint to also check lifetimes.

cc `@traviscross`
r? parser
  • Loading branch information
rust-timer authored Sep 8, 2024
2 parents 12b26c1 + b6a86be commit ec67311
Show file tree
Hide file tree
Showing 40 changed files with 373 additions and 85 deletions.
4 changes: 2 additions & 2 deletions compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,7 @@ fn visit_lazy_tts<T: MutVisitor>(vis: &mut T, lazy_tts: &mut Option<LazyAttrToke
pub fn visit_token<T: MutVisitor>(vis: &mut T, t: &mut Token) {
let Token { kind, span } = t;
match kind {
token::Ident(name, _ /*raw*/) | token::Lifetime(name) => {
token::Ident(name, _is_raw) | token::Lifetime(name, _is_raw) => {
let mut ident = Ident::new(*name, *span);
vis.visit_ident(&mut ident);
*name = ident.name;
Expand All @@ -762,7 +762,7 @@ pub fn visit_token<T: MutVisitor>(vis: &mut T, t: &mut Token) {
token::NtIdent(ident, _is_raw) => {
vis.visit_ident(ident);
}
token::NtLifetime(ident) => {
token::NtLifetime(ident, _is_raw) => {
vis.visit_ident(ident);
}
token::Interpolated(nt) => {
Expand Down
18 changes: 10 additions & 8 deletions compiler/rustc_ast/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,11 +331,11 @@ pub enum TokenKind {
/// Do not forget about `NtLifetime` when you want to match on lifetime identifiers.
/// It's recommended to use `Token::(lifetime,uninterpolate,uninterpolated_span)` to
/// treat regular and interpolated lifetime identifiers in the same way.
Lifetime(Symbol),
Lifetime(Symbol, IdentIsRaw),
/// This identifier (and its span) is the lifetime passed to the
/// declarative macro. The span in the surrounding `Token` is the span of
/// the `lifetime` metavariable in the macro's RHS.
NtLifetime(Ident),
NtLifetime(Ident, IdentIsRaw),

/// An embedded AST node, as produced by a macro. This only exists for
/// historical reasons. We'd like to get rid of it, for multiple reasons.
Expand Down Expand Up @@ -458,7 +458,7 @@ impl Token {
/// if they keep spans or perform edition checks.
pub fn uninterpolated_span(&self) -> Span {
match self.kind {
NtIdent(ident, _) | NtLifetime(ident) => ident.span,
NtIdent(ident, _) | NtLifetime(ident, _) => ident.span,
Interpolated(ref nt) => nt.use_span(),
_ => self.span,
}
Expand Down Expand Up @@ -661,7 +661,9 @@ impl Token {
pub fn uninterpolate(&self) -> Cow<'_, Token> {
match self.kind {
NtIdent(ident, is_raw) => Cow::Owned(Token::new(Ident(ident.name, is_raw), ident.span)),
NtLifetime(ident) => Cow::Owned(Token::new(Lifetime(ident.name), ident.span)),
NtLifetime(ident, is_raw) => {
Cow::Owned(Token::new(Lifetime(ident.name, is_raw), ident.span))
}
_ => Cow::Borrowed(self),
}
}
Expand All @@ -679,11 +681,11 @@ impl Token {

/// Returns a lifetime identifier if this token is a lifetime.
#[inline]
pub fn lifetime(&self) -> Option<Ident> {
pub fn lifetime(&self) -> Option<(Ident, IdentIsRaw)> {
// We avoid using `Token::uninterpolate` here because it's slow.
match self.kind {
Lifetime(name) => Some(Ident::new(name, self.span)),
NtLifetime(ident) => Some(ident),
Lifetime(name, is_raw) => Some((Ident::new(name, self.span), is_raw)),
NtLifetime(ident, is_raw) => Some((ident, is_raw)),
_ => None,
}
}
Expand Down Expand Up @@ -865,7 +867,7 @@ impl Token {
_ => return None,
},
SingleQuote => match joint.kind {
Ident(name, IdentIsRaw::No) => Lifetime(Symbol::intern(&format!("'{name}"))),
Ident(name, is_raw) => Lifetime(Symbol::intern(&format!("'{name}")), is_raw),
_ => return None,
},

Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_ast/src/tokenstream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,11 +482,11 @@ impl TokenStream {
token::NtIdent(ident, is_raw) => {
TokenTree::Token(Token::new(token::Ident(ident.name, is_raw), ident.span), spacing)
}
token::NtLifetime(ident) => TokenTree::Delimited(
token::NtLifetime(ident, is_raw) => TokenTree::Delimited(
DelimSpan::from_single(token.span),
DelimSpacing::new(Spacing::JointHidden, spacing),
Delimiter::Invisible,
TokenStream::token_alone(token::Lifetime(ident.name), ident.span),
TokenStream::token_alone(token::Lifetime(ident.name, is_raw), ident.span),
),
token::Interpolated(ref nt) => TokenTree::Delimited(
DelimSpan::from_single(token.span),
Expand Down
13 changes: 10 additions & 3 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ use std::borrow::Cow;
use ast::TraitBoundModifiers;
use rustc_ast::attr::AttrIdGenerator;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, BinOpToken, CommentKind, Delimiter, Nonterminal, Token, TokenKind};
use rustc_ast::token::{
self, BinOpToken, CommentKind, Delimiter, IdentIsRaw, Nonterminal, Token, TokenKind,
};
use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree};
use rustc_ast::util::classify;
use rustc_ast::util::comments::{Comment, CommentStyle};
Expand Down Expand Up @@ -947,8 +949,13 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
token::NtIdent(ident, is_raw) => {
IdentPrinter::for_ast_ident(ident, is_raw.into()).to_string().into()
}
token::Lifetime(name) => name.to_string().into(),
token::NtLifetime(ident) => ident.name.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()
}

/* Other */
token::DocComment(comment_kind, attr_style, data) => {
Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_expand/src/mbe/macro_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,8 +398,10 @@ pub(crate) enum NamedMatch {
fn token_name_eq(t1: &Token, t2: &Token) -> bool {
if let (Some((ident1, is_raw1)), Some((ident2, is_raw2))) = (t1.ident(), t2.ident()) {
ident1.name == ident2.name && is_raw1 == is_raw2
} else if let (Some(ident1), Some(ident2)) = (t1.lifetime(), t2.lifetime()) {
ident1.name == ident2.name
} else if let (Some((ident1, is_raw1)), Some((ident2, is_raw2))) =
(t1.lifetime(), t2.lifetime())
{
ident1.name == ident2.name && is_raw1 == is_raw2
} else {
t1.kind == t2.kind
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_expand/src/mbe/transcribe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,9 +283,9 @@ pub(super) fn transcribe<'a>(
let kind = token::NtIdent(*ident, *is_raw);
TokenTree::token_alone(kind, sp)
}
MatchedSingle(ParseNtResult::Lifetime(ident)) => {
MatchedSingle(ParseNtResult::Lifetime(ident, is_raw)) => {
marker.visit_span(&mut sp);
let kind = token::NtLifetime(*ident);
let kind = token::NtLifetime(*ident, *is_raw);
TokenTree::token_alone(kind, sp)
}
MatchedSingle(ParseNtResult::Nt(nt)) => {
Expand Down
9 changes: 5 additions & 4 deletions compiler/rustc_expand/src/proc_macro_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,15 +229,16 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre
span: ident.span,
})),

Lifetime(name) => {
Lifetime(name, is_raw) => {
let ident = symbol::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: false, span }),
TokenTree::Ident(Ident { sym: ident.name, is_raw: is_raw.into(), span }),
]);
}
NtLifetime(ident) => {
let stream = TokenStream::token_alone(token::Lifetime(ident.name), ident.span);
NtLifetime(ident, is_raw) => {
let stream =
TokenStream::token_alone(token::Lifetime(ident.name, is_raw), ident.span);
trees.push(TokenTree::Group(Group {
delimiter: pm::Delimiter::None,
stream: Some(stream),
Expand Down
39 changes: 29 additions & 10 deletions compiler/rustc_lexer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,15 @@ pub enum TokenKind {
/// tokens.
UnknownPrefix,

/// An unknown prefix in a lifetime, like `'foo#`.
///
/// Note that like above, only the `'` and prefix are included in the token
/// and not the separator.
UnknownPrefixLifetime,

/// `'r#lt`, which in edition < 2021 is split into several tokens: `'r # lt`.
RawLifetime,

/// Similar to the above, but *always* an error on every edition. This is used
/// for emoji identifier recovery, as those are not meant to be ever accepted.
InvalidPrefix,
Expand Down Expand Up @@ -677,9 +686,17 @@ impl Cursor<'_> {
return Literal { kind, suffix_start };
}

if self.first() == 'r' && self.second() == '#' && is_id_start(self.third()) {
// Eat "r" and `#`, and identifier start characters.
self.bump();
self.bump();
self.bump();
self.eat_while(is_id_continue);
return RawLifetime;
}

// Either a lifetime or a character literal with
// length greater than 1.

let starts_with_number = self.first().is_ascii_digit();

// Skip the literal contents.
Expand All @@ -688,15 +705,17 @@ impl Cursor<'_> {
self.bump();
self.eat_while(is_id_continue);

// Check if after skipping literal contents we've met a closing
// single quote (which means that user attempted to create a
// string with single quotes).
if self.first() == '\'' {
self.bump();
let kind = Char { terminated: true };
Literal { kind, suffix_start: self.pos_within_token() }
} else {
Lifetime { starts_with_number }
match self.first() {
// Check if after skipping literal contents we've met a closing
// single quote (which means that user attempted to create a
// string with single quotes).
'\'' => {
self.bump();
let kind = Char { terminated: true };
Literal { kind, suffix_start: self.pos_within_token() }
}
'#' if !starts_with_number => UnknownPrefixLifetime,
_ => Lifetime { starts_with_number },
}
}

Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,10 @@ lint_range_endpoint_out_of_range = range endpoint is out of range for `{$ty}`
lint_range_use_inclusive_range = use an inclusive range instead
lint_raw_prefix = prefix `'r` is reserved
.label = reserved prefix
.suggestion = insert whitespace here to avoid this being parsed as a prefix in Rust 2021
lint_reason_must_be_string_literal = reason must be a string literal
lint_reason_must_come_last = reason in lint attribute must come last
Expand Down
20 changes: 16 additions & 4 deletions compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1851,9 +1851,16 @@ impl KeywordIdents {
TokenTree::Token(token, _) => {
if let Some((ident, token::IdentIsRaw::No)) = token.ident() {
if !prev_dollar {
self.check_ident_token(cx, UnderMacro(true), ident);
self.check_ident_token(cx, UnderMacro(true), ident, "");
}
} else if *token == TokenKind::Dollar {
} else if let Some((ident, token::IdentIsRaw::No)) = token.lifetime() {
self.check_ident_token(
cx,
UnderMacro(true),
ident.without_first_quote(),
"'",
);
} else if token.kind == TokenKind::Dollar {
prev_dollar = true;
continue;
}
Expand All @@ -1869,6 +1876,7 @@ impl KeywordIdents {
cx: &EarlyContext<'_>,
UnderMacro(under_macro): UnderMacro,
ident: Ident,
prefix: &'static str,
) {
let (lint, edition) = match ident.name {
kw::Async | kw::Await | kw::Try => (KEYWORD_IDENTS_2018, Edition::Edition2018),
Expand Down Expand Up @@ -1902,7 +1910,7 @@ impl KeywordIdents {
cx.emit_span_lint(
lint,
ident.span,
BuiltinKeywordIdents { kw: ident, next: edition, suggestion: ident.span },
BuiltinKeywordIdents { kw: ident, next: edition, suggestion: ident.span, prefix },
);
}
}
Expand All @@ -1915,7 +1923,11 @@ impl EarlyLintPass for KeywordIdents {
self.check_tokens(cx, &mac.args.tokens);
}
fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) {
self.check_ident_token(cx, UnderMacro(false), ident);
if ident.name.as_str().starts_with('\'') {
self.check_ident_token(cx, UnderMacro(false), ident.without_first_quote(), "'");
} else {
self.check_ident_token(cx, UnderMacro(false), ident, "");
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_lint/src/context/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: &
}
.decorate_lint(diag);
}
BuiltinLintDiag::RawPrefix(label_span) => {
lints::RawPrefix { label: label_span, suggestion: label_span.shrink_to_hi() }
.decorate_lint(diag);
}
BuiltinLintDiag::UnusedBuiltinAttribute { attr_name, macro_name, invoc_span } => {
lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name }.decorate_lint(diag);
}
Expand Down
12 changes: 11 additions & 1 deletion compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,8 +363,9 @@ pub(crate) enum BuiltinEllipsisInclusiveRangePatternsLint {
pub(crate) struct BuiltinKeywordIdents {
pub kw: Ident,
pub next: Edition,
#[suggestion(code = "r#{kw}", applicability = "machine-applicable")]
#[suggestion(code = "{prefix}r#{kw}", applicability = "machine-applicable")]
pub suggestion: Span,
pub prefix: &'static str,
}

#[derive(LintDiagnostic)]
Expand Down Expand Up @@ -2814,6 +2815,15 @@ pub(crate) struct ReservedPrefix {
pub prefix: String,
}

#[derive(LintDiagnostic)]
#[diag(lint_raw_prefix)]
pub(crate) struct RawPrefix {
#[label]
pub label: Span,
#[suggestion(code = " ", applicability = "machine-applicable")]
pub suggestion: Span,
}

#[derive(LintDiagnostic)]
#[diag(lint_unused_builtin_attribute)]
pub(crate) struct UnusedBuiltinAttribute {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_lint_defs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,8 @@ pub enum BuiltinLintDiag {
LegacyDeriveHelpers(Span),
OrPatternsBackCompat(Span, String),
ReservedPrefix(Span, String),
/// `'r#` in edition < 2021.
RawPrefix(Span),
TrailingMacro(bool, Ident),
BreakWithLabelAndLoop(Span),
UnicodeTextFlow(Span, String),
Expand Down
Loading

0 comments on commit ec67311

Please sign in to comment.