Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -776,7 +776,7 @@ fn visit_lazy_tts<T: MutVisitor>(lazy_tts: &mut Option<LazyAttrTokenStream>, vis
pub fn visit_token<T: MutVisitor>(t: &mut Token, vis: &mut T) {
let Token { kind, span } = t;
match kind {
token::Ident(name, _) | token::Lifetime(name) => {
token::Ident(name, _) | token::Lifetime(name, _) => {
let mut ident = Ident::new(*name, *span);
vis.visit_ident(&mut ident);
*name = ident.name;
Expand All @@ -786,7 +786,7 @@ pub fn visit_token<T: MutVisitor>(t: &mut Token, vis: &mut T) {
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
22 changes: 14 additions & 8 deletions compiler/rustc_ast/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,11 +328,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 @@ -455,7 +455,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 @@ -627,7 +627,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 @@ -645,11 +647,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 All @@ -660,6 +662,10 @@ impl Token {
}

/// Returns `true` if the token is a lifetime.
///
/// This does not check that the lifetime is a *valid* lifetime -- for that,
/// you need to go through `expect_lifetime`, since we need to check that the
/// lifetime name is not reserved.
pub fn is_lifetime(&self) -> bool {
self.lifetime().is_some()
}
Expand Down Expand Up @@ -832,7 +838,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 @@ -487,11 +487,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
6 changes: 0 additions & 6 deletions compiler/rustc_ast_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,6 @@ ast_passes_inherent_cannot_be = inherent impls cannot be {$annotation}
.type = inherent impl for this type
.only_trait = only trait implementations may be annotated with {$annotation}

ast_passes_invalid_label =
invalid label name `{$name}`

ast_passes_invalid_unnamed_field =
unnamed fields are not allowed outside of structs or unions
.label = unnamed field declared here
Expand All @@ -170,9 +167,6 @@ ast_passes_invalid_unnamed_field_ty =
ast_passes_item_underscore = `{$kind}` items in this context need a name
.label = `_` is not a valid name for this `{$kind}` item

ast_passes_keyword_lifetime =
lifetimes cannot use keyword names

ast_passes_match_arm_with_no_body =
`match` arm with no body
.suggestion = add a body after the pattern
Expand Down
26 changes: 0 additions & 26 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,19 +257,6 @@ impl<'a> AstValidator<'a> {
self.session.dcx()
}

fn check_lifetime(&self, ident: Ident) {
let valid_names = [kw::UnderscoreLifetime, kw::StaticLifetime, kw::Empty];
if !valid_names.contains(&ident.name) && ident.without_first_quote().is_reserved() {
self.dcx().emit_err(errors::KeywordLifetime { span: ident.span });
}
}

fn check_label(&self, ident: Ident) {
if ident.without_first_quote().is_reserved() {
self.dcx().emit_err(errors::InvalidLabel { span: ident.span, name: ident.name });
}
}

fn visibility_not_permitted(&self, vis: &Visibility, note: errors::VisibilityNotPermittedNote) {
if let VisibilityKind::Inherited = vis.kind {
return;
Expand Down Expand Up @@ -879,16 +866,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.walk_ty(ty)
}

fn visit_label(&mut self, label: &'a Label) {
self.check_label(label.ident);
visit::walk_label(self, label);
}

fn visit_lifetime(&mut self, lifetime: &'a Lifetime, _: visit::LifetimeCtxt) {
self.check_lifetime(lifetime.ident);
visit::walk_lifetime(self, lifetime);
}

fn visit_field_def(&mut self, field: &'a FieldDef) {
self.deny_unnamed_field(field);
visit::walk_field_def(self, field)
Expand Down Expand Up @@ -1315,9 +1292,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}

fn visit_generic_param(&mut self, param: &'a GenericParam) {
if let GenericParamKind::Lifetime { .. } = param.kind {
self.check_lifetime(param.ident);
}
visit::walk_generic_param(self, param);
}

Expand Down
15 changes: 0 additions & 15 deletions compiler/rustc_ast_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,6 @@ use rustc_span::{symbol::Ident, Span, Symbol};

use crate::fluent_generated as fluent;

#[derive(Diagnostic)]
#[diag(ast_passes_keyword_lifetime)]
pub struct KeywordLifetime {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(ast_passes_invalid_label)]
pub struct InvalidLabel {
#[primary_span]
pub span: Span,
pub name: Symbol,
}

#[derive(Diagnostic)]
#[diag(ast_passes_visibility_not_permitted, code = E0449)]
pub struct VisibilityNotPermitted {
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 @@ -12,7 +12,9 @@ use crate::pprust::state::fixup::FixupContext;
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 @@ -946,8 +948,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 @@ -278,9 +278,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 @@ -227,15 +227,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
14 changes: 13 additions & 1 deletion compiler/rustc_lexer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ pub enum TokenKind {
/// "'a"
Lifetime { starts_with_number: bool },

/// `'r#async`
RawLifetime,

// One-char tokens:
/// ";"
Semi,
Expand Down Expand Up @@ -676,9 +679,18 @@ impl Cursor<'_> {
return Literal { kind, suffix_start };
}

if self.first() == 'r' && self.second() == '#' && is_id_start(self.third()) {
// Eat "r" character.
self.bump();
// Eat "#" symbol.
self.bump();
// Eat the identifier part of RawIdent.
self.eat_identifier();
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 Down
19 changes: 16 additions & 3 deletions compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1888,8 +1888,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, "");
}
}
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 @@ -1906,6 +1914,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 @@ -1939,7 +1948,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 @@ -1952,7 +1961,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
3 changes: 2 additions & 1 deletion compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,8 +368,9 @@ pub enum BuiltinEllipsisInclusiveRangePatternsLint {
pub 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
6 changes: 6 additions & 0 deletions compiler/rustc_parse/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,9 @@ 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
Expand All @@ -416,6 +419,9 @@ parse_invalid_unicode_escape = invalid unicode character escape
parse_invalid_variable_declaration =
invalid variable declaration

parse_keyword_lifetime =
lifetimes cannot use keyword names

parse_kw_bad_case = keyword `{$kw}` is written in the wrong case
.suggestion = write it in the correct case

Expand Down
15 changes: 15 additions & 0 deletions compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1907,6 +1907,21 @@ pub struct CannotBeRawIdent {
pub ident: Symbol,
}

#[derive(Diagnostic)]
#[diag(parse_keyword_lifetime)]
pub struct KeywordLifetime {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(parse_invalid_label)]
pub struct InvalidLabel {
#[primary_span]
pub span: Span,
pub name: Symbol,
}

#[derive(Diagnostic)]
#[diag(parse_cr_doc_comment)]
pub struct CrDocComment {
Expand Down
Loading
Loading