diff options
Diffstat (limited to 'compiler/rustc_parse/src')
| -rw-r--r-- | compiler/rustc_parse/src/errors.rs | 526 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/lexer/mod.rs | 135 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/lexer/unescape_error_reporting.rs | 61 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/lib.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/attr.rs | 32 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/attr_wrapper.rs | 28 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/diagnostics.rs | 95 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 418 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/generics.rs | 36 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/item.rs | 140 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/mod.rs | 90 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/pat.rs | 29 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/path.rs | 17 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/stmt.rs | 93 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/ty.rs | 65 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/validate_attr.rs | 10 |
16 files changed, 1076 insertions, 703 deletions
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 98fee997427..211450250fd 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -9,7 +9,7 @@ use rustc_span::{Span, Symbol}; use crate::parser::TokenDescription; #[derive(Diagnostic)] -#[diag(parser::maybe_report_ambiguous_plus)] +#[diag(parser_maybe_report_ambiguous_plus)] pub(crate) struct AmbiguousPlus { pub sum_ty: String, #[primary_span] @@ -18,7 +18,7 @@ pub(crate) struct AmbiguousPlus { } #[derive(Diagnostic)] -#[diag(parser::maybe_recover_from_bad_type_plus, code = "E0178")] +#[diag(parser_maybe_recover_from_bad_type_plus, code = "E0178")] pub(crate) struct BadTypePlus { pub ty: String, #[primary_span] @@ -30,7 +30,7 @@ pub(crate) struct BadTypePlus { #[derive(Subdiagnostic)] pub(crate) enum BadTypePlusSub { #[suggestion( - parser::add_paren, + parser_add_paren, code = "{sum_with_parens}", applicability = "machine-applicable" )] @@ -39,12 +39,12 @@ pub(crate) enum BadTypePlusSub { #[primary_span] span: Span, }, - #[label(parser::forgot_paren)] + #[label(parser_forgot_paren)] ForgotParen { #[primary_span] span: Span, }, - #[label(parser::expect_path)] + #[label(parser_expect_path)] ExpectPath { #[primary_span] span: Span, @@ -52,7 +52,7 @@ pub(crate) enum BadTypePlusSub { } #[derive(Diagnostic)] -#[diag(parser::maybe_recover_from_bad_qpath_stage_2)] +#[diag(parser_maybe_recover_from_bad_qpath_stage_2)] pub(crate) struct BadQPathStage2 { #[primary_span] #[suggestion(code = "", applicability = "maybe-incorrect")] @@ -61,10 +61,10 @@ pub(crate) struct BadQPathStage2 { } #[derive(Diagnostic)] -#[diag(parser::incorrect_semicolon)] +#[diag(parser_incorrect_semicolon)] pub(crate) struct IncorrectSemicolon<'a> { #[primary_span] - #[suggestion_short(code = "", applicability = "machine-applicable")] + #[suggestion(style = "short", code = "", applicability = "machine-applicable")] pub span: Span, #[help] pub opt_help: Option<()>, @@ -72,26 +72,26 @@ pub(crate) struct IncorrectSemicolon<'a> { } #[derive(Diagnostic)] -#[diag(parser::incorrect_use_of_await)] +#[diag(parser_incorrect_use_of_await)] pub(crate) struct IncorrectUseOfAwait { #[primary_span] - #[suggestion(parser::parentheses_suggestion, code = "", applicability = "machine-applicable")] + #[suggestion(parentheses_suggestion, code = "", applicability = "machine-applicable")] pub span: Span, } #[derive(Diagnostic)] -#[diag(parser::incorrect_use_of_await)] +#[diag(parser_incorrect_use_of_await)] pub(crate) struct IncorrectAwait { #[primary_span] pub span: Span, - #[suggestion(parser::postfix_suggestion, code = "{expr}.await{question_mark}")] + #[suggestion(postfix_suggestion, code = "{expr}.await{question_mark}")] pub sugg_span: (Span, Applicability), pub expr: String, pub question_mark: &'static str, } #[derive(Diagnostic)] -#[diag(parser::in_in_typo)] +#[diag(parser_in_in_typo)] pub(crate) struct InInTypo { #[primary_span] pub span: Span, @@ -100,7 +100,7 @@ pub(crate) struct InInTypo { } #[derive(Diagnostic)] -#[diag(parser::invalid_variable_declaration)] +#[diag(parser_invalid_variable_declaration)] pub(crate) struct InvalidVariableDeclaration { #[primary_span] pub span: Span, @@ -110,26 +110,22 @@ pub(crate) struct InvalidVariableDeclaration { #[derive(Subdiagnostic)] pub(crate) enum InvalidVariableDeclarationSub { - #[suggestion( - parser::switch_mut_let_order, - applicability = "maybe-incorrect", - code = "let mut" - )] + #[suggestion(parser_switch_mut_let_order, applicability = "maybe-incorrect", code = "let mut")] SwitchMutLetOrder(#[primary_span] Span), #[suggestion( - parser::missing_let_before_mut, + parser_missing_let_before_mut, applicability = "machine-applicable", code = "let mut" )] MissingLet(#[primary_span] Span), - #[suggestion(parser::use_let_not_auto, applicability = "machine-applicable", code = "let")] + #[suggestion(parser_use_let_not_auto, applicability = "machine-applicable", code = "let")] UseLetNotAuto(#[primary_span] Span), - #[suggestion(parser::use_let_not_var, applicability = "machine-applicable", code = "let")] + #[suggestion(parser_use_let_not_var, applicability = "machine-applicable", code = "let")] UseLetNotVar(#[primary_span] Span), } #[derive(Diagnostic)] -#[diag(parser::invalid_comparison_operator)] +#[diag(parser_invalid_comparison_operator)] pub(crate) struct InvalidComparisonOperator { #[primary_span] pub span: Span, @@ -140,8 +136,9 @@ pub(crate) struct InvalidComparisonOperator { #[derive(Subdiagnostic)] pub(crate) enum InvalidComparisonOperatorSub { - #[suggestion_short( - parser::use_instead, + #[suggestion( + use_instead, + style = "short", applicability = "machine-applicable", code = "{correct}" )] @@ -151,12 +148,12 @@ pub(crate) enum InvalidComparisonOperatorSub { invalid: String, correct: String, }, - #[label(parser::spaceship_operator_invalid)] + #[label(spaceship_operator_invalid)] Spaceship(#[primary_span] Span), } #[derive(Diagnostic)] -#[diag(parser::invalid_logical_operator)] +#[diag(parser_invalid_logical_operator)] #[note] pub(crate) struct InvalidLogicalOperator { #[primary_span] @@ -168,14 +165,16 @@ pub(crate) struct InvalidLogicalOperator { #[derive(Subdiagnostic)] pub(crate) enum InvalidLogicalOperatorSub { - #[suggestion_short( - parser::use_amp_amp_for_conjunction, + #[suggestion( + use_amp_amp_for_conjunction, + style = "short", applicability = "machine-applicable", code = "&&" )] Conjunction(#[primary_span] Span), - #[suggestion_short( - parser::use_pipe_pipe_for_disjunction, + #[suggestion( + use_pipe_pipe_for_disjunction, + style = "short", applicability = "machine-applicable", code = "||" )] @@ -183,15 +182,15 @@ pub(crate) enum InvalidLogicalOperatorSub { } #[derive(Diagnostic)] -#[diag(parser::tilde_is_not_unary_operator)] +#[diag(parser_tilde_is_not_unary_operator)] pub(crate) struct TildeAsUnaryOperator( #[primary_span] - #[suggestion_short(applicability = "machine-applicable", code = "!")] + #[suggestion(style = "short", applicability = "machine-applicable", code = "!")] pub Span, ); #[derive(Diagnostic)] -#[diag(parser::unexpected_token_after_not)] +#[diag(parser_unexpected_token_after_not)] pub(crate) struct NotAsNegationOperator { #[primary_span] pub negated: Span, @@ -202,22 +201,25 @@ pub(crate) struct NotAsNegationOperator { #[derive(Subdiagnostic)] pub enum NotAsNegationOperatorSub { - #[suggestion_short( - parser::unexpected_token_after_not_default, + #[suggestion( + parser_unexpected_token_after_not_default, + style = "short", applicability = "machine-applicable", code = "!" )] SuggestNotDefault(#[primary_span] Span), - #[suggestion_short( - parser::unexpected_token_after_not_bitwise, + #[suggestion( + parser_unexpected_token_after_not_bitwise, + style = "short", applicability = "machine-applicable", code = "!" )] SuggestNotBitwise(#[primary_span] Span), - #[suggestion_short( - parser::unexpected_token_after_not_logical, + #[suggestion( + parser_unexpected_token_after_not_logical, + style = "short", applicability = "machine-applicable", code = "!" )] @@ -225,7 +227,7 @@ pub enum NotAsNegationOperatorSub { } #[derive(Diagnostic)] -#[diag(parser::malformed_loop_label)] +#[diag(parser_malformed_loop_label)] pub(crate) struct MalformedLoopLabel { #[primary_span] #[suggestion(applicability = "machine-applicable", code = "{correct_label}")] @@ -234,7 +236,7 @@ pub(crate) struct MalformedLoopLabel { } #[derive(Diagnostic)] -#[diag(parser::lifetime_in_borrow_expression)] +#[diag(parser_lifetime_in_borrow_expression)] pub(crate) struct LifetimeInBorrowExpression { #[primary_span] pub span: Span, @@ -244,27 +246,27 @@ pub(crate) struct LifetimeInBorrowExpression { } #[derive(Diagnostic)] -#[diag(parser::field_expression_with_generic)] +#[diag(parser_field_expression_with_generic)] pub(crate) struct FieldExpressionWithGeneric(#[primary_span] pub Span); #[derive(Diagnostic)] -#[diag(parser::macro_invocation_with_qualified_path)] +#[diag(parser_macro_invocation_with_qualified_path)] pub(crate) struct MacroInvocationWithQualifiedPath(#[primary_span] pub Span); #[derive(Diagnostic)] -#[diag(parser::unexpected_token_after_label)] +#[diag(parser_unexpected_token_after_label)] pub(crate) struct UnexpectedTokenAfterLabel { #[primary_span] - #[label(parser::unexpected_token_after_label)] + #[label(parser_unexpected_token_after_label)] pub span: Span, - #[suggestion_verbose(parser::suggestion_remove_label, code = "")] + #[suggestion(suggestion_remove_label, style = "verbose", code = "")] pub remove_label: Option<Span>, #[subdiagnostic] pub enclose_in_block: Option<UnexpectedTokenAfterLabelSugg>, } #[derive(Subdiagnostic)] -#[multipart_suggestion(parser::suggestion_enclose_in_block, applicability = "machine-applicable")] +#[multipart_suggestion(suggestion_enclose_in_block, applicability = "machine-applicable")] pub(crate) struct UnexpectedTokenAfterLabelSugg { #[suggestion_part(code = "{{ ")] pub left: Span, @@ -273,19 +275,19 @@ pub(crate) struct UnexpectedTokenAfterLabelSugg { } #[derive(Diagnostic)] -#[diag(parser::require_colon_after_labeled_expression)] +#[diag(parser_require_colon_after_labeled_expression)] #[note] pub(crate) struct RequireColonAfterLabeledExpression { #[primary_span] pub span: Span, #[label] pub label: Span, - #[suggestion_short(applicability = "machine-applicable", code = ": ")] + #[suggestion(style = "short", applicability = "machine-applicable", code = ": ")] pub label_end: Span, } #[derive(Diagnostic)] -#[diag(parser::do_catch_syntax_removed)] +#[diag(parser_do_catch_syntax_removed)] #[note] pub(crate) struct DoCatchSyntaxRemoved { #[primary_span] @@ -294,7 +296,7 @@ pub(crate) struct DoCatchSyntaxRemoved { } #[derive(Diagnostic)] -#[diag(parser::float_literal_requires_integer_part)] +#[diag(parser_float_literal_requires_integer_part)] pub(crate) struct FloatLiteralRequiresIntegerPart { #[primary_span] #[suggestion(applicability = "machine-applicable", code = "{correct}")] @@ -303,71 +305,25 @@ pub(crate) struct FloatLiteralRequiresIntegerPart { } #[derive(Diagnostic)] -#[diag(parser::invalid_int_literal_width)] -#[help] -pub(crate) struct InvalidIntLiteralWidth { - #[primary_span] - pub span: Span, - pub width: String, -} - -#[derive(Diagnostic)] -#[diag(parser::invalid_num_literal_base_prefix)] -#[note] -pub(crate) struct InvalidNumLiteralBasePrefix { - #[primary_span] - #[suggestion(applicability = "maybe-incorrect", code = "{fixed}")] - pub span: Span, - pub fixed: String, -} - -#[derive(Diagnostic)] -#[diag(parser::invalid_num_literal_suffix)] -#[help] -pub(crate) struct InvalidNumLiteralSuffix { - #[primary_span] - #[label] - pub span: Span, - pub suffix: String, -} - -#[derive(Diagnostic)] -#[diag(parser::invalid_float_literal_width)] -#[help] -pub(crate) struct InvalidFloatLiteralWidth { - #[primary_span] - pub span: Span, - pub width: String, -} - -#[derive(Diagnostic)] -#[diag(parser::invalid_float_literal_suffix)] -#[help] -pub(crate) struct InvalidFloatLiteralSuffix { - #[primary_span] - #[label] - pub span: Span, - pub suffix: String, -} - -#[derive(Diagnostic)] -#[diag(parser::int_literal_too_large)] -pub(crate) struct IntLiteralTooLarge { +#[diag(parser_missing_semicolon_before_array)] +pub(crate) struct MissingSemicolonBeforeArray { #[primary_span] - pub span: Span, + pub open_delim: Span, + #[suggestion(style = "verbose", applicability = "maybe-incorrect", code = ";")] + pub semicolon: Span, } #[derive(Diagnostic)] -#[diag(parser::missing_semicolon_before_array)] -pub(crate) struct MissingSemicolonBeforeArray { +#[diag(parser_expect_dotdot_not_dotdotdot)] +pub(crate) struct MissingDotDot { #[primary_span] - pub open_delim: Span, - #[suggestion_verbose(applicability = "maybe-incorrect", code = ";")] - pub semicolon: Span, + pub token_span: Span, + #[suggestion(applicability = "maybe-incorrect", code = "..", style = "verbose")] + pub sugg_span: Span, } #[derive(Diagnostic)] -#[diag(parser::invalid_block_macro_segment)] +#[diag(parser_invalid_block_macro_segment)] pub(crate) struct InvalidBlockMacroSegment { #[primary_span] pub span: Span, @@ -376,7 +332,7 @@ pub(crate) struct InvalidBlockMacroSegment { } #[derive(Diagnostic)] -#[diag(parser::if_expression_missing_then_block)] +#[diag(parser_if_expression_missing_then_block)] pub(crate) struct IfExpressionMissingThenBlock { #[primary_span] pub if_span: Span, @@ -386,31 +342,40 @@ pub(crate) struct IfExpressionMissingThenBlock { #[derive(Subdiagnostic)] pub(crate) enum IfExpressionMissingThenBlockSub { - #[help(parser::condition_possibly_unfinished)] + #[help(condition_possibly_unfinished)] UnfinishedCondition(#[primary_span] Span), - #[help(parser::add_then_block)] + #[help(add_then_block)] AddThenBlock(#[primary_span] Span), } #[derive(Diagnostic)] -#[diag(parser::if_expression_missing_condition)] +#[diag(parser_if_expression_missing_condition)] pub(crate) struct IfExpressionMissingCondition { #[primary_span] - #[label(parser::condition_label)] + #[label(condition_label)] pub if_span: Span, - #[label(parser::block_label)] + #[label(block_label)] pub block_span: Span, } #[derive(Diagnostic)] -#[diag(parser::expected_expression_found_let)] +#[diag(parser_expected_expression_found_let)] pub(crate) struct ExpectedExpressionFoundLet { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parser::expected_else_block)] +#[diag(parser_expect_eq_instead_of_eqeq)] +pub(crate) struct ExpectedEqForLetExpr { + #[primary_span] + pub span: Span, + #[suggestion(applicability = "maybe-incorrect", code = "=", style = "verbose")] + pub sugg_span: Span, +} + +#[derive(Diagnostic)] +#[diag(parser_expected_else_block)] pub(crate) struct ExpectedElseBlock { #[primary_span] pub first_tok_span: Span, @@ -422,15 +387,15 @@ pub(crate) struct ExpectedElseBlock { } #[derive(Diagnostic)] -#[diag(parser::outer_attribute_not_allowed_on_if_else)] +#[diag(parser_outer_attribute_not_allowed_on_if_else)] pub(crate) struct OuterAttributeNotAllowedOnIfElse { #[primary_span] pub last: Span, - #[label(parser::branch_label)] + #[label(branch_label)] pub branch_span: Span, - #[label(parser::ctx_label)] + #[label(ctx_label)] pub ctx_span: Span, pub ctx: String, @@ -439,7 +404,7 @@ pub(crate) struct OuterAttributeNotAllowedOnIfElse { } #[derive(Diagnostic)] -#[diag(parser::missing_in_in_for_loop)] +#[diag(parser_missing_in_in_for_loop)] pub(crate) struct MissingInInForLoop { #[primary_span] pub span: Span, @@ -450,14 +415,14 @@ 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_short(parser::use_in_not_of, applicability = "maybe-incorrect", code = "in")] + #[suggestion(use_in_not_of, style = "short", applicability = "maybe-incorrect", code = "in")] InNotOf(#[primary_span] Span), - #[suggestion_short(parser::add_in, applicability = "maybe-incorrect", code = " in ")] + #[suggestion(add_in, style = "short", applicability = "maybe-incorrect", code = " in ")] AddIn(#[primary_span] Span), } #[derive(Diagnostic)] -#[diag(parser::missing_comma_after_match_arm)] +#[diag(parser_missing_comma_after_match_arm)] pub(crate) struct MissingCommaAfterMatchArm { #[primary_span] #[suggestion(applicability = "machine-applicable", code = ",")] @@ -465,7 +430,7 @@ pub(crate) struct MissingCommaAfterMatchArm { } #[derive(Diagnostic)] -#[diag(parser::catch_after_try)] +#[diag(parser_catch_after_try)] #[help] pub(crate) struct CatchAfterTry { #[primary_span] @@ -473,17 +438,17 @@ pub(crate) struct CatchAfterTry { } #[derive(Diagnostic)] -#[diag(parser::comma_after_base_struct)] +#[diag(parser_comma_after_base_struct)] #[note] pub(crate) struct CommaAfterBaseStruct { #[primary_span] pub span: Span, - #[suggestion_short(applicability = "machine-applicable", code = "")] + #[suggestion(style = "short", applicability = "machine-applicable", code = "")] pub comma: Span, } #[derive(Diagnostic)] -#[diag(parser::eq_field_init)] +#[diag(parser_eq_field_init)] pub(crate) struct EqFieldInit { #[primary_span] pub span: Span, @@ -492,16 +457,16 @@ pub(crate) struct EqFieldInit { } #[derive(Diagnostic)] -#[diag(parser::dotdotdot)] +#[diag(parser_dotdotdot)] pub(crate) struct DotDotDot { #[primary_span] - #[suggestion(parser::suggest_exclusive_range, applicability = "maybe-incorrect", code = "..")] - #[suggestion(parser::suggest_inclusive_range, applicability = "maybe-incorrect", code = "..=")] + #[suggestion(suggest_exclusive_range, applicability = "maybe-incorrect", code = "..")] + #[suggestion(suggest_inclusive_range, applicability = "maybe-incorrect", code = "..=")] pub span: Span, } #[derive(Diagnostic)] -#[diag(parser::left_arrow_operator)] +#[diag(parser_left_arrow_operator)] pub(crate) struct LeftArrowOperator { #[primary_span] #[suggestion(applicability = "maybe-incorrect", code = "< -")] @@ -509,7 +474,7 @@ pub(crate) struct LeftArrowOperator { } #[derive(Diagnostic)] -#[diag(parser::remove_let)] +#[diag(parser_remove_let)] pub(crate) struct RemoveLet { #[primary_span] #[suggestion(applicability = "machine-applicable", code = "")] @@ -517,49 +482,49 @@ pub(crate) struct RemoveLet { } #[derive(Diagnostic)] -#[diag(parser::use_eq_instead)] +#[diag(parser_use_eq_instead)] pub(crate) struct UseEqInstead { #[primary_span] - #[suggestion_short(applicability = "machine-applicable", code = "=")] + #[suggestion(style = "short", applicability = "machine-applicable", code = "=")] pub span: Span, } #[derive(Diagnostic)] -#[diag(parser::use_empty_block_not_semi)] +#[diag(parser_use_empty_block_not_semi)] pub(crate) struct UseEmptyBlockNotSemi { #[primary_span] - #[suggestion_hidden(applicability = "machine-applicable", code = "{{}}")] + #[suggestion(style = "hidden", applicability = "machine-applicable", code = "{{}}")] pub span: Span, } #[derive(Diagnostic)] -#[diag(parser::comparison_interpreted_as_generic)] +#[diag(parser_comparison_interpreted_as_generic)] pub(crate) struct ComparisonInterpretedAsGeneric { #[primary_span] - #[label(parser::label_comparison)] + #[label(label_comparison)] pub comparison: Span, pub r#type: Path, - #[label(parser::label_args)] + #[label(label_args)] pub args: Span, #[subdiagnostic] pub suggestion: ComparisonOrShiftInterpretedAsGenericSugg, } #[derive(Diagnostic)] -#[diag(parser::shift_interpreted_as_generic)] +#[diag(parser_shift_interpreted_as_generic)] pub(crate) struct ShiftInterpretedAsGeneric { #[primary_span] - #[label(parser::label_comparison)] + #[label(label_comparison)] pub shift: Span, pub r#type: Path, - #[label(parser::label_args)] + #[label(label_args)] pub args: Span, #[subdiagnostic] pub suggestion: ComparisonOrShiftInterpretedAsGenericSugg, } #[derive(Subdiagnostic)] -#[multipart_suggestion(parser::suggestion, applicability = "machine-applicable")] +#[multipart_suggestion(suggestion, applicability = "machine-applicable")] pub(crate) struct ComparisonOrShiftInterpretedAsGenericSugg { #[suggestion_part(code = "(")] pub left: Span, @@ -568,7 +533,7 @@ pub(crate) struct ComparisonOrShiftInterpretedAsGenericSugg { } #[derive(Diagnostic)] -#[diag(parser::found_expr_would_be_stmt)] +#[diag(parser_found_expr_would_be_stmt)] pub(crate) struct FoundExprWouldBeStmt { #[primary_span] #[label] @@ -579,13 +544,14 @@ pub(crate) struct FoundExprWouldBeStmt { } #[derive(Diagnostic)] -#[diag(parser::leading_plus_not_supported)] +#[diag(parser_leading_plus_not_supported)] pub(crate) struct LeadingPlusNotSupported { #[primary_span] #[label] pub span: Span, - #[suggestion_verbose( - parser::suggestion_remove_plus, + #[suggestion( + suggestion_remove_plus, + style = "verbose", code = "", applicability = "machine-applicable" )] @@ -595,7 +561,7 @@ pub(crate) struct LeadingPlusNotSupported { } #[derive(Diagnostic)] -#[diag(parser::parentheses_with_struct_fields)] +#[diag(parser_parentheses_with_struct_fields)] pub(crate) struct ParenthesesWithStructFields { #[primary_span] pub span: Span, @@ -607,7 +573,7 @@ pub(crate) struct ParenthesesWithStructFields { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parser::suggestion_braces_for_struct, applicability = "maybe-incorrect")] +#[multipart_suggestion(suggestion_braces_for_struct, applicability = "maybe-incorrect")] pub(crate) struct BracesForStructLiteral { #[suggestion_part(code = " {{ ")] pub first: Span, @@ -616,14 +582,14 @@ pub(crate) struct BracesForStructLiteral { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parser::suggestion_no_fields_for_fn, applicability = "maybe-incorrect")] +#[multipart_suggestion(suggestion_no_fields_for_fn, applicability = "maybe-incorrect")] pub(crate) struct NoFieldsForFnCall { #[suggestion_part(code = "")] pub fields: Vec<Span>, } #[derive(Diagnostic)] -#[diag(parser::labeled_loop_in_break)] +#[diag(parser_labeled_loop_in_break)] pub(crate) struct LabeledLoopInBreak { #[primary_span] pub span: Span, @@ -633,7 +599,7 @@ pub(crate) struct LabeledLoopInBreak { #[derive(Subdiagnostic)] #[multipart_suggestion( - parser::sugg_wrap_expression_in_parentheses, + parser_sugg_wrap_expression_in_parentheses, applicability = "machine-applicable" )] pub(crate) struct WrapExpressionInParentheses { @@ -644,7 +610,7 @@ pub(crate) struct WrapExpressionInParentheses { } #[derive(Diagnostic)] -#[diag(parser::array_brackets_instead_of_braces)] +#[diag(parser_array_brackets_instead_of_braces)] pub(crate) struct ArrayBracketsInsteadOfSpaces { #[primary_span] pub span: Span, @@ -653,7 +619,7 @@ pub(crate) struct ArrayBracketsInsteadOfSpaces { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parser::suggestion, applicability = "maybe-incorrect")] +#[multipart_suggestion(suggestion, applicability = "maybe-incorrect")] pub(crate) struct ArrayBracketsInsteadOfSpacesSugg { #[suggestion_part(code = "[")] pub left: Span, @@ -662,12 +628,12 @@ pub(crate) struct ArrayBracketsInsteadOfSpacesSugg { } #[derive(Diagnostic)] -#[diag(parser::match_arm_body_without_braces)] +#[diag(parser_match_arm_body_without_braces)] pub(crate) struct MatchArmBodyWithoutBraces { #[primary_span] - #[label(parser::label_statements)] + #[label(label_statements)] pub statements: Span, - #[label(parser::label_arrow)] + #[label(label_arrow)] pub arrow: Span, pub num_statements: usize, #[subdiagnostic] @@ -676,7 +642,7 @@ pub(crate) struct MatchArmBodyWithoutBraces { #[derive(Subdiagnostic)] pub(crate) enum MatchArmBodyWithoutBracesSugg { - #[multipart_suggestion(parser::suggestion_add_braces, applicability = "machine-applicable")] + #[multipart_suggestion(suggestion_add_braces, applicability = "machine-applicable")] AddBraces { #[suggestion_part(code = "{{ ")] left: Span, @@ -684,7 +650,7 @@ pub(crate) enum MatchArmBodyWithoutBracesSugg { right: Span, }, #[suggestion( - parser::suggestion_use_comma_not_semicolon, + suggestion_use_comma_not_semicolon, code = ",", applicability = "machine-applicable" )] @@ -695,7 +661,7 @@ pub(crate) enum MatchArmBodyWithoutBracesSugg { } #[derive(Diagnostic)] -#[diag(parser::struct_literal_not_allowed_here)] +#[diag(parser_struct_literal_not_allowed_here)] pub(crate) struct StructLiteralNotAllowedHere { #[primary_span] pub span: Span, @@ -704,7 +670,7 @@ pub(crate) struct StructLiteralNotAllowedHere { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parser::suggestion, applicability = "machine-applicable")] +#[multipart_suggestion(suggestion, applicability = "machine-applicable")] pub(crate) struct StructLiteralNotAllowedHereSugg { #[suggestion_part(code = "(")] pub left: Span, @@ -713,62 +679,27 @@ pub(crate) struct StructLiteralNotAllowedHereSugg { } #[derive(Diagnostic)] -#[diag(parser::invalid_interpolated_expression)] +#[diag(parser_invalid_interpolated_expression)] pub(crate) struct InvalidInterpolatedExpression { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parser::hexadecimal_float_literal_not_supported)] -pub(crate) struct HexadecimalFloatLiteralNotSupported { - #[primary_span] - #[label(parser::not_supported)] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(parser::octal_float_literal_not_supported)] -pub(crate) struct OctalFloatLiteralNotSupported { - #[primary_span] - #[label(parser::not_supported)] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(parser::binary_float_literal_not_supported)] -pub(crate) struct BinaryFloatLiteralNotSupported { - #[primary_span] - #[label(parser::not_supported)] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(parser::invalid_literal_suffix)] -pub(crate) struct InvalidLiteralSuffix { - #[primary_span] - #[label] - pub span: Span, - // FIXME(#100717) - pub kind: String, - pub suffix: Symbol, -} - -#[derive(Diagnostic)] -#[diag(parser::invalid_literal_suffix_on_tuple_index)] +#[diag(parser_invalid_literal_suffix_on_tuple_index)] pub(crate) struct InvalidLiteralSuffixOnTupleIndex { #[primary_span] #[label] pub span: Span, pub suffix: Symbol, - #[help(parser::tuple_exception_line_1)] - #[help(parser::tuple_exception_line_2)] - #[help(parser::tuple_exception_line_3)] + #[help(tuple_exception_line_1)] + #[help(tuple_exception_line_2)] + #[help(tuple_exception_line_3)] pub exception: Option<()>, } #[derive(Diagnostic)] -#[diag(parser::non_string_abi_literal)] +#[diag(parser_non_string_abi_literal)] pub(crate) struct NonStringAbiLiteral { #[primary_span] #[suggestion(code = "\"C\"", applicability = "maybe-incorrect")] @@ -776,21 +707,21 @@ pub(crate) struct NonStringAbiLiteral { } #[derive(Diagnostic)] -#[diag(parser::mismatched_closing_delimiter)] +#[diag(parser_mismatched_closing_delimiter)] pub(crate) struct MismatchedClosingDelimiter { #[primary_span] pub spans: Vec<Span>, pub delimiter: String, - #[label(parser::label_unmatched)] + #[label(label_unmatched)] pub unmatched: Span, - #[label(parser::label_opening_candidate)] + #[label(label_opening_candidate)] pub opening_candidate: Option<Span>, - #[label(parser::label_unclosed)] + #[label(label_unclosed)] pub unclosed: Option<Span>, } #[derive(Diagnostic)] -#[diag(parser::incorrect_visibility_restriction, code = "E0704")] +#[diag(parser_incorrect_visibility_restriction, code = "E0704")] #[help] pub(crate) struct IncorrectVisibilityRestriction { #[primary_span] @@ -800,21 +731,21 @@ pub(crate) struct IncorrectVisibilityRestriction { } #[derive(Diagnostic)] -#[diag(parser::assignment_else_not_allowed)] +#[diag(parser_assignment_else_not_allowed)] pub(crate) struct AssignmentElseNotAllowed { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parser::expected_statement_after_outer_attr)] +#[diag(parser_expected_statement_after_outer_attr)] pub(crate) struct ExpectedStatementAfterOuterAttr { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(parser::doc_comment_does_not_document_anything, code = "E0585")] +#[diag(parser_doc_comment_does_not_document_anything, code = "E0585")] #[help] pub(crate) struct DocCommentDoesNotDocumentAnything { #[primary_span] @@ -824,7 +755,7 @@ pub(crate) struct DocCommentDoesNotDocumentAnything { } #[derive(Diagnostic)] -#[diag(parser::const_let_mutually_exclusive)] +#[diag(parser_const_let_mutually_exclusive)] pub(crate) struct ConstLetMutuallyExclusive { #[primary_span] #[suggestion(code = "const", applicability = "maybe-incorrect")] @@ -832,7 +763,7 @@ pub(crate) struct ConstLetMutuallyExclusive { } #[derive(Diagnostic)] -#[diag(parser::invalid_expression_in_let_else)] +#[diag(parser_invalid_expression_in_let_else)] pub(crate) struct InvalidExpressionInLetElse { #[primary_span] pub span: Span, @@ -842,7 +773,7 @@ pub(crate) struct InvalidExpressionInLetElse { } #[derive(Diagnostic)] -#[diag(parser::invalid_curly_in_let_else)] +#[diag(parser_invalid_curly_in_let_else)] pub(crate) struct InvalidCurlyInLetElse { #[primary_span] pub span: Span, @@ -851,16 +782,16 @@ pub(crate) struct InvalidCurlyInLetElse { } #[derive(Diagnostic)] -#[diag(parser::compound_assignment_expression_in_let)] +#[diag(parser_compound_assignment_expression_in_let)] #[help] pub(crate) struct CompoundAssignmentExpressionInLet { #[primary_span] - #[suggestion_short(code = "=", applicability = "maybe-incorrect")] + #[suggestion(style = "short", code = "=", applicability = "maybe-incorrect")] pub span: Span, } #[derive(Diagnostic)] -#[diag(parser::suffixed_literal_in_attribute)] +#[diag(parser_suffixed_literal_in_attribute)] #[help] pub(crate) struct SuffixedLiteralInAttribute { #[primary_span] @@ -868,7 +799,7 @@ pub(crate) struct SuffixedLiteralInAttribute { } #[derive(Diagnostic)] -#[diag(parser::invalid_meta_item)] +#[diag(parser_invalid_meta_item)] pub(crate) struct InvalidMetaItem { #[primary_span] pub span: Span, @@ -876,8 +807,9 @@ pub(crate) struct InvalidMetaItem { } #[derive(Subdiagnostic)] -#[suggestion_verbose( - parser::sugg_escape_to_use_as_identifier, +#[suggestion( + parser_sugg_escape_to_use_as_identifier, + style = "verbose", applicability = "maybe-incorrect", code = "r#" )] @@ -888,7 +820,7 @@ pub(crate) struct SuggEscapeToUseAsIdentifier { } #[derive(Subdiagnostic)] -#[suggestion(parser::sugg_remove_comma, applicability = "machine-applicable", code = "")] +#[suggestion(parser_sugg_remove_comma, applicability = "machine-applicable", code = "")] pub(crate) struct SuggRemoveComma { #[primary_span] pub span: Span, @@ -896,15 +828,15 @@ pub(crate) struct SuggRemoveComma { #[derive(Subdiagnostic)] pub(crate) enum ExpectedIdentifierFound { - #[label(parser::expected_identifier_found_reserved_identifier)] + #[label(parser_expected_identifier_found_reserved_identifier)] ReservedIdentifier(#[primary_span] Span), - #[label(parser::expected_identifier_found_keyword)] + #[label(parser_expected_identifier_found_keyword)] Keyword(#[primary_span] Span), - #[label(parser::expected_identifier_found_reserved_keyword)] + #[label(parser_expected_identifier_found_reserved_keyword)] ReservedKeyword(#[primary_span] Span), - #[label(parser::expected_identifier_found_doc_comment)] + #[label(parser_expected_identifier_found_doc_comment)] DocComment(#[primary_span] Span), - #[label(parser::expected_identifier)] + #[label(parser_expected_identifier)] Other(#[primary_span] Span), } @@ -930,6 +862,7 @@ pub(crate) struct ExpectedIdentifier { } impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for ExpectedIdentifier { + #[track_caller] fn into_diagnostic( self, handler: &'a rustc_errors::Handler, @@ -938,18 +871,16 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for ExpectedIdentifier { let mut diag = handler.struct_diagnostic(match token_descr { Some(TokenDescription::ReservedIdentifier) => { - fluent::parser::expected_identifier_found_reserved_identifier_str - } - Some(TokenDescription::Keyword) => { - fluent::parser::expected_identifier_found_keyword_str + fluent::parser_expected_identifier_found_reserved_identifier_str } + Some(TokenDescription::Keyword) => fluent::parser_expected_identifier_found_keyword_str, Some(TokenDescription::ReservedKeyword) => { - fluent::parser::expected_identifier_found_reserved_keyword_str + fluent::parser_expected_identifier_found_reserved_keyword_str } Some(TokenDescription::DocComment) => { - fluent::parser::expected_identifier_found_doc_comment_str + fluent::parser_expected_identifier_found_doc_comment_str } - None => fluent::parser::expected_identifier_found_str, + None => fluent::parser_expected_identifier_found_str, }); diag.set_span(self.span); diag.set_arg("token", self.token); @@ -977,6 +908,7 @@ pub(crate) struct ExpectedSemi { } impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for ExpectedSemi { + #[track_caller] fn into_diagnostic( self, handler: &'a rustc_errors::Handler, @@ -985,22 +917,22 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for ExpectedSemi { let mut diag = handler.struct_diagnostic(match token_descr { Some(TokenDescription::ReservedIdentifier) => { - fluent::parser::expected_semi_found_reserved_identifier_str + fluent::parser_expected_semi_found_reserved_identifier_str } - Some(TokenDescription::Keyword) => fluent::parser::expected_semi_found_keyword_str, + Some(TokenDescription::Keyword) => fluent::parser_expected_semi_found_keyword_str, Some(TokenDescription::ReservedKeyword) => { - fluent::parser::expected_semi_found_reserved_keyword_str + fluent::parser_expected_semi_found_reserved_keyword_str } Some(TokenDescription::DocComment) => { - fluent::parser::expected_semi_found_doc_comment_str + fluent::parser_expected_semi_found_doc_comment_str } - None => fluent::parser::expected_semi_found_str, + None => fluent::parser_expected_semi_found_str, }); diag.set_span(self.span); diag.set_arg("token", self.token); if let Some(unexpected_token_label) = self.unexpected_token_label { - diag.span_label(unexpected_token_label, fluent::parser::label_unexpected_token); + diag.span_label(unexpected_token_label, fluent::parser_label_unexpected_token); } self.sugg.add_to_diagnostic(&mut diag); @@ -1012,17 +944,22 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for ExpectedSemi { #[derive(Subdiagnostic)] pub(crate) enum ExpectedSemiSugg { #[suggestion( - parser::sugg_change_this_to_semi, + parser_sugg_change_this_to_semi, code = ";", applicability = "machine-applicable" )] ChangeToSemi(#[primary_span] Span), - #[suggestion_short(parser::sugg_add_semi, code = ";", applicability = "machine-applicable")] + #[suggestion( + parser_sugg_add_semi, + style = "short", + code = ";", + applicability = "machine-applicable" + )] AddSemi(#[primary_span] Span), } #[derive(Diagnostic)] -#[diag(parser::struct_literal_body_without_path)] +#[diag(parser_struct_literal_body_without_path)] pub(crate) struct StructLiteralBodyWithoutPath { #[primary_span] pub span: Span, @@ -1031,7 +968,7 @@ pub(crate) struct StructLiteralBodyWithoutPath { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parser::suggestion, applicability = "has-placeholders")] +#[multipart_suggestion(suggestion, applicability = "has-placeholders")] pub(crate) struct StructLiteralBodyWithoutPathSugg { #[suggestion_part(code = "{{ SomeStruct ")] pub before: Span, @@ -1040,7 +977,7 @@ pub(crate) struct StructLiteralBodyWithoutPathSugg { } #[derive(Diagnostic)] -#[diag(parser::unmatched_angle_brackets)] +#[diag(parser_unmatched_angle_brackets)] pub(crate) struct UnmatchedAngleBrackets { #[primary_span] #[suggestion(code = "", applicability = "machine-applicable")] @@ -1049,7 +986,7 @@ pub(crate) struct UnmatchedAngleBrackets { } #[derive(Diagnostic)] -#[diag(parser::generic_parameters_without_angle_brackets)] +#[diag(parser_generic_parameters_without_angle_brackets)] pub(crate) struct GenericParamsWithoutAngleBrackets { #[primary_span] pub span: Span, @@ -1058,7 +995,7 @@ pub(crate) struct GenericParamsWithoutAngleBrackets { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parser::suggestion, applicability = "machine-applicable")] +#[multipart_suggestion(suggestion, applicability = "machine-applicable")] pub(crate) struct GenericParamsWithoutAngleBracketsSugg { #[suggestion_part(code = "<")] pub left: Span, @@ -1067,18 +1004,19 @@ pub(crate) struct GenericParamsWithoutAngleBracketsSugg { } #[derive(Diagnostic)] -#[diag(parser::comparison_operators_cannot_be_chained)] +#[diag(parser_comparison_operators_cannot_be_chained)] pub(crate) struct ComparisonOperatorsCannotBeChained { #[primary_span] pub span: Vec<Span>, - #[suggestion_verbose( - parser::sugg_turbofish_syntax, + #[suggestion( + parser_sugg_turbofish_syntax, + style = "verbose", code = "::", applicability = "maybe-incorrect" )] pub suggest_turbofish: Option<Span>, - #[help(parser::sugg_turbofish_syntax)] - #[help(parser::sugg_parentheses_for_function_args)] + #[help(parser_sugg_turbofish_syntax)] + #[help(sugg_parentheses_for_function_args)] pub help_turbofish: Option<()>, #[subdiagnostic] pub chaining_sugg: Option<ComparisonOperatorsCannotBeChainedSugg>, @@ -1086,8 +1024,9 @@ pub(crate) struct ComparisonOperatorsCannotBeChained { #[derive(Subdiagnostic)] pub(crate) enum ComparisonOperatorsCannotBeChainedSugg { - #[suggestion_verbose( - parser::sugg_split_comparison, + #[suggestion( + sugg_split_comparison, + style = "verbose", code = " && {middle_term}", applicability = "maybe-incorrect" )] @@ -1096,7 +1035,7 @@ pub(crate) enum ComparisonOperatorsCannotBeChainedSugg { span: Span, middle_term: String, }, - #[multipart_suggestion(parser::sugg_parenthesize, applicability = "maybe-incorrect")] + #[multipart_suggestion(sugg_parenthesize, applicability = "maybe-incorrect")] Parenthesize { #[suggestion_part(code = "(")] left: Span, @@ -1106,7 +1045,7 @@ pub(crate) enum ComparisonOperatorsCannotBeChainedSugg { } #[derive(Diagnostic)] -#[diag(parser::question_mark_in_type)] +#[diag(parser_question_mark_in_type)] pub(crate) struct QuestionMarkInType { #[primary_span] #[label] @@ -1116,7 +1055,7 @@ pub(crate) struct QuestionMarkInType { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parser::suggestion, applicability = "machine-applicable")] +#[multipart_suggestion(suggestion, applicability = "machine-applicable")] pub(crate) struct QuestionMarkInTypeSugg { #[suggestion_part(code = "Option<")] pub left: Span, @@ -1125,7 +1064,7 @@ pub(crate) struct QuestionMarkInTypeSugg { } #[derive(Diagnostic)] -#[diag(parser::unexpected_parentheses_in_for_head)] +#[diag(parser_unexpected_parentheses_in_for_head)] pub(crate) struct ParenthesesInForHead { #[primary_span] pub span: Vec<Span>, @@ -1134,16 +1073,18 @@ pub(crate) struct ParenthesesInForHead { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parser::suggestion, applicability = "machine-applicable")] +#[multipart_suggestion(suggestion, applicability = "machine-applicable")] pub(crate) struct ParenthesesInForHeadSugg { - #[suggestion_part(code = "")] + #[suggestion_part(code = "{left_snippet}")] pub left: Span, - #[suggestion_part(code = "")] + pub left_snippet: String, + #[suggestion_part(code = "{right_snippet}")] pub right: Span, + pub right_snippet: String, } #[derive(Diagnostic)] -#[diag(parser::doc_comment_on_param_type)] +#[diag(parser_doc_comment_on_param_type)] pub(crate) struct DocCommentOnParamType { #[primary_span] #[label] @@ -1151,7 +1092,7 @@ pub(crate) struct DocCommentOnParamType { } #[derive(Diagnostic)] -#[diag(parser::attribute_on_param_type)] +#[diag(parser_attribute_on_param_type)] pub(crate) struct AttributeOnParamType { #[primary_span] #[label] @@ -1159,7 +1100,7 @@ pub(crate) struct AttributeOnParamType { } #[derive(Diagnostic)] -#[diag(parser::pattern_method_param_without_body, code = "E0642")] +#[diag(parser_pattern_method_param_without_body, code = "E0642")] pub(crate) struct PatternMethodParamWithoutBody { #[primary_span] #[suggestion(code = "_", applicability = "machine-applicable")] @@ -1167,7 +1108,7 @@ pub(crate) struct PatternMethodParamWithoutBody { } #[derive(Diagnostic)] -#[diag(parser::self_param_not_first)] +#[diag(parser_self_param_not_first)] pub(crate) struct SelfParamNotFirst { #[primary_span] #[label] @@ -1175,7 +1116,15 @@ pub(crate) struct SelfParamNotFirst { } #[derive(Diagnostic)] -#[diag(parser::const_generic_without_braces)] +#[diag(parser_invalid_identifier_with_leading_number)] +pub(crate) struct InvalidIdentiferStartsWithNumber { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parser_const_generic_without_braces)] pub(crate) struct ConstGenericWithoutBraces { #[primary_span] pub span: Span, @@ -1184,7 +1133,7 @@ pub(crate) struct ConstGenericWithoutBraces { } #[derive(Subdiagnostic)] -#[multipart_suggestion(parser::suggestion, applicability = "machine-applicable")] +#[multipart_suggestion(suggestion, applicability = "machine-applicable")] pub(crate) struct ConstGenericWithoutBracesSugg { #[suggestion_part(code = "{{ ")] pub left: Span, @@ -1193,7 +1142,7 @@ pub(crate) struct ConstGenericWithoutBracesSugg { } #[derive(Diagnostic)] -#[diag(parser::unexpected_const_param_declaration)] +#[diag(parser_unexpected_const_param_declaration)] pub(crate) struct UnexpectedConstParamDeclaration { #[primary_span] #[label] @@ -1204,7 +1153,7 @@ pub(crate) struct UnexpectedConstParamDeclaration { #[derive(Subdiagnostic)] pub(crate) enum UnexpectedConstParamDeclarationSugg { - #[multipart_suggestion(parser::suggestion, applicability = "machine-applicable")] + #[multipart_suggestion(suggestion, applicability = "machine-applicable")] AddParam { #[suggestion_part(code = "<{snippet}>")] impl_generics: Span, @@ -1213,7 +1162,7 @@ pub(crate) enum UnexpectedConstParamDeclarationSugg { snippet: String, ident: String, }, - #[multipart_suggestion(parser::suggestion, applicability = "machine-applicable")] + #[multipart_suggestion(suggestion, applicability = "machine-applicable")] AppendParam { #[suggestion_part(code = ", {snippet}")] impl_generics_end: Span, @@ -1225,27 +1174,56 @@ pub(crate) enum UnexpectedConstParamDeclarationSugg { } #[derive(Diagnostic)] -#[diag(parser::unexpected_const_in_generic_param)] +#[diag(parser_unexpected_const_in_generic_param)] pub(crate) struct UnexpectedConstInGenericParam { #[primary_span] pub span: Span, - #[suggestion_verbose(code = "", applicability = "maybe-incorrect")] + #[suggestion(style = "verbose", code = "", applicability = "maybe-incorrect")] pub to_remove: Option<Span>, } #[derive(Diagnostic)] -#[diag(parser::async_move_order_incorrect)] +#[diag(parser_async_move_order_incorrect)] pub(crate) struct AsyncMoveOrderIncorrect { #[primary_span] - #[suggestion_verbose(code = "async move", applicability = "maybe-incorrect")] + #[suggestion(style = "verbose", code = "async move", applicability = "maybe-incorrect")] pub span: Span, } #[derive(Diagnostic)] -#[diag(parser::double_colon_in_bound)] +#[diag(parser_double_colon_in_bound)] pub(crate) struct DoubleColonInBound { #[primary_span] pub span: Span, #[suggestion(code = ": ", applicability = "machine-applicable")] pub between: Span, } + +#[derive(Diagnostic)] +#[diag(parser_fn_ptr_with_generics)] +pub(crate) struct FnPtrWithGenerics { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub sugg: Option<FnPtrWithGenericsSugg>, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(suggestion, applicability = "maybe-incorrect")] +pub(crate) struct FnPtrWithGenericsSugg { + #[suggestion_part(code = "{snippet}")] + pub left: Span, + pub snippet: String, + #[suggestion_part(code = "")] + pub right: Span, + pub arity: usize, + pub for_param_list_exists: bool, +} + +#[derive(Diagnostic)] +#[diag(parser_unexpected_if_with_if)] +pub(crate) struct UnexpectedIfWithIf( + #[primary_span] + #[suggestion(applicability = "machine-applicable", code = " ", style = "verbose")] + pub Span, +); diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 88540e13ef2..f027843e6b4 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -3,7 +3,9 @@ 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}; +use rustc_errors::{ + error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult, StashKey, +}; use rustc_lexer::unescape::{self, Mode}; use rustc_lexer::Cursor; use rustc_lexer::{Base, DocStyle, RawStrError}; @@ -173,20 +175,10 @@ impl<'a> StringReader<'a> { if string == "_" { self.sess .span_diagnostic - .struct_span_warn( + .struct_span_err( self.mk_sp(suffix_start, self.pos), "underscore literal suffix is not allowed", ) - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .note( - "see issue #42326 \ - <https://github.com/rust-lang/rust/issues/42326> \ - for more information", - ) .emit(); None } else { @@ -203,7 +195,10 @@ impl<'a> StringReader<'a> { // this is necessary. let lifetime_name = self.str_from(start); if starts_with_number { - self.err_span_(start, self.pos, "lifetimes cannot start with a number"); + let span = self.mk_sp(start, self.pos); + let mut diag = self.sess.struct_err("lifetimes cannot start with a number"); + diag.set_span(span); + diag.stash(span, StashKey::LifetimeIsChar); } let ident = Symbol::intern(lifetime_name); token::Lifetime(ident) @@ -358,55 +353,55 @@ impl<'a> StringReader<'a> { fn cook_lexer_literal( &self, start: BytePos, - suffix_start: BytePos, + end: BytePos, kind: rustc_lexer::LiteralKind, ) -> (token::LitKind, Symbol) { - // prefix means `"` or `br"` or `r###"`, ... - let (lit_kind, mode, prefix_len, postfix_len) = match kind { + match kind { rustc_lexer::LiteralKind::Char { terminated } => { if !terminated { self.sess.span_diagnostic.span_fatal_with_code( - self.mk_sp(start, suffix_start), + self.mk_sp(start, end), "unterminated character literal", error_code!(E0762), ) } - (token::Char, Mode::Char, 1, 1) // ' ' + self.cook_quoted(token::Char, Mode::Char, start, end, 1, 1) // ' ' } rustc_lexer::LiteralKind::Byte { terminated } => { if !terminated { self.sess.span_diagnostic.span_fatal_with_code( - self.mk_sp(start + BytePos(1), suffix_start), + self.mk_sp(start + BytePos(1), end), "unterminated byte constant", error_code!(E0763), ) } - (token::Byte, Mode::Byte, 2, 1) // b' ' + self.cook_quoted(token::Byte, Mode::Byte, start, end, 2, 1) // b' ' } rustc_lexer::LiteralKind::Str { terminated } => { if !terminated { self.sess.span_diagnostic.span_fatal_with_code( - self.mk_sp(start, suffix_start), + self.mk_sp(start, end), "unterminated double quote string", error_code!(E0765), ) } - (token::Str, Mode::Str, 1, 1) // " " + self.cook_quoted(token::Str, Mode::Str, start, end, 1, 1) // " " } rustc_lexer::LiteralKind::ByteStr { terminated } => { if !terminated { self.sess.span_diagnostic.span_fatal_with_code( - self.mk_sp(start + BytePos(1), suffix_start), + self.mk_sp(start + BytePos(1), end), "unterminated double quote byte string", error_code!(E0766), ) } - (token::ByteStr, Mode::ByteStr, 2, 1) // b" " + self.cook_quoted(token::ByteStr, Mode::ByteStr, start, end, 2, 1) // b" " } rustc_lexer::LiteralKind::RawStr { n_hashes } => { if let Some(n_hashes) = n_hashes { let n = u32::from(n_hashes); - (token::StrRaw(n_hashes), Mode::RawStr, 2 + n, 1 + n) // r##" "## + let kind = token::StrRaw(n_hashes); + self.cook_quoted(kind, Mode::RawStr, start, end, 2 + n, 1 + n) // r##" "## } else { self.report_raw_str_error(start, 1); } @@ -414,56 +409,59 @@ impl<'a> StringReader<'a> { rustc_lexer::LiteralKind::RawByteStr { n_hashes } => { if let Some(n_hashes) = n_hashes { let n = u32::from(n_hashes); - (token::ByteStrRaw(n_hashes), Mode::RawByteStr, 3 + n, 1 + n) // br##" "## + let kind = token::ByteStrRaw(n_hashes); + self.cook_quoted(kind, Mode::RawByteStr, start, end, 3 + n, 1 + n) // br##" "## } else { self.report_raw_str_error(start, 2); } } rustc_lexer::LiteralKind::Int { base, empty_int } => { - return if empty_int { + if empty_int { self.sess .span_diagnostic .struct_span_err_with_code( - self.mk_sp(start, suffix_start), + self.mk_sp(start, end), "no valid digits found for number", error_code!(E0768), ) .emit(); (token::Integer, sym::integer(0)) } else { - self.validate_int_literal(base, start, suffix_start); - (token::Integer, self.symbol_from_to(start, suffix_start)) - }; + 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() { + 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), + ); + } + } + } + (token::Integer, self.symbol_from_to(start, end)) + } } rustc_lexer::LiteralKind::Float { base, empty_exponent } => { if empty_exponent { self.err_span_(start, self.pos, "expected at least one digit in exponent"); } - match base { - Base::Hexadecimal => self.err_span_( - start, - suffix_start, - "hexadecimal float literal is not supported", - ), + Base::Hexadecimal => { + self.err_span_(start, end, "hexadecimal float literal is not supported") + } Base::Octal => { - self.err_span_(start, suffix_start, "octal float literal is not supported") + self.err_span_(start, end, "octal float literal is not supported") } Base::Binary => { - self.err_span_(start, suffix_start, "binary float literal is not supported") + self.err_span_(start, end, "binary float literal is not supported") } - _ => (), + _ => {} } - - let id = self.symbol_from_to(start, suffix_start); - return (token::Float, id); + (token::Float, self.symbol_from_to(start, end)) } - }; - let content_start = start + BytePos(prefix_len); - let content_end = suffix_start - BytePos(postfix_len); - let id = self.symbol_from_to(content_start, content_end); - self.validate_literal_escape(mode, content_start, content_end, prefix_len, postfix_len); - (lit_kind, id) + } } #[inline] @@ -654,24 +652,30 @@ impl<'a> StringReader<'a> { ) } - fn validate_literal_escape( + fn cook_quoted( &self, + kind: token::LitKind, mode: Mode, - content_start: BytePos, - content_end: BytePos, + start: BytePos, + end: BytePos, prefix_len: u32, postfix_len: u32, - ) { + ) -> (token::LitKind, Symbol) { + let mut has_fatal_err = false; + let content_start = start + BytePos(prefix_len); + let content_end = end - BytePos(postfix_len); let lit_content = self.str_from_to(content_start, content_end); unescape::unescape_literal(lit_content, mode, &mut |range, result| { // Here we only check for errors. The actual unescaping is done later. if let Err(err) = result { - let span_with_quotes = self - .mk_sp(content_start - BytePos(prefix_len), content_end + BytePos(postfix_len)); + let span_with_quotes = self.mk_sp(start, end); let (start, end) = (range.start as u32, range.end as u32); let lo = content_start + BytePos(start); let hi = lo + BytePos(end - start); let span = self.mk_sp(lo, hi); + if err.is_fatal() { + has_fatal_err = true; + } emit_unescape_error( &self.sess.span_diagnostic, lit_content, @@ -683,22 +687,13 @@ impl<'a> StringReader<'a> { ); } }); - } - fn validate_int_literal(&self, base: Base, content_start: BytePos, content_end: BytePos) { - let base = match base { - Base::Binary => 2, - Base::Octal => 8, - _ => return, - }; - let s = self.str_from_to(content_start + BytePos(2), content_end); - for (idx, c) in s.char_indices() { - let idx = idx as u32; - if c != '_' && c.to_digit(base).is_none() { - let lo = content_start + BytePos(2 + idx); - let hi = content_start + BytePos(2 + idx + c.len_utf8() as u32); - self.err_span_(lo, hi, &format!("invalid digit for a base {} literal", base)); - } + // We normally exclude the quotes for the symbol, but for errors we + // include it because it results in clearer error messages. + if !has_fatal_err { + (kind, Symbol::intern(lit_content)) + } else { + (token::Err, self.symbol_from_to(start, end)) } } } diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index 77c4fadab45..6373f5b4fd6 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -108,16 +108,31 @@ pub(crate) fn emit_unescape_error( } if !has_help { - let (prefix, msg) = if mode.is_bytes() { + 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 mut escaped = String::with_capacity(lit.len()); + let mut chrs = lit.chars().peekable(); + while let Some(first) = chrs.next() { + match (first, chrs.peek()) { + ('\\', Some('"')) => { + escaped.push('\\'); + escaped.push('"'); + chrs.next(); + } + ('"', _) => { + escaped.push('\\'); + escaped.push('"') + } + (c, _) => escaped.push(c), + }; + } handler.span_suggestion( span_with_quotes, msg, - format!("{}\"{}\"", prefix, lit), + format!("{prefix}\"{escaped}\""), Applicability::MachineApplicable, ); } @@ -127,7 +142,7 @@ pub(crate) fn emit_unescape_error( EscapeError::EscapeOnlyChar => { let (c, char_span) = last_char(); - let msg = if mode.is_bytes() { + let msg = if mode.is_byte() { "byte constant must be escaped" } else { "character constant must be escaped" @@ -167,11 +182,11 @@ pub(crate) fn emit_unescape_error( let (c, span) = last_char(); let label = - if mode.is_bytes() { "unknown byte escape" } else { "unknown character escape" }; + if mode.is_byte() { "unknown byte escape" } else { "unknown character escape" }; let ec = escaped_char(c); let mut diag = handler.struct_span_err(span, &format!("{}: `{}`", label, ec)); diag.span_label(span, label); - if c == '{' || c == '}' && !mode.is_bytes() { + if c == '{' || c == '}' && !mode.is_byte() { diag.help( "if used in a formatting string, curly braces are escaped with `{{` and `}}`", ); @@ -181,7 +196,7 @@ pub(crate) fn emit_unescape_error( version control settings", ); } else { - if !mode.is_bytes() { + if !mode.is_byte() { diag.span_suggestion( span_with_quotes, "if you meant to write a literal backslash (perhaps escaping in a regular expression), consider a raw string literal", @@ -216,16 +231,23 @@ pub(crate) fn emit_unescape_error( .emit(); } EscapeError::NonAsciiCharInByte => { - assert!(mode.is_bytes()); let (c, span) = last_char(); - let mut err = handler.struct_span_err(span, "non-ASCII character in byte constant"); + let desc = match mode { + Mode::Byte => "byte literal", + Mode::ByteStr => "byte string literal", + Mode::RawByteStr => "raw byte string literal", + _ => panic!("non-is_byte literal paired with NonAsciiCharInByte"), + }; + let mut err = handler.struct_span_err(span, format!("non-ASCII character in {}", desc)); let postfix = if unicode_width::UnicodeWidthChar::width(c).unwrap_or(1) == 0 { format!(" but is {:?}", c) } else { String::new() }; - err.span_label(span, &format!("byte constant must be ASCII{}", postfix)); - if (c as u32) <= 0xFF { + err.span_label(span, &format!("must be ASCII{}", postfix)); + // Note: the \\xHH suggestions are not given for raw byte string + // literals, because they are araw and so cannot use any escapes. + if (c as u32) <= 0xFF && mode != Mode::RawByteStr { err.span_suggestion( span, &format!( @@ -235,9 +257,9 @@ pub(crate) fn emit_unescape_error( format!("\\x{:X}", c as u32), Applicability::MaybeIncorrect, ); - } else if matches!(mode, Mode::Byte) { + } else if mode == Mode::Byte { err.span_label(span, "this multibyte character does not fit into a single byte"); - } else if matches!(mode, Mode::ByteStr) { + } else if mode != Mode::RawByteStr { let mut utf8 = String::new(); utf8.push(c); err.span_suggestion( @@ -255,19 +277,6 @@ pub(crate) fn emit_unescape_error( } err.emit(); } - EscapeError::NonAsciiCharInByteString => { - assert!(mode.is_bytes()); - let (c, span) = last_char(); - let postfix = if unicode_width::UnicodeWidthChar::width(c).unwrap_or(1) == 0 { - format!(" but is {:?}", c) - } else { - String::new() - }; - handler - .struct_span_err(span, "raw byte string must be ASCII") - .span_label(span, &format!("must be ASCII{}", postfix)) - .emit(); - } EscapeError::OutOfRangeHexEscape => { handler .struct_span_err(span, "out of range hex escape") diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 0bdfe10359c..c78479b098b 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -3,6 +3,7 @@ #![feature(array_windows)] #![feature(box_patterns)] #![feature(if_let_guard)] +#![feature(iter_intersperse)] #![feature(let_chains)] #![feature(never_type)] #![feature(rustc_attrs)] @@ -39,7 +40,8 @@ mod errors; // uses a HOF to parse anything, and <source> includes file and // `source_str`. -/// A variant of 'panictry!' that works on a Vec<Diagnostic> instead of a single DiagnosticBuilder. +/// A variant of 'panictry!' that works on a `Vec<Diagnostic>` instead of a single +/// `DiagnosticBuilder`. macro_rules! panictry_buffer { ($handler:expr, $e:expr) => {{ use rustc_errors::FatalError; diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 58be348883c..612accf3e3b 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -55,7 +55,7 @@ impl<'a> Parser<'a> { let span = self.token.span; let mut err = self.sess.span_diagnostic.struct_span_err_with_code( span, - fluent::parser::inner_doc_comment_not_permitted, + fluent::parser_inner_doc_comment_not_permitted, error_code!(E0753), ); if let Some(replacement_span) = self.annotate_following_item_if_applicable( @@ -66,10 +66,10 @@ impl<'a> Parser<'a> { token::CommentKind::Block => OuterAttributeType::DocBlockComment, }, ) { - err.note(fluent::parser::note); + err.note(fluent::note); err.span_suggestion_verbose( replacement_span, - fluent::parser::suggestion, + fluent::suggestion, "", rustc_errors::Applicability::MachineApplicable, ); @@ -173,10 +173,10 @@ impl<'a> Parser<'a> { Ok(Some(item)) => { // FIXME(#100717) err.set_arg("item", item.kind.descr()); - err.span_label(item.span, fluent::parser::label_does_not_annotate_this); + err.span_label(item.span, fluent::label_does_not_annotate_this); err.span_suggestion_verbose( replacement_span, - fluent::parser::sugg_change_inner_to_outer, + fluent::sugg_change_inner_to_outer, match attr_type { OuterAttributeType::Attribute => "", OuterAttributeType::DocBlockComment => "*", @@ -200,27 +200,27 @@ impl<'a> Parser<'a> { Some(InnerAttrForbiddenReason::AfterOuterDocComment { prev_doc_comment_span }) => { let mut diag = self.struct_span_err( attr_sp, - fluent::parser::inner_attr_not_permitted_after_outer_doc_comment, + fluent::parser_inner_attr_not_permitted_after_outer_doc_comment, ); - diag.span_label(attr_sp, fluent::parser::label_attr) - .span_label(prev_doc_comment_span, fluent::parser::label_prev_doc_comment); + diag.span_label(attr_sp, fluent::label_attr) + .span_label(prev_doc_comment_span, fluent::label_prev_doc_comment); diag } Some(InnerAttrForbiddenReason::AfterOuterAttribute { prev_outer_attr_sp }) => { let mut diag = self.struct_span_err( attr_sp, - fluent::parser::inner_attr_not_permitted_after_outer_attr, + fluent::parser_inner_attr_not_permitted_after_outer_attr, ); - diag.span_label(attr_sp, fluent::parser::label_attr) - .span_label(prev_outer_attr_sp, fluent::parser::label_prev_attr); + diag.span_label(attr_sp, fluent::label_attr) + .span_label(prev_outer_attr_sp, fluent::label_prev_attr); diag } Some(InnerAttrForbiddenReason::InCodeBlock) | None => { - self.struct_span_err(attr_sp, fluent::parser::inner_attr_not_permitted) + self.struct_span_err(attr_sp, fluent::parser_inner_attr_not_permitted) } }; - diag.note(fluent::parser::inner_attr_explanation); + diag.note(fluent::parser_inner_attr_explanation); if self .annotate_following_item_if_applicable( &mut diag, @@ -229,7 +229,7 @@ impl<'a> Parser<'a> { ) .is_some() { - diag.note(fluent::parser::outer_attr_explanation); + diag.note(fluent::parser_outer_attr_explanation); }; diag.emit(); } @@ -316,8 +316,8 @@ impl<'a> Parser<'a> { } pub(crate) fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> { - let lit = self.parse_lit()?; - debug!("checking if {:?} is unusuffixed", lit); + let lit = self.parse_ast_lit()?; + debug!("checking if {:?} is unsuffixed", lit); if !lit.kind.is_unsuffixed() { self.sess.emit_err(SuffixedLiteralInAttribute { span: lit.span }); diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 0dc05475ce9..c8160548763 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -5,7 +5,8 @@ use rustc_ast::tokenstream::{AttrTokenTree, DelimSpan, LazyAttrTokenStream, Spac use rustc_ast::{self as ast}; use rustc_ast::{AttrVec, Attribute, HasAttrs, HasTokens}; use rustc_errors::PResult; -use rustc_span::{sym, Span}; +use rustc_session::parse::ParseSess; +use rustc_span::{sym, Span, DUMMY_SP}; use std::convert::TryInto; use std::ops::Range; @@ -39,8 +40,13 @@ impl AttrWrapper { pub fn empty() -> AttrWrapper { AttrWrapper { attrs: AttrVec::new(), start_pos: usize::MAX } } - // FIXME: Delay span bug here? - pub(crate) fn take_for_recovery(self) -> AttrVec { + + pub(crate) fn take_for_recovery(self, sess: &ParseSess) -> AttrVec { + sess.span_diagnostic.delay_span_bug( + self.attrs.get(0).map(|attr| attr.span).unwrap_or(DUMMY_SP), + "AttrVec is taken for recovery but no error is produced", + ); + self.attrs } @@ -273,16 +279,23 @@ impl<'a> Parser<'a> { let cursor_snapshot_next_calls = cursor_snapshot.num_next_calls; let mut end_pos = self.token_cursor.num_next_calls; + let mut captured_trailing = false; + // Capture a trailing token if requested by the callback 'f' match trailing { TrailingToken::None => {} + TrailingToken::Gt => { + assert_eq!(self.token.kind, token::Gt); + } TrailingToken::Semi => { assert_eq!(self.token.kind, token::Semi); end_pos += 1; + captured_trailing = true; } TrailingToken::MaybeComma => { if self.token.kind == token::Comma { end_pos += 1; + captured_trailing = true; } } } @@ -292,11 +305,7 @@ impl<'a> Parser<'a> { // was not actually bumped past it. When the `LazyAttrTokenStream` gets converted // into an `AttrTokenStream`, we will create the proper token. if self.token_cursor.break_last_token { - assert_eq!( - trailing, - TrailingToken::None, - "Cannot set `break_last_token` and have trailing token" - ); + assert!(!captured_trailing, "Cannot set break_last_token and have trailing token"); end_pos += 1; } @@ -459,7 +468,8 @@ fn make_token_stream( mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(AttrWrapper, 16); static_assert_size!(LazyAttrTokenStreamImpl, 144); + // tidy-alphabetical-end } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index f57bd9cec19..2558ac801ad 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -18,6 +18,7 @@ use crate::errors::{ }; use crate::lexer::UnmatchedBrace; +use crate::parser; use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Lit, LitKind, TokenKind}; @@ -37,11 +38,10 @@ use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{Span, SpanSnippetError, DUMMY_SP}; -use std::ops::{Deref, DerefMut}; - use std::mem::take; - -use crate::parser; +use std::ops::{Deref, DerefMut}; +use thin_vec::{thin_vec, ThinVec}; +use tracing::{debug, trace}; /// Creates a placeholder argument. pub(super) fn dummy_arg(ident: Ident) -> Param { @@ -65,7 +65,7 @@ pub(super) fn dummy_arg(ident: Ident) -> Param { pub(super) trait RecoverQPath: Sized + 'static { const PATH_STYLE: PathStyle = PathStyle::Expr; fn to_ty(&self) -> Option<P<Ty>>; - fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self; + fn recovered(qself: Option<P<QSelf>>, path: ast::Path) -> Self; } impl RecoverQPath for Ty { @@ -73,7 +73,7 @@ impl RecoverQPath for Ty { fn to_ty(&self) -> Option<P<Ty>> { Some(P(self.clone())) } - fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self { + fn recovered(qself: Option<P<QSelf>>, path: ast::Path) -> Self { Self { span: path.span, kind: TyKind::Path(qself, path), @@ -87,7 +87,7 @@ impl RecoverQPath for Pat { fn to_ty(&self) -> Option<P<Ty>> { self.to_ty() } - fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self { + fn recovered(qself: Option<P<QSelf>>, path: ast::Path) -> Self { Self { span: path.span, kind: PatKind::Path(qself, path), @@ -101,7 +101,7 @@ impl RecoverQPath for Expr { fn to_ty(&self) -> Option<P<Ty>> { self.to_ty() } - fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self { + fn recovered(qself: Option<P<QSelf>>, path: ast::Path) -> Self { Self { span: path.span, kind: ExprKind::Path(qself, path), @@ -638,8 +638,11 @@ impl<'a> Parser<'a> { // field: value, // } let mut snapshot = self.create_snapshot_for_diagnostic(); - let path = - Path { segments: vec![], span: self.prev_token.span.shrink_to_lo(), tokens: None }; + let path = Path { + segments: ThinVec::new(), + span: self.prev_token.span.shrink_to_lo(), + tokens: None, + }; let struct_expr = snapshot.parse_struct_expr(None, path, false); let block_tail = self.parse_block_tail(lo, s, AttemptLocalParseRecovery::No); return Some(match (struct_expr, block_tail) { @@ -769,6 +772,10 @@ impl<'a> Parser<'a> { segment: &PathSegment, end: &[&TokenKind], ) -> bool { + if !self.may_recover() { + return false; + } + // This function is intended to be invoked after parsing a path segment where there are two // cases: // @@ -863,6 +870,10 @@ impl<'a> Parser<'a> { /// Check if a method call with an intended turbofish has been written without surrounding /// angle brackets. pub(super) fn check_turbofish_missing_angle_brackets(&mut self, segment: &mut PathSegment) { + if !self.may_recover() { + return; + } + if token::ModSep == self.token.kind && segment.args.is_none() { let snapshot = self.create_snapshot_for_diagnostic(); self.bump(); @@ -926,7 +937,7 @@ impl<'a> Parser<'a> { if self.eat(&token::Gt) { e.span_suggestion_verbose( binop.span.shrink_to_lo(), - fluent::parser::sugg_turbofish_syntax, + fluent::parser_sugg_turbofish_syntax, "::", Applicability::MaybeIncorrect, ) @@ -1374,9 +1385,17 @@ impl<'a> Parser<'a> { kind: IncDecRecovery, (pre_span, post_span): (Span, Span), ) -> MultiSugg { + let mut patches = Vec::new(); + + if !pre_span.is_empty() { + patches.push((pre_span, String::new())); + } + + patches.push((post_span, format!(" {}= 1", kind.op.chr()))); + MultiSugg { msg: format!("use `{}= 1` instead", kind.op.chr()), - patches: vec![(pre_span, String::new()), (post_span, format!(" {}= 1", kind.op.chr()))], + patches, applicability: Applicability::MachineApplicable, } } @@ -1388,6 +1407,10 @@ impl<'a> Parser<'a> { &mut self, base: P<T>, ) -> PResult<'a, P<T>> { + if !self.may_recover() { + return Ok(base); + } + // Do not add `::` to expected tokens. if self.token == token::ModSep { if let Some(ty) = base.to_ty() { @@ -1406,7 +1429,7 @@ impl<'a> Parser<'a> { ) -> PResult<'a, P<T>> { self.expect(&token::ModSep)?; - let mut path = ast::Path { segments: Vec::new(), span: DUMMY_SP, tokens: None }; + let mut path = ast::Path { segments: ThinVec::new(), span: DUMMY_SP, tokens: None }; self.parse_path_segments(&mut path.segments, T::PATH_STYLE, None)?; path.span = ty_span.to(self.prev_token.span); @@ -1417,7 +1440,7 @@ impl<'a> Parser<'a> { }); let path_span = ty_span.shrink_to_hi(); // Use an empty path since `position == 0`. - Ok(P(T::recovered(Some(QSelf { ty, path_span, position: 0 }), path))) + Ok(P(T::recovered(Some(P(QSelf { ty, path_span, position: 0 })), path))) } pub fn maybe_consume_incorrect_semicolon(&mut self, items: &[P<Item>]) -> bool { @@ -1461,7 +1484,7 @@ impl<'a> Parser<'a> { let (prev_sp, sp) = match (&self.token.kind, self.subparser_name) { // Point at the end of the macro call when reaching end of macro arguments. (token::Eof, Some(_)) => { - let sp = self.sess.source_map().next_point(self.prev_token.span); + let sp = self.prev_token.span.shrink_to_hi(); (sp, sp) } // We don't want to point at the following span after DUMMY_SP. @@ -1633,15 +1656,29 @@ impl<'a> Parser<'a> { (token::CloseDelim(Delimiter::Parenthesis), Some(begin_par_sp)) => { self.bump(); + let sm = self.sess.source_map(); + let left = begin_par_sp; + let right = self.prev_token.span; + let left_snippet = if let Ok(snip) = sm.span_to_prev_source(left) && + !snip.ends_with(' ') { + " ".to_string() + } else { + "".to_string() + }; + + let right_snippet = if let Ok(snip) = sm.span_to_next_source(right) && + !snip.starts_with(' ') { + " ".to_string() + } else { + "".to_string() + }; + self.sess.emit_err(ParenthesesInForHead { - span: vec![begin_par_sp, self.prev_token.span], + span: vec![left, right], // With e.g. `for (x) in y)` this would replace `(x) in y)` // with `x) in y)` which is syntactically invalid. // However, this is prevented before we get here. - sugg: ParenthesesInForHeadSugg { - left: begin_par_sp, - right: self.prev_token.span, - }, + sugg: ParenthesesInForHeadSugg { left, right, left_snippet, right_snippet }, }); // Unwrap `(pat)` into `pat` to avoid the `unused_parens` lint. @@ -2039,7 +2076,7 @@ impl<'a> Parser<'a> { pub(super) fn expected_expression_found(&self) -> DiagnosticBuilder<'a, ErrorGuaranteed> { let (span, msg) = match (&self.token.kind, self.subparser_name) { (&token::Eof, Some(origin)) => { - let sp = self.sess.source_map().next_point(self.prev_token.span); + let sp = self.prev_token.span.shrink_to_hi(); (sp, format!("expected expression, found end of {origin}")) } _ => ( @@ -2232,7 +2269,7 @@ impl<'a> Parser<'a> { /// /// When encountering code like `foo::< bar + 3 >` or `foo::< bar - baz >` we suggest /// `foo::<{ bar + 3 }>` and `foo::<{ bar - baz }>`, respectively. We only provide a suggestion - /// if we think that that the resulting expression would be well formed. + /// if we think that the resulting expression would be well formed. pub fn recover_const_arg( &mut self, start: Span, @@ -2400,7 +2437,7 @@ impl<'a> Parser<'a> { None, Path { span: new_span, - segments: vec![ + segments: thin_vec![ PathSegment::from_ident(*old_ident), PathSegment::from_ident(*ident), ], @@ -2448,11 +2485,15 @@ impl<'a> Parser<'a> { } pub(crate) fn maybe_recover_unexpected_block_label(&mut self) -> bool { - let Some(label) = self.eat_label().filter(|_| { - self.eat(&token::Colon) && self.token.kind == token::OpenDelim(Delimiter::Brace) - }) else { + // Check for `'a : {` + if !(self.check_lifetime() + && self.look_ahead(1, |tok| tok.kind == token::Colon) + && self.look_ahead(2, |tok| tok.kind == token::OpenDelim(Delimiter::Brace))) + { return false; - }; + } + let label = self.eat_label().expect("just checked if a label exists"); + self.bump(); // eat `:` let span = label.ident.span.to(self.prev_token.span); let mut err = self.struct_span_err(span, "block label not supported here"); err.span_label(span, "not supported here"); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 11301f03e48..fe7401786a0 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -7,34 +7,30 @@ use super::{ }; use crate::errors::{ ArrayBracketsInsteadOfSpaces, ArrayBracketsInsteadOfSpacesSugg, AsyncMoveOrderIncorrect, - BinaryFloatLiteralNotSupported, BracesForStructLiteral, CatchAfterTry, CommaAfterBaseStruct, - ComparisonInterpretedAsGeneric, ComparisonOrShiftInterpretedAsGenericSugg, - DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, ExpectedElseBlock, ExpectedExpressionFoundLet, + BracesForStructLiteral, CatchAfterTry, CommaAfterBaseStruct, ComparisonInterpretedAsGeneric, + ComparisonOrShiftInterpretedAsGenericSugg, DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, + ExpectedElseBlock, ExpectedEqForLetExpr, ExpectedExpressionFoundLet, FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart, FoundExprWouldBeStmt, - HexadecimalFloatLiteralNotSupported, IfExpressionMissingCondition, - IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub, IntLiteralTooLarge, + IfExpressionMissingCondition, IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub, InvalidBlockMacroSegment, InvalidComparisonOperator, InvalidComparisonOperatorSub, - InvalidFloatLiteralSuffix, InvalidFloatLiteralWidth, InvalidIntLiteralWidth, - InvalidInterpolatedExpression, InvalidLiteralSuffix, InvalidLiteralSuffixOnTupleIndex, - InvalidLogicalOperator, InvalidLogicalOperatorSub, InvalidNumLiteralBasePrefix, - InvalidNumLiteralSuffix, LabeledLoopInBreak, LeadingPlusNotSupported, LeftArrowOperator, + InvalidInterpolatedExpression, InvalidLiteralSuffixOnTupleIndex, InvalidLogicalOperator, + InvalidLogicalOperatorSub, LabeledLoopInBreak, LeadingPlusNotSupported, LeftArrowOperator, LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, MalformedLoopLabel, MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg, MissingCommaAfterMatchArm, - MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray, NoFieldsForFnCall, - NotAsNegationOperator, NotAsNegationOperatorSub, OctalFloatLiteralNotSupported, + MissingDotDot, MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray, + NoFieldsForFnCall, NotAsNegationOperator, NotAsNegationOperatorSub, OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields, RequireColonAfterLabeledExpression, ShiftInterpretedAsGeneric, StructLiteralNotAllowedHere, - StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator, UnexpectedTokenAfterLabel, - UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses, + StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator, UnexpectedIfWithIf, + UnexpectedTokenAfterLabel, UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses, }; use crate::maybe_recover_from_interpolated_ty_qpath; - use core::mem; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::Spacing; +use rustc_ast::util::case::Case; use rustc_ast::util::classify; -use rustc_ast::util::literal::LitError; use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity}; use rustc_ast::visit::Visitor; use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, Lit, UnOp, DUMMY_NODE_ID}; @@ -42,9 +38,11 @@ use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits}; use rustc_ast::{ClosureBinder, StmtKind}; use rustc_ast_pretty::pprust; -use rustc_errors::IntoDiagnostic; -use rustc_errors::{Applicability, Diagnostic, PResult}; -use rustc_session::errors::ExprParenthesesNeeded; +use rustc_errors::{ + 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; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_span::source_map::{self, Span, Spanned}; @@ -130,7 +128,7 @@ impl<'a> Parser<'a> { Ok(expr) => Ok(expr), Err(mut err) => match self.token.ident() { Some((Ident { name: kw::Underscore, .. }, false)) - if self.look_ahead(1, |t| t == &token::Comma) => + if self.may_recover() && self.look_ahead(1, |t| t == &token::Comma) => { // Special-case handling of `foo(_, _, _)` err.emit(); @@ -454,7 +452,7 @@ impl<'a> Parser<'a> { return None; } (Some(op), _) => (op, self.token.span), - (None, Some((Ident { name: sym::and, span }, false))) => { + (None, Some((Ident { name: sym::and, span }, false))) if self.may_recover() => { self.sess.emit_err(InvalidLogicalOperator { span: self.token.span, incorrect: "and".into(), @@ -462,7 +460,7 @@ impl<'a> Parser<'a> { }); (AssocOp::LAnd, span) } - (None, Some((Ident { name: sym::or, span }, false))) => { + (None, Some((Ident { name: sym::or, span }, false))) if self.may_recover() => { self.sess.emit_err(InvalidLogicalOperator { span: self.token.span, incorrect: "or".into(), @@ -613,7 +611,7 @@ impl<'a> Parser<'a> { token::Ident(..) if this.token.is_keyword(kw::Box) => { make_it!(this, attrs, |this, _| this.parse_box_expr(lo)) } - token::Ident(..) if this.is_mistaken_not_ident_negation() => { + 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)), @@ -716,6 +714,10 @@ impl<'a> Parser<'a> { let cast_expr = match self.parse_as_cast_ty() { Ok(rhs) => mk_expr(self, lhs, rhs), Err(type_err) => { + if !self.may_recover() { + return Err(type_err); + } + // Rewind to before attempting to parse the type with generics, to recover // from situations like `x as usize < y` in which we first tried to parse // `usize < y` as a type with generic arguments. @@ -827,23 +829,18 @@ impl<'a> Parser<'a> { ("cast", None) }; - // Save the memory location of expr before parsing any following postfix operators. - // This will be compared with the memory location of the output expression. - // If they different we can assume we parsed another expression because the existing expression is not reallocated. - let addr_before = &*cast_expr as *const _ as usize; let with_postfix = self.parse_dot_or_call_expr_with_(cast_expr, span)?; - let changed = addr_before != &*with_postfix as *const _ as usize; // Check if an illegal postfix operator has been added after the cast. - // If the resulting expression is not a cast, or has a different memory location, it is an illegal postfix operator. - if !matches!(with_postfix.kind, ExprKind::Cast(_, _) | ExprKind::Type(_, _)) || changed { + // If the resulting expression is not a cast, it is an illegal postfix operator. + if !matches!(with_postfix.kind, ExprKind::Cast(_, _) | ExprKind::Type(_, _)) { let msg = format!( "{cast_kind} cannot be followed by {}", match with_postfix.kind { ExprKind::Index(_, _) => "indexing", ExprKind::Try(_) => "`?`", ExprKind::Field(_, _) => "a field access", - ExprKind::MethodCall(_, _, _, _) => "a method call", + ExprKind::MethodCall(_) => "a method call", ExprKind::Call(_, _) => "a function call", ExprKind::Await(_) => "`.await`", ExprKind::Err => return Ok(with_postfix), @@ -1195,6 +1192,10 @@ impl<'a> Parser<'a> { seq: &mut PResult<'a, P<Expr>>, snapshot: Option<(SnapshotParser<'a>, ExprKind)>, ) -> Option<P<Expr>> { + if !self.may_recover() { + return None; + } + match (seq.as_mut(), snapshot) { (Err(err), Some((mut snapshot, ExprKind::Path(None, path)))) => { snapshot.bump(); // `(` @@ -1261,24 +1262,32 @@ impl<'a> Parser<'a> { } let fn_span_lo = self.token.span; - let mut segment = self.parse_path_segment(PathStyle::Expr, None)?; - self.check_trailing_angle_brackets(&segment, &[&token::OpenDelim(Delimiter::Parenthesis)]); - self.check_turbofish_missing_angle_brackets(&mut segment); + let mut seg = self.parse_path_segment(PathStyle::Expr, None)?; + self.check_trailing_angle_brackets(&seg, &[&token::OpenDelim(Delimiter::Parenthesis)]); + self.check_turbofish_missing_angle_brackets(&mut seg); if self.check(&token::OpenDelim(Delimiter::Parenthesis)) { // Method call `expr.f()` let args = self.parse_paren_expr_seq()?; let fn_span = fn_span_lo.to(self.prev_token.span); let span = lo.to(self.prev_token.span); - Ok(self.mk_expr(span, ExprKind::MethodCall(segment, self_arg, args, fn_span))) + Ok(self.mk_expr( + span, + ExprKind::MethodCall(Box::new(ast::MethodCall { + seg, + receiver: self_arg, + args, + span: fn_span, + })), + )) } else { // Field access `expr.f` - if let Some(args) = segment.args { + if let Some(args) = seg.args { self.sess.emit_err(FieldExpressionWithGeneric(args.span())); } let span = lo.to(self.prev_token.span); - Ok(self.mk_expr(span, ExprKind::Field(self_arg, segment.ident))) + Ok(self.mk_expr(span, ExprKind::Field(self_arg, seg.ident))) } } @@ -1358,7 +1367,7 @@ impl<'a> Parser<'a> { ) } else if self.check_inline_const(0) { self.parse_const_block(lo.to(self.token.span), false) - } else if self.is_do_catch_block() { + } else if self.may_recover() && self.is_do_catch_block() { self.recover_do_catch() } else if self.is_try_block() { self.expect_keyword(kw::Try)?; @@ -1409,9 +1418,9 @@ impl<'a> Parser<'a> { fn parse_lit_expr(&mut self) -> PResult<'a, P<Expr>> { let lo = self.token.span; - match self.parse_opt_lit() { - Some(literal) => { - let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Lit(literal)); + match self.parse_opt_token_lit() { + Some((token_lit, _)) => { + let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Lit(token_lit)); self.maybe_recover_from_bad_qpath(expr) } None => self.try_macro_suggestion(), @@ -1497,7 +1506,7 @@ impl<'a> Parser<'a> { }); (lo.to(self.prev_token.span), ExprKind::MacCall(mac)) } else if self.check(&token::OpenDelim(Delimiter::Brace)) && - let Some(expr) = self.maybe_parse_struct_expr(qself.as_ref(), &path) { + let Some(expr) = self.maybe_parse_struct_expr(&qself, &path) { if qself.is_some() { self.sess.gated_spans.gate(sym::more_qualified_paths, path.span); } @@ -1513,11 +1522,11 @@ impl<'a> Parser<'a> { /// Parse `'label: $expr`. The label is already parsed. fn parse_labeled_expr( &mut self, - label: Label, + label_: Label, mut consume_colon: bool, ) -> PResult<'a, P<Expr>> { - let lo = label.ident.span; - let label = Some(label); + let lo = label_.ident.span; + 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) @@ -1530,6 +1539,20 @@ impl<'a> Parser<'a> { { self.parse_block_expr(label, lo, BlockCheckMode::Default) } else if !ate_colon + && self.may_recover() + && (matches!(self.token.kind, token::CloseDelim(_) | token::Comma) + || self.token.is_op()) + { + let lit = self.recover_unclosed_char(label_.ident, |self_| { + self_.sess.create_err(UnexpectedTokenAfterLabel { + span: self_.token.span, + remove_label: None, + enclose_in_block: None, + }) + }); + consume_colon = false; + Ok(self.mk_expr(lo, ExprKind::Lit(lit.token_lit))) + } else if !ate_colon && (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt)) { // We're probably inside of a `Path<'a>` that needs a turbofish @@ -1603,6 +1626,40 @@ impl<'a> Parser<'a> { Ok(expr) } + /// Emit an error when a char is parsed as a lifetime because of a missing quote + pub(super) fn recover_unclosed_char( + &self, + lifetime: Ident, + err: impl FnOnce(&Self) -> DiagnosticBuilder<'a, ErrorGuaranteed>, + ) -> ast::Lit { + if let Some(mut diag) = + self.sess.span_diagnostic.steal_diagnostic(lifetime.span, StashKey::LifetimeIsChar) + { + diag.span_suggestion_verbose( + lifetime.span.shrink_to_hi(), + "add `'` to close the char literal", + "'", + Applicability::MaybeIncorrect, + ) + .emit(); + } else { + err(self) + .span_suggestion_verbose( + lifetime.span.shrink_to_hi(), + "add `'` to close the char literal", + "'", + Applicability::MaybeIncorrect, + ) + .emit(); + } + let name = lifetime.without_first_quote().name; + ast::Lit { + token_lit: token::Lit::new(token::LitKind::Char, name, None), + kind: ast::LitKind::Char(name.as_str().chars().next().unwrap_or('_')), + span: lifetime.span, + } + } + /// Recover on the syntax `do catch { ... }` suggesting `try { ... }` instead. fn recover_do_catch(&mut self) -> PResult<'a, P<Expr>> { let lo = self.token.span; @@ -1712,7 +1769,7 @@ impl<'a> Parser<'a> { /// In case of error returns `Some(lit)` if the next token is a literal with a wrong kind, /// and returns `None` if the next token is not literal at all. pub fn parse_str_lit(&mut self) -> Result<ast::StrLit, Option<Lit>> { - match self.parse_opt_lit() { + match self.parse_opt_ast_lit() { Some(lit) => match lit.kind { ast::LitKind::Str(symbol_unescaped, style) => Ok(ast::StrLit { style, @@ -1727,31 +1784,47 @@ impl<'a> Parser<'a> { } } - pub(super) fn parse_lit(&mut self) -> PResult<'a, Lit> { - self.parse_opt_lit().ok_or_else(|| { - if let token::Interpolated(inner) = &self.token.kind { - let expr = match inner.as_ref() { - token::NtExpr(expr) => Some(expr), - token::NtLiteral(expr) => Some(expr), - _ => None, - }; - if let Some(expr) = expr { - if matches!(expr.kind, ExprKind::Err) { - let mut err = InvalidInterpolatedExpression { span: self.token.span } - .into_diagnostic(&self.sess.span_diagnostic); - err.downgrade_to_delayed_bug(); - return err; - } + fn handle_missing_lit(&mut self) -> PResult<'a, Lit> { + if let token::Interpolated(inner) = &self.token.kind { + let expr = match inner.as_ref() { + token::NtExpr(expr) => Some(expr), + token::NtLiteral(expr) => Some(expr), + _ => None, + }; + if let Some(expr) = expr { + if matches!(expr.kind, ExprKind::Err) { + let mut err = InvalidInterpolatedExpression { span: self.token.span } + .into_diagnostic(&self.sess.span_diagnostic); + err.downgrade_to_delayed_bug(); + return Err(err); } } - let msg = format!("unexpected token: {}", super::token_descr(&self.token)); - self.struct_span_err(self.token.span, &msg) - }) + } + let token = self.token.clone(); + let err = |self_: &Self| { + let msg = format!("unexpected token: {}", super::token_descr(&token)); + self_.struct_span_err(token.span, &msg) + }; + // On an error path, eagerly consider a lifetime to be an unclosed character lit + if self.token.is_lifetime() { + let lt = self.expect_lifetime(); + Ok(self.recover_unclosed_char(lt.ident, err)) + } else { + Err(err(self)) + } } - /// Matches `lit = true | false | token_lit`. - /// Returns `None` if the next token is not a literal. - pub(super) fn parse_opt_lit(&mut self) -> Option<Lit> { + pub(super) fn parse_token_lit(&mut self) -> PResult<'a, (token::Lit, Span)> { + self.parse_opt_token_lit() + .ok_or(()) + .or_else(|()| self.handle_missing_lit().map(|lit| (lit.token_lit, lit.span))) + } + + pub(super) fn parse_ast_lit(&mut self) -> PResult<'a, Lit> { + self.parse_opt_ast_lit().ok_or(()).or_else(|()| self.handle_missing_lit()) + } + + fn recover_after_dot(&mut self) -> Option<Token> { let mut recovered = None; if self.token == token::Dot { // Attempt to recover `.4` as `0.4`. We don't currently have any syntax where @@ -1777,100 +1850,50 @@ impl<'a> Parser<'a> { } } - let token = recovered.as_ref().unwrap_or(&self.token); - match Lit::from_token(token) { - Ok(lit) => { - self.bump(); - Some(lit) - } - Err(LitError::NotLiteral) => None, - Err(err) => { - let span = token.span; - let token::Literal(lit) = token.kind else { - unreachable!(); - }; - self.bump(); - self.report_lit_error(err, lit, span); - // Pack possible quotes and prefixes from the original literal into - // the error literal's symbol so they can be pretty-printed faithfully. - let suffixless_lit = token::Lit::new(lit.kind, lit.symbol, None); - let symbol = Symbol::intern(&suffixless_lit.to_string()); - let lit = token::Lit::new(token::Err, symbol, lit.suffix); - Some(Lit::from_token_lit(lit, span).unwrap_or_else(|_| unreachable!())) - } - } + recovered } - fn report_lit_error(&self, err: LitError, lit: token::Lit, span: Span) { - // Checks if `s` looks like i32 or u1234 etc. - fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool { - s.len() > 1 && s.starts_with(first_chars) && s[1..].chars().all(|c| c.is_ascii_digit()) - } - - // Try to lowercase the prefix if it's a valid base prefix. - fn fix_base_capitalisation(s: &str) -> Option<String> { - if let Some(stripped) = s.strip_prefix('B') { - Some(format!("0b{stripped}")) - } else if let Some(stripped) = s.strip_prefix('O') { - Some(format!("0o{stripped}")) - } else if let Some(stripped) = s.strip_prefix('X') { - Some(format!("0x{stripped}")) - } else { - None - } - } + /// Matches `lit = true | false | token_lit`. + /// Returns `None` if the next token is not a literal. + pub(super) fn parse_opt_token_lit(&mut self) -> Option<(token::Lit, Span)> { + let recovered = self.recover_after_dot(); + let token = recovered.as_ref().unwrap_or(&self.token); + let span = token.span; + token::Lit::from_token(token).map(|token_lit| { + self.bump(); + (token_lit, span) + }) + } - let token::Lit { kind, suffix, .. } = lit; - match err { - // `NotLiteral` is not an error by itself, so we don't report - // it and give the parser opportunity to try something else. - LitError::NotLiteral => {} - // `LexerError` *is* an error, but it was already reported - // by lexer, so here we don't report it the second time. - LitError::LexerError => {} - LitError::InvalidSuffix => { - if let Some(suffix) = suffix { - self.sess.emit_err(InvalidLiteralSuffix { - span, - kind: format!("{}", kind.descr()), - suffix, - }); - } - } - LitError::InvalidIntSuffix => { - let suf = suffix.expect("suffix error with no suffix"); - let suf = suf.as_str(); - if looks_like_width_suffix(&['i', 'u'], &suf) { - // If it looks like a width, try to be helpful. - self.sess.emit_err(InvalidIntLiteralWidth { span, width: suf[1..].into() }); - } else if let Some(fixed) = fix_base_capitalisation(suf) { - self.sess.emit_err(InvalidNumLiteralBasePrefix { span, fixed }); - } else { - self.sess.emit_err(InvalidNumLiteralSuffix { span, suffix: suf.to_string() }); - } - } - LitError::InvalidFloatSuffix => { - let suf = suffix.expect("suffix error with no suffix"); - let suf = suf.as_str(); - if looks_like_width_suffix(&['f'], suf) { - // If it looks like a width, try to be helpful. - self.sess - .emit_err(InvalidFloatLiteralWidth { span, width: suf[1..].to_string() }); - } else { - self.sess.emit_err(InvalidFloatLiteralSuffix { span, suffix: suf.to_string() }); + /// Matches `lit = true | false | token_lit`. + /// Returns `None` if the next token is not a literal. + pub(super) fn parse_opt_ast_lit(&mut self) -> Option<Lit> { + let recovered = self.recover_after_dot(); + let token = recovered.as_ref().unwrap_or(&self.token); + match token::Lit::from_token(token) { + Some(token_lit) => { + match Lit::from_token_lit(token_lit, token.span) { + Ok(lit) => { + self.bump(); + Some(lit) + } + Err(err) => { + let span = token.span; + let token::Literal(lit) = token.kind else { + unreachable!(); + }; + self.bump(); + report_lit_error(&self.sess, err, lit, span); + // Pack possible quotes and prefixes from the original literal into + // the error literal's symbol so they can be pretty-printed faithfully. + let suffixless_lit = token::Lit::new(lit.kind, lit.symbol, None); + let symbol = Symbol::intern(&suffixless_lit.to_string()); + let lit = token::Lit::new(token::Err, symbol, lit.suffix); + Some(Lit::from_token_lit(lit, span).unwrap_or_else(|_| unreachable!())) + } } } - LitError::NonDecimalFloat(base) => { - match base { - 16 => self.sess.emit_err(HexadecimalFloatLiteralNotSupported { span }), - 8 => self.sess.emit_err(OctalFloatLiteralNotSupported { span }), - 2 => self.sess.emit_err(BinaryFloatLiteralNotSupported { span }), - _ => unreachable!(), - }; - } - LitError::IntTooLarge => { - self.sess.emit_err(IntLiteralTooLarge { span }); - } + None => None, } } @@ -1895,8 +1918,8 @@ impl<'a> Parser<'a> { let lo = self.token.span; let minus_present = self.eat(&token::BinOp(token::Minus)); - let lit = self.parse_lit()?; - let expr = self.mk_expr(lit.span, ExprKind::Lit(lit)); + let (token_lit, span) = self.parse_token_lit()?; + let expr = self.mk_expr(span, ExprKind::Lit(token_lit)); if minus_present { Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_unary(UnOp::Neg, expr))) @@ -1941,6 +1964,10 @@ impl<'a> Parser<'a> { prev_span: Span, open_delim_span: Span, ) -> PResult<'a, ()> { + if !self.may_recover() { + return Ok(()); + } + if self.token.kind == token::Comma { if !self.sess.source_map().is_multiline(prev_span.until(self.token.span)) { return Ok(()); @@ -1981,7 +2008,7 @@ impl<'a> Parser<'a> { lo: Span, blk_mode: BlockCheckMode, ) -> PResult<'a, P<Expr>> { - if self.is_array_like_block() { + if self.may_recover() && self.is_array_like_block() { if let Some(arr) = self.maybe_suggest_brackets_instead_of_braces(lo) { return Ok(arr); } @@ -2024,15 +2051,15 @@ impl<'a> Parser<'a> { if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable }; let asyncness = if self.token.uninterpolated_span().rust_2018() { - self.parse_asyncness() + self.parse_asyncness(Case::Sensitive) } else { Async::No }; let capture_clause = self.parse_capture_clause()?; - let decl = self.parse_fn_block_decl()?; + let fn_decl = self.parse_fn_block_decl()?; let decl_hi = self.prev_token.span; - let mut body = match decl.output { + let mut body = match fn_decl.output { FnRetTy::Default(_) => { let restrictions = self.restrictions - Restrictions::STMT_EXPR; self.parse_expr_res(restrictions, None)? @@ -2051,6 +2078,7 @@ impl<'a> Parser<'a> { if self.token.kind == TokenKind::Semi && matches!(self.token_cursor.frame.delim_sp, Some((Delimiter::Parenthesis, _))) + && self.may_recover() { // It is likely that the closure body is a block but where the // braces have been removed. We will recover and eat the next @@ -2062,15 +2090,15 @@ impl<'a> Parser<'a> { let closure = self.mk_expr( lo.to(body.span), - ExprKind::Closure( + ExprKind::Closure(Box::new(ast::Closure { binder, capture_clause, asyncness, movability, - decl, + fn_decl, body, - lo.to(decl_hi), - ), + fn_decl_span: lo.to(decl_hi), + })), ); // Disable recovery for closure body @@ -2172,7 +2200,7 @@ impl<'a> Parser<'a> { }, ExprKind::Block(_, None) => { self.sess.emit_err(IfExpressionMissingCondition { - if_span: self.sess.source_map().next_point(lo), + if_span: lo.shrink_to_hi(), block_span: self.sess.source_map().start_point(cond_span), }); std::mem::replace(&mut cond, this.mk_expr_err(cond_span.shrink_to_hi())) @@ -2199,13 +2227,14 @@ impl<'a> Parser<'a> { self.mk_block_err(cond_span.shrink_to_hi()) } } else { - let attrs = self.parse_outer_attributes()?.take_for_recovery(); // For recovery. + let attrs = self.parse_outer_attributes()?; // For recovery. let block = if self.check(&token::OpenDelim(Delimiter::Brace)) { self.parse_block()? } else { if let Some(block) = recover_block_from_condition(self) { block } else { + self.error_on_extra_if(&cond)?; // Parse block, which will always fail, but we can add a nice note to the error self.parse_block().map_err(|mut err| { err.span_note( @@ -2216,7 +2245,7 @@ impl<'a> Parser<'a> { })? } }; - self.error_on_if_block_attrs(lo, false, block.span, &attrs); + self.error_on_if_block_attrs(lo, false, block.span, attrs); block }; let els = if self.eat_keyword(kw::Else) { Some(self.parse_else_expr()?) } else { None }; @@ -2257,7 +2286,15 @@ impl<'a> Parser<'a> { RecoverColon::Yes, CommaRecoveryMode::LikelyTuple, )?; - self.expect(&token::Eq)?; + if self.token == token::EqEq { + self.sess.emit_err(ExpectedEqForLetExpr { + span: self.token.span, + sugg_span: self.token.span, + }); + self.bump(); + } else { + self.expect(&token::Eq)?; + } let expr = self.with_res(self.restrictions | Restrictions::NO_STRUCT_LITERAL, |this| { this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into()) })?; @@ -2269,7 +2306,7 @@ impl<'a> Parser<'a> { /// Parses an `else { ... }` expression (`else` token already eaten). fn parse_else_expr(&mut self) -> PResult<'a, P<Expr>> { let else_span = self.prev_token.span; // `else` - let attrs = self.parse_outer_attributes()?.take_for_recovery(); // For recovery. + let attrs = self.parse_outer_attributes()?; // For recovery. let expr = if self.eat_keyword(kw::If) { self.parse_if_expr()? } else if self.check(&TokenKind::OpenDelim(Delimiter::Brace)) { @@ -2304,7 +2341,7 @@ impl<'a> Parser<'a> { }, } }; - self.error_on_if_block_attrs(else_span, true, expr.span, &attrs); + self.error_on_if_block_attrs(else_span, true, expr.span, attrs); Ok(expr) } @@ -2313,8 +2350,13 @@ impl<'a> Parser<'a> { ctx_span: Span, is_ctx_else: bool, branch_span: Span, - attrs: &[ast::Attribute], + attrs: AttrWrapper, ) { + if attrs.is_empty() { + return; + } + + let attrs: &[ast::Attribute] = &attrs.take_for_recovery(self.sess); let (attributes, last) = match attrs { [] => return, [x0 @ xn] | [x0, .., xn] => (x0.span.to(xn.span), xn.span), @@ -2329,6 +2371,16 @@ impl<'a> Parser<'a> { }); } + fn error_on_extra_if(&mut self, cond: &P<Expr>) -> PResult<'a, ()> { + if let ExprKind::Binary(Spanned { span: binop_span, node: binop}, _, right) = &cond.kind && + let BinOpKind::And = binop && + let ExprKind::If(cond, ..) = &right.kind { + Err(self.sess.create_err(UnexpectedIfWithIf(binop_span.shrink_to_hi().to(cond.span.shrink_to_lo())))) + } else { + Ok(()) + } + } + /// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten). fn parse_for_expr(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> { // Record whether we are about to parse `for (`. @@ -2762,7 +2814,7 @@ impl<'a> Parser<'a> { fn maybe_parse_struct_expr( &mut self, - qself: Option<&ast::QSelf>, + qself: &Option<P<ast::QSelf>>, path: &ast::Path, ) -> Option<PResult<'a, P<Expr>>> { let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL); @@ -2770,7 +2822,7 @@ impl<'a> Parser<'a> { if let Err(err) = self.expect(&token::OpenDelim(Delimiter::Brace)) { return Some(Err(err)); } - let expr = self.parse_struct_expr(qself.cloned(), path.clone(), true); + let expr = self.parse_struct_expr(qself.clone(), path.clone(), true); if let (Ok(expr), false) = (&expr, struct_allowed) { // This is a struct literal, but we don't can't accept them here. self.sess.emit_err(StructLiteralNotAllowedHere { @@ -2803,7 +2855,7 @@ impl<'a> Parser<'a> { }; while self.token != token::CloseDelim(close_delim) { - if self.eat(&token::DotDot) { + if self.eat(&token::DotDot) || self.recover_struct_field_dots(close_delim) { let exp_span = self.prev_token.span; // We permit `.. }` on the left-hand side of a destructuring assignment. if self.check(&token::CloseDelim(close_delim)) { @@ -2901,7 +2953,7 @@ impl<'a> Parser<'a> { /// Precondition: already parsed the '{'. pub(super) fn parse_struct_expr( &mut self, - qself: Option<ast::QSelf>, + qself: Option<P<ast::QSelf>>, pth: ast::Path, recover: bool, ) -> PResult<'a, P<Expr>> { @@ -2950,6 +3002,18 @@ impl<'a> Parser<'a> { self.recover_stmt(); } + fn recover_struct_field_dots(&mut self, close_delim: Delimiter) -> bool { + if !self.look_ahead(1, |t| *t == token::CloseDelim(close_delim)) + && self.eat(&token::DotDotDot) + { + // recover from typo of `...`, suggest `..` + let span = self.prev_token.span; + self.sess.emit_err(MissingDotDot { token_span: span, sugg_span: span }); + return true; + } + false + } + /// Parses `ident (COLON expr)?`. fn parse_expr_field(&mut self) -> PResult<'a, ExprField> { let attrs = self.parse_outer_attributes()?; @@ -3080,6 +3144,8 @@ impl<'a> Parser<'a> { && this.token.kind == token::Semi { TrailingToken::Semi + } else if this.token.kind == token::Gt { + TrailingToken::Gt } else { // FIXME - pass this through from the place where we know // we need a comma, rather than assuming that `#[attr] expr,` diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 4d0a8b05eb0..fa75670b2ed 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -1,7 +1,9 @@ use super::{ForceCollect, Parser, TrailingToken}; use rustc_ast::token; -use rustc_ast::{self as ast, AttrVec, GenericBounds, GenericParam, GenericParamKind, WhereClause}; +use rustc_ast::{ + self as ast, AttrVec, GenericBounds, GenericParam, GenericParamKind, TyKind, WhereClause, +}; use rustc_errors::{Applicability, PResult}; use rustc_span::symbol::kw; @@ -31,13 +33,43 @@ impl<'a> Parser<'a> { let mut colon_span = None; let bounds = if self.eat(&token::Colon) { colon_span = Some(self.prev_token.span); + // recover from `impl Trait` in type param bound + if self.token.is_keyword(kw::Impl) { + let impl_span = self.token.span; + let snapshot = self.create_snapshot_for_diagnostic(); + match self.parse_ty() { + Ok(p) => { + if let TyKind::ImplTrait(_, bounds) = &(*p).kind { + let span = impl_span.to(self.token.span.shrink_to_lo()); + let mut err = self.struct_span_err( + span, + "expected trait bound, found `impl Trait` type", + ); + err.span_label(span, "not a trait"); + if let [bound, ..] = &bounds[..] { + err.span_suggestion_verbose( + impl_span.until(bound.span()), + "use the trait bounds directly", + String::new(), + Applicability::MachineApplicable, + ); + } + err.emit(); + return Err(err); + } + } + Err(err) => { + err.cancel(); + } + } + self.restore_snapshot(snapshot); + } self.parse_generic_bounds(colon_span)? } else { Vec::new() }; let default = if self.eat(&token::Eq) { Some(self.parse_ty()?) } else { None }; - Ok(GenericParam { ident, id: ast::DUMMY_NODE_ID, diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 25425fbb2c6..e5f58ca3894 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -3,11 +3,11 @@ use crate::errors::{DocCommentDoesNotDocumentAnything, UseEmptyBlockNotSemi}; use super::diagnostics::{dummy_arg, ConsumeClosingDelim}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, TrailingToken}; - use rustc_ast::ast::*; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; +use rustc_ast::util::case::Case; use rustc_ast::{self as ast, AttrVec, Attribute, DUMMY_NODE_ID}; use rustc_ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind}; use rustc_ast::{BindingAnnotation, Block, FnDecl, FnSig, Param, SelfKind}; @@ -21,9 +21,10 @@ use rustc_span::lev_distance::lev_distance; use rustc_span::source_map::{self, Span}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::DUMMY_SP; - use std::convert::TryFrom; use std::mem; +use thin_vec::ThinVec; +use tracing::debug; impl<'a> Parser<'a> { /// Parses a source module as a crate. This is the main entry point for the parser. @@ -34,7 +35,7 @@ impl<'a> Parser<'a> { /// Parses a `mod <foo> { ... }` or `mod <foo>;` item. fn parse_item_mod(&mut self, attrs: &mut AttrVec) -> PResult<'a, ItemInfo> { - let unsafety = self.parse_unsafety(); + let unsafety = self.parse_unsafety(Case::Sensitive); self.expect_keyword(kw::Mod)?; let id = self.parse_ident()?; let mod_kind = if self.eat(&token::Semi) { @@ -143,8 +144,15 @@ impl<'a> Parser<'a> { let lo = self.token.span; let vis = self.parse_visibility(FollowedByType::No)?; let mut def = self.parse_defaultness(); - let kind = - self.parse_item_kind(&mut attrs, mac_allowed, lo, &vis, &mut def, fn_parse_mode)?; + let kind = self.parse_item_kind( + &mut attrs, + mac_allowed, + lo, + &vis, + &mut def, + fn_parse_mode, + Case::Sensitive, + )?; if let Some((ident, kind)) = kind { self.error_on_unconsumed_default(def, &kind); let span = lo.to(self.prev_token.span); @@ -205,16 +213,17 @@ impl<'a> Parser<'a> { vis: &Visibility, def: &mut Defaultness, fn_parse_mode: FnParseMode, + case: Case, ) -> PResult<'a, Option<ItemInfo>> { let def_final = def == &Defaultness::Final; - let mut def = || mem::replace(def, Defaultness::Final); + let mut def_ = || mem::replace(def, Defaultness::Final); - let info = if self.eat_keyword(kw::Use) { + let info = if self.eat_keyword_case(kw::Use, case) { self.parse_use_item()? - } else if self.check_fn_front_matter(def_final) { + } else if self.check_fn_front_matter(def_final, case) { // FUNCTION ITEM let (ident, sig, generics, body) = self.parse_fn(attrs, fn_parse_mode, lo, vis)?; - (ident, ItemKind::Fn(Box::new(Fn { defaultness: def(), sig, generics, body }))) + (ident, ItemKind::Fn(Box::new(Fn { defaultness: def_(), sig, generics, body }))) } else if self.eat_keyword(kw::Extern) { if self.eat_keyword(kw::Crate) { // EXTERN CRATE @@ -225,7 +234,7 @@ impl<'a> Parser<'a> { } } else if self.is_unsafe_foreign_mod() { // EXTERN BLOCK - let unsafety = self.parse_unsafety(); + let unsafety = self.parse_unsafety(Case::Sensitive); self.expect_keyword(kw::Extern)?; self.parse_item_foreign_mod(attrs, unsafety)? } else if self.is_static_global() { @@ -234,15 +243,15 @@ impl<'a> Parser<'a> { let m = self.parse_mutability(); let (ident, ty, expr) = self.parse_item_global(Some(m))?; (ident, ItemKind::Static(ty, m, expr)) - } else if let Const::Yes(const_span) = self.parse_constness() { + } else if let Const::Yes(const_span) = self.parse_constness(Case::Sensitive) { // CONST ITEM if self.token.is_keyword(kw::Impl) { // recover from `const impl`, suggest `impl const` - self.recover_const_impl(const_span, attrs, def())? + self.recover_const_impl(const_span, attrs, def_())? } else { self.recover_const_mut(const_span); let (ident, ty, expr) = self.parse_item_global(None)?; - (ident, ItemKind::Const(def(), ty, expr)) + (ident, ItemKind::Const(def_(), ty, expr)) } } else if self.check_keyword(kw::Trait) || self.check_auto_or_unsafe_trait_item() { // TRAIT ITEM @@ -251,7 +260,7 @@ impl<'a> Parser<'a> { || self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Impl]) { // IMPL ITEM - self.parse_item_impl(attrs, def())? + self.parse_item_impl(attrs, def_())? } else if self.check_keyword(kw::Mod) || self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Mod]) { @@ -259,7 +268,7 @@ impl<'a> Parser<'a> { self.parse_item_mod(attrs)? } else if self.eat_keyword(kw::Type) { // TYPE ITEM - self.parse_type_alias(def())? + self.parse_type_alias(def_())? } else if self.eat_keyword(kw::Enum) { // ENUM ITEM self.parse_item_enum()? @@ -286,6 +295,19 @@ impl<'a> Parser<'a> { } else if self.isnt_macro_invocation() && vis.kind.is_pub() { self.recover_missing_kw_before_item()?; return Ok(None); + } else if self.isnt_macro_invocation() && case == Case::Sensitive { + _ = def_; + + // Recover wrong cased keywords + return self.parse_item_kind( + attrs, + macros_allowed, + lo, + vis, + def, + fn_parse_mode, + Case::Insensitive, + ); } else if macros_allowed && self.check_path() { // MACRO INVOCATION ITEM (Ident::empty(), ItemKind::MacCall(P(self.parse_item_macro(vis)?))) @@ -538,7 +560,7 @@ impl<'a> Parser<'a> { attrs: &mut AttrVec, defaultness: Defaultness, ) -> PResult<'a, ItemInfo> { - let unsafety = self.parse_unsafety(); + let unsafety = self.parse_unsafety(Case::Sensitive); self.expect_keyword(kw::Impl)?; // First, parse generic parameters if necessary. @@ -552,7 +574,7 @@ impl<'a> Parser<'a> { generics }; - let constness = self.parse_constness(); + let constness = self.parse_constness(Case::Sensitive); if let Const::Yes(span) = constness { self.sess.gated_spans.gate(sym::const_trait_impl, span); } @@ -796,7 +818,7 @@ impl<'a> Parser<'a> { /// Parses `unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`. fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemInfo> { - let unsafety = self.parse_unsafety(); + let unsafety = self.parse_unsafety(Case::Sensitive); // Parse optional `auto` prefix. let is_auto = if self.eat_keyword(kw::Auto) { IsAuto::Yes } else { IsAuto::No }; @@ -950,7 +972,8 @@ impl<'a> Parser<'a> { fn parse_use_tree(&mut self) -> PResult<'a, UseTree> { let lo = self.token.span; - let mut prefix = ast::Path { segments: Vec::new(), span: lo.shrink_to_lo(), tokens: None }; + let mut prefix = + ast::Path { segments: ThinVec::new(), span: lo.shrink_to_lo(), tokens: None }; let kind = if self.check(&token::OpenDelim(Delimiter::Brace)) || self.check(&token::BinOp(token::Star)) || self.is_import_coupler() @@ -971,6 +994,23 @@ impl<'a> Parser<'a> { if self.eat(&token::ModSep) { self.parse_use_tree_glob_or_nested()? } else { + // Recover from using a colon as path separator. + while self.eat_noexpect(&token::Colon) { + self.struct_span_err(self.prev_token.span, "expected `::`, found `:`") + .span_suggestion_short( + self.prev_token.span, + "use double colon", + "::", + Applicability::MachineApplicable, + ) + .note_once("import paths are delimited using `::`") + .emit(); + + // We parse the rest of the path and append it to the original prefix. + self.parse_path_segments(&mut prefix.segments, PathStyle::Mod, None)?; + prefix.span = lo.to(self.prev_token.span); + } + UseTreeKind::Simple(self.parse_rename()?, DUMMY_NODE_ID, DUMMY_NODE_ID) } }; @@ -1601,7 +1641,7 @@ impl<'a> Parser<'a> { self.sess.emit_err(err); } else { if !seen_comma { - let sp = self.sess.source_map().next_point(previous_span); + let sp = previous_span.shrink_to_hi(); err.missing_comma = Some(sp); } return Err(err.into_diagnostic(&self.sess.span_diagnostic)); @@ -1745,7 +1785,7 @@ impl<'a> Parser<'a> { let (ident, is_raw) = self.ident_or_err()?; if !is_raw && ident.is_reserved() { let snapshot = self.create_snapshot_for_diagnostic(); - let err = if self.check_fn_front_matter(false) { + let err = if self.check_fn_front_matter(false, Case::Sensitive) { let inherited_vis = Visibility { span: rustc_span::DUMMY_SP, kind: VisibilityKind::Inherited, @@ -1788,7 +1828,28 @@ impl<'a> Parser<'a> { } } } else { - self.expected_ident_found() + let mut err = self.expected_ident_found(); + if self.eat_keyword_noexpect(kw::Let) + && let removal_span = self.prev_token.span.until(self.token.span) + && let Ok(ident) = self.parse_ident_common(false) + // Cancel this error, we don't need it. + .map_err(|err| err.cancel()) + && self.token.kind == TokenKind::Colon + { + err.span_suggestion( + removal_span, + "remove this `let` keyword", + String::new(), + Applicability::MachineApplicable, + ); + err.note("the `let` keyword is not allowed in `struct` fields"); + err.note("see <https://doc.rust-lang.org/book/ch05-01-defining-structs.html> for more information"); + err.emit(); + return Ok(ident); + } else { + self.restore_snapshot(snapshot); + } + err }; return Err(err); } @@ -2134,7 +2195,7 @@ impl<'a> Parser<'a> { /// /// `check_pub` adds additional `pub` to the checks in case users place it /// wrongly, can be used to ensure `pub` never comes after `default`. - pub(super) fn check_fn_front_matter(&mut self, check_pub: bool) -> bool { + pub(super) fn check_fn_front_matter(&mut self, check_pub: bool, case: Case) -> bool { // We use an over-approximation here. // `const const`, `fn const` won't parse, but we're not stepping over other syntax either. // `pub` is added in case users got confused with the ordering like `async pub fn`, @@ -2144,23 +2205,30 @@ impl<'a> Parser<'a> { } else { &[kw::Const, kw::Async, kw::Unsafe, kw::Extern] }; - self.check_keyword(kw::Fn) // Definitely an `fn`. + self.check_keyword_case(kw::Fn, case) // Definitely an `fn`. // `$qual fn` or `$qual $qual`: - || quals.iter().any(|&kw| self.check_keyword(kw)) + || quals.iter().any(|&kw| self.check_keyword_case(kw, case)) && self.look_ahead(1, |t| { // `$qual fn`, e.g. `const fn` or `async fn`. - t.is_keyword(kw::Fn) + t.is_keyword_case(kw::Fn, case) // Two qualifiers `$qual $qual` is enough, e.g. `async unsafe`. - || t.is_non_raw_ident_where(|i| quals.contains(&i.name) - // Rule out 2015 `const async: T = val`. - && i.is_reserved() + || ( + ( + t.is_non_raw_ident_where(|i| + quals.contains(&i.name) + // Rule out 2015 `const async: T = val`. + && i.is_reserved() + ) + || case == Case::Insensitive + && t.is_non_raw_ident_where(|i| quals.iter().any(|qual| qual.as_str() == i.name.as_str().to_lowercase())) + ) // Rule out unsafe extern block. && !self.is_unsafe_foreign_mod()) }) // `extern ABI fn` - || self.check_keyword(kw::Extern) + || self.check_keyword_case(kw::Extern, case) && self.look_ahead(1, |t| t.can_begin_literal_maybe_minus()) - && self.look_ahead(2, |t| t.is_keyword(kw::Fn)) + && self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, case)) } /// Parses all the "front matter" (or "qualifiers") for a `fn` declaration, @@ -2176,22 +2244,22 @@ impl<'a> Parser<'a> { /// `Visibility::Inherited` when no visibility is known. pub(super) fn parse_fn_front_matter(&mut self, orig_vis: &Visibility) -> PResult<'a, FnHeader> { let sp_start = self.token.span; - let constness = self.parse_constness(); + let constness = self.parse_constness(Case::Insensitive); let async_start_sp = self.token.span; - let asyncness = self.parse_asyncness(); + let asyncness = self.parse_asyncness(Case::Insensitive); let unsafe_start_sp = self.token.span; - let unsafety = self.parse_unsafety(); + let unsafety = self.parse_unsafety(Case::Insensitive); let ext_start_sp = self.token.span; - let ext = self.parse_extern(); + let ext = self.parse_extern(Case::Insensitive); if let Async::Yes { span, .. } = asyncness { self.ban_async_in_2015(span); } - if !self.eat_keyword(kw::Fn) { + if !self.eat_keyword_case(kw::Fn, Case::Insensitive) { // It is possible for `expect_one_of` to recover given the contents of // `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't // account for this. diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index b934e087608..13a38a17735 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -22,6 +22,7 @@ use rustc_ast::token::{self, Delimiter, Nonterminal, Token, TokenKind}; use rustc_ast::tokenstream::AttributesData; use rustc_ast::tokenstream::{self, DelimSpan, Spacing}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; +use rustc_ast::util::case::Case; use rustc_ast::AttrId; use rustc_ast::DUMMY_NODE_ID; use rustc_ast::{self as ast, AnonConst, AttrStyle, AttrVec, Const, Extern}; @@ -79,6 +80,7 @@ pub enum ForceCollect { pub enum TrailingToken { None, Semi, + Gt, /// If the trailing token is a comma, then capture it /// Otherwise, ignore the trailing token MaybeComma, @@ -103,6 +105,7 @@ macro_rules! maybe_whole { macro_rules! maybe_recover_from_interpolated_ty_qpath { ($self: expr, $allow_qpath_recovery: expr) => { if $allow_qpath_recovery + && $self.may_recover() && $self.look_ahead(1, |t| t == &token::ModSep) && let token::Interpolated(nt) = &$self.token.kind && let token::NtTy(ty) = &**nt @@ -114,6 +117,12 @@ macro_rules! maybe_recover_from_interpolated_ty_qpath { }; } +#[derive(Clone, Copy)] +pub enum Recovery { + Allowed, + Forbidden, +} + #[derive(Clone)] pub struct Parser<'a> { pub sess: &'a ParseSess, @@ -151,12 +160,15 @@ pub struct Parser<'a> { /// This allows us to recover when the user forget to add braces around /// multiple statements in the closure body. pub current_closure: Option<ClosureSpans>, + /// Whether the parser is allowed to do recovery. + /// This is disabled when parsing macro arguments, see #103534 + pub recovery: Recovery, } -// This type is used a lot, e.g. it's cloned when matching many declarative macro rules. Make sure +// This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure // it doesn't unintentionally get bigger. #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(Parser<'_>, 328); +rustc_data_structures::static_assert_size!(Parser<'_>, 336); /// Stores span information about a closure. #[derive(Clone)] @@ -482,6 +494,7 @@ impl<'a> Parser<'a> { inner_attr_ranges: Default::default(), }, current_closure: None, + recovery: Recovery::Allowed, }; // Make parser point to the first token. @@ -490,6 +503,22 @@ impl<'a> Parser<'a> { parser } + pub fn recovery(mut self, recovery: Recovery) -> Self { + self.recovery = recovery; + self + } + + /// Whether the parser is allowed to recover from broken code. + /// + /// If this returns false, recovering broken code into valid code (especially if this recovery does lookahead) + /// is not allowed. All recovery done by the parser must be gated behind this check. + /// + /// Technically, this only needs to restrict eager recovery by doing lookahead at more tokens. + /// But making the distinction is very subtle, and simply forbidding all recovery is a lot simpler to uphold. + fn may_recover(&self) -> bool { + matches!(self.recovery, Recovery::Allowed) + } + pub fn unexpected<T>(&mut self) -> PResult<'a, T> { match self.expect_one_of(&[], &[]) { Err(e) => Err(e), @@ -608,6 +637,20 @@ impl<'a> Parser<'a> { self.token.is_keyword(kw) } + fn check_keyword_case(&mut self, kw: Symbol, case: Case) -> bool { + if self.check_keyword(kw) { + return true; + } + + if case == Case::Insensitive + && let Some((ident, /* is_raw */ false)) = self.token.ident() + && ident.as_str().to_lowercase() == kw.as_str().to_lowercase() { + true + } else { + false + } + } + /// If the next token is the given keyword, eats it and returns `true`. /// Otherwise, returns `false`. An expectation is also added for diagnostics purposes. // Public for rustfmt usage. @@ -620,6 +663,33 @@ impl<'a> Parser<'a> { } } + /// Eats a keyword, optionally ignoring the case. + /// If the case differs (and is ignored) an error is issued. + /// This is useful for recovery. + fn eat_keyword_case(&mut self, kw: Symbol, case: Case) -> bool { + if self.eat_keyword(kw) { + return true; + } + + if case == Case::Insensitive + && let Some((ident, /* is_raw */ false)) = self.token.ident() + && ident.as_str().to_lowercase() == kw.as_str().to_lowercase() { + self + .struct_span_err(ident.span, format!("keyword `{kw}` is written in a wrong case")) + .span_suggestion( + ident.span, + "write it in the correct case", + kw, + Applicability::MachineApplicable + ).emit(); + + self.bump(); + return true; + } + + false + } + fn eat_keyword_noexpect(&mut self, kw: Symbol) -> bool { if self.token.is_keyword(kw) { self.bump(); @@ -1099,8 +1169,8 @@ impl<'a> Parser<'a> { } /// Parses asyncness: `async` or nothing. - fn parse_asyncness(&mut self) -> Async { - if self.eat_keyword(kw::Async) { + fn parse_asyncness(&mut self, case: Case) -> Async { + if self.eat_keyword_case(kw::Async, case) { let span = self.prev_token.uninterpolated_span(); Async::Yes { span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID } } else { @@ -1109,8 +1179,8 @@ impl<'a> Parser<'a> { } /// Parses unsafety: `unsafe` or nothing. - fn parse_unsafety(&mut self) -> Unsafe { - if self.eat_keyword(kw::Unsafe) { + fn parse_unsafety(&mut self, case: Case) -> Unsafe { + if self.eat_keyword_case(kw::Unsafe, case) { Unsafe::Yes(self.prev_token.uninterpolated_span()) } else { Unsafe::No @@ -1118,10 +1188,10 @@ impl<'a> Parser<'a> { } /// Parses constness: `const` or nothing. - fn parse_constness(&mut self) -> Const { + fn parse_constness(&mut self, case: Case) -> Const { // Avoid const blocks to be parsed as const items if self.look_ahead(1, |t| t != &token::OpenDelim(Delimiter::Brace)) - && self.eat_keyword(kw::Const) + && self.eat_keyword_case(kw::Const, case) { Const::Yes(self.prev_token.uninterpolated_span()) } else { @@ -1376,8 +1446,8 @@ impl<'a> Parser<'a> { } /// Parses `extern string_literal?`. - fn parse_extern(&mut self) -> Extern { - if self.eat_keyword(kw::Extern) { + fn parse_extern(&mut self, case: Case) -> Extern { + if self.eat_keyword_case(kw::Extern, case) { let mut extern_span = self.prev_token.span; let abi = self.parse_abi(); if let Some(abi) = abi { diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 0250b518243..0e202645a39 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -402,6 +402,25 @@ impl<'a> Parser<'a> { } else { PatKind::Path(qself, path) } + } else if matches!(self.token.kind, token::Lifetime(_)) + // In pattern position, we're totally fine with using "next token isn't colon" + // as a heuristic. We could probably just always try to recover if it's a lifetime, + // because we never have `'a: label {}` in a pattern position anyways, but it does + // keep us from suggesting something like `let 'a: Ty = ..` => `let 'a': Ty = ..` + && !self.look_ahead(1, |token| matches!(token.kind, token::Colon)) + { + // Recover a `'a` as a `'a'` literal + let lt = self.expect_lifetime(); + let lit = self.recover_unclosed_char(lt.ident, |self_| { + let expected = expected.unwrap_or("pattern"); + let msg = + format!("expected {}, found {}", expected, super::token_descr(&self_.token)); + + let mut err = self_.struct_span_err(self_.token.span, &msg); + err.span_label(self_.token.span, format!("expected {}", expected)); + err + }); + PatKind::Lit(self.mk_expr(lo, ExprKind::Lit(lit.token_lit))) } else { // Try to parse everything else as literal with optional minus match self.parse_literal_maybe_minus() { @@ -777,7 +796,6 @@ impl<'a> Parser<'a> { /// expression syntax `...expr` for splatting in expressions. fn parse_pat_range_to(&mut self, mut re: Spanned<RangeEnd>) -> PResult<'a, PatKind> { let end = self.parse_pat_range_end()?; - self.sess.gated_spans.gate(sym::half_open_range_patterns, re.span.to(self.prev_token.span)); if let RangeEnd::Included(ref mut syn @ RangeSyntax::DotDotDot) = &mut re.node { *syn = RangeSyntax::DotDotEq; self.struct_span_err(re.span, "range-to patterns with `...` are not allowed") @@ -800,6 +818,7 @@ impl<'a> Parser<'a> { || t.kind == token::Dot // e.g. `.5` for recovery; || t.can_begin_literal_maybe_minus() // e.g. `42`. || t.is_whole_expr() + || t.is_lifetime() // recover `'a` instead of `'a'` }) } @@ -870,7 +889,7 @@ impl<'a> Parser<'a> { } /// Parse a struct ("record") pattern (e.g. `Foo { ... }` or `Foo::Bar { ... }`). - fn parse_pat_struct(&mut self, qself: Option<QSelf>, path: Path) -> PResult<'a, PatKind> { + fn parse_pat_struct(&mut self, qself: Option<P<QSelf>>, path: Path) -> PResult<'a, PatKind> { if qself.is_some() { // Feature gate the use of qualified paths in patterns self.sess.gated_spans.gate(sym::more_qualified_paths, path.span); @@ -887,7 +906,11 @@ impl<'a> Parser<'a> { } /// Parse tuple struct or tuple variant pattern (e.g. `Foo(...)` or `Foo::Bar(...)`). - fn parse_pat_tuple_struct(&mut self, qself: Option<QSelf>, path: Path) -> PResult<'a, PatKind> { + fn parse_pat_tuple_struct( + &mut self, + qself: Option<P<QSelf>>, + path: Path, + ) -> PResult<'a, PatKind> { let (fields, _) = self.parse_paren_comma_seq(|p| { p.parse_pat_allow_top_alt( None, diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index fdc1af27f82..2d432e3f5bd 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -11,8 +11,9 @@ use rustc_ast::{ use rustc_errors::{pluralize, Applicability, PResult}; use rustc_span::source_map::{BytePos, Span}; use rustc_span::symbol::{kw, sym, Ident}; - use std::mem; +use thin_vec::ThinVec; +use tracing::debug; /// Specifies how to parse a path. #[derive(Copy, Clone, PartialEq)] @@ -48,7 +49,7 @@ impl<'a> Parser<'a> { /// `<T as U>::a` /// `<T as U>::F::a<S>` (without disambiguator) /// `<T as U>::F::a::<S>` (with disambiguator) - pub(super) fn parse_qpath(&mut self, style: PathStyle) -> PResult<'a, (QSelf, Path)> { + pub(super) fn parse_qpath(&mut self, style: PathStyle) -> PResult<'a, (P<QSelf>, Path)> { let lo = self.prev_token.span; let ty = self.parse_ty()?; @@ -63,7 +64,7 @@ impl<'a> Parser<'a> { path_span = path_lo.to(self.prev_token.span); } else { path_span = self.token.span.to(self.token.span); - path = ast::Path { segments: Vec::new(), span: path_span, tokens: None }; + path = ast::Path { segments: ThinVec::new(), span: path_span, tokens: None }; } // See doc comment for `unmatched_angle_bracket_count`. @@ -77,7 +78,7 @@ impl<'a> Parser<'a> { self.expect(&token::ModSep)?; } - let qself = QSelf { ty, path_span, position: path.segments.len() }; + let qself = P(QSelf { ty, path_span, position: path.segments.len() }); self.parse_path_segments(&mut path.segments, style, None)?; Ok(( @@ -179,7 +180,7 @@ impl<'a> Parser<'a> { } let lo = self.token.span; - let mut segments = Vec::new(); + let mut segments = ThinVec::new(); let mod_sep_ctxt = self.token.span.ctxt(); if self.eat(&token::ModSep) { segments.push(PathSegment::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt))); @@ -191,7 +192,7 @@ impl<'a> Parser<'a> { pub(super) fn parse_path_segments( &mut self, - segments: &mut Vec<PathSegment>, + segments: &mut ThinVec<PathSegment>, style: PathStyle, ty_generics: Option<&Generics>, ) -> PResult<'a, ()> { @@ -631,7 +632,9 @@ impl<'a> Parser<'a> { /// - A single-segment path. pub(super) fn expr_is_valid_const_arg(&self, expr: &P<rustc_ast::Expr>) -> bool { match &expr.kind { - ast::ExprKind::Block(_, _) | ast::ExprKind::Lit(_) => true, + ast::ExprKind::Block(_, _) + | ast::ExprKind::Lit(_) + | ast::ExprKind::IncludedBytes(..) => true, ast::ExprKind::Unary(ast::UnOp::Neg, expr) => { matches!(expr.kind, ast::ExprKind::Lit(_)) } diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index a61e77b7c3b..7820bbc1789 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -10,8 +10,8 @@ use super::{ use crate::errors::{ AssignmentElseNotAllowed, CompoundAssignmentExpressionInLet, ConstLetMutuallyExclusive, DocCommentDoesNotDocumentAnything, ExpectedStatementAfterOuterAttr, InvalidCurlyInLetElse, - InvalidExpressionInLetElse, InvalidVariableDeclaration, InvalidVariableDeclarationSub, - WrapExpressionInParentheses, + InvalidExpressionInLetElse, InvalidIdentiferStartsWithNumber, InvalidVariableDeclaration, + InvalidVariableDeclarationSub, WrapExpressionInParentheses, }; use crate::maybe_whole; @@ -19,7 +19,7 @@ use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, TokenKind}; use rustc_ast::util::classify; -use rustc_ast::{AttrStyle, AttrVec, Attribute, LocalKind, MacCall, MacCallStmt, MacStmtStyle}; +use rustc_ast::{AttrStyle, AttrVec, LocalKind, MacCall, MacCallStmt, MacStmtStyle}; use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, HasAttrs, Local, Stmt}; use rustc_ast::{StmtKind, DUMMY_NODE_ID}; use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult}; @@ -101,7 +101,7 @@ impl<'a> Parser<'a> { self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item))) } else if self.eat(&token::Semi) { // Do not attempt to parse an expression if we're done here. - self.error_outer_attrs(&attrs.take_for_recovery()); + self.error_outer_attrs(attrs); self.mk_stmt(lo, StmtKind::Empty) } else if self.token != token::CloseDelim(Delimiter::Brace) { // Remainder are line-expr stmts. @@ -120,7 +120,7 @@ impl<'a> Parser<'a> { } self.mk_stmt(lo.to(e.span), StmtKind::Expr(e)) } else { - self.error_outer_attrs(&attrs.take_for_recovery()); + self.error_outer_attrs(attrs); return Ok(None); })) } @@ -199,8 +199,10 @@ impl<'a> Parser<'a> { /// Error on outer attributes in this context. /// Also error if the previous token was a doc comment. - fn error_outer_attrs(&self, attrs: &[Attribute]) { - if let [.., last] = attrs { + fn error_outer_attrs(&self, attrs: AttrWrapper) { + if !attrs.is_empty() + && let attrs = attrs.take_for_recovery(self.sess) + && let attrs @ [.., last] = &*attrs { if last.is_doc_comment() { self.sess.emit_err(DocCommentDoesNotDocumentAnything { span: last.span, @@ -262,6 +264,7 @@ impl<'a> Parser<'a> { self.bump(); } + self.report_invalid_identifier_error()?; let (pat, colon) = self.parse_pat_before_ty(None, RecoverComma::Yes, "`let` bindings")?; let (err, ty) = if colon { @@ -353,6 +356,17 @@ impl<'a> Parser<'a> { Ok(P(ast::Local { ty, pat, kind, id: DUMMY_NODE_ID, span: lo.to(hi), attrs, tokens: None })) } + /// report error for `let 1x = 123` + pub fn report_invalid_identifier_error(&mut self) -> PResult<'a, ()> { + if let token::Literal(lit) = self.token.uninterpolate().kind && + rustc_ast::Lit::from_token(&self.token).is_none() && + (lit.kind == token::LitKind::Integer || lit.kind == token::LitKind::Float) && + self.look_ahead(1, |t| matches!(t.kind, token::Eq) || matches!(t.kind, token::Colon ) ) { + return Err(self.sess.create_err(InvalidIdentiferStartsWithNumber { span: self.token.span })); + } + Ok(()) + } + fn check_let_else_init_bool_expr(&self, init: &ast::Expr) { if let ast::ExprKind::Binary(op, ..) = init.kind { if op.node.lazy() { @@ -553,39 +567,46 @@ impl<'a> Parser<'a> { match stmt.kind { // Expression without semicolon. StmtKind::Expr(ref mut expr) - if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) => - { + if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) => { // Just check for errors and recover; do not eat semicolon yet. - if let Err(mut e) = - self.expect_one_of(&[], &[token::Semi, token::CloseDelim(Delimiter::Brace)]) - { - if let TokenKind::DocComment(..) = self.token.kind { - if let Ok(snippet) = self.span_to_snippet(self.token.span) { - let sp = self.token.span; - let marker = &snippet[..3]; - let (comment_marker, doc_comment_marker) = marker.split_at(2); - - e.span_suggestion( - sp.with_hi(sp.lo() + BytePos(marker.len() as u32)), - &format!( - "add a space before `{}` to use a regular comment", - doc_comment_marker, - ), - format!("{} {}", comment_marker, doc_comment_marker), - Applicability::MaybeIncorrect, - ); + // `expect_one_of` returns PResult<'a, bool /* recovered */> + let replace_with_err = + match self.expect_one_of(&[], &[token::Semi, token::CloseDelim(Delimiter::Brace)]) { + // Recover from parser, skip type error to avoid extra errors. + Ok(true) => true, + Err(mut e) => { + if let TokenKind::DocComment(..) = self.token.kind && + let Ok(snippet) = self.span_to_snippet(self.token.span) { + let sp = self.token.span; + let marker = &snippet[..3]; + let (comment_marker, doc_comment_marker) = marker.split_at(2); + + e.span_suggestion( + sp.with_hi(sp.lo() + BytePos(marker.len() as u32)), + &format!( + "add a space before `{}` to use a regular comment", + doc_comment_marker, + ), + format!("{} {}", comment_marker, doc_comment_marker), + Applicability::MaybeIncorrect, + ); } - } - if let Err(mut e) = - self.check_mistyped_turbofish_with_multiple_type_params(e, expr) - { - if recover.no() { - return Err(e); + + if let Err(mut e) = + self.check_mistyped_turbofish_with_multiple_type_params(e, expr) + { + if recover.no() { + return Err(e); + } + e.emit(); + self.recover_stmt(); } - e.emit(); - self.recover_stmt(); + true } - // Don't complain about type errors in body tail after parse error (#57383). + _ => false + }; + if replace_with_err { + // We already emitted an error, so don't emit another type error let sp = expr.span.to(self.prev_token.span); *expr = self.mk_expr_err(sp); } diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 2a8512acf8c..d6854f07025 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -1,9 +1,11 @@ use super::{Parser, PathStyle, TokenType}; +use crate::errors::{FnPtrWithGenerics, FnPtrWithGenericsSugg}; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; +use rustc_ast::util::case::Case; use rustc_ast::{ self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind, @@ -267,16 +269,21 @@ impl<'a> Parser<'a> { } else if self.eat_keyword(kw::Underscore) { // A type to be inferred `_` TyKind::Infer - } else if self.check_fn_front_matter(false) { + } else if self.check_fn_front_matter(false, Case::Sensitive) { // Function pointer type - self.parse_ty_bare_fn(lo, Vec::new(), recover_return_sign)? + self.parse_ty_bare_fn(lo, Vec::new(), None, recover_return_sign)? } else if self.check_keyword(kw::For) { // Function pointer type or bound list (trait object type) starting with a poly-trait. // `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T` // `for<'lt> Trait1<'lt> + Trait2 + 'a` let lifetime_defs = self.parse_late_bound_lifetime_defs()?; - if self.check_fn_front_matter(false) { - self.parse_ty_bare_fn(lo, lifetime_defs, recover_return_sign)? + if self.check_fn_front_matter(false, Case::Sensitive) { + self.parse_ty_bare_fn( + lo, + lifetime_defs, + Some(self.prev_token.span.shrink_to_lo()), + recover_return_sign, + )? } else { let path = self.parse_path(PathStyle::Type)?; let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus(); @@ -401,7 +408,7 @@ impl<'a> Parser<'a> { .span_suggestions( span.shrink_to_hi(), "add `mut` or `const` here", - ["mut ".to_string(), "const ".to_string()].into_iter(), + ["mut ".to_string(), "const ".to_string()], Applicability::HasPlaceholders, ) .emit(); @@ -518,7 +525,8 @@ impl<'a> Parser<'a> { fn parse_ty_bare_fn( &mut self, lo: Span, - params: Vec<GenericParam>, + mut params: Vec<GenericParam>, + param_insertion_point: Option<Span>, recover_return_sign: RecoverReturnSign, ) -> PResult<'a, TyKind> { let inherited_vis = rustc_ast::Visibility { @@ -529,6 +537,9 @@ impl<'a> Parser<'a> { let span_start = self.token.span; let ast::FnHeader { ext, unsafety, constness, asyncness } = self.parse_fn_front_matter(&inherited_vis)?; + if self.may_recover() && self.token.kind == TokenKind::Lt { + self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?; + } let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?; let whole_span = lo.to(self.prev_token.span); if let ast::Const::Yes(span) = constness { @@ -544,6 +555,48 @@ impl<'a> Parser<'a> { Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params: params, decl, decl_span }))) } + /// Recover from function pointer types with a generic parameter list (e.g. `fn<'a>(&'a str)`). + fn recover_fn_ptr_with_generics( + &mut self, + lo: Span, + params: &mut Vec<GenericParam>, + param_insertion_point: Option<Span>, + ) -> PResult<'a, ()> { + let generics = self.parse_generics()?; + let arity = generics.params.len(); + + let mut lifetimes: Vec<_> = generics + .params + .into_iter() + .filter(|param| matches!(param.kind, ast::GenericParamKind::Lifetime)) + .collect(); + + let sugg = if !lifetimes.is_empty() { + let snippet = + lifetimes.iter().map(|param| param.ident.as_str()).intersperse(", ").collect(); + + let (left, snippet) = if let Some(span) = param_insertion_point { + (span, if params.is_empty() { snippet } else { format!(", {snippet}") }) + } else { + (lo.shrink_to_lo(), format!("for<{snippet}> ")) + }; + + Some(FnPtrWithGenericsSugg { + left, + snippet, + right: generics.span, + arity, + for_param_list_exists: param_insertion_point.is_some(), + }) + } else { + None + }; + + self.sess.emit_err(FnPtrWithGenerics { span: generics.span, sugg }); + params.append(&mut lifetimes); + Ok(()) + } + /// Emit an error for the given bad function pointer qualifier. fn error_fn_ptr_bad_qualifier(&self, span: Span, qual_span: Span, qual: &str) { self.struct_span_err(span, &format!("an `fn` pointer type cannot be `{}`", qual)) diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 47477898b24..8e7f8bfe0f5 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -49,10 +49,12 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta MetaItemKind::List(nmis) } MacArgs::Eq(_, MacArgsEq::Ast(expr)) => { - if let ast::ExprKind::Lit(lit) = &expr.kind { - if !lit.kind.is_unsuffixed() { + if let ast::ExprKind::Lit(token_lit) = expr.kind + && let Ok(lit) = ast::Lit::from_token_lit(token_lit, expr.span) + { + if token_lit.suffix.is_some() { let mut err = sess.span_diagnostic.struct_span_err( - lit.span, + expr.span, "suffixed literals are not allowed in attributes", ); err.help( @@ -61,7 +63,7 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta ); return Err(err); } else { - MetaItemKind::NameValue(lit.clone()) + MetaItemKind::NameValue(lit) } } else { // The non-error case can happen with e.g. `#[foo = 1+1]`. The error case can |
