summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_parse
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_parse')
-rw-r--r--compiler/rustc_parse/messages.ftl26
-rw-r--r--compiler/rustc_parse/src/errors.rs85
-rw-r--r--compiler/rustc_parse/src/lexer/diagnostics.rs2
-rw-r--r--compiler/rustc_parse/src/lexer/mod.rs42
-rw-r--r--compiler/rustc_parse/src/lexer/tokentrees.rs2
-rw-r--r--compiler/rustc_parse/src/lexer/unescape_error_reporting.rs39
-rw-r--r--compiler/rustc_parse/src/lexer/unicode_chars.rs2
-rw-r--r--compiler/rustc_parse/src/lib.rs7
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs6
-rw-r--r--compiler/rustc_parse/src/parser/attr_wrapper.rs41
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs124
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs94
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs2
-rw-r--r--compiler/rustc_parse/src/parser/item.rs193
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs240
-rw-r--r--compiler/rustc_parse/src/parser/nonterminal.rs89
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs132
-rw-r--r--compiler/rustc_parse/src/parser/path.rs2
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs12
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs26
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs25
21 files changed, 703 insertions, 488 deletions
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 9787d98c1..34cc0998c 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -23,6 +23,8 @@ parse_async_block_in_2015 = `async` blocks are only allowed in Rust 2018 or late
parse_async_fn_in_2015 = `async fn` is not permitted in Rust 2015
.label = to use `async fn`, switch to Rust 2018 or later
+parse_async_move_block_in_2015 = `async move` blocks are only allowed in Rust 2018 or later
+
parse_async_move_order_incorrect = the order of `move` and `async` is incorrect
.suggestion = try switching the order
@@ -270,6 +272,8 @@ parse_found_expr_would_be_stmt = expected expression, found `{$token}`
parse_function_body_equals_expr = function body cannot be `= expression;`
.suggestion = surround the expression with `{"{"}` and `{"}"}` instead of `=` and `;`
+parse_generic_args_in_pat_require_turbofish_syntax = generic args in patterns require the turbofish syntax
+
parse_generic_parameters_without_angle_brackets = generic parameters without surrounding angle brackets
.suggestion = surround the type parameters with angle brackets
@@ -306,8 +310,8 @@ parse_inclusive_range_no_end = inclusive range with no end
.suggestion_open_range = use `..` instead
.note = inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
-parse_incorrect_braces_trait_bounds = incorrect braces around trait bounds
- .suggestion = remove the parentheses
+parse_incorrect_parens_trait_bounds = incorrect parentheses around trait bounds
+parse_incorrect_parens_trait_bounds_sugg = fix the parentheses
parse_incorrect_semicolon =
expected item, found `;`
@@ -457,6 +461,12 @@ parse_loop_else = `{$loop_kind}...else` loops are not supported
.note = consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
.loop_keyword = `else` is attached to this loop
+parse_macro_expands_to_adt_field = macros cannot expand to {$adt_ty} fields
+
+parse_macro_expands_to_enum_variant = macros cannot expand to enum variants
+
+parse_macro_expands_to_match_arm = macros cannot expand to match arms
+
parse_macro_invocation_visibility = can't qualify macro invocation with `pub`
.suggestion = remove the visibility
.help = try adjusting the macro to put `{$vis}` inside the invocation
@@ -690,6 +700,8 @@ parse_single_colon_import_path = expected `::`, found `:`
parse_single_colon_struct_type = found single colon in a struct field type path
.suggestion = write a path separator here
+parse_static_with_generics = static items may not have generic parameters
+
parse_struct_literal_body_without_path =
struct literal body without path
.suggestion = you might have forgotten to add the struct literal inside the block
@@ -722,6 +734,10 @@ parse_sugg_wrap_pattern_in_parens = wrap the pattern in parentheses
parse_switch_mut_let_order =
switch the order of `mut` and `let`
+
+parse_ternary_operator = Rust has no ternary operator
+ .help = use an `if-else` expression instead
+
parse_tilde_const_lifetime = `~const` may only modify trait bounds, not lifetime bounds
parse_tilde_is_not_unary_operator = `~` cannot be used as a unary operator
@@ -847,6 +863,12 @@ parse_visibility_not_followed_by_item = visibility `{$vis}` is not followed by a
.label = the visibility
.help = you likely meant to define an item, e.g., `{$vis} fn foo() {"{}"}`
+parse_where_clause_before_const_body = where clauses are not allowed before const item bodies
+ .label = unexpected where clause
+ .name_label = while parsing this const item
+ .body_label = the item body
+ .suggestion = move the body before the where clause
+
parse_where_clause_before_tuple_struct_body = where clauses are not allowed before tuple struct bodies
.label = unexpected where clause
.name_label = while parsing this tuple struct
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 96e1c0e3c..e0b1e3678 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -365,6 +365,14 @@ pub(crate) enum IfExpressionMissingThenBlockSub {
AddThenBlock(#[primary_span] Span),
}
+#[derive(Diagnostic)]
+#[diag(parse_ternary_operator)]
+#[help]
+pub struct TernaryOperator {
+ #[primary_span]
+ pub span: Span,
+}
+
#[derive(Subdiagnostic)]
#[suggestion(parse_extra_if_in_let_else, applicability = "maybe-incorrect", code = "")]
pub(crate) struct IfExpressionLetSomeSub {
@@ -1427,6 +1435,13 @@ pub(crate) struct AsyncBlockIn2015 {
}
#[derive(Diagnostic)]
+#[diag(parse_async_move_block_in_2015)]
+pub(crate) struct AsyncMoveBlockIn2015 {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
#[diag(parse_self_argument_pointer)]
pub(crate) struct SelfArgumentPointer {
#[primary_span]
@@ -1801,6 +1816,12 @@ pub struct UnknownPrefix<'a> {
}
#[derive(Subdiagnostic)]
+#[note(parse_macro_expands_to_adt_field)]
+pub struct MacroExpandsToAdtField<'a> {
+ pub adt_ty: &'a str,
+}
+
+#[derive(Subdiagnostic)]
pub enum UnknownPrefixSugg {
#[suggestion(
parse_suggestion_br,
@@ -2615,21 +2636,24 @@ pub(crate) struct MissingPlusBounds {
}
#[derive(Diagnostic)]
-#[diag(parse_incorrect_braces_trait_bounds)]
-pub(crate) struct IncorrectBracesTraitBounds {
+#[diag(parse_incorrect_parens_trait_bounds)]
+pub(crate) struct IncorrectParensTraitBounds {
#[primary_span]
pub span: Vec<Span>,
#[subdiagnostic]
- pub sugg: IncorrectBracesTraitBoundsSugg,
+ pub sugg: IncorrectParensTraitBoundsSugg,
}
#[derive(Subdiagnostic)]
-#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
-pub(crate) struct IncorrectBracesTraitBoundsSugg {
+#[multipart_suggestion(
+ parse_incorrect_parens_trait_bounds_sugg,
+ applicability = "machine-applicable"
+)]
+pub(crate) struct IncorrectParensTraitBoundsSugg {
#[suggestion_part(code = " ")]
- pub l: Span,
- #[suggestion_part(code = "")]
- pub r: Span,
+ pub wrong_span: Span,
+ #[suggestion_part(code = "(")]
+ pub new_span: Span,
}
#[derive(Diagnostic)]
@@ -2692,3 +2716,48 @@ pub(crate) struct ExpectedBuiltinIdent {
#[primary_span]
pub span: Span,
}
+
+#[derive(Diagnostic)]
+#[diag(parse_static_with_generics)]
+pub(crate) struct StaticWithGenerics {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_where_clause_before_const_body)]
+pub(crate) struct WhereClauseBeforeConstBody {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ #[label(parse_name_label)]
+ pub name: Span,
+ #[label(parse_body_label)]
+ pub body: Span,
+ #[subdiagnostic]
+ pub sugg: Option<WhereClauseBeforeConstBodySugg>,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
+pub(crate) struct WhereClauseBeforeConstBodySugg {
+ #[suggestion_part(code = "= {snippet} ")]
+ pub left: Span,
+ pub snippet: String,
+ #[suggestion_part(code = "")]
+ pub right: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_generic_args_in_pat_require_turbofish_syntax)]
+pub(crate) struct GenericArgsInPatRequireTurbofishSyntax {
+ #[primary_span]
+ pub span: Span,
+ #[suggestion(
+ parse_sugg_turbofish_syntax,
+ style = "verbose",
+ code = "::",
+ applicability = "maybe-incorrect"
+ )]
+ pub suggest_turbofish: Span,
+}
diff --git a/compiler/rustc_parse/src/lexer/diagnostics.rs b/compiler/rustc_parse/src/lexer/diagnostics.rs
index 9e6d27bf0..b50bb47f2 100644
--- a/compiler/rustc_parse/src/lexer/diagnostics.rs
+++ b/compiler/rustc_parse/src/lexer/diagnostics.rs
@@ -46,7 +46,7 @@ pub fn report_missing_open_delim(
};
err.span_label(
unmatch_brace.found_span.shrink_to_lo(),
- format!("missing open `{}` for this delimiter", missed_open),
+ format!("missing open `{missed_open}` for this delimiter"),
);
reported_missing_open = true;
}
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index c6e6b46e4..a375a1d69 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -9,8 +9,8 @@ use rustc_ast::tokenstream::TokenStream;
use rustc_ast::util::unicode::contains_text_flow_control_chars;
use rustc_errors::{error_code, Applicability, Diagnostic, DiagnosticBuilder, StashKey};
use rustc_lexer::unescape::{self, EscapeError, Mode};
-use rustc_lexer::Cursor;
use rustc_lexer::{Base, DocStyle, RawStrError};
+use rustc_lexer::{Cursor, LiteralKind};
use rustc_session::lint::builtin::{
RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
};
@@ -74,7 +74,6 @@ pub(crate) fn parse_token_trees<'a>(
// because the delimiter mismatch is more likely to be the root cause of error
let mut buffer = Vec::with_capacity(1);
- // Not using `emit_unclosed_delims` to use `db.buffer`
for unmatched in unmatched_delims {
if let Some(err) = make_unclosed_delims_error(unmatched, &sess) {
err.buffer(&mut buffer);
@@ -118,6 +117,7 @@ impl<'a> StringReader<'a> {
let mut swallow_next_invalid = 0;
// Skip trivial (whitespace & comments) tokens
loop {
+ let str_before = self.cursor.as_str();
let token = self.cursor.advance_token();
let start = self.pos;
self.pos = self.pos + BytePos(token.len);
@@ -165,10 +165,7 @@ impl<'a> StringReader<'a> {
continue;
}
rustc_lexer::TokenKind::Ident => {
- let sym = nfc_normalize(self.str_from(start));
- let span = self.mk_sp(start, self.pos);
- self.sess.symbol_gallery.insert(sym, span);
- token::Ident(sym, false)
+ self.ident(start)
}
rustc_lexer::TokenKind::RawIdent => {
let sym = nfc_normalize(self.str_from(start + BytePos(2)));
@@ -182,10 +179,7 @@ impl<'a> StringReader<'a> {
}
rustc_lexer::TokenKind::UnknownPrefix => {
self.report_unknown_prefix(start);
- let sym = nfc_normalize(self.str_from(start));
- let span = self.mk_sp(start, self.pos);
- self.sess.symbol_gallery.insert(sym, span);
- token::Ident(sym, false)
+ self.ident(start)
}
rustc_lexer::TokenKind::InvalidIdent
// Do not recover an identifier with emoji if the codepoint is a confusable
@@ -203,6 +197,27 @@ impl<'a> StringReader<'a> {
.push(span);
token::Ident(sym, false)
}
+ // split up (raw) c string literals to an ident and a string literal when edition < 2021.
+ rustc_lexer::TokenKind::Literal {
+ kind: kind @ (LiteralKind::CStr { .. } | LiteralKind::RawCStr { .. }),
+ suffix_start: _,
+ } if !self.mk_sp(start, self.pos).edition().at_least_rust_2021() => {
+ let prefix_len = match kind {
+ LiteralKind::CStr { .. } => 1,
+ LiteralKind::RawCStr { .. } => 2,
+ _ => unreachable!(),
+ };
+
+ // reset the state so that only the prefix ("c" or "cr")
+ // was consumed.
+ let lit_start = start + BytePos(prefix_len);
+ self.pos = lit_start;
+ self.cursor = Cursor::new(&str_before[prefix_len as usize..]);
+
+ self.report_unknown_prefix(start);
+ let prefix_span = self.mk_sp(start, lit_start);
+ return (Token::new(self.ident(start), prefix_span), preceded_by_whitespace);
+ }
rustc_lexer::TokenKind::Literal { kind, suffix_start } => {
let suffix_start = start + BytePos(suffix_start);
let (kind, symbol) = self.cook_lexer_literal(start, suffix_start, kind);
@@ -317,6 +332,13 @@ impl<'a> StringReader<'a> {
}
}
+ fn ident(&self, start: BytePos) -> TokenKind {
+ let sym = nfc_normalize(self.str_from(start));
+ let span = self.mk_sp(start, self.pos);
+ self.sess.symbol_gallery.insert(sym, span);
+ token::Ident(sym, false)
+ }
+
fn struct_fatal_span_char(
&self,
from_pos: BytePos,
diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs
index 318a29985..07910113d 100644
--- a/compiler/rustc_parse/src/lexer/tokentrees.rs
+++ b/compiler/rustc_parse/src/lexer/tokentrees.rs
@@ -198,7 +198,7 @@ impl<'a> TokenTreesReader<'a> {
// An unexpected closing delimiter (i.e., there is no
// matching opening delimiter).
let token_str = token_to_string(&self.token);
- let msg = format!("unexpected closing delimiter: `{}`", token_str);
+ let msg = format!("unexpected closing delimiter: `{token_str}`");
let mut err = self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, msg);
report_suspicious_mismatch_block(
diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
index 461a34b67..b659c40b2 100644
--- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
+++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
@@ -27,7 +27,7 @@ pub(crate) fn emit_unescape_error(
lit, span_with_quotes, mode, range, error
);
let last_char = || {
- let c = lit[range.clone()].chars().rev().next().unwrap();
+ let c = lit[range.clone()].chars().next_back().unwrap();
let span = span.with_lo(span.hi() - BytePos(c.len_utf8() as u32));
(c, span)
};
@@ -80,20 +80,14 @@ pub(crate) fn emit_unescape_error(
let sugg = sugg.unwrap_or_else(|| {
let prefix = mode.prefix_noraw();
let mut escaped = String::with_capacity(lit.len());
- let mut chrs = lit.chars().peekable();
- while let Some(first) = chrs.next() {
- match (first, chrs.peek()) {
- ('\\', Some('"')) => {
- escaped.push('\\');
- escaped.push('"');
- chrs.next();
- }
- ('"', _) => {
- escaped.push('\\');
- escaped.push('"')
- }
- (c, _) => escaped.push(c),
- };
+ let mut in_escape = false;
+ for c in lit.chars() {
+ match c {
+ '\\' => in_escape = !in_escape,
+ '"' if !in_escape => escaped.push('\\'),
+ _ => in_escape = false,
+ }
+ escaped.push(c);
}
let sugg = format!("{prefix}\"{escaped}\"");
MoreThanOneCharSugg::Quotes {
@@ -135,7 +129,7 @@ pub(crate) fn emit_unescape_error(
"unknown character escape"
};
let ec = escaped_char(c);
- let mut diag = handler.struct_span_err(span, format!("{}: `{}`", label, ec));
+ let mut diag = handler.struct_span_err(span, format!("{label}: `{ec}`"));
diag.span_label(span, label);
if c == '{' || c == '}' && matches!(mode, Mode::Str | Mode::RawStr) {
diag.help(
@@ -151,7 +145,7 @@ pub(crate) fn emit_unescape_error(
diag.span_suggestion(
span_with_quotes,
"if you meant to write a literal backslash (perhaps escaping in a regular expression), consider a raw string literal",
- format!("r\"{}\"", lit),
+ format!("r\"{lit}\""),
Applicability::MaybeIncorrect,
);
}
@@ -180,21 +174,20 @@ pub(crate) fn emit_unescape_error(
Mode::RawByteStr => "raw byte string literal",
_ => panic!("non-is_byte literal paired with NonAsciiCharInByte"),
};
- let mut err = handler.struct_span_err(span, format!("non-ASCII character in {}", desc));
+ let mut err = handler.struct_span_err(span, format!("non-ASCII character in {desc}"));
let postfix = if unicode_width::UnicodeWidthChar::width(c).unwrap_or(1) == 0 {
- format!(" but is {:?}", c)
+ format!(" but is {c:?}")
} else {
String::new()
};
- err.span_label(span, format!("must be ASCII{}", postfix));
+ err.span_label(span, format!("must be ASCII{postfix}"));
// Note: the \\xHH suggestions are not given for raw byte string
// literals, because they are araw and so cannot use any escapes.
if (c as u32) <= 0xFF && mode != Mode::RawByteStr {
err.span_suggestion(
span,
format!(
- "if you meant to use the unicode code point for {:?}, use a \\xHH escape",
- c
+ "if you meant to use the unicode code point for {c:?}, use a \\xHH escape"
),
format!("\\x{:X}", c as u32),
Applicability::MaybeIncorrect,
@@ -206,7 +199,7 @@ pub(crate) fn emit_unescape_error(
utf8.push(c);
err.span_suggestion(
span,
- format!("if you meant to use the UTF-8 encoding of {:?}, use \\xHH escapes", c),
+ format!("if you meant to use the UTF-8 encoding of {c:?}, use \\xHH escapes"),
utf8.as_bytes()
.iter()
.map(|b: &u8| format!("\\x{:X}", *b))
diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs
index 829d9693e..bbfb160eb 100644
--- a/compiler/rustc_parse/src/lexer/unicode_chars.rs
+++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs
@@ -349,7 +349,7 @@ pub(super) fn check_for_substitution(
let span = Span::with_root_ctxt(pos, pos + Pos::from_usize(ch.len_utf8() * count));
let Some((_, ascii_name, token)) = ASCII_ARRAY.iter().find(|&&(s, _, _)| s == ascii_str) else {
- let msg = format!("substitution character not found for '{}'", ch);
+ let msg = format!("substitution character not found for '{ch}'");
reader.sess.span_diagnostic.span_bug_no_panic(span, msg);
return (None, None);
};
diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs
index 25de78085..892be36aa 100644
--- a/compiler/rustc_parse/src/lib.rs
+++ b/compiler/rustc_parse/src/lib.rs
@@ -8,6 +8,7 @@
#![feature(never_type)]
#![feature(rustc_attrs)]
#![recursion_limit = "256"]
+#![cfg_attr(not(bootstrap), allow(internal_features))]
#[macro_use]
extern crate tracing;
@@ -205,7 +206,7 @@ pub fn stream_to_parser<'a>(
stream: TokenStream,
subparser_name: Option<&'static str>,
) -> Parser<'a> {
- Parser::new(sess, stream, false, subparser_name)
+ Parser::new(sess, stream, subparser_name)
}
/// Runs the given subparser `f` on the tokens of the given `attr`'s item.
@@ -215,7 +216,7 @@ pub fn parse_in<'a, T>(
name: &'static str,
mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
) -> PResult<'a, T> {
- let mut parser = Parser::new(sess, tts, false, Some(name));
+ let mut parser = Parser::new(sess, tts, Some(name));
let result = f(&mut parser)?;
if parser.token != token::Eof {
parser.unexpected()?;
@@ -247,7 +248,7 @@ pub fn parse_cfg_attr(
match parse_in(parse_sess, tokens.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) {
Ok(r) => return Some(r),
Err(mut e) => {
- e.help(format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP))
+ e.help(format!("the valid syntax is `{CFG_ATTR_GRAMMAR_HELP}`"))
.note(CFG_ATTR_NOTE_REF)
.emit();
}
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index ee0abba1c..104de47b9 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -36,7 +36,7 @@ impl<'a> Parser<'a> {
pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, AttrWrapper> {
let mut outer_attrs = ast::AttrVec::new();
let mut just_parsed_doc_comment = false;
- let start_pos = self.token_cursor.num_next_calls;
+ let start_pos = self.num_bump_calls;
loop {
let attr = if self.check(&token::Pound) {
let prev_outer_attr_sp = outer_attrs.last().map(|attr| attr.span);
@@ -277,7 +277,7 @@ impl<'a> Parser<'a> {
pub(crate) fn parse_inner_attributes(&mut self) -> PResult<'a, ast::AttrVec> {
let mut attrs = ast::AttrVec::new();
loop {
- let start_pos: u32 = self.token_cursor.num_next_calls.try_into().unwrap();
+ let start_pos: u32 = self.num_bump_calls.try_into().unwrap();
// Only try to parse if it is an inner attribute (has `!`).
let attr = if self.check(&token::Pound) && self.look_ahead(1, |t| t == &token::Not) {
Some(self.parse_attribute(InnerAttrPolicy::Permitted)?)
@@ -298,7 +298,7 @@ impl<'a> Parser<'a> {
None
};
if let Some(attr) = attr {
- let end_pos: u32 = self.token_cursor.num_next_calls.try_into().unwrap();
+ let end_pos: u32 = self.num_bump_calls.try_into().unwrap();
// If we are currently capturing tokens, mark the location of this inner attribute.
// If capturing ends up creating a `LazyAttrTokenStream`, we will include
// this replace range with it, removing the inner attribute from the final
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index b579da098..5d6c574ba 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -107,7 +107,7 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
let tokens =
std::iter::once((FlatToken::Token(self.start_token.0.clone()), self.start_token.1))
.chain((0..self.num_calls).map(|_| {
- let token = cursor_snapshot.next(cursor_snapshot.desugar_doc_comments);
+ let token = cursor_snapshot.next();
(FlatToken::Token(token.0), token.1)
}))
.take(self.num_calls);
@@ -145,13 +145,11 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
// another replace range will capture the *replaced* tokens for the inner
// range, not the original tokens.
for (range, new_tokens) in replace_ranges.into_iter().rev() {
- assert!(!range.is_empty(), "Cannot replace an empty range: {:?}", range);
+ assert!(!range.is_empty(), "Cannot replace an empty range: {range:?}");
// Replace ranges are only allowed to decrease the number of tokens.
assert!(
range.len() >= new_tokens.len(),
- "Range {:?} has greater len than {:?}",
- range,
- new_tokens
+ "Range {range:?} has greater len than {new_tokens:?}"
);
// Replace any removed tokens with `FlatToken::Empty`.
@@ -215,6 +213,7 @@ impl<'a> Parser<'a> {
let start_token = (self.token.clone(), self.token_spacing);
let cursor_snapshot = self.token_cursor.clone();
+ let start_pos = self.num_bump_calls;
let has_outer_attrs = !attrs.attrs.is_empty();
let prev_capturing = std::mem::replace(&mut self.capture_state.capturing, Capturing::Yes);
@@ -275,8 +274,7 @@ impl<'a> Parser<'a> {
let replace_ranges_end = self.capture_state.replace_ranges.len();
- let cursor_snapshot_next_calls = cursor_snapshot.num_next_calls;
- let mut end_pos = self.token_cursor.num_next_calls;
+ let mut end_pos = self.num_bump_calls;
let mut captured_trailing = false;
@@ -303,12 +301,12 @@ impl<'a> Parser<'a> {
// then extend the range of captured tokens to include it, since the parser
// was not actually bumped past it. When the `LazyAttrTokenStream` gets converted
// into an `AttrTokenStream`, we will create the proper token.
- if self.token_cursor.break_last_token {
+ if self.break_last_token {
assert!(!captured_trailing, "Cannot set break_last_token and have trailing token");
end_pos += 1;
}
- let num_calls = end_pos - cursor_snapshot_next_calls;
+ let num_calls = end_pos - start_pos;
// If we have no attributes, then we will never need to
// use any replace ranges.
@@ -318,7 +316,7 @@ impl<'a> Parser<'a> {
// Grab any replace ranges that occur *inside* the current AST node.
// We will perform the actual replacement when we convert the `LazyAttrTokenStream`
// to an `AttrTokenStream`.
- let start_calls: u32 = cursor_snapshot_next_calls.try_into().unwrap();
+ let start_calls: u32 = start_pos.try_into().unwrap();
self.capture_state.replace_ranges[replace_ranges_start..replace_ranges_end]
.iter()
.cloned()
@@ -333,7 +331,7 @@ impl<'a> Parser<'a> {
start_token,
num_calls,
cursor_snapshot,
- break_last_token: self.token_cursor.break_last_token,
+ break_last_token: self.break_last_token,
replace_ranges,
});
@@ -361,14 +359,10 @@ impl<'a> Parser<'a> {
// with a `FlatToken::AttrTarget`. If this AST node is inside an item
// that has `#[derive]`, then this will allow us to cfg-expand this
// AST node.
- let start_pos =
- if has_outer_attrs { attrs.start_pos } else { cursor_snapshot_next_calls };
+ let start_pos = if has_outer_attrs { attrs.start_pos } else { start_pos };
let new_tokens = vec![(FlatToken::AttrTarget(attr_data), Spacing::Alone)];
- assert!(
- !self.token_cursor.break_last_token,
- "Should not have unglued last token with cfg attr"
- );
+ assert!(!self.break_last_token, "Should not have unglued last token with cfg attr");
let range: Range<u32> = (start_pos.try_into().unwrap())..(end_pos.try_into().unwrap());
self.capture_state.replace_ranges.push((range, new_tokens));
self.capture_state.replace_ranges.extend(inner_attr_replace_ranges);
@@ -409,22 +403,19 @@ fn make_token_stream(
FlatToken::Token(Token { kind: TokenKind::CloseDelim(delim), span }) => {
let frame_data = stack
.pop()
- .unwrap_or_else(|| panic!("Token stack was empty for token: {:?}", token));
+ .unwrap_or_else(|| panic!("Token stack was empty for token: {token:?}"));
let (open_delim, open_sp) = frame_data.open_delim_sp.unwrap();
assert_eq!(
open_delim, delim,
- "Mismatched open/close delims: open={:?} close={:?}",
- open_delim, span
+ "Mismatched open/close delims: open={open_delim:?} close={span:?}"
);
let dspan = DelimSpan::from_pair(open_sp, span);
let stream = AttrTokenStream::new(frame_data.inner);
let delimited = AttrTokenTree::Delimited(dspan, delim, stream);
stack
.last_mut()
- .unwrap_or_else(|| {
- panic!("Bottom token frame is missing for token: {:?}", token)
- })
+ .unwrap_or_else(|| panic!("Bottom token frame is missing for token: {token:?}"))
.inner
.push(delimited);
}
@@ -456,7 +447,7 @@ fn make_token_stream(
.inner
.push(AttrTokenTree::Token(Token::new(unglued_first, first_span), spacing));
} else {
- panic!("Unexpected last token {:?}", last_token)
+ panic!("Unexpected last token {last_token:?}")
}
}
AttrTokenStream::new(final_buf.inner)
@@ -469,6 +460,6 @@ mod size_asserts {
use rustc_data_structures::static_assert_size;
// tidy-alphabetical-start
static_assert_size!(AttrWrapper, 16);
- static_assert_size!(LazyAttrTokenStreamImpl, 120);
+ static_assert_size!(LazyAttrTokenStreamImpl, 104);
// tidy-alphabetical-end
}
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 0ce6a570d..6c8ef3406 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -4,17 +4,18 @@ use super::{
TokenExpectType, TokenType,
};
use crate::errors::{
- AmbiguousPlus, AttributeOnParamType, BadQPathStage2, BadTypePlus, BadTypePlusSub, ColonAsSemi,
- ComparisonOperatorsCannotBeChained, ComparisonOperatorsCannotBeChainedSugg,
- ConstGenericWithoutBraces, ConstGenericWithoutBracesSugg, DocCommentDoesNotDocumentAnything,
- DocCommentOnParamType, DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg,
+ AmbiguousPlus, AsyncMoveBlockIn2015, AttributeOnParamType, BadQPathStage2, BadTypePlus,
+ BadTypePlusSub, ColonAsSemi, ComparisonOperatorsCannotBeChained,
+ ComparisonOperatorsCannotBeChainedSugg, ConstGenericWithoutBraces,
+ ConstGenericWithoutBracesSugg, DocCommentDoesNotDocumentAnything, DocCommentOnParamType,
+ DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg,
GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg,
HelpIdentifierStartsWithNumber, InInTypo, IncorrectAwait, IncorrectSemicolon,
IncorrectUseOfAwait, ParenthesesInForHead, ParenthesesInForHeadSugg,
PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst,
StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens,
StructLiteralNeedingParensSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma,
- UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
+ TernaryOperator, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead,
};
@@ -247,7 +248,7 @@ impl<'a> Parser<'a> {
self.sess.span_diagnostic.struct_span_err(sp, m)
}
- pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, m: impl Into<DiagnosticMessage>) -> ! {
+ pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, m: impl Into<String>) -> ! {
self.sess.span_diagnostic.span_bug(sp, m)
}
@@ -500,6 +501,10 @@ impl<'a> Parser<'a> {
// Special-case "expected `;`" errors
if expected.contains(&TokenType::Token(token::Semi)) {
+ if self.prev_token == token::Question && self.maybe_recover_from_ternary_operator() {
+ return Ok(true);
+ }
+
if self.token.span == DUMMY_SP || self.prev_token.span == DUMMY_SP {
// Likely inside a macro, can't provide meaningful suggestions.
} else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) {
@@ -569,6 +574,12 @@ impl<'a> Parser<'a> {
return Err(self.sess.create_err(UseEqInstead { span: self.token.span }));
}
+ if self.token.is_keyword(kw::Move) && self.prev_token.is_keyword(kw::Async) {
+ // The 2015 edition is in use because parsing of `async move` has failed.
+ let span = self.prev_token.span.to(self.token.span);
+ return Err(self.sess.create_err(AsyncMoveBlockIn2015 { span }));
+ }
+
let expect = tokens_to_string(&expected);
let actual = super::token_descr(&self.token);
let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 {
@@ -608,13 +619,13 @@ impl<'a> Parser<'a> {
if let TokenKind::Ident(prev, _) = &self.prev_token.kind
&& let TokenKind::Ident(cur, _) = &self.token.kind
{
- let concat = Symbol::intern(&format!("{}{}", prev, cur));
+ let concat = Symbol::intern(&format!("{prev}{cur}"));
let ident = Ident::new(concat, DUMMY_SP);
if ident.is_used_keyword() || ident.is_reserved() || ident.is_raw_guess() {
let span = self.prev_token.span.to(self.token.span);
err.span_suggestion_verbose(
span,
- format!("consider removing the space to spell keyword `{}`", concat),
+ format!("consider removing the space to spell keyword `{concat}`"),
concat,
Applicability::MachineApplicable,
);
@@ -1330,6 +1341,45 @@ impl<'a> Parser<'a> {
}
}
+ /// Rust has no ternary operator (`cond ? then : else`). Parse it and try
+ /// to recover from it if `then` and `else` are valid expressions. Returns
+ /// whether it was a ternary operator.
+ pub(super) fn maybe_recover_from_ternary_operator(&mut self) -> bool {
+ if self.prev_token != token::Question {
+ return false;
+ }
+
+ let lo = self.prev_token.span.lo();
+ let snapshot = self.create_snapshot_for_diagnostic();
+
+ if match self.parse_expr() {
+ Ok(_) => true,
+ Err(err) => {
+ err.cancel();
+ // The colon can sometimes be mistaken for type
+ // ascription. Catch when this happens and continue.
+ self.token == token::Colon
+ }
+ } {
+ if self.eat_noexpect(&token::Colon) {
+ match self.parse_expr() {
+ Ok(_) => {
+ self.sess.emit_err(TernaryOperator { span: self.token.span.with_lo(lo) });
+ return true;
+ }
+ Err(err) => {
+ err.cancel();
+ self.restore_snapshot(snapshot);
+ }
+ };
+ }
+ } else {
+ self.restore_snapshot(snapshot);
+ };
+
+ false
+ }
+
pub(super) fn maybe_recover_from_bad_type_plus(&mut self, ty: &Ty) -> PResult<'a, ()> {
// Do not add `+` to expected tokens.
if !self.token.is_like_plus() {
@@ -1434,8 +1484,9 @@ impl<'a> Parser<'a> {
self.inc_dec_standalone_suggest(kind, spans).emit_verbose(&mut err)
}
IsStandalone::Subexpr => {
- let Ok(base_src) = self.span_to_snippet(base.span)
- else { return help_base_case(err, base) };
+ let Ok(base_src) = self.span_to_snippet(base.span) else {
+ return help_base_case(err, base);
+ };
match kind.fixity {
UnaryFixity::Pre => {
self.prefix_inc_dec_suggest(base_src, kind, spans).emit(&mut err)
@@ -1666,13 +1717,7 @@ impl<'a> Parser<'a> {
self.recover_await_prefix(await_sp)?
};
let sp = self.error_on_incorrect_await(lo, hi, &expr, is_question);
- let kind = match expr.kind {
- // Avoid knock-down errors as we don't know whether to interpret this as `foo().await?`
- // or `foo()?.await` (the very reason we went with postfix syntax 😅).
- ExprKind::Try(_) => ExprKind::Err,
- _ => ExprKind::Await(expr, await_sp),
- };
- let expr = self.mk_expr(lo.to(sp), kind);
+ let expr = self.mk_expr(lo.to(sp), ExprKind::Err);
self.maybe_recover_from_bad_qpath(expr)
}
@@ -2056,7 +2101,7 @@ impl<'a> Parser<'a> {
}
pub(super) fn recover_arg_parse(&mut self) -> PResult<'a, (P<ast::Pat>, P<ast::Ty>)> {
- let pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName))?;
+ let pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName), None)?;
self.expect(&token::Colon)?;
let ty = self.parse_ty()?;
@@ -2110,7 +2155,7 @@ impl<'a> Parser<'a> {
}
_ => (
self.token.span,
- format!("expected expression, found {}", super::token_descr(&self.token),),
+ format!("expected expression, found {}", super::token_descr(&self.token)),
),
};
let mut err = self.struct_span_err(span, msg);
@@ -2464,7 +2509,7 @@ impl<'a> Parser<'a> {
// Skip the `:`.
snapshot_pat.bump();
snapshot_type.bump();
- match snapshot_pat.parse_pat_no_top_alt(expected) {
+ match snapshot_pat.parse_pat_no_top_alt(expected, None) {
Err(inner_err) => {
inner_err.cancel();
}
@@ -2590,6 +2635,7 @@ impl<'a> Parser<'a> {
pub(crate) fn maybe_recover_unexpected_comma(
&mut self,
lo: Span,
+ is_mac_invoc: bool,
rt: CommaRecoveryMode,
) -> PResult<'a, ()> {
if self.token != token::Comma {
@@ -2610,24 +2656,28 @@ impl<'a> Parser<'a> {
let seq_span = lo.to(self.prev_token.span);
let mut err = self.struct_span_err(comma_span, "unexpected `,` in pattern");
if let Ok(seq_snippet) = self.span_to_snippet(seq_span) {
- err.multipart_suggestion(
- format!(
- "try adding parentheses to match on a tuple{}",
- if let CommaRecoveryMode::LikelyTuple = rt { "" } else { "..." },
- ),
- vec![
- (seq_span.shrink_to_lo(), "(".to_string()),
- (seq_span.shrink_to_hi(), ")".to_string()),
- ],
- Applicability::MachineApplicable,
- );
- if let CommaRecoveryMode::EitherTupleOrPipe = rt {
- err.span_suggestion(
- seq_span,
- "...or a vertical bar to match on multiple alternatives",
- seq_snippet.replace(',', " |"),
+ if is_mac_invoc {
+ err.note(fluent::parse_macro_expands_to_match_arm);
+ } else {
+ err.multipart_suggestion(
+ format!(
+ "try adding parentheses to match on a tuple{}",
+ if let CommaRecoveryMode::LikelyTuple = rt { "" } else { "..." },
+ ),
+ vec![
+ (seq_span.shrink_to_lo(), "(".to_string()),
+ (seq_span.shrink_to_hi(), ")".to_string()),
+ ],
Applicability::MachineApplicable,
);
+ if let CommaRecoveryMode::EitherTupleOrPipe = rt {
+ err.span_suggestion(
+ seq_span,
+ "...or a vertical bar to match on multiple alternatives",
+ seq_snippet.replace(',', " |"),
+ Applicability::MachineApplicable,
+ );
+ }
}
}
Err(err)
@@ -2728,7 +2778,7 @@ impl<'a> Parser<'a> {
/// sequence of patterns until `)` is reached.
fn skip_pat_list(&mut self) -> PResult<'a, ()> {
while !self.check(&token::CloseDelim(Delimiter::Parenthesis)) {
- self.parse_pat_no_top_alt(None)?;
+ self.parse_pat_no_top_alt(None, None)?;
if !self.eat(&token::Comma) {
return Ok(());
}
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 7ede4fbc3..9ae3ef617 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -22,6 +22,7 @@ use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty
use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
use rustc_ast::{ClosureBinder, MetaItemLit, StmtKind};
use rustc_ast_pretty::pprust;
+use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::{
AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic,
PResult, StashKey,
@@ -193,13 +194,7 @@ impl<'a> Parser<'a> {
self.expected_tokens.push(TokenType::Operator);
while let Some(op) = self.check_assoc_op() {
- // Adjust the span for interpolated LHS to point to the `$lhs` token
- // and not to what it refers to.
- let lhs_span = match self.prev_token.kind {
- TokenKind::Interpolated(..) => self.prev_token.span,
- _ => lhs.span,
- };
-
+ let lhs_span = self.interpolated_or_expr_span(&lhs);
let cur_op_span = self.token.span;
let restrictions = if op.node.is_assign_like() {
self.restrictions & Restrictions::NO_STRUCT_LITERAL
@@ -238,7 +233,7 @@ impl<'a> Parser<'a> {
_ => unreachable!(),
}
.into();
- let invalid = format!("{}=", &sugg);
+ let invalid = format!("{sugg}=");
self.sess.emit_err(errors::InvalidComparisonOperator {
span: sp,
invalid: invalid.clone(),
@@ -626,8 +621,8 @@ impl<'a> Parser<'a> {
fn parse_expr_prefix_common(&mut self, lo: Span) -> PResult<'a, (Span, P<Expr>)> {
self.bump();
- let expr = self.parse_expr_prefix(None);
- let (span, expr) = self.interpolated_or_expr_span(expr)?;
+ let expr = self.parse_expr_prefix(None)?;
+ let span = self.interpolated_or_expr_span(&expr);
Ok((lo.to(span), expr))
}
@@ -702,20 +697,12 @@ impl<'a> Parser<'a> {
self.parse_expr_unary(lo, UnOp::Not)
}
- /// Returns the span of expr, if it was not interpolated or the span of the interpolated token.
- fn interpolated_or_expr_span(
- &self,
- expr: PResult<'a, P<Expr>>,
- ) -> PResult<'a, (Span, P<Expr>)> {
- expr.map(|e| {
- (
- match self.prev_token.kind {
- TokenKind::Interpolated(..) => self.prev_token.span,
- _ => e.span,
- },
- e,
- )
- })
+ /// Returns the span of expr if it was not interpolated, or the span of the interpolated token.
+ fn interpolated_or_expr_span(&self, expr: &Expr) -> Span {
+ match self.prev_token.kind {
+ TokenKind::Interpolated(..) => self.prev_token.span,
+ _ => expr.span,
+ }
}
fn parse_assoc_op_cast(
@@ -857,7 +844,7 @@ impl<'a> Parser<'a> {
let msg = format!(
"cast cannot be followed by {}",
match with_postfix.kind {
- ExprKind::Index(_, _) => "indexing",
+ ExprKind::Index(..) => "indexing",
ExprKind::Try(_) => "`?`",
ExprKind::Field(_, _) => "a field access",
ExprKind::MethodCall(_) => "a method call",
@@ -898,8 +885,8 @@ impl<'a> Parser<'a> {
self.parse_expr_prefix_range(None)
} else {
self.parse_expr_prefix(None)
- };
- let (hi, expr) = self.interpolated_or_expr_span(expr)?;
+ }?;
+ let hi = self.interpolated_or_expr_span(&expr);
let span = lo.to(hi);
if let Some(lt) = lifetime {
self.error_remove_borrow_lifetime(span, lt.ident.span);
@@ -930,8 +917,8 @@ impl<'a> Parser<'a> {
fn parse_expr_dot_or_call(&mut self, attrs: Option<AttrWrapper>) -> PResult<'a, P<Expr>> {
let attrs = self.parse_or_use_outer_attributes(attrs)?;
self.collect_tokens_for_expr(attrs, |this, attrs| {
- let base = this.parse_expr_bottom();
- let (span, base) = this.interpolated_or_expr_span(base)?;
+ let base = this.parse_expr_bottom()?;
+ let span = this.interpolated_or_expr_span(&base);
this.parse_expr_dot_or_call_with(base, span, attrs)
})
}
@@ -1052,7 +1039,7 @@ impl<'a> Parser<'a> {
}
components.push(Punct(c));
} else {
- panic!("unexpected character in a float token: {:?}", c)
+ panic!("unexpected character in a float token: {c:?}")
}
}
if !ident_like.is_empty() {
@@ -1113,7 +1100,7 @@ impl<'a> Parser<'a> {
self.error_unexpected_after_dot();
DestructuredFloat::Error
}
- _ => panic!("unexpected components in a float token: {:?}", components),
+ _ => panic!("unexpected components in a float token: {components:?}"),
}
}
@@ -1167,7 +1154,7 @@ impl<'a> Parser<'a> {
DestructuredFloat::TrailingDot(sym, sym_span, dot_span) => {
assert!(suffix.is_none());
// Analogous to `Self::break_and_eat`
- self.token_cursor.break_last_token = true;
+ self.break_last_token = true;
// This might work, in cases like `1. 2`, and might not,
// in cases like `offset_of!(Ty, 1.)`. It depends on what comes
// after the float-like token, and therefore we have to make
@@ -1304,12 +1291,15 @@ impl<'a> Parser<'a> {
let index = self.parse_expr()?;
self.suggest_missing_semicolon_before_array(prev_span, open_delim_span)?;
self.expect(&token::CloseDelim(Delimiter::Bracket))?;
- Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_index(base, index)))
+ Ok(self.mk_expr(
+ lo.to(self.prev_token.span),
+ self.mk_index(base, index, open_delim_span.to(self.prev_token.span)),
+ ))
}
/// Assuming we have just parsed `.`, continue parsing into an expression.
fn parse_dot_suffix(&mut self, self_arg: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> {
- if self.token.uninterpolated_span().rust_2018() && self.eat_keyword(kw::Await) {
+ if self.token.uninterpolated_span().at_least_rust_2018() && self.eat_keyword(kw::Await) {
return Ok(self.mk_await_expr(self_arg, lo));
}
@@ -1442,8 +1432,8 @@ impl<'a> Parser<'a> {
self.parse_expr_let()
} else if self.eat_keyword(kw::Underscore) {
Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore))
- } else if self.token.uninterpolated_span().rust_2018() {
- // `Span::rust_2018()` is somewhat expensive; don't get it repeatedly.
+ } else if self.token.uninterpolated_span().at_least_rust_2018() {
+ // `Span:.at_least_rust_2018()` is somewhat expensive; don't get it repeatedly.
if self.check_keyword(kw::Async) {
if self.is_async_block() {
// Check for `async {` and `async move {`.
@@ -2230,7 +2220,7 @@ impl<'a> Parser<'a> {
let movability =
if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable };
- let asyncness = if self.token.uninterpolated_span().rust_2018() {
+ let asyncness = if self.token.uninterpolated_span().at_least_rust_2018() {
self.parse_asyncness(Case::Sensitive)
} else {
Async::No
@@ -2338,11 +2328,11 @@ impl<'a> Parser<'a> {
let lo = self.token.span;
let attrs = self.parse_outer_attributes()?;
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
- let pat = this.parse_pat_no_top_alt(Some(Expected::ParameterName))?;
+ let pat = this.parse_pat_no_top_alt(Some(Expected::ParameterName), None)?;
let ty = if this.eat(&token::Colon) {
this.parse_ty()?
} else {
- this.mk_ty(this.prev_token.span, TyKind::Infer)
+ this.mk_ty(pat.span, TyKind::Infer)
};
Ok((
@@ -2500,7 +2490,7 @@ impl<'a> Parser<'a> {
let else_span = self.prev_token.span; // `else`
let attrs = self.parse_outer_attributes()?; // For recovery.
let expr = if self.eat_keyword(kw::If) {
- self.parse_expr_if()?
+ ensure_sufficient_stack(|| self.parse_expr_if())?
} else if self.check(&TokenKind::OpenDelim(Delimiter::Brace)) {
self.parse_simple_block()?
} else {
@@ -2599,7 +2589,7 @@ impl<'a> Parser<'a> {
// Recover from missing expression in `for` loop
if matches!(expr.kind, ExprKind::Block(..))
- && !matches!(self.token.kind, token::OpenDelim(token::Delimiter::Brace))
+ && !matches!(self.token.kind, token::OpenDelim(Delimiter::Brace))
&& self.may_recover()
{
self.sess
@@ -2781,7 +2771,7 @@ impl<'a> Parser<'a> {
return None;
}
let pre_pat_snapshot = self.create_snapshot_for_diagnostic();
- match self.parse_pat_no_top_alt(None) {
+ match self.parse_pat_no_top_alt(None, None) {
Ok(_pat) => {
if self.token.kind == token::FatArrow {
// Reached arm end.
@@ -3003,7 +2993,8 @@ impl<'a> Parser<'a> {
fn is_do_catch_block(&self) -> bool {
self.token.is_keyword(kw::Do)
&& self.is_keyword_ahead(1, &[kw::Catch])
- && self.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace))
+ && self
+ .look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block())
&& !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
}
@@ -3013,8 +3004,9 @@ impl<'a> Parser<'a> {
fn is_try_block(&self) -> bool {
self.token.is_keyword(kw::Try)
- && self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace))
- && self.token.uninterpolated_span().rust_2018()
+ && self
+ .look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block())
+ && self.token.uninterpolated_span().at_least_rust_2018()
}
/// Parses an `async move? {...}` expression.
@@ -3032,10 +3024,14 @@ impl<'a> Parser<'a> {
&& ((
// `async move {`
self.is_keyword_ahead(1, &[kw::Move])
- && self.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace))
+ && self.look_ahead(2, |t| {
+ *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block()
+ })
) || (
// `async {`
- self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace))
+ self.look_ahead(1, |t| {
+ *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block()
+ })
))
}
@@ -3360,8 +3356,8 @@ impl<'a> Parser<'a> {
ExprKind::Binary(binop, lhs, rhs)
}
- fn mk_index(&self, expr: P<Expr>, idx: P<Expr>) -> ExprKind {
- ExprKind::Index(expr, idx)
+ fn mk_index(&self, expr: P<Expr>, idx: P<Expr>, brackets_span: Span) -> ExprKind {
+ ExprKind::Index(expr, idx, brackets_span)
}
fn mk_call(&self, f: P<Expr>, args: ThinVec<P<Expr>>) -> ExprKind {
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index 8ab38c4fb..242c9d332 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -49,7 +49,7 @@ impl<'a> Parser<'a> {
&& self.check_ident()
// `Const` followed by IDENT
{
- return Ok(self.recover_const_param_with_mistyped_const(preceding_attrs, ident)?);
+ return self.recover_const_param_with_mistyped_const(preceding_attrs, ident);
}
// Parse optional colon and param bounds.
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 1470180de..24c65d061 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -1,20 +1,20 @@
-use crate::errors;
-
use super::diagnostics::{dummy_arg, ConsumeClosingDelim};
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
use super::{AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, TrailingToken};
+use crate::errors::{self, MacroExpandsToAdtField};
+use crate::fluent_generated as fluent;
use ast::StaticItem;
use rustc_ast::ast::*;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, TokenKind};
use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
use rustc_ast::util::case::Case;
+use rustc_ast::MacCall;
use rustc_ast::{self as ast, AttrVec, Attribute, DUMMY_NODE_ID};
use rustc_ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind};
use rustc_ast::{BindingAnnotation, Block, FnDecl, FnSig, Param, SelfKind};
use rustc_ast::{EnumDef, FieldDef, Generics, TraitRef, Ty, TyKind, Variant, VariantData};
use rustc_ast::{FnHeader, ForeignItem, Path, PathSegment, Visibility, VisibilityKind};
-use rustc_ast::{MacCall, MacDelimiter};
use rustc_ast_pretty::pprust;
use rustc_errors::{
struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, PResult,
@@ -226,9 +226,9 @@ impl<'a> Parser<'a> {
} else if self.is_static_global() {
// STATIC ITEM
self.bump(); // `static`
- let m = self.parse_mutability();
- let (ident, ty, expr) = self.parse_item_global(Some(m))?;
- (ident, ItemKind::Static(Box::new(StaticItem { ty, mutability: m, expr })))
+ let mutability = self.parse_mutability();
+ let (ident, item) = self.parse_static_item(mutability)?;
+ (ident, ItemKind::Static(Box::new(item)))
} else if let Const::Yes(const_span) = self.parse_constness(Case::Sensitive) {
// CONST ITEM
if self.token.is_keyword(kw::Impl) {
@@ -236,8 +236,16 @@ impl<'a> Parser<'a> {
self.recover_const_impl(const_span, attrs, def_())?
} else {
self.recover_const_mut(const_span);
- let (ident, ty, expr) = self.parse_item_global(None)?;
- (ident, ItemKind::Const(Box::new(ConstItem { defaultness: def_(), ty, expr })))
+ let (ident, generics, ty, expr) = self.parse_const_item()?;
+ (
+ ident,
+ ItemKind::Const(Box::new(ConstItem {
+ defaultness: def_(),
+ generics,
+ ty,
+ expr,
+ })),
+ )
}
} else if self.check_keyword(kw::Trait) || self.check_auto_or_unsafe_trait_item() {
// TRAIT ITEM
@@ -878,6 +886,7 @@ impl<'a> Parser<'a> {
self.sess.emit_err(errors::AssociatedStaticItemNotAllowed { span });
AssocItemKind::Const(Box::new(ConstItem {
defaultness: Defaultness::Final,
+ generics: Generics::default(),
ty,
expr,
}))
@@ -892,7 +901,7 @@ impl<'a> Parser<'a> {
/// Parses a `type` alias with the following grammar:
/// ```ebnf
- /// TypeAlias = "type" Ident Generics {":" GenericBounds}? {"=" Ty}? ";" ;
+ /// TypeAlias = "type" Ident Generics (":" GenericBounds)? WhereClause ("=" Ty)? WhereClause ";" ;
/// ```
/// The `"type"` has already been eaten.
fn parse_type_alias(&mut self, defaultness: Defaultness) -> PResult<'a, ItemInfo> {
@@ -1220,33 +1229,132 @@ impl<'a> Parser<'a> {
Ok(impl_info)
}
- /// Parse `["const" | ("static" "mut"?)] $ident ":" $ty (= $expr)?` with
- /// `["const" | ("static" "mut"?)]` already parsed and stored in `m`.
+ /// Parse a static item with the prefix `"static" "mut"?` already parsed and stored in `mutability`.
///
- /// When `m` is `"const"`, `$ident` may also be `"_"`.
- fn parse_item_global(
- &mut self,
- m: Option<Mutability>,
- ) -> PResult<'a, (Ident, P<Ty>, Option<P<ast::Expr>>)> {
- let id = if m.is_none() { self.parse_ident_or_underscore() } else { self.parse_ident() }?;
+ /// ```ebnf
+ /// Static = "static" "mut"? $ident ":" $ty (= $expr)? ";" ;
+ /// ```
+ fn parse_static_item(&mut self, mutability: Mutability) -> PResult<'a, (Ident, StaticItem)> {
+ let ident = self.parse_ident()?;
- // Parse the type of a `const` or `static mut?` item.
- // That is, the `":" $ty` fragment.
+ if self.token.kind == TokenKind::Lt && self.may_recover() {
+ let generics = self.parse_generics()?;
+ self.sess.emit_err(errors::StaticWithGenerics { span: generics.span });
+ }
+
+ // Parse the type of a static item. That is, the `":" $ty` fragment.
+ // FIXME: This could maybe benefit from `.may_recover()`?
let ty = match (self.eat(&token::Colon), self.check(&token::Eq) | self.check(&token::Semi))
{
- // If there wasn't a `:` or the colon was followed by a `=` or `;` recover a missing type.
(true, false) => self.parse_ty()?,
- (colon, _) => self.recover_missing_const_type(colon, m),
+ // If there wasn't a `:` or the colon was followed by a `=` or `;`, recover a missing type.
+ (colon, _) => self.recover_missing_global_item_type(colon, Some(mutability)),
};
let expr = if self.eat(&token::Eq) { Some(self.parse_expr()?) } else { None };
+
+ self.expect_semi()?;
+
+ Ok((ident, StaticItem { ty, mutability, expr }))
+ }
+
+ /// Parse a constant item with the prefix `"const"` already parsed.
+ ///
+ /// ```ebnf
+ /// Const = "const" ($ident | "_") Generics ":" $ty (= $expr)? WhereClause ";" ;
+ /// ```
+ fn parse_const_item(&mut self) -> PResult<'a, (Ident, Generics, P<Ty>, Option<P<ast::Expr>>)> {
+ let ident = self.parse_ident_or_underscore()?;
+
+ let mut generics = self.parse_generics()?;
+
+ // Check the span for emptiness instead of the list of parameters in order to correctly
+ // recognize and subsequently flag empty parameter lists (`<>`) as unstable.
+ if !generics.span.is_empty() {
+ self.sess.gated_spans.gate(sym::generic_const_items, generics.span);
+ }
+
+ // Parse the type of a constant item. That is, the `":" $ty` fragment.
+ // FIXME: This could maybe benefit from `.may_recover()`?
+ let ty = match (
+ self.eat(&token::Colon),
+ self.check(&token::Eq) | self.check(&token::Semi) | self.check_keyword(kw::Where),
+ ) {
+ (true, false) => self.parse_ty()?,
+ // If there wasn't a `:` or the colon was followed by a `=`, `;` or `where`, recover a missing type.
+ (colon, _) => self.recover_missing_global_item_type(colon, None),
+ };
+
+ // Proactively parse a where-clause to be able to provide a good error message in case we
+ // encounter the item body following it.
+ let before_where_clause =
+ if self.may_recover() { self.parse_where_clause()? } else { WhereClause::default() };
+
+ let expr = if self.eat(&token::Eq) { Some(self.parse_expr()?) } else { None };
+
+ let after_where_clause = self.parse_where_clause()?;
+
+ // Provide a nice error message if the user placed a where-clause before the item body.
+ // Users may be tempted to write such code if they are still used to the deprecated
+ // where-clause location on type aliases and associated types. See also #89122.
+ if before_where_clause.has_where_token && let Some(expr) = &expr {
+ self.sess.emit_err(errors::WhereClauseBeforeConstBody {
+ span: before_where_clause.span,
+ name: ident.span,
+ body: expr.span,
+ sugg: if !after_where_clause.has_where_token {
+ self.sess.source_map().span_to_snippet(expr.span).ok().map(|body| {
+ errors::WhereClauseBeforeConstBodySugg {
+ left: before_where_clause.span.shrink_to_lo(),
+ snippet: body,
+ right: before_where_clause.span.shrink_to_hi().to(expr.span),
+ }
+ })
+ } else {
+ // FIXME(generic_const_items): Provide a structured suggestion to merge the first
+ // where-clause into the second one.
+ None
+ },
+ });
+ }
+
+ // Merge the predicates of both where-clauses since either one can be relevant.
+ // If we didn't parse a body (which is valid for associated consts in traits) and we were
+ // allowed to recover, `before_where_clause` contains the predicates, otherwise they are
+ // in `after_where_clause`. Further, both of them might contain predicates iff two
+ // where-clauses were provided which is syntactically ill-formed but we want to recover from
+ // it and treat them as one large where-clause.
+ let mut predicates = before_where_clause.predicates;
+ predicates.extend(after_where_clause.predicates);
+ let where_clause = WhereClause {
+ has_where_token: before_where_clause.has_where_token
+ || after_where_clause.has_where_token,
+ predicates,
+ span: if after_where_clause.has_where_token {
+ after_where_clause.span
+ } else {
+ before_where_clause.span
+ },
+ };
+
+ if where_clause.has_where_token {
+ self.sess.gated_spans.gate(sym::generic_const_items, where_clause.span);
+ }
+
+ generics.where_clause = where_clause;
+
self.expect_semi()?;
- Ok((id, ty, expr))
+
+ Ok((ident, generics, ty, expr))
}
/// We were supposed to parse `":" $ty` but the `:` or the type was missing.
/// This means that the type is missing.
- fn recover_missing_const_type(&mut self, colon_present: bool, m: Option<Mutability>) -> P<Ty> {
+ fn recover_missing_global_item_type(
+ &mut self,
+ colon_present: bool,
+ m: Option<Mutability>,
+ ) -> P<Ty> {
// Construct the error and stash it away with the hope
// that typeck will later enrich the error with a type.
let kind = match m {
@@ -1342,6 +1450,17 @@ impl<'a> Parser<'a> {
}
let ident = this.parse_field_ident("enum", vlo)?;
+ if this.token == token::Not {
+ if let Err(mut err) = this.unexpected::<()>() {
+ err.note(fluent::parse_macro_expands_to_enum_variant).emit();
+ }
+
+ this.bump();
+ this.parse_delim_args()?;
+
+ return Ok((None, TrailingToken::MaybeComma));
+ }
+
let struct_def = if this.check(&token::OpenDelim(Delimiter::Brace)) {
// Parse a struct variant.
let (fields, recovered) =
@@ -1369,7 +1488,7 @@ impl<'a> Parser<'a> {
Ok((Some(vr), TrailingToken::MaybeComma))
},
- ).map_err(|mut err|{
+ ).map_err(|mut err| {
err.help("enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`");
err
})
@@ -1579,7 +1698,8 @@ impl<'a> Parser<'a> {
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
let lo = this.token.span;
let vis = this.parse_visibility(FollowedByType::No)?;
- Ok((this.parse_single_struct_field(adt_ty, lo, vis, attrs)?, TrailingToken::None))
+ this.parse_single_struct_field(adt_ty, lo, vis, attrs)
+ .map(|field| (field, TrailingToken::None))
})
}
@@ -1713,8 +1833,8 @@ impl<'a> Parser<'a> {
"field names and their types are separated with `:`",
":",
Applicability::MachineApplicable,
- );
- err.emit();
+ )
+ .emit();
} else {
return Err(err);
}
@@ -1731,6 +1851,23 @@ impl<'a> Parser<'a> {
attrs: AttrVec,
) -> PResult<'a, FieldDef> {
let name = self.parse_field_ident(adt_ty, lo)?;
+ // Parse the macro invocation and recover
+ if self.token.kind == token::Not {
+ if let Err(mut err) = self.unexpected::<FieldDef>() {
+ err.subdiagnostic(MacroExpandsToAdtField { adt_ty }).emit();
+ self.bump();
+ self.parse_delim_args()?;
+ return Ok(FieldDef {
+ span: DUMMY_SP,
+ ident: None,
+ vis,
+ id: DUMMY_NODE_ID,
+ ty: self.mk_ty(DUMMY_SP, TyKind::Err),
+ attrs,
+ is_placeholder: false,
+ });
+ }
+ }
self.expect_field_ty_separator()?;
let ty = self.parse_ty()?;
if self.token.kind == token::Colon && self.look_ahead(1, |tok| tok.kind != token::Colon) {
@@ -1860,7 +1997,7 @@ impl<'a> Parser<'a> {
let arrow = TokenTree::token_alone(token::FatArrow, pspan.between(bspan)); // `=>`
let tokens = TokenStream::new(vec![params, arrow, body]);
let dspan = DelimSpan::from_pair(pspan.shrink_to_lo(), bspan.shrink_to_hi());
- P(DelimArgs { dspan, delim: MacDelimiter::Brace, tokens })
+ P(DelimArgs { dspan, delim: Delimiter::Brace, tokens })
} else {
return self.unexpected();
};
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index c23420661..77c59bb38 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -24,12 +24,11 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor};
use rustc_ast::util::case::Case;
use rustc_ast::AttrId;
use rustc_ast::DUMMY_NODE_ID;
-use rustc_ast::{self as ast, AnonConst, AttrStyle, Const, DelimArgs, Extern};
-use rustc_ast::{Async, AttrArgs, AttrArgsEq, Expr, ExprKind, MacDelimiter, Mutability, StrLit};
+use rustc_ast::{self as ast, AnonConst, Const, DelimArgs, Extern};
+use rustc_ast::{Async, AttrArgs, AttrArgsEq, Expr, ExprKind, Mutability, StrLit};
use rustc_ast::{HasAttrs, HasTokens, Unsafe, Visibility, VisibilityKind};
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::sync::Ordering;
use rustc_errors::PResult;
use rustc_errors::{
Applicability, DiagnosticBuilder, ErrorGuaranteed, FatalError, IntoDiagnostic, MultiSpan,
@@ -38,7 +37,7 @@ use rustc_session::parse::ParseSess;
use rustc_span::source_map::{Span, DUMMY_SP};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use std::ops::Range;
-use std::{cmp, mem, slice};
+use std::{mem, slice};
use thin_vec::ThinVec;
use tracing::debug;
@@ -135,10 +134,24 @@ pub struct Parser<'a> {
pub capture_cfg: bool,
restrictions: Restrictions,
expected_tokens: Vec<TokenType>,
- // Important: This must only be advanced from `bump` to ensure that
- // `token_cursor.num_next_calls` is updated properly.
token_cursor: TokenCursor,
- desugar_doc_comments: bool,
+ // The number of calls to `bump`, i.e. the position in the token stream.
+ num_bump_calls: usize,
+ // During parsing we may sometimes need to 'unglue' a glued token into two
+ // component tokens (e.g. '>>' into '>' and '>), so the parser can consume
+ // them one at a time. This process bypasses the normal capturing mechanism
+ // (e.g. `num_bump_calls` will not be incremented), since the 'unglued'
+ // tokens due not exist in the original `TokenStream`.
+ //
+ // If we end up consuming both unglued tokens, this is not an issue. We'll
+ // end up capturing the single 'glued' token.
+ //
+ // However, sometimes we may want to capture just the first 'unglued'
+ // token. For example, capturing the `Vec<u8>` in `Option<Vec<u8>>`
+ // requires us to unglue the trailing `>>` token. The `break_last_token`
+ // field is used to track this token. It gets appended to the captured
+ // stream when we evaluate a `LazyAttrTokenStream`.
+ break_last_token: bool,
/// This field is used to keep track of how many left angle brackets we have seen. This is
/// required in order to detect extra leading left angle brackets (`<` characters) and error
/// appropriately.
@@ -162,7 +175,7 @@ pub struct Parser<'a> {
// This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure
// it doesn't unintentionally get bigger.
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-rustc_data_structures::static_assert_size!(Parser<'_>, 272);
+rustc_data_structures::static_assert_size!(Parser<'_>, 264);
/// Stores span information about a closure.
#[derive(Clone)]
@@ -224,64 +237,29 @@ struct TokenCursor {
// tokens are in `stack[n-1]`. `stack[0]` (when present) has no delimiters
// because it's the outermost token stream which never has delimiters.
stack: Vec<(TokenTreeCursor, Delimiter, DelimSpan)>,
-
- desugar_doc_comments: bool,
-
- // Counts the number of calls to `{,inlined_}next`.
- num_next_calls: usize,
-
- // During parsing, we may sometimes need to 'unglue' a
- // glued token into two component tokens
- // (e.g. '>>' into '>' and '>), so that the parser
- // can consume them one at a time. This process
- // bypasses the normal capturing mechanism
- // (e.g. `num_next_calls` will not be incremented),
- // since the 'unglued' tokens due not exist in
- // the original `TokenStream`.
- //
- // If we end up consuming both unglued tokens,
- // then this is not an issue - we'll end up
- // capturing the single 'glued' token.
- //
- // However, in certain circumstances, we may
- // want to capture just the first 'unglued' token.
- // For example, capturing the `Vec<u8>`
- // in `Option<Vec<u8>>` requires us to unglue
- // the trailing `>>` token. The `break_last_token`
- // field is used to track this token - it gets
- // appended to the captured stream when
- // we evaluate a `LazyAttrTokenStream`.
- break_last_token: bool,
}
impl TokenCursor {
- fn next(&mut self, desugar_doc_comments: bool) -> (Token, Spacing) {
- self.inlined_next(desugar_doc_comments)
+ fn next(&mut self) -> (Token, Spacing) {
+ self.inlined_next()
}
/// This always-inlined version should only be used on hot code paths.
#[inline(always)]
- fn inlined_next(&mut self, desugar_doc_comments: bool) -> (Token, Spacing) {
+ fn inlined_next(&mut self) -> (Token, Spacing) {
loop {
- // FIXME: we currently don't return `Delimiter` open/close delims. To fix #67062 we will
- // need to, whereupon the `delim != Delimiter::Invisible` conditions below can be
- // removed.
+ // FIXME: we currently don't return `Delimiter::Invisible` open/close delims. To fix
+ // #67062 we will need to, whereupon the `delim != Delimiter::Invisible` conditions
+ // below can be removed.
if let Some(tree) = self.tree_cursor.next_ref() {
match tree {
- &TokenTree::Token(ref token, spacing) => match (desugar_doc_comments, token) {
- (true, &Token { kind: token::DocComment(_, attr_style, data), span }) => {
- let desugared = self.desugar(attr_style, data, span);
- self.tree_cursor.replace_prev_and_rewind(desugared);
- // Continue to get the first token of the desugared doc comment.
- }
- _ => {
- debug_assert!(!matches!(
- token.kind,
- token::OpenDelim(_) | token::CloseDelim(_)
- ));
- return (token.clone(), spacing);
- }
- },
+ &TokenTree::Token(ref token, spacing) => {
+ debug_assert!(!matches!(
+ token.kind,
+ token::OpenDelim(_) | token::CloseDelim(_)
+ ));
+ return (token.clone(), spacing);
+ }
&TokenTree::Delimited(sp, delim, ref tts) => {
let trees = tts.clone().into_trees();
self.stack.push((mem::replace(&mut self.tree_cursor, trees), delim, sp));
@@ -304,52 +282,6 @@ impl TokenCursor {
}
}
}
-
- // Desugar a doc comment into something like `#[doc = r"foo"]`.
- fn desugar(&mut self, attr_style: AttrStyle, data: Symbol, span: Span) -> Vec<TokenTree> {
- // Searches for the occurrences of `"#*` and returns the minimum number of `#`s
- // required to wrap the text. E.g.
- // - `abc d` is wrapped as `r"abc d"` (num_of_hashes = 0)
- // - `abc "d"` is wrapped as `r#"abc "d""#` (num_of_hashes = 1)
- // - `abc "##d##"` is wrapped as `r###"abc ##"d"##"###` (num_of_hashes = 3)
- let mut num_of_hashes = 0;
- let mut count = 0;
- for ch in data.as_str().chars() {
- count = match ch {
- '"' => 1,
- '#' if count > 0 => count + 1,
- _ => 0,
- };
- num_of_hashes = cmp::max(num_of_hashes, count);
- }
-
- // `/// foo` becomes `doc = r"foo"`.
- let delim_span = DelimSpan::from_single(span);
- let body = TokenTree::Delimited(
- delim_span,
- Delimiter::Bracket,
- [
- TokenTree::token_alone(token::Ident(sym::doc, false), span),
- TokenTree::token_alone(token::Eq, span),
- TokenTree::token_alone(
- TokenKind::lit(token::StrRaw(num_of_hashes), data, None),
- span,
- ),
- ]
- .into_iter()
- .collect::<TokenStream>(),
- );
-
- if attr_style == AttrStyle::Inner {
- vec![
- TokenTree::token_alone(token::Pound, span),
- TokenTree::token_alone(token::Not, span),
- body,
- ]
- } else {
- vec![TokenTree::token_alone(token::Pound, span), body]
- }
- }
}
#[derive(Debug, Clone, PartialEq)]
@@ -368,7 +300,7 @@ impl TokenType {
fn to_string(&self) -> String {
match self {
TokenType::Token(t) => format!("`{}`", pprust::token_kind_to_string(t)),
- TokenType::Keyword(kw) => format!("`{}`", kw),
+ TokenType::Keyword(kw) => format!("`{kw}`"),
TokenType::Operator => "an operator".to_string(),
TokenType::Lifetime => "lifetime".to_string(),
TokenType::Ident => "identifier".to_string(),
@@ -438,14 +370,13 @@ pub(super) fn token_descr(token: &Token) -> String {
TokenDescription::DocComment => "doc comment",
});
- if let Some(kind) = kind { format!("{} `{}`", kind, name) } else { format!("`{}`", name) }
+ if let Some(kind) = kind { format!("{kind} `{name}`") } else { format!("`{name}`") }
}
impl<'a> Parser<'a> {
pub fn new(
sess: &'a ParseSess,
- tokens: TokenStream,
- desugar_doc_comments: bool,
+ stream: TokenStream,
subparser_name: Option<&'static str>,
) -> Self {
let mut parser = Parser {
@@ -456,14 +387,9 @@ impl<'a> Parser<'a> {
capture_cfg: false,
restrictions: Restrictions::empty(),
expected_tokens: Vec::new(),
- token_cursor: TokenCursor {
- tree_cursor: tokens.into_trees(),
- stack: Vec::new(),
- num_next_calls: 0,
- desugar_doc_comments,
- break_last_token: false,
- },
- desugar_doc_comments,
+ token_cursor: TokenCursor { tree_cursor: stream.into_trees(), stack: Vec::new() },
+ num_bump_calls: 0,
+ break_last_token: false,
unmatched_angle_bracket_count: 0,
max_angle_bracket_count: 0,
last_unexpected_token_span: None,
@@ -766,7 +692,7 @@ impl<'a> Parser<'a> {
// If we consume any additional tokens, then this token
// is not needed (we'll capture the entire 'glued' token),
// and `bump` will set this field to `None`
- self.token_cursor.break_last_token = true;
+ self.break_last_token = true;
// Use the spacing of the glued token as the spacing
// of the unglued second token.
self.bump_with((Token::new(second, second_span), self.token_spacing));
@@ -923,7 +849,7 @@ impl<'a> Parser<'a> {
expect_err
.span_suggestion_short(
sp,
- format!("missing `{}`", token_str),
+ format!("missing `{token_str}`"),
token_str,
Applicability::MaybeIncorrect,
)
@@ -1107,12 +1033,12 @@ impl<'a> Parser<'a> {
pub fn bump(&mut self) {
// Note: destructuring here would give nicer code, but it was found in #96210 to be slower
// than `.0`/`.1` access.
- let mut next = self.token_cursor.inlined_next(self.desugar_doc_comments);
- self.token_cursor.num_next_calls += 1;
+ let mut next = self.token_cursor.inlined_next();
+ self.num_bump_calls += 1;
// We've retrieved an token from the underlying
// cursor, so we no longer need to worry about
// an unglued token. See `break_and_eat` for more details
- self.token_cursor.break_last_token = false;
+ self.break_last_token = false;
if next.0.span.is_dummy() {
// Tweak the location for better diagnostics, but keep syntactic context intact.
let fallback_span = self.token.span;
@@ -1126,38 +1052,53 @@ impl<'a> Parser<'a> {
}
/// Look-ahead `dist` tokens of `self.token` and get access to that token there.
- /// When `dist == 0` then the current token is looked at.
+ /// When `dist == 0` then the current token is looked at. `Eof` will be
+ /// returned if the look-ahead is any distance past the end of the tokens.
pub fn look_ahead<R>(&self, dist: usize, looker: impl FnOnce(&Token) -> R) -> R {
if dist == 0 {
return looker(&self.token);
}
- let tree_cursor = &self.token_cursor.tree_cursor;
if let Some(&(_, delim, span)) = self.token_cursor.stack.last()
&& delim != Delimiter::Invisible
{
+ // We are not in the outermost token stream, and the token stream
+ // we are in has non-skipped delimiters. Look for skipped
+ // delimiters in the lookahead range.
+ let tree_cursor = &self.token_cursor.tree_cursor;
let all_normal = (0..dist).all(|i| {
let token = tree_cursor.look_ahead(i);
!matches!(token, Some(TokenTree::Delimited(_, Delimiter::Invisible, _)))
});
if all_normal {
+ // There were no skipped delimiters. Do lookahead by plain indexing.
return match tree_cursor.look_ahead(dist - 1) {
- Some(tree) => match tree {
- TokenTree::Token(token, _) => looker(token),
- TokenTree::Delimited(dspan, delim, _) => {
- looker(&Token::new(token::OpenDelim(*delim), dspan.open))
+ Some(tree) => {
+ // Indexing stayed within the current token stream.
+ match tree {
+ TokenTree::Token(token, _) => looker(token),
+ TokenTree::Delimited(dspan, delim, _) => {
+ looker(&Token::new(token::OpenDelim(*delim), dspan.open))
+ }
}
- },
- None => looker(&Token::new(token::CloseDelim(delim), span.close)),
+ }
+ None => {
+ // Indexing went past the end of the current token
+ // stream. Use the close delimiter, no matter how far
+ // ahead `dist` went.
+ looker(&Token::new(token::CloseDelim(delim), span.close))
+ }
};
}
}
+ // We are in a more complex case. Just clone the token cursor and use
+ // `next`, skipping delimiters as necessary. Slow but simple.
let mut cursor = self.token_cursor.clone();
let mut i = 0;
let mut token = Token::dummy();
while i < dist {
- token = cursor.next(/* desugar_doc_comments */ false).0;
+ token = cursor.next().0;
if matches!(
token.kind,
token::OpenDelim(Delimiter::Invisible) | token::CloseDelim(Delimiter::Invisible)
@@ -1166,7 +1107,7 @@ impl<'a> Parser<'a> {
}
i += 1;
}
- return looker(&token);
+ looker(&token)
}
/// Returns whether any of the given keywords are `dist` tokens ahead of the current one.
@@ -1210,7 +1151,8 @@ impl<'a> Parser<'a> {
fn parse_constness_(&mut self, case: Case, is_closure: bool) -> Const {
// Avoid const blocks and const closures to be parsed as const items
if (self.check_const_closure() == is_closure)
- && self.look_ahead(1, |t| t != &token::OpenDelim(Delimiter::Brace))
+ && !self
+ .look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block())
&& self.eat_keyword_case(kw::Const, case)
{
Const::Yes(self.prev_token.uninterpolated_span())
@@ -1288,10 +1230,10 @@ impl<'a> Parser<'a> {
|| self.check(&token::OpenDelim(Delimiter::Brace));
delimited.then(|| {
- // We've confirmed above that there is a delimiter so unwrapping is OK.
- let TokenTree::Delimited(dspan, delim, tokens) = self.parse_token_tree() else { unreachable!() };
-
- DelimArgs { dspan, delim: MacDelimiter::from_token(delim).unwrap(), tokens }
+ let TokenTree::Delimited(dspan, delim, tokens) = self.parse_token_tree() else {
+ unreachable!()
+ };
+ DelimArgs { dspan, delim, tokens }
})
}
@@ -1307,12 +1249,11 @@ impl<'a> Parser<'a> {
}
/// Parses a single token tree from the input.
- pub(crate) fn parse_token_tree(&mut self) -> TokenTree {
+ pub fn parse_token_tree(&mut self) -> TokenTree {
match self.token.kind {
token::OpenDelim(..) => {
// Grab the tokens within the delimiters.
- let tree_cursor = &self.token_cursor.tree_cursor;
- let stream = tree_cursor.stream.clone();
+ let stream = self.token_cursor.tree_cursor.stream.clone();
let (_, delim, span) = *self.token_cursor.stack.last().unwrap();
// Advance the token cursor through the entire delimited
@@ -1343,15 +1284,6 @@ impl<'a> Parser<'a> {
}
}
- /// Parses a stream of tokens into a list of `TokenTree`s, up to EOF.
- pub fn parse_all_token_trees(&mut self) -> PResult<'a, Vec<TokenTree>> {
- let mut tts = Vec::new();
- while self.token != token::Eof {
- tts.push(self.parse_token_tree());
- }
- Ok(tts)
- }
-
pub fn parse_tokens(&mut self) -> TokenStream {
let mut result = Vec::new();
loop {
@@ -1511,7 +1443,7 @@ impl<'a> Parser<'a> {
}
pub fn approx_token_stream_pos(&self) -> usize {
- self.token_cursor.num_next_calls
+ self.num_bump_calls
}
}
@@ -1537,18 +1469,6 @@ pub(crate) fn make_unclosed_delims_error(
Some(err)
}
-pub fn emit_unclosed_delims(unclosed_delims: &mut Vec<UnmatchedDelim>, sess: &ParseSess) {
- let _ = sess.reached_eof.fetch_or(
- unclosed_delims.iter().any(|unmatched_delim| unmatched_delim.found_delim.is_none()),
- Ordering::Relaxed,
- );
- for unmatched in unclosed_delims.drain(..) {
- if let Some(mut e) = make_unclosed_delims_error(unmatched, sess) {
- e.emit();
- }
- }
-}
-
/// A helper struct used when building an `AttrTokenStream` from
/// a `LazyAttrTokenStream`. Both delimiter and non-delimited tokens
/// are stored as `FlatToken::Token`. A vector of `FlatToken`s
@@ -1571,7 +1491,7 @@ pub enum FlatToken {
}
#[derive(Debug)]
-pub enum NtOrTt {
+pub enum ParseNtResult {
Nt(Nonterminal),
Tt(TokenTree),
}
diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs
index adb0d372a..ff059a7e8 100644
--- a/compiler/rustc_parse/src/parser/nonterminal.rs
+++ b/compiler/rustc_parse/src/parser/nonterminal.rs
@@ -1,5 +1,5 @@
use rustc_ast::ptr::P;
-use rustc_ast::token::{self, Delimiter, NonterminalKind, Token};
+use rustc_ast::token::{self, Delimiter, Nonterminal::*, NonterminalKind, Token};
use rustc_ast::HasTokens;
use rustc_ast_pretty::pprust;
use rustc_errors::IntoDiagnostic;
@@ -8,7 +8,7 @@ use rustc_span::symbol::{kw, Ident};
use crate::errors::UnexpectedNonterminal;
use crate::parser::pat::{CommaRecoveryMode, RecoverColon, RecoverComma};
-use crate::parser::{FollowedByType, ForceCollect, NtOrTt, Parser, PathStyle};
+use crate::parser::{FollowedByType, ForceCollect, ParseNtResult, Parser, PathStyle};
impl<'a> Parser<'a> {
/// Checks whether a non-terminal may begin with a particular token.
@@ -20,10 +20,21 @@ impl<'a> Parser<'a> {
pub fn nonterminal_may_begin_with(kind: NonterminalKind, token: &Token) -> bool {
/// Checks whether the non-terminal may contain a single (non-keyword) identifier.
fn may_be_ident(nt: &token::Nonterminal) -> bool {
- !matches!(
- *nt,
- token::NtItem(_) | token::NtBlock(_) | token::NtVis(_) | token::NtLifetime(_)
- )
+ match nt {
+ NtStmt(_)
+ | NtPat(_)
+ | NtExpr(_)
+ | NtTy(_)
+ | NtIdent(..)
+ | NtLiteral(_) // `true`, `false`
+ | NtMeta(_)
+ | NtPath(_) => true,
+
+ NtItem(_)
+ | NtBlock(_)
+ | NtVis(_)
+ | NtLifetime(_) => false,
+ }
}
match kind {
@@ -44,27 +55,19 @@ impl<'a> Parser<'a> {
},
NonterminalKind::Block => match &token.kind {
token::OpenDelim(Delimiter::Brace) => true,
- token::Interpolated(nt) => !matches!(
- **nt,
- token::NtItem(_)
- | token::NtPat(_)
- | token::NtTy(_)
- | token::NtIdent(..)
- | token::NtMeta(_)
- | token::NtPath(_)
- | token::NtVis(_)
- ),
+ token::Interpolated(nt) => match **nt {
+ NtBlock(_) | NtLifetime(_) | NtStmt(_) | NtExpr(_) | NtLiteral(_) => true,
+ NtItem(_) | NtPat(_) | NtTy(_) | NtIdent(..) | NtMeta(_) | NtPath(_)
+ | NtVis(_) => false,
+ },
_ => false,
},
NonterminalKind::Path | NonterminalKind::Meta => match &token.kind {
token::ModSep | token::Ident(..) => true,
- token::Interpolated(nt) => match **nt {
- token::NtPath(_) | token::NtMeta(_) => true,
- _ => may_be_ident(&nt),
- },
+ token::Interpolated(nt) => may_be_ident(nt),
_ => false,
},
- NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr { .. } => {
+ NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => {
match &token.kind {
token::Ident(..) | // box, ref, mut, and other identifiers (can stricten)
token::OpenDelim(Delimiter::Parenthesis) | // tuple pattern
@@ -79,7 +82,7 @@ impl<'a> Parser<'a> {
token::Lt | // path (UFCS constant)
token::BinOp(token::Shl) => true, // path (double UFCS)
// leading vert `|` or-pattern
- token::BinOp(token::Or) => matches!(kind, NonterminalKind::PatWithOr {..}),
+ token::BinOp(token::Or) => matches!(kind, NonterminalKind::PatWithOr),
token::Interpolated(nt) => may_be_ident(nt),
_ => false,
}
@@ -87,7 +90,7 @@ impl<'a> Parser<'a> {
NonterminalKind::Lifetime => match &token.kind {
token::Lifetime(_) => true,
token::Interpolated(nt) => {
- matches!(**nt, token::NtLifetime(_))
+ matches!(**nt, NtLifetime(_))
}
_ => false,
},
@@ -100,18 +103,16 @@ impl<'a> Parser<'a> {
/// Parse a non-terminal (e.g. MBE `:pat` or `:ident`). Inlined because there is only one call
/// site.
#[inline]
- pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, NtOrTt> {
- // Any `Nonterminal` which stores its tokens (currently `NtItem` and `NtExpr`)
- // needs to have them force-captured here.
+ pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, ParseNtResult> {
// A `macro_rules!` invocation may pass a captured item/expr to a proc-macro,
// which requires having captured tokens available. Since we cannot determine
// in advance whether or not a proc-macro will be (transitively) invoked,
// we always capture tokens for any `Nonterminal` which needs them.
let mut nt = match kind {
// Note that TT is treated differently to all the others.
- NonterminalKind::TT => return Ok(NtOrTt::Tt(self.parse_token_tree())),
+ NonterminalKind::TT => return Ok(ParseNtResult::Tt(self.parse_token_tree())),
NonterminalKind::Item => match self.parse_item(ForceCollect::Yes)? {
- Some(item) => token::NtItem(item),
+ Some(item) => NtItem(item),
None => {
return Err(UnexpectedNonterminal::Item(self.token.span)
.into_diagnostic(&self.sess.span_diagnostic));
@@ -120,19 +121,19 @@ impl<'a> Parser<'a> {
NonterminalKind::Block => {
// While a block *expression* may have attributes (e.g. `#[my_attr] { ... }`),
// the ':block' matcher does not support them
- token::NtBlock(self.collect_tokens_no_attrs(|this| this.parse_block())?)
+ NtBlock(self.collect_tokens_no_attrs(|this| this.parse_block())?)
}
NonterminalKind::Stmt => match self.parse_stmt(ForceCollect::Yes)? {
- Some(s) => token::NtStmt(P(s)),
+ Some(s) => NtStmt(P(s)),
None => {
return Err(UnexpectedNonterminal::Statement(self.token.span)
.into_diagnostic(&self.sess.span_diagnostic));
}
},
- NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr { .. } => {
- token::NtPat(self.collect_tokens_no_attrs(|this| match kind {
- NonterminalKind::PatParam { .. } => this.parse_pat_no_top_alt(None),
- NonterminalKind::PatWithOr { .. } => this.parse_pat_allow_top_alt(
+ NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => {
+ NtPat(self.collect_tokens_no_attrs(|this| match kind {
+ NonterminalKind::PatParam { .. } => this.parse_pat_no_top_alt(None, None),
+ NonterminalKind::PatWithOr => this.parse_pat_allow_top_alt(
None,
RecoverComma::No,
RecoverColon::No,
@@ -142,16 +143,16 @@ impl<'a> Parser<'a> {
})?)
}
- NonterminalKind::Expr => token::NtExpr(self.parse_expr_force_collect()?),
+ NonterminalKind::Expr => NtExpr(self.parse_expr_force_collect()?),
NonterminalKind::Literal => {
// The `:literal` matcher does not support attributes
- token::NtLiteral(
+ NtLiteral(
self.collect_tokens_no_attrs(|this| this.parse_literal_maybe_minus())?,
)
}
- NonterminalKind::Ty => token::NtTy(
- self.collect_tokens_no_attrs(|this| this.parse_no_question_mark_recover())?,
+ NonterminalKind::Ty => NtTy(
+ self.collect_tokens_no_attrs(|this| this.parse_ty_no_question_mark_recover())?,
),
// this could be handled like a token, since it is one
@@ -159,7 +160,7 @@ impl<'a> Parser<'a> {
if let Some((ident, is_raw)) = get_macro_ident(&self.token) =>
{
self.bump();
- token::NtIdent(ident, is_raw)
+ NtIdent(ident, is_raw)
}
NonterminalKind::Ident => {
return Err(UnexpectedNonterminal::Ident {
@@ -167,16 +168,16 @@ impl<'a> Parser<'a> {
token: self.token.clone(),
}.into_diagnostic(&self.sess.span_diagnostic));
}
- NonterminalKind::Path => token::NtPath(
+ NonterminalKind::Path => NtPath(
P(self.collect_tokens_no_attrs(|this| this.parse_path(PathStyle::Type))?),
),
- NonterminalKind::Meta => token::NtMeta(P(self.parse_attr_item(true)?)),
- NonterminalKind::Vis => token::NtVis(
+ NonterminalKind::Meta => NtMeta(P(self.parse_attr_item(true)?)),
+ NonterminalKind::Vis => NtVis(
P(self.collect_tokens_no_attrs(|this| this.parse_visibility(FollowedByType::Yes))?),
),
NonterminalKind::Lifetime => {
if self.check_lifetime() {
- token::NtLifetime(self.expect_lifetime().ident)
+ NtLifetime(self.expect_lifetime().ident)
} else {
return Err(UnexpectedNonterminal::Lifetime {
span: self.token.span,
@@ -196,7 +197,7 @@ impl<'a> Parser<'a> {
);
}
- Ok(NtOrTt::Nt(nt))
+ Ok(ParseNtResult::Nt(nt))
}
}
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index fdf365178..3e4e92789 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -2,13 +2,13 @@ use super::{ForceCollect, Parser, PathStyle, TrailingToken};
use crate::errors::{
self, AmbiguousRangePattern, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed,
DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt,
- ExpectedCommaAfterPatternField, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow,
- InclusiveRangeNoEnd, InvalidMutInPattern, PatternOnWrongSideOfAt, RefMutOrderIncorrect,
- RemoveLet, RepeatedMutInPattern, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg,
- TrailingVertNotAllowed, UnexpectedLifetimeInPattern, UnexpectedVertVertBeforeFunctionParam,
+ ExpectedCommaAfterPatternField, GenericArgsInPatRequireTurbofishSyntax,
+ InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern,
+ PatternOnWrongSideOfAt, RefMutOrderIncorrect, RemoveLet, RepeatedMutInPattern,
+ TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed,
+ UnexpectedLifetimeInPattern, UnexpectedVertVertBeforeFunctionParam,
UnexpectedVertVertInPattern,
};
-use crate::fluent_generated as fluent;
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor};
use rustc_ast::ptr::P;
@@ -81,7 +81,8 @@ enum EatOrResult {
}
/// The syntax location of a given pattern. Used for diagnostics.
-pub(super) enum PatternLocation {
+#[derive(Clone, Copy)]
+pub enum PatternLocation {
LetBinding,
FunctionParameter,
}
@@ -92,8 +93,12 @@ impl<'a> Parser<'a> {
/// Corresponds to `pat<no_top_alt>` in RFC 2535 and does not admit or-patterns
/// at the top level. Used when parsing the parameters of lambda expressions,
/// functions, function pointers, and `pat` macro fragments.
- pub fn parse_pat_no_top_alt(&mut self, expected: Option<Expected>) -> PResult<'a, P<Pat>> {
- self.parse_pat_with_range_pat(true, expected)
+ pub fn parse_pat_no_top_alt(
+ &mut self,
+ expected: Option<Expected>,
+ syntax_loc: Option<PatternLocation>,
+ ) -> PResult<'a, P<Pat>> {
+ self.parse_pat_with_range_pat(true, expected, syntax_loc)
}
/// Parses a pattern.
@@ -111,7 +116,7 @@ impl<'a> Parser<'a> {
ra: RecoverColon,
rt: CommaRecoveryMode,
) -> PResult<'a, P<Pat>> {
- self.parse_pat_allow_top_alt_inner(expected, rc, ra, rt).map(|(pat, _)| pat)
+ self.parse_pat_allow_top_alt_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat)
}
/// Returns the pattern and a bool indicating whether we recovered from a trailing vert (true =
@@ -122,6 +127,7 @@ impl<'a> Parser<'a> {
rc: RecoverComma,
ra: RecoverColon,
rt: CommaRecoveryMode,
+ syntax_loc: Option<PatternLocation>,
) -> PResult<'a, (P<Pat>, bool)> {
// Keep track of whether we recovered from a trailing vert so that we can avoid duplicated
// suggestions (which bothers rustfix).
@@ -134,9 +140,13 @@ impl<'a> Parser<'a> {
};
// Parse the first pattern (`p_0`).
- let mut first_pat = self.parse_pat_no_top_alt(expected)?;
+ let mut first_pat = self.parse_pat_no_top_alt(expected, syntax_loc)?;
if rc == RecoverComma::Yes {
- self.maybe_recover_unexpected_comma(first_pat.span, rt)?;
+ self.maybe_recover_unexpected_comma(
+ first_pat.span,
+ matches!(first_pat.kind, PatKind::MacCall(_)),
+ rt,
+ )?;
}
// If the next token is not a `|`,
@@ -173,12 +183,12 @@ impl<'a> Parser<'a> {
break;
}
}
- let pat = self.parse_pat_no_top_alt(expected).map_err(|mut err| {
+ let pat = self.parse_pat_no_top_alt(expected, syntax_loc).map_err(|mut err| {
err.span_label(lo, WHILE_PARSING_OR_MSG);
err
})?;
if rc == RecoverComma::Yes {
- self.maybe_recover_unexpected_comma(pat.span, rt)?;
+ self.maybe_recover_unexpected_comma(pat.span, false, rt)?;
}
pats.push(pat);
}
@@ -209,46 +219,31 @@ impl<'a> Parser<'a> {
rc,
RecoverColon::No,
CommaRecoveryMode::LikelyTuple,
+ Some(syntax_loc),
)?;
let colon = self.eat(&token::Colon);
if let PatKind::Or(pats) = &pat.kind {
let span = pat.span;
-
- if trailing_vert {
- // We already emitted an error and suggestion to remove the trailing vert. Don't
- // emit again.
-
- // FIXME(#100717): pass `TopLevelOrPatternNotAllowed::* { sub: None }` to
- // `delay_span_bug()` instead of fluent message
- self.sess.span_diagnostic.delay_span_bug(
- span,
- match syntax_loc {
- PatternLocation::LetBinding => {
- fluent::parse_or_pattern_not_allowed_in_let_binding
- }
- PatternLocation::FunctionParameter => {
- fluent::parse_or_pattern_not_allowed_in_fn_parameters
- }
- },
- );
+ let pat = pprust::pat_to_string(&pat);
+ let sub = if pats.len() == 1 {
+ Some(TopLevelOrPatternNotAllowedSugg::RemoveLeadingVert { span, pat })
} else {
- let pat = pprust::pat_to_string(&pat);
- let sub = if pats.len() == 1 {
- Some(TopLevelOrPatternNotAllowedSugg::RemoveLeadingVert { span, pat })
- } else {
- Some(TopLevelOrPatternNotAllowedSugg::WrapInParens { span, pat })
- };
+ Some(TopLevelOrPatternNotAllowedSugg::WrapInParens { span, pat })
+ };
- self.sess.emit_err(match syntax_loc {
- PatternLocation::LetBinding => {
- TopLevelOrPatternNotAllowed::LetBinding { span, sub }
- }
- PatternLocation::FunctionParameter => {
- TopLevelOrPatternNotAllowed::FunctionParameter { span, sub }
- }
- });
+ let mut err = self.sess.create_err(match syntax_loc {
+ PatternLocation::LetBinding => {
+ TopLevelOrPatternNotAllowed::LetBinding { span, sub }
+ }
+ PatternLocation::FunctionParameter => {
+ TopLevelOrPatternNotAllowed::FunctionParameter { span, sub }
+ }
+ });
+ if trailing_vert {
+ err.delay_as_bug();
}
+ err.emit();
}
Ok((pat, colon))
@@ -336,6 +331,7 @@ impl<'a> Parser<'a> {
&mut self,
allow_range_pat: bool,
expected: Option<Expected>,
+ syntax_loc: Option<PatternLocation>,
) -> PResult<'a, P<Pat>> {
maybe_recover_from_interpolated_ty_qpath!(self, true);
maybe_whole!(self, NtPat, |x| x);
@@ -375,11 +371,11 @@ impl<'a> Parser<'a> {
// Parse _
PatKind::Wild
} else if self.eat_keyword(kw::Mut) {
- self.parse_pat_ident_mut()?
+ self.parse_pat_ident_mut(syntax_loc)?
} else if self.eat_keyword(kw::Ref) {
// Parse ref ident @ pat / ref mut ident @ pat
let mutbl = self.parse_mutability();
- self.parse_pat_ident(BindingAnnotation(ByRef::Yes, mutbl))?
+ self.parse_pat_ident(BindingAnnotation(ByRef::Yes, mutbl), syntax_loc)?
} else if self.eat_keyword(kw::Box) {
self.parse_pat_box()?
} else if self.check_inline_const(0) {
@@ -401,7 +397,7 @@ impl<'a> Parser<'a> {
// Parse `ident @ pat`
// This can give false positives and parse nullary enums,
// they are dealt with later in resolve.
- self.parse_pat_ident(BindingAnnotation::NONE)?
+ self.parse_pat_ident(BindingAnnotation::NONE, syntax_loc)?
} else if self.is_start_of_pat_with_path() {
// Parse pattern starting with a path
let (qself, path) = if self.eat_lt() {
@@ -445,7 +441,7 @@ impl<'a> Parser<'a> {
);
let mut err = self_.struct_span_err(self_.token.span, msg);
- err.span_label(self_.token.span, format!("expected {}", expected));
+ err.span_label(self_.token.span, format!("expected {expected}"));
err
});
PatKind::Lit(self.mk_expr(lo, ExprKind::Lit(lit)))
@@ -502,7 +498,7 @@ impl<'a> Parser<'a> {
// At this point we attempt to parse `@ $pat_rhs` and emit an error.
self.bump(); // `@`
- let mut rhs = self.parse_pat_no_top_alt(None)?;
+ let mut rhs = self.parse_pat_no_top_alt(None, None)?;
let whole_span = lhs.span.to(rhs.span);
if let PatKind::Ident(_, _, sub @ None) = &mut rhs.kind {
@@ -558,7 +554,7 @@ impl<'a> Parser<'a> {
}
let mutbl = self.parse_mutability();
- let subpat = self.parse_pat_with_range_pat(false, expected)?;
+ let subpat = self.parse_pat_with_range_pat(false, expected, None)?;
Ok(PatKind::Ref(subpat, mutbl))
}
@@ -583,12 +579,12 @@ impl<'a> Parser<'a> {
}
/// Parse a mutable binding with the `mut` token already eaten.
- fn parse_pat_ident_mut(&mut self) -> PResult<'a, PatKind> {
+ fn parse_pat_ident_mut(&mut self, syntax_loc: Option<PatternLocation>) -> PResult<'a, PatKind> {
let mut_span = self.prev_token.span;
if self.eat_keyword(kw::Ref) {
self.sess.emit_err(RefMutOrderIncorrect { span: mut_span.to(self.prev_token.span) });
- return self.parse_pat_ident(BindingAnnotation::REF_MUT);
+ return self.parse_pat_ident(BindingAnnotation::REF_MUT, syntax_loc);
}
self.recover_additional_muts();
@@ -601,7 +597,7 @@ impl<'a> Parser<'a> {
}
// Parse the pattern we hope to be an identifier.
- let mut pat = self.parse_pat_no_top_alt(Some(Expected::Identifier))?;
+ let mut pat = self.parse_pat_no_top_alt(Some(Expected::Identifier), None)?;
// If we don't have `mut $ident (@ pat)?`, error.
if let PatKind::Ident(BindingAnnotation(ByRef::No, m @ Mutability::Not), ..) = &mut pat.kind
@@ -681,7 +677,7 @@ impl<'a> Parser<'a> {
let msg = format!("expected {}, found {}", expected, super::token_descr(&self.token));
let mut err = self.struct_span_err(self.token.span, msg);
- err.span_label(self.token.span, format!("expected {}", expected));
+ err.span_label(self.token.span, format!("expected {expected}"));
let sp = self.sess.source_map().start_point(self.token.span);
if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&sp) {
@@ -827,10 +823,26 @@ impl<'a> Parser<'a> {
/// Parses `ident` or `ident @ pat`.
/// Used by the copy foo and ref foo patterns to give a good
/// error message when parsing mistakes like `ref foo(a, b)`.
- fn parse_pat_ident(&mut self, binding_annotation: BindingAnnotation) -> PResult<'a, PatKind> {
+ fn parse_pat_ident(
+ &mut self,
+ binding_annotation: BindingAnnotation,
+ syntax_loc: Option<PatternLocation>,
+ ) -> PResult<'a, PatKind> {
let ident = self.parse_ident()?;
+
+ if self.may_recover()
+ && !matches!(syntax_loc, Some(PatternLocation::FunctionParameter))
+ && self.check_noexpect(&token::Lt)
+ && self.look_ahead(1, |t| t.can_begin_type())
+ {
+ return Err(self.sess.create_err(GenericArgsInPatRequireTurbofishSyntax {
+ span: self.token.span,
+ suggest_turbofish: self.token.span.shrink_to_lo(),
+ }));
+ }
+
let sub = if self.eat(&token::At) {
- Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern))?)
+ Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?)
} else {
None
};
@@ -919,14 +931,14 @@ impl<'a> Parser<'a> {
// We cannot use `parse_pat_ident()` since it will complain `box`
// is not an identifier.
let sub = if self.eat(&token::At) {
- Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern))?)
+ Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?)
} else {
None
};
Ok(PatKind::Ident(BindingAnnotation::NONE, Ident::new(kw::Box, box_span), sub))
} else {
- let pat = self.parse_pat_with_range_pat(false, None)?;
+ let pat = self.parse_pat_with_range_pat(false, None, None)?;
self.sess.gated_spans.gate(sym::box_patterns, box_span.to(self.prev_token.span));
Ok(PatKind::Box(pat))
}
@@ -994,7 +1006,7 @@ impl<'a> Parser<'a> {
break;
}
let token_str = super::token_descr(&self.token);
- let msg = format!("expected `}}`, found {}", token_str);
+ let msg = format!("expected `}}`, found {token_str}");
let mut err = self.struct_span_err(self.token.span, msg);
err.span_label(self.token.span, "expected `}`");
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index feb7e829c..445516c03 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -679,7 +679,7 @@ impl<'a> Parser<'a> {
);
err.span_suggestion(
eq.to(before_next),
- format!("remove the `=` if `{}` is a type", ident),
+ format!("remove the `=` if `{ident}` is a type"),
"",
Applicability::MaybeIncorrect,
)
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 9fcf51a04..12c267351 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -193,10 +193,9 @@ impl<'a> Parser<'a> {
/// At this point, the `!` token after the path has already been eaten.
fn parse_stmt_mac(&mut self, lo: Span, attrs: AttrVec, path: ast::Path) -> PResult<'a, Stmt> {
let args = self.parse_delim_args()?;
- let delim = args.delim.to_token();
let hi = self.prev_token.span;
- let style = match delim {
+ let style = match args.delim {
Delimiter::Brace => MacStmtStyle::Braces,
_ => MacStmtStyle::NoBraces,
};
@@ -300,7 +299,7 @@ impl<'a> Parser<'a> {
Ok(ty) => (None, Some(ty)),
Err(mut err) => {
if let Ok(snip) = self.span_to_snippet(pat.span) {
- err.span_label(pat.span, format!("while parsing the type for `{}`", snip));
+ err.span_label(pat.span, format!("while parsing the type for `{snip}`"));
}
// we use noexpect here because we don't actually expect Eq to be here
// but we are still checking for it in order to be able to handle it if
@@ -502,7 +501,7 @@ impl<'a> Parser<'a> {
fn error_block_no_opening_brace<T>(&mut self) -> PResult<'a, T> {
let tok = super::token_descr(&self.token);
- let msg = format!("expected `{{`, found {}", tok);
+ let msg = format!("expected `{{`, found {tok}");
Err(self.error_block_no_opening_brace_msg(Cow::from(msg)))
}
@@ -638,10 +637,9 @@ impl<'a> Parser<'a> {
e.span_suggestion(
sp.with_hi(sp.lo() + BytePos(marker.len() as u32)),
format!(
- "add a space before `{}` to use a regular comment",
- doc_comment_marker,
+ "add a space before `{doc_comment_marker}` to use a regular comment",
),
- format!("{} {}", comment_marker, doc_comment_marker),
+ format!("{comment_marker} {doc_comment_marker}"),
Applicability::MaybeIncorrect,
);
}
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index a29b696ae..2d888efb1 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -180,7 +180,7 @@ impl<'a> Parser<'a> {
)
}
- pub(super) fn parse_no_question_mark_recover(&mut self) -> PResult<'a, P<Ty>> {
+ pub(super) fn parse_ty_no_question_mark_recover(&mut self) -> PResult<'a, P<Ty>> {
self.parse_ty_common(
AllowPlus::Yes,
AllowCVariadic::No,
@@ -608,7 +608,7 @@ impl<'a> Parser<'a> {
/// Is a `dyn B0 + ... + Bn` type allowed here?
fn is_explicit_dyn_type(&mut self) -> bool {
self.check_keyword(kw::Dyn)
- && (self.token.uninterpolated_span().rust_2018()
+ && (self.token.uninterpolated_span().at_least_rust_2018()
|| self.look_ahead(1, |t| {
(t.can_begin_bound() || t.kind == TokenKind::BinOp(token::Star))
&& !can_continue_type_after_non_fn_ident(t)
@@ -714,6 +714,7 @@ impl<'a> Parser<'a> {
/// ```
fn parse_generic_bound(&mut self) -> PResult<'a, GenericBound> {
let lo = self.token.span;
+ let leading_token = self.prev_token.clone();
let has_parens = self.eat(&token::OpenDelim(Delimiter::Parenthesis));
let inner_lo = self.token.span;
@@ -722,7 +723,7 @@ impl<'a> Parser<'a> {
self.error_lt_bound_with_modifiers(modifiers);
self.parse_generic_lt_bound(lo, inner_lo, has_parens)?
} else {
- self.parse_generic_ty_bound(lo, has_parens, modifiers)?
+ self.parse_generic_ty_bound(lo, has_parens, modifiers, &leading_token)?
};
Ok(bound)
@@ -827,6 +828,7 @@ impl<'a> Parser<'a> {
lo: Span,
has_parens: bool,
modifiers: BoundModifiers,
+ leading_token: &Token,
) -> PResult<'a, GenericBound> {
let mut lifetime_defs = self.parse_late_bound_lifetime_defs()?;
let mut path = if self.token.is_keyword(kw::Fn)
@@ -873,18 +875,18 @@ impl<'a> Parser<'a> {
}
if has_parens {
- if self.token.is_like_plus() {
- // Someone has written something like `&dyn (Trait + Other)`. The correct code
- // would be `&(dyn Trait + Other)`, but we don't have access to the appropriate
- // span to suggest that. When written as `&dyn Trait + Other`, an appropriate
- // suggestion is given.
+ // Someone has written something like `&dyn (Trait + Other)`. The correct code
+ // would be `&(dyn Trait + Other)`
+ if self.token.is_like_plus() && leading_token.is_keyword(kw::Dyn) {
let bounds = vec![];
self.parse_remaining_bounds(bounds, true)?;
self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
- let sp = vec![lo, self.prev_token.span];
- self.sess.emit_err(errors::IncorrectBracesTraitBounds {
- span: sp,
- sugg: errors::IncorrectBracesTraitBoundsSugg { l: lo, r: self.prev_token.span },
+ self.sess.emit_err(errors::IncorrectParensTraitBounds {
+ span: vec![lo, self.prev_token.span],
+ sugg: errors::IncorrectParensTraitBoundsSugg {
+ wrong_span: leading_token.span.shrink_to_hi().to(lo),
+ new_span: leading_token.span.shrink_to_lo(),
+ },
});
} else {
self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index 928fdce31..f73965982 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -2,9 +2,10 @@
use crate::{errors, parse_in};
+use rustc_ast::token::Delimiter;
use rustc_ast::tokenstream::DelimSpan;
use rustc_ast::MetaItemKind;
-use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, Attribute, DelimArgs, MacDelimiter, MetaItem};
+use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, Attribute, DelimArgs, MetaItem};
use rustc_ast_pretty::pprust;
use rustc_errors::{Applicability, FatalError, PResult};
use rustc_feature::{AttributeTemplate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
@@ -84,8 +85,8 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta
})
}
-pub fn check_meta_bad_delim(sess: &ParseSess, span: DelimSpan, delim: MacDelimiter) {
- if let ast::MacDelimiter::Parenthesis = delim {
+pub fn check_meta_bad_delim(sess: &ParseSess, span: DelimSpan, delim: Delimiter) {
+ if let Delimiter::Parenthesis = delim {
return;
}
sess.emit_err(errors::MetaBadDelim {
@@ -94,8 +95,8 @@ pub fn check_meta_bad_delim(sess: &ParseSess, span: DelimSpan, delim: MacDelimit
});
}
-pub fn check_cfg_attr_bad_delim(sess: &ParseSess, span: DelimSpan, delim: MacDelimiter) {
- if let ast::MacDelimiter::Parenthesis = delim {
+pub fn check_cfg_attr_bad_delim(sess: &ParseSess, span: DelimSpan, delim: Delimiter) {
+ if let Delimiter::Parenthesis = delim {
return;
}
sess.emit_err(errors::CfgAttrBadDelim {
@@ -157,15 +158,15 @@ fn emit_malformed_attribute(
matches!(name, sym::doc | sym::ignore | sym::inline | sym::link | sym::test | sym::bench)
};
- let error_msg = format!("malformed `{}` attribute input", name);
+ let error_msg = format!("malformed `{name}` attribute input");
let mut msg = "attribute must be of the form ".to_owned();
let mut suggestions = vec![];
let mut first = true;
let inner = if style == ast::AttrStyle::Inner { "!" } else { "" };
if template.word {
first = false;
- let code = format!("#{}[{}]", inner, name);
- msg.push_str(&format!("`{}`", &code));
+ let code = format!("#{inner}[{name}]");
+ msg.push_str(&format!("`{code}`"));
suggestions.push(code);
}
if let Some(descr) = template.list {
@@ -173,16 +174,16 @@ fn emit_malformed_attribute(
msg.push_str(" or ");
}
first = false;
- let code = format!("#{}[{}({})]", inner, name, descr);
- msg.push_str(&format!("`{}`", &code));
+ let code = format!("#{inner}[{name}({descr})]");
+ msg.push_str(&format!("`{code}`"));
suggestions.push(code);
}
if let Some(descr) = template.name_value_str {
if !first {
msg.push_str(" or ");
}
- let code = format!("#{}[{} = \"{}\"]", inner, name, descr);
- msg.push_str(&format!("`{}`", &code));
+ let code = format!("#{inner}[{name} = \"{descr}\"]");
+ msg.push_str(&format!("`{code}`"));
suggestions.push(code);
}
if should_warn(name) {