mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-21 22:34:05 +00:00
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
This commit is contained in:
commit
ccf3f6e59d
@ -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) {
|
pub fn visit_token<T: MutVisitor>(vis: &mut T, t: &mut Token) {
|
||||||
let Token { kind, span } = t;
|
let Token { kind, span } = t;
|
||||||
match kind {
|
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);
|
let mut ident = Ident::new(*name, *span);
|
||||||
vis.visit_ident(&mut ident);
|
vis.visit_ident(&mut ident);
|
||||||
*name = ident.name;
|
*name = ident.name;
|
||||||
@ -762,7 +762,7 @@ pub fn visit_token<T: MutVisitor>(vis: &mut T, t: &mut Token) {
|
|||||||
token::NtIdent(ident, _is_raw) => {
|
token::NtIdent(ident, _is_raw) => {
|
||||||
vis.visit_ident(ident);
|
vis.visit_ident(ident);
|
||||||
}
|
}
|
||||||
token::NtLifetime(ident) => {
|
token::NtLifetime(ident, _is_raw) => {
|
||||||
vis.visit_ident(ident);
|
vis.visit_ident(ident);
|
||||||
}
|
}
|
||||||
token::Interpolated(nt) => {
|
token::Interpolated(nt) => {
|
||||||
|
@ -331,11 +331,11 @@ pub enum TokenKind {
|
|||||||
/// Do not forget about `NtLifetime` when you want to match on lifetime identifiers.
|
/// Do not forget about `NtLifetime` when you want to match on lifetime identifiers.
|
||||||
/// It's recommended to use `Token::(lifetime,uninterpolate,uninterpolated_span)` to
|
/// It's recommended to use `Token::(lifetime,uninterpolate,uninterpolated_span)` to
|
||||||
/// treat regular and interpolated lifetime identifiers in the same way.
|
/// 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
|
/// This identifier (and its span) is the lifetime passed to the
|
||||||
/// declarative macro. The span in the surrounding `Token` is the span of
|
/// declarative macro. The span in the surrounding `Token` is the span of
|
||||||
/// the `lifetime` metavariable in the macro's RHS.
|
/// 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
|
/// 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.
|
/// historical reasons. We'd like to get rid of it, for multiple reasons.
|
||||||
@ -458,7 +458,7 @@ impl Token {
|
|||||||
/// if they keep spans or perform edition checks.
|
/// if they keep spans or perform edition checks.
|
||||||
pub fn uninterpolated_span(&self) -> Span {
|
pub fn uninterpolated_span(&self) -> Span {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
NtIdent(ident, _) | NtLifetime(ident) => ident.span,
|
NtIdent(ident, _) | NtLifetime(ident, _) => ident.span,
|
||||||
Interpolated(ref nt) => nt.use_span(),
|
Interpolated(ref nt) => nt.use_span(),
|
||||||
_ => self.span,
|
_ => self.span,
|
||||||
}
|
}
|
||||||
@ -661,7 +661,9 @@ impl Token {
|
|||||||
pub fn uninterpolate(&self) -> Cow<'_, Token> {
|
pub fn uninterpolate(&self) -> Cow<'_, Token> {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
NtIdent(ident, is_raw) => Cow::Owned(Token::new(Ident(ident.name, is_raw), ident.span)),
|
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),
|
_ => Cow::Borrowed(self),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -679,11 +681,11 @@ impl Token {
|
|||||||
|
|
||||||
/// Returns a lifetime identifier if this token is a lifetime.
|
/// Returns a lifetime identifier if this token is a lifetime.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn lifetime(&self) -> Option<Ident> {
|
pub fn lifetime(&self) -> Option<(Ident, IdentIsRaw)> {
|
||||||
// We avoid using `Token::uninterpolate` here because it's slow.
|
// We avoid using `Token::uninterpolate` here because it's slow.
|
||||||
match self.kind {
|
match self.kind {
|
||||||
Lifetime(name) => Some(Ident::new(name, self.span)),
|
Lifetime(name, is_raw) => Some((Ident::new(name, self.span), is_raw)),
|
||||||
NtLifetime(ident) => Some(ident),
|
NtLifetime(ident, is_raw) => Some((ident, is_raw)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -865,7 +867,7 @@ impl Token {
|
|||||||
_ => return None,
|
_ => return None,
|
||||||
},
|
},
|
||||||
SingleQuote => match joint.kind {
|
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,
|
_ => return None,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -482,11 +482,11 @@ impl TokenStream {
|
|||||||
token::NtIdent(ident, is_raw) => {
|
token::NtIdent(ident, is_raw) => {
|
||||||
TokenTree::Token(Token::new(token::Ident(ident.name, is_raw), ident.span), spacing)
|
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),
|
DelimSpan::from_single(token.span),
|
||||||
DelimSpacing::new(Spacing::JointHidden, spacing),
|
DelimSpacing::new(Spacing::JointHidden, spacing),
|
||||||
Delimiter::Invisible,
|
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(
|
token::Interpolated(ref nt) => TokenTree::Delimited(
|
||||||
DelimSpan::from_single(token.span),
|
DelimSpan::from_single(token.span),
|
||||||
|
@ -11,7 +11,9 @@ use std::borrow::Cow;
|
|||||||
use ast::TraitBoundModifiers;
|
use ast::TraitBoundModifiers;
|
||||||
use rustc_ast::attr::AttrIdGenerator;
|
use rustc_ast::attr::AttrIdGenerator;
|
||||||
use rustc_ast::ptr::P;
|
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::tokenstream::{Spacing, TokenStream, TokenTree};
|
||||||
use rustc_ast::util::classify;
|
use rustc_ast::util::classify;
|
||||||
use rustc_ast::util::comments::{Comment, CommentStyle};
|
use rustc_ast::util::comments::{Comment, CommentStyle};
|
||||||
@ -947,8 +949,13 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
|||||||
token::NtIdent(ident, is_raw) => {
|
token::NtIdent(ident, is_raw) => {
|
||||||
IdentPrinter::for_ast_ident(ident, is_raw.into()).to_string().into()
|
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 */
|
/* Other */
|
||||||
token::DocComment(comment_kind, attr_style, data) => {
|
token::DocComment(comment_kind, attr_style, data) => {
|
||||||
|
@ -398,8 +398,10 @@ pub(crate) enum NamedMatch {
|
|||||||
fn token_name_eq(t1: &Token, t2: &Token) -> bool {
|
fn token_name_eq(t1: &Token, t2: &Token) -> bool {
|
||||||
if let (Some((ident1, is_raw1)), Some((ident2, is_raw2))) = (t1.ident(), t2.ident()) {
|
if let (Some((ident1, is_raw1)), Some((ident2, is_raw2))) = (t1.ident(), t2.ident()) {
|
||||||
ident1.name == ident2.name && is_raw1 == is_raw2
|
ident1.name == ident2.name && is_raw1 == is_raw2
|
||||||
} else if let (Some(ident1), Some(ident2)) = (t1.lifetime(), t2.lifetime()) {
|
} else if let (Some((ident1, is_raw1)), Some((ident2, is_raw2))) =
|
||||||
ident1.name == ident2.name
|
(t1.lifetime(), t2.lifetime())
|
||||||
|
{
|
||||||
|
ident1.name == ident2.name && is_raw1 == is_raw2
|
||||||
} else {
|
} else {
|
||||||
t1.kind == t2.kind
|
t1.kind == t2.kind
|
||||||
}
|
}
|
||||||
|
@ -283,9 +283,9 @@ pub(super) fn transcribe<'a>(
|
|||||||
let kind = token::NtIdent(*ident, *is_raw);
|
let kind = token::NtIdent(*ident, *is_raw);
|
||||||
TokenTree::token_alone(kind, sp)
|
TokenTree::token_alone(kind, sp)
|
||||||
}
|
}
|
||||||
MatchedSingle(ParseNtResult::Lifetime(ident)) => {
|
MatchedSingle(ParseNtResult::Lifetime(ident, is_raw)) => {
|
||||||
marker.visit_span(&mut sp);
|
marker.visit_span(&mut sp);
|
||||||
let kind = token::NtLifetime(*ident);
|
let kind = token::NtLifetime(*ident, *is_raw);
|
||||||
TokenTree::token_alone(kind, sp)
|
TokenTree::token_alone(kind, sp)
|
||||||
}
|
}
|
||||||
MatchedSingle(ParseNtResult::Nt(nt)) => {
|
MatchedSingle(ParseNtResult::Nt(nt)) => {
|
||||||
|
@ -229,15 +229,16 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre
|
|||||||
span: ident.span,
|
span: ident.span,
|
||||||
})),
|
})),
|
||||||
|
|
||||||
Lifetime(name) => {
|
Lifetime(name, is_raw) => {
|
||||||
let ident = symbol::Ident::new(name, span).without_first_quote();
|
let ident = symbol::Ident::new(name, span).without_first_quote();
|
||||||
trees.extend([
|
trees.extend([
|
||||||
TokenTree::Punct(Punct { ch: b'\'', joint: true, span }),
|
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) => {
|
NtLifetime(ident, is_raw) => {
|
||||||
let stream = TokenStream::token_alone(token::Lifetime(ident.name), ident.span);
|
let stream =
|
||||||
|
TokenStream::token_alone(token::Lifetime(ident.name, is_raw), ident.span);
|
||||||
trees.push(TokenTree::Group(Group {
|
trees.push(TokenTree::Group(Group {
|
||||||
delimiter: pm::Delimiter::None,
|
delimiter: pm::Delimiter::None,
|
||||||
stream: Some(stream),
|
stream: Some(stream),
|
||||||
|
@ -91,6 +91,15 @@ pub enum TokenKind {
|
|||||||
/// tokens.
|
/// tokens.
|
||||||
UnknownPrefix,
|
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
|
/// 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.
|
/// for emoji identifier recovery, as those are not meant to be ever accepted.
|
||||||
InvalidPrefix,
|
InvalidPrefix,
|
||||||
@ -677,9 +686,17 @@ impl Cursor<'_> {
|
|||||||
return Literal { kind, suffix_start };
|
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
|
// Either a lifetime or a character literal with
|
||||||
// length greater than 1.
|
// length greater than 1.
|
||||||
|
|
||||||
let starts_with_number = self.first().is_ascii_digit();
|
let starts_with_number = self.first().is_ascii_digit();
|
||||||
|
|
||||||
// Skip the literal contents.
|
// Skip the literal contents.
|
||||||
@ -688,15 +705,17 @@ impl Cursor<'_> {
|
|||||||
self.bump();
|
self.bump();
|
||||||
self.eat_while(is_id_continue);
|
self.eat_while(is_id_continue);
|
||||||
|
|
||||||
// Check if after skipping literal contents we've met a closing
|
match self.first() {
|
||||||
// single quote (which means that user attempted to create a
|
// Check if after skipping literal contents we've met a closing
|
||||||
// string with single quotes).
|
// single quote (which means that user attempted to create a
|
||||||
if self.first() == '\'' {
|
// string with single quotes).
|
||||||
self.bump();
|
'\'' => {
|
||||||
let kind = Char { terminated: true };
|
self.bump();
|
||||||
Literal { kind, suffix_start: self.pos_within_token() }
|
let kind = Char { terminated: true };
|
||||||
} else {
|
Literal { kind, suffix_start: self.pos_within_token() }
|
||||||
Lifetime { starts_with_number }
|
}
|
||||||
|
'#' if !starts_with_number => UnknownPrefixLifetime,
|
||||||
|
_ => Lifetime { starts_with_number },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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_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_be_string_literal = reason must be a string literal
|
||||||
|
|
||||||
lint_reason_must_come_last = reason in lint attribute must come last
|
lint_reason_must_come_last = reason in lint attribute must come last
|
||||||
|
@ -1851,9 +1851,16 @@ impl KeywordIdents {
|
|||||||
TokenTree::Token(token, _) => {
|
TokenTree::Token(token, _) => {
|
||||||
if let Some((ident, token::IdentIsRaw::No)) = token.ident() {
|
if let Some((ident, token::IdentIsRaw::No)) = token.ident() {
|
||||||
if !prev_dollar {
|
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;
|
prev_dollar = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1869,6 +1876,7 @@ impl KeywordIdents {
|
|||||||
cx: &EarlyContext<'_>,
|
cx: &EarlyContext<'_>,
|
||||||
UnderMacro(under_macro): UnderMacro,
|
UnderMacro(under_macro): UnderMacro,
|
||||||
ident: Ident,
|
ident: Ident,
|
||||||
|
prefix: &'static str,
|
||||||
) {
|
) {
|
||||||
let (lint, edition) = match ident.name {
|
let (lint, edition) = match ident.name {
|
||||||
kw::Async | kw::Await | kw::Try => (KEYWORD_IDENTS_2018, Edition::Edition2018),
|
kw::Async | kw::Await | kw::Try => (KEYWORD_IDENTS_2018, Edition::Edition2018),
|
||||||
@ -1902,7 +1910,7 @@ impl KeywordIdents {
|
|||||||
cx.emit_span_lint(
|
cx.emit_span_lint(
|
||||||
lint,
|
lint,
|
||||||
ident.span,
|
ident.span,
|
||||||
BuiltinKeywordIdents { kw: ident, next: edition, suggestion: ident.span },
|
BuiltinKeywordIdents { kw: ident, next: edition, suggestion: ident.span, prefix },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1915,7 +1923,11 @@ impl EarlyLintPass for KeywordIdents {
|
|||||||
self.check_tokens(cx, &mac.args.tokens);
|
self.check_tokens(cx, &mac.args.tokens);
|
||||||
}
|
}
|
||||||
fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) {
|
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, "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,6 +172,10 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: &
|
|||||||
}
|
}
|
||||||
.decorate_lint(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 } => {
|
BuiltinLintDiag::UnusedBuiltinAttribute { attr_name, macro_name, invoc_span } => {
|
||||||
lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name }.decorate_lint(diag);
|
lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name }.decorate_lint(diag);
|
||||||
}
|
}
|
||||||
|
@ -363,8 +363,9 @@ pub(crate) enum BuiltinEllipsisInclusiveRangePatternsLint {
|
|||||||
pub(crate) struct BuiltinKeywordIdents {
|
pub(crate) struct BuiltinKeywordIdents {
|
||||||
pub kw: Ident,
|
pub kw: Ident,
|
||||||
pub next: Edition,
|
pub next: Edition,
|
||||||
#[suggestion(code = "r#{kw}", applicability = "machine-applicable")]
|
#[suggestion(code = "{prefix}r#{kw}", applicability = "machine-applicable")]
|
||||||
pub suggestion: Span,
|
pub suggestion: Span,
|
||||||
|
pub prefix: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(LintDiagnostic)]
|
#[derive(LintDiagnostic)]
|
||||||
@ -2814,6 +2815,15 @@ pub(crate) struct ReservedPrefix {
|
|||||||
pub prefix: String,
|
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)]
|
#[derive(LintDiagnostic)]
|
||||||
#[diag(lint_unused_builtin_attribute)]
|
#[diag(lint_unused_builtin_attribute)]
|
||||||
pub(crate) struct UnusedBuiltinAttribute {
|
pub(crate) struct UnusedBuiltinAttribute {
|
||||||
|
@ -612,6 +612,8 @@ pub enum BuiltinLintDiag {
|
|||||||
LegacyDeriveHelpers(Span),
|
LegacyDeriveHelpers(Span),
|
||||||
OrPatternsBackCompat(Span, String),
|
OrPatternsBackCompat(Span, String),
|
||||||
ReservedPrefix(Span, String),
|
ReservedPrefix(Span, String),
|
||||||
|
/// `'r#` in edition < 2021.
|
||||||
|
RawPrefix(Span),
|
||||||
TrailingMacro(bool, Ident),
|
TrailingMacro(bool, Ident),
|
||||||
BreakWithLabelAndLoop(Span),
|
BreakWithLabelAndLoop(Span),
|
||||||
UnicodeTextFlow(Span, String),
|
UnicodeTextFlow(Span, String),
|
||||||
|
@ -13,7 +13,6 @@ use rustc_session::lint::builtin::{
|
|||||||
};
|
};
|
||||||
use rustc_session::lint::BuiltinLintDiag;
|
use rustc_session::lint::BuiltinLintDiag;
|
||||||
use rustc_session::parse::ParseSess;
|
use rustc_session::parse::ParseSess;
|
||||||
use rustc_span::edition::Edition;
|
|
||||||
use rustc_span::symbol::Symbol;
|
use rustc_span::symbol::Symbol;
|
||||||
use rustc_span::{BytePos, Pos, Span};
|
use rustc_span::{BytePos, Pos, Span};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
@ -188,9 +187,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> {
|
|||||||
preceded_by_whitespace = true;
|
preceded_by_whitespace = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
rustc_lexer::TokenKind::Ident => {
|
rustc_lexer::TokenKind::Ident => self.ident(start),
|
||||||
self.ident(start)
|
|
||||||
}
|
|
||||||
rustc_lexer::TokenKind::RawIdent => {
|
rustc_lexer::TokenKind::RawIdent => {
|
||||||
let sym = nfc_normalize(self.str_from(start + BytePos(2)));
|
let sym = nfc_normalize(self.str_from(start + BytePos(2)));
|
||||||
let span = self.mk_sp(start, self.pos);
|
let span = self.mk_sp(start, self.pos);
|
||||||
@ -205,20 +202,31 @@ impl<'psess, 'src> StringReader<'psess, 'src> {
|
|||||||
self.report_unknown_prefix(start);
|
self.report_unknown_prefix(start);
|
||||||
self.ident(start)
|
self.ident(start)
|
||||||
}
|
}
|
||||||
rustc_lexer::TokenKind::InvalidIdent
|
rustc_lexer::TokenKind::UnknownPrefixLifetime => {
|
||||||
| rustc_lexer::TokenKind::InvalidPrefix
|
self.report_unknown_prefix(start);
|
||||||
|
// Include the leading `'` in the real identifier, for macro
|
||||||
|
// expansion purposes. See #12512 for the gory details of why
|
||||||
|
// this is necessary.
|
||||||
|
let lifetime_name = self.str_from(start);
|
||||||
|
self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1)));
|
||||||
|
let ident = Symbol::intern(lifetime_name);
|
||||||
|
token::Lifetime(ident, IdentIsRaw::No)
|
||||||
|
}
|
||||||
|
rustc_lexer::TokenKind::InvalidIdent | rustc_lexer::TokenKind::InvalidPrefix
|
||||||
// Do not recover an identifier with emoji if the codepoint is a confusable
|
// Do not recover an identifier with emoji if the codepoint is a confusable
|
||||||
// with a recoverable substitution token, like `➖`.
|
// with a recoverable substitution token, like `➖`.
|
||||||
if !UNICODE_ARRAY
|
if !UNICODE_ARRAY.iter().any(|&(c, _, _)| {
|
||||||
.iter()
|
let sym = self.str_from(start);
|
||||||
.any(|&(c, _, _)| {
|
sym.chars().count() == 1 && c == sym.chars().next().unwrap()
|
||||||
let sym = self.str_from(start);
|
}) =>
|
||||||
sym.chars().count() == 1 && c == sym.chars().next().unwrap()
|
|
||||||
}) =>
|
|
||||||
{
|
{
|
||||||
let sym = nfc_normalize(self.str_from(start));
|
let sym = nfc_normalize(self.str_from(start));
|
||||||
let span = self.mk_sp(start, self.pos);
|
let span = self.mk_sp(start, self.pos);
|
||||||
self.psess.bad_unicode_identifiers.borrow_mut().entry(sym).or_default()
|
self.psess
|
||||||
|
.bad_unicode_identifiers
|
||||||
|
.borrow_mut()
|
||||||
|
.entry(sym)
|
||||||
|
.or_default()
|
||||||
.push(span);
|
.push(span);
|
||||||
token::Ident(sym, IdentIsRaw::No)
|
token::Ident(sym, IdentIsRaw::No)
|
||||||
}
|
}
|
||||||
@ -249,9 +257,9 @@ impl<'psess, 'src> StringReader<'psess, 'src> {
|
|||||||
let suffix = if suffix_start < self.pos {
|
let suffix = if suffix_start < self.pos {
|
||||||
let string = self.str_from(suffix_start);
|
let string = self.str_from(suffix_start);
|
||||||
if string == "_" {
|
if string == "_" {
|
||||||
self
|
self.dcx().emit_err(errors::UnderscoreLiteralSuffix {
|
||||||
.dcx()
|
span: self.mk_sp(suffix_start, self.pos),
|
||||||
.emit_err(errors::UnderscoreLiteralSuffix { span: self.mk_sp(suffix_start, self.pos) });
|
});
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(Symbol::intern(string))
|
Some(Symbol::intern(string))
|
||||||
@ -269,12 +277,47 @@ impl<'psess, 'src> StringReader<'psess, 'src> {
|
|||||||
self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1)));
|
self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1)));
|
||||||
if starts_with_number {
|
if starts_with_number {
|
||||||
let span = self.mk_sp(start, self.pos);
|
let span = self.mk_sp(start, self.pos);
|
||||||
self.dcx().struct_err("lifetimes cannot start with a number")
|
self.dcx()
|
||||||
|
.struct_err("lifetimes cannot start with a number")
|
||||||
.with_span(span)
|
.with_span(span)
|
||||||
.stash(span, StashKey::LifetimeIsChar);
|
.stash(span, StashKey::LifetimeIsChar);
|
||||||
}
|
}
|
||||||
let ident = Symbol::intern(lifetime_name);
|
let ident = Symbol::intern(lifetime_name);
|
||||||
token::Lifetime(ident)
|
token::Lifetime(ident, IdentIsRaw::No)
|
||||||
|
}
|
||||||
|
rustc_lexer::TokenKind::RawLifetime => {
|
||||||
|
self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1)));
|
||||||
|
|
||||||
|
let ident_start = start + BytePos(3);
|
||||||
|
let prefix_span = self.mk_sp(start, ident_start);
|
||||||
|
|
||||||
|
if prefix_span.at_least_rust_2021() {
|
||||||
|
let lifetime_name_without_tick = self.str_from(ident_start);
|
||||||
|
// Put the `'` back onto the lifetime name.
|
||||||
|
let mut lifetime_name = String::with_capacity(lifetime_name_without_tick.len() + 1);
|
||||||
|
lifetime_name.push('\'');
|
||||||
|
lifetime_name += lifetime_name_without_tick;
|
||||||
|
let sym = Symbol::intern(&lifetime_name);
|
||||||
|
|
||||||
|
token::Lifetime(sym, IdentIsRaw::Yes)
|
||||||
|
} else {
|
||||||
|
// Otherwise, this should be parsed like `'r`. Warn about it though.
|
||||||
|
self.psess.buffer_lint(
|
||||||
|
RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
|
||||||
|
prefix_span,
|
||||||
|
ast::CRATE_NODE_ID,
|
||||||
|
BuiltinLintDiag::RawPrefix(prefix_span),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Reset the state so we just lex the `'r`.
|
||||||
|
let lt_start = start + BytePos(2);
|
||||||
|
self.pos = lt_start;
|
||||||
|
self.cursor = Cursor::new(&str_before[2 as usize..]);
|
||||||
|
|
||||||
|
let lifetime_name = self.str_from(start);
|
||||||
|
let ident = Symbol::intern(lifetime_name);
|
||||||
|
token::Lifetime(ident, IdentIsRaw::No)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rustc_lexer::TokenKind::Semi => token::Semi,
|
rustc_lexer::TokenKind::Semi => token::Semi,
|
||||||
rustc_lexer::TokenKind::Comma => token::Comma,
|
rustc_lexer::TokenKind::Comma => token::Comma,
|
||||||
@ -331,16 +374,19 @@ impl<'psess, 'src> StringReader<'psess, 'src> {
|
|||||||
// first remove compound tokens like `<<` from `rustc_lexer`, and then add
|
// first remove compound tokens like `<<` from `rustc_lexer`, and then add
|
||||||
// fancier error recovery to it, as there will be less overall work to do this
|
// fancier error recovery to it, as there will be less overall work to do this
|
||||||
// way.
|
// way.
|
||||||
let (token, sugg) = unicode_chars::check_for_substitution(self, start, c, repeats+1);
|
let (token, sugg) =
|
||||||
|
unicode_chars::check_for_substitution(self, start, c, repeats + 1);
|
||||||
self.dcx().emit_err(errors::UnknownTokenStart {
|
self.dcx().emit_err(errors::UnknownTokenStart {
|
||||||
span: self.mk_sp(start, self.pos + Pos::from_usize(repeats * c.len_utf8())),
|
span: self.mk_sp(start, self.pos + Pos::from_usize(repeats * c.len_utf8())),
|
||||||
escaped: escaped_char(c),
|
escaped: escaped_char(c),
|
||||||
sugg,
|
sugg,
|
||||||
null: if c == '\x00' {Some(errors::UnknownTokenNull)} else {None},
|
null: if c == '\x00' { Some(errors::UnknownTokenNull) } else { None },
|
||||||
repeat: if repeats > 0 {
|
repeat: if repeats > 0 {
|
||||||
swallow_next_invalid = repeats;
|
swallow_next_invalid = repeats;
|
||||||
Some(errors::UnknownTokenRepeat { repeats })
|
Some(errors::UnknownTokenRepeat { repeats })
|
||||||
} else {None}
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(token) = token {
|
if let Some(token) = token {
|
||||||
@ -699,7 +745,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> {
|
|||||||
|
|
||||||
let expn_data = prefix_span.ctxt().outer_expn_data();
|
let expn_data = prefix_span.ctxt().outer_expn_data();
|
||||||
|
|
||||||
if expn_data.edition >= Edition::Edition2021 {
|
if expn_data.edition.at_least_rust_2021() {
|
||||||
// In Rust 2021, this is a hard error.
|
// In Rust 2021, this is a hard error.
|
||||||
let sugg = if prefix == "rb" {
|
let sugg = if prefix == "rb" {
|
||||||
Some(errors::UnknownPrefixSugg::UseBr(prefix_span))
|
Some(errors::UnknownPrefixSugg::UseBr(prefix_span))
|
||||||
|
@ -2050,7 +2050,7 @@ impl<'a> Parser<'a> {
|
|||||||
};
|
};
|
||||||
// On an error path, eagerly consider a lifetime to be an unclosed character lit, if that
|
// On an error path, eagerly consider a lifetime to be an unclosed character lit, if that
|
||||||
// makes sense.
|
// makes sense.
|
||||||
if let Some(ident) = self.token.lifetime()
|
if let Some((ident, IdentIsRaw::No)) = self.token.lifetime()
|
||||||
&& could_be_unclosed_char_literal(ident)
|
&& could_be_unclosed_char_literal(ident)
|
||||||
{
|
{
|
||||||
let lt = self.expect_lifetime();
|
let lt = self.expect_lifetime();
|
||||||
@ -2925,9 +2925,9 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn eat_label(&mut self) -> Option<Label> {
|
pub(crate) fn eat_label(&mut self) -> Option<Label> {
|
||||||
if let Some(ident) = self.token.lifetime() {
|
if let Some((ident, is_raw)) = self.token.lifetime() {
|
||||||
// Disallow `'fn`, but with a better error message than `expect_lifetime`.
|
// Disallow `'fn`, but with a better error message than `expect_lifetime`.
|
||||||
if ident.without_first_quote().is_reserved() {
|
if matches!(is_raw, IdentIsRaw::No) && ident.without_first_quote().is_reserved() {
|
||||||
self.dcx().emit_err(errors::InvalidLabel { span: ident.span, name: ident.name });
|
self.dcx().emit_err(errors::InvalidLabel { span: ident.span, name: ident.name });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1666,7 +1666,7 @@ enum FlatToken {
|
|||||||
pub enum ParseNtResult {
|
pub enum ParseNtResult {
|
||||||
Tt(TokenTree),
|
Tt(TokenTree),
|
||||||
Ident(Ident, IdentIsRaw),
|
Ident(Ident, IdentIsRaw),
|
||||||
Lifetime(Ident),
|
Lifetime(Ident, IdentIsRaw),
|
||||||
|
|
||||||
/// This case will eventually be removed, along with `Token::Interpolate`.
|
/// This case will eventually be removed, along with `Token::Interpolate`.
|
||||||
Nt(Lrc<Nonterminal>),
|
Nt(Lrc<Nonterminal>),
|
||||||
|
@ -88,7 +88,7 @@ impl<'a> Parser<'a> {
|
|||||||
},
|
},
|
||||||
NonterminalKind::Pat(pat_kind) => token.can_begin_pattern(pat_kind),
|
NonterminalKind::Pat(pat_kind) => token.can_begin_pattern(pat_kind),
|
||||||
NonterminalKind::Lifetime => match &token.kind {
|
NonterminalKind::Lifetime => match &token.kind {
|
||||||
token::Lifetime(_) | token::NtLifetime(..) => true,
|
token::Lifetime(..) | token::NtLifetime(..) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
NonterminalKind::TT | NonterminalKind::Item | NonterminalKind::Stmt => {
|
NonterminalKind::TT | NonterminalKind::Item | NonterminalKind::Stmt => {
|
||||||
@ -171,9 +171,9 @@ impl<'a> Parser<'a> {
|
|||||||
NonterminalKind::Lifetime => {
|
NonterminalKind::Lifetime => {
|
||||||
// We want to keep `'keyword` parsing, just like `keyword` is still
|
// We want to keep `'keyword` parsing, just like `keyword` is still
|
||||||
// an ident for nonterminal purposes.
|
// an ident for nonterminal purposes.
|
||||||
return if let Some(ident) = self.token.lifetime() {
|
return if let Some((ident, is_raw)) = self.token.lifetime() {
|
||||||
self.bump();
|
self.bump();
|
||||||
Ok(ParseNtResult::Lifetime(ident))
|
Ok(ParseNtResult::Lifetime(ident, is_raw))
|
||||||
} else {
|
} else {
|
||||||
Err(self.dcx().create_err(UnexpectedNonterminal::Lifetime {
|
Err(self.dcx().create_err(UnexpectedNonterminal::Lifetime {
|
||||||
span: self.token.span,
|
span: self.token.span,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use rustc_ast::mut_visit::{walk_pat, MutVisitor};
|
use rustc_ast::mut_visit::{walk_pat, MutVisitor};
|
||||||
use rustc_ast::ptr::P;
|
use rustc_ast::ptr::P;
|
||||||
use rustc_ast::token::{self, BinOpToken, Delimiter, Token};
|
use rustc_ast::token::{self, BinOpToken, Delimiter, IdentIsRaw, Token};
|
||||||
use rustc_ast::{
|
use rustc_ast::{
|
||||||
self as ast, AttrVec, BindingMode, ByRef, Expr, ExprKind, MacCall, Mutability, Pat, PatField,
|
self as ast, AttrVec, BindingMode, ByRef, Expr, ExprKind, MacCall, Mutability, Pat, PatField,
|
||||||
PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax,
|
PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax,
|
||||||
@ -548,7 +548,7 @@ impl<'a> Parser<'a> {
|
|||||||
None => PatKind::Path(qself, path),
|
None => PatKind::Path(qself, path),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(lt) = self.token.lifetime()
|
} else if let Some((lt, IdentIsRaw::No)) = self.token.lifetime()
|
||||||
// In pattern position, we're totally fine with using "next token isn't colon"
|
// In pattern position, we're totally fine with using "next token isn't colon"
|
||||||
// as a heuristic. We could probably just always try to recover if it's a lifetime,
|
// as a heuristic. We could probably just always try to recover if it's a lifetime,
|
||||||
// because we never have `'a: label {}` in a pattern position anyways, but it does
|
// because we never have `'a: label {}` in a pattern position anyways, but it does
|
||||||
@ -689,7 +689,7 @@ impl<'a> Parser<'a> {
|
|||||||
/// Parse `&pat` / `&mut pat`.
|
/// Parse `&pat` / `&mut pat`.
|
||||||
fn parse_pat_deref(&mut self, expected: Option<Expected>) -> PResult<'a, PatKind> {
|
fn parse_pat_deref(&mut self, expected: Option<Expected>) -> PResult<'a, PatKind> {
|
||||||
self.expect_and()?;
|
self.expect_and()?;
|
||||||
if let Some(lifetime) = self.token.lifetime() {
|
if let Some((lifetime, _)) = self.token.lifetime() {
|
||||||
self.bump(); // `'a`
|
self.bump(); // `'a`
|
||||||
|
|
||||||
self.dcx().emit_err(UnexpectedLifetimeInPattern {
|
self.dcx().emit_err(UnexpectedLifetimeInPattern {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use rustc_ast::ptr::P;
|
use rustc_ast::ptr::P;
|
||||||
use rustc_ast::token::{self, BinOpToken, Delimiter, Token, TokenKind};
|
use rustc_ast::token::{self, BinOpToken, Delimiter, IdentIsRaw, Token, TokenKind};
|
||||||
use rustc_ast::util::case::Case;
|
use rustc_ast::util::case::Case;
|
||||||
use rustc_ast::{
|
use rustc_ast::{
|
||||||
self as ast, BareFnTy, BoundAsyncness, BoundConstness, BoundPolarity, FnRetTy, GenericBound,
|
self as ast, BareFnTy, BoundAsyncness, BoundConstness, BoundPolarity, FnRetTy, GenericBound,
|
||||||
@ -1285,8 +1285,9 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
/// Parses a single lifetime `'a` or panics.
|
/// Parses a single lifetime `'a` or panics.
|
||||||
pub(super) fn expect_lifetime(&mut self) -> Lifetime {
|
pub(super) fn expect_lifetime(&mut self) -> Lifetime {
|
||||||
if let Some(ident) = self.token.lifetime() {
|
if let Some((ident, is_raw)) = self.token.lifetime() {
|
||||||
if ident.without_first_quote().is_reserved()
|
if matches!(is_raw, IdentIsRaw::No)
|
||||||
|
&& ident.without_first_quote().is_reserved()
|
||||||
&& ![kw::UnderscoreLifetime, kw::StaticLifetime].contains(&ident.name)
|
&& ![kw::UnderscoreLifetime, kw::StaticLifetime].contains(&ident.name)
|
||||||
{
|
{
|
||||||
self.dcx().emit_err(errors::KeywordLifetime { span: ident.span });
|
self.dcx().emit_err(errors::KeywordLifetime { span: ident.span });
|
||||||
|
@ -879,7 +879,9 @@ impl<'src> Classifier<'src> {
|
|||||||
| TokenKind::UnknownPrefix
|
| TokenKind::UnknownPrefix
|
||||||
| TokenKind::InvalidPrefix
|
| TokenKind::InvalidPrefix
|
||||||
| TokenKind::InvalidIdent => Class::Ident(self.new_span(before, text)),
|
| TokenKind::InvalidIdent => Class::Ident(self.new_span(before, text)),
|
||||||
TokenKind::Lifetime { .. } => Class::Lifetime,
|
TokenKind::Lifetime { .. }
|
||||||
|
| TokenKind::RawLifetime
|
||||||
|
| TokenKind::UnknownPrefixLifetime => Class::Lifetime,
|
||||||
TokenKind::Eof => panic!("Eof in advance"),
|
TokenKind::Eof => panic!("Eof in advance"),
|
||||||
};
|
};
|
||||||
// Anything that didn't return above is the simple case where we the
|
// Anything that didn't return above is the simple case where we the
|
||||||
|
@ -198,6 +198,13 @@ impl<'a> Converter<'a> {
|
|||||||
}
|
}
|
||||||
LIFETIME_IDENT
|
LIFETIME_IDENT
|
||||||
}
|
}
|
||||||
|
rustc_lexer::TokenKind::UnknownPrefixLifetime => {
|
||||||
|
err = "Unknown lifetime prefix";
|
||||||
|
LIFETIME_IDENT
|
||||||
|
}
|
||||||
|
rustc_lexer::TokenKind::RawLifetime => {
|
||||||
|
LIFETIME_IDENT
|
||||||
|
}
|
||||||
|
|
||||||
rustc_lexer::TokenKind::Semi => T![;],
|
rustc_lexer::TokenKind::Semi => T![;],
|
||||||
rustc_lexer::TokenKind::Comma => T![,],
|
rustc_lexer::TokenKind::Comma => T![,],
|
||||||
|
@ -462,7 +462,7 @@ fn rewrite_empty_block(
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let label_str = rewrite_label(label);
|
let label_str = rewrite_label(context, label);
|
||||||
if attrs.map_or(false, |a| !inner_attributes(a).is_empty()) {
|
if attrs.map_or(false, |a| !inner_attributes(a).is_empty()) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -527,7 +527,7 @@ fn rewrite_single_line_block(
|
|||||||
if let Some(block_expr) = stmt::Stmt::from_simple_block(context, block, attrs) {
|
if let Some(block_expr) = stmt::Stmt::from_simple_block(context, block, attrs) {
|
||||||
let expr_shape = shape.offset_left(last_line_width(prefix))?;
|
let expr_shape = shape.offset_left(last_line_width(prefix))?;
|
||||||
let expr_str = block_expr.rewrite(context, expr_shape)?;
|
let expr_str = block_expr.rewrite(context, expr_shape)?;
|
||||||
let label_str = rewrite_label(label);
|
let label_str = rewrite_label(context, label);
|
||||||
let result = format!("{prefix}{label_str}{{ {expr_str} }}");
|
let result = format!("{prefix}{label_str}{{ {expr_str} }}");
|
||||||
if result.len() <= shape.width && !result.contains('\n') {
|
if result.len() <= shape.width && !result.contains('\n') {
|
||||||
return Some(result);
|
return Some(result);
|
||||||
@ -562,7 +562,7 @@ pub(crate) fn rewrite_block_with_visitor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let inner_attrs = attrs.map(inner_attributes);
|
let inner_attrs = attrs.map(inner_attributes);
|
||||||
let label_str = rewrite_label(label);
|
let label_str = rewrite_label(context, label);
|
||||||
visitor.visit_block(block, inner_attrs.as_deref(), has_braces);
|
visitor.visit_block(block, inner_attrs.as_deref(), has_braces);
|
||||||
let visitor_context = visitor.get_context();
|
let visitor_context = visitor.get_context();
|
||||||
context
|
context
|
||||||
@ -939,7 +939,7 @@ impl<'a> ControlFlow<'a> {
|
|||||||
fresh_shape
|
fresh_shape
|
||||||
};
|
};
|
||||||
|
|
||||||
let label_string = rewrite_label(self.label);
|
let label_string = rewrite_label(context, self.label);
|
||||||
// 1 = space after keyword.
|
// 1 = space after keyword.
|
||||||
let offset = self.keyword.len() + label_string.len() + 1;
|
let offset = self.keyword.len() + label_string.len() + 1;
|
||||||
|
|
||||||
@ -1168,9 +1168,9 @@ impl<'a> Rewrite for ControlFlow<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rewrite_label(opt_label: Option<ast::Label>) -> Cow<'static, str> {
|
fn rewrite_label(context: &RewriteContext<'_>, opt_label: Option<ast::Label>) -> Cow<'static, str> {
|
||||||
match opt_label {
|
match opt_label {
|
||||||
Some(label) => Cow::from(format!("{}: ", label.ident)),
|
Some(label) => Cow::from(format!("{}: ", context.snippet(label.ident.span))),
|
||||||
None => Cow::from(""),
|
None => Cow::from(""),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1074,7 +1074,7 @@ fn force_space_before(tok: &TokenKind) -> bool {
|
|||||||
fn ident_like(tok: &Token) -> bool {
|
fn ident_like(tok: &Token) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
tok.kind,
|
tok.kind,
|
||||||
TokenKind::Ident(..) | TokenKind::Literal(..) | TokenKind::Lifetime(_)
|
TokenKind::Ident(..) | TokenKind::Literal(..) | TokenKind::Lifetime(..)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1099,7 +1099,9 @@ fn next_space(tok: &TokenKind) -> SpaceState {
|
|||||||
| TokenKind::OpenDelim(_)
|
| TokenKind::OpenDelim(_)
|
||||||
| TokenKind::CloseDelim(_) => SpaceState::Never,
|
| TokenKind::CloseDelim(_) => SpaceState::Never,
|
||||||
|
|
||||||
TokenKind::Literal(..) | TokenKind::Ident(..) | TokenKind::Lifetime(_) => SpaceState::Ident,
|
TokenKind::Literal(..) | TokenKind::Ident(..) | TokenKind::Lifetime(..) => {
|
||||||
|
SpaceState::Ident
|
||||||
|
}
|
||||||
|
|
||||||
_ => SpaceState::Always,
|
_ => SpaceState::Always,
|
||||||
}
|
}
|
||||||
|
@ -548,7 +548,7 @@ impl Rewrite for ast::AnonConst {
|
|||||||
|
|
||||||
impl Rewrite for ast::Lifetime {
|
impl Rewrite for ast::Lifetime {
|
||||||
fn rewrite(&self, context: &RewriteContext<'_>, _: Shape) -> Option<String> {
|
fn rewrite(&self, context: &RewriteContext<'_>, _: Shape) -> Option<String> {
|
||||||
Some(rewrite_ident(context, self.ident).to_owned())
|
Some(context.snippet(self.ident.span).to_owned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
15
src/tools/rustfmt/tests/target/raw-lifetimes.rs
Normal file
15
src/tools/rustfmt/tests/target/raw-lifetimes.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// rustfmt-edition: 2021
|
||||||
|
|
||||||
|
// Simple idempotence test for raw lifetimes.
|
||||||
|
|
||||||
|
fn test<'r#gen>() -> &'r#gen () {
|
||||||
|
// Test raw lifetimes...
|
||||||
|
}
|
||||||
|
|
||||||
|
fn label() {
|
||||||
|
'r#label: {
|
||||||
|
// Test raw labels.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
10
tests/ui/lexer/prefixed-lifetime.rs
Normal file
10
tests/ui/lexer/prefixed-lifetime.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
//@ edition: 2021
|
||||||
|
|
||||||
|
macro_rules! w {
|
||||||
|
($($tt:tt)*) => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
w!('foo#lifetime);
|
||||||
|
//~^ ERROR prefix `'foo` is unknown
|
||||||
|
|
||||||
|
fn main() {}
|
14
tests/ui/lexer/prefixed-lifetime.stderr
Normal file
14
tests/ui/lexer/prefixed-lifetime.stderr
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
error: prefix `'foo` is unknown
|
||||||
|
--> $DIR/prefixed-lifetime.rs:7:4
|
||||||
|
|
|
||||||
|
LL | w!('foo#lifetime);
|
||||||
|
| ^^^^ unknown prefix
|
||||||
|
|
|
||||||
|
= note: prefixed identifiers and literals are reserved since Rust 2021
|
||||||
|
help: consider inserting whitespace here
|
||||||
|
|
|
||||||
|
LL | w!('foo #lifetime);
|
||||||
|
| +
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
8
tests/ui/lifetimes/raw/gen-lt.e2024.stderr
Normal file
8
tests/ui/lifetimes/raw/gen-lt.e2024.stderr
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
error: lifetimes cannot use keyword names
|
||||||
|
--> $DIR/gen-lt.rs:11:11
|
||||||
|
|
|
||||||
|
LL | fn gen_lt<'gen>() {}
|
||||||
|
| ^^^^
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
14
tests/ui/lifetimes/raw/gen-lt.rs
Normal file
14
tests/ui/lifetimes/raw/gen-lt.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
//@ revisions: e2021 e2024
|
||||||
|
|
||||||
|
//@[e2021] edition:2021
|
||||||
|
//@[e2024] edition:2024
|
||||||
|
//@[e2024] compile-flags: -Zunstable-options
|
||||||
|
|
||||||
|
//@[e2021] check-pass
|
||||||
|
|
||||||
|
fn raw_gen_lt<'r#gen>() {}
|
||||||
|
|
||||||
|
fn gen_lt<'gen>() {}
|
||||||
|
//[e2024]~^ ERROR lifetimes cannot use keyword names
|
||||||
|
|
||||||
|
fn main() {}
|
8
tests/ui/lifetimes/raw/lifetimes-eq.rs
Normal file
8
tests/ui/lifetimes/raw/lifetimes-eq.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
//@ edition: 2021
|
||||||
|
//@ check-pass
|
||||||
|
|
||||||
|
// Test that `'r#a` is `'a`.
|
||||||
|
|
||||||
|
fn test<'r#a>(x: &'a ()) {}
|
||||||
|
|
||||||
|
fn main() {}
|
12
tests/ui/lifetimes/raw/macro-lt.rs
Normal file
12
tests/ui/lifetimes/raw/macro-lt.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
//@ check-pass
|
||||||
|
//@ edition: 2021
|
||||||
|
|
||||||
|
macro_rules! lifetime {
|
||||||
|
($lt:lifetime) => {
|
||||||
|
fn hello<$lt>() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lifetime!('r#struct);
|
||||||
|
|
||||||
|
fn main() {}
|
6
tests/ui/lifetimes/raw/multiple-prefixes.rs
Normal file
6
tests/ui/lifetimes/raw/multiple-prefixes.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
//@ edition: 2021
|
||||||
|
|
||||||
|
fn test(x: &'r#r#r ()) {}
|
||||||
|
//~^ ERROR expected type, found `#`
|
||||||
|
|
||||||
|
fn main() {}
|
8
tests/ui/lifetimes/raw/multiple-prefixes.stderr
Normal file
8
tests/ui/lifetimes/raw/multiple-prefixes.stderr
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
error: expected type, found `#`
|
||||||
|
--> $DIR/multiple-prefixes.rs:3:17
|
||||||
|
|
|
||||||
|
LL | fn test(x: &'r#r#r ()) {}
|
||||||
|
| ^ expected type
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
8
tests/ui/lifetimes/raw/prim-lt.rs
Normal file
8
tests/ui/lifetimes/raw/prim-lt.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
//@ check-pass
|
||||||
|
//@ edition: 2021
|
||||||
|
|
||||||
|
// Checks a primitive name can be defined as a lifetime.
|
||||||
|
|
||||||
|
fn foo<'r#i32>() {}
|
||||||
|
|
||||||
|
fn main() {}
|
21
tests/ui/lifetimes/raw/simple.rs
Normal file
21
tests/ui/lifetimes/raw/simple.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
//@ check-pass
|
||||||
|
//@ edition: 2021
|
||||||
|
|
||||||
|
fn foo<'r#struct>() {}
|
||||||
|
|
||||||
|
fn hr<T>() where for<'r#struct> T: Into<&'r#struct ()> {}
|
||||||
|
|
||||||
|
trait Foo<'r#struct> {}
|
||||||
|
|
||||||
|
trait Bar<'r#struct> {
|
||||||
|
fn method(&'r#struct self) {}
|
||||||
|
fn method2(self: &'r#struct Self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn labeled() {
|
||||||
|
'r#struct: loop {
|
||||||
|
break 'r#struct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
8
tests/ui/lifetimes/raw/static-lt.rs
Normal file
8
tests/ui/lifetimes/raw/static-lt.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
//@ check-pass
|
||||||
|
//@ edition: 2021
|
||||||
|
|
||||||
|
// Makes sure that `'r#static` is `'static`
|
||||||
|
|
||||||
|
const FOO: &'r#static str = "hello, world";
|
||||||
|
|
||||||
|
fn main() {}
|
12
tests/ui/lifetimes/raw/three-tokens.rs
Normal file
12
tests/ui/lifetimes/raw/three-tokens.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
//@ edition: 2015
|
||||||
|
//@ check-pass
|
||||||
|
// Ensure that we parse `'r#lt` as three tokens in edition 2015.
|
||||||
|
|
||||||
|
macro_rules! ed2015 {
|
||||||
|
('r # lt) => {};
|
||||||
|
($lt:lifetime) => { compile_error!() };
|
||||||
|
}
|
||||||
|
|
||||||
|
ed2015!('r#lt);
|
||||||
|
|
||||||
|
fn main() {}
|
@ -31,5 +31,14 @@ LL | () => { mod test { fn gen() {} } }
|
|||||||
= warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024!
|
= warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024!
|
||||||
= note: for more information, see issue #49716 <https://github.com/rust-lang/rust/issues/49716>
|
= note: for more information, see issue #49716 <https://github.com/rust-lang/rust/issues/49716>
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
error: `gen` is a keyword in the 2024 edition
|
||||||
|
--> $DIR/gen-kw.rs:25:9
|
||||||
|
|
|
||||||
|
LL | fn test<'gen>() {}
|
||||||
|
| ^^^^ help: you can use a raw identifier to stay compatible: `'r#gen`
|
||||||
|
|
|
||||||
|
= warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024!
|
||||||
|
= note: for more information, see issue #49716 <https://github.com/rust-lang/rust/issues/49716>
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
||||||
|
@ -31,5 +31,14 @@ LL | () => { mod test { fn gen() {} } }
|
|||||||
= warning: this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2024!
|
= warning: this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2024!
|
||||||
= note: for more information, see issue #49716 <https://github.com/rust-lang/rust/issues/49716>
|
= note: for more information, see issue #49716 <https://github.com/rust-lang/rust/issues/49716>
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
error: `gen` is a keyword in the 2024 edition
|
||||||
|
--> $DIR/gen-kw.rs:25:9
|
||||||
|
|
|
||||||
|
LL | fn test<'gen>() {}
|
||||||
|
| ^^^^ help: you can use a raw identifier to stay compatible: `'r#gen`
|
||||||
|
|
|
||||||
|
= warning: this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2024!
|
||||||
|
= note: for more information, see issue #49716 <https://github.com/rust-lang/rust/issues/49716>
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
||||||
|
@ -22,4 +22,9 @@ macro_rules! t {
|
|||||||
//[e2018]~| WARNING this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2024!
|
//[e2018]~| WARNING this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2024!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test<'gen>() {}
|
||||||
|
//~^ ERROR `gen` is a keyword in the 2024 edition
|
||||||
|
//[e2015]~| WARNING this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024!
|
||||||
|
//[e2018]~| WARNING this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2024!
|
||||||
|
|
||||||
t!();
|
t!();
|
||||||
|
Loading…
Reference in New Issue
Block a user