summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_parse/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:50 +0000
commit9835e2ae736235810b4ea1c162ca5e65c547e770 (patch)
tree3fcebf40ed70e581d776a8a4c65923e8ec20e026 /compiler/rustc_parse/src
parentReleasing progress-linux version 1.70.0+dfsg2-1~progress7.99u1. (diff)
downloadrustc-9835e2ae736235810b4ea1c162ca5e65c547e770.tar.xz
rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.zip
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_parse/src')
-rw-r--r--compiler/rustc_parse/src/errors.rs399
-rw-r--r--compiler/rustc_parse/src/lexer/mod.rs77
-rw-r--r--compiler/rustc_parse/src/lexer/tokentrees.rs3
-rw-r--r--compiler/rustc_parse/src/lexer/unescape_error_reporting.rs33
-rw-r--r--compiler/rustc_parse/src/lexer/unicode_chars.rs2
-rw-r--r--compiler/rustc_parse/src/lib.rs25
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs6
-rw-r--r--compiler/rustc_parse/src/parser/attr_wrapper.rs2
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs178
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs197
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs46
-rw-r--r--compiler/rustc_parse/src/parser/item.rs222
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs39
-rw-r--r--compiler/rustc_parse/src/parser/nonterminal.rs10
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs37
-rw-r--r--compiler/rustc_parse/src/parser/path.rs113
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs137
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs204
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs33
19 files changed, 1088 insertions, 675 deletions
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 069217165..84494eab8 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -907,6 +907,18 @@ pub(crate) struct SuggRemoveComma {
}
#[derive(Subdiagnostic)]
+#[suggestion(
+ parse_sugg_add_let_for_stmt,
+ style = "verbose",
+ applicability = "maybe-incorrect",
+ code = "let "
+)]
+pub(crate) struct SuggAddMissingLetStmt {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Subdiagnostic)]
pub(crate) enum ExpectedIdentifierFound {
#[label(parse_expected_identifier_found_reserved_identifier)]
ReservedIdentifier(#[primary_span] Span),
@@ -1341,6 +1353,28 @@ pub(crate) struct ExpectedFnPathFoundFnKeyword {
}
#[derive(Diagnostic)]
+#[diag(parse_path_single_colon)]
+pub(crate) struct PathSingleColon {
+ #[primary_span]
+ #[suggestion(applicability = "machine-applicable", code = "::")]
+ pub span: Span,
+
+ #[note(parse_type_ascription_removed)]
+ pub type_ascription: Option<()>,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_colon_as_semi)]
+pub(crate) struct ColonAsSemi {
+ #[primary_span]
+ #[suggestion(applicability = "machine-applicable", code = ";")]
+ pub span: Span,
+
+ #[note(parse_type_ascription_removed)]
+ pub type_ascription: Option<()>,
+}
+
+#[derive(Diagnostic)]
#[diag(parse_where_clause_before_tuple_struct_body)]
pub(crate) struct WhereClauseBeforeTupleStructBody {
#[primary_span]
@@ -1486,6 +1520,16 @@ pub(crate) struct ExpectedTraitInTraitImplFoundType {
}
#[derive(Diagnostic)]
+#[diag(parse_extra_impl_keyword_in_trait_impl)]
+pub(crate) struct ExtraImplKeywordInTraitImpl {
+ #[primary_span]
+ #[suggestion(code = "", applicability = "maybe-incorrect")]
+ pub extra_impl_kw: Span,
+ #[note]
+ pub impl_trait_span: Span,
+}
+
+#[derive(Diagnostic)]
#[diag(parse_bounds_not_allowed_on_trait_aliases)]
pub(crate) struct BoundsNotAllowedOnTraitAliases {
#[primary_span]
@@ -2258,31 +2302,6 @@ pub(crate) struct InvalidDynKeyword {
pub span: Span,
}
-#[derive(Diagnostic)]
-#[diag(parse_negative_bounds_not_supported)]
-pub(crate) struct NegativeBoundsNotSupported {
- #[primary_span]
- pub negative_bounds: Vec<Span>,
- #[label]
- pub last_span: Span,
- #[subdiagnostic]
- pub sub: Option<NegativeBoundsNotSupportedSugg>,
-}
-
-#[derive(Subdiagnostic)]
-#[suggestion(
- parse_suggestion,
- style = "tool-only",
- code = "{fixed}",
- applicability = "machine-applicable"
-)]
-pub(crate) struct NegativeBoundsNotSupportedSugg {
- #[primary_span]
- pub bound_list: Span,
- pub num_bounds: usize,
- pub fixed: String,
-}
-
#[derive(Subdiagnostic)]
pub enum HelpUseLatestEdition {
#[help(parse_help_set_edition_cargo)]
@@ -2332,3 +2351,333 @@ pub(crate) struct BadReturnTypeNotationDotDot {
#[suggestion(code = "", applicability = "maybe-incorrect")]
pub span: Span,
}
+
+#[derive(Diagnostic)]
+#[diag(parse_bad_assoc_type_bounds)]
+pub(crate) struct BadAssocTypeBounds {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_attr_after_generic)]
+pub(crate) struct AttrAfterGeneric {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_attr_without_generics)]
+pub(crate) struct AttrWithoutGenerics {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_where_generics)]
+pub(crate) struct WhereOnGenerics {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_generics_in_path)]
+pub(crate) struct GenericsInPath {
+ #[primary_span]
+ pub span: Vec<Span>,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_assoc_lifetime)]
+#[help]
+pub(crate) struct AssocLifetime {
+ #[primary_span]
+ pub span: Span,
+ #[label]
+ pub lifetime: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_tilde_const_lifetime)]
+pub(crate) struct TildeConstLifetime {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_modifier_lifetime)]
+pub(crate) struct ModifierLifetime {
+ #[primary_span]
+ #[suggestion(style = "tool-only", applicability = "maybe-incorrect", code = "")]
+ pub span: Span,
+ pub sigil: &'static str,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_parenthesized_lifetime)]
+pub(crate) struct ParenthesizedLifetime {
+ #[primary_span]
+ pub span: Span,
+ #[suggestion(style = "short", applicability = "machine-applicable", code = "{snippet}")]
+ pub sugg: Option<Span>,
+ pub snippet: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_const_bounds_missing_tilde)]
+pub(crate) struct ConstMissingTilde {
+ #[primary_span]
+ pub span: Span,
+ #[suggestion(code = "~", applicability = "machine-applicable")]
+ pub start: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_underscore_literal_suffix)]
+pub(crate) struct UnderscoreLiteralSuffix {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_expect_label_found_ident)]
+pub(crate) struct ExpectedLabelFoundIdent {
+ #[primary_span]
+ pub span: Span,
+ #[suggestion(code = "'", applicability = "machine-applicable", style = "short")]
+ pub start: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_inappropriate_default)]
+#[note]
+pub(crate) struct InappropriateDefault {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ pub article: &'static str,
+ pub descr: &'static str,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_recover_import_as_use)]
+pub(crate) struct RecoverImportAsUse {
+ #[primary_span]
+ #[suggestion(code = "use", applicability = "machine-applicable", style = "short")]
+ pub span: Span,
+ pub token_name: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_single_colon_import_path)]
+#[note]
+pub(crate) struct SingleColonImportPath {
+ #[primary_span]
+ #[suggestion(code = "::", applicability = "machine-applicable", style = "short")]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_bad_item_kind)]
+#[help]
+pub(crate) struct BadItemKind {
+ #[primary_span]
+ pub span: Span,
+ pub descr: &'static str,
+ pub ctx: &'static str,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_single_colon_struct_type)]
+pub(crate) struct SingleColonStructType {
+ #[primary_span]
+ #[suggestion(code = "::", applicability = "maybe-incorrect", style = "verbose")]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_equals_struct_default)]
+pub(crate) struct EqualsStructDefault {
+ #[primary_span]
+ #[suggestion(code = "", applicability = "machine-applicable")]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_macro_rules_missing_bang)]
+pub(crate) struct MacroRulesMissingBang {
+ #[primary_span]
+ pub span: Span,
+ #[suggestion(code = "!", applicability = "machine-applicable", style = "verbose")]
+ pub hi: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_macro_name_remove_bang)]
+pub(crate) struct MacroNameRemoveBang {
+ #[primary_span]
+ #[suggestion(code = "", applicability = "machine-applicable")]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_macro_rules_visibility)]
+pub(crate) struct MacroRulesVisibility<'a> {
+ #[primary_span]
+ #[suggestion(code = "#[macro_export]", applicability = "maybe-incorrect")]
+ pub span: Span,
+ pub vis: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_macro_invocation_visibility)]
+#[help]
+pub(crate) struct MacroInvocationVisibility<'a> {
+ #[primary_span]
+ #[suggestion(code = "", applicability = "machine-applicable")]
+ pub span: Span,
+ pub vis: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_nested_adt)]
+pub(crate) struct NestedAdt<'a> {
+ #[primary_span]
+ pub span: Span,
+ #[suggestion(code = "", applicability = "maybe-incorrect")]
+ pub item: Span,
+ pub keyword: &'a str,
+ pub kw_str: Cow<'a, str>,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_function_body_equals_expr)]
+pub(crate) struct FunctionBodyEqualsExpr {
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub sugg: FunctionBodyEqualsExprSugg,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
+pub(crate) struct FunctionBodyEqualsExprSugg {
+ #[suggestion_part(code = "{{")]
+ pub eq: Span,
+ #[suggestion_part(code = " }}")]
+ pub semi: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_box_not_pat)]
+pub(crate) struct BoxNotPat {
+ #[primary_span]
+ pub span: Span,
+ #[note]
+ pub kw: Span,
+ #[suggestion(code = "r#", applicability = "maybe-incorrect", style = "verbose")]
+ pub lo: Span,
+ pub descr: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_unmatched_angle)]
+pub(crate) struct UnmatchedAngle {
+ #[primary_span]
+ #[suggestion(code = "", applicability = "machine-applicable")]
+ pub span: Span,
+ pub plural: bool,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_missing_plus_in_bounds)]
+pub(crate) struct MissingPlusBounds {
+ #[primary_span]
+ pub span: Span,
+ #[suggestion(code = " +", applicability = "maybe-incorrect", style = "verbose")]
+ pub hi: Span,
+ pub sym: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_incorrect_braces_trait_bounds)]
+pub(crate) struct IncorrectBracesTraitBounds {
+ #[primary_span]
+ pub span: Vec<Span>,
+ #[subdiagnostic]
+ pub sugg: IncorrectBracesTraitBoundsSugg,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
+pub(crate) struct IncorrectBracesTraitBoundsSugg {
+ #[suggestion_part(code = " ")]
+ pub l: Span,
+ #[suggestion_part(code = "")]
+ pub r: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_kw_bad_case)]
+pub(crate) struct KwBadCase<'a> {
+ #[primary_span]
+ #[suggestion(code = "{kw}", applicability = "machine-applicable")]
+ pub span: Span,
+ pub kw: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_meta_bad_delim)]
+pub(crate) struct MetaBadDelim {
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub sugg: MetaBadDelimSugg,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_cfg_attr_bad_delim)]
+pub(crate) struct CfgAttrBadDelim {
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub sugg: MetaBadDelimSugg,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(parse_meta_bad_delim_suggestion, applicability = "machine-applicable")]
+pub(crate) struct MetaBadDelimSugg {
+ #[suggestion_part(code = "(")]
+ pub open: Span,
+ #[suggestion_part(code = ")")]
+ pub close: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_malformed_cfg_attr)]
+#[note]
+pub(crate) struct MalformedCfgAttr {
+ #[primary_span]
+ #[suggestion(code = "{sugg}")]
+ pub span: Span,
+ pub sugg: &'static str,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_unknown_builtin_construct)]
+pub(crate) struct UnknownBuiltinConstruct {
+ #[primary_span]
+ pub span: Span,
+ pub name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_expected_builtin_ident)]
+pub(crate) struct ExpectedBuiltinIdent {
+ #[primary_span]
+ pub span: Span,
+}
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index 9e856c9f2..c6e6b46e4 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -1,3 +1,5 @@
+use std::ops::Range;
+
use crate::errors;
use crate::lexer::unicode_chars::UNICODE_ARRAY;
use crate::make_unclosed_delims_error;
@@ -6,7 +8,7 @@ use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind};
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, Mode};
+use rustc_lexer::unescape::{self, EscapeError, Mode};
use rustc_lexer::Cursor;
use rustc_lexer::{Base, DocStyle, RawStrError};
use rustc_session::lint::builtin::{
@@ -67,7 +69,7 @@ pub(crate) fn parse_token_trees<'a>(
match token_trees {
Ok(stream) if unmatched_delims.is_empty() => Ok(stream),
_ => {
- // Return error if there are unmatched delimiters or unclosng delimiters.
+ // Return error if there are unmatched delimiters or unclosed delimiters.
// We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch
// because the delimiter mismatch is more likely to be the root cause of error
@@ -204,16 +206,15 @@ impl<'a> StringReader<'a> {
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);
+ if let token::LitKind::CStr | token::LitKind::CStrRaw(_) = kind {
+ self.sess.gated_spans.gate(sym::c_str_literals, self.mk_sp(start, self.pos));
+ }
let suffix = if suffix_start < self.pos {
let string = self.str_from(suffix_start);
if string == "_" {
self.sess
.span_diagnostic
- .struct_span_err(
- self.mk_sp(suffix_start, self.pos),
- "underscore literal suffix is not allowed",
- )
- .emit();
+ .emit_err(errors::UnderscoreLiteralSuffix { span: self.mk_sp(suffix_start, self.pos) });
None
} else {
Some(Symbol::intern(string))
@@ -325,7 +326,7 @@ impl<'a> StringReader<'a> {
) -> DiagnosticBuilder<'a, !> {
self.sess
.span_diagnostic
- .struct_span_fatal(self.mk_sp(from_pos, to_pos), &format!("{}: {}", m, escaped_char(c)))
+ .struct_span_fatal(self.mk_sp(from_pos, to_pos), format!("{}: {}", m, escaped_char(c)))
}
/// Detect usages of Unicode codepoints changing the direction of the text on screen and loudly
@@ -419,6 +420,16 @@ impl<'a> StringReader<'a> {
}
self.cook_quoted(token::ByteStr, Mode::ByteStr, start, end, 2, 1) // b" "
}
+ rustc_lexer::LiteralKind::CStr { terminated } => {
+ if !terminated {
+ self.sess.span_diagnostic.span_fatal_with_code(
+ self.mk_sp(start + BytePos(1), end),
+ "unterminated C string",
+ error_code!(E0767),
+ )
+ }
+ self.cook_c_string(token::CStr, Mode::CStr, start, end, 2, 1) // c" "
+ }
rustc_lexer::LiteralKind::RawStr { n_hashes } => {
if let Some(n_hashes) = n_hashes {
let n = u32::from(n_hashes);
@@ -437,6 +448,15 @@ impl<'a> StringReader<'a> {
self.report_raw_str_error(start, 2);
}
}
+ rustc_lexer::LiteralKind::RawCStr { n_hashes } => {
+ if let Some(n_hashes) = n_hashes {
+ let n = u32::from(n_hashes);
+ let kind = token::CStrRaw(n_hashes);
+ self.cook_c_string(kind, Mode::RawCStr, start, end, 3 + n, 1 + n) // cr##" "##
+ } else {
+ self.report_raw_str_error(start, 2);
+ }
+ }
rustc_lexer::LiteralKind::Int { base, empty_int } => {
if empty_int {
let span = self.mk_sp(start, end);
@@ -546,7 +566,7 @@ impl<'a> StringReader<'a> {
err.span_label(self.mk_sp(start, start), "unterminated raw string");
if n_hashes > 0 {
- err.note(&format!(
+ err.note(format!(
"this raw string should be terminated with `\"{}`",
"#".repeat(n_hashes as usize)
));
@@ -642,7 +662,7 @@ impl<'a> StringReader<'a> {
&RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
prefix_span,
ast::CRATE_NODE_ID,
- &format!("prefix `{prefix}` is unknown"),
+ format!("prefix `{prefix}` is unknown"),
BuiltinLintDiagnostics::ReservedPrefix(prefix_span),
);
}
@@ -652,7 +672,7 @@ impl<'a> StringReader<'a> {
self.sess.emit_fatal(errors::TooManyHashes { span: self.mk_sp(start, self.pos), num });
}
- fn cook_quoted(
+ fn cook_common(
&self,
kind: token::LitKind,
mode: Mode,
@@ -660,12 +680,13 @@ impl<'a> StringReader<'a> {
end: BytePos,
prefix_len: u32,
postfix_len: u32,
+ unescape: fn(&str, Mode, &mut dyn FnMut(Range<usize>, Result<(), EscapeError>)),
) -> (token::LitKind, Symbol) {
let mut has_fatal_err = false;
let content_start = start + BytePos(prefix_len);
let content_end = end - BytePos(postfix_len);
let lit_content = self.str_from_to(content_start, content_end);
- unescape::unescape_literal(lit_content, mode, &mut |range, result| {
+ unescape(lit_content, mode, &mut |range, result| {
// Here we only check for errors. The actual unescaping is done later.
if let Err(err) = result {
let span_with_quotes = self.mk_sp(start, end);
@@ -696,6 +717,38 @@ impl<'a> StringReader<'a> {
(token::Err, self.symbol_from_to(start, end))
}
}
+
+ fn cook_quoted(
+ &self,
+ kind: token::LitKind,
+ mode: Mode,
+ start: BytePos,
+ end: BytePos,
+ prefix_len: u32,
+ postfix_len: u32,
+ ) -> (token::LitKind, Symbol) {
+ self.cook_common(kind, mode, start, end, prefix_len, postfix_len, |src, mode, callback| {
+ unescape::unescape_literal(src, mode, &mut |span, result| {
+ callback(span, result.map(drop))
+ })
+ })
+ }
+
+ fn cook_c_string(
+ &self,
+ kind: token::LitKind,
+ mode: Mode,
+ start: BytePos,
+ end: BytePos,
+ prefix_len: u32,
+ postfix_len: u32,
+ ) -> (token::LitKind, Symbol) {
+ self.cook_common(kind, mode, start, end, prefix_len, postfix_len, |src, mode, callback| {
+ unescape::unescape_c_string(src, mode, &mut |span, result| {
+ callback(span, result.map(drop))
+ })
+ })
+ }
}
pub fn nfc_normalize(string: &str) -> Symbol {
diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs
index 7c2c08951..318a29985 100644
--- a/compiler/rustc_parse/src/lexer/tokentrees.rs
+++ b/compiler/rustc_parse/src/lexer/tokentrees.rs
@@ -199,8 +199,7 @@ impl<'a> TokenTreesReader<'a> {
// matching opening delimiter).
let token_str = token_to_string(&self.token);
let msg = format!("unexpected closing delimiter: `{}`", token_str);
- let mut err =
- self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, &msg);
+ let mut err = self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, msg);
report_suspicious_mismatch_block(
&mut err,
diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
index 0d12ec608..eb9625f92 100644
--- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
+++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
@@ -78,8 +78,7 @@ pub(crate) fn emit_unescape_error(
}
};
let sugg = sugg.unwrap_or_else(|| {
- let is_byte = mode.is_byte();
- let prefix = if is_byte { "b" } 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() {
@@ -97,7 +96,11 @@ pub(crate) fn emit_unescape_error(
};
}
let sugg = format!("{prefix}\"{escaped}\"");
- MoreThanOneCharSugg::Quotes { span: span_with_quotes, is_byte, sugg }
+ MoreThanOneCharSugg::Quotes {
+ span: span_with_quotes,
+ is_byte: mode == Mode::Byte,
+ sugg,
+ }
});
handler.emit_err(UnescapeError::MoreThanOneChar {
span: span_with_quotes,
@@ -112,7 +115,7 @@ pub(crate) fn emit_unescape_error(
char_span,
escaped_sugg: c.escape_default().to_string(),
escaped_msg: escaped_char(c),
- byte: mode.is_byte(),
+ byte: mode == Mode::Byte,
});
}
EscapeError::BareCarriageReturn => {
@@ -126,12 +129,15 @@ pub(crate) fn emit_unescape_error(
EscapeError::InvalidEscape => {
let (c, span) = last_char();
- let label =
- if mode.is_byte() { "unknown byte escape" } else { "unknown character escape" };
+ let label = if mode == Mode::Byte || mode == Mode::ByteStr {
+ "unknown byte escape"
+ } else {
+ "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 == '}' && !mode.is_byte() {
+ if c == '{' || c == '}' && matches!(mode, Mode::Str | Mode::RawStr) {
diag.help(
"if used in a formatting string, curly braces are escaped with `{{` and `}}`",
);
@@ -141,7 +147,7 @@ pub(crate) fn emit_unescape_error(
version control settings",
);
} else {
- if !mode.is_byte() {
+ if mode == Mode::Str || mode == Mode::Char {
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",
@@ -180,13 +186,13 @@ pub(crate) fn emit_unescape_error(
} 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!(
+ format!(
"if you meant to use the unicode code point for {:?}, use a \\xHH escape",
c
),
@@ -200,10 +206,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 {:?}, use \\xHH escapes", c),
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 1f027c08f..829d9693e 100644
--- a/compiler/rustc_parse/src/lexer/unicode_chars.rs
+++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs
@@ -350,7 +350,7 @@ pub(super) fn check_for_substitution(
let Some((_, ascii_name, token)) = ASCII_ARRAY.iter().find(|&&(s, _, _)| s == ascii_str) else {
let msg = format!("substitution character not found for '{}'", ch);
- reader.sess.span_diagnostic.span_bug_no_panic(span, &msg);
+ 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 17466cd0e..25de78085 100644
--- a/compiler/rustc_parse/src/lib.rs
+++ b/compiler/rustc_parse/src/lib.rs
@@ -18,9 +18,9 @@ use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{AttrItem, Attribute, MetaItem};
use rustc_ast_pretty::pprust;
use rustc_data_structures::sync::Lrc;
-use rustc_errors::{Applicability, Diagnostic, FatalError, Level, PResult};
+use rustc_errors::{Diagnostic, FatalError, Level, PResult};
use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
-use rustc_macros::fluent_messages;
+use rustc_fluent_macro::fluent_messages;
use rustc_session::parse::ParseSess;
use rustc_span::{FileName, SourceFile, Span};
@@ -153,7 +153,7 @@ fn try_file_to_source_file(
) -> Result<Lrc<SourceFile>, Diagnostic> {
sess.source_map().load_file(path).map_err(|e| {
let msg = format!("couldn't read {}: {}", path.display(), e);
- let mut diag = Diagnostic::new(Level::Fatal, &msg);
+ let mut diag = Diagnostic::new(Level::Fatal, msg);
if let Some(sp) = spanopt {
diag.set_span(sp);
}
@@ -190,7 +190,7 @@ pub fn maybe_file_to_stream(
override_span: Option<Span>,
) -> Result<TokenStream, Vec<Diagnostic>> {
let src = source_file.src.as_ref().unwrap_or_else(|| {
- sess.span_diagnostic.bug(&format!(
+ sess.span_diagnostic.bug(format!(
"cannot lex `source_file` without source: {}",
sess.source_map().filename_for_diagnostics(&source_file.name)
));
@@ -243,12 +243,11 @@ pub fn parse_cfg_attr(
ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, ref tokens })
if !tokens.is_empty() =>
{
- let msg = "wrong `cfg_attr` delimiters";
- crate::validate_attr::check_meta_bad_delim(parse_sess, dspan, delim, msg);
+ crate::validate_attr::check_cfg_attr_bad_delim(parse_sess, dspan, delim);
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();
}
@@ -265,15 +264,5 @@ const CFG_ATTR_NOTE_REF: &str = "for more information, visit \
#the-cfg_attr-attribute>";
fn error_malformed_cfg_attr_missing(span: Span, parse_sess: &ParseSess) {
- parse_sess
- .span_diagnostic
- .struct_span_err(span, "malformed `cfg_attr` attribute input")
- .span_suggestion(
- span,
- "missing condition and attribute",
- CFG_ATTR_GRAMMAR_HELP,
- Applicability::HasPlaceholders,
- )
- .note(CFG_ATTR_NOTE_REF)
- .emit();
+ parse_sess.emit_err(errors::MalformedCfgAttr { span, sugg: CFG_ATTR_GRAMMAR_HELP });
}
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index e3e7c63e3..e1db19557 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -45,10 +45,10 @@ impl<'a> Parser<'a> {
Some(InnerAttrForbiddenReason::AfterOuterDocComment {
prev_doc_comment_span: prev_outer_attr_sp.unwrap(),
})
- } else if let Some(prev_outer_attr_sp) = prev_outer_attr_sp {
- Some(InnerAttrForbiddenReason::AfterOuterAttribute { prev_outer_attr_sp })
} else {
- None
+ prev_outer_attr_sp.map(|prev_outer_attr_sp| {
+ InnerAttrForbiddenReason::AfterOuterAttribute { prev_outer_attr_sp }
+ })
};
let inner_parse_policy = InnerAttrPolicy::Forbidden(inner_error_reason);
just_parsed_doc_comment = false;
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index b0ab0f106..1e6ac5496 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -72,7 +72,7 @@ fn has_cfg_or_cfg_attr(attrs: &[Attribute]) -> bool {
// Therefore, the absence of a literal `cfg` or `cfg_attr` guarantees that
// we don't need to do any eager expansion.
attrs.iter().any(|attr| {
- attr.ident().map_or(false, |ident| ident.name == sym::cfg || ident.name == sym::cfg_attr)
+ attr.ident().is_some_and(|ident| ident.name == sym::cfg || ident.name == sym::cfg_attr)
})
}
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index e03ce5d71..c14540396 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -4,7 +4,7 @@ use super::{
TokenExpectType, TokenType,
};
use crate::errors::{
- AmbiguousPlus, AttributeOnParamType, BadQPathStage2, BadTypePlus, BadTypePlusSub,
+ AmbiguousPlus, AttributeOnParamType, BadQPathStage2, BadTypePlus, BadTypePlusSub, ColonAsSemi,
ComparisonOperatorsCannotBeChained, ComparisonOperatorsCannotBeChainedSugg,
ConstGenericWithoutBraces, ConstGenericWithoutBracesSugg, DocCommentDoesNotDocumentAnything,
DocCommentOnParamType, DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg,
@@ -13,7 +13,7 @@ use crate::errors::{
IncorrectUseOfAwait, ParenthesesInForHead, ParenthesesInForHeadSugg,
PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst,
StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens,
- StructLiteralNeedingParensSugg, SuggEscapeIdentifier, SuggRemoveComma,
+ StructLiteralNeedingParensSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma,
UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead,
};
@@ -32,8 +32,8 @@ use rustc_ast::{
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{
- pluralize, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed,
- FatalError, Handler, IntoDiagnostic, MultiSpan, PResult,
+ pluralize, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage,
+ ErrorGuaranteed, FatalError, Handler, IntoDiagnostic, MultiSpan, PResult,
};
use rustc_session::errors::ExprParenthesesNeeded;
use rustc_span::source_map::Spanned;
@@ -84,6 +84,7 @@ impl RecoverQPath for Ty {
}
impl RecoverQPath for Pat {
+ const PATH_STYLE: PathStyle = PathStyle::Pat;
fn to_ty(&self) -> Option<P<Ty>> {
self.to_ty()
}
@@ -206,11 +207,11 @@ struct MultiSugg {
impl MultiSugg {
fn emit(self, err: &mut Diagnostic) {
- err.multipart_suggestion(&self.msg, self.patches, self.applicability);
+ err.multipart_suggestion(self.msg, self.patches, self.applicability);
}
fn emit_verbose(self, err: &mut Diagnostic) {
- err.multipart_suggestion_verbose(&self.msg, self.patches, self.applicability);
+ err.multipart_suggestion_verbose(self.msg, self.patches, self.applicability);
}
}
@@ -237,6 +238,7 @@ impl<'a> DerefMut for SnapshotParser<'a> {
impl<'a> Parser<'a> {
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn struct_span_err<S: Into<MultiSpan>>(
&self,
sp: S,
@@ -570,15 +572,13 @@ impl<'a> Parser<'a> {
let expect = tokens_to_string(&expected);
let actual = super::token_descr(&self.token);
let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 {
+ let fmt = format!("expected one of {expect}, found {actual}");
let short_expect = if expected.len() > 6 {
format!("{} possible tokens", expected.len())
} else {
- expect.clone()
+ expect
};
- (
- format!("expected one of {expect}, found {actual}"),
- (self.prev_token.span.shrink_to_hi(), format!("expected one of {short_expect}")),
- )
+ (fmt, (self.prev_token.span.shrink_to_hi(), format!("expected one of {short_expect}")))
} else if expected.is_empty() {
(
format!("unexpected token: {actual}"),
@@ -592,13 +592,13 @@ impl<'a> Parser<'a> {
};
self.last_unexpected_token_span = Some(self.token.span);
// FIXME: translation requires list formatting (for `expect`)
- let mut err = self.struct_span_err(self.token.span, &msg_exp);
+ let mut err = self.struct_span_err(self.token.span, msg_exp);
if let TokenKind::Ident(symbol, _) = &self.prev_token.kind {
if ["def", "fun", "func", "function"].contains(&symbol.as_str()) {
err.span_suggestion_short(
self.prev_token.span,
- &format!("write `fn` instead of `{symbol}` to declare a function"),
+ format!("write `fn` instead of `{symbol}` to declare a function"),
"fn",
Applicability::MachineApplicable,
);
@@ -665,7 +665,6 @@ impl<'a> Parser<'a> {
err.span_label(sp, label_exp);
err.span_label(self.token.span, "unexpected token");
}
- self.maybe_annotate_with_ascription(&mut err, false);
Err(err)
}
@@ -697,13 +696,13 @@ impl<'a> Parser<'a> {
err.set_span(span);
err.span_suggestion(
span,
- &format!("remove the extra `#`{}", pluralize!(count)),
+ format!("remove the extra `#`{}", pluralize!(count)),
"",
Applicability::MachineApplicable,
);
err.span_label(
str_span,
- &format!("this raw string started with {n_hashes} `#`{}", pluralize!(n_hashes)),
+ format!("this raw string started with {n_hashes} `#`{}", pluralize!(n_hashes)),
);
true
}
@@ -790,59 +789,6 @@ impl<'a> Parser<'a> {
None
}
- pub fn maybe_annotate_with_ascription(
- &mut self,
- err: &mut Diagnostic,
- maybe_expected_semicolon: bool,
- ) {
- if let Some((sp, likely_path)) = self.last_type_ascription.take() {
- let sm = self.sess.source_map();
- let next_pos = sm.lookup_char_pos(self.token.span.lo());
- let op_pos = sm.lookup_char_pos(sp.hi());
-
- let allow_unstable = self.sess.unstable_features.is_nightly_build();
-
- if likely_path {
- err.span_suggestion(
- sp,
- "maybe write a path separator here",
- "::",
- if allow_unstable {
- Applicability::MaybeIncorrect
- } else {
- Applicability::MachineApplicable
- },
- );
- self.sess.type_ascription_path_suggestions.borrow_mut().insert(sp);
- } else if op_pos.line != next_pos.line && maybe_expected_semicolon {
- err.span_suggestion(
- sp,
- "try using a semicolon",
- ";",
- Applicability::MaybeIncorrect,
- );
- } else if allow_unstable {
- err.span_label(sp, "tried to parse a type due to this type ascription");
- } else {
- err.span_label(sp, "tried to parse a type due to this");
- }
- if allow_unstable {
- // Give extra information about type ascription only if it's a nightly compiler.
- err.note(
- "`#![feature(type_ascription)]` lets you annotate an expression with a type: \
- `<expr>: <type>`",
- );
- if !likely_path {
- // Avoid giving too much info when it was likely an unrelated typo.
- err.note(
- "see issue #23416 <https://github.com/rust-lang/rust/issues/23416> \
- for more information",
- );
- }
- }
- }
- }
-
/// Eats and discards tokens until one of `kets` is encountered. Respects token trees,
/// passes through any errors encountered. Used for error recovery.
pub(super) fn eat_to_tokens(&mut self, kets: &[&TokenKind]) {
@@ -899,7 +845,7 @@ impl<'a> Parser<'a> {
//
// `x.foo::<u32>>>(3)`
let parsed_angle_bracket_args =
- segment.args.as_ref().map_or(false, |args| args.is_angle_bracketed());
+ segment.args.as_ref().is_some_and(|args| args.is_angle_bracketed());
debug!(
"check_trailing_angle_brackets: parsed_angle_bracket_args={:?}",
@@ -1061,6 +1007,31 @@ impl<'a> Parser<'a> {
Err(e)
}
+ /// Suggest add the missing `let` before the identifier in stmt
+ /// `a: Ty = 1` -> `let a: Ty = 1`
+ pub(super) fn suggest_add_missing_let_for_stmt(
+ &mut self,
+ err: &mut DiagnosticBuilder<'a, ErrorGuaranteed>,
+ ) {
+ if self.token == token::Colon {
+ let prev_span = self.prev_token.span.shrink_to_lo();
+ let snapshot = self.create_snapshot_for_diagnostic();
+ self.bump();
+ match self.parse_ty() {
+ Ok(_) => {
+ if self.token == token::Eq {
+ let sugg = SuggAddMissingLetStmt { span: prev_span };
+ sugg.add_to_diagnostic(err);
+ }
+ }
+ Err(e) => {
+ e.cancel();
+ }
+ }
+ self.restore_snapshot(snapshot);
+ }
+ }
+
/// Check to see if a pair of chained operators looks like an attempt at chained comparison,
/// e.g. `1 < x <= 3`. If so, suggest either splitting the comparison into two, or
/// parenthesising the leftmost comparison.
@@ -1339,7 +1310,7 @@ impl<'a> Parser<'a> {
}
self.bump(); // `+`
- let bounds = self.parse_generic_bounds(None)?;
+ let bounds = self.parse_generic_bounds()?;
let sum_span = ty.span.to(self.prev_token.span);
let sub = match &ty.kind {
@@ -1415,12 +1386,12 @@ impl<'a> Parser<'a> {
) -> PResult<'a, P<Expr>> {
let mut err = self.struct_span_err(
op_span,
- &format!("Rust has no {} {} operator", kind.fixity, kind.op.name()),
+ format!("Rust has no {} {} operator", kind.fixity, kind.op.name()),
);
- err.span_label(op_span, &format!("not a valid {} operator", kind.fixity));
+ err.span_label(op_span, format!("not a valid {} operator", kind.fixity));
let help_base_case = |mut err: DiagnosticBuilder<'_, _>, base| {
- err.help(&format!("use `{}= 1` instead", kind.op.chr()));
+ err.help(format!("use `{}= 1` instead", kind.op.chr()));
err.emit();
Ok(base)
};
@@ -1609,7 +1580,7 @@ impl<'a> Parser<'a> {
_ => this_token_str,
},
);
- let mut err = self.struct_span_err(sp, &msg);
+ let mut err = self.struct_span_err(sp, msg);
let label_exp = format!("expected `{token_str}`");
let sm = self.sess.source_map();
if !sm.is_multiline(prev_sp.until(sp)) {
@@ -1624,12 +1595,36 @@ impl<'a> Parser<'a> {
}
pub(super) fn expect_semi(&mut self) -> PResult<'a, ()> {
- if self.eat(&token::Semi) {
+ if self.eat(&token::Semi) || self.recover_colon_as_semi() {
return Ok(());
}
self.expect(&token::Semi).map(drop) // Error unconditionally
}
+ pub(super) fn recover_colon_as_semi(&mut self) -> bool {
+ let line_idx = |span: Span| {
+ self.sess
+ .source_map()
+ .span_to_lines(span)
+ .ok()
+ .and_then(|lines| Some(lines.lines.get(0)?.line_index))
+ };
+
+ if self.may_recover()
+ && self.token == token::Colon
+ && self.look_ahead(1, |next| line_idx(self.token.span) < line_idx(next.span))
+ {
+ self.sess.emit_err(ColonAsSemi {
+ span: self.token.span,
+ type_ascription: self.sess.unstable_features.is_nightly_build().then_some(()),
+ });
+ self.bump();
+ return true;
+ }
+
+ false
+ }
+
/// Consumes alternative await syntaxes like `await!(<expr>)`, `await <expr>`,
/// `await? <expr>`, `await(<expr>)`, and `await { <expr> }`.
pub(super) fn recover_incorrect_await_syntax(
@@ -1648,7 +1643,7 @@ impl<'a> Parser<'a> {
// 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),
+ _ => ExprKind::Await(expr, await_sp),
};
let expr = self.mk_expr(lo.to(sp), kind);
self.maybe_recover_from_bad_qpath(expr)
@@ -1736,7 +1731,7 @@ impl<'a> Parser<'a> {
Applicability::MachineApplicable,
);
}
- err.span_suggestion(lo.shrink_to_lo(), &format!("{prefix}you can still access the deprecated `try!()` macro using the \"raw identifier\" syntax"), "r#", Applicability::MachineApplicable);
+ err.span_suggestion(lo.shrink_to_lo(), format!("{prefix}you can still access the deprecated `try!()` macro using the \"raw identifier\" syntax"), "r#", Applicability::MachineApplicable);
err.emit();
Ok(self.mk_expr_err(lo.to(hi)))
} else {
@@ -1792,24 +1787,6 @@ impl<'a> Parser<'a> {
}
}
- pub(super) fn could_ascription_be_path(&self, node: &ast::ExprKind) -> bool {
- (self.token == token::Lt && // `foo:<bar`, likely a typoed turbofish.
- self.look_ahead(1, |t| t.is_ident() && !t.is_reserved_ident()))
- || self.token.is_ident() &&
- matches!(node, ast::ExprKind::Path(..) | ast::ExprKind::Field(..)) &&
- !self.token.is_reserved_ident() && // v `foo:bar(baz)`
- self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Parenthesis))
- || self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace)) // `foo:bar {`
- || self.look_ahead(1, |t| t == &token::Colon) && // `foo:bar::<baz`
- self.look_ahead(2, |t| t == &token::Lt) &&
- self.look_ahead(3, |t| t.is_ident())
- || self.look_ahead(1, |t| t == &token::Colon) && // `foo:bar:baz`
- self.look_ahead(2, |t| t.is_ident())
- || self.look_ahead(1, |t| t == &token::ModSep)
- && (self.look_ahead(2, |t| t.is_ident()) || // `foo:bar::baz`
- self.look_ahead(2, |t| t == &token::Lt)) // `foo:bar::<baz>`
- }
-
pub(super) fn recover_seq_parse_error(
&mut self,
delim: Delimiter,
@@ -1904,7 +1881,6 @@ impl<'a> Parser<'a> {
&& brace_depth == 0
&& bracket_depth == 0 =>
{
- debug!("recover_stmt_ return - Semi");
break;
}
_ => self.bump(),
@@ -2110,7 +2086,7 @@ impl<'a> Parser<'a> {
format!("expected expression, found {}", super::token_descr(&self.token),),
),
};
- let mut err = self.struct_span_err(span, &msg);
+ let mut err = self.struct_span_err(span, msg);
let sp = self.sess.source_map().start_point(self.token.span);
if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&sp) {
err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
@@ -2181,7 +2157,7 @@ impl<'a> Parser<'a> {
// arguments after a comma.
let mut err = self.struct_span_err(
self.token.span,
- &format!("expected one of `,` or `>`, found {}", super::token_descr(&self.token)),
+ format!("expected one of `,` or `>`, found {}", super::token_descr(&self.token)),
);
err.span_label(self.token.span, "expected one of `,` or `>`");
match self.recover_const_arg(arg.span(), err) {
@@ -2608,7 +2584,7 @@ impl<'a> Parser<'a> {
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!(
+ format!(
"try adding parentheses to match on a tuple{}",
if let CommaRecoveryMode::LikelyTuple = rt { "" } else { "..." },
),
@@ -2634,7 +2610,7 @@ impl<'a> Parser<'a> {
let TyKind::Path(qself, path) = &ty.kind else { return Ok(()) };
let qself_position = qself.as_ref().map(|qself| qself.position);
for (i, segments) in path.segments.windows(2).enumerate() {
- if qself_position.map(|pos| i < pos).unwrap_or(false) {
+ if qself_position.is_some_and(|pos| i < pos) {
continue;
}
if let [a, b] = segments {
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 03c82fbd3..1b28f3c97 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -174,10 +174,8 @@ impl<'a> Parser<'a> {
self.parse_expr_prefix(attrs)?
}
};
- let last_type_ascription_set = self.last_type_ascription.is_some();
if !self.should_continue_as_assoc_expr(&lhs) {
- self.last_type_ascription = None;
return Ok(lhs);
}
@@ -301,9 +299,6 @@ impl<'a> Parser<'a> {
if op == AssocOp::As {
lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Cast)?;
continue;
- } else if op == AssocOp::Colon {
- lhs = self.parse_assoc_op_ascribe(lhs, lhs_span)?;
- continue;
} else if op == AssocOp::DotDot || op == AssocOp::DotDotEq {
// If we didn't have to handle `x..`/`x..=`, it would be pretty easy to
// generalise it to the Fixity::None code.
@@ -364,7 +359,7 @@ impl<'a> Parser<'a> {
let aopexpr = self.mk_assign_op(source_map::respan(cur_op_span, aop), lhs, rhs);
self.mk_expr(span, aopexpr)
}
- AssocOp::As | AssocOp::Colon | AssocOp::DotDot | AssocOp::DotDotEq => {
+ AssocOp::As | AssocOp::DotDot | AssocOp::DotDotEq => {
self.span_bug(span, "AssocOp should have been handled by special case")
}
};
@@ -373,9 +368,7 @@ impl<'a> Parser<'a> {
break;
}
}
- if last_type_ascription_set {
- self.last_type_ascription = None;
- }
+
Ok(lhs)
}
@@ -743,7 +736,7 @@ impl<'a> Parser<'a> {
(
// `foo: `
ExprKind::Path(None, ast::Path { segments, .. }),
- TokenKind::Ident(kw::For | kw::Loop | kw::While, false),
+ token::Ident(kw::For | kw::Loop | kw::While, false),
) if segments.len() == 1 => {
let snapshot = self.create_snapshot_for_diagnostic();
let label = Label {
@@ -838,33 +831,31 @@ impl<'a> Parser<'a> {
&mut self,
cast_expr: P<Expr>,
) -> PResult<'a, P<Expr>> {
+ if let ExprKind::Type(_, _) = cast_expr.kind {
+ panic!("ExprKind::Type must not be parsed");
+ }
+
let span = cast_expr.span;
- let (cast_kind, maybe_ascription_span) =
- if let ExprKind::Type(ascripted_expr, _) = &cast_expr.kind {
- ("type ascription", Some(ascripted_expr.span.shrink_to_hi().with_hi(span.hi())))
- } else {
- ("cast", None)
- };
let with_postfix = self.parse_expr_dot_or_call_with_(cast_expr, span)?;
// Check if an illegal postfix operator has been added after the cast.
// If the resulting expression is not a cast, it is an illegal postfix operator.
- if !matches!(with_postfix.kind, ExprKind::Cast(_, _) | ExprKind::Type(_, _)) {
+ if !matches!(with_postfix.kind, ExprKind::Cast(_, _)) {
let msg = format!(
- "{cast_kind} cannot be followed by {}",
+ "cast cannot be followed by {}",
match with_postfix.kind {
ExprKind::Index(_, _) => "indexing",
ExprKind::Try(_) => "`?`",
ExprKind::Field(_, _) => "a field access",
ExprKind::MethodCall(_) => "a method call",
ExprKind::Call(_, _) => "a function call",
- ExprKind::Await(_) => "`.await`",
+ ExprKind::Await(_, _) => "`.await`",
ExprKind::Err => return Ok(with_postfix),
_ => unreachable!("parse_dot_or_call_expr_with_ shouldn't produce this"),
}
);
- let mut err = self.struct_span_err(span, &msg);
+ let mut err = self.struct_span_err(span, msg);
let suggest_parens = |err: &mut Diagnostic| {
let suggestions = vec![
@@ -878,44 +869,13 @@ impl<'a> Parser<'a> {
);
};
- // If type ascription is "likely an error", the user will already be getting a useful
- // help message, and doesn't need a second.
- if self.last_type_ascription.map_or(false, |last_ascription| last_ascription.1) {
- self.maybe_annotate_with_ascription(&mut err, false);
- } else if let Some(ascription_span) = maybe_ascription_span {
- let is_nightly = self.sess.unstable_features.is_nightly_build();
- if is_nightly {
- suggest_parens(&mut err);
- }
- err.span_suggestion(
- ascription_span,
- &format!(
- "{}remove the type ascription",
- if is_nightly { "alternatively, " } else { "" }
- ),
- "",
- if is_nightly {
- Applicability::MaybeIncorrect
- } else {
- Applicability::MachineApplicable
- },
- );
- } else {
- suggest_parens(&mut err);
- }
+ suggest_parens(&mut err);
+
err.emit();
};
Ok(with_postfix)
}
- fn parse_assoc_op_ascribe(&mut self, lhs: P<Expr>, lhs_span: Span) -> PResult<'a, P<Expr>> {
- let maybe_path = self.could_ascription_be_path(&lhs.kind);
- self.last_type_ascription = Some((self.prev_token.span, maybe_path));
- let lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Type)?;
- self.sess.gated_spans.gate(sym::type_ascription, lhs.span);
- Ok(lhs)
- }
-
/// Parse `& mut? <expr>` or `& raw [ const | mut ] <expr>`.
fn parse_expr_borrow(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
self.expect_and()?;
@@ -1010,7 +970,7 @@ impl<'a> Parser<'a> {
};
if has_dot {
// expr.f
- e = self.parse_expr_dot_suffix(lo, e)?;
+ e = self.parse_dot_suffix_expr(lo, e)?;
continue;
}
if self.expr_is_complete(&e) {
@@ -1024,13 +984,7 @@ impl<'a> Parser<'a> {
}
}
- fn look_ahead_type_ascription_as_field(&mut self) -> bool {
- self.look_ahead(1, |t| t.is_ident())
- && self.look_ahead(2, |t| t == &token::Colon)
- && self.look_ahead(3, |t| t.can_begin_expr())
- }
-
- fn parse_expr_dot_suffix(&mut self, lo: Span, base: P<Expr>) -> PResult<'a, P<Expr>> {
+ fn parse_dot_suffix_expr(&mut self, lo: Span, base: P<Expr>) -> PResult<'a, P<Expr>> {
match self.token.uninterpolate().kind {
token::Ident(..) => self.parse_dot_suffix(base, lo),
token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => {
@@ -1183,9 +1137,7 @@ impl<'a> Parser<'a> {
/// Parse a function call expression, `expr(...)`.
fn parse_expr_fn_call(&mut self, lo: Span, fun: P<Expr>) -> P<Expr> {
- let snapshot = if self.token.kind == token::OpenDelim(Delimiter::Parenthesis)
- && self.look_ahead_type_ascription_as_field()
- {
+ let snapshot = if self.token.kind == token::OpenDelim(Delimiter::Parenthesis) {
Some((self.create_snapshot_for_diagnostic(), fun.kind.clone()))
} else {
None
@@ -1216,7 +1168,6 @@ impl<'a> Parser<'a> {
if !self.may_recover() {
return None;
}
-
match (seq.as_mut(), snapshot) {
(Err(err), Some((mut snapshot, ExprKind::Path(None, path)))) => {
snapshot.bump(); // `(`
@@ -1229,11 +1180,15 @@ impl<'a> Parser<'a> {
self.restore_snapshot(snapshot);
let close_paren = self.prev_token.span;
let span = lo.to(close_paren);
+ // filter shorthand fields
+ let fields: Vec<_> =
+ fields.into_iter().filter(|field| !field.is_shorthand).collect();
+
if !fields.is_empty() &&
// `token.kind` should not be compared here.
// This is because the `snapshot.token.kind` is treated as the same as
// that of the open delim in `TokenTreesReader::parse_token_tree`, even if they are different.
- self.span_to_snippet(close_paren).map_or(false, |snippet| snippet == ")")
+ self.span_to_snippet(close_paren).is_ok_and(|snippet| snippet == ")")
{
let mut replacement_err = errors::ParenthesesWithStructFields {
span,
@@ -1260,9 +1215,7 @@ impl<'a> Parser<'a> {
return Some(self.mk_expr_err(span));
}
Ok(_) => {}
- Err(mut err) => {
- err.emit();
- }
+ Err(err) => err.cancel(),
}
}
_ => {}
@@ -1351,6 +1304,8 @@ impl<'a> Parser<'a> {
})
} else if self.check(&token::OpenDelim(Delimiter::Bracket)) {
self.parse_expr_array_or_repeat(Delimiter::Bracket)
+ } else if self.is_builtin() {
+ self.parse_expr_builtin()
} else if self.check_path() {
self.parse_expr_path_start()
} else if self.check_keyword(kw::Move)
@@ -1499,8 +1454,19 @@ impl<'a> Parser<'a> {
}
fn parse_expr_path_start(&mut self) -> PResult<'a, P<Expr>> {
+ let maybe_eq_tok = self.prev_token.clone();
let (qself, path) = if self.eat_lt() {
- let (qself, path) = self.parse_qpath(PathStyle::Expr)?;
+ let lt_span = self.prev_token.span;
+ let (qself, path) = self.parse_qpath(PathStyle::Expr).map_err(|mut err| {
+ // Suggests using '<=' if there is an error parsing qpath when the previous token
+ // is an '=' token. Only emits suggestion if the '<' token and '=' token are
+ // directly adjacent (i.e. '=<')
+ if maybe_eq_tok.kind == TokenKind::Eq && maybe_eq_tok.span.hi() == lt_span.lo() {
+ let eq_lt = maybe_eq_tok.span.to(lt_span);
+ err.span_suggestion(eq_lt, "did you mean", "<=", Applicability::Unspecified);
+ }
+ err
+ })?;
(Some(qself), path)
} else {
(None, self.parse_path(PathStyle::Expr)?)
@@ -1516,7 +1482,6 @@ impl<'a> Parser<'a> {
let mac = P(MacCall {
path,
args: self.parse_delim_args()?,
- prior_type_ascription: self.last_type_ascription,
});
(lo.to(self.prev_token.span), ExprKind::MacCall(mac))
} else if self.check(&token::OpenDelim(Delimiter::Brace))
@@ -1535,7 +1500,7 @@ impl<'a> Parser<'a> {
}
/// Parse `'label: $expr`. The label is already parsed.
- fn parse_expr_labeled(
+ pub(super) fn parse_expr_labeled(
&mut self,
label_: Label,
mut consume_colon: bool,
@@ -1807,6 +1772,61 @@ impl<'a> Parser<'a> {
self.maybe_recover_from_bad_qpath(expr)
}
+ /// Parse `builtin # ident(args,*)`.
+ fn parse_expr_builtin(&mut self) -> PResult<'a, P<Expr>> {
+ self.parse_builtin(|this, lo, ident| {
+ if ident.name == sym::offset_of {
+ return Ok(Some(this.parse_expr_offset_of(lo)?));
+ }
+
+ Ok(None)
+ })
+ }
+
+ pub(crate) fn parse_builtin<T>(
+ &mut self,
+ parse: impl FnOnce(&mut Parser<'a>, Span, Ident) -> PResult<'a, Option<T>>,
+ ) -> PResult<'a, T> {
+ let lo = self.token.span;
+
+ self.bump(); // `builtin`
+ self.bump(); // `#`
+
+ let Some((ident, false)) = self.token.ident() else {
+ let err = errors::ExpectedBuiltinIdent { span: self.token.span }
+ .into_diagnostic(&self.sess.span_diagnostic);
+ return Err(err);
+ };
+ self.sess.gated_spans.gate(sym::builtin_syntax, ident.span);
+ self.bump();
+
+ self.expect(&TokenKind::OpenDelim(Delimiter::Parenthesis))?;
+ let ret = if let Some(res) = parse(self, lo, ident)? {
+ Ok(res)
+ } else {
+ let err = errors::UnknownBuiltinConstruct { span: lo.to(ident.span), name: ident.name }
+ .into_diagnostic(&self.sess.span_diagnostic);
+ return Err(err);
+ };
+ self.expect(&TokenKind::CloseDelim(Delimiter::Parenthesis))?;
+
+ ret
+ }
+
+ pub(crate) fn parse_expr_offset_of(&mut self, lo: Span) -> PResult<'a, P<Expr>> {
+ let container = self.parse_ty()?;
+ self.expect(&TokenKind::Comma)?;
+
+ let seq_sep = SeqSep { sep: Some(token::Dot), trailing_sep_allowed: false };
+ let (fields, _trailing, _recovered) = self.parse_seq_to_before_end(
+ &TokenKind::CloseDelim(Delimiter::Parenthesis),
+ seq_sep,
+ Parser::parse_field_name,
+ )?;
+ let span = lo.to(self.token.span);
+ Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields.to_vec().into())))
+ }
+
/// Returns a string literal if the next token is a string literal.
/// In case of error returns `Some(lit)` if the next token is a literal with a wrong kind,
/// and returns `None` if the next token is not literal at all.
@@ -1855,7 +1875,7 @@ impl<'a> Parser<'a> {
let token = self.token.clone();
let err = |self_: &Self| {
let msg = format!("unexpected token: {}", super::token_descr(&token));
- self_.struct_span_err(token.span, &msg)
+ self_.struct_span_err(token.span, msg)
};
// On an error path, eagerly consider a lifetime to be an unclosed character lit
if self.token.is_lifetime() {
@@ -1922,6 +1942,7 @@ impl<'a> Parser<'a> {
let recovered = self.recover_after_dot();
let token = recovered.as_ref().unwrap_or(&self.token);
let span = token.span;
+
token::Lit::from_token(token).map(|token_lit| {
self.bump();
(token_lit, span)
@@ -2057,7 +2078,7 @@ impl<'a> Parser<'a> {
// Therefore, `token.kind` should not be compared here.
if snapshot
.span_to_snippet(snapshot.token.span)
- .map_or(false, |snippet| snippet == "]") =>
+ .is_ok_and(|snippet| snippet == "]") =>
{
return Err(errors::MissingSemicolonBeforeArray {
open_delim: open_delim_span,
@@ -2752,7 +2773,7 @@ impl<'a> Parser<'a> {
// We might have a `=>` -> `=` or `->` typo (issue #89396).
if TokenKind::FatArrow
.similar_tokens()
- .map_or(false, |similar_tokens| similar_tokens.contains(&this.token.kind))
+ .is_some_and(|similar_tokens| similar_tokens.contains(&this.token.kind))
{
err.span_suggestion(
this.token.span,
@@ -2875,6 +2896,10 @@ impl<'a> Parser<'a> {
})
}
+ pub(crate) fn is_builtin(&self) -> bool {
+ self.token.is_keyword(kw::Builtin) && self.look_ahead(1, |t| *t == token::Pound)
+ }
+
/// Parses a `try {...}` expression (`try` token already eaten).
fn parse_try_block(&mut self, span_lo: Span) -> PResult<'a, P<Expr>> {
let (attrs, body) = self.parse_inner_attrs_and_block()?;
@@ -3013,6 +3038,11 @@ impl<'a> Parser<'a> {
} else {
e.span_label(pth.span, "while parsing this struct");
}
+
+ if !recover {
+ return Err(e);
+ }
+
e.emit();
// If the next token is a comma, then try to parse
@@ -3024,11 +3054,12 @@ impl<'a> Parser<'a> {
break;
}
}
+
None
}
};
- let is_shorthand = parsed_field.as_ref().map_or(false, |f| f.is_shorthand);
+ let is_shorthand = parsed_field.as_ref().is_some_and(|f| f.is_shorthand);
// A shorthand field can be turned into a full field with `:`.
// We should point this out.
self.check_or_expected(!is_shorthand, TokenType::Token(token::Colon));
@@ -3151,14 +3182,10 @@ impl<'a> Parser<'a> {
let label = format!("'{}", ident.name);
let ident = Ident { name: Symbol::intern(&label), span: ident.span };
- self.struct_span_err(ident.span, "expected a label, found an identifier")
- .span_suggestion(
- ident.span,
- "labels start with a tick",
- label,
- Applicability::MachineApplicable,
- )
- .emit();
+ self.sess.emit_err(errors::ExpectedLabelFoundIdent {
+ span: ident.span,
+ start: ident.span.shrink_to_lo(),
+ });
Label { ident }
}
@@ -3256,7 +3283,7 @@ impl<'a> Parser<'a> {
fn mk_await_expr(&mut self, self_arg: P<Expr>, lo: Span) -> P<Expr> {
let span = lo.to(self.prev_token.span);
- let await_expr = self.mk_expr(span, ExprKind::Await(self_arg));
+ let await_expr = self.mk_expr(span, ExprKind::Await(self_arg, self.prev_token.span));
self.recover_from_await_method_call();
await_expr
}
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index f8ef1307c..cd779b0b4 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -1,5 +1,5 @@
use crate::errors::{
- MultipleWhereClauses, UnexpectedDefaultValueForLifetimeInGenericParameters,
+ self, MultipleWhereClauses, UnexpectedDefaultValueForLifetimeInGenericParameters,
UnexpectedSelfInGenericParameters, WhereClauseBeforeTupleStructBody,
WhereClauseBeforeTupleStructBodySugg,
};
@@ -78,7 +78,7 @@ impl<'a> Parser<'a> {
}
self.restore_snapshot(snapshot);
}
- self.parse_generic_bounds(colon_span)?
+ self.parse_generic_bounds()?
} else {
Vec::new()
};
@@ -181,12 +181,9 @@ impl<'a> Parser<'a> {
let snapshot = this.create_snapshot_for_diagnostic();
match this.parse_ty_where_predicate() {
Ok(where_predicate) => {
- this.struct_span_err(
- where_predicate.span(),
- "bounds on associated types do not belong here",
- )
- .span_label(where_predicate.span(), "belongs in `where` clause")
- .emit();
+ this.sess.emit_err(errors::BadAssocTypeBounds {
+ span: where_predicate.span(),
+ });
// FIXME - try to continue parsing other generics?
return Ok((None, TrailingToken::None));
}
@@ -201,22 +198,11 @@ impl<'a> Parser<'a> {
// Check for trailing attributes and stop parsing.
if !attrs.is_empty() {
if !params.is_empty() {
- this.struct_span_err(
- attrs[0].span,
- "trailing attribute after generic parameter",
- )
- .span_label(attrs[0].span, "attributes must go before parameters")
- .emit();
+ this.sess
+ .emit_err(errors::AttrAfterGeneric { span: attrs[0].span });
} else {
- this.struct_span_err(
- attrs[0].span,
- "attribute without generic parameters",
- )
- .span_label(
- attrs[0].span,
- "attributes are only permitted when preceding parameters",
- )
- .emit();
+ this.sess
+ .emit_err(errors::AttrWithoutGenerics { span: attrs[0].span });
}
}
return Ok((None, TrailingToken::None));
@@ -304,12 +290,7 @@ impl<'a> Parser<'a> {
// change we parse those generics now, but report an error.
if self.choose_generics_over_qpath(0) {
let generics = self.parse_generics()?;
- self.struct_span_err(
- generics.span,
- "generic parameters on `where` clauses are reserved for future use",
- )
- .span_label(generics.span, "currently unsupported")
- .emit();
+ self.sess.emit_err(errors::WhereOnGenerics { span: generics.span });
}
loop {
@@ -438,7 +419,7 @@ impl<'a> Parser<'a> {
// or with mandatory equality sign and the second type.
let ty = self.parse_ty_for_where_clause()?;
if self.eat(&token::Colon) {
- let bounds = self.parse_generic_bounds(Some(self.prev_token.span))?;
+ let bounds = self.parse_generic_bounds()?;
Ok(ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
span: lo.to(self.prev_token.span),
bound_generic_params: lifetime_defs,
@@ -472,6 +453,8 @@ impl<'a> Parser<'a> {
// `<` (LIFETIME|IDENT) `:` - generic parameter with bounds
// `<` (LIFETIME|IDENT) `=` - generic parameter with a default
// `<` const - generic const parameter
+ // `<` IDENT `?` - RECOVERY for `impl<T ?Bound` missing a `:`, meant to
+ // avoid the `T?` to `Option<T>` recovery for types.
// The only truly ambiguous case is
// `<` IDENT `>` `::` IDENT ...
// we disambiguate it in favor of generics (`impl<T> ::absolute::Path<T> { ... }`)
@@ -482,6 +465,9 @@ impl<'a> Parser<'a> {
|| self.look_ahead(start + 1, |t| t.is_lifetime() || t.is_ident())
&& self.look_ahead(start + 2, |t| {
matches!(t.kind, token::Gt | token::Comma | token::Colon | token::Eq)
+ // Recovery-only branch -- this could be removed,
+ // since it only affects diagnostics currently.
+ || matches!(t.kind, token::Question)
})
|| self.is_keyword_ahead(start + 1, &[kw::Const]))
}
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 6422b8ac1..3783ec41b 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -71,7 +71,7 @@ impl<'a> Parser<'a> {
if !self.eat(term) {
let token_str = super::token_descr(&self.token);
if !self.maybe_consume_incorrect_semicolon(&items) {
- let msg = &format!("expected item, found {token_str}");
+ let msg = format!("expected item, found {token_str}");
let mut err = self.struct_span_err(self.token.span, msg);
let label = if self.is_kw_followed_by_ident(kw::Let) {
"consider using `const` or `static` instead of `let` for global variables"
@@ -181,11 +181,11 @@ impl<'a> Parser<'a> {
/// Error in-case `default` was parsed in an in-appropriate context.
fn error_on_unconsumed_default(&self, def: Defaultness, kind: &ItemKind) {
if let Defaultness::Default(span) = def {
- let msg = format!("{} {} cannot be `default`", kind.article(), kind.descr());
- self.struct_span_err(span, &msg)
- .span_label(span, "`default` because of this")
- .note("only associated `fn`, `const`, and `type` items can be `default`")
- .emit();
+ self.sess.emit_err(errors::InappropriateDefault {
+ span,
+ article: kind.article(),
+ descr: kind.descr(),
+ });
}
}
@@ -265,6 +265,9 @@ impl<'a> Parser<'a> {
// UNION ITEM
self.bump(); // `union`
self.parse_item_union()?
+ } else if self.is_builtin() {
+ // BUILTIN# ITEM
+ return self.parse_item_builtin();
} else if self.eat_keyword(kw::Macro) {
// MACROS 2.0 ITEM
self.parse_item_decl_macro(lo)?
@@ -310,14 +313,7 @@ impl<'a> Parser<'a> {
self.bump();
match self.parse_use_item() {
Ok(u) => {
- self.struct_span_err(span, format!("expected item, found {token_name}"))
- .span_suggestion_short(
- span,
- "items are imported using the `use` keyword",
- "use",
- Applicability::MachineApplicable,
- )
- .emit();
+ self.sess.emit_err(errors::RecoverImportAsUse { span, token_name });
Ok(Some(u))
}
Err(e) => {
@@ -441,6 +437,11 @@ impl<'a> Parser<'a> {
}
}
+ fn parse_item_builtin(&mut self) -> PResult<'a, Option<ItemInfo>> {
+ // To be expanded
+ return Ok(None);
+ }
+
/// Parses an item macro, e.g., `item!();`.
fn parse_item_macro(&mut self, vis: &Visibility) -> PResult<'a, MacCall> {
let path = self.parse_path(PathStyle::Mod)?; // `foo::bar`
@@ -450,7 +451,7 @@ impl<'a> Parser<'a> {
Ok(args) => {
self.eat_semi_for_macro_if_needed(&args);
self.complain_if_pub_macro(vis, false);
- Ok(MacCall { path, args, prior_type_ascription: self.last_type_ascription })
+ Ok(MacCall { path, args })
}
Err(mut err) => {
@@ -602,10 +603,24 @@ impl<'a> Parser<'a> {
let path = match ty_first.kind {
// This notably includes paths passed through `ty` macro fragments (#46438).
TyKind::Path(None, path) => path,
- _ => {
- self.sess.emit_err(errors::ExpectedTraitInTraitImplFoundType {
- span: ty_first.span,
- });
+ other => {
+ if let TyKind::ImplTrait(_, bounds) = other
+ && let [bound] = bounds.as_slice()
+ {
+ // Suggest removing extra `impl` keyword:
+ // `impl<T: Default> impl Default for Wrapper<T>`
+ // ^^^^^
+ let extra_impl_kw = ty_first.span.until(bound.span());
+ self.sess
+ .emit_err(errors::ExtraImplKeywordInTraitImpl {
+ extra_impl_kw,
+ impl_trait_span: ty_first.span
+ });
+ } else {
+ self.sess.emit_err(errors::ExpectedTraitInTraitImplFoundType {
+ span: ty_first.span,
+ });
+ }
err_path(ty_first.span)
}
};
@@ -684,7 +699,7 @@ impl<'a> Parser<'a> {
// ```
&& self
.span_to_snippet(self.prev_token.span)
- .map_or(false, |snippet| snippet == "}")
+ .is_ok_and(|snippet| snippet == "}")
&& self.token.kind == token::Semi;
let mut semicolon_span = self.token.span;
if !is_unnecessary_semicolon {
@@ -795,11 +810,7 @@ impl<'a> Parser<'a> {
// Parse optional colon and supertrait bounds.
let had_colon = self.eat(&token::Colon);
let span_at_colon = self.prev_token.span;
- let bounds = if had_colon {
- self.parse_generic_bounds(Some(self.prev_token.span))?
- } else {
- Vec::new()
- };
+ let bounds = if had_colon { self.parse_generic_bounds()? } else { Vec::new() };
let span_before_eq = self.prev_token.span;
if self.eat(&token::Eq) {
@@ -809,7 +820,7 @@ impl<'a> Parser<'a> {
self.sess.emit_err(errors::BoundsNotAllowedOnTraitAliases { span });
}
- let bounds = self.parse_generic_bounds(None)?;
+ let bounds = self.parse_generic_bounds()?;
generics.where_clause = self.parse_where_clause()?;
self.expect_semi()?;
@@ -890,7 +901,7 @@ impl<'a> Parser<'a> {
// Parse optional colon and param bounds.
let bounds =
- if self.eat(&token::Colon) { self.parse_generic_bounds(None)? } else { Vec::new() };
+ if self.eat(&token::Colon) { self.parse_generic_bounds()? } else { Vec::new() };
let before_where_clause = self.parse_where_clause()?;
let ty = if self.eat(&token::Eq) { Some(self.parse_ty()?) } else { None };
@@ -963,15 +974,8 @@ impl<'a> Parser<'a> {
} else {
// Recover from using a colon as path separator.
while self.eat_noexpect(&token::Colon) {
- self.struct_span_err(self.prev_token.span, "expected `::`, found `:`")
- .span_suggestion_short(
- self.prev_token.span,
- "use double colon",
- "::",
- Applicability::MachineApplicable,
- )
- .note_once("import paths are delimited using `::`")
- .emit();
+ self.sess
+ .emit_err(errors::SingleColonImportPath { span: self.prev_token.span });
// We parse the rest of the path and append it to the original prefix.
self.parse_path_segments(&mut prefix.segments, PathStyle::Mod, None)?;
@@ -1134,13 +1138,11 @@ impl<'a> Parser<'a> {
))
}
- fn error_bad_item_kind<T>(&self, span: Span, kind: &ItemKind, ctx: &str) -> Option<T> {
+ fn error_bad_item_kind<T>(&self, span: Span, kind: &ItemKind, ctx: &'static str) -> Option<T> {
// FIXME(#100717): needs variant for each `ItemKind` (instead of using `ItemKind::descr()`)
let span = self.sess.source_map().guess_head_span(span);
let descr = kind.descr();
- self.struct_span_err(span, &format!("{descr} is not supported in {ctx}"))
- .help(&format!("consider moving the {descr} out to a nearby module scope"))
- .emit();
+ self.sess.emit_err(errors::BadItemKind { span, descr, ctx });
None
}
@@ -1282,6 +1284,7 @@ impl<'a> Parser<'a> {
}
}
+ let prev_span = self.prev_token.span;
let id = self.parse_ident()?;
let mut generics = self.parse_generics()?;
generics.where_clause = self.parse_where_clause()?;
@@ -1293,10 +1296,28 @@ impl<'a> Parser<'a> {
(thin_vec![], false)
} else {
self.parse_delim_comma_seq(Delimiter::Brace, |p| p.parse_enum_variant()).map_err(
- |mut e| {
- e.span_label(id.span, "while parsing this enum");
+ |mut err| {
+ err.span_label(id.span, "while parsing this enum");
+ if self.token == token::Colon {
+ let snapshot = self.create_snapshot_for_diagnostic();
+ self.bump();
+ match self.parse_ty() {
+ Ok(_) => {
+ err.span_suggestion_verbose(
+ prev_span,
+ "perhaps you meant to use `struct` here",
+ "struct".to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ Err(e) => {
+ e.cancel();
+ }
+ }
+ self.restore_snapshot(snapshot);
+ }
self.recover_stmt();
- e
+ err
},
)?
};
@@ -1445,7 +1466,7 @@ impl<'a> Parser<'a> {
VariantData::Struct(fields, recovered)
} else {
let token_str = super::token_descr(&self.token);
- let msg = &format!("expected `where` or `{{` after union name, found {token_str}");
+ let msg = format!("expected `where` or `{{` after union name, found {token_str}");
let mut err = self.struct_span_err(self.token.span, msg);
err.span_label(self.token.span, "expected `where` or `{` after union name");
return Err(err);
@@ -1481,7 +1502,7 @@ impl<'a> Parser<'a> {
self.eat(&token::CloseDelim(Delimiter::Brace));
} else {
let token_str = super::token_descr(&self.token);
- let msg = &format!(
+ let msg = format!(
"expected {}`{{` after struct name, found {}",
if parsed_where { "" } else { "`where`, or " },
token_str
@@ -1618,7 +1639,7 @@ impl<'a> Parser<'a> {
let sp = self.prev_token.span.shrink_to_hi();
let mut err = self.struct_span_err(
sp,
- &format!("expected `,`, or `}}`, found {}", super::token_descr(&self.token)),
+ format!("expected `,`, or `}}`, found {}", super::token_descr(&self.token)),
);
// Try to recover extra trailing angle brackets
@@ -1713,27 +1734,13 @@ impl<'a> Parser<'a> {
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) {
- self.struct_span_err(self.token.span, "found single colon in a struct field type path")
- .span_suggestion_verbose(
- self.token.span,
- "write a path separator here",
- "::",
- Applicability::MaybeIncorrect,
- )
- .emit();
+ self.sess.emit_err(errors::SingleColonStructType { span: self.token.span });
}
if self.token.kind == token::Eq {
self.bump();
let const_expr = self.parse_expr_anon_const()?;
let sp = ty.span.shrink_to_hi().to(const_expr.value.span);
- self.struct_span_err(sp, "default values on `struct` fields aren't supported")
- .span_suggestion(
- sp,
- "remove this unsupported default value",
- "",
- Applicability::MachineApplicable,
- )
- .emit();
+ self.sess.emit_err(errors::EqualsStructDefault { span: sp });
}
Ok(FieldDef {
span: lo.to(self.prev_token.span),
@@ -1770,7 +1777,7 @@ impl<'a> Parser<'a> {
Ok(_) => {
let mut err = self.struct_span_err(
lo.to(self.prev_token.span),
- &format!("functions are not allowed in {adt_ty} definitions"),
+ format!("functions are not allowed in {adt_ty} definitions"),
);
err.help(
"unlike in C++, Java, and C#, functions are declared in `impl` blocks",
@@ -1789,7 +1796,7 @@ impl<'a> Parser<'a> {
Ok((ident, _)) => {
let mut err = self.struct_span_err(
lo.with_hi(ident.span.hi()),
- &format!("structs are not allowed in {adt_ty} definitions"),
+ format!("structs are not allowed in {adt_ty} definitions"),
);
err.help("consider creating a new `struct` definition instead of nesting");
err
@@ -1871,14 +1878,10 @@ impl<'a> Parser<'a> {
return IsMacroRulesItem::Yes { has_bang: true };
} else if self.look_ahead(1, |t| (t.is_ident())) {
// macro_rules foo
- self.struct_span_err(macro_rules_span, "expected `!` after `macro_rules`")
- .span_suggestion(
- macro_rules_span,
- "add a `!`",
- "macro_rules!",
- Applicability::MachineApplicable,
- )
- .emit();
+ self.sess.emit_err(errors::MacroRulesMissingBang {
+ span: macro_rules_span,
+ hi: macro_rules_span.shrink_to_hi(),
+ });
return IsMacroRulesItem::Yes { has_bang: false };
}
@@ -1903,9 +1906,7 @@ impl<'a> Parser<'a> {
if self.eat(&token::Not) {
// Handle macro_rules! foo!
let span = self.prev_token.span;
- self.struct_span_err(span, "macro names aren't followed by a `!`")
- .span_suggestion(span, "remove the `!`", "", Applicability::MachineApplicable)
- .emit();
+ self.sess.emit_err(errors::MacroNameRemoveBang { span });
}
let body = self.parse_delim_args()?;
@@ -1925,25 +1926,9 @@ impl<'a> Parser<'a> {
let vstr = pprust::vis_to_string(vis);
let vstr = vstr.trim_end();
if macro_rules {
- let msg = format!("can't qualify macro_rules invocation with `{vstr}`");
- self.struct_span_err(vis.span, &msg)
- .span_suggestion(
- vis.span,
- "try exporting the macro",
- "#[macro_export]",
- Applicability::MaybeIncorrect, // speculative
- )
- .emit();
+ self.sess.emit_err(errors::MacroRulesVisibility { span: vis.span, vis: vstr });
} else {
- self.struct_span_err(vis.span, "can't qualify macro invocation with `pub`")
- .span_suggestion(
- vis.span,
- "remove the visibility",
- "",
- Applicability::MachineApplicable,
- )
- .help(&format!("try adjusting the macro to put `{vstr}` inside the invocation"))
- .emit();
+ self.sess.emit_err(errors::MacroInvocationVisibility { span: vis.span, vis: vstr });
}
}
@@ -1989,18 +1974,12 @@ impl<'a> Parser<'a> {
let kw_token = self.token.clone();
let kw_str = pprust::token_to_string(&kw_token);
let item = self.parse_item(ForceCollect::No)?;
-
- self.struct_span_err(
- kw_token.span,
- &format!("`{kw_str}` definition cannot be nested inside `{keyword}`"),
- )
- .span_suggestion(
- item.unwrap().span,
- &format!("consider creating a new `{kw_str}` definition instead of nesting"),
- "",
- Applicability::MaybeIncorrect,
- )
- .emit();
+ self.sess.emit_err(errors::NestedAdt {
+ span: kw_token.span,
+ item: item.unwrap().span,
+ kw_str,
+ keyword: keyword.as_str(),
+ });
// We successfully parsed the item but we must inform the caller about nested problem.
return Ok(false);
}
@@ -2139,13 +2118,10 @@ impl<'a> Parser<'a> {
let _ = self.parse_expr()?;
self.expect_semi()?; // `;`
let span = eq_sp.to(self.prev_token.span);
- self.struct_span_err(span, "function body cannot be `= expression;`")
- .multipart_suggestion(
- "surround the expression with `{` and `}` instead of `=` and `;`",
- vec![(eq_sp, "{".to_string()), (self.prev_token.span, " }".to_string())],
- Applicability::MachineApplicable,
- )
- .emit();
+ self.sess.emit_err(errors::FunctionBodyEqualsExpr {
+ span,
+ sugg: errors::FunctionBodyEqualsExprSugg { eq: eq_sp, semi: self.prev_token.span },
+ });
(AttrVec::new(), Some(self.mk_block_err(span)))
} else {
let expected = if req_body {
@@ -2289,11 +2265,11 @@ impl<'a> Parser<'a> {
err.span_suggestion(
self.token.uninterpolated_span(),
- &format!("`{original_kw}` already used earlier, remove this one"),
+ format!("`{original_kw}` already used earlier, remove this one"),
"",
Applicability::MachineApplicable,
)
- .span_note(original_sp, &format!("`{original_kw}` first seen here"));
+ .span_note(original_sp, format!("`{original_kw}` first seen here"));
}
// The keyword has not been seen yet, suggest correct placement in the function front matter
else if let Some(WrongKw::Misplaced(correct_pos_sp)) = wrong_kw {
@@ -2304,7 +2280,7 @@ impl<'a> Parser<'a> {
err.span_suggestion(
correct_pos_sp.to(misplaced_qual_sp),
- &format!("`{misplaced_qual}` must come before `{current_qual}`"),
+ format!("`{misplaced_qual}` must come before `{current_qual}`"),
format!("{misplaced_qual} {current_qual}"),
Applicability::MachineApplicable,
).note("keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`");
@@ -2328,7 +2304,7 @@ impl<'a> Parser<'a> {
if matches!(orig_vis.kind, VisibilityKind::Inherited) {
err.span_suggestion(
sp_start.to(self.prev_token.span),
- &format!("visibility `{vs}` must come before `{snippet}`"),
+ format!("visibility `{vs}` must come before `{snippet}`"),
format!("{vs} {snippet}"),
Applicability::MachineApplicable,
);
@@ -2577,14 +2553,12 @@ impl<'a> Parser<'a> {
}
fn recover_self_param(&mut self) -> bool {
- match self
- .parse_outer_attributes()
- .and_then(|_| self.parse_self_param())
- .map_err(|e| e.cancel())
- {
- Ok(Some(_)) => true,
- _ => false,
- }
+ matches!(
+ self.parse_outer_attributes()
+ .and_then(|_| self.parse_self_param())
+ .map_err(|e| e.cancel()),
+ Ok(Some(_))
+ )
}
}
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index aa57b8047..c23420661 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -43,7 +43,7 @@ use thin_vec::ThinVec;
use tracing::debug;
use crate::errors::{
- IncorrectVisibilityRestriction, MismatchedClosingDelimiter, NonStringAbiLiteral,
+ self, IncorrectVisibilityRestriction, MismatchedClosingDelimiter, NonStringAbiLiteral,
};
bitflags::bitflags! {
@@ -148,9 +148,6 @@ pub struct Parser<'a> {
max_angle_bracket_count: u32,
last_unexpected_token_span: Option<Span>,
- /// Span pointing at the `:` for the last type ascription the parser has seen, and whether it
- /// looked like it could have been a mistyped path or literal `Option:Some(42)`).
- pub last_type_ascription: Option<(Span, bool /* likely path typo */)>,
/// If present, this `Parser` is not parsing Rust code but rather a macro call.
subparser_name: Option<&'static str>,
capture_state: CaptureState,
@@ -165,7 +162,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<'_>, 288);
+rustc_data_structures::static_assert_size!(Parser<'_>, 272);
/// Stores span information about a closure.
#[derive(Clone)]
@@ -470,7 +467,6 @@ impl<'a> Parser<'a> {
unmatched_angle_bracket_count: 0,
max_angle_bracket_count: 0,
last_unexpected_token_span: None,
- last_type_ascription: None,
subparser_name,
capture_state: CaptureState {
capturing: Capturing::No,
@@ -540,7 +536,9 @@ impl<'a> Parser<'a> {
} else if inedible.contains(&self.token.kind) {
// leave it in the input
Ok(false)
- } else if self.last_unexpected_token_span == Some(self.token.span) {
+ } else if self.token.kind != token::Eof
+ && self.last_unexpected_token_span == Some(self.token.span)
+ {
FatalError.raise();
} else {
self.expected_one_of_not_found(edible, inedible)
@@ -663,15 +661,10 @@ impl<'a> Parser<'a> {
if case == Case::Insensitive
&& let Some((ident, /* is_raw */ false)) = self.token.ident()
&& ident.as_str().to_lowercase() == kw.as_str().to_lowercase() {
- self
- .struct_span_err(ident.span, format!("keyword `{kw}` is written in a wrong case"))
- .span_suggestion(
- ident.span,
- "write it in the correct case",
- kw,
- Applicability::MachineApplicable
- ).emit();
-
+ self.sess.emit_err(errors::KwBadCase {
+ span: ident.span,
+ kw: kw.as_str()
+ });
self.bump();
return true;
}
@@ -914,7 +907,7 @@ impl<'a> Parser<'a> {
expect_err
.span_suggestion_verbose(
self.prev_token.span.shrink_to_hi().until(self.token.span),
- &msg,
+ msg,
" @ ",
Applicability::MaybeIncorrect,
)
@@ -930,7 +923,7 @@ impl<'a> Parser<'a> {
expect_err
.span_suggestion_short(
sp,
- &format!("missing `{}`", token_str),
+ format!("missing `{}`", token_str),
token_str,
Applicability::MaybeIncorrect,
)
@@ -946,10 +939,14 @@ impl<'a> Parser<'a> {
// propagate the help message from sub error 'e' to main error 'expect_err;
expect_err.children.push(xx.clone());
}
- expect_err.emit();
-
e.cancel();
- break;
+ if self.token == token::Colon {
+ // we will try to recover in `maybe_recover_struct_lit_bad_delims`
+ return Err(expect_err);
+ } else {
+ expect_err.emit();
+ break;
+ }
}
}
}
diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs
index 7a4d53ed8..adb0d372a 100644
--- a/compiler/rustc_parse/src/parser/nonterminal.rs
+++ b/compiler/rustc_parse/src/parser/nonterminal.rs
@@ -20,12 +20,10 @@ 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 {
- match *nt {
- token::NtItem(_) | token::NtBlock(_) | token::NtVis(_) | token::NtLifetime(_) => {
- false
- }
- _ => true,
- }
+ !matches!(
+ *nt,
+ token::NtItem(_) | token::NtBlock(_) | token::NtVis(_) | token::NtLifetime(_)
+ )
}
match kind {
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index 2246002f5..c317d9636 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -1,6 +1,6 @@
use super::{ForceCollect, Parser, PathStyle, TrailingToken};
use crate::errors::{
- AmbiguousRangePattern, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed,
+ self, AmbiguousRangePattern, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed,
DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt,
ExpectedCommaAfterPatternField, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow,
InclusiveRangeNoEnd, InvalidMutInPattern, PatternOnWrongSideOfAt, RefMutOrderIncorrect,
@@ -406,11 +406,11 @@ impl<'a> Parser<'a> {
// Parse pattern starting with a path
let (qself, path) = if self.eat_lt() {
// Parse a qualified path
- let (qself, path) = self.parse_qpath(PathStyle::Expr)?;
+ let (qself, path) = self.parse_qpath(PathStyle::Pat)?;
(Some(qself), path)
} else {
// Parse an unqualified path
- (None, self.parse_path(PathStyle::Expr)?)
+ (None, self.parse_path(PathStyle::Pat)?)
};
let span = lo.to(self.prev_token.span);
@@ -444,7 +444,7 @@ impl<'a> Parser<'a> {
super::token_descr(&self_.token)
);
- let mut err = self_.struct_span_err(self_.token.span, &msg);
+ let mut err = self_.struct_span_err(self_.token.span, msg);
err.span_label(self_.token.span, format!("expected {}", expected));
err
});
@@ -666,7 +666,7 @@ impl<'a> Parser<'a> {
fn parse_pat_mac_invoc(&mut self, path: Path) -> PResult<'a, PatKind> {
self.bump();
let args = self.parse_delim_args()?;
- let mac = P(MacCall { path, args, prior_type_ascription: self.last_type_ascription });
+ let mac = P(MacCall { path, args });
Ok(PatKind::MacCall(mac))
}
@@ -680,7 +680,7 @@ impl<'a> Parser<'a> {
let expected = Expected::to_string_or_fallback(expected);
let msg = format!("expected {}, found {}", expected, super::token_descr(&self.token));
- let mut err = self.struct_span_err(self.token.span, &msg);
+ let mut err = self.struct_span_err(self.token.span, msg);
err.span_label(self.token.span, format!("expected {}", expected));
let sp = self.sess.source_map().start_point(self.token.span);
@@ -789,11 +789,11 @@ impl<'a> Parser<'a> {
let lo = self.token.span;
let (qself, path) = if self.eat_lt() {
// Parse a qualified path
- let (qself, path) = self.parse_qpath(PathStyle::Expr)?;
+ let (qself, path) = self.parse_qpath(PathStyle::Pat)?;
(Some(qself), path)
} else {
// Parse an unqualified path
- (None, self.parse_path(PathStyle::Expr)?)
+ (None, self.parse_path(PathStyle::Pat)?)
};
let hi = self.prev_token.span;
Ok(self.mk_expr(lo.to(hi), ExprKind::Path(qself, path)))
@@ -908,18 +908,13 @@ impl<'a> Parser<'a> {
let box_span = self.prev_token.span;
if self.isnt_pattern_start() {
- self.struct_span_err(
- self.token.span,
- format!("expected pattern, found {}", super::token_descr(&self.token)),
- )
- .span_note(box_span, "`box` is a reserved keyword")
- .span_suggestion_verbose(
- box_span.shrink_to_lo(),
- "escape `box` to use it as an identifier",
- "r#",
- Applicability::MaybeIncorrect,
- )
- .emit();
+ let descr = super::token_descr(&self.token);
+ self.sess.emit_err(errors::BoxNotPat {
+ span: self.token.span,
+ kw: box_span,
+ lo: box_span.shrink_to_lo(),
+ descr,
+ });
// We cannot use `parse_pat_ident()` since it will complain `box`
// is not an identifier.
@@ -983,7 +978,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 c25c23d84..feb7e829c 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -1,5 +1,6 @@
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
use super::{Parser, Restrictions, TokenType};
+use crate::errors::PathSingleColon;
use crate::{errors, maybe_whole};
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
@@ -8,7 +9,7 @@ use rustc_ast::{
AssocConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, ParenthesizedArgs,
Path, PathSegment, QSelf,
};
-use rustc_errors::{pluralize, Applicability, PResult};
+use rustc_errors::{Applicability, IntoDiagnostic, PResult};
use rustc_span::source_map::{BytePos, Span};
use rustc_span::symbol::{kw, sym, Ident};
use std::mem;
@@ -24,7 +25,19 @@ pub enum PathStyle {
/// In all such contexts the non-path interpretation is preferred by default for practical
/// reasons, but the path interpretation can be forced by the disambiguator `::`, e.g.
/// `x<y>` - comparisons, `x::<y>` - unambiguously a path.
+ ///
+ /// Also, a path may never be followed by a `:`. This means that we can eagerly recover if
+ /// we encounter it.
Expr,
+ /// The same as `Expr`, but may be followed by a `:`.
+ /// For example, this code:
+ /// ```rust
+ /// struct S;
+ ///
+ /// let S: S;
+ /// // ^ Followed by a `:`
+ /// ```
+ Pat,
/// In other contexts, notably in types, no ambiguity exists and paths can be written
/// without the disambiguator, e.g., `x<y>` - unambiguously a path.
/// Paths with disambiguators are still accepted, `x::<Y>` - unambiguously a path too.
@@ -38,6 +51,12 @@ pub enum PathStyle {
Mod,
}
+impl PathStyle {
+ fn has_generic_ambiguity(&self) -> bool {
+ matches!(self, Self::Expr | Self::Pat)
+ }
+}
+
impl<'a> Parser<'a> {
/// Parses a qualified path.
/// Assumes that the leading `<` has been parsed already.
@@ -150,16 +169,13 @@ impl<'a> Parser<'a> {
//
if style == PathStyle::Mod && path.segments.iter().any(|segment| segment.args.is_some())
{
- parser
- .struct_span_err(
- path.segments
- .iter()
- .filter_map(|segment| segment.args.as_ref())
- .map(|arg| arg.span())
- .collect::<Vec<_>>(),
- "unexpected generic arguments in path",
- )
- .emit();
+ let span = path
+ .segments
+ .iter()
+ .filter_map(|segment| segment.args.as_ref())
+ .map(|arg| arg.span())
+ .collect::<Vec<_>>();
+ parser.sess.emit_err(errors::GenericsInPath { span });
}
};
@@ -186,7 +202,6 @@ impl<'a> Parser<'a> {
segments.push(PathSegment::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt)));
}
self.parse_path_segments(&mut segments, style, ty_generics)?;
-
Ok(Path { segments, span: lo.to(self.prev_token.span), tokens: None })
}
@@ -198,7 +213,7 @@ impl<'a> Parser<'a> {
) -> PResult<'a, ()> {
loop {
let segment = self.parse_path_segment(style, ty_generics)?;
- if style == PathStyle::Expr {
+ if style.has_generic_ambiguity() {
// In order to check for trailing angle brackets, we must have finished
// recursing (`parse_path_segment` can indirectly call this function),
// that is, the next token must be the highlighted part of the below example:
@@ -220,6 +235,29 @@ impl<'a> Parser<'a> {
segments.push(segment);
if self.is_import_coupler() || !self.eat(&token::ModSep) {
+ if style == PathStyle::Expr
+ && self.may_recover()
+ && self.token == token::Colon
+ && self.look_ahead(1, |token| token.is_ident() && !token.is_reserved_ident())
+ {
+ // Emit a special error message for `a::b:c` to help users
+ // otherwise, `a: c` might have meant to introduce a new binding
+ if self.token.span.lo() == self.prev_token.span.hi()
+ && self.look_ahead(1, |token| self.token.span.hi() == token.span.lo())
+ {
+ self.bump(); // bump past the colon
+ self.sess.emit_err(PathSingleColon {
+ span: self.prev_token.span,
+ type_ascription: self
+ .sess
+ .unstable_features
+ .is_nightly_build()
+ .then_some(()),
+ });
+ }
+ continue;
+ }
+
return Ok(());
}
}
@@ -273,8 +311,25 @@ impl<'a> Parser<'a> {
ty_generics,
)?;
self.expect_gt().map_err(|mut err| {
+ // Try to recover a `:` into a `::`
+ if self.token == token::Colon
+ && self.look_ahead(1, |token| {
+ token.is_ident() && !token.is_reserved_ident()
+ })
+ {
+ err.cancel();
+ err = PathSingleColon {
+ span: self.token.span,
+ type_ascription: self
+ .sess
+ .unstable_features
+ .is_nightly_build()
+ .then_some(()),
+ }
+ .into_diagnostic(self.diagnostic());
+ }
// Attempt to find places where a missing `>` might belong.
- if let Some(arg) = args
+ else if let Some(arg) = args
.iter()
.rev()
.find(|arg| !matches!(arg, AngleBracketedArg::Constraint(_)))
@@ -467,23 +522,10 @@ impl<'a> Parser<'a> {
// i.e. no multibyte characters, in this range.
let span =
lo.with_hi(lo.lo() + BytePos(snapshot.unmatched_angle_bracket_count));
- self.struct_span_err(
+ self.sess.emit_err(errors::UnmatchedAngle {
span,
- &format!(
- "unmatched angle bracket{}",
- pluralize!(snapshot.unmatched_angle_bracket_count)
- ),
- )
- .span_suggestion(
- span,
- &format!(
- "remove extra angle bracket{}",
- pluralize!(snapshot.unmatched_angle_bracket_count)
- ),
- "",
- Applicability::MachineApplicable,
- )
- .emit();
+ plural: snapshot.unmatched_angle_bracket_count > 1,
+ });
// Try again without unmatched angle bracket characters.
self.parse_angle_args(ty_generics)
@@ -564,7 +606,7 @@ impl<'a> Parser<'a> {
let kind = if self.eat(&token::Colon) {
// Parse associated type constraint bound.
- let bounds = self.parse_generic_bounds(Some(self.prev_token.span))?;
+ let bounds = self.parse_generic_bounds()?;
AssocConstraintKind::Bound { bounds }
} else if self.eat(&token::Eq) {
self.parse_assoc_equality_term(ident, self.prev_token.span)?
@@ -620,10 +662,7 @@ impl<'a> Parser<'a> {
c.into()
}
Some(GenericArg::Lifetime(lt)) => {
- self.struct_span_err(span, "associated lifetimes are not supported")
- .span_label(lt.ident.span, "the lifetime is given here")
- .help("if you meant to specify a trait object, write `dyn Trait + 'lifetime`")
- .emit();
+ self.sess.emit_err(errors::AssocLifetime { span, lifetime: lt.ident.span });
self.mk_ty(span, ast::TyKind::Err).into()
}
None => {
@@ -640,14 +679,14 @@ impl<'a> Parser<'a> {
);
err.span_suggestion(
eq.to(before_next),
- &format!("remove the `=` if `{}` is a type", ident),
+ format!("remove the `=` if `{}` is a type", ident),
"",
Applicability::MaybeIncorrect,
)
} else {
err.span_label(
self.token.span,
- &format!("expected type, found {}", super::token_descr(&self.token)),
+ format!("expected type, found {}", super::token_descr(&self.token)),
)
};
return Err(err);
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index fbe5b88c4..54f9fc5d2 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -10,6 +10,8 @@ use super::{
use crate::errors;
use crate::maybe_whole;
+use crate::errors::MalformedLoopLabel;
+use ast::Label;
use rustc_ast as ast;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, TokenKind};
@@ -19,7 +21,8 @@ use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, HasAttrs, Local, Stmt};
use rustc_ast::{StmtKind, DUMMY_NODE_ID};
use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult};
use rustc_span::source_map::{BytePos, Span};
-use rustc_span::symbol::{kw, sym};
+use rustc_span::symbol::{kw, sym, Ident};
+
use std::mem;
use thin_vec::{thin_vec, ThinVec};
@@ -37,7 +40,8 @@ impl<'a> Parser<'a> {
/// If `force_collect` is [`ForceCollect::Yes`], forces collection of tokens regardless of whether
/// or not we have attributes
- pub(crate) fn parse_stmt_without_recovery(
+ // Public for `cfg_eval` macro expansion.
+ pub fn parse_stmt_without_recovery(
&mut self,
capture_semi: bool,
force_collect: ForceCollect,
@@ -87,7 +91,11 @@ impl<'a> Parser<'a> {
attrs,
errors::InvalidVariableDeclarationSub::UseLetNotVar,
)?
- } else if self.check_path() && !self.token.is_qpath_start() && !self.is_path_start_item() {
+ } else if self.check_path()
+ && !self.token.is_qpath_start()
+ && !self.is_path_start_item()
+ && !self.is_builtin()
+ {
// We have avoided contextual keywords like `union`, items with `crate` visibility,
// or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something
// that starts like a path (1 token), but it fact not a path.
@@ -96,7 +104,13 @@ impl<'a> Parser<'a> {
ForceCollect::Yes => {
self.collect_tokens_no_attrs(|this| this.parse_stmt_path_start(lo, attrs))?
}
- ForceCollect::No => self.parse_stmt_path_start(lo, attrs)?,
+ ForceCollect::No => match self.parse_stmt_path_start(lo, attrs) {
+ Ok(stmt) => stmt,
+ Err(mut err) => {
+ self.suggest_add_missing_let_for_stmt(&mut err);
+ return Err(err);
+ }
+ },
}
} else if let Some(item) = self.parse_item_common(
attrs.clone(),
@@ -186,7 +200,7 @@ impl<'a> Parser<'a> {
_ => MacStmtStyle::NoBraces,
};
- let mac = P(MacCall { path, args, prior_type_ascription: self.last_type_ascription });
+ let mac = P(MacCall { path, args });
let kind = if (style == MacStmtStyle::Braces
&& self.token != token::Dot
@@ -546,10 +560,27 @@ impl<'a> Parser<'a> {
}
let stmt = match self.parse_full_stmt(recover) {
Err(mut err) if recover.yes() => {
- self.maybe_annotate_with_ascription(&mut err, false);
if let Some(ref mut snapshot) = snapshot {
snapshot.recover_diff_marker();
}
+ if self.token == token::Colon {
+ // if next token is following a colon, it's likely a path
+ // and we can suggest a path separator
+ self.bump();
+ if self.token.span.lo() == self.prev_token.span.hi() {
+ err.span_suggestion_verbose(
+ self.prev_token.span,
+ "maybe write a path separator here",
+ "::",
+ Applicability::MaybeIncorrect,
+ );
+ }
+ if self.sess.unstable_features.is_nightly_build() {
+ // FIXME(Nilstrieb): Remove this again after a few months.
+ err.note("type ascription syntax has been removed, see issue #101728 <https://github.com/rust-lang/rust/issues/101728>");
+ }
+ }
+
err.emit();
self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
Some(self.mk_stmt_err(self.token.span))
@@ -580,47 +611,104 @@ impl<'a> Parser<'a> {
};
let mut eat_semi = true;
+ let mut add_semi_to_stmt = false;
+
match &mut stmt.kind {
// Expression without semicolon.
StmtKind::Expr(expr)
if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) => {
// Just check for errors and recover; do not eat semicolon yet.
// `expect_one_of` returns PResult<'a, bool /* recovered */>
- let replace_with_err =
- match self.expect_one_of(&[], &[token::Semi, token::CloseDelim(Delimiter::Brace)]) {
+
+ let expect_result = self.expect_one_of(&[], &[token::Semi, token::CloseDelim(Delimiter::Brace)]);
+
+ let replace_with_err = 'break_recover: {
+ match expect_result {
// Recover from parser, skip type error to avoid extra errors.
- Ok(true) => true,
- Err(mut e) => {
- if let TokenKind::DocComment(..) = self.token.kind &&
- let Ok(snippet) = self.span_to_snippet(self.token.span) {
+ Ok(true) => true,
+ Err(mut e) => {
+ if let TokenKind::DocComment(..) = self.token.kind
+ && let Ok(snippet) = self.span_to_snippet(self.token.span)
+ {
let sp = self.token.span;
let marker = &snippet[..3];
let (comment_marker, doc_comment_marker) = marker.split_at(2);
e.span_suggestion(
sp.with_hi(sp.lo() + BytePos(marker.len() as u32)),
- &format!(
+ format!(
"add a space before `{}` to use a regular comment",
doc_comment_marker,
),
format!("{} {}", comment_marker, doc_comment_marker),
Applicability::MaybeIncorrect,
);
- }
+ }
+
+ if self.recover_colon_as_semi() {
+ // recover_colon_as_semi has already emitted a nicer error.
+ e.delay_as_bug();
+ add_semi_to_stmt = true;
+ eat_semi = false;
- if let Err(mut e) =
- self.check_mistyped_turbofish_with_multiple_type_params(e, expr)
- {
- if recover.no() {
- return Err(e);
+ break 'break_recover false;
}
- e.emit();
- self.recover_stmt();
+
+ match &expr.kind {
+ ExprKind::Path(None, ast::Path { segments, .. }) if segments.len() == 1 => {
+ if self.token == token::Colon
+ && self.look_ahead(1, |token| {
+ token.is_whole_block() || matches!(
+ token.kind,
+ token::Ident(kw::For | kw::Loop | kw::While, false)
+ | token::OpenDelim(Delimiter::Brace)
+ )
+ })
+ {
+ let snapshot = self.create_snapshot_for_diagnostic();
+ let label = Label {
+ ident: Ident::from_str_and_span(
+ &format!("'{}", segments[0].ident),
+ segments[0].ident.span,
+ ),
+ };
+ match self.parse_expr_labeled(label, false) {
+ Ok(labeled_expr) => {
+ e.delay_as_bug();
+ self.sess.emit_err(MalformedLoopLabel {
+ span: label.ident.span,
+ correct_label: label.ident,
+ });
+ *expr = labeled_expr;
+ break 'break_recover false;
+ }
+ Err(err) => {
+ err.cancel();
+ self.restore_snapshot(snapshot);
+ }
+ }
+ }
+ }
+ _ => {}
+ }
+
+ if let Err(mut e) =
+ self.check_mistyped_turbofish_with_multiple_type_params(e, expr)
+ {
+ if recover.no() {
+ return Err(e);
+ }
+ e.emit();
+ self.recover_stmt();
+ }
+
+ true
+
}
- true
+ Ok(false) => false
}
- _ => false
};
+
if replace_with_err {
// We already emitted an error, so don't emit another type error
let sp = expr.span.to(self.prev_token.span);
@@ -643,9 +731,10 @@ impl<'a> Parser<'a> {
StmtKind::Empty | StmtKind::Item(_) | StmtKind::Local(_) | StmtKind::Semi(_) => eat_semi = false,
}
- if eat_semi && self.eat(&token::Semi) {
+ if add_semi_to_stmt || (eat_semi && self.eat(&token::Semi)) {
stmt = stmt.add_trailing_semicolon();
}
+
stmt.span = stmt.span.to(self.prev_token.span);
Ok(Some(stmt))
}
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 400c8dbe9..a29b696ae 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -1,10 +1,9 @@
use super::{Parser, PathStyle, TokenType};
use crate::errors::{
- DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType,
+ self, DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType,
FnPointerCannotBeAsync, FnPointerCannotBeConst, FnPtrWithGenerics, FnPtrWithGenericsSugg,
- InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime,
- NegativeBoundsNotSupported, NegativeBoundsNotSupportedSugg, NestedCVariadicType,
+ InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, NestedCVariadicType,
ReturnTypesUseThinArrow,
};
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
@@ -14,8 +13,9 @@ use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
use rustc_ast::util::case::Case;
use rustc_ast::{
- self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime,
- MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind,
+ self as ast, BareFnTy, BoundPolarity, FnRetTy, GenericBound, GenericBounds, GenericParam,
+ Generics, Lifetime, MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier,
+ TraitObjectSyntax, Ty, TyKind,
};
use rustc_errors::{Applicability, PResult};
use rustc_span::source_map::Span;
@@ -23,10 +23,10 @@ use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::Symbol;
use thin_vec::{thin_vec, ThinVec};
-/// Any `?` or `~const` modifiers that appear at the start of a bound.
+/// Any `?`, `!`, or `~const` modifiers that appear at the start of a bound.
struct BoundModifiers {
/// `?Trait`.
- maybe: Option<Span>,
+ bound_polarity: BoundPolarity,
/// `~const Trait`.
maybe_const: Option<Span>,
@@ -34,11 +34,13 @@ struct BoundModifiers {
impl BoundModifiers {
fn to_trait_bound_modifier(&self) -> TraitBoundModifier {
- match (self.maybe, self.maybe_const) {
- (None, None) => TraitBoundModifier::None,
- (Some(_), None) => TraitBoundModifier::Maybe,
- (None, Some(_)) => TraitBoundModifier::MaybeConst,
- (Some(_), Some(_)) => TraitBoundModifier::MaybeConstMaybe,
+ match (self.bound_polarity, self.maybe_const) {
+ (BoundPolarity::Positive, None) => TraitBoundModifier::None,
+ (BoundPolarity::Negative(_), None) => TraitBoundModifier::Negative,
+ (BoundPolarity::Maybe(_), None) => TraitBoundModifier::Maybe,
+ (BoundPolarity::Positive, Some(_)) => TraitBoundModifier::MaybeConst,
+ (BoundPolarity::Negative(_), Some(_)) => TraitBoundModifier::MaybeConstNegative,
+ (BoundPolarity::Maybe(_), Some(_)) => TraitBoundModifier::MaybeConstMaybe,
}
}
}
@@ -315,9 +317,8 @@ impl<'a> Parser<'a> {
}
} else {
let msg = format!("expected type, found {}", super::token_descr(&self.token));
- let mut err = self.struct_span_err(self.token.span, &msg);
+ let mut err = self.struct_span_err(self.token.span, msg);
err.span_label(self.token.span, "expected type");
- self.maybe_annotate_with_ascription(&mut err, true);
return Err(err);
};
@@ -369,7 +370,7 @@ impl<'a> Parser<'a> {
fn parse_bare_trait_object(&mut self, lo: Span, allow_plus: AllowPlus) -> PResult<'a, TyKind> {
let lt_no_plus = self.check_lifetime() && !self.look_ahead(1, |t| t.is_like_plus());
- let bounds = self.parse_generic_bounds_common(allow_plus, None)?;
+ let bounds = self.parse_generic_bounds_common(allow_plus)?;
if lt_no_plus {
self.sess.emit_err(NeedPlusAfterTraitObjectLifetime { span: lo });
}
@@ -396,7 +397,7 @@ impl<'a> Parser<'a> {
) -> PResult<'a, TyKind> {
if plus {
self.eat_plus(); // `+`, or `+=` gets split and `+` is discarded
- bounds.append(&mut self.parse_generic_bounds(Some(self.prev_token.span))?);
+ bounds.append(&mut self.parse_generic_bounds()?);
}
Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None))
}
@@ -588,24 +589,18 @@ impl<'a> Parser<'a> {
// Always parse bounds greedily for better error recovery.
if self.token.is_lifetime() {
self.look_ahead(1, |t| {
- if let token::Ident(symname, _) = t.kind {
+ if let token::Ident(sym, _) = t.kind {
// parse pattern with "'a Sized" we're supposed to give suggestion like
// "'a + Sized"
- self.struct_span_err(
- self.token.span,
- &format!("expected `+` between lifetime and {}", symname),
- )
- .span_suggestion_verbose(
- self.token.span.shrink_to_hi(),
- "add `+`",
- " +",
- Applicability::MaybeIncorrect,
- )
- .emit();
+ self.sess.emit_err(errors::MissingPlusBounds {
+ span: self.token.span,
+ hi: self.token.span.shrink_to_hi(),
+ sym,
+ });
}
})
}
- let bounds = self.parse_generic_bounds(None)?;
+ let bounds = self.parse_generic_bounds()?;
*impl_dyn_multi = bounds.len() > 1 || self.prev_token.kind == TokenKind::BinOp(token::Plus);
Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds))
}
@@ -636,7 +631,7 @@ impl<'a> Parser<'a> {
};
// Always parse bounds greedily for better error recovery.
- let bounds = self.parse_generic_bounds(None)?;
+ let bounds = self.parse_generic_bounds()?;
*impl_dyn_multi = bounds.len() > 1 || self.prev_token.kind == TokenKind::BinOp(token::Plus);
Ok(TyKind::TraitObject(bounds, syntax))
}
@@ -657,11 +652,7 @@ impl<'a> Parser<'a> {
let path = self.parse_path_inner(PathStyle::Type, ty_generics)?;
if self.eat(&token::Not) {
// Macro invocation in type position
- Ok(TyKind::MacCall(P(MacCall {
- path,
- args: self.parse_delim_args()?,
- prior_type_ascription: self.last_type_ascription,
- })))
+ Ok(TyKind::MacCall(P(MacCall { path, args: self.parse_delim_args()? })))
} else if allow_plus == AllowPlus::Yes && self.check_plus() {
// `Trait1 + Trait2 + 'a`
self.parse_remaining_bounds_path(ThinVec::new(), path, lo, true)
@@ -671,23 +662,15 @@ impl<'a> Parser<'a> {
}
}
- pub(super) fn parse_generic_bounds(
- &mut self,
- colon_span: Option<Span>,
- ) -> PResult<'a, GenericBounds> {
- self.parse_generic_bounds_common(AllowPlus::Yes, colon_span)
+ pub(super) fn parse_generic_bounds(&mut self) -> PResult<'a, GenericBounds> {
+ self.parse_generic_bounds_common(AllowPlus::Yes)
}
/// Parses bounds of a type parameter `BOUND + BOUND + ...`, possibly with trailing `+`.
///
/// See `parse_generic_bound` for the `BOUND` grammar.
- fn parse_generic_bounds_common(
- &mut self,
- allow_plus: AllowPlus,
- colon_span: Option<Span>,
- ) -> PResult<'a, GenericBounds> {
+ fn parse_generic_bounds_common(&mut self, allow_plus: AllowPlus) -> PResult<'a, GenericBounds> {
let mut bounds = Vec::new();
- let mut negative_bounds = Vec::new();
// In addition to looping while we find generic bounds:
// We continue even if we find a keyword. This is necessary for error recovery on,
@@ -704,19 +687,12 @@ impl<'a> Parser<'a> {
self.sess.emit_err(InvalidDynKeyword { span: self.token.span });
self.bump();
}
- match self.parse_generic_bound()? {
- Ok(bound) => bounds.push(bound),
- Err(neg_sp) => negative_bounds.push(neg_sp),
- }
+ bounds.push(self.parse_generic_bound()?);
if allow_plus == AllowPlus::No || !self.eat_plus() {
break;
}
}
- if !negative_bounds.is_empty() {
- self.error_negative_bounds(colon_span, &bounds, negative_bounds);
- }
-
Ok(bounds)
}
@@ -724,55 +700,22 @@ impl<'a> Parser<'a> {
fn can_begin_bound(&mut self) -> bool {
// This needs to be synchronized with `TokenKind::can_begin_bound`.
self.check_path()
- || self.check_lifetime()
- || self.check(&token::Not) // Used for error reporting only.
- || self.check(&token::Question)
- || self.check(&token::Tilde)
- || self.check_keyword(kw::For)
- || self.check(&token::OpenDelim(Delimiter::Parenthesis))
- }
-
- fn error_negative_bounds(
- &self,
- colon_span: Option<Span>,
- bounds: &[GenericBound],
- negative_bounds: Vec<Span>,
- ) {
- let sub = if let Some(bound_list) = colon_span {
- let bound_list = bound_list.to(self.prev_token.span);
- let mut new_bound_list = String::new();
- if !bounds.is_empty() {
- let mut snippets = bounds.iter().map(|bound| self.span_to_snippet(bound.span()));
- while let Some(Ok(snippet)) = snippets.next() {
- new_bound_list.push_str(" + ");
- new_bound_list.push_str(&snippet);
- }
- new_bound_list = new_bound_list.replacen(" +", ":", 1);
- }
-
- Some(NegativeBoundsNotSupportedSugg {
- bound_list,
- num_bounds: negative_bounds.len(),
- fixed: new_bound_list,
- })
- } else {
- None
- };
-
- let last_span = *negative_bounds.last().expect("no negative bounds, but still error?");
- self.sess.emit_err(NegativeBoundsNotSupported { negative_bounds, last_span, sub });
+ || self.check_lifetime()
+ || self.check(&token::Not)
+ || self.check(&token::Question)
+ || self.check(&token::Tilde)
+ || self.check_keyword(kw::For)
+ || self.check(&token::OpenDelim(Delimiter::Parenthesis))
}
/// Parses a bound according to the grammar:
/// ```ebnf
/// BOUND = TY_BOUND | LT_BOUND
/// ```
- fn parse_generic_bound(&mut self) -> PResult<'a, Result<GenericBound, Span>> {
- let anchor_lo = self.prev_token.span;
+ fn parse_generic_bound(&mut self) -> PResult<'a, GenericBound> {
let lo = self.token.span;
let has_parens = self.eat(&token::OpenDelim(Delimiter::Parenthesis));
let inner_lo = self.token.span;
- let is_negative = self.eat(&token::Not);
let modifiers = self.parse_ty_bound_modifiers()?;
let bound = if self.token.is_lifetime() {
@@ -782,7 +725,7 @@ impl<'a> Parser<'a> {
self.parse_generic_ty_bound(lo, has_parens, modifiers)?
};
- Ok(if is_negative { Err(anchor_lo.to(self.prev_token.span)) } else { Ok(bound) })
+ Ok(bound)
}
/// Parses a lifetime ("outlives") bound, e.g. `'a`, according to:
@@ -807,16 +750,17 @@ impl<'a> Parser<'a> {
/// Emits an error if any trait bound modifiers were present.
fn error_lt_bound_with_modifiers(&self, modifiers: BoundModifiers) {
if let Some(span) = modifiers.maybe_const {
- self.struct_span_err(
- span,
- "`~const` may only modify trait bounds, not lifetime bounds",
- )
- .emit();
+ self.sess.emit_err(errors::TildeConstLifetime { span });
}
- if let Some(span) = modifiers.maybe {
- self.struct_span_err(span, "`?` may only modify trait bounds, not lifetime bounds")
- .emit();
+ match modifiers.bound_polarity {
+ BoundPolarity::Positive => {}
+ BoundPolarity::Negative(span) => {
+ self.sess.emit_err(errors::ModifierLifetime { span, sigil: "!" });
+ }
+ BoundPolarity::Maybe(span) => {
+ self.sess.emit_err(errors::ModifierLifetime { span, sigil: "?" });
+ }
}
}
@@ -824,19 +768,14 @@ impl<'a> Parser<'a> {
fn recover_paren_lifetime(&mut self, lo: Span, inner_lo: Span) -> PResult<'a, ()> {
let inner_span = inner_lo.to(self.prev_token.span);
self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
- let mut err = self.struct_span_err(
- lo.to(self.prev_token.span),
- "parenthesized lifetime bounds are not supported",
- );
- if let Ok(snippet) = self.span_to_snippet(inner_span) {
- err.span_suggestion_short(
- lo.to(self.prev_token.span),
- "remove the parentheses",
- snippet,
- Applicability::MachineApplicable,
- );
- }
- err.emit();
+ let span = lo.to(self.prev_token.span);
+ let (sugg, snippet) = if let Ok(snippet) = self.span_to_snippet(inner_span) {
+ (Some(span), snippet)
+ } else {
+ (None, String::new())
+ };
+
+ self.sess.emit_err(errors::ParenthesizedLifetime { span, sugg, snippet });
Ok(())
}
@@ -857,24 +796,23 @@ impl<'a> Parser<'a> {
} else if self.eat_keyword(kw::Const) {
let span = self.prev_token.span;
self.sess.gated_spans.gate(sym::const_trait_impl, span);
-
- self.struct_span_err(span, "const bounds must start with `~`")
- .span_suggestion(
- span.shrink_to_lo(),
- "add `~`",
- "~",
- Applicability::MachineApplicable,
- )
- .emit();
+ self.sess.emit_err(errors::ConstMissingTilde { span, start: span.shrink_to_lo() });
Some(span)
} else {
None
};
- let maybe = self.eat(&token::Question).then_some(self.prev_token.span);
+ let bound_polarity = if self.eat(&token::Question) {
+ BoundPolarity::Maybe(self.prev_token.span)
+ } else if self.eat(&token::Not) {
+ self.sess.gated_spans.gate(sym::negative_bounds, self.prev_token.span);
+ BoundPolarity::Negative(self.prev_token.span)
+ } else {
+ BoundPolarity::Positive
+ };
- Ok(BoundModifiers { maybe, maybe_const })
+ Ok(BoundModifiers { bound_polarity, maybe_const })
}
/// Parses a type bound according to:
@@ -944,14 +882,10 @@ impl<'a> Parser<'a> {
self.parse_remaining_bounds(bounds, true)?;
self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
let sp = vec![lo, self.prev_token.span];
- let sugg = vec![(lo, String::from(" ")), (self.prev_token.span, String::new())];
- self.struct_span_err(sp, "incorrect braces around trait bounds")
- .multipart_suggestion(
- "remove the parentheses",
- sugg,
- Applicability::MachineApplicable,
- )
- .emit();
+ self.sess.emit_err(errors::IncorrectBracesTraitBounds {
+ span: sp,
+ sugg: errors::IncorrectBracesTraitBoundsSugg { l: lo, r: self.prev_token.span },
+ });
} 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 72402a200..928fdce31 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -1,6 +1,6 @@
//! Meta-syntax validation logic of attributes for post-expansion.
-use crate::parse_in;
+use crate::{errors, parse_in};
use rustc_ast::tokenstream::DelimSpan;
use rustc_ast::MetaItemKind;
@@ -45,7 +45,7 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta
kind: match &item.args {
AttrArgs::Empty => MetaItemKind::Word,
AttrArgs::Delimited(DelimArgs { dspan, delim, tokens }) => {
- check_meta_bad_delim(sess, *dspan, *delim, "wrong meta list delimiters");
+ check_meta_bad_delim(sess, *dspan, *delim);
let nmis = parse_in(sess, tokens.clone(), "meta list", |p| p.parse_meta_seq_top())?;
MetaItemKind::List(nmis)
}
@@ -68,7 +68,7 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta
}
} else {
// The non-error case can happen with e.g. `#[foo = 1+1]`. The error case can
- // happen with e.g. `#[foo = include_str!("non-existent-file.rs")]`; in that
+ // happen with e.g. `#[foo = include_str!("nonexistent-file.rs")]`; in that
// case we delay the error because an earlier error will have already been
// reported.
let msg = format!("unexpected expression: `{}`", pprust::expr_to_string(expr));
@@ -84,19 +84,24 @@ 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, msg: &str) {
+pub fn check_meta_bad_delim(sess: &ParseSess, span: DelimSpan, delim: MacDelimiter) {
if let ast::MacDelimiter::Parenthesis = delim {
return;
}
+ sess.emit_err(errors::MetaBadDelim {
+ span: span.entire(),
+ sugg: errors::MetaBadDelimSugg { open: span.open, close: span.close },
+ });
+}
- sess.span_diagnostic
- .struct_span_err(span.entire(), msg)
- .multipart_suggestion(
- "the delimiters should be `(` and `)`",
- vec![(span.open, "(".to_string()), (span.close, ")".to_string())],
- Applicability::MachineApplicable,
- )
- .emit();
+pub fn check_cfg_attr_bad_delim(sess: &ParseSess, span: DelimSpan, delim: MacDelimiter) {
+ if let ast::MacDelimiter::Parenthesis = delim {
+ return;
+ }
+ sess.emit_err(errors::CfgAttrBadDelim {
+ span: span.entire(),
+ sugg: errors::MetaBadDelimSugg { open: span.open, close: span.close },
+ });
}
/// Checks that the given meta-item is compatible with this `AttributeTemplate`.
@@ -181,10 +186,10 @@ fn emit_malformed_attribute(
suggestions.push(code);
}
if should_warn(name) {
- sess.buffer_lint(&ILL_FORMED_ATTRIBUTE_INPUT, span, ast::CRATE_NODE_ID, &msg);
+ sess.buffer_lint(&ILL_FORMED_ATTRIBUTE_INPUT, span, ast::CRATE_NODE_ID, msg);
} else {
sess.span_diagnostic
- .struct_span_err(span, &error_msg)
+ .struct_span_err(span, error_msg)
.span_suggestions(
span,
if suggestions.len() == 1 {