From 2e00214b3efbdfeefaa0fe9e8b8fd519de7adc35 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:19:50 +0200 Subject: Merging upstream version 1.69.0+dfsg1. Signed-off-by: Daniel Baumann --- compiler/rustc_parse/src/errors.rs | 1136 +++++++++++++++++++- compiler/rustc_parse/src/lexer/diagnostics.rs | 119 ++ compiler/rustc_parse/src/lexer/mod.rs | 189 ++-- compiler/rustc_parse/src/lexer/tokentrees.rs | 150 +-- .../src/lexer/unescape_error_reporting.rs | 248 ++--- compiler/rustc_parse/src/lexer/unicode_chars.rs | 60 +- compiler/rustc_parse/src/lib.rs | 36 +- compiler/rustc_parse/src/parser/attr.rs | 26 +- compiler/rustc_parse/src/parser/attr_wrapper.rs | 6 +- compiler/rustc_parse/src/parser/diagnostics.rs | 173 ++- compiler/rustc_parse/src/parser/expr.rs | 543 ++++++---- compiler/rustc_parse/src/parser/generics.rs | 61 +- compiler/rustc_parse/src/parser/item.rs | 429 +++----- compiler/rustc_parse/src/parser/mod.rs | 203 ++-- compiler/rustc_parse/src/parser/nonterminal.rs | 22 +- compiler/rustc_parse/src/parser/pat.rs | 382 +++---- compiler/rustc_parse/src/parser/path.rs | 58 +- compiler/rustc_parse/src/parser/stmt.rs | 96 +- compiler/rustc_parse/src/parser/ty.rs | 182 ++-- 19 files changed, 2553 insertions(+), 1566 deletions(-) create mode 100644 compiler/rustc_parse/src/lexer/diagnostics.rs (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 06b970ad9..1662db36d 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1,11 +1,15 @@ +use std::borrow::Cow; + use rustc_ast::token::Token; -use rustc_ast::Path; -use rustc_errors::{fluent, AddToDiagnostic, Applicability, EmissionGuarantee, IntoDiagnostic}; +use rustc_ast::{Path, Visibility}; +use rustc_errors::{AddToDiagnostic, Applicability, EmissionGuarantee, IntoDiagnostic}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_session::errors::ExprParenthesesNeeded; +use rustc_span::edition::{Edition, LATEST_STABLE_EDITION}; use rustc_span::symbol::Ident; use rustc_span::{Span, Symbol}; +use crate::fluent_generated as fluent; use crate::parser::TokenDescription; #[derive(Diagnostic)] @@ -75,7 +79,7 @@ pub(crate) struct IncorrectSemicolon<'a> { #[diag(parse_incorrect_use_of_await)] pub(crate) struct IncorrectUseOfAwait { #[primary_span] - #[suggestion(parentheses_suggestion, code = "", applicability = "machine-applicable")] + #[suggestion(parse_parentheses_suggestion, code = "", applicability = "machine-applicable")] pub span: Span, } @@ -84,7 +88,7 @@ pub(crate) struct IncorrectUseOfAwait { pub(crate) struct IncorrectAwait { #[primary_span] pub span: Span, - #[suggestion(postfix_suggestion, code = "{expr}.await{question_mark}")] + #[suggestion(parse_postfix_suggestion, code = "{expr}.await{question_mark}")] pub sugg_span: (Span, Applicability), pub expr: String, pub question_mark: &'static str, @@ -137,7 +141,7 @@ pub(crate) struct InvalidComparisonOperator { #[derive(Subdiagnostic)] pub(crate) enum InvalidComparisonOperatorSub { #[suggestion( - use_instead, + parse_use_instead, style = "short", applicability = "machine-applicable", code = "{correct}" @@ -148,7 +152,7 @@ pub(crate) enum InvalidComparisonOperatorSub { invalid: String, correct: String, }, - #[label(spaceship_operator_invalid)] + #[label(parse_spaceship_operator_invalid)] Spaceship(#[primary_span] Span), } @@ -166,14 +170,14 @@ pub(crate) struct InvalidLogicalOperator { #[derive(Subdiagnostic)] pub(crate) enum InvalidLogicalOperatorSub { #[suggestion( - use_amp_amp_for_conjunction, + parse_use_amp_amp_for_conjunction, style = "short", applicability = "machine-applicable", code = "&&" )] Conjunction(#[primary_span] Span), #[suggestion( - use_pipe_pipe_for_disjunction, + parse_use_pipe_pipe_for_disjunction, style = "short", applicability = "machine-applicable", code = "||" @@ -259,14 +263,14 @@ pub(crate) struct UnexpectedTokenAfterLabel { #[primary_span] #[label(parse_unexpected_token_after_label)] pub span: Span, - #[suggestion(suggestion_remove_label, style = "verbose", code = "")] + #[suggestion(parse_suggestion_remove_label, style = "verbose", code = "")] pub remove_label: Option, #[subdiagnostic] pub enclose_in_block: Option, } #[derive(Subdiagnostic)] -#[multipart_suggestion(suggestion_enclose_in_block, applicability = "machine-applicable")] +#[multipart_suggestion(parse_suggestion_enclose_in_block, applicability = "machine-applicable")] pub(crate) struct UnexpectedTokenAfterLabelSugg { #[suggestion_part(code = "{{ ")] pub left: Span, @@ -337,24 +341,33 @@ pub(crate) struct IfExpressionMissingThenBlock { #[primary_span] pub if_span: Span, #[subdiagnostic] - pub sub: IfExpressionMissingThenBlockSub, + pub missing_then_block_sub: IfExpressionMissingThenBlockSub, + #[subdiagnostic] + pub let_else_sub: Option, } #[derive(Subdiagnostic)] pub(crate) enum IfExpressionMissingThenBlockSub { - #[help(condition_possibly_unfinished)] + #[help(parse_condition_possibly_unfinished)] UnfinishedCondition(#[primary_span] Span), - #[help(add_then_block)] + #[help(parse_add_then_block)] AddThenBlock(#[primary_span] Span), } +#[derive(Subdiagnostic)] +#[suggestion(parse_extra_if_in_let_else, applicability = "maybe-incorrect", code = "")] +pub(crate) struct IfExpressionLetSomeSub { + #[primary_span] + pub if_span: Span, +} + #[derive(Diagnostic)] #[diag(parse_if_expression_missing_condition)] pub(crate) struct IfExpressionMissingCondition { #[primary_span] - #[label(condition_label)] + #[label(parse_condition_label)] pub if_span: Span, - #[label(block_label)] + #[label(parse_block_label)] pub block_span: Span, } @@ -392,10 +405,10 @@ pub(crate) struct OuterAttributeNotAllowedOnIfElse { #[primary_span] pub last: Span, - #[label(branch_label)] + #[label(parse_branch_label)] pub branch_span: Span, - #[label(ctx_label)] + #[label(parse_ctx_label)] pub ctx_span: Span, pub ctx: String, @@ -415,12 +428,40 @@ pub(crate) struct MissingInInForLoop { #[derive(Subdiagnostic)] pub(crate) enum MissingInInForLoopSub { // Has been misleading, at least in the past (closed Issue #48492), thus maybe-incorrect - #[suggestion(use_in_not_of, style = "short", applicability = "maybe-incorrect", code = "in")] + #[suggestion( + parse_use_in_not_of, + style = "short", + applicability = "maybe-incorrect", + code = "in" + )] InNotOf(#[primary_span] Span), - #[suggestion(add_in, style = "short", applicability = "maybe-incorrect", code = " in ")] + #[suggestion(parse_add_in, style = "short", applicability = "maybe-incorrect", code = " in ")] AddIn(#[primary_span] Span), } +#[derive(Diagnostic)] +#[diag(parse_missing_expression_in_for_loop)] +pub(crate) struct MissingExpressionInForLoop { + #[primary_span] + #[suggestion( + code = "/* expression */ ", + applicability = "has-placeholders", + style = "verbose" + )] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_loop_else)] +#[note] +pub(crate) struct LoopElseNotSupported { + #[primary_span] + pub span: Span, + pub loop_kind: &'static str, + #[label(parse_loop_keyword)] + pub loop_kw: Span, +} + #[derive(Diagnostic)] #[diag(parse_missing_comma_after_match_arm)] pub(crate) struct MissingCommaAfterMatchArm { @@ -460,8 +501,8 @@ pub(crate) struct EqFieldInit { #[diag(parse_dotdotdot)] pub(crate) struct DotDotDot { #[primary_span] - #[suggestion(suggest_exclusive_range, applicability = "maybe-incorrect", code = "..")] - #[suggestion(suggest_inclusive_range, applicability = "maybe-incorrect", code = "..=")] + #[suggestion(parse_suggest_exclusive_range, applicability = "maybe-incorrect", code = "..")] + #[suggestion(parse_suggest_inclusive_range, applicability = "maybe-incorrect", code = "..=")] pub span: Span, } @@ -501,10 +542,10 @@ pub(crate) struct UseEmptyBlockNotSemi { #[diag(parse_comparison_interpreted_as_generic)] pub(crate) struct ComparisonInterpretedAsGeneric { #[primary_span] - #[label(label_comparison)] + #[label(parse_label_comparison)] pub comparison: Span, pub r#type: Path, - #[label(label_args)] + #[label(parse_label_args)] pub args: Span, #[subdiagnostic] pub suggestion: ComparisonOrShiftInterpretedAsGenericSugg, @@ -514,17 +555,17 @@ pub(crate) struct ComparisonInterpretedAsGeneric { #[diag(parse_shift_interpreted_as_generic)] pub(crate) struct ShiftInterpretedAsGeneric { #[primary_span] - #[label(label_comparison)] + #[label(parse_label_comparison)] pub shift: Span, pub r#type: Path, - #[label(label_args)] + #[label(parse_label_args)] pub args: Span, #[subdiagnostic] pub suggestion: ComparisonOrShiftInterpretedAsGenericSugg, } #[derive(Subdiagnostic)] -#[multipart_suggestion(suggestion, applicability = "machine-applicable")] +#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] pub(crate) struct ComparisonOrShiftInterpretedAsGenericSugg { #[suggestion_part(code = "(")] pub left: Span, @@ -550,7 +591,7 @@ pub(crate) struct LeadingPlusNotSupported { #[label] pub span: Span, #[suggestion( - suggestion_remove_plus, + parse_suggestion_remove_plus, style = "verbose", code = "", applicability = "machine-applicable" @@ -573,7 +614,7 @@ pub(crate) struct ParenthesesWithStructFields { } #[derive(Subdiagnostic)] -#[multipart_suggestion(suggestion_braces_for_struct, applicability = "maybe-incorrect")] +#[multipart_suggestion(parse_suggestion_braces_for_struct, applicability = "maybe-incorrect")] pub(crate) struct BracesForStructLiteral { #[suggestion_part(code = " {{ ")] pub first: Span, @@ -582,7 +623,7 @@ pub(crate) struct BracesForStructLiteral { } #[derive(Subdiagnostic)] -#[multipart_suggestion(suggestion_no_fields_for_fn, applicability = "maybe-incorrect")] +#[multipart_suggestion(parse_suggestion_no_fields_for_fn, applicability = "maybe-incorrect")] pub(crate) struct NoFieldsForFnCall { #[suggestion_part(code = "")] pub fields: Vec, @@ -619,7 +660,7 @@ pub(crate) struct ArrayBracketsInsteadOfSpaces { } #[derive(Subdiagnostic)] -#[multipart_suggestion(suggestion, applicability = "maybe-incorrect")] +#[multipart_suggestion(parse_suggestion, applicability = "maybe-incorrect")] pub(crate) struct ArrayBracketsInsteadOfSpacesSugg { #[suggestion_part(code = "[")] pub left: Span, @@ -631,18 +672,57 @@ pub(crate) struct ArrayBracketsInsteadOfSpacesSugg { #[diag(parse_match_arm_body_without_braces)] pub(crate) struct MatchArmBodyWithoutBraces { #[primary_span] - #[label(label_statements)] + #[label(parse_label_statements)] pub statements: Span, - #[label(label_arrow)] + #[label(parse_label_arrow)] pub arrow: Span, pub num_statements: usize, #[subdiagnostic] pub sub: MatchArmBodyWithoutBracesSugg, } +#[derive(Diagnostic)] +#[diag(parse_inclusive_range_extra_equals)] +#[note] +pub(crate) struct InclusiveRangeExtraEquals { + #[primary_span] + #[suggestion( + parse_suggestion_remove_eq, + style = "short", + code = "..=", + applicability = "maybe-incorrect" + )] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_inclusive_range_match_arrow)] +pub(crate) struct InclusiveRangeMatchArrow { + #[primary_span] + pub arrow: Span, + #[label] + pub span: Span, + #[suggestion(style = "verbose", code = " ", applicability = "machine-applicable")] + pub after_pat: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_inclusive_range_no_end, code = "E0586")] +#[note] +pub(crate) struct InclusiveRangeNoEnd { + #[primary_span] + #[suggestion( + parse_suggestion_open_range, + code = "..", + applicability = "machine-applicable", + style = "short" + )] + pub span: Span, +} + #[derive(Subdiagnostic)] pub(crate) enum MatchArmBodyWithoutBracesSugg { - #[multipart_suggestion(suggestion_add_braces, applicability = "machine-applicable")] + #[multipart_suggestion(parse_suggestion_add_braces, applicability = "machine-applicable")] AddBraces { #[suggestion_part(code = "{{ ")] left: Span, @@ -650,7 +730,7 @@ pub(crate) enum MatchArmBodyWithoutBracesSugg { right: Span, }, #[suggestion( - suggestion_use_comma_not_semicolon, + parse_suggestion_use_comma_not_semicolon, code = ",", applicability = "machine-applicable" )] @@ -670,7 +750,7 @@ pub(crate) struct StructLiteralNotAllowedHere { } #[derive(Subdiagnostic)] -#[multipart_suggestion(suggestion, applicability = "machine-applicable")] +#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] pub(crate) struct StructLiteralNotAllowedHereSugg { #[suggestion_part(code = "(")] pub left: Span, @@ -692,9 +772,9 @@ pub(crate) struct InvalidLiteralSuffixOnTupleIndex { #[label] pub span: Span, pub suffix: Symbol, - #[help(tuple_exception_line_1)] - #[help(tuple_exception_line_2)] - #[help(tuple_exception_line_3)] + #[help(parse_tuple_exception_line_1)] + #[help(parse_tuple_exception_line_2)] + #[help(parse_tuple_exception_line_3)] pub exception: Option<()>, } @@ -712,11 +792,11 @@ pub(crate) struct MismatchedClosingDelimiter { #[primary_span] pub spans: Vec, pub delimiter: String, - #[label(label_unmatched)] + #[label(parse_label_unmatched)] pub unmatched: Span, - #[label(label_opening_candidate)] + #[label(parse_label_opening_candidate)] pub opening_candidate: Option, - #[label(label_unclosed)] + #[label(parse_label_unclosed)] pub unclosed: Option, } @@ -867,7 +947,7 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for ExpectedIdentifier { self, handler: &'a rustc_errors::Handler, ) -> rustc_errors::DiagnosticBuilder<'a, G> { - let token_descr = super::parser::TokenDescription::from_token(&self.token); + let token_descr = TokenDescription::from_token(&self.token); let mut diag = handler.struct_diagnostic(match token_descr { Some(TokenDescription::ReservedIdentifier) => { @@ -913,7 +993,7 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for ExpectedSemi { self, handler: &'a rustc_errors::Handler, ) -> rustc_errors::DiagnosticBuilder<'a, G> { - let token_descr = super::parser::TokenDescription::from_token(&self.token); + let token_descr = TokenDescription::from_token(&self.token); let mut diag = handler.struct_diagnostic(match token_descr { Some(TokenDescription::ReservedIdentifier) => { @@ -962,7 +1042,7 @@ pub(crate) struct StructLiteralBodyWithoutPath { } #[derive(Subdiagnostic)] -#[multipart_suggestion(suggestion, applicability = "has-placeholders")] +#[multipart_suggestion(parse_suggestion, applicability = "has-placeholders")] pub(crate) struct StructLiteralBodyWithoutPathSugg { #[suggestion_part(code = "{{ SomeStruct ")] pub before: Span, @@ -980,7 +1060,7 @@ pub(crate) struct StructLiteralNeedingParens { } #[derive(Subdiagnostic)] -#[multipart_suggestion(suggestion, applicability = "machine-applicable")] +#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] pub(crate) struct StructLiteralNeedingParensSugg { #[suggestion_part(code = "(")] pub before: Span, @@ -1007,7 +1087,7 @@ pub(crate) struct GenericParamsWithoutAngleBrackets { } #[derive(Subdiagnostic)] -#[multipart_suggestion(suggestion, applicability = "machine-applicable")] +#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] pub(crate) struct GenericParamsWithoutAngleBracketsSugg { #[suggestion_part(code = "<")] pub left: Span, @@ -1028,7 +1108,7 @@ pub(crate) struct ComparisonOperatorsCannotBeChained { )] pub suggest_turbofish: Option, #[help(parse_sugg_turbofish_syntax)] - #[help(sugg_parentheses_for_function_args)] + #[help(parse_sugg_parentheses_for_function_args)] pub help_turbofish: Option<()>, #[subdiagnostic] pub chaining_sugg: Option, @@ -1037,7 +1117,7 @@ pub(crate) struct ComparisonOperatorsCannotBeChained { #[derive(Subdiagnostic)] pub(crate) enum ComparisonOperatorsCannotBeChainedSugg { #[suggestion( - sugg_split_comparison, + parse_sugg_split_comparison, style = "verbose", code = " && {middle_term}", applicability = "maybe-incorrect" @@ -1047,7 +1127,7 @@ pub(crate) enum ComparisonOperatorsCannotBeChainedSugg { span: Span, middle_term: String, }, - #[multipart_suggestion(sugg_parenthesize, applicability = "maybe-incorrect")] + #[multipart_suggestion(parse_sugg_parenthesize, applicability = "maybe-incorrect")] Parenthesize { #[suggestion_part(code = "(")] left: Span, @@ -1067,7 +1147,7 @@ pub(crate) struct QuestionMarkInType { } #[derive(Subdiagnostic)] -#[multipart_suggestion(suggestion, applicability = "machine-applicable")] +#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] pub(crate) struct QuestionMarkInTypeSugg { #[suggestion_part(code = "Option<")] pub left: Span, @@ -1085,7 +1165,7 @@ pub(crate) struct ParenthesesInForHead { } #[derive(Subdiagnostic)] -#[multipart_suggestion(suggestion, applicability = "machine-applicable")] +#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] pub(crate) struct ParenthesesInForHeadSugg { #[suggestion_part(code = "{left_snippet}")] pub left: Span, @@ -1145,7 +1225,7 @@ pub(crate) struct ConstGenericWithoutBraces { } #[derive(Subdiagnostic)] -#[multipart_suggestion(suggestion, applicability = "machine-applicable")] +#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] pub(crate) struct ConstGenericWithoutBracesSugg { #[suggestion_part(code = "{{ ")] pub left: Span, @@ -1165,7 +1245,7 @@ pub(crate) struct UnexpectedConstParamDeclaration { #[derive(Subdiagnostic)] pub(crate) enum UnexpectedConstParamDeclarationSugg { - #[multipart_suggestion(suggestion, applicability = "machine-applicable")] + #[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] AddParam { #[suggestion_part(code = "<{snippet}>")] impl_generics: Span, @@ -1174,7 +1254,7 @@ pub(crate) enum UnexpectedConstParamDeclarationSugg { snippet: String, ident: String, }, - #[multipart_suggestion(suggestion, applicability = "machine-applicable")] + #[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] AppendParam { #[suggestion_part(code = ", {snippet}")] impl_generics_end: Span, @@ -1221,7 +1301,7 @@ pub(crate) struct FnPtrWithGenerics { } #[derive(Subdiagnostic)] -#[multipart_suggestion(suggestion, applicability = "maybe-incorrect")] +#[multipart_suggestion(parse_suggestion, applicability = "maybe-incorrect")] pub(crate) struct FnPtrWithGenericsSugg { #[suggestion_part(code = "{snippet}")] pub left: Span, @@ -1262,16 +1342,16 @@ pub(crate) struct WhereClauseBeforeTupleStructBody { #[primary_span] #[label] pub span: Span, - #[label(name_label)] + #[label(parse_name_label)] pub name: Span, - #[label(body_label)] + #[label(parse_body_label)] pub body: Span, #[subdiagnostic] pub sugg: Option, } #[derive(Subdiagnostic)] -#[multipart_suggestion(suggestion, applicability = "machine-applicable")] +#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] pub(crate) struct WhereClauseBeforeTupleStructBodySugg { #[suggestion_part(code = "{snippet}")] pub left: Span, @@ -1279,3 +1359,943 @@ pub(crate) struct WhereClauseBeforeTupleStructBodySugg { #[suggestion_part(code = "")] pub right: Span, } + +#[derive(Diagnostic)] +#[diag(parse_async_fn_in_2015, code = "E0670")] +pub(crate) struct AsyncFnIn2015 { + #[primary_span] + #[label] + pub span: Span, + #[subdiagnostic] + pub help: HelpUseLatestEdition, +} + +#[derive(Subdiagnostic)] +#[label(parse_async_block_in_2015)] +pub(crate) struct AsyncBlockIn2015 { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_self_argument_pointer)] +pub(crate) struct SelfArgumentPointer { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_unexpected_token_after_dot)] +pub struct UnexpectedTokenAfterDot<'a> { + #[primary_span] + pub span: Span, + pub actual: Cow<'a, str>, +} + +#[derive(Diagnostic)] +#[diag(parse_visibility_not_followed_by_item)] +#[help] +pub(crate) struct VisibilityNotFollowedByItem { + #[primary_span] + #[label] + pub span: Span, + pub vis: Visibility, +} + +#[derive(Diagnostic)] +#[diag(parse_default_not_followed_by_item)] +#[note] +pub(crate) struct DefaultNotFollowedByItem { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +pub(crate) enum MissingKeywordForItemDefinition { + #[diag(parse_missing_struct_for_struct_definition)] + Struct { + #[primary_span] + #[suggestion(style = "short", applicability = "maybe-incorrect", code = " struct ")] + span: Span, + ident: Ident, + }, + #[diag(parse_missing_fn_for_function_definition)] + Function { + #[primary_span] + #[suggestion(style = "short", applicability = "maybe-incorrect", code = " fn ")] + span: Span, + ident: Ident, + }, + #[diag(parse_missing_fn_for_method_definition)] + Method { + #[primary_span] + #[suggestion(style = "short", applicability = "maybe-incorrect", code = " fn ")] + span: Span, + ident: Ident, + }, + #[diag(parse_ambiguous_missing_keyword_for_item_definition)] + Ambiguous { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiag: Option, + }, +} + +#[derive(Subdiagnostic)] +pub(crate) enum AmbiguousMissingKwForItemSub { + #[suggestion(parse_suggestion, applicability = "maybe-incorrect", code = "{snippet}!")] + SuggestMacro { + #[primary_span] + span: Span, + snippet: String, + }, + #[help(parse_help)] + HelpMacro, +} + +#[derive(Diagnostic)] +#[diag(parse_missing_trait_in_trait_impl)] +pub(crate) struct MissingTraitInTraitImpl { + #[primary_span] + #[suggestion(parse_suggestion_add_trait, code = " Trait ", applicability = "has-placeholders")] + pub span: Span, + #[suggestion(parse_suggestion_remove_for, code = "", applicability = "maybe-incorrect")] + pub for_span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_missing_for_in_trait_impl)] +pub(crate) struct MissingForInTraitImpl { + #[primary_span] + #[suggestion(style = "short", code = " for ", applicability = "machine-applicable")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_expected_trait_in_trait_impl_found_type)] +pub(crate) struct ExpectedTraitInTraitImplFoundType { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_bounds_not_allowed_on_trait_aliases)] +pub(crate) struct BoundsNotAllowedOnTraitAliases { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_trait_alias_cannot_be_auto)] +pub(crate) struct TraitAliasCannotBeAuto { + #[primary_span] + #[label(parse_trait_alias_cannot_be_auto)] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_trait_alias_cannot_be_unsafe)] +pub(crate) struct TraitAliasCannotBeUnsafe { + #[primary_span] + #[label(parse_trait_alias_cannot_be_unsafe)] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_associated_static_item_not_allowed)] +pub(crate) struct AssociatedStaticItemNotAllowed { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_extern_crate_name_with_dashes)] +pub(crate) struct ExternCrateNameWithDashes { + #[primary_span] + #[label] + pub span: Span, + #[subdiagnostic] + pub sugg: ExternCrateNameWithDashesSugg, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +pub(crate) struct ExternCrateNameWithDashesSugg { + #[suggestion_part(code = "_")] + pub dashes: Vec, +} + +#[derive(Diagnostic)] +#[diag(parse_extern_item_cannot_be_const)] +#[note] +pub(crate) struct ExternItemCannotBeConst { + #[primary_span] + pub ident_span: Span, + #[suggestion(code = "static ", applicability = "machine-applicable")] + pub const_span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_const_global_cannot_be_mutable)] +pub(crate) struct ConstGlobalCannotBeMutable { + #[primary_span] + #[label] + pub ident_span: Span, + #[suggestion(code = "static", applicability = "maybe-incorrect")] + pub const_span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_missing_const_type)] +pub(crate) struct MissingConstType { + #[primary_span] + #[suggestion(code = "{colon} ", applicability = "has-placeholders")] + pub span: Span, + + pub kind: &'static str, + pub colon: &'static str, +} + +#[derive(Diagnostic)] +#[diag(parse_enum_struct_mutually_exclusive)] +pub(crate) struct EnumStructMutuallyExclusive { + #[primary_span] + #[suggestion(code = "enum", applicability = "machine-applicable")] + pub span: Span, +} + +#[derive(Diagnostic)] +pub(crate) enum UnexpectedTokenAfterStructName { + #[diag(parse_unexpected_token_after_struct_name_found_reserved_identifier)] + ReservedIdentifier { + #[primary_span] + #[label(parse_unexpected_token_after_struct_name)] + span: Span, + token: Token, + }, + #[diag(parse_unexpected_token_after_struct_name_found_keyword)] + Keyword { + #[primary_span] + #[label(parse_unexpected_token_after_struct_name)] + span: Span, + token: Token, + }, + #[diag(parse_unexpected_token_after_struct_name_found_reserved_keyword)] + ReservedKeyword { + #[primary_span] + #[label(parse_unexpected_token_after_struct_name)] + span: Span, + token: Token, + }, + #[diag(parse_unexpected_token_after_struct_name_found_doc_comment)] + DocComment { + #[primary_span] + #[label(parse_unexpected_token_after_struct_name)] + span: Span, + token: Token, + }, + #[diag(parse_unexpected_token_after_struct_name_found_other)] + Other { + #[primary_span] + #[label(parse_unexpected_token_after_struct_name)] + span: Span, + token: Token, + }, +} + +impl UnexpectedTokenAfterStructName { + pub fn new(span: Span, token: Token) -> Self { + match TokenDescription::from_token(&token) { + Some(TokenDescription::ReservedIdentifier) => Self::ReservedIdentifier { span, token }, + Some(TokenDescription::Keyword) => Self::Keyword { span, token }, + Some(TokenDescription::ReservedKeyword) => Self::ReservedKeyword { span, token }, + Some(TokenDescription::DocComment) => Self::DocComment { span, token }, + None => Self::Other { span, token }, + } + } +} + +#[derive(Diagnostic)] +#[diag(parse_unexpected_self_in_generic_parameters)] +#[note] +pub(crate) struct UnexpectedSelfInGenericParameters { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_unexpected_default_value_for_lifetime_in_generic_parameters)] +pub(crate) struct UnexpectedDefaultValueForLifetimeInGenericParameters { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_multiple_where_clauses)] +pub(crate) struct MultipleWhereClauses { + #[primary_span] + pub span: Span, + #[label] + pub previous: Span, + #[suggestion(style = "verbose", code = ",", applicability = "maybe-incorrect")] + pub between: Span, +} + +#[derive(Diagnostic)] +pub(crate) enum UnexpectedNonterminal { + #[diag(parse_nonterminal_expected_item_keyword)] + Item(#[primary_span] Span), + #[diag(parse_nonterminal_expected_statement)] + Statement(#[primary_span] Span), + #[diag(parse_nonterminal_expected_ident)] + Ident { + #[primary_span] + span: Span, + token: Token, + }, + #[diag(parse_nonterminal_expected_lifetime)] + Lifetime { + #[primary_span] + span: Span, + token: Token, + }, +} + +#[derive(Diagnostic)] +pub(crate) enum TopLevelOrPatternNotAllowed { + #[diag(parse_or_pattern_not_allowed_in_let_binding)] + LetBinding { + #[primary_span] + span: Span, + #[subdiagnostic] + sub: Option, + }, + #[diag(parse_or_pattern_not_allowed_in_fn_parameters)] + FunctionParameter { + #[primary_span] + span: Span, + #[subdiagnostic] + sub: Option, + }, +} + +#[derive(Diagnostic)] +#[diag(parse_cannot_be_raw_ident)] +pub struct CannotBeRawIdent { + #[primary_span] + pub span: Span, + pub ident: Symbol, +} + +#[derive(Diagnostic)] +#[diag(parse_cr_doc_comment)] +pub struct CrDocComment { + #[primary_span] + pub span: Span, + pub block: bool, +} + +#[derive(Diagnostic)] +#[diag(parse_no_digits_literal, code = "E0768")] +pub struct NoDigitsLiteral { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_invalid_digit_literal)] +pub struct InvalidDigitLiteral { + #[primary_span] + pub span: Span, + pub base: u32, +} + +#[derive(Diagnostic)] +#[diag(parse_empty_exponent_float)] +pub struct EmptyExponentFloat { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_float_literal_unsupported_base)] +pub struct FloatLiteralUnsupportedBase { + #[primary_span] + pub span: Span, + pub base: &'static str, +} + +#[derive(Diagnostic)] +#[diag(parse_unknown_prefix)] +#[note] +pub struct UnknownPrefix<'a> { + #[primary_span] + #[label] + pub span: Span, + pub prefix: &'a str, + #[subdiagnostic] + pub sugg: Option, +} + +#[derive(Subdiagnostic)] +pub enum UnknownPrefixSugg { + #[suggestion( + parse_suggestion_br, + code = "br", + applicability = "maybe-incorrect", + style = "verbose" + )] + UseBr(#[primary_span] Span), + #[suggestion( + parse_suggestion_whitespace, + code = " ", + applicability = "maybe-incorrect", + style = "verbose" + )] + Whitespace(#[primary_span] Span), +} + +#[derive(Diagnostic)] +#[diag(parse_too_many_hashes)] +pub struct TooManyHashes { + #[primary_span] + pub span: Span, + pub num: u32, +} + +#[derive(Diagnostic)] +#[diag(parse_unknown_start_of_token)] +pub struct UnknownTokenStart { + #[primary_span] + pub span: Span, + pub escaped: String, + #[subdiagnostic] + pub sugg: Option, + #[subdiagnostic] + pub null: Option, + #[subdiagnostic] + pub repeat: Option, +} + +#[derive(Subdiagnostic)] +pub enum TokenSubstitution { + #[suggestion(parse_sugg_quotes, code = "{suggestion}", applicability = "maybe-incorrect")] + DirectedQuotes { + #[primary_span] + span: Span, + suggestion: String, + ascii_str: &'static str, + ascii_name: &'static str, + }, + #[suggestion(parse_sugg_other, code = "{suggestion}", applicability = "maybe-incorrect")] + Other { + #[primary_span] + span: Span, + suggestion: String, + ch: String, + u_name: &'static str, + ascii_str: &'static str, + ascii_name: &'static str, + }, +} + +#[derive(Subdiagnostic)] +#[note(parse_note_repeats)] +pub struct UnknownTokenRepeat { + pub repeats: usize, +} + +#[derive(Subdiagnostic)] +#[help(parse_help_null)] +pub struct UnknownTokenNull; + +#[derive(Diagnostic)] +pub enum UnescapeError { + #[diag(parse_invalid_unicode_escape)] + #[help] + InvalidUnicodeEscape { + #[primary_span] + #[label] + span: Span, + surrogate: bool, + }, + #[diag(parse_escape_only_char)] + EscapeOnlyChar { + #[primary_span] + span: Span, + #[suggestion(parse_escape, applicability = "machine-applicable", code = "{escaped_sugg}")] + char_span: Span, + escaped_sugg: String, + escaped_msg: String, + byte: bool, + }, + #[diag(parse_bare_cr)] + BareCr { + #[primary_span] + #[suggestion(parse_escape, applicability = "machine-applicable", code = "\\r")] + span: Span, + double_quotes: bool, + }, + #[diag(parse_bare_cr_in_raw_string)] + BareCrRawString(#[primary_span] Span), + #[diag(parse_too_short_hex_escape)] + TooShortHexEscape(#[primary_span] Span), + #[diag(parse_invalid_char_in_escape)] + InvalidCharInEscape { + #[primary_span] + #[label] + span: Span, + is_hex: bool, + ch: String, + }, + #[diag(parse_out_of_range_hex_escape)] + OutOfRangeHexEscape( + #[primary_span] + #[label] + Span, + ), + #[diag(parse_leading_underscore_unicode_escape)] + LeadingUnderscoreUnicodeEscape { + #[primary_span] + #[label(parse_leading_underscore_unicode_escape_label)] + span: Span, + ch: String, + }, + #[diag(parse_overlong_unicode_escape)] + OverlongUnicodeEscape( + #[primary_span] + #[label] + Span, + ), + #[diag(parse_unclosed_unicode_escape)] + UnclosedUnicodeEscape( + #[primary_span] + #[label] + Span, + #[suggestion( + parse_terminate, + code = "}}", + applicability = "maybe-incorrect", + style = "verbose" + )] + Span, + ), + #[diag(parse_no_brace_unicode_escape)] + NoBraceInUnicodeEscape { + #[primary_span] + span: Span, + #[label] + label: Option, + #[subdiagnostic] + sub: NoBraceUnicodeSub, + }, + #[diag(parse_unicode_escape_in_byte)] + #[help] + UnicodeEscapeInByte( + #[primary_span] + #[label] + Span, + ), + #[diag(parse_empty_unicode_escape)] + EmptyUnicodeEscape( + #[primary_span] + #[label] + Span, + ), + #[diag(parse_zero_chars)] + ZeroChars( + #[primary_span] + #[label] + Span, + ), + #[diag(parse_lone_slash)] + LoneSlash( + #[primary_span] + #[label] + Span, + ), + #[diag(parse_unskipped_whitespace)] + UnskippedWhitespace { + #[primary_span] + span: Span, + #[label] + char_span: Span, + ch: String, + }, + #[diag(parse_multiple_skipped_lines)] + MultipleSkippedLinesWarning( + #[primary_span] + #[label] + Span, + ), + #[diag(parse_more_than_one_char)] + MoreThanOneChar { + #[primary_span] + span: Span, + #[subdiagnostic] + note: Option, + #[subdiagnostic] + suggestion: MoreThanOneCharSugg, + }, +} + +#[derive(Subdiagnostic)] +pub enum MoreThanOneCharSugg { + #[suggestion( + parse_consider_normalized, + code = "{normalized}", + applicability = "machine-applicable" + )] + NormalizedForm { + #[primary_span] + span: Span, + ch: String, + normalized: String, + }, + #[suggestion(parse_remove_non, code = "{ch}", applicability = "maybe-incorrect")] + RemoveNonPrinting { + #[primary_span] + span: Span, + ch: String, + }, + #[suggestion(parse_use_double_quotes, code = "{sugg}", applicability = "machine-applicable")] + Quotes { + #[primary_span] + span: Span, + is_byte: bool, + sugg: String, + }, +} + +#[derive(Subdiagnostic)] +pub enum MoreThanOneCharNote { + #[note(parse_followed_by)] + AllCombining { + #[primary_span] + span: Span, + chr: String, + len: usize, + escaped_marks: String, + }, + #[note(parse_non_printing)] + NonPrinting { + #[primary_span] + span: Span, + escaped: String, + }, +} + +#[derive(Subdiagnostic)] +pub enum NoBraceUnicodeSub { + #[suggestion(parse_use_braces, code = "{suggestion}", applicability = "maybe-incorrect")] + Suggestion { + #[primary_span] + span: Span, + suggestion: String, + }, + #[help(parse_format_of_unicode)] + Help, +} + +#[derive(Subdiagnostic)] +pub(crate) enum TopLevelOrPatternNotAllowedSugg { + #[suggestion( + parse_sugg_remove_leading_vert_in_pattern, + code = "{pat}", + applicability = "machine-applicable" + )] + RemoveLeadingVert { + #[primary_span] + span: Span, + pat: String, + }, + #[suggestion( + parse_sugg_wrap_pattern_in_parens, + code = "({pat})", + applicability = "machine-applicable" + )] + WrapInParens { + #[primary_span] + span: Span, + pat: String, + }, +} + +#[derive(Diagnostic)] +#[diag(parse_unexpected_vert_vert_before_function_parameter)] +#[note(parse_note_pattern_alternatives_use_single_vert)] +pub(crate) struct UnexpectedVertVertBeforeFunctionParam { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_unexpected_vert_vert_in_pattern)] +pub(crate) struct UnexpectedVertVertInPattern { + #[primary_span] + #[suggestion(code = "|", applicability = "machine-applicable")] + pub span: Span, + #[label(parse_label_while_parsing_or_pattern_here)] + pub start: Option, +} + +#[derive(Diagnostic)] +#[diag(parse_trailing_vert_not_allowed)] +pub(crate) struct TrailingVertNotAllowed { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub span: Span, + #[label(parse_label_while_parsing_or_pattern_here)] + pub start: Option, + pub token: Token, + #[note(parse_note_pattern_alternatives_use_single_vert)] + pub note_double_vert: Option<()>, +} + +#[derive(Diagnostic)] +#[diag(parse_dotdotdot_rest_pattern)] +pub(crate) struct DotDotDotRestPattern { + #[primary_span] + #[suggestion(style = "short", code = "..", applicability = "machine-applicable")] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_pattern_on_wrong_side_of_at)] +pub(crate) struct PatternOnWrongSideOfAt { + #[primary_span] + #[suggestion(code = "{whole_pat}", applicability = "machine-applicable")] + pub whole_span: Span, + pub whole_pat: String, + #[label(parse_label_pattern)] + pub pattern: Span, + #[label(parse_label_binding)] + pub binding: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_expected_binding_left_of_at)] +#[note] +pub(crate) struct ExpectedBindingLeftOfAt { + #[primary_span] + pub whole_span: Span, + #[label(parse_label_lhs)] + pub lhs: Span, + #[label(parse_label_rhs)] + pub rhs: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_ambiguous_range_pattern)] +pub(crate) struct AmbiguousRangePattern { + #[primary_span] + #[suggestion(code = "({pat})", applicability = "maybe-incorrect")] + pub span: Span, + pub pat: String, +} + +#[derive(Diagnostic)] +#[diag(parse_unexpected_lifetime_in_pattern)] +pub(crate) struct UnexpectedLifetimeInPattern { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub span: Span, + pub symbol: Symbol, +} + +#[derive(Diagnostic)] +#[diag(parse_ref_mut_order_incorrect)] +pub(crate) struct RefMutOrderIncorrect { + #[primary_span] + #[suggestion(code = "ref mut", applicability = "machine-applicable")] + pub span: Span, +} + +#[derive(Diagnostic)] +pub(crate) enum InvalidMutInPattern { + #[diag(parse_mut_on_nested_ident_pattern)] + #[note(parse_note_mut_pattern_usage)] + NestedIdent { + #[primary_span] + #[suggestion(code = "{pat}", applicability = "machine-applicable")] + span: Span, + pat: String, + }, + #[diag(parse_mut_on_non_ident_pattern)] + #[note(parse_note_mut_pattern_usage)] + NonIdent { + #[primary_span] + #[suggestion(code = "{pat}", applicability = "machine-applicable")] + span: Span, + pat: String, + }, +} + +#[derive(Diagnostic)] +#[diag(parse_repeated_mut_in_pattern)] +pub(crate) struct RepeatedMutInPattern { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_dot_dot_dot_range_to_pattern_not_allowed)] +pub(crate) struct DotDotDotRangeToPatternNotAllowed { + #[primary_span] + #[suggestion(style = "short", code = "..=", applicability = "machine-applicable")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_enum_pattern_instead_of_identifier)] +pub(crate) struct EnumPatternInsteadOfIdentifier { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_dot_dot_dot_for_remaining_fields)] +pub(crate) struct DotDotDotForRemainingFields { + #[primary_span] + #[suggestion(code = "..", style = "verbose", applicability = "machine-applicable")] + pub span: Span, + pub token_str: Cow<'static, str>, +} + +#[derive(Diagnostic)] +#[diag(parse_expected_comma_after_pattern_field)] +pub(crate) struct ExpectedCommaAfterPatternField { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_return_types_use_thin_arrow)] +pub(crate) struct ReturnTypesUseThinArrow { + #[primary_span] + #[suggestion(style = "short", code = "->", applicability = "machine-applicable")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_need_plus_after_trait_object_lifetime)] +pub(crate) struct NeedPlusAfterTraitObjectLifetime { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_expected_mut_or_const_in_raw_pointer_type)] +pub(crate) struct ExpectedMutOrConstInRawPointerType { + #[primary_span] + pub span: Span, + #[suggestion(code("mut ", "const "), applicability = "has-placeholders")] + pub after_asterisk: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_lifetime_after_mut)] +pub(crate) struct LifetimeAfterMut { + #[primary_span] + pub span: Span, + #[suggestion(code = "&{snippet} mut", applicability = "maybe-incorrect")] + pub suggest_lifetime: Option, + pub snippet: String, +} + +#[derive(Diagnostic)] +#[diag(parse_dyn_after_mut)] +pub(crate) struct DynAfterMut { + #[primary_span] + #[suggestion(code = "&mut dyn", applicability = "machine-applicable")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_fn_pointer_cannot_be_const)] +pub(crate) struct FnPointerCannotBeConst { + #[primary_span] + pub span: Span, + #[suggestion(code = "", applicability = "maybe-incorrect")] + #[label] + pub qualifier: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_fn_pointer_cannot_be_async)] +pub(crate) struct FnPointerCannotBeAsync { + #[primary_span] + pub span: Span, + #[suggestion(code = "", applicability = "maybe-incorrect")] + #[label] + pub qualifier: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_nested_c_variadic_type, code = "E0743")] +pub(crate) struct NestedCVariadicType { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_invalid_dyn_keyword)] +#[help] +pub(crate) struct InvalidDynKeyword { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_negative_bounds_not_supported)] +pub(crate) struct NegativeBoundsNotSupported { + #[primary_span] + pub negative_bounds: Vec, + #[label] + pub last_span: Span, + #[subdiagnostic] + pub sub: Option, +} + +#[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)] + #[note(parse_note_edition_guide)] + Cargo { edition: Edition }, + #[help(parse_help_set_edition_standalone)] + #[note(parse_note_edition_guide)] + Standalone { edition: Edition }, +} + +impl HelpUseLatestEdition { + pub fn new() -> Self { + let edition = LATEST_STABLE_EDITION; + if std::env::var_os("CARGO").is_some() { + Self::Cargo { edition } + } else { + Self::Standalone { edition } + } + } +} diff --git a/compiler/rustc_parse/src/lexer/diagnostics.rs b/compiler/rustc_parse/src/lexer/diagnostics.rs new file mode 100644 index 000000000..27f4428d3 --- /dev/null +++ b/compiler/rustc_parse/src/lexer/diagnostics.rs @@ -0,0 +1,119 @@ +use super::UnmatchedDelim; +use rustc_ast::token::Delimiter; +use rustc_errors::Diagnostic; +use rustc_span::source_map::SourceMap; +use rustc_span::Span; + +#[derive(Default)] +pub struct TokenTreeDiagInfo { + /// Stack of open delimiters and their spans. Used for error message. + pub open_braces: Vec<(Delimiter, Span)>, + pub unmatched_delims: Vec, + + /// Used only for error recovery when arriving to EOF with mismatched braces. + pub last_unclosed_found_span: Option, + + /// Collect empty block spans that might have been auto-inserted by editors. + pub empty_block_spans: Vec, + + /// Collect the spans of braces (Open, Close). Used only + /// for detecting if blocks are empty and only braces. + pub matching_block_spans: Vec<(Span, Span)>, +} + +pub fn same_identation_level(sm: &SourceMap, open_sp: Span, close_sp: Span) -> bool { + match (sm.span_to_margin(open_sp), sm.span_to_margin(close_sp)) { + (Some(open_padding), Some(close_padding)) => open_padding == close_padding, + _ => false, + } +} + +// When we get a `)` or `]` for `{`, we should emit help message here +// it's more friendly compared to report `unmatched error` in later phase +pub fn report_missing_open_delim( + err: &mut Diagnostic, + unmatched_delims: &[UnmatchedDelim], +) -> bool { + let mut reported_missing_open = false; + for unmatch_brace in unmatched_delims.iter() { + if let Some(delim) = unmatch_brace.found_delim + && matches!(delim, Delimiter::Parenthesis | Delimiter::Bracket) + { + let missed_open = match delim { + Delimiter::Parenthesis => "(", + Delimiter::Bracket => "[", + _ => unreachable!(), + }; + err.span_label( + unmatch_brace.found_span.shrink_to_lo(), + format!("missing open `{}` for this delimiter", missed_open), + ); + reported_missing_open = true; + } + } + reported_missing_open +} + +pub fn report_suspicious_mismatch_block( + err: &mut Diagnostic, + diag_info: &TokenTreeDiagInfo, + sm: &SourceMap, + delim: Delimiter, +) { + if report_missing_open_delim(err, &diag_info.unmatched_delims) { + return; + } + + let mut matched_spans: Vec<(Span, bool)> = diag_info + .matching_block_spans + .iter() + .map(|&(open, close)| (open.with_hi(close.lo()), same_identation_level(sm, open, close))) + .collect(); + + // sort by `lo`, so the large block spans in the front + matched_spans.sort_by(|a, b| a.0.lo().cmp(&b.0.lo())); + + // We use larger block whose identation is well to cover those inner mismatched blocks + // O(N^2) here, but we are on error reporting path, so it is fine + for i in 0..matched_spans.len() { + let (block_span, same_ident) = matched_spans[i]; + if same_ident { + for j in i + 1..matched_spans.len() { + let (inner_block, inner_same_ident) = matched_spans[j]; + if block_span.contains(inner_block) && !inner_same_ident { + matched_spans[j] = (inner_block, true); + } + } + } + } + + // Find the inner-most span candidate for final report + let candidate_span = + matched_spans.into_iter().rev().find(|&(_, same_ident)| !same_ident).map(|(span, _)| span); + + if let Some(block_span) = candidate_span { + err.span_label(block_span.shrink_to_lo(), "this delimiter might not be properly closed..."); + err.span_label( + block_span.shrink_to_hi(), + "...as it matches this but it has different indentation", + ); + + // If there is a empty block in the mismatched span, note it + if delim == Delimiter::Brace { + for span in diag_info.empty_block_spans.iter() { + if block_span.contains(*span) { + err.span_label(*span, "block is empty, you might have not meant to close it"); + break; + } + } + } + } else { + // If there is no suspicious span, give the last properly closed block may help + if let Some(parent) = diag_info.matching_block_spans.last() + && diag_info.open_braces.last().is_none() + && diag_info.empty_block_spans.iter().all(|&sp| sp != parent.0.to(parent.1)) { + err.span_label(parent.0, "this opening brace..."); + err.span_label(parent.1, "...matches this closing brace"); + } + } +} diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 9fe8d9836..59958a309 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -1,11 +1,11 @@ +use crate::errors; use crate::lexer::unicode_chars::UNICODE_ARRAY; +use crate::make_unclosed_delims_error; use rustc_ast::ast::{self, AttrStyle}; 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, DiagnosticBuilder, ErrorGuaranteed, PResult, StashKey, -}; +use rustc_errors::{error_code, Applicability, Diagnostic, DiagnosticBuilder, StashKey}; use rustc_lexer::unescape::{self, Mode}; use rustc_lexer::Cursor; use rustc_lexer::{Base, DocStyle, RawStrError}; @@ -17,6 +17,7 @@ use rustc_session::parse::ParseSess; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{edition::Edition, BytePos, Pos, Span}; +mod diagnostics; mod tokentrees; mod unescape_error_reporting; mod unicode_chars; @@ -31,7 +32,7 @@ use unescape_error_reporting::{emit_unescape_error, escaped_char}; rustc_data_structures::static_assert_size!(rustc_lexer::Token, 12); #[derive(Clone, Debug)] -pub struct UnmatchedBrace { +pub struct UnmatchedDelim { pub expected_delim: Delimiter, pub found_delim: Option, pub found_span: Span, @@ -44,7 +45,7 @@ pub(crate) fn parse_token_trees<'a>( mut src: &'a str, mut start_pos: BytePos, override_span: Option, -) -> (PResult<'a, TokenStream>, Vec) { +) -> Result> { // Skip `#!`, if present. if let Some(shebang_len) = rustc_lexer::strip_shebang(src) { src = &src[shebang_len..]; @@ -61,7 +62,29 @@ pub(crate) fn parse_token_trees<'a>( override_span, nbsp_is_whitespace: false, }; - tokentrees::TokenTreesReader::parse_all_token_trees(string_reader) + let (token_trees, unmatched_delims) = + tokentrees::TokenTreesReader::parse_all_token_trees(string_reader); + match token_trees { + Ok(stream) if unmatched_delims.is_empty() => Ok(stream), + _ => { + // Return error if there are unmatched delimiters or unclosng 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 + + let mut buffer = Vec::with_capacity(1); + // Not using `emit_unclosed_delims` to use `db.buffer` + for unmatched in unmatched_delims { + if let Some(err) = make_unclosed_delims_error(unmatched, &sess) { + err.buffer(&mut buffer); + } + } + if let Err(err) = token_trees { + // Add unclosing delimiter error + err.buffer(&mut buffer); + } + Err(buffer) + } + } } struct StringReader<'a> { @@ -150,7 +173,7 @@ impl<'a> StringReader<'a> { let span = self.mk_sp(start, self.pos); self.sess.symbol_gallery.insert(sym, span); if !sym.can_be_raw() { - self.err_span(span, &format!("`{}` cannot be a raw identifier", sym)); + self.sess.emit_err(errors::CannotBeRawIdent { span, ident: sym }); } self.sess.raw_identifier_spans.borrow_mut().push(span); token::Ident(sym, true) @@ -261,27 +284,24 @@ impl<'a> StringReader<'a> { self.nbsp_is_whitespace = true; } let repeats = it.take_while(|c1| *c1 == c).count(); - let mut err = - self.struct_err_span_char(start, self.pos + Pos::from_usize(repeats * c.len_utf8()), "unknown start of token", c); // FIXME: the lexer could be used to turn the ASCII version of unicode // homoglyphs, instead of keeping a table in `check_for_substitution`into the // token. Ideally, this should be inside `rustc_lexer`. However, we should // first remove compound tokens like `<<` from `rustc_lexer`, and then add // fancier error recovery to it, as there will be less overall work to do this // way. - let token = unicode_chars::check_for_substitution(self, start, c, &mut err, repeats+1); - if c == '\x00' { - err.help("source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used"); - } - if repeats > 0 { - if repeats == 1 { - err.note(format!("character appears once more")); - } else { - err.note(format!("character appears {repeats} more times")); - } - swallow_next_invalid = repeats; - } - err.emit(); + let (token, sugg) = unicode_chars::check_for_substitution(self, start, c, repeats+1); + self.sess.emit_err(errors::UnknownTokenStart { + span: self.mk_sp(start, self.pos + Pos::from_usize(repeats * c.len_utf8())), + escaped: escaped_char(c), + sugg, + null: if c == '\x00' {Some(errors::UnknownTokenNull)} else {None}, + repeat: if repeats > 0 { + swallow_next_invalid = repeats; + Some(errors::UnknownTokenRepeat { repeats }) + } else {None} + }); + if let Some(token) = token { token } else { @@ -296,26 +316,6 @@ impl<'a> StringReader<'a> { } } - /// Report a fatal lexical error with a given span. - fn fatal_span(&self, sp: Span, m: &str) -> ! { - self.sess.span_diagnostic.span_fatal(sp, m) - } - - /// Report a lexical error with a given span. - fn err_span(&self, sp: Span, m: &str) { - self.sess.span_diagnostic.struct_span_err(sp, m).emit(); - } - - /// Report a fatal error spanning [`from_pos`, `to_pos`). - fn fatal_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) -> ! { - self.fatal_span(self.mk_sp(from_pos, to_pos), m) - } - - /// Report a lexical error spanning [`from_pos`, `to_pos`). - fn err_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) { - self.err_span(self.mk_sp(from_pos, to_pos), m) - } - fn struct_fatal_span_char( &self, from_pos: BytePos, @@ -328,18 +328,6 @@ impl<'a> StringReader<'a> { .struct_span_fatal(self.mk_sp(from_pos, to_pos), &format!("{}: {}", m, escaped_char(c))) } - fn struct_err_span_char( - &self, - from_pos: BytePos, - to_pos: BytePos, - m: &str, - c: char, - ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { - self.sess - .span_diagnostic - .struct_span_err(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 /// complain about it. fn lint_unicode_text_flow(&self, start: BytePos) { @@ -367,14 +355,12 @@ impl<'a> StringReader<'a> { ) -> TokenKind { if content.contains('\r') { for (idx, _) in content.char_indices().filter(|&(_, c)| c == '\r') { - self.err_span_( + let span = self.mk_sp( content_start + BytePos(idx as u32), content_start + BytePos(idx as u32 + 1), - match comment_kind { - CommentKind::Line => "bare CR not allowed in doc-comment", - CommentKind::Block => "bare CR not allowed in block doc-comment", - }, ); + let block = matches!(comment_kind, CommentKind::Block); + self.sess.emit_err(errors::CrDocComment { span, block }); } } @@ -453,26 +439,20 @@ impl<'a> StringReader<'a> { } rustc_lexer::LiteralKind::Int { base, empty_int } => { if empty_int { - self.sess - .span_diagnostic - .struct_span_err_with_code( - self.mk_sp(start, end), - "no valid digits found for number", - error_code!(E0768), - ) - .emit(); + let span = self.mk_sp(start, end); + self.sess.emit_err(errors::NoDigitsLiteral { span }); (token::Integer, sym::integer(0)) } else { if matches!(base, Base::Binary | Base::Octal) { let base = base as u32; let s = self.str_from_to(start + BytePos(2), end); for (idx, c) in s.char_indices() { + let span = self.mk_sp( + start + BytePos::from_usize(2 + idx), + start + BytePos::from_usize(2 + idx + c.len_utf8()), + ); if c != '_' && c.to_digit(base).is_none() { - self.err_span_( - start + BytePos::from_usize(2 + idx), - start + BytePos::from_usize(2 + idx + c.len_utf8()), - &format!("invalid digit for a base {} literal", base), - ); + self.sess.emit_err(errors::InvalidDigitLiteral { span, base }); } } } @@ -481,19 +461,18 @@ impl<'a> StringReader<'a> { } rustc_lexer::LiteralKind::Float { base, empty_exponent } => { if empty_exponent { - self.err_span_(start, self.pos, "expected at least one digit in exponent"); + let span = self.mk_sp(start, self.pos); + self.sess.emit_err(errors::EmptyExponentFloat { span }); } - match base { - Base::Hexadecimal => { - self.err_span_(start, end, "hexadecimal float literal is not supported") - } - Base::Octal => { - self.err_span_(start, end, "octal float literal is not supported") - } - Base::Binary => { - self.err_span_(start, end, "binary float literal is not supported") - } - _ => {} + let base = match base { + Base::Hexadecimal => Some("hexadecimal"), + Base::Octal => Some("octal"), + Base::Binary => Some("binary"), + _ => None, + }; + if let Some(base) = base { + let span = self.mk_sp(start, end); + self.sess.emit_err(errors::FloatLiteralUnsupportedBase { span, base }); } (token::Float, self.symbol_from_to(start, end)) } @@ -643,54 +622,34 @@ impl<'a> StringReader<'a> { // identifier tokens. fn report_unknown_prefix(&self, start: BytePos) { let prefix_span = self.mk_sp(start, self.pos); - let prefix_str = self.str_from_to(start, self.pos); - let msg = format!("prefix `{}` is unknown", prefix_str); + let prefix = self.str_from_to(start, self.pos); let expn_data = prefix_span.ctxt().outer_expn_data(); if expn_data.edition >= Edition::Edition2021 { // In Rust 2021, this is a hard error. - let mut err = self.sess.span_diagnostic.struct_span_err(prefix_span, &msg); - err.span_label(prefix_span, "unknown prefix"); - if prefix_str == "rb" { - err.span_suggestion_verbose( - prefix_span, - "use `br` for a raw byte string", - "br", - Applicability::MaybeIncorrect, - ); + let sugg = if prefix == "rb" { + Some(errors::UnknownPrefixSugg::UseBr(prefix_span)) } else if expn_data.is_root() { - err.span_suggestion_verbose( - prefix_span.shrink_to_hi(), - "consider inserting whitespace here", - " ", - Applicability::MaybeIncorrect, - ); - } - err.note("prefixed identifiers and literals are reserved since Rust 2021"); - err.emit(); + Some(errors::UnknownPrefixSugg::Whitespace(prefix_span.shrink_to_hi())) + } else { + None + }; + self.sess.emit_err(errors::UnknownPrefix { span: prefix_span, prefix, sugg }); } else { // Before Rust 2021, only emit a lint for migration. self.sess.buffer_lint_with_diagnostic( &RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, prefix_span, ast::CRATE_NODE_ID, - &msg, + &format!("prefix `{prefix}` is unknown"), BuiltinLintDiagnostics::ReservedPrefix(prefix_span), ); } } - fn report_too_many_hashes(&self, start: BytePos, found: u32) -> ! { - self.fatal_span_( - start, - self.pos, - &format!( - "too many `#` symbols: raw strings may be delimited \ - by up to 255 `#` symbols, but found {}", - found - ), - ) + fn report_too_many_hashes(&self, start: BytePos, num: u32) -> ! { + self.sess.emit_fatal(errors::TooManyHashes { span: self.mk_sp(start, self.pos), num }); } fn cook_quoted( diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index b2701817d..36fd1e37d 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -1,47 +1,31 @@ -use super::{StringReader, UnmatchedBrace}; +use super::diagnostics::report_suspicious_mismatch_block; +use super::diagnostics::same_identation_level; +use super::diagnostics::TokenTreeDiagInfo; +use super::{StringReader, UnmatchedDelim}; use rustc_ast::token::{self, Delimiter, Token}; use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_ast_pretty::pprust::token_to_string; -use rustc_data_structures::fx::FxHashMap; use rustc_errors::{PErr, PResult}; -use rustc_span::Span; pub(super) struct TokenTreesReader<'a> { string_reader: StringReader<'a>, /// The "next" token, which has been obtained from the `StringReader` but /// not yet handled by the `TokenTreesReader`. token: Token, - /// Stack of open delimiters and their spans. Used for error message. - open_braces: Vec<(Delimiter, Span)>, - unmatched_braces: Vec, - /// The type and spans for all braces - /// - /// Used only for error recovery when arriving to EOF with mismatched braces. - matching_delim_spans: Vec<(Delimiter, Span, Span)>, - last_unclosed_found_span: Option, - /// Collect empty block spans that might have been auto-inserted by editors. - last_delim_empty_block_spans: FxHashMap, - /// Collect the spans of braces (Open, Close). Used only - /// for detecting if blocks are empty and only braces. - matching_block_spans: Vec<(Span, Span)>, + diag_info: TokenTreeDiagInfo, } impl<'a> TokenTreesReader<'a> { pub(super) fn parse_all_token_trees( string_reader: StringReader<'a>, - ) -> (PResult<'a, TokenStream>, Vec) { + ) -> (PResult<'a, TokenStream>, Vec) { let mut tt_reader = TokenTreesReader { string_reader, token: Token::dummy(), - open_braces: Vec::new(), - unmatched_braces: Vec::new(), - matching_delim_spans: Vec::new(), - last_unclosed_found_span: None, - last_delim_empty_block_spans: FxHashMap::default(), - matching_block_spans: Vec::new(), + diag_info: TokenTreeDiagInfo::default(), }; let res = tt_reader.parse_token_trees(/* is_delimited */ false); - (res, tt_reader.unmatched_braces) + (res, tt_reader.diag_info.unmatched_delims) } // Parse a stream of tokens into a list of `TokenTree`s. @@ -50,7 +34,7 @@ impl<'a> TokenTreesReader<'a> { let mut buf = Vec::new(); loop { match self.token.kind { - token::OpenDelim(delim) => buf.push(self.parse_token_tree_open_delim(delim)), + token::OpenDelim(delim) => buf.push(self.parse_token_tree_open_delim(delim)?), token::CloseDelim(delim) => { return if is_delimited { Ok(TokenStream::new(buf)) @@ -59,10 +43,11 @@ impl<'a> TokenTreesReader<'a> { }; } token::Eof => { - if is_delimited { - self.eof_err().emit(); - } - return Ok(TokenStream::new(buf)); + return if is_delimited { + Err(self.eof_err()) + } else { + Ok(TokenStream::new(buf)) + }; } _ => { // Get the next normal token. This might require getting multiple adjacent @@ -92,9 +77,9 @@ impl<'a> TokenTreesReader<'a> { fn eof_err(&mut self) -> PErr<'a> { let msg = "this file contains an unclosed delimiter"; let mut err = self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, msg); - for &(_, sp) in &self.open_braces { + for &(_, sp) in &self.diag_info.open_braces { err.span_label(sp, "unclosed delimiter"); - self.unmatched_braces.push(UnmatchedBrace { + self.diag_info.unmatched_delims.push(UnmatchedDelim { expected_delim: Delimiter::Brace, found_delim: None, found_span: self.token.span, @@ -103,69 +88,53 @@ impl<'a> TokenTreesReader<'a> { }); } - if let Some((delim, _)) = self.open_braces.last() { - if let Some((_, open_sp, close_sp)) = - self.matching_delim_spans.iter().find(|(d, open_sp, close_sp)| { - let sm = self.string_reader.sess.source_map(); - if let Some(close_padding) = sm.span_to_margin(*close_sp) { - if let Some(open_padding) = sm.span_to_margin(*open_sp) { - return delim == d && close_padding != open_padding; - } - } - false - }) - // these are in reverse order as they get inserted on close, but - { - // we want the last open/first close - err.span_label(*open_sp, "this delimiter might not be properly closed..."); - err.span_label(*close_sp, "...as it matches this but it has different indentation"); - } + if let Some((delim, _)) = self.diag_info.open_braces.last() { + report_suspicious_mismatch_block( + &mut err, + &self.diag_info, + &self.string_reader.sess.source_map(), + *delim, + ) } err } - fn parse_token_tree_open_delim(&mut self, open_delim: Delimiter) -> TokenTree { + fn parse_token_tree_open_delim(&mut self, open_delim: Delimiter) -> PResult<'a, TokenTree> { // The span for beginning of the delimited section let pre_span = self.token.span; - self.open_braces.push((open_delim, self.token.span)); + self.diag_info.open_braces.push((open_delim, self.token.span)); // Parse the token trees within the delimiters. // We stop at any delimiter so we can try to recover if the user // uses an incorrect delimiter. - let tts = self.parse_token_trees(/* is_delimited */ true).unwrap(); + let tts = self.parse_token_trees(/* is_delimited */ true)?; // Expand to cover the entire delimited token tree let delim_span = DelimSpan::from_pair(pre_span, self.token.span); + let sm = self.string_reader.sess.source_map(); match self.token.kind { // Correct delimiter. token::CloseDelim(close_delim) if close_delim == open_delim => { - let (open_brace, open_brace_span) = self.open_braces.pop().unwrap(); + let (open_brace, open_brace_span) = self.diag_info.open_braces.pop().unwrap(); let close_brace_span = self.token.span; - if tts.is_empty() { + if tts.is_empty() && close_delim == Delimiter::Brace { let empty_block_span = open_brace_span.to(close_brace_span); - let sm = self.string_reader.sess.source_map(); if !sm.is_multiline(empty_block_span) { // Only track if the block is in the form of `{}`, otherwise it is // likely that it was written on purpose. - self.last_delim_empty_block_spans.insert(open_delim, empty_block_span); + self.diag_info.empty_block_spans.push(empty_block_span); } } - //only add braces + // only add braces if let (Delimiter::Brace, Delimiter::Brace) = (open_brace, open_delim) { - self.matching_block_spans.push((open_brace_span, close_brace_span)); + // Add all the matching spans, we will sort by span later + self.diag_info.matching_block_spans.push((open_brace_span, close_brace_span)); } - if self.open_braces.is_empty() { - // Clear up these spans to avoid suggesting them as we've found - // properly matched delimiters so far for an entire block. - self.matching_delim_spans.clear(); - } else { - self.matching_delim_spans.push((open_brace, open_brace_span, close_brace_span)); - } // Move past the closing delimiter. self.token = self.string_reader.next_token().0; } @@ -174,28 +143,25 @@ impl<'a> TokenTreesReader<'a> { let mut unclosed_delimiter = None; let mut candidate = None; - if self.last_unclosed_found_span != Some(self.token.span) { + if self.diag_info.last_unclosed_found_span != Some(self.token.span) { // do not complain about the same unclosed delimiter multiple times - self.last_unclosed_found_span = Some(self.token.span); + self.diag_info.last_unclosed_found_span = Some(self.token.span); // This is a conservative error: only report the last unclosed // delimiter. The previous unclosed delimiters could actually be // closed! The parser just hasn't gotten to them yet. - if let Some(&(_, sp)) = self.open_braces.last() { + if let Some(&(_, sp)) = self.diag_info.open_braces.last() { unclosed_delimiter = Some(sp); }; - let sm = self.string_reader.sess.source_map(); - if let Some(current_padding) = sm.span_to_margin(self.token.span) { - for (brace, brace_span) in &self.open_braces { - if let Some(padding) = sm.span_to_margin(*brace_span) { - // high likelihood of these two corresponding - if current_padding == padding && brace == &close_delim { - candidate = Some(*brace_span); - } - } + for (brace, brace_span) in &self.diag_info.open_braces { + if same_identation_level(&sm, self.token.span, *brace_span) + && brace == &close_delim + { + // high likelihood of these two corresponding + candidate = Some(*brace_span); } } - let (tok, _) = self.open_braces.pop().unwrap(); - self.unmatched_braces.push(UnmatchedBrace { + let (tok, _) = self.diag_info.open_braces.pop().unwrap(); + self.diag_info.unmatched_delims.push(UnmatchedDelim { expected_delim: tok, found_delim: Some(close_delim), found_span: self.token.span, @@ -203,7 +169,7 @@ impl<'a> TokenTreesReader<'a> { candidate_span: candidate, }); } else { - self.open_braces.pop(); + self.diag_info.open_braces.pop(); } // If the incorrect delimiter matches an earlier opening @@ -213,7 +179,7 @@ impl<'a> TokenTreesReader<'a> { // fn foo() { // bar(baz( // } // Incorrect delimiter but matches the earlier `{` - if !self.open_braces.iter().any(|&(b, _)| b == close_delim) { + if !self.diag_info.open_braces.iter().any(|&(b, _)| b == close_delim) { self.token = self.string_reader.next_token().0; } } @@ -225,7 +191,7 @@ impl<'a> TokenTreesReader<'a> { _ => unreachable!(), } - TokenTree::Delimited(delim_span, open_delim, tts) + Ok(TokenTree::Delimited(delim_span, open_delim, tts)) } fn close_delim_err(&mut self, delim: Delimiter) -> PErr<'a> { @@ -236,22 +202,12 @@ impl<'a> TokenTreesReader<'a> { let mut err = self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, &msg); - // Braces are added at the end, so the last element is the biggest block - if let Some(parent) = self.matching_block_spans.last() { - if let Some(span) = self.last_delim_empty_block_spans.remove(&delim) { - // Check if the (empty block) is in the last properly closed block - if (parent.0.to(parent.1)).contains(span) { - err.span_label(span, "block is empty, you might have not meant to close it"); - } else { - err.span_label(parent.0, "this opening brace..."); - err.span_label(parent.1, "...matches this closing brace"); - } - } else { - err.span_label(parent.0, "this opening brace..."); - err.span_label(parent.1, "...matches this closing brace"); - } - } - + report_suspicious_mismatch_block( + &mut err, + &self.diag_info, + &self.string_reader.sess.source_map(), + delim, + ); err.span_label(self.token.span, "unexpected closing delimiter"); err } diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index 6373f5b4f..0d12ec608 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -3,10 +3,12 @@ use std::iter::once; use std::ops::Range; -use rustc_errors::{pluralize, Applicability, Handler}; +use rustc_errors::{Applicability, Handler}; use rustc_lexer::unescape::{EscapeError, Mode}; use rustc_span::{BytePos, Span}; +use crate::errors::{MoreThanOneCharNote, MoreThanOneCharSugg, NoBraceUnicodeSub, UnescapeError}; + pub(crate) fn emit_unescape_error( handler: &Handler, // interior part of the literal, without quotes @@ -31,53 +33,32 @@ pub(crate) fn emit_unescape_error( }; match error { EscapeError::LoneSurrogateUnicodeEscape => { - handler - .struct_span_err(span, "invalid unicode character escape") - .span_label(span, "invalid escape") - .help("unicode escape must not be a surrogate") - .emit(); + handler.emit_err(UnescapeError::InvalidUnicodeEscape { span, surrogate: true }); } EscapeError::OutOfRangeUnicodeEscape => { - handler - .struct_span_err(span, "invalid unicode character escape") - .span_label(span, "invalid escape") - .help("unicode escape must be at most 10FFFF") - .emit(); + handler.emit_err(UnescapeError::InvalidUnicodeEscape { span, surrogate: false }); } EscapeError::MoreThanOneChar => { use unicode_normalization::{char::is_combining_mark, UnicodeNormalization}; + let mut sugg = None; + let mut note = None; - let mut has_help = false; - let mut handler = handler.struct_span_err( - span_with_quotes, - "character literal may only contain one codepoint", - ); - - if lit.chars().skip(1).all(|c| is_combining_mark(c)) { - let escaped_marks = - lit.chars().skip(1).map(|c| c.escape_default().to_string()).collect::>(); - handler.span_note( - span, - &format!( - "this `{}` is followed by the combining mark{} `{}`", - lit.chars().next().unwrap(), - pluralize!(escaped_marks.len()), - escaped_marks.join(""), - ), - ); + let lit_chars = lit.chars().collect::>(); + let (first, rest) = lit_chars.split_first().unwrap(); + if rest.iter().copied().all(is_combining_mark) { let normalized = lit.nfc().to_string(); if normalized.chars().count() == 1 { - has_help = true; - handler.span_suggestion( - span, - &format!( - "consider using the normalized form `{}` of this character", - normalized.chars().next().unwrap().escape_default() - ), - normalized, - Applicability::MachineApplicable, - ); + let ch = normalized.chars().next().unwrap().escape_default().to_string(); + sugg = Some(MoreThanOneCharSugg::NormalizedForm { span, ch, normalized }); } + let escaped_marks = + rest.iter().map(|c| c.escape_default().to_string()).collect::>(); + note = Some(MoreThanOneCharNote::AllCombining { + span, + chr: format!("{first}"), + len: escaped_marks.len(), + escaped_marks: escaped_marks.join(""), + }); } else { let printable: Vec = lit .chars() @@ -87,32 +68,18 @@ pub(crate) fn emit_unescape_error( }) .collect(); - if let [ch] = printable.as_slice() { - has_help = true; - - handler.span_note( + if let &[ch] = printable.as_slice() { + sugg = + Some(MoreThanOneCharSugg::RemoveNonPrinting { span, ch: ch.to_string() }); + note = Some(MoreThanOneCharNote::NonPrinting { span, - &format!( - "there are non-printing characters, the full sequence is `{}`", - lit.escape_default(), - ), - ); - - handler.span_suggestion( - span, - "consider removing the non-printing characters", - ch, - Applicability::MaybeIncorrect, - ); + escaped: lit.escape_default().to_string(), + }); } - } - - if !has_help { - let (prefix, msg) = if mode.is_byte() { - ("b", "if you meant to write a byte string literal, use double quotes") - } else { - ("", "if you meant to write a `str` literal, use double quotes") - }; + }; + let sugg = sugg.unwrap_or_else(|| { + let is_byte = mode.is_byte(); + let prefix = if is_byte { "b" } else { "" }; let mut escaped = String::with_capacity(lit.len()); let mut chrs = lit.chars().peekable(); while let Some(first) = chrs.next() { @@ -129,54 +96,32 @@ pub(crate) fn emit_unescape_error( (c, _) => escaped.push(c), }; } - handler.span_suggestion( - span_with_quotes, - msg, - format!("{prefix}\"{escaped}\""), - Applicability::MachineApplicable, - ); - } - - handler.emit(); + let sugg = format!("{prefix}\"{escaped}\""); + MoreThanOneCharSugg::Quotes { span: span_with_quotes, is_byte, sugg } + }); + handler.emit_err(UnescapeError::MoreThanOneChar { + span: span_with_quotes, + note, + suggestion: sugg, + }); } EscapeError::EscapeOnlyChar => { let (c, char_span) = last_char(); - - let msg = if mode.is_byte() { - "byte constant must be escaped" - } else { - "character constant must be escaped" - }; - handler - .struct_span_err(span, &format!("{}: `{}`", msg, escaped_char(c))) - .span_suggestion( - char_span, - "escape the character", - c.escape_default(), - Applicability::MachineApplicable, - ) - .emit(); + handler.emit_err(UnescapeError::EscapeOnlyChar { + span, + char_span, + escaped_sugg: c.escape_default().to_string(), + escaped_msg: escaped_char(c), + byte: mode.is_byte(), + }); } EscapeError::BareCarriageReturn => { - let msg = if mode.in_double_quotes() { - "bare CR not allowed in string, use `\\r` instead" - } else { - "character constant must be escaped: `\\r`" - }; - handler - .struct_span_err(span, msg) - .span_suggestion( - span, - "escape the character", - "\\r", - Applicability::MachineApplicable, - ) - .emit(); + let double_quotes = mode.in_double_quotes(); + handler.emit_err(UnescapeError::BareCr { span, double_quotes }); } EscapeError::BareCarriageReturnInRawString => { assert!(mode.in_double_quotes()); - let msg = "bare CR not allowed in raw string"; - handler.span_err(span, msg); + handler.emit_err(UnescapeError::BareCrRawString(span)); } EscapeError::InvalidEscape => { let (c, span) = last_char(); @@ -213,22 +158,13 @@ pub(crate) fn emit_unescape_error( diag.emit(); } EscapeError::TooShortHexEscape => { - handler.span_err(span, "numeric character escape is too short"); + handler.emit_err(UnescapeError::TooShortHexEscape(span)); } EscapeError::InvalidCharInHexEscape | EscapeError::InvalidCharInUnicodeEscape => { let (c, span) = last_char(); - - let msg = if error == EscapeError::InvalidCharInHexEscape { - "invalid character in numeric character escape" - } else { - "invalid character in unicode escape" - }; - let c = escaped_char(c); - - handler - .struct_span_err(span, &format!("{}: `{}`", msg, c)) - .span_label(span, msg) - .emit(); + let is_hex = error == EscapeError::InvalidCharInHexEscape; + let ch = escaped_char(c); + handler.emit_err(UnescapeError::InvalidCharInEscape { span, is_hex, ch }); } EscapeError::NonAsciiCharInByte => { let (c, span) = last_char(); @@ -278,41 +214,22 @@ pub(crate) fn emit_unescape_error( err.emit(); } EscapeError::OutOfRangeHexEscape => { - handler - .struct_span_err(span, "out of range hex escape") - .span_label(span, "must be a character in the range [\\x00-\\x7f]") - .emit(); + handler.emit_err(UnescapeError::OutOfRangeHexEscape(span)); } EscapeError::LeadingUnderscoreUnicodeEscape => { let (c, span) = last_char(); - let msg = "invalid start of unicode escape"; - handler - .struct_span_err(span, &format!("{}: `{}`", msg, c)) - .span_label(span, msg) - .emit(); + handler.emit_err(UnescapeError::LeadingUnderscoreUnicodeEscape { + span, + ch: escaped_char(c), + }); } EscapeError::OverlongUnicodeEscape => { - handler - .struct_span_err(span, "overlong unicode escape") - .span_label(span, "must have at most 6 hex digits") - .emit(); + handler.emit_err(UnescapeError::OverlongUnicodeEscape(span)); } EscapeError::UnclosedUnicodeEscape => { - handler - .struct_span_err(span, "unterminated unicode escape") - .span_label(span, "missing a closing `}`") - .span_suggestion_verbose( - span.shrink_to_hi(), - "terminate the unicode escape", - "}", - Applicability::MaybeIncorrect, - ) - .emit(); + handler.emit_err(UnescapeError::UnclosedUnicodeEscape(span, span.shrink_to_hi())); } EscapeError::NoBraceInUnicodeEscape => { - let msg = "incorrect unicode escape sequence"; - let mut diag = handler.struct_span_err(span, msg); - let mut suggestion = "\\u{".to_owned(); let mut suggestion_len = 0; let (c, char_span) = last_char(); @@ -322,54 +239,37 @@ pub(crate) fn emit_unescape_error( suggestion_len += c.len_utf8(); } - if suggestion_len > 0 { + let (label, sub) = if suggestion_len > 0 { suggestion.push('}'); let hi = char_span.lo() + BytePos(suggestion_len as u32); - diag.span_suggestion( - span.with_hi(hi), - "format of unicode escape sequences uses braces", - suggestion, - Applicability::MaybeIncorrect, - ); + (None, NoBraceUnicodeSub::Suggestion { span: span.with_hi(hi), suggestion }) } else { - diag.span_label(span, msg); - diag.help("format of unicode escape sequences is `\\u{...}`"); - } - - diag.emit(); + (Some(span), NoBraceUnicodeSub::Help) + }; + handler.emit_err(UnescapeError::NoBraceInUnicodeEscape { span, label, sub }); } EscapeError::UnicodeEscapeInByte => { - let msg = "unicode escape in byte string"; - handler - .struct_span_err(span, msg) - .span_label(span, msg) - .help("unicode escape sequences cannot be used as a byte or in a byte string") - .emit(); + handler.emit_err(UnescapeError::UnicodeEscapeInByte(span)); } EscapeError::EmptyUnicodeEscape => { - handler - .struct_span_err(span, "empty unicode escape") - .span_label(span, "this escape must have at least 1 hex digit") - .emit(); + handler.emit_err(UnescapeError::EmptyUnicodeEscape(span)); } EscapeError::ZeroChars => { - let msg = "empty character literal"; - handler.struct_span_err(span, msg).span_label(span, msg).emit(); + handler.emit_err(UnescapeError::ZeroChars(span)); } EscapeError::LoneSlash => { - let msg = "invalid trailing slash in literal"; - handler.struct_span_err(span, msg).span_label(span, msg).emit(); + handler.emit_err(UnescapeError::LoneSlash(span)); } EscapeError::UnskippedWhitespaceWarning => { let (c, char_span) = last_char(); - let msg = - format!("non-ASCII whitespace symbol '{}' is not skipped", c.escape_unicode()); - handler.struct_span_warn(span, &msg).span_label(char_span, &msg).emit(); + handler.emit_warning(UnescapeError::UnskippedWhitespace { + span, + ch: escaped_char(c), + char_span, + }); } EscapeError::MultipleSkippedLinesWarning => { - let msg = "multiple lines skipped by escaped newline"; - let bottom_msg = "skipping everything up to and including this point"; - handler.struct_span_warn(span, msg).span_label(span, bottom_msg).emit(); + handler.emit_warning(UnescapeError::MultipleSkippedLinesWarning(span)); } } } diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs index 34d003ccf..d4f971d5b 100644 --- a/compiler/rustc_parse/src/lexer/unicode_chars.rs +++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs @@ -2,8 +2,10 @@ //! use super::StringReader; -use crate::token::{self, Delimiter}; -use rustc_errors::{Applicability, Diagnostic}; +use crate::{ + errors::TokenSubstitution, + token::{self, Delimiter}, +}; use rustc_span::{symbol::kw, BytePos, Pos, Span}; #[rustfmt::skip] // for line breaks @@ -338,48 +340,44 @@ pub(super) fn check_for_substitution<'a>( reader: &StringReader<'a>, pos: BytePos, ch: char, - err: &mut Diagnostic, count: usize, -) -> Option { - let &(_, u_name, ascii_str) = UNICODE_ARRAY.iter().find(|&&(c, _, _)| c == ch)?; +) -> (Option, Option) { + let Some(&(_, u_name, ascii_str)) = UNICODE_ARRAY.iter().find(|&&(c, _, _)| c == ch) else { + return (None, None); + }; let span = Span::with_root_ctxt(pos, pos + Pos::from_usize(ch.len_utf8() * count)); let Some((_, ascii_name, token)) = ASCII_ARRAY.iter().find(|&&(s, _, _)| s == ascii_str) else { let msg = format!("substitution character not found for '{}'", ch); reader.sess.span_diagnostic.span_bug_no_panic(span, &msg); - return None; + return (None, None); }; // special help suggestion for "directed" double quotes - if let Some(s) = peek_delimited(&reader.src[reader.src_index(pos)..], '“', '”') { - let msg = format!( - "Unicode characters '“' (Left Double Quotation Mark) and \ - '”' (Right Double Quotation Mark) look like '{}' ({}), but are not", - ascii_str, ascii_name - ); - err.span_suggestion( - Span::with_root_ctxt( - pos, - pos + Pos::from_usize('“'.len_utf8() + s.len() + '”'.len_utf8()), - ), - &msg, - format!("\"{}\"", s), - Applicability::MaybeIncorrect, + let sugg = if let Some(s) = peek_delimited(&reader.src[reader.src_index(pos)..], '“', '”') { + let span = Span::with_root_ctxt( + pos, + pos + Pos::from_usize('“'.len_utf8() + s.len() + '”'.len_utf8()), ); + Some(TokenSubstitution::DirectedQuotes { + span, + suggestion: format!("\"{s}\""), + ascii_str, + ascii_name, + }) } else { - let msg = format!( - "Unicode character '{}' ({}) looks like '{}' ({}), but it is not", - ch, u_name, ascii_str, ascii_name - ); - err.span_suggestion( + let suggestion = ascii_str.to_string().repeat(count); + Some(TokenSubstitution::Other { span, - &msg, - ascii_str.to_string().repeat(count), - Applicability::MaybeIncorrect, - ); - } - token.clone() + suggestion, + ch: ch.to_string(), + u_name, + ascii_str, + ascii_name, + }) + }; + (token.clone(), sugg) } /// Extract string if found at current position with given delimiters diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index b49a01d75..d1c3fd0cd 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -19,6 +19,8 @@ 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::{DiagnosticMessage, SubdiagnosticMessage}; +use rustc_macros::fluent_messages; use rustc_session::parse::ParseSess; use rustc_span::{FileName, SourceFile, Span}; @@ -28,12 +30,14 @@ pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments"); #[macro_use] pub mod parser; -use parser::{emit_unclosed_delims, make_unclosed_delims_error, Parser}; +use parser::{make_unclosed_delims_error, Parser}; pub mod lexer; pub mod validate_attr; mod errors; +fluent_messages! { "../locales/en-US.ftl" } + // A bunch of utility functions of the form `parse__from_` // where includes crate, expr, item, stmt, tts, and one that // uses a HOF to parse anything, and includes file and @@ -92,10 +96,7 @@ pub fn parse_stream_from_source_str( sess: &ParseSess, override_span: Option, ) -> TokenStream { - let (stream, mut errors) = - source_file_to_stream(sess, sess.source_map().new_source_file(name, source), override_span); - emit_unclosed_delims(&mut errors, &sess); - stream + source_file_to_stream(sess, sess.source_map().new_source_file(name, source), override_span) } /// Creates a new parser from a source string. @@ -131,9 +132,8 @@ fn maybe_source_file_to_parser( source_file: Lrc, ) -> Result, Vec> { let end_pos = source_file.end_pos; - let (stream, unclosed_delims) = maybe_file_to_stream(sess, source_file, None)?; + let stream = maybe_file_to_stream(sess, source_file, None)?; let mut parser = stream_to_parser(sess, stream, None); - parser.unclosed_delims = unclosed_delims; if parser.token == token::Eof { parser.token.span = Span::new(end_pos, end_pos, parser.token.span.ctxt(), None); } @@ -178,7 +178,7 @@ pub fn source_file_to_stream( sess: &ParseSess, source_file: Lrc, override_span: Option, -) -> (TokenStream, Vec) { +) -> TokenStream { panictry_buffer!(&sess.span_diagnostic, maybe_file_to_stream(sess, source_file, override_span)) } @@ -188,7 +188,7 @@ pub fn maybe_file_to_stream( sess: &ParseSess, source_file: Lrc, override_span: Option, -) -> Result<(TokenStream, Vec), Vec> { +) -> Result> { let src = source_file.src.as_ref().unwrap_or_else(|| { sess.span_diagnostic.bug(&format!( "cannot lex `source_file` without source: {}", @@ -196,23 +196,7 @@ pub fn maybe_file_to_stream( )); }); - let (token_trees, unmatched_braces) = - lexer::parse_token_trees(sess, src.as_str(), source_file.start_pos, override_span); - - match token_trees { - Ok(stream) => Ok((stream, unmatched_braces)), - Err(err) => { - let mut buffer = Vec::with_capacity(1); - err.buffer(&mut buffer); - // Not using `emit_unclosed_delims` to use `db.buffer` - for unmatched in unmatched_braces { - if let Some(err) = make_unclosed_delims_error(unmatched, &sess) { - err.buffer(&mut buffer); - } - } - Err(buffer) - } - } + lexer::parse_token_trees(sess, src.as_str(), source_file.start_pos, override_span) } /// Given a stream and the `ParseSess`, produces a parser. diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 686454a8f..e3e7c63e3 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -1,11 +1,15 @@ use crate::errors::{InvalidMetaItem, SuffixedLiteralInAttribute}; +use crate::fluent_generated as fluent; use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle}; use rustc_ast as ast; use rustc_ast::attr; use rustc_ast::token::{self, Delimiter, Nonterminal}; -use rustc_errors::{error_code, fluent, Diagnostic, IntoDiagnostic, PResult}; +use rustc_errors::{error_code, Diagnostic, IntoDiagnostic, PResult}; use rustc_span::{sym, BytePos, Span}; +use std::convert::TryInto; +use thin_vec::ThinVec; +use tracing::debug; // Public for rustfmt usage #[derive(Debug)] @@ -65,10 +69,10 @@ impl<'a> Parser<'a> { token::CommentKind::Block => OuterAttributeType::DocBlockComment, }, ) { - err.note(fluent::note); + err.note(fluent::parse_note); err.span_suggestion_verbose( replacement_span, - fluent::suggestion, + fluent::parse_suggestion, "", rustc_errors::Applicability::MachineApplicable, ); @@ -172,10 +176,10 @@ impl<'a> Parser<'a> { Ok(Some(item)) => { // FIXME(#100717) err.set_arg("item", item.kind.descr()); - err.span_label(item.span, fluent::label_does_not_annotate_this); + err.span_label(item.span, fluent::parse_label_does_not_annotate_this); err.span_suggestion_verbose( replacement_span, - fluent::sugg_change_inner_to_outer, + fluent::parse_sugg_change_inner_to_outer, match attr_type { OuterAttributeType::Attribute => "", OuterAttributeType::DocBlockComment => "*", @@ -201,8 +205,8 @@ impl<'a> Parser<'a> { attr_sp, fluent::parse_inner_attr_not_permitted_after_outer_doc_comment, ); - diag.span_label(attr_sp, fluent::label_attr) - .span_label(prev_doc_comment_span, fluent::label_prev_doc_comment); + diag.span_label(attr_sp, fluent::parse_label_attr) + .span_label(prev_doc_comment_span, fluent::parse_label_prev_doc_comment); diag } Some(InnerAttrForbiddenReason::AfterOuterAttribute { prev_outer_attr_sp }) => { @@ -210,8 +214,8 @@ impl<'a> Parser<'a> { attr_sp, fluent::parse_inner_attr_not_permitted_after_outer_attr, ); - diag.span_label(attr_sp, fluent::label_attr) - .span_label(prev_outer_attr_sp, fluent::label_prev_attr); + diag.span_label(attr_sp, fluent::parse_label_attr) + .span_label(prev_outer_attr_sp, fluent::parse_label_prev_attr); diag } Some(InnerAttrForbiddenReason::InCodeBlock) | None => { @@ -346,9 +350,9 @@ impl<'a> Parser<'a> { } /// Matches `COMMASEP(meta_item_inner)`. - pub(crate) fn parse_meta_seq_top(&mut self) -> PResult<'a, Vec> { + pub(crate) fn parse_meta_seq_top(&mut self) -> PResult<'a, ThinVec> { // Presumably, the majority of the time there will only be one attr. - let mut nmis = Vec::with_capacity(1); + let mut nmis = ThinVec::with_capacity(1); while self.token.kind != token::Eof { nmis.push(self.parse_meta_item_inner()?); if !self.eat(&token::Comma) { diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index b97f22417..b0ab0f106 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -134,11 +134,11 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { // Process the replace ranges, starting from the highest start // position and working our way back. If have tokens like: // - // `#[cfg(FALSE)]` struct Foo { #[cfg(FALSE)] field: bool }` + // `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }` // // Then we will generate replace ranges for both // the `#[cfg(FALSE)] field: bool` and the entire - // `#[cfg(FALSE)]` struct Foo { #[cfg(FALSE)] field: bool }` + // `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }` // // By starting processing from the replace range with the greatest // start position, we ensure that any replace range which encloses @@ -469,6 +469,6 @@ mod size_asserts { use rustc_data_structures::static_assert_size; // tidy-alphabetical-start static_assert_size!(AttrWrapper, 16); - static_assert_size!(LazyAttrTokenStreamImpl, 144); + static_assert_size!(LazyAttrTokenStreamImpl, 120); // tidy-alphabetical-end } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 4c918c670..a051dbe9f 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -18,7 +18,8 @@ use crate::errors::{ UseEqInstead, }; -use crate::lexer::UnmatchedBrace; +use crate::fluent_generated as fluent; +use crate::lexer::UnmatchedDelim; use crate::parser; use rustc_ast as ast; use rustc_ast::ptr::P; @@ -32,10 +33,9 @@ use rustc_ast::{ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ - fluent, Applicability, DiagnosticBuilder, DiagnosticMessage, FatalError, Handler, MultiSpan, - PResult, + pluralize, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed, + FatalError, Handler, IntoDiagnostic, MultiSpan, PResult, }; -use rustc_errors::{pluralize, Diagnostic, ErrorGuaranteed, IntoDiagnostic}; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident}; @@ -165,8 +165,6 @@ enum IsStandalone { #[derive(Debug, Copy, Clone, PartialEq, Eq)] enum IncOrDec { Inc, - // FIXME: `i--` recovery isn't implemented yet - #[allow(dead_code)] Dec, } @@ -222,7 +220,7 @@ impl MultiSugg { /// is dropped. pub struct SnapshotParser<'a> { parser: Parser<'a>, - unclosed_delims: Vec, + unclosed_delims: Vec, } impl<'a> Deref for SnapshotParser<'a> { @@ -264,7 +262,7 @@ impl<'a> Parser<'a> { self.unclosed_delims.extend(snapshot.unclosed_delims); } - pub fn unclosed_delims(&self) -> &[UnmatchedBrace] { + pub fn unclosed_delims(&self) -> &[UnmatchedDelim] { &self.unclosed_delims } @@ -284,7 +282,7 @@ impl<'a> Parser<'a> { self.sess.source_map().span_to_snippet(span) } - pub(super) fn expected_ident_found(&self) -> DiagnosticBuilder<'a, ErrorGuaranteed> { + pub(super) fn expected_ident_found(&mut self) -> DiagnosticBuilder<'a, ErrorGuaranteed> { let valid_follow = &[ TokenKind::Eq, TokenKind::Colon, @@ -324,7 +322,61 @@ impl<'a> Parser<'a> { suggest_raw, suggest_remove_comma, }; - err.into_diagnostic(&self.sess.span_diagnostic) + let mut err = err.into_diagnostic(&self.sess.span_diagnostic); + + // if the token we have is a `<` + // it *might* be a misplaced generic + if self.token == token::Lt { + // all keywords that could have generic applied + let valid_prev_keywords = + [kw::Fn, kw::Type, kw::Struct, kw::Enum, kw::Union, kw::Trait]; + + // If we've expected an identifier, + // and the current token is a '<' + // if the previous token is a valid keyword + // that might use a generic, then suggest a correct + // generic placement (later on) + let maybe_keyword = self.prev_token.clone(); + if valid_prev_keywords.into_iter().any(|x| maybe_keyword.is_keyword(x)) { + // if we have a valid keyword, attempt to parse generics + // also obtain the keywords symbol + match self.parse_generics() { + Ok(generic) => { + if let TokenKind::Ident(symbol, _) = maybe_keyword.kind { + let ident_name = symbol; + // at this point, we've found something like + // `fn id` + // and current token should be Ident with the item name (i.e. the function name) + // if there is a `<` after the fn name, then don't show a suggestion, show help + + if !self.look_ahead(1, |t| *t == token::Lt) && + let Ok(snippet) = self.sess.source_map().span_to_snippet(generic.span) { + err.multipart_suggestion_verbose( + format!("place the generic parameter name after the {ident_name} name"), + vec![ + (self.token.span.shrink_to_hi(), snippet), + (generic.span, String::new()) + ], + Applicability::MaybeIncorrect, + ); + } else { + err.help(format!( + "place the generic parameter name after the {ident_name} name" + )); + } + } + } + Err(err) => { + // if there's an error parsing the generics, + // then don't do a misplaced generics suggestion + // and emit the expected ident error instead; + err.cancel(); + } + } + } + } + + err } pub(super) fn expected_one_of_not_found( @@ -639,7 +691,7 @@ impl<'a> Parser<'a> { span: self.prev_token.span.shrink_to_lo(), tokens: None, }; - let struct_expr = snapshot.parse_struct_expr(None, path, false); + let struct_expr = snapshot.parse_expr_struct(None, path, false); let block_tail = self.parse_block_tail(lo, s, AttemptLocalParseRecovery::No); return Some(match (struct_expr, block_tail) { (Ok(expr), Err(mut err)) => { @@ -654,7 +706,7 @@ impl<'a> Parser<'a> { err.delay_as_bug(); self.restore_snapshot(snapshot); let mut tail = self.mk_block( - vec![self.mk_stmt_err(expr.span)], + thin_vec![self.mk_stmt_err(expr.span)], s, lo.to(self.prev_token.span), ); @@ -1303,6 +1355,20 @@ impl<'a> Parser<'a> { self.recover_from_inc_dec(operand_expr, kind, op_span) } + pub(super) fn recover_from_postfix_decrement( + &mut self, + operand_expr: P, + op_span: Span, + start_stmt: bool, + ) -> PResult<'a, P> { + let kind = IncDecRecovery { + standalone: if start_stmt { IsStandalone::Standalone } else { IsStandalone::Subexpr }, + op: IncOrDec::Dec, + fixity: UnaryFixity::Post, + }; + self.recover_from_inc_dec(operand_expr, kind, op_span) + } + fn recover_from_inc_dec( &mut self, base: P, @@ -1570,7 +1636,7 @@ impl<'a> Parser<'a> { // Handle `await { }`. // This needs to be handled separately from the next arm to avoid // interpreting `await { }?` as `?.await`. - self.parse_block_expr(None, self.token.span, BlockCheckMode::Default) + self.parse_expr_block(None, self.token.span, BlockCheckMode::Default) } else { self.parse_expr() } @@ -2030,7 +2096,7 @@ impl<'a> Parser<'a> { } pub(super) fn recover_arg_parse(&mut self) -> PResult<'a, (P, P)> { - let pat = self.parse_pat_no_top_alt(Some("argument name"))?; + let pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName))?; self.expect(&token::Colon)?; let ty = self.parse_ty()?; @@ -2121,7 +2187,7 @@ impl<'a> Parser<'a> { /// the parameters are *names* (so we don't emit errors about not being able to find `b` in /// the local scope), but if we find the same name multiple times, like in `fn foo(i8, i8)`, /// we deduplicate them to not complain about duplicated parameter names. - pub(super) fn deduplicate_recovered_params_names(&self, fn_inputs: &mut Vec) { + pub(super) fn deduplicate_recovered_params_names(&self, fn_inputs: &mut ThinVec) { let mut seen_inputs = FxHashSet::default(); for input in fn_inputs.iter_mut() { let opt_ident = if let (PatKind::Ident(_, ident, _), TyKind::Err) = @@ -2145,7 +2211,7 @@ impl<'a> Parser<'a> { /// like the user has forgotten them. pub fn handle_ambiguous_unbraced_const_arg( &mut self, - args: &mut Vec, + args: &mut ThinVec, ) -> PResult<'a, bool> { // If we haven't encountered a closing `>`, then the argument is malformed. // It's likely that the user has written a const expression without enclosing it @@ -2353,6 +2419,28 @@ impl<'a> Parser<'a> { Err(err) } + /// Try to recover from an unbraced const argument whose first token [could begin a type][ty]. + /// + /// [ty]: token::Token::can_begin_type + pub(crate) fn recover_unbraced_const_arg_that_can_begin_ty( + &mut self, + mut snapshot: SnapshotParser<'a>, + ) -> Option> { + match snapshot.parse_expr_res(Restrictions::CONST_EXPR, None) { + // Since we don't know the exact reason why we failed to parse the type or the + // expression, employ a simple heuristic to weed out some pathological cases. + Ok(expr) if let token::Comma | token::Gt = snapshot.token.kind => { + self.restore_snapshot(snapshot); + Some(expr) + } + Ok(_) => None, + Err(err) => { + err.cancel(); + None + } + } + } + /// Creates a dummy const argument, and reports that the expression must be enclosed in braces pub fn dummy_const_arg_needs_braces( &self, @@ -2375,7 +2463,7 @@ impl<'a> Parser<'a> { pub(crate) fn maybe_recover_colon_colon_in_pat_typo( &mut self, mut first_pat: P, - expected: Expected, + expected: Option, ) -> P { if token::Colon != self.token.kind { return first_pat; @@ -2383,26 +2471,42 @@ impl<'a> Parser<'a> { if !matches!(first_pat.kind, PatKind::Ident(_, _, None) | PatKind::Path(..)) || !self.look_ahead(1, |token| token.is_ident() && !token.is_reserved_ident()) { + let mut snapshot_type = self.create_snapshot_for_diagnostic(); + snapshot_type.bump(); // `:` + match snapshot_type.parse_ty() { + Err(inner_err) => { + inner_err.cancel(); + } + Ok(ty) => { + let Err(mut err) = self.expected_one_of_not_found(&[], &[]) else { + return first_pat; + }; + err.span_label(ty.span, "specifying the type of a pattern isn't supported"); + self.restore_snapshot(snapshot_type); + let span = first_pat.span.to(ty.span); + first_pat = self.mk_pat(span, PatKind::Wild); + err.emit(); + } + } return first_pat; } // The pattern looks like it might be a path with a `::` -> `:` typo: // `match foo { bar:baz => {} }` - let span = self.token.span; + let colon_span = self.token.span; // We only emit "unexpected `:`" error here if we can successfully parse the // whole pattern correctly in that case. - let snapshot = self.create_snapshot_for_diagnostic(); + let mut snapshot_pat = self.create_snapshot_for_diagnostic(); + let mut snapshot_type = self.create_snapshot_for_diagnostic(); // Create error for "unexpected `:`". match self.expected_one_of_not_found(&[], &[]) { Err(mut err) => { - self.bump(); // Skip the `:`. - match self.parse_pat_no_top_alt(expected) { + // Skip the `:`. + snapshot_pat.bump(); + snapshot_type.bump(); + match snapshot_pat.parse_pat_no_top_alt(expected) { Err(inner_err) => { - // Carry on as if we had not done anything, callers will emit a - // reasonable error. inner_err.cancel(); - err.cancel(); - self.restore_snapshot(snapshot); } Ok(mut pat) => { // We've parsed the rest of the pattern. @@ -2466,8 +2570,8 @@ impl<'a> Parser<'a> { _ => {} } if show_sugg { - err.span_suggestion( - span, + err.span_suggestion_verbose( + colon_span.until(self.look_ahead(1, |t| t.span)), "maybe write a path separator here", "::", Applicability::MaybeIncorrect, @@ -2475,13 +2579,24 @@ impl<'a> Parser<'a> { } else { first_pat = self.mk_pat(new_span, PatKind::Wild); } - err.emit(); + self.restore_snapshot(snapshot_pat); + } + } + match snapshot_type.parse_ty() { + Err(inner_err) => { + inner_err.cancel(); + } + Ok(ty) => { + err.span_label(ty.span, "specifying the type of a pattern isn't supported"); + self.restore_snapshot(snapshot_type); + let new_span = first_pat.span.to(ty.span); + first_pat = self.mk_pat(new_span, PatKind::Wild); } } + err.emit(); } _ => { // Carry on as if we had not done anything. This should be unreachable. - self.restore_snapshot(snapshot); } }; first_pat diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index bf93a89f0..95a7ca80d 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1,29 +1,12 @@ use super::diagnostics::SnapshotParser; -use super::pat::{CommaRecoveryMode, RecoverColon, RecoverComma, PARAM_EXPECTED}; +use super::pat::{CommaRecoveryMode, Expected, RecoverColon, RecoverComma}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{ AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions, SemiColonMode, SeqSep, TokenExpectType, TokenType, TrailingToken, }; -use crate::errors::{ - ArrayBracketsInsteadOfSpaces, ArrayBracketsInsteadOfSpacesSugg, AsyncMoveOrderIncorrect, - BracesForStructLiteral, CatchAfterTry, CommaAfterBaseStruct, ComparisonInterpretedAsGeneric, - ComparisonOrShiftInterpretedAsGenericSugg, DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, - ExpectedElseBlock, ExpectedEqForLetExpr, ExpectedExpressionFoundLet, - FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart, FoundExprWouldBeStmt, - IfExpressionMissingCondition, IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub, - InvalidBlockMacroSegment, InvalidComparisonOperator, InvalidComparisonOperatorSub, - InvalidInterpolatedExpression, InvalidLiteralSuffixOnTupleIndex, InvalidLogicalOperator, - InvalidLogicalOperatorSub, LabeledLoopInBreak, LeadingPlusNotSupported, LeftArrowOperator, - LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, MalformedLoopLabel, - MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg, MissingCommaAfterMatchArm, - MissingDotDot, MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray, - NoFieldsForFnCall, NotAsNegationOperator, NotAsNegationOperatorSub, - OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields, - RequireColonAfterLabeledExpression, ShiftInterpretedAsGeneric, StructLiteralNotAllowedHere, - StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator, UnexpectedIfWithIf, - UnexpectedTokenAfterLabel, UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses, -}; + +use crate::errors; use crate::maybe_recover_from_interpolated_ty_qpath; use core::mem; use rustc_ast::ptr::P; @@ -39,8 +22,8 @@ use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, R use rustc_ast::{ClosureBinder, MetaItemLit, StmtKind}; use rustc_ast_pretty::pprust; use rustc_errors::{ - Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, PResult, - StashKey, + AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, + PResult, StashKey, }; use rustc_session::errors::{report_lit_error, ExprParenthesesNeeded}; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; @@ -48,6 +31,7 @@ use rustc_session::lint::BuiltinLintDiagnostics; use rustc_span::source_map::{self, Span, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, Pos}; +use thin_vec::{thin_vec, ThinVec}; /// Possibly accepts an `token::Interpolated` expression (a pre-parsed expression /// dropped into the token stream, which happens while parsing the result of @@ -119,7 +103,7 @@ impl<'a> Parser<'a> { self.collect_tokens_no_attrs(|this| this.parse_expr()) } - pub fn parse_anon_const_expr(&mut self) -> PResult<'a, AnonConst> { + pub fn parse_expr_anon_const(&mut self) -> PResult<'a, AnonConst> { self.parse_expr().map(|value| AnonConst { id: DUMMY_NODE_ID, value }) } @@ -141,7 +125,7 @@ impl<'a> Parser<'a> { } /// Parses a sequence of expressions delimited by parentheses. - fn parse_paren_expr_seq(&mut self) -> PResult<'a, Vec>> { + fn parse_expr_paren_seq(&mut self) -> PResult<'a, ThinVec>> { self.parse_paren_comma_seq(|p| p.parse_expr_catch_underscore()).map(|(r, _)| r) } @@ -152,7 +136,7 @@ impl<'a> Parser<'a> { r: Restrictions, already_parsed_attrs: Option, ) -> PResult<'a, P> { - self.with_res(r, |this| this.parse_assoc_expr(already_parsed_attrs)) + self.with_res(r, |this| this.parse_expr_assoc(already_parsed_attrs)) } /// Parses an associative expression. @@ -160,15 +144,15 @@ impl<'a> Parser<'a> { /// This parses an expression accounting for associativity and precedence of the operators in /// the expression. #[inline] - fn parse_assoc_expr( + fn parse_expr_assoc( &mut self, already_parsed_attrs: Option, ) -> PResult<'a, P> { - self.parse_assoc_expr_with(0, already_parsed_attrs.into()) + self.parse_expr_assoc_with(0, already_parsed_attrs.into()) } /// Parses an associative expression with operators of at least `min_prec` precedence. - pub(super) fn parse_assoc_expr_with( + pub(super) fn parse_expr_assoc_with( &mut self, min_prec: usize, lhs: LhsExpr, @@ -183,9 +167,9 @@ impl<'a> Parser<'a> { _ => None, }; if self.token.is_range_separator() { - return self.parse_prefix_range_expr(attrs); + return self.parse_expr_prefix_range(attrs); } else { - self.parse_prefix_expr(attrs)? + self.parse_expr_prefix(attrs)? } }; let last_type_ascription_set = self.last_type_ascription.is_some(); @@ -243,10 +227,10 @@ impl<'a> Parser<'a> { } .into(); let invalid = format!("{}=", &sugg); - self.sess.emit_err(InvalidComparisonOperator { + self.sess.emit_err(errors::InvalidComparisonOperator { span: sp, invalid: invalid.clone(), - sub: InvalidComparisonOperatorSub::Correctable { + sub: errors::InvalidComparisonOperatorSub::Correctable { span: sp, invalid, correct: sugg, @@ -261,10 +245,10 @@ impl<'a> Parser<'a> { && self.prev_token.span.hi() == self.token.span.lo() { let sp = op.span.to(self.token.span); - self.sess.emit_err(InvalidComparisonOperator { + self.sess.emit_err(errors::InvalidComparisonOperator { span: sp, invalid: "<>".into(), - sub: InvalidComparisonOperatorSub::Correctable { + sub: errors::InvalidComparisonOperatorSub::Correctable { span: sp, invalid: "<>".into(), correct: "!=".into(), @@ -279,10 +263,10 @@ impl<'a> Parser<'a> { && self.prev_token.span.hi() == self.token.span.lo() { let sp = op.span.to(self.token.span); - self.sess.emit_err(InvalidComparisonOperator { + self.sess.emit_err(errors::InvalidComparisonOperator { span: sp, invalid: "<=>".into(), - sub: InvalidComparisonOperatorSub::Spaceship(sp), + sub: errors::InvalidComparisonOperatorSub::Spaceship(sp), }); self.bump(); } @@ -298,6 +282,18 @@ impl<'a> Parser<'a> { continue; } + if self.prev_token == token::BinOp(token::Minus) + && self.token == token::BinOp(token::Minus) + && self.prev_token.span.between(self.token.span).is_empty() + && !self.look_ahead(1, |tok| tok.can_begin_expr()) + { + let op_span = self.prev_token.span.to(self.token.span); + // Eat the second `-` + self.bump(); + lhs = self.recover_from_postfix_decrement(lhs, op_span, starts_stmt)?; + continue; + } + let op = op.node; // Special cases: if op == AssocOp::As { @@ -309,7 +305,7 @@ impl<'a> Parser<'a> { } 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. - lhs = self.parse_range_expr(prec, lhs, op, cur_op_span)?; + lhs = self.parse_expr_range(prec, lhs, op, cur_op_span)?; break; } @@ -322,7 +318,7 @@ impl<'a> Parser<'a> { Fixity::None => 1, }; let rhs = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| { - this.parse_assoc_expr_with(prec + prec_adjustment, LhsExpr::NotYetParsed) + this.parse_expr_assoc_with(prec + prec_adjustment, LhsExpr::NotYetParsed) })?; let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span); @@ -419,7 +415,7 @@ impl<'a> Parser<'a> { /// but the next token implies this should be parsed as an expression. /// For example: `if let Some(x) = x { x } else { 0 } / 2`. fn error_found_expr_would_be_stmt(&self, lhs: &Expr) { - self.sess.emit_err(FoundExprWouldBeStmt { + self.sess.emit_err(errors::FoundExprWouldBeStmt { span: self.token.span, token: self.token.clone(), suggestion: ExprParenthesesNeeded::surrounding(lhs.span), @@ -446,18 +442,18 @@ impl<'a> Parser<'a> { } (Some(op), _) => (op, self.token.span), (None, Some((Ident { name: sym::and, span }, false))) if self.may_recover() => { - self.sess.emit_err(InvalidLogicalOperator { + self.sess.emit_err(errors::InvalidLogicalOperator { span: self.token.span, incorrect: "and".into(), - sub: InvalidLogicalOperatorSub::Conjunction(self.token.span), + sub: errors::InvalidLogicalOperatorSub::Conjunction(self.token.span), }); (AssocOp::LAnd, span) } (None, Some((Ident { name: sym::or, span }, false))) if self.may_recover() => { - self.sess.emit_err(InvalidLogicalOperator { + self.sess.emit_err(errors::InvalidLogicalOperator { span: self.token.span, incorrect: "or".into(), - sub: InvalidLogicalOperatorSub::Disjunction(self.token.span), + sub: errors::InvalidLogicalOperatorSub::Disjunction(self.token.span), }); (AssocOp::LOr, span) } @@ -474,7 +470,7 @@ impl<'a> Parser<'a> { /// Parses `x..y`, `x..=y`, and `x..`/`x..=`. /// The other two variants are handled in `parse_prefix_range_expr` below. - fn parse_range_expr( + fn parse_expr_range( &mut self, prec: usize, lhs: P, @@ -482,7 +478,7 @@ impl<'a> Parser<'a> { cur_op_span: Span, ) -> PResult<'a, P> { let rhs = if self.is_at_start_of_range_notation_rhs() { - Some(self.parse_assoc_expr_with(prec + 1, LhsExpr::NotYetParsed)?) + Some(self.parse_expr_assoc_with(prec + 1, LhsExpr::NotYetParsed)?) } else { None }; @@ -507,7 +503,7 @@ impl<'a> Parser<'a> { } /// Parses prefix-forms of range notation: `..expr`, `..`, `..=expr`. - fn parse_prefix_range_expr(&mut self, attrs: Option) -> PResult<'a, P> { + fn parse_expr_prefix_range(&mut self, attrs: Option) -> PResult<'a, P> { // Check for deprecated `...` syntax. if self.token == token::DotDotDot { self.err_dotdotdot_syntax(self.token.span); @@ -534,7 +530,7 @@ impl<'a> Parser<'a> { this.bump(); let (span, opt_end) = if this.is_at_start_of_range_notation_rhs() { // RHS must be parsed with more associativity than the dots. - this.parse_assoc_expr_with(op.unwrap().precedence() + 1, LhsExpr::NotYetParsed) + this.parse_expr_assoc_with(op.unwrap().precedence() + 1, LhsExpr::NotYetParsed) .map(|x| (lo.to(x.span), Some(x)))? } else { (lo, None) @@ -545,7 +541,7 @@ impl<'a> Parser<'a> { } /// Parses a prefix-unary-operator expr. - fn parse_prefix_expr(&mut self, attrs: Option) -> PResult<'a, P> { + fn parse_expr_prefix(&mut self, attrs: Option) -> PResult<'a, P> { let attrs = self.parse_or_use_outer_attributes(attrs)?; let lo = self.token.span; @@ -563,25 +559,28 @@ impl<'a> Parser<'a> { // Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr() match this.token.uninterpolate().kind { // `!expr` - token::Not => make_it!(this, attrs, |this, _| this.parse_unary_expr(lo, UnOp::Not)), + token::Not => make_it!(this, attrs, |this, _| this.parse_expr_unary(lo, UnOp::Not)), // `~expr` token::Tilde => make_it!(this, attrs, |this, _| this.recover_tilde_expr(lo)), // `-expr` token::BinOp(token::Minus) => { - make_it!(this, attrs, |this, _| this.parse_unary_expr(lo, UnOp::Neg)) + make_it!(this, attrs, |this, _| this.parse_expr_unary(lo, UnOp::Neg)) } // `*expr` token::BinOp(token::Star) => { - make_it!(this, attrs, |this, _| this.parse_unary_expr(lo, UnOp::Deref)) + make_it!(this, attrs, |this, _| this.parse_expr_unary(lo, UnOp::Deref)) } // `&expr` and `&&expr` token::BinOp(token::And) | token::AndAnd => { - make_it!(this, attrs, |this, _| this.parse_borrow_expr(lo)) + make_it!(this, attrs, |this, _| this.parse_expr_borrow(lo)) } // `+lit` token::BinOp(token::Plus) if this.look_ahead(1, |tok| tok.is_numeric_lit()) => { - let mut err = - LeadingPlusNotSupported { span: lo, remove_plus: None, add_parentheses: None }; + let mut err = errors::LeadingPlusNotSupported { + span: lo, + remove_plus: None, + add_parentheses: None, + }; // a block on the LHS might have been intended to be an expression instead if let Some(sp) = this.sess.ambiguous_block_expr_parse.borrow().get(&lo) { @@ -592,7 +591,7 @@ impl<'a> Parser<'a> { this.sess.emit_err(err); this.bump(); - this.parse_prefix_expr(None) + this.parse_expr_prefix(None) } // Recover from `++x`: token::BinOp(token::Plus) @@ -605,41 +604,41 @@ impl<'a> Parser<'a> { this.bump(); this.bump(); - let operand_expr = this.parse_dot_or_call_expr(Default::default())?; + let operand_expr = this.parse_expr_dot_or_call(Default::default())?; this.recover_from_prefix_increment(operand_expr, pre_span, starts_stmt) } token::Ident(..) if this.token.is_keyword(kw::Box) => { - make_it!(this, attrs, |this, _| this.parse_box_expr(lo)) + make_it!(this, attrs, |this, _| this.parse_expr_box(lo)) } token::Ident(..) if this.may_recover() && this.is_mistaken_not_ident_negation() => { make_it!(this, attrs, |this, _| this.recover_not_expr(lo)) } - _ => return this.parse_dot_or_call_expr(Some(attrs)), + _ => return this.parse_expr_dot_or_call(Some(attrs)), } } - fn parse_prefix_expr_common(&mut self, lo: Span) -> PResult<'a, (Span, P)> { + fn parse_expr_prefix_common(&mut self, lo: Span) -> PResult<'a, (Span, P)> { self.bump(); - let expr = self.parse_prefix_expr(None); + let expr = self.parse_expr_prefix(None); let (span, expr) = self.interpolated_or_expr_span(expr)?; Ok((lo.to(span), expr)) } - fn parse_unary_expr(&mut self, lo: Span, op: UnOp) -> PResult<'a, (Span, ExprKind)> { - let (span, expr) = self.parse_prefix_expr_common(lo)?; + fn parse_expr_unary(&mut self, lo: Span, op: UnOp) -> PResult<'a, (Span, ExprKind)> { + let (span, expr) = self.parse_expr_prefix_common(lo)?; Ok((span, self.mk_unary(op, expr))) } /// Recover on `~expr` in favor of `!expr`. fn recover_tilde_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { - self.sess.emit_err(TildeAsUnaryOperator(lo)); + self.sess.emit_err(errors::TildeAsUnaryOperator(lo)); - self.parse_unary_expr(lo, UnOp::Not) + self.parse_expr_unary(lo, UnOp::Not) } /// Parse `box expr`. - fn parse_box_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { - let (span, expr) = self.parse_prefix_expr_common(lo)?; + fn parse_expr_box(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { + let (span, expr) = self.parse_expr_prefix_common(lo)?; self.sess.gated_spans.gate(sym::box_syntax, span); Ok((span, ExprKind::Box(expr))) } @@ -660,14 +659,14 @@ impl<'a> Parser<'a> { let negated_token = self.look_ahead(1, |t| t.clone()); let sub_diag = if negated_token.is_numeric_lit() { - NotAsNegationOperatorSub::SuggestNotBitwise + errors::NotAsNegationOperatorSub::SuggestNotBitwise } else if negated_token.is_bool_lit() { - NotAsNegationOperatorSub::SuggestNotLogical + errors::NotAsNegationOperatorSub::SuggestNotLogical } else { - NotAsNegationOperatorSub::SuggestNotDefault + errors::NotAsNegationOperatorSub::SuggestNotDefault }; - self.sess.emit_err(NotAsNegationOperator { + self.sess.emit_err(errors::NotAsNegationOperator { negated: negated_token.span, negated_desc: super::token_descr(&negated_token), // Span the `not` plus trailing whitespace to avoid @@ -677,7 +676,7 @@ impl<'a> Parser<'a> { ), }); - self.parse_unary_expr(lo, UnOp::Not) + self.parse_expr_unary(lo, UnOp::Not) } /// Returns the span of expr, if it was not interpolated or the span of the interpolated token. @@ -735,10 +734,10 @@ impl<'a> Parser<'a> { segments[0].ident.span, ), }; - match self.parse_labeled_expr(label, false) { + match self.parse_expr_labeled(label, false) { Ok(expr) => { type_err.cancel(); - self.sess.emit_err(MalformedLoopLabel { + self.sess.emit_err(errors::MalformedLoopLabel { span: label.ident.span, correct_label: label.ident, }); @@ -763,20 +762,22 @@ impl<'a> Parser<'a> { ); let args_span = self.look_ahead(1, |t| t.span).to(span_after_type); - let suggestion = ComparisonOrShiftInterpretedAsGenericSugg { + let suggestion = errors::ComparisonOrShiftInterpretedAsGenericSugg { left: expr.span.shrink_to_lo(), right: expr.span.shrink_to_hi(), }; match self.token.kind { - token::Lt => self.sess.emit_err(ComparisonInterpretedAsGeneric { - comparison: self.token.span, - r#type: path, - args: args_span, - suggestion, - }), + token::Lt => { + self.sess.emit_err(errors::ComparisonInterpretedAsGeneric { + comparison: self.token.span, + r#type: path, + args: args_span, + suggestion, + }) + } token::BinOp(token::Shl) => { - self.sess.emit_err(ShiftInterpretedAsGeneric { + self.sess.emit_err(errors::ShiftInterpretedAsGeneric { shift: self.token.span, r#type: path, args: args_span, @@ -827,7 +828,7 @@ impl<'a> Parser<'a> { ("cast", None) }; - let with_postfix = self.parse_dot_or_call_expr_with_(cast_expr, span)?; + 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. @@ -898,15 +899,15 @@ impl<'a> Parser<'a> { } /// Parse `& mut? ` or `& raw [ const | mut ] `. - fn parse_borrow_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { + fn parse_expr_borrow(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { self.expect_and()?; let has_lifetime = self.token.is_lifetime() && self.look_ahead(1, |t| t != &token::Colon); let lifetime = has_lifetime.then(|| self.expect_lifetime()); // For recovery, see below. let (borrow_kind, mutbl) = self.parse_borrow_modifiers(lo); let expr = if self.token.is_range_separator() { - self.parse_prefix_range_expr(None) + self.parse_expr_prefix_range(None) } else { - self.parse_prefix_expr(None) + self.parse_expr_prefix(None) }; let (hi, expr) = self.interpolated_or_expr_span(expr)?; let span = lo.to(hi); @@ -917,7 +918,7 @@ impl<'a> Parser<'a> { } fn error_remove_borrow_lifetime(&self, span: Span, lt_span: Span) { - self.sess.emit_err(LifetimeInBorrowExpression { span, lifetime_span: lt_span }); + self.sess.emit_err(errors::LifetimeInBorrowExpression { span, lifetime_span: lt_span }); } /// Parse `mut?` or `raw [ const | mut ]`. @@ -936,16 +937,16 @@ impl<'a> Parser<'a> { } /// Parses `a.b` or `a(13)` or `a[4]` or just `a`. - fn parse_dot_or_call_expr(&mut self, attrs: Option) -> PResult<'a, P> { + fn parse_expr_dot_or_call(&mut self, attrs: Option) -> PResult<'a, P> { let attrs = self.parse_or_use_outer_attributes(attrs)?; self.collect_tokens_for_expr(attrs, |this, attrs| { - let base = this.parse_bottom_expr(); + let base = this.parse_expr_bottom(); let (span, base) = this.interpolated_or_expr_span(base)?; - this.parse_dot_or_call_expr_with(base, span, attrs) + this.parse_expr_dot_or_call_with(base, span, attrs) }) } - pub(super) fn parse_dot_or_call_expr_with( + pub(super) fn parse_expr_dot_or_call_with( &mut self, e0: P, lo: Span, @@ -954,7 +955,7 @@ impl<'a> Parser<'a> { // Stitch the list of outer attributes onto the return value. // A little bit ugly, but the best way given the current code // structure - let res = self.parse_dot_or_call_expr_with_(e0, lo); + let res = self.parse_expr_dot_or_call_with_(e0, lo); if attrs.is_empty() { res } else { @@ -968,7 +969,7 @@ impl<'a> Parser<'a> { } } - fn parse_dot_or_call_expr_with_(&mut self, mut e: P, lo: Span) -> PResult<'a, P> { + fn parse_expr_dot_or_call_with_(&mut self, mut e: P, lo: Span) -> PResult<'a, P> { loop { let has_question = if self.prev_token.kind == TokenKind::Ident(kw::Return, false) { // we are using noexpect here because we don't expect a `?` directly after a `return` @@ -991,15 +992,15 @@ impl<'a> Parser<'a> { }; if has_dot { // expr.f - e = self.parse_dot_suffix_expr(lo, e)?; + e = self.parse_expr_dot_suffix(lo, e)?; continue; } if self.expr_is_complete(&e) { return Ok(e); } e = match self.token.kind { - token::OpenDelim(Delimiter::Parenthesis) => self.parse_fn_call_expr(lo, e), - token::OpenDelim(Delimiter::Bracket) => self.parse_index_expr(lo, e)?, + token::OpenDelim(Delimiter::Parenthesis) => self.parse_expr_fn_call(lo, e), + token::OpenDelim(Delimiter::Bracket) => self.parse_expr_index(lo, e)?, _ => return Ok(e), } } @@ -1011,14 +1012,14 @@ impl<'a> Parser<'a> { && self.look_ahead(3, |t| t.can_begin_expr()) } - fn parse_dot_suffix_expr(&mut self, lo: Span, base: P) -> PResult<'a, P> { + fn parse_expr_dot_suffix(&mut self, lo: Span, base: P) -> PResult<'a, P> { match self.token.uninterpolate().kind { token::Ident(..) => self.parse_dot_suffix(base, lo), token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => { - Ok(self.parse_tuple_field_access_expr(lo, base, symbol, suffix, None)) + Ok(self.parse_expr_tuple_field_access(lo, base, symbol, suffix, None)) } token::Literal(token::Lit { kind: token::Float, symbol, suffix }) => { - Ok(self.parse_tuple_field_access_expr_float(lo, base, symbol, suffix)) + Ok(self.parse_expr_tuple_field_access_float(lo, base, symbol, suffix)) } _ => { self.error_unexpected_after_dot(); @@ -1030,7 +1031,7 @@ impl<'a> Parser<'a> { fn error_unexpected_after_dot(&self) { // FIXME Could factor this out into non_fatal_unexpected or something. let actual = pprust::token_to_string(&self.token); - self.struct_span_err(self.token.span, &format!("unexpected token: `{actual}`")).emit(); + self.sess.emit_err(errors::UnexpectedTokenAfterDot { span: self.token.span, actual }); } // We need an identifier or integer, but the next token is a float. @@ -1040,7 +1041,7 @@ impl<'a> Parser<'a> { // support pushing "future tokens" (would be also helpful to `break_and_eat`), or // we should break everything including floats into more basic proc-macro style // tokens in the lexer (probably preferable). - fn parse_tuple_field_access_expr_float( + fn parse_expr_tuple_field_access_float( &mut self, lo: Span, base: P, @@ -1083,7 +1084,7 @@ impl<'a> Parser<'a> { match &*components { // 1e2 [IdentLike(i)] => { - self.parse_tuple_field_access_expr(lo, base, Symbol::intern(&i), suffix, None) + self.parse_expr_tuple_field_access(lo, base, Symbol::intern(&i), suffix, None) } // 1. [IdentLike(i), Punct('.')] => { @@ -1099,7 +1100,7 @@ impl<'a> Parser<'a> { let symbol = Symbol::intern(&i); self.token = Token::new(token::Ident(symbol, false), ident_span); let next_token = (Token::new(token::Dot, dot_span), self.token_spacing); - self.parse_tuple_field_access_expr(lo, base, symbol, None, Some(next_token)) + self.parse_expr_tuple_field_access(lo, base, symbol, None, Some(next_token)) } // 1.2 | 1.2e3 [IdentLike(i1), Punct('.'), IdentLike(i2)] => { @@ -1120,11 +1121,11 @@ impl<'a> Parser<'a> { // See issue #76399 and PR #76285 for more details let next_token1 = (Token::new(token::Dot, dot_span), Spacing::Alone); let base1 = - self.parse_tuple_field_access_expr(lo, base, symbol1, None, Some(next_token1)); + self.parse_expr_tuple_field_access(lo, base, symbol1, None, Some(next_token1)); let symbol2 = Symbol::intern(&i2); let next_token2 = Token::new(token::Ident(symbol2, false), ident2_span); self.bump_with((next_token2, self.token_spacing)); // `.` - self.parse_tuple_field_access_expr(lo, base1, symbol2, suffix, None) + self.parse_expr_tuple_field_access(lo, base1, symbol2, suffix, None) } // 1e+ | 1e- (recovered) [IdentLike(_), Punct('+' | '-')] | @@ -1142,7 +1143,7 @@ impl<'a> Parser<'a> { } } - fn parse_tuple_field_access_expr( + fn parse_expr_tuple_field_access( &mut self, lo: Span, base: P, @@ -1163,7 +1164,7 @@ impl<'a> Parser<'a> { } /// Parse a function call expression, `expr(...)`. - fn parse_fn_call_expr(&mut self, lo: Span, fun: P) -> P { + fn parse_expr_fn_call(&mut self, lo: Span, fun: P) -> P { let snapshot = if self.token.kind == token::OpenDelim(Delimiter::Parenthesis) && self.look_ahead_type_ascription_as_field() { @@ -1174,7 +1175,7 @@ impl<'a> Parser<'a> { let open_paren = self.token.span; let mut seq = self - .parse_paren_expr_seq() + .parse_expr_paren_seq() .map(|args| self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args))); if let Some(expr) = self.maybe_recover_struct_lit_bad_delims(lo, open_paren, &mut seq, snapshot) @@ -1209,16 +1210,21 @@ impl<'a> Parser<'a> { // `Enum::Foo { a: 3, b: 4 }` or `Enum::Foo(3, 4)`. self.restore_snapshot(snapshot); let close_paren = self.prev_token.span; - let span = lo.to(self.prev_token.span); - if !fields.is_empty() { - let mut replacement_err = ParenthesesWithStructFields { + let span = lo.to(close_paren); + 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 == ")") + { + let mut replacement_err = errors::ParenthesesWithStructFields { span, r#type: path, - braces_for_struct: BracesForStructLiteral { + braces_for_struct: errors::BracesForStructLiteral { first: open_paren, second: close_paren, }, - no_fields_for_fn: NoFieldsForFnCall { + no_fields_for_fn: errors::NoFieldsForFnCall { fields: fields .into_iter() .map(|field| field.span.until(field.expr.span)) @@ -1247,7 +1253,7 @@ impl<'a> Parser<'a> { } /// Parse an indexing expression `expr[...]`. - fn parse_index_expr(&mut self, lo: Span, base: P) -> PResult<'a, P> { + fn parse_expr_index(&mut self, lo: Span, base: P) -> PResult<'a, P> { let prev_span = self.prev_token.span; let open_delim_span = self.token.span; self.bump(); // `[` @@ -1270,7 +1276,7 @@ impl<'a> Parser<'a> { if self.check(&token::OpenDelim(Delimiter::Parenthesis)) { // Method call `expr.f()` - let args = self.parse_paren_expr_seq()?; + let args = self.parse_expr_paren_seq()?; let fn_span = fn_span_lo.to(self.prev_token.span); let span = lo.to(self.prev_token.span); Ok(self.mk_expr( @@ -1285,7 +1291,7 @@ impl<'a> Parser<'a> { } else { // Field access `expr.f` if let Some(args) = seg.args { - self.sess.emit_err(FieldExpressionWithGeneric(args.span())); + self.sess.emit_err(errors::FieldExpressionWithGeneric(args.span())); } let span = lo.to(self.prev_token.span); @@ -1298,7 +1304,7 @@ impl<'a> Parser<'a> { /// /// N.B., this does not parse outer attributes, and is private because it only works /// correctly if called from `parse_dot_or_call_expr()`. - fn parse_bottom_expr(&mut self) -> PResult<'a, P> { + fn parse_expr_bottom(&mut self) -> PResult<'a, P> { maybe_recover_from_interpolated_ty_qpath!(self, true); maybe_whole_expr!(self); @@ -1311,13 +1317,13 @@ impl<'a> Parser<'a> { // This match arm is a special-case of the `_` match arm below and // could be removed without changing functionality, but it's faster // to have it here, especially for programs with large constants. - self.parse_lit_expr() + self.parse_expr_lit() } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) { - self.parse_tuple_parens_expr() + self.parse_expr_tuple_parens() } else if self.check(&token::OpenDelim(Delimiter::Brace)) { - self.parse_block_expr(None, lo, BlockCheckMode::Default) + self.parse_expr_block(None, lo, BlockCheckMode::Default) } else if self.check(&token::BinOp(token::Or)) || self.check(&token::OrOr) { - self.parse_closure_expr().map_err(|mut err| { + self.parse_expr_closure().map_err(|mut err| { // If the input is something like `if a { 1 } else { 2 } | if a { 3 } else { 4 }` // then suggest parens around the lhs. if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&lo) { @@ -1326,42 +1332,42 @@ impl<'a> Parser<'a> { err }) } else if self.check(&token::OpenDelim(Delimiter::Bracket)) { - self.parse_array_or_repeat_expr(Delimiter::Bracket) + self.parse_expr_array_or_repeat(Delimiter::Bracket) } else if self.check_path() { - self.parse_path_start_expr() + self.parse_expr_path_start() } else if self.check_keyword(kw::Move) || self.check_keyword(kw::Static) || self.check_const_closure() { - self.parse_closure_expr() + self.parse_expr_closure() } else if self.eat_keyword(kw::If) { - self.parse_if_expr() + self.parse_expr_if() } else if self.check_keyword(kw::For) { if self.choose_generics_over_qpath(1) { - self.parse_closure_expr() + self.parse_expr_closure() } else { assert!(self.eat_keyword(kw::For)); - self.parse_for_expr(None, self.prev_token.span) + self.parse_expr_for(None, self.prev_token.span) } } else if self.eat_keyword(kw::While) { - self.parse_while_expr(None, self.prev_token.span) + self.parse_expr_while(None, self.prev_token.span) } else if let Some(label) = self.eat_label() { - self.parse_labeled_expr(label, true) + self.parse_expr_labeled(label, true) } else if self.eat_keyword(kw::Loop) { let sp = self.prev_token.span; - self.parse_loop_expr(None, self.prev_token.span).map_err(|mut err| { + self.parse_expr_loop(None, self.prev_token.span).map_err(|mut err| { err.span_label(sp, "while parsing this `loop` expression"); err }) } else if self.eat_keyword(kw::Match) { let match_sp = self.prev_token.span; - self.parse_match_expr().map_err(|mut err| { + self.parse_expr_match().map_err(|mut err| { err.span_label(match_sp, "while parsing this `match` expression"); err }) } else if self.eat_keyword(kw::Unsafe) { let sp = self.prev_token.span; - self.parse_block_expr(None, lo, BlockCheckMode::Unsafe(ast::UserProvided)).map_err( + self.parse_expr_block(None, lo, BlockCheckMode::Unsafe(ast::UserProvided)).map_err( |mut err| { err.span_label(sp, "while parsing this `unsafe` expression"); err @@ -1375,17 +1381,17 @@ impl<'a> Parser<'a> { self.expect_keyword(kw::Try)?; self.parse_try_block(lo) } else if self.eat_keyword(kw::Return) { - self.parse_return_expr() + self.parse_expr_return() } else if self.eat_keyword(kw::Continue) { - self.parse_continue_expr(lo) + self.parse_expr_continue(lo) } else if self.eat_keyword(kw::Break) { - self.parse_break_expr() + self.parse_expr_break() } else if self.eat_keyword(kw::Yield) { - self.parse_yield_expr() + self.parse_expr_yield() } else if self.is_do_yeet() { - self.parse_yeet_expr() + self.parse_expr_yeet() } else if self.check_keyword(kw::Let) { - self.parse_let_expr() + self.parse_expr_let() } else if self.eat_keyword(kw::Underscore) { Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore)) } else if !self.unclosed_delims.is_empty() && self.check(&token::Semi) { @@ -1408,19 +1414,19 @@ impl<'a> Parser<'a> { // Check for `async {` and `async move {`. self.parse_async_block() } else { - self.parse_closure_expr() + self.parse_expr_closure() } } else if self.eat_keyword(kw::Await) { self.recover_incorrect_await_syntax(lo, self.prev_token.span) } else { - self.parse_lit_expr() + self.parse_expr_lit() } } else { - self.parse_lit_expr() + self.parse_expr_lit() } } - fn parse_lit_expr(&mut self) -> PResult<'a, P> { + fn parse_expr_lit(&mut self) -> PResult<'a, P> { let lo = self.token.span; match self.parse_opt_token_lit() { Some((token_lit, _)) => { @@ -1431,7 +1437,7 @@ impl<'a> Parser<'a> { } } - fn parse_tuple_parens_expr(&mut self) -> PResult<'a, P> { + fn parse_expr_tuple_parens(&mut self) -> PResult<'a, P> { let lo = self.token.span; self.expect(&token::OpenDelim(Delimiter::Parenthesis))?; let (es, trailing_comma) = match self.parse_seq_to_end( @@ -1455,20 +1461,20 @@ impl<'a> Parser<'a> { self.maybe_recover_from_bad_qpath(expr) } - fn parse_array_or_repeat_expr(&mut self, close_delim: Delimiter) -> PResult<'a, P> { + fn parse_expr_array_or_repeat(&mut self, close_delim: Delimiter) -> PResult<'a, P> { let lo = self.token.span; self.bump(); // `[` or other open delim let close = &token::CloseDelim(close_delim); let kind = if self.eat(close) { // Empty vector - ExprKind::Array(Vec::new()) + ExprKind::Array(ThinVec::new()) } else { // Non-empty vector let first_expr = self.parse_expr()?; if self.eat(&token::Semi) { // Repeating array syntax: `[ 0; 512 ]` - let count = self.parse_anon_const_expr()?; + let count = self.parse_expr_anon_const()?; self.expect(close)?; ExprKind::Repeat(first_expr, count) } else if self.eat(&token::Comma) { @@ -1480,14 +1486,14 @@ impl<'a> Parser<'a> { } else { // Vector with one element self.expect(close)?; - ExprKind::Array(vec![first_expr]) + ExprKind::Array(thin_vec![first_expr]) } }; let expr = self.mk_expr(lo.to(self.prev_token.span), kind); self.maybe_recover_from_bad_qpath(expr) } - fn parse_path_start_expr(&mut self) -> PResult<'a, P> { + fn parse_expr_path_start(&mut self) -> PResult<'a, P> { let (qself, path) = if self.eat_lt() { let (qself, path) = self.parse_qpath(PathStyle::Expr)?; (Some(qself), path) @@ -1499,7 +1505,7 @@ impl<'a> Parser<'a> { let (span, kind) = if self.eat(&token::Not) { // MACRO INVOCATION expression if qself.is_some() { - self.sess.emit_err(MacroInvocationWithQualifiedPath(path.span)); + self.sess.emit_err(errors::MacroInvocationWithQualifiedPath(path.span)); } let lo = path.span; let mac = P(MacCall { @@ -1524,7 +1530,7 @@ impl<'a> Parser<'a> { } /// Parse `'label: $expr`. The label is already parsed. - fn parse_labeled_expr( + fn parse_expr_labeled( &mut self, label_: Label, mut consume_colon: bool, @@ -1533,15 +1539,15 @@ impl<'a> Parser<'a> { let label = Some(label_); let ate_colon = self.eat(&token::Colon); let expr = if self.eat_keyword(kw::While) { - self.parse_while_expr(label, lo) + self.parse_expr_while(label, lo) } else if self.eat_keyword(kw::For) { - self.parse_for_expr(label, lo) + self.parse_expr_for(label, lo) } else if self.eat_keyword(kw::Loop) { - self.parse_loop_expr(label, lo) + self.parse_expr_loop(label, lo) } else if self.check_noexpect(&token::OpenDelim(Delimiter::Brace)) || self.token.is_whole_block() { - self.parse_block_expr(label, lo, BlockCheckMode::Default) + self.parse_expr_block(label, lo, BlockCheckMode::Default) } else if !ate_colon && self.may_recover() && (matches!(self.token.kind, token::CloseDelim(_) | token::Comma) @@ -1549,7 +1555,7 @@ impl<'a> Parser<'a> { { let (lit, _) = self.recover_unclosed_char(label_.ident, Parser::mk_token_lit_char, |self_| { - self_.sess.create_err(UnexpectedTokenAfterLabel { + self_.sess.create_err(errors::UnexpectedTokenAfterLabel { span: self_.token.span, remove_label: None, enclose_in_block: None, @@ -1561,7 +1567,7 @@ impl<'a> Parser<'a> { && (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt)) { // We're probably inside of a `Path<'a>` that needs a turbofish - self.sess.emit_err(UnexpectedTokenAfterLabel { + self.sess.emit_err(errors::UnexpectedTokenAfterLabel { span: self.token.span, remove_label: None, enclose_in_block: None, @@ -1569,7 +1575,7 @@ impl<'a> Parser<'a> { consume_colon = false; Ok(self.mk_expr_err(lo)) } else { - let mut err = UnexpectedTokenAfterLabel { + let mut err = errors::UnexpectedTokenAfterLabel { span: self.token.span, remove_label: None, enclose_in_block: None, @@ -1605,14 +1611,14 @@ impl<'a> Parser<'a> { return expr; } - err.enclose_in_block = Some(UnexpectedTokenAfterLabelSugg { + err.enclose_in_block = Some(errors::UnexpectedTokenAfterLabelSugg { left: span.shrink_to_lo(), right: span.shrink_to_hi(), }); // Replace `'label: non_block_expr` with `'label: {non_block_expr}` in order to suppress future errors about `break 'label`. let stmt = self.mk_stmt(span, StmtKind::Expr(expr)); - let blk = self.mk_block(vec![stmt], BlockCheckMode::Default, span); + let blk = self.mk_block(thin_vec![stmt], BlockCheckMode::Default, span); self.mk_expr(span, ExprKind::Block(blk, label)) }); @@ -1621,7 +1627,7 @@ impl<'a> Parser<'a> { }?; if !ate_colon && consume_colon { - self.sess.emit_err(RequireColonAfterLabeledExpression { + self.sess.emit_err(errors::RequireColonAfterLabeledExpression { span: expr.span, label: lo, label_end: lo.shrink_to_hi(), @@ -1670,7 +1676,7 @@ impl<'a> Parser<'a> { self.bump(); // `catch` let span = lo.to(self.prev_token.span); - self.sess.emit_err(DoCatchSyntaxRemoved { span }); + self.sess.emit_err(errors::DoCatchSyntaxRemoved { span }); self.parse_try_block(lo) } @@ -1681,7 +1687,7 @@ impl<'a> Parser<'a> { } /// Parse `"return" expr?`. - fn parse_return_expr(&mut self) -> PResult<'a, P> { + fn parse_expr_return(&mut self) -> PResult<'a, P> { let lo = self.prev_token.span; let kind = ExprKind::Ret(self.parse_expr_opt()?); let expr = self.mk_expr(lo.to(self.prev_token.span), kind); @@ -1689,7 +1695,7 @@ impl<'a> Parser<'a> { } /// Parse `"do" "yeet" expr?`. - fn parse_yeet_expr(&mut self) -> PResult<'a, P> { + fn parse_expr_yeet(&mut self) -> PResult<'a, P> { let lo = self.token.span; self.bump(); // `do` @@ -1711,16 +1717,16 @@ impl<'a> Parser<'a> { /// `break 'lbl: loop {}`); a labeled break with an unlabeled loop as its value /// expression only gets a warning for compatibility reasons; and a labeled break /// with a labeled loop does not even get a warning because there is no ambiguity. - fn parse_break_expr(&mut self) -> PResult<'a, P> { + fn parse_expr_break(&mut self) -> PResult<'a, P> { let lo = self.prev_token.span; let mut label = self.eat_label(); let kind = if self.token == token::Colon && let Some(label) = label.take() { // The value expression can be a labeled loop, see issue #86948, e.g.: // `loop { break 'label: loop { break 'label 42; }; }` - let lexpr = self.parse_labeled_expr(label, true)?; - self.sess.emit_err(LabeledLoopInBreak { + let lexpr = self.parse_expr_labeled(label, true)?; + self.sess.emit_err(errors::LabeledLoopInBreak { span: lexpr.span, - sub: WrapExpressionInParentheses { + sub: errors::WrapExpressionInParentheses { left: lexpr.span.shrink_to_lo(), right: lexpr.span.shrink_to_hi(), }, @@ -1770,7 +1776,7 @@ impl<'a> Parser<'a> { } /// Parse `"continue" label?`. - fn parse_continue_expr(&mut self, lo: Span) -> PResult<'a, P> { + fn parse_expr_continue(&mut self, lo: Span) -> PResult<'a, P> { let mut label = self.eat_label(); // Recover `continue label` -> `continue 'label` @@ -1787,7 +1793,7 @@ impl<'a> Parser<'a> { } /// Parse `"yield" expr?`. - fn parse_yield_expr(&mut self) -> PResult<'a, P> { + fn parse_expr_yield(&mut self) -> PResult<'a, P> { let lo = self.prev_token.span; let kind = ExprKind::Yield(self.parse_expr_opt()?); let span = lo.to(self.prev_token.span); @@ -1840,7 +1846,7 @@ impl<'a> Parser<'a> { }; if let Some(expr) = expr { if matches!(expr.kind, ExprKind::Err) { - let mut err = InvalidInterpolatedExpression { span: self.token.span } + let mut err = errors::InvalidInterpolatedExpression { span: self.token.span } .into_diagnostic(&self.sess.span_diagnostic); err.downgrade_to_delayed_bug(); return Err(err); @@ -1882,7 +1888,16 @@ impl<'a> Parser<'a> { if let token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) = next_token.kind { - if self.token.span.hi() == next_token.span.lo() { + // If this integer looks like a float, then recover as such. + // + // We will never encounter the exponent part of a floating + // point literal here, since there's no use of the exponent + // syntax that also constitutes a valid integer, so we need + // not check for that. + if suffix.map_or(true, |s| s == sym::f32 || s == sym::f64) + && symbol.as_str().chars().all(|c| c.is_numeric() || c == '_') + && self.token.span.hi() == next_token.span.lo() + { let s = String::from("0.") + symbol.as_str(); let kind = TokenKind::lit(token::Float, Symbol::intern(&s), suffix); return Some(Token::new(kind, self.token.span.to(next_token.span))); @@ -1892,7 +1907,7 @@ impl<'a> Parser<'a> { }); if let Some(token) = &recovered { self.bump(); - self.sess.emit_err(FloatLiteralRequiresIntegerPart { + self.sess.emit_err(errors::FloatLiteralRequiresIntegerPart { span: token.span, correct: pprust::token_to_string(token).into_owned(), }); @@ -1953,13 +1968,17 @@ impl<'a> Parser<'a> { if [sym::i32, sym::u32, sym::isize, sym::usize].contains(&suffix) { // #59553: warn instead of reject out of hand to allow the fix to percolate // through the ecosystem when people fix their macros - self.sess.emit_warning(InvalidLiteralSuffixOnTupleIndex { + self.sess.emit_warning(errors::InvalidLiteralSuffixOnTupleIndex { span, suffix, exception: Some(()), }); } else { - self.sess.emit_err(InvalidLiteralSuffixOnTupleIndex { span, suffix, exception: None }); + self.sess.emit_err(errors::InvalidLiteralSuffixOnTupleIndex { + span, + suffix, + exception: None, + }); } } @@ -1991,11 +2010,11 @@ impl<'a> Parser<'a> { /// expression. fn maybe_suggest_brackets_instead_of_braces(&mut self, lo: Span) -> Option> { let mut snapshot = self.create_snapshot_for_diagnostic(); - match snapshot.parse_array_or_repeat_expr(Delimiter::Brace) { + match snapshot.parse_expr_array_or_repeat(Delimiter::Brace) { Ok(arr) => { - self.sess.emit_err(ArrayBracketsInsteadOfSpaces { + self.sess.emit_err(errors::ArrayBracketsInsteadOfSpaces { span: arr.span, - sub: ArrayBracketsInsteadOfSpacesSugg { + sub: errors::ArrayBracketsInsteadOfSpacesSugg { left: lo, right: snapshot.prev_token.span, }, @@ -2041,7 +2060,7 @@ impl<'a> Parser<'a> { .span_to_snippet(snapshot.token.span) .map_or(false, |snippet| snippet == "]") => { - return Err(MissingSemicolonBeforeArray { + return Err(errors::MissingSemicolonBeforeArray { open_delim: open_delim_span, semicolon: prev_span.shrink_to_hi(), }.into_diagnostic(&self.sess.span_diagnostic)); @@ -2054,7 +2073,7 @@ impl<'a> Parser<'a> { } /// Parses a block or unsafe block. - pub(super) fn parse_block_expr( + pub(super) fn parse_expr_block( &mut self, opt_label: Option