summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_parse
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--compiler/rustc_parse/Cargo.toml1
-rw-r--r--compiler/rustc_parse/src/errors.rs1237
-rw-r--r--compiler/rustc_parse/src/lexer/mod.rs399
-rw-r--r--compiler/rustc_parse/src/lexer/tokentrees.rs428
-rw-r--r--compiler/rustc_parse/src/lexer/unescape_error_reporting.rs27
-rw-r--r--compiler/rustc_parse/src/lib.rs10
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs176
-rw-r--r--compiler/rustc_parse/src/parser/attr_wrapper.rs109
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs628
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs1282
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs45
-rw-r--r--compiler/rustc_parse/src/parser/item.rs353
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs196
-rw-r--r--compiler/rustc_parse/src/parser/nonterminal.rs24
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs83
-rw-r--r--compiler/rustc_parse/src/parser/path.rs22
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs211
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs50
-rw-r--r--compiler/rustc_parse_format/src/lib.rs117
-rw-r--r--compiler/rustc_parse_format/src/tests.rs58
20 files changed, 3331 insertions, 2125 deletions
diff --git a/compiler/rustc_parse/Cargo.toml b/compiler/rustc_parse/Cargo.toml
index c6ca260e9..a5c94e164 100644
--- a/compiler/rustc_parse/Cargo.toml
+++ b/compiler/rustc_parse/Cargo.toml
@@ -4,7 +4,6 @@ version = "0.0.0"
edition = "2021"
[lib]
-doctest = false
[dependencies]
bitflags = "1.0"
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
new file mode 100644
index 000000000..9b177c518
--- /dev/null
+++ b/compiler/rustc_parse/src/errors.rs
@@ -0,0 +1,1237 @@
+use rustc_ast::token::Token;
+use rustc_ast::Path;
+use rustc_errors::{fluent, AddToDiagnostic, Applicability, EmissionGuarantee, IntoDiagnostic};
+use rustc_macros::{Diagnostic, Subdiagnostic};
+use rustc_session::errors::ExprParenthesesNeeded;
+use rustc_span::symbol::Ident;
+use rustc_span::{Span, Symbol};
+
+use crate::parser::TokenDescription;
+
+#[derive(Diagnostic)]
+#[diag(parser_maybe_report_ambiguous_plus)]
+pub(crate) struct AmbiguousPlus {
+ pub sum_ty: String,
+ #[primary_span]
+ #[suggestion(code = "({sum_ty})")]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_maybe_recover_from_bad_type_plus, code = "E0178")]
+pub(crate) struct BadTypePlus {
+ pub ty: String,
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub sub: BadTypePlusSub,
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum BadTypePlusSub {
+ #[suggestion(
+ parser_add_paren,
+ code = "{sum_with_parens}",
+ applicability = "machine-applicable"
+ )]
+ AddParen {
+ sum_with_parens: String,
+ #[primary_span]
+ span: Span,
+ },
+ #[label(parser_forgot_paren)]
+ ForgotParen {
+ #[primary_span]
+ span: Span,
+ },
+ #[label(parser_expect_path)]
+ ExpectPath {
+ #[primary_span]
+ span: Span,
+ },
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_maybe_recover_from_bad_qpath_stage_2)]
+pub(crate) struct BadQPathStage2 {
+ #[primary_span]
+ #[suggestion(code = "", applicability = "maybe-incorrect")]
+ pub span: Span,
+ pub ty: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_incorrect_semicolon)]
+pub(crate) struct IncorrectSemicolon<'a> {
+ #[primary_span]
+ #[suggestion_short(code = "", applicability = "machine-applicable")]
+ pub span: Span,
+ #[help]
+ pub opt_help: Option<()>,
+ pub name: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_incorrect_use_of_await)]
+pub(crate) struct IncorrectUseOfAwait {
+ #[primary_span]
+ #[suggestion(parentheses_suggestion, code = "", applicability = "machine-applicable")]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_incorrect_use_of_await)]
+pub(crate) struct IncorrectAwait {
+ #[primary_span]
+ pub span: Span,
+ #[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)]
+pub(crate) struct InInTypo {
+ #[primary_span]
+ pub span: Span,
+ #[suggestion(code = "", applicability = "machine-applicable")]
+ pub sugg_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_invalid_variable_declaration)]
+pub(crate) struct InvalidVariableDeclaration {
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub sub: InvalidVariableDeclarationSub,
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum InvalidVariableDeclarationSub {
+ #[suggestion(parser_switch_mut_let_order, applicability = "maybe-incorrect", code = "let mut")]
+ SwitchMutLetOrder(#[primary_span] Span),
+ #[suggestion(
+ 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")]
+ UseLetNotAuto(#[primary_span] Span),
+ #[suggestion(parser_use_let_not_var, applicability = "machine-applicable", code = "let")]
+ UseLetNotVar(#[primary_span] Span),
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_invalid_comparison_operator)]
+pub(crate) struct InvalidComparisonOperator {
+ #[primary_span]
+ pub span: Span,
+ pub invalid: String,
+ #[subdiagnostic]
+ pub sub: InvalidComparisonOperatorSub,
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum InvalidComparisonOperatorSub {
+ #[suggestion_short(use_instead, applicability = "machine-applicable", code = "{correct}")]
+ Correctable {
+ #[primary_span]
+ span: Span,
+ invalid: String,
+ correct: String,
+ },
+ #[label(spaceship_operator_invalid)]
+ Spaceship(#[primary_span] Span),
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_invalid_logical_operator)]
+#[note]
+pub(crate) struct InvalidLogicalOperator {
+ #[primary_span]
+ pub span: Span,
+ pub incorrect: String,
+ #[subdiagnostic]
+ pub sub: InvalidLogicalOperatorSub,
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum InvalidLogicalOperatorSub {
+ #[suggestion_short(
+ use_amp_amp_for_conjunction,
+ applicability = "machine-applicable",
+ code = "&&"
+ )]
+ Conjunction(#[primary_span] Span),
+ #[suggestion_short(
+ use_pipe_pipe_for_disjunction,
+ applicability = "machine-applicable",
+ code = "||"
+ )]
+ Disjunction(#[primary_span] Span),
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_tilde_is_not_unary_operator)]
+pub(crate) struct TildeAsUnaryOperator(
+ #[primary_span]
+ #[suggestion_short(applicability = "machine-applicable", code = "!")]
+ pub Span,
+);
+
+#[derive(Diagnostic)]
+#[diag(parser_unexpected_token_after_not)]
+pub(crate) struct NotAsNegationOperator {
+ #[primary_span]
+ pub negated: Span,
+ pub negated_desc: String,
+ #[subdiagnostic]
+ pub sub: NotAsNegationOperatorSub,
+}
+
+#[derive(Subdiagnostic)]
+pub enum NotAsNegationOperatorSub {
+ #[suggestion_short(
+ parser_unexpected_token_after_not_default,
+ applicability = "machine-applicable",
+ code = "!"
+ )]
+ SuggestNotDefault(#[primary_span] Span),
+
+ #[suggestion_short(
+ parser_unexpected_token_after_not_bitwise,
+ applicability = "machine-applicable",
+ code = "!"
+ )]
+ SuggestNotBitwise(#[primary_span] Span),
+
+ #[suggestion_short(
+ parser_unexpected_token_after_not_logical,
+ applicability = "machine-applicable",
+ code = "!"
+ )]
+ SuggestNotLogical(#[primary_span] Span),
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_malformed_loop_label)]
+pub(crate) struct MalformedLoopLabel {
+ #[primary_span]
+ #[suggestion(applicability = "machine-applicable", code = "{correct_label}")]
+ pub span: Span,
+ pub correct_label: Ident,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_lifetime_in_borrow_expression)]
+pub(crate) struct LifetimeInBorrowExpression {
+ #[primary_span]
+ pub span: Span,
+ #[suggestion(applicability = "machine-applicable", code = "")]
+ #[label]
+ pub lifetime_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_field_expression_with_generic)]
+pub(crate) struct FieldExpressionWithGeneric(#[primary_span] pub Span);
+
+#[derive(Diagnostic)]
+#[diag(parser_macro_invocation_with_qualified_path)]
+pub(crate) struct MacroInvocationWithQualifiedPath(#[primary_span] pub Span);
+
+#[derive(Diagnostic)]
+#[diag(parser_unexpected_token_after_label)]
+pub(crate) struct UnexpectedTokenAfterLabel {
+ #[primary_span]
+ #[label(parser_unexpected_token_after_label)]
+ pub span: Span,
+ #[suggestion_verbose(suggestion_remove_label, code = "")]
+ pub remove_label: Option<Span>,
+ #[subdiagnostic]
+ pub enclose_in_block: Option<UnexpectedTokenAfterLabelSugg>,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(suggestion_enclose_in_block, applicability = "machine-applicable")]
+pub(crate) struct UnexpectedTokenAfterLabelSugg {
+ #[suggestion_part(code = "{{ ")]
+ pub left: Span,
+ #[suggestion_part(code = " }}")]
+ pub right: Span,
+}
+
+#[derive(Diagnostic)]
+#[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 = ": ")]
+ pub label_end: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_do_catch_syntax_removed)]
+#[note]
+pub(crate) struct DoCatchSyntaxRemoved {
+ #[primary_span]
+ #[suggestion(applicability = "machine-applicable", code = "try")]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_float_literal_requires_integer_part)]
+pub(crate) struct FloatLiteralRequiresIntegerPart {
+ #[primary_span]
+ #[suggestion(applicability = "machine-applicable", code = "{correct}")]
+ pub span: Span,
+ pub correct: String,
+}
+
+#[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 {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_missing_semicolon_before_array)]
+pub(crate) struct MissingSemicolonBeforeArray {
+ #[primary_span]
+ pub open_delim: Span,
+ #[suggestion_verbose(applicability = "maybe-incorrect", code = ";")]
+ pub semicolon: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_invalid_block_macro_segment)]
+pub(crate) struct InvalidBlockMacroSegment {
+ #[primary_span]
+ pub span: Span,
+ #[label]
+ pub context: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_if_expression_missing_then_block)]
+pub(crate) struct IfExpressionMissingThenBlock {
+ #[primary_span]
+ pub if_span: Span,
+ #[subdiagnostic]
+ pub sub: IfExpressionMissingThenBlockSub,
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum IfExpressionMissingThenBlockSub {
+ #[help(condition_possibly_unfinished)]
+ UnfinishedCondition(#[primary_span] Span),
+ #[help(add_then_block)]
+ AddThenBlock(#[primary_span] Span),
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_if_expression_missing_condition)]
+pub(crate) struct IfExpressionMissingCondition {
+ #[primary_span]
+ #[label(condition_label)]
+ pub if_span: Span,
+ #[label(block_label)]
+ pub block_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_expected_expression_found_let)]
+pub(crate) struct ExpectedExpressionFoundLet {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_expected_else_block)]
+pub(crate) struct ExpectedElseBlock {
+ #[primary_span]
+ pub first_tok_span: Span,
+ pub first_tok: String,
+ #[label]
+ pub else_span: Span,
+ #[suggestion(applicability = "maybe-incorrect", code = "if ")]
+ pub condition_start: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_outer_attribute_not_allowed_on_if_else)]
+pub(crate) struct OuterAttributeNotAllowedOnIfElse {
+ #[primary_span]
+ pub last: Span,
+
+ #[label(branch_label)]
+ pub branch_span: Span,
+
+ #[label(ctx_label)]
+ pub ctx_span: Span,
+ pub ctx: String,
+
+ #[suggestion(applicability = "machine-applicable", code = "")]
+ pub attributes: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_missing_in_in_for_loop)]
+pub(crate) struct MissingInInForLoop {
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub sub: MissingInInForLoopSub,
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum MissingInInForLoopSub {
+ // Has been misleading, at least in the past (closed Issue #48492), thus maybe-incorrect
+ #[suggestion_short(use_in_not_of, applicability = "maybe-incorrect", code = "in")]
+ InNotOf(#[primary_span] Span),
+ #[suggestion_short(add_in, applicability = "maybe-incorrect", code = " in ")]
+ AddIn(#[primary_span] Span),
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_missing_comma_after_match_arm)]
+pub(crate) struct MissingCommaAfterMatchArm {
+ #[primary_span]
+ #[suggestion(applicability = "machine-applicable", code = ",")]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_catch_after_try)]
+#[help]
+pub(crate) struct CatchAfterTry {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_comma_after_base_struct)]
+#[note]
+pub(crate) struct CommaAfterBaseStruct {
+ #[primary_span]
+ pub span: Span,
+ #[suggestion_short(applicability = "machine-applicable", code = "")]
+ pub comma: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_eq_field_init)]
+pub(crate) struct EqFieldInit {
+ #[primary_span]
+ pub span: Span,
+ #[suggestion(applicability = "machine-applicable", code = ":")]
+ pub eq: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_dotdotdot)]
+pub(crate) struct DotDotDot {
+ #[primary_span]
+ #[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)]
+pub(crate) struct LeftArrowOperator {
+ #[primary_span]
+ #[suggestion(applicability = "maybe-incorrect", code = "< -")]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_remove_let)]
+pub(crate) struct RemoveLet {
+ #[primary_span]
+ #[suggestion(applicability = "machine-applicable", code = "")]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_use_eq_instead)]
+pub(crate) struct UseEqInstead {
+ #[primary_span]
+ #[suggestion_short(applicability = "machine-applicable", code = "=")]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_use_empty_block_not_semi)]
+pub(crate) struct UseEmptyBlockNotSemi {
+ #[primary_span]
+ #[suggestion_hidden(applicability = "machine-applicable", code = "{{}}")]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_comparison_interpreted_as_generic)]
+pub(crate) struct ComparisonInterpretedAsGeneric {
+ #[primary_span]
+ #[label(label_comparison)]
+ pub comparison: Span,
+ pub r#type: Path,
+ #[label(label_args)]
+ pub args: Span,
+ #[subdiagnostic]
+ pub suggestion: ComparisonOrShiftInterpretedAsGenericSugg,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_shift_interpreted_as_generic)]
+pub(crate) struct ShiftInterpretedAsGeneric {
+ #[primary_span]
+ #[label(label_comparison)]
+ pub shift: Span,
+ pub r#type: Path,
+ #[label(label_args)]
+ pub args: Span,
+ #[subdiagnostic]
+ pub suggestion: ComparisonOrShiftInterpretedAsGenericSugg,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(suggestion, applicability = "machine-applicable")]
+pub(crate) struct ComparisonOrShiftInterpretedAsGenericSugg {
+ #[suggestion_part(code = "(")]
+ pub left: Span,
+ #[suggestion_part(code = ")")]
+ pub right: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_found_expr_would_be_stmt)]
+pub(crate) struct FoundExprWouldBeStmt {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ pub token: Token,
+ #[subdiagnostic]
+ pub suggestion: ExprParenthesesNeeded,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_leading_plus_not_supported)]
+pub(crate) struct LeadingPlusNotSupported {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ #[suggestion_verbose(suggestion_remove_plus, code = "", applicability = "machine-applicable")]
+ pub remove_plus: Option<Span>,
+ #[subdiagnostic]
+ pub add_parentheses: Option<ExprParenthesesNeeded>,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_parentheses_with_struct_fields)]
+pub(crate) struct ParenthesesWithStructFields {
+ #[primary_span]
+ pub span: Span,
+ pub r#type: Path,
+ #[subdiagnostic]
+ pub braces_for_struct: BracesForStructLiteral,
+ #[subdiagnostic]
+ pub no_fields_for_fn: NoFieldsForFnCall,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(suggestion_braces_for_struct, applicability = "maybe-incorrect")]
+pub(crate) struct BracesForStructLiteral {
+ #[suggestion_part(code = " {{ ")]
+ pub first: Span,
+ #[suggestion_part(code = " }}")]
+ pub second: Span,
+}
+
+#[derive(Subdiagnostic)]
+#[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)]
+pub(crate) struct LabeledLoopInBreak {
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub sub: WrapExpressionInParentheses,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(
+ parser_sugg_wrap_expression_in_parentheses,
+ applicability = "machine-applicable"
+)]
+pub(crate) struct WrapExpressionInParentheses {
+ #[suggestion_part(code = "(")]
+ pub left: Span,
+ #[suggestion_part(code = ")")]
+ pub right: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_array_brackets_instead_of_braces)]
+pub(crate) struct ArrayBracketsInsteadOfSpaces {
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub sub: ArrayBracketsInsteadOfSpacesSugg,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(suggestion, applicability = "maybe-incorrect")]
+pub(crate) struct ArrayBracketsInsteadOfSpacesSugg {
+ #[suggestion_part(code = "[")]
+ pub left: Span,
+ #[suggestion_part(code = "]")]
+ pub right: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_match_arm_body_without_braces)]
+pub(crate) struct MatchArmBodyWithoutBraces {
+ #[primary_span]
+ #[label(label_statements)]
+ pub statements: Span,
+ #[label(label_arrow)]
+ pub arrow: Span,
+ pub num_statements: usize,
+ #[subdiagnostic]
+ pub sub: MatchArmBodyWithoutBracesSugg,
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum MatchArmBodyWithoutBracesSugg {
+ #[multipart_suggestion(suggestion_add_braces, applicability = "machine-applicable")]
+ AddBraces {
+ #[suggestion_part(code = "{{ ")]
+ left: Span,
+ #[suggestion_part(code = " }}")]
+ right: Span,
+ },
+ #[suggestion(
+ suggestion_use_comma_not_semicolon,
+ code = ",",
+ applicability = "machine-applicable"
+ )]
+ UseComma {
+ #[primary_span]
+ semicolon: Span,
+ },
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_struct_literal_not_allowed_here)]
+pub(crate) struct StructLiteralNotAllowedHere {
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub sub: StructLiteralNotAllowedHereSugg,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(suggestion, applicability = "machine-applicable")]
+pub(crate) struct StructLiteralNotAllowedHereSugg {
+ #[suggestion_part(code = "(")]
+ pub left: Span,
+ #[suggestion_part(code = ")")]
+ pub right: Span,
+}
+
+#[derive(Diagnostic)]
+#[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)]
+pub(crate) struct InvalidLiteralSuffixOnTupleIndex {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ pub suffix: Symbol,
+ #[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)]
+pub(crate) struct NonStringAbiLiteral {
+ #[primary_span]
+ #[suggestion(code = "\"C\"", applicability = "maybe-incorrect")]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_mismatched_closing_delimiter)]
+pub(crate) struct MismatchedClosingDelimiter {
+ #[primary_span]
+ pub spans: Vec<Span>,
+ pub delimiter: String,
+ #[label(label_unmatched)]
+ pub unmatched: Span,
+ #[label(label_opening_candidate)]
+ pub opening_candidate: Option<Span>,
+ #[label(label_unclosed)]
+ pub unclosed: Option<Span>,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_incorrect_visibility_restriction, code = "E0704")]
+#[help]
+pub(crate) struct IncorrectVisibilityRestriction {
+ #[primary_span]
+ #[suggestion(code = "in {inner_str}", applicability = "machine-applicable")]
+ pub span: Span,
+ pub inner_str: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_assignment_else_not_allowed)]
+pub(crate) struct AssignmentElseNotAllowed {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[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")]
+#[help]
+pub(crate) struct DocCommentDoesNotDocumentAnything {
+ #[primary_span]
+ pub span: Span,
+ #[suggestion(code = ",", applicability = "machine-applicable")]
+ pub missing_comma: Option<Span>,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_const_let_mutually_exclusive)]
+pub(crate) struct ConstLetMutuallyExclusive {
+ #[primary_span]
+ #[suggestion(code = "const", applicability = "maybe-incorrect")]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_invalid_expression_in_let_else)]
+pub(crate) struct InvalidExpressionInLetElse {
+ #[primary_span]
+ pub span: Span,
+ pub operator: &'static str,
+ #[subdiagnostic]
+ pub sugg: WrapExpressionInParentheses,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_invalid_curly_in_let_else)]
+pub(crate) struct InvalidCurlyInLetElse {
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub sugg: WrapExpressionInParentheses,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_compound_assignment_expression_in_let)]
+#[help]
+pub(crate) struct CompoundAssignmentExpressionInLet {
+ #[primary_span]
+ #[suggestion_short(code = "=", applicability = "maybe-incorrect")]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_suffixed_literal_in_attribute)]
+#[help]
+pub(crate) struct SuffixedLiteralInAttribute {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_invalid_meta_item)]
+pub(crate) struct InvalidMetaItem {
+ #[primary_span]
+ pub span: Span,
+ pub token: Token,
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion_verbose(
+ parser_sugg_escape_to_use_as_identifier,
+ applicability = "maybe-incorrect",
+ code = "r#"
+)]
+pub(crate) struct SuggEscapeToUseAsIdentifier {
+ #[primary_span]
+ pub span: Span,
+ pub ident_name: String,
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(parser_sugg_remove_comma, applicability = "machine-applicable", code = "")]
+pub(crate) struct SuggRemoveComma {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum ExpectedIdentifierFound {
+ #[label(parser_expected_identifier_found_reserved_identifier)]
+ ReservedIdentifier(#[primary_span] Span),
+ #[label(parser_expected_identifier_found_keyword)]
+ Keyword(#[primary_span] Span),
+ #[label(parser_expected_identifier_found_reserved_keyword)]
+ ReservedKeyword(#[primary_span] Span),
+ #[label(parser_expected_identifier_found_doc_comment)]
+ DocComment(#[primary_span] Span),
+ #[label(parser_expected_identifier)]
+ Other(#[primary_span] Span),
+}
+
+impl ExpectedIdentifierFound {
+ pub fn new(token_descr: Option<TokenDescription>, span: Span) -> Self {
+ (match token_descr {
+ Some(TokenDescription::ReservedIdentifier) => {
+ ExpectedIdentifierFound::ReservedIdentifier
+ }
+ Some(TokenDescription::Keyword) => ExpectedIdentifierFound::Keyword,
+ Some(TokenDescription::ReservedKeyword) => ExpectedIdentifierFound::ReservedKeyword,
+ Some(TokenDescription::DocComment) => ExpectedIdentifierFound::DocComment,
+ None => ExpectedIdentifierFound::Other,
+ })(span)
+ }
+}
+
+pub(crate) struct ExpectedIdentifier {
+ pub span: Span,
+ pub token: Token,
+ pub suggest_raw: Option<SuggEscapeToUseAsIdentifier>,
+ pub suggest_remove_comma: Option<SuggRemoveComma>,
+}
+
+impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for ExpectedIdentifier {
+ fn into_diagnostic(
+ self,
+ handler: &'a rustc_errors::Handler,
+ ) -> rustc_errors::DiagnosticBuilder<'a, G> {
+ let token_descr = super::parser::TokenDescription::from_token(&self.token);
+
+ 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,
+ Some(TokenDescription::ReservedKeyword) => {
+ fluent::parser_expected_identifier_found_reserved_keyword_str
+ }
+ Some(TokenDescription::DocComment) => {
+ fluent::parser_expected_identifier_found_doc_comment_str
+ }
+ None => fluent::parser_expected_identifier_found_str,
+ });
+ diag.set_span(self.span);
+ diag.set_arg("token", self.token);
+
+ if let Some(sugg) = self.suggest_raw {
+ sugg.add_to_diagnostic(&mut diag);
+ }
+
+ ExpectedIdentifierFound::new(token_descr, self.span).add_to_diagnostic(&mut diag);
+
+ if let Some(sugg) = self.suggest_remove_comma {
+ sugg.add_to_diagnostic(&mut diag);
+ }
+
+ diag
+ }
+}
+
+pub(crate) struct ExpectedSemi {
+ pub span: Span,
+ pub token: Token,
+
+ pub unexpected_token_label: Option<Span>,
+ pub sugg: ExpectedSemiSugg,
+}
+
+impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for ExpectedSemi {
+ fn into_diagnostic(
+ self,
+ handler: &'a rustc_errors::Handler,
+ ) -> rustc_errors::DiagnosticBuilder<'a, G> {
+ let token_descr = super::parser::TokenDescription::from_token(&self.token);
+
+ let mut diag = handler.struct_diagnostic(match token_descr {
+ Some(TokenDescription::ReservedIdentifier) => {
+ fluent::parser_expected_semi_found_reserved_identifier_str
+ }
+ Some(TokenDescription::Keyword) => fluent::parser_expected_semi_found_keyword_str,
+ Some(TokenDescription::ReservedKeyword) => {
+ fluent::parser_expected_semi_found_reserved_keyword_str
+ }
+ Some(TokenDescription::DocComment) => {
+ fluent::parser_expected_semi_found_doc_comment_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);
+ }
+
+ self.sugg.add_to_diagnostic(&mut diag);
+
+ diag
+ }
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum ExpectedSemiSugg {
+ #[suggestion(
+ parser_sugg_change_this_to_semi,
+ code = ";",
+ applicability = "machine-applicable"
+ )]
+ ChangeToSemi(#[primary_span] Span),
+ #[suggestion_short(parser_sugg_add_semi, code = ";", applicability = "machine-applicable")]
+ AddSemi(#[primary_span] Span),
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_struct_literal_body_without_path)]
+pub(crate) struct StructLiteralBodyWithoutPath {
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub sugg: StructLiteralBodyWithoutPathSugg,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(suggestion, applicability = "has-placeholders")]
+pub(crate) struct StructLiteralBodyWithoutPathSugg {
+ #[suggestion_part(code = "{{ SomeStruct ")]
+ pub before: Span,
+ #[suggestion_part(code = " }}")]
+ pub after: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_unmatched_angle_brackets)]
+pub(crate) struct UnmatchedAngleBrackets {
+ #[primary_span]
+ #[suggestion(code = "", applicability = "machine-applicable")]
+ pub span: Span,
+ pub num_extra_brackets: usize,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_generic_parameters_without_angle_brackets)]
+pub(crate) struct GenericParamsWithoutAngleBrackets {
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub sugg: GenericParamsWithoutAngleBracketsSugg,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(suggestion, applicability = "machine-applicable")]
+pub(crate) struct GenericParamsWithoutAngleBracketsSugg {
+ #[suggestion_part(code = "<")]
+ pub left: Span,
+ #[suggestion_part(code = ">")]
+ pub right: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_comparison_operators_cannot_be_chained)]
+pub(crate) struct ComparisonOperatorsCannotBeChained {
+ #[primary_span]
+ pub span: Vec<Span>,
+ #[suggestion_verbose(
+ parser_sugg_turbofish_syntax,
+ code = "::",
+ applicability = "maybe-incorrect"
+ )]
+ pub suggest_turbofish: Option<Span>,
+ #[help(parser_sugg_turbofish_syntax)]
+ #[help(sugg_parentheses_for_function_args)]
+ pub help_turbofish: Option<()>,
+ #[subdiagnostic]
+ pub chaining_sugg: Option<ComparisonOperatorsCannotBeChainedSugg>,
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum ComparisonOperatorsCannotBeChainedSugg {
+ #[suggestion_verbose(
+ sugg_split_comparison,
+ code = " && {middle_term}",
+ applicability = "maybe-incorrect"
+ )]
+ SplitComparison {
+ #[primary_span]
+ span: Span,
+ middle_term: String,
+ },
+ #[multipart_suggestion(sugg_parenthesize, applicability = "maybe-incorrect")]
+ Parenthesize {
+ #[suggestion_part(code = "(")]
+ left: Span,
+ #[suggestion_part(code = ")")]
+ right: Span,
+ },
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_question_mark_in_type)]
+pub(crate) struct QuestionMarkInType {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ #[subdiagnostic]
+ pub sugg: QuestionMarkInTypeSugg,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(suggestion, applicability = "machine-applicable")]
+pub(crate) struct QuestionMarkInTypeSugg {
+ #[suggestion_part(code = "Option<")]
+ pub left: Span,
+ #[suggestion_part(code = ">")]
+ pub right: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_unexpected_parentheses_in_for_head)]
+pub(crate) struct ParenthesesInForHead {
+ #[primary_span]
+ pub span: Vec<Span>,
+ #[subdiagnostic]
+ pub sugg: ParenthesesInForHeadSugg,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(suggestion, applicability = "machine-applicable")]
+pub(crate) struct ParenthesesInForHeadSugg {
+ #[suggestion_part(code = "")]
+ pub left: Span,
+ #[suggestion_part(code = "")]
+ pub right: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_doc_comment_on_param_type)]
+pub(crate) struct DocCommentOnParamType {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_attribute_on_param_type)]
+pub(crate) struct AttributeOnParamType {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_pattern_method_param_without_body, code = "E0642")]
+pub(crate) struct PatternMethodParamWithoutBody {
+ #[primary_span]
+ #[suggestion(code = "_", applicability = "machine-applicable")]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_self_param_not_first)]
+pub(crate) struct SelfParamNotFirst {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_const_generic_without_braces)]
+pub(crate) struct ConstGenericWithoutBraces {
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub sugg: ConstGenericWithoutBracesSugg,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(suggestion, applicability = "machine-applicable")]
+pub(crate) struct ConstGenericWithoutBracesSugg {
+ #[suggestion_part(code = "{{ ")]
+ pub left: Span,
+ #[suggestion_part(code = " }}")]
+ pub right: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_unexpected_const_param_declaration)]
+pub(crate) struct UnexpectedConstParamDeclaration {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ #[subdiagnostic]
+ pub sugg: Option<UnexpectedConstParamDeclarationSugg>,
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum UnexpectedConstParamDeclarationSugg {
+ #[multipart_suggestion(suggestion, applicability = "machine-applicable")]
+ AddParam {
+ #[suggestion_part(code = "<{snippet}>")]
+ impl_generics: Span,
+ #[suggestion_part(code = "{ident}")]
+ incorrect_decl: Span,
+ snippet: String,
+ ident: String,
+ },
+ #[multipart_suggestion(suggestion, applicability = "machine-applicable")]
+ AppendParam {
+ #[suggestion_part(code = ", {snippet}")]
+ impl_generics_end: Span,
+ #[suggestion_part(code = "{ident}")]
+ incorrect_decl: Span,
+ snippet: String,
+ ident: String,
+ },
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_unexpected_const_in_generic_param)]
+pub(crate) struct UnexpectedConstInGenericParam {
+ #[primary_span]
+ pub span: Span,
+ #[suggestion_verbose(code = "", applicability = "maybe-incorrect")]
+ pub to_remove: Option<Span>,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_async_move_order_incorrect)]
+pub(crate) struct AsyncMoveOrderIncorrect {
+ #[primary_span]
+ #[suggestion_verbose(code = "async move", applicability = "maybe-incorrect")]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_double_colon_in_bound)]
+pub(crate) struct DoubleColonInBound {
+ #[primary_span]
+ pub span: Span,
+ #[suggestion(code = ": ", applicability = "machine-applicable")]
+ pub between: Span,
+}
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index 848e142e5..462bce16a 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -1,10 +1,13 @@
use crate::lexer::unicode_chars::UNICODE_ARRAY;
use rustc_ast::ast::{self, AttrStyle};
use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind};
-use rustc_ast::tokenstream::{Spacing, TokenStream};
+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};
use rustc_session::lint::builtin::{
RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
@@ -14,8 +17,6 @@ use rustc_session::parse::ParseSess;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::{edition::Edition, BytePos, Pos, Span};
-use tracing::debug;
-
mod tokentrees;
mod unescape_error_reporting;
mod unicode_chars;
@@ -40,11 +41,20 @@ pub struct UnmatchedBrace {
pub(crate) fn parse_token_trees<'a>(
sess: &'a ParseSess,
- src: &'a str,
- start_pos: BytePos,
+ mut src: &'a str,
+ mut start_pos: BytePos,
override_span: Option<Span>,
) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) {
- StringReader { sess, start_pos, pos: start_pos, src, override_span }.into_token_trees()
+ // Skip `#!`, if present.
+ if let Some(shebang_len) = rustc_lexer::strip_shebang(src) {
+ src = &src[shebang_len..];
+ start_pos = start_pos + BytePos::from_usize(shebang_len);
+ }
+
+ let cursor = Cursor::new(src);
+ let string_reader =
+ StringReader { sess, start_pos, pos: start_pos, src, cursor, override_span };
+ tokentrees::TokenTreesReader::parse_all_token_trees(string_reader)
}
struct StringReader<'a> {
@@ -55,6 +65,8 @@ struct StringReader<'a> {
pos: BytePos,
/// Source text to tokenize.
src: &'a str,
+ /// Cursor for getting lexer tokens.
+ cursor: Cursor<'a>,
override_span: Option<Span>,
}
@@ -63,42 +75,198 @@ impl<'a> StringReader<'a> {
self.override_span.unwrap_or_else(|| Span::with_root_ctxt(lo, hi))
}
- /// Returns the next token, and info about preceding whitespace, if any.
- fn next_token(&mut self) -> (Spacing, Token) {
- let mut spacing = Spacing::Joint;
-
- // Skip `#!` at the start of the file
- if self.pos == self.start_pos
- && let Some(shebang_len) = rustc_lexer::strip_shebang(self.src)
- {
- self.pos = self.pos + BytePos::from_usize(shebang_len);
- spacing = Spacing::Alone;
- }
+ /// Returns the next token, paired with a bool indicating if the token was
+ /// preceded by whitespace.
+ fn next_token(&mut self) -> (Token, bool) {
+ let mut preceded_by_whitespace = false;
// Skip trivial (whitespace & comments) tokens
loop {
- let start_src_index = self.src_index(self.pos);
- let text: &str = &self.src[start_src_index..];
-
- if text.is_empty() {
- let span = self.mk_sp(self.pos, self.pos);
- return (spacing, Token::new(token::Eof, span));
- }
-
- let token = rustc_lexer::first_token(text);
-
+ let token = self.cursor.advance_token();
let start = self.pos;
self.pos = self.pos + BytePos(token.len);
debug!("next_token: {:?}({:?})", token.kind, self.str_from(start));
- match self.cook_lexer_token(token.kind, start) {
- Some(kind) => {
+ // Now "cook" the token, converting the simple `rustc_lexer::TokenKind` enum into a
+ // rich `rustc_ast::TokenKind`. This turns strings into interned symbols and runs
+ // additional validation.
+ let kind = match token.kind {
+ rustc_lexer::TokenKind::LineComment { doc_style } => {
+ // Skip non-doc comments
+ let Some(doc_style) = doc_style else {
+ self.lint_unicode_text_flow(start);
+ preceded_by_whitespace = true;
+ continue;
+ };
+
+ // Opening delimiter of the length 3 is not included into the symbol.
+ let content_start = start + BytePos(3);
+ let content = self.str_from(content_start);
+ self.cook_doc_comment(content_start, content, CommentKind::Line, doc_style)
+ }
+ rustc_lexer::TokenKind::BlockComment { doc_style, terminated } => {
+ if !terminated {
+ self.report_unterminated_block_comment(start, doc_style);
+ }
+
+ // Skip non-doc comments
+ let Some(doc_style) = doc_style else {
+ self.lint_unicode_text_flow(start);
+ preceded_by_whitespace = true;
+ continue;
+ };
+
+ // Opening delimiter of the length 3 and closing delimiter of the length 2
+ // are not included into the symbol.
+ let content_start = start + BytePos(3);
+ let content_end = self.pos - BytePos(if terminated { 2 } else { 0 });
+ let content = self.str_from_to(content_start, content_end);
+ self.cook_doc_comment(content_start, content, CommentKind::Block, doc_style)
+ }
+ rustc_lexer::TokenKind::Whitespace => {
+ preceded_by_whitespace = true;
+ continue;
+ }
+ rustc_lexer::TokenKind::Ident => {
+ let sym = nfc_normalize(self.str_from(start));
let span = self.mk_sp(start, self.pos);
- return (spacing, Token::new(kind, span));
+ self.sess.symbol_gallery.insert(sym, span);
+ token::Ident(sym, false)
}
- None => spacing = Spacing::Alone,
- }
+ rustc_lexer::TokenKind::RawIdent => {
+ let sym = nfc_normalize(self.str_from(start + BytePos(2)));
+ let span = self.mk_sp(start, self.pos);
+ self.sess.symbol_gallery.insert(sym, span);
+ if !sym.can_be_raw() {
+ self.err_span(span, &format!("`{}` cannot be a raw identifier", sym));
+ }
+ self.sess.raw_identifier_spans.borrow_mut().push(span);
+ token::Ident(sym, true)
+ }
+ rustc_lexer::TokenKind::UnknownPrefix => {
+ self.report_unknown_prefix(start);
+ let sym = nfc_normalize(self.str_from(start));
+ let span = self.mk_sp(start, self.pos);
+ self.sess.symbol_gallery.insert(sym, span);
+ token::Ident(sym, false)
+ }
+ rustc_lexer::TokenKind::InvalidIdent
+ // Do not recover an identifier with emoji if the codepoint is a confusable
+ // with a recoverable substitution token, like `âž–`.
+ if !UNICODE_ARRAY
+ .iter()
+ .any(|&(c, _, _)| {
+ let sym = self.str_from(start);
+ sym.chars().count() == 1 && c == sym.chars().next().unwrap()
+ }) =>
+ {
+ let sym = nfc_normalize(self.str_from(start));
+ let span = self.mk_sp(start, self.pos);
+ self.sess.bad_unicode_identifiers.borrow_mut().entry(sym).or_default()
+ .push(span);
+ token::Ident(sym, false)
+ }
+ rustc_lexer::TokenKind::Literal { kind, suffix_start } => {
+ let suffix_start = start + BytePos(suffix_start);
+ let (kind, symbol) = self.cook_lexer_literal(start, suffix_start, kind);
+ let suffix = if suffix_start < self.pos {
+ let string = self.str_from(suffix_start);
+ if string == "_" {
+ self.sess
+ .span_diagnostic
+ .struct_span_warn(
+ 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 {
+ Some(Symbol::intern(string))
+ }
+ } else {
+ None
+ };
+ token::Literal(token::Lit { kind, symbol, suffix })
+ }
+ rustc_lexer::TokenKind::Lifetime { starts_with_number } => {
+ // Include the leading `'` in the real identifier, for macro
+ // expansion purposes. See #12512 for the gory details of why
+ // this is necessary.
+ let lifetime_name = self.str_from(start);
+ if starts_with_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)
+ }
+ rustc_lexer::TokenKind::Semi => token::Semi,
+ rustc_lexer::TokenKind::Comma => token::Comma,
+ rustc_lexer::TokenKind::Dot => token::Dot,
+ rustc_lexer::TokenKind::OpenParen => token::OpenDelim(Delimiter::Parenthesis),
+ rustc_lexer::TokenKind::CloseParen => token::CloseDelim(Delimiter::Parenthesis),
+ rustc_lexer::TokenKind::OpenBrace => token::OpenDelim(Delimiter::Brace),
+ rustc_lexer::TokenKind::CloseBrace => token::CloseDelim(Delimiter::Brace),
+ rustc_lexer::TokenKind::OpenBracket => token::OpenDelim(Delimiter::Bracket),
+ rustc_lexer::TokenKind::CloseBracket => token::CloseDelim(Delimiter::Bracket),
+ rustc_lexer::TokenKind::At => token::At,
+ rustc_lexer::TokenKind::Pound => token::Pound,
+ rustc_lexer::TokenKind::Tilde => token::Tilde,
+ rustc_lexer::TokenKind::Question => token::Question,
+ rustc_lexer::TokenKind::Colon => token::Colon,
+ rustc_lexer::TokenKind::Dollar => token::Dollar,
+ rustc_lexer::TokenKind::Eq => token::Eq,
+ rustc_lexer::TokenKind::Bang => token::Not,
+ rustc_lexer::TokenKind::Lt => token::Lt,
+ rustc_lexer::TokenKind::Gt => token::Gt,
+ rustc_lexer::TokenKind::Minus => token::BinOp(token::Minus),
+ rustc_lexer::TokenKind::And => token::BinOp(token::And),
+ rustc_lexer::TokenKind::Or => token::BinOp(token::Or),
+ rustc_lexer::TokenKind::Plus => token::BinOp(token::Plus),
+ rustc_lexer::TokenKind::Star => token::BinOp(token::Star),
+ rustc_lexer::TokenKind::Slash => token::BinOp(token::Slash),
+ rustc_lexer::TokenKind::Caret => token::BinOp(token::Caret),
+ rustc_lexer::TokenKind::Percent => token::BinOp(token::Percent),
+
+ rustc_lexer::TokenKind::Unknown | rustc_lexer::TokenKind::InvalidIdent => {
+ let c = self.str_from(start).chars().next().unwrap();
+ let mut err =
+ self.struct_err_span_char(start, self.pos, "unknown start of token", c);
+ // FIXME: the lexer could be used to turn the ASCII version of unicode
+ // homoglyphs, instead of keeping a table in `check_for_substitution`into the
+ // token. Ideally, this should be inside `rustc_lexer`. However, we should
+ // first remove compound tokens like `<<` from `rustc_lexer`, and then add
+ // fancier error recovery to it, as there will be less overall work to do this
+ // way.
+ let token = unicode_chars::check_for_substitution(self, start, c, &mut err);
+ if c == '\x00' {
+ err.help("source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used");
+ }
+ err.emit();
+ if let Some(token) = token {
+ token
+ } else {
+ preceded_by_whitespace = true;
+ continue;
+ }
+ }
+ rustc_lexer::TokenKind::Eof => token::Eof,
+ };
+ let span = self.mk_sp(start, self.pos);
+ return (Token::new(kind, span), preceded_by_whitespace);
}
}
@@ -164,171 +332,6 @@ impl<'a> StringReader<'a> {
}
}
- /// Turns simple `rustc_lexer::TokenKind` enum into a rich
- /// `rustc_ast::TokenKind`. This turns strings into interned
- /// symbols and runs additional validation.
- fn cook_lexer_token(&self, token: rustc_lexer::TokenKind, start: BytePos) -> Option<TokenKind> {
- Some(match token {
- rustc_lexer::TokenKind::LineComment { doc_style } => {
- // Skip non-doc comments
- let Some(doc_style) = doc_style else {
- self.lint_unicode_text_flow(start);
- return None;
- };
-
- // Opening delimiter of the length 3 is not included into the symbol.
- let content_start = start + BytePos(3);
- let content = self.str_from(content_start);
- self.cook_doc_comment(content_start, content, CommentKind::Line, doc_style)
- }
- rustc_lexer::TokenKind::BlockComment { doc_style, terminated } => {
- if !terminated {
- self.report_unterminated_block_comment(start, doc_style);
- }
-
- // Skip non-doc comments
- let Some(doc_style) = doc_style else {
- self.lint_unicode_text_flow(start);
- return None;
- };
-
- // Opening delimiter of the length 3 and closing delimiter of the length 2
- // are not included into the symbol.
- let content_start = start + BytePos(3);
- let content_end = self.pos - BytePos(if terminated { 2 } else { 0 });
- let content = self.str_from_to(content_start, content_end);
- self.cook_doc_comment(content_start, content, CommentKind::Block, doc_style)
- }
- rustc_lexer::TokenKind::Whitespace => return None,
- rustc_lexer::TokenKind::Ident
- | rustc_lexer::TokenKind::RawIdent
- | rustc_lexer::TokenKind::UnknownPrefix => {
- let is_raw_ident = token == rustc_lexer::TokenKind::RawIdent;
- let is_unknown_prefix = token == rustc_lexer::TokenKind::UnknownPrefix;
- let mut ident_start = start;
- if is_raw_ident {
- ident_start = ident_start + BytePos(2);
- }
- if is_unknown_prefix {
- self.report_unknown_prefix(start);
- }
- let sym = nfc_normalize(self.str_from(ident_start));
- let span = self.mk_sp(start, self.pos);
- self.sess.symbol_gallery.insert(sym, span);
- if is_raw_ident {
- if !sym.can_be_raw() {
- self.err_span(span, &format!("`{}` cannot be a raw identifier", sym));
- }
- self.sess.raw_identifier_spans.borrow_mut().push(span);
- }
- token::Ident(sym, is_raw_ident)
- }
- rustc_lexer::TokenKind::InvalidIdent
- // Do not recover an identifier with emoji if the codepoint is a confusable
- // with a recoverable substitution token, like `âž–`.
- if !UNICODE_ARRAY
- .iter()
- .any(|&(c, _, _)| {
- let sym = self.str_from(start);
- sym.chars().count() == 1 && c == sym.chars().next().unwrap()
- })
- =>
- {
- let sym = nfc_normalize(self.str_from(start));
- let span = self.mk_sp(start, self.pos);
- self.sess.bad_unicode_identifiers.borrow_mut().entry(sym).or_default().push(span);
- token::Ident(sym, false)
- }
- rustc_lexer::TokenKind::Literal { kind, suffix_start } => {
- let suffix_start = start + BytePos(suffix_start);
- let (kind, symbol) = self.cook_lexer_literal(start, suffix_start, kind);
- let suffix = if suffix_start < self.pos {
- let string = self.str_from(suffix_start);
- if string == "_" {
- self.sess
- .span_diagnostic
- .struct_span_warn(
- 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 {
- Some(Symbol::intern(string))
- }
- } else {
- None
- };
- token::Literal(token::Lit { kind, symbol, suffix })
- }
- rustc_lexer::TokenKind::Lifetime { starts_with_number } => {
- // Include the leading `'` in the real identifier, for macro
- // expansion purposes. See #12512 for the gory details of why
- // this is necessary.
- let lifetime_name = self.str_from(start);
- if starts_with_number {
- self.err_span_(start, self.pos, "lifetimes cannot start with a number");
- }
- let ident = Symbol::intern(lifetime_name);
- token::Lifetime(ident)
- }
- rustc_lexer::TokenKind::Semi => token::Semi,
- rustc_lexer::TokenKind::Comma => token::Comma,
- rustc_lexer::TokenKind::Dot => token::Dot,
- rustc_lexer::TokenKind::OpenParen => token::OpenDelim(Delimiter::Parenthesis),
- rustc_lexer::TokenKind::CloseParen => token::CloseDelim(Delimiter::Parenthesis),
- rustc_lexer::TokenKind::OpenBrace => token::OpenDelim(Delimiter::Brace),
- rustc_lexer::TokenKind::CloseBrace => token::CloseDelim(Delimiter::Brace),
- rustc_lexer::TokenKind::OpenBracket => token::OpenDelim(Delimiter::Bracket),
- rustc_lexer::TokenKind::CloseBracket => token::CloseDelim(Delimiter::Bracket),
- rustc_lexer::TokenKind::At => token::At,
- rustc_lexer::TokenKind::Pound => token::Pound,
- rustc_lexer::TokenKind::Tilde => token::Tilde,
- rustc_lexer::TokenKind::Question => token::Question,
- rustc_lexer::TokenKind::Colon => token::Colon,
- rustc_lexer::TokenKind::Dollar => token::Dollar,
- rustc_lexer::TokenKind::Eq => token::Eq,
- rustc_lexer::TokenKind::Bang => token::Not,
- rustc_lexer::TokenKind::Lt => token::Lt,
- rustc_lexer::TokenKind::Gt => token::Gt,
- rustc_lexer::TokenKind::Minus => token::BinOp(token::Minus),
- rustc_lexer::TokenKind::And => token::BinOp(token::And),
- rustc_lexer::TokenKind::Or => token::BinOp(token::Or),
- rustc_lexer::TokenKind::Plus => token::BinOp(token::Plus),
- rustc_lexer::TokenKind::Star => token::BinOp(token::Star),
- rustc_lexer::TokenKind::Slash => token::BinOp(token::Slash),
- rustc_lexer::TokenKind::Caret => token::BinOp(token::Caret),
- rustc_lexer::TokenKind::Percent => token::BinOp(token::Percent),
-
- rustc_lexer::TokenKind::Unknown | rustc_lexer::TokenKind::InvalidIdent => {
- let c = self.str_from(start).chars().next().unwrap();
- let mut err =
- self.struct_err_span_char(start, self.pos, "unknown start of token", c);
- // FIXME: the lexer could be used to turn the ASCII version of unicode homoglyphs,
- // instead of keeping a table in `check_for_substitution`into the token. Ideally,
- // this should be inside `rustc_lexer`. However, we should first remove compound
- // tokens like `<<` from `rustc_lexer`, and then add fancier error recovery to it,
- // as there will be less overall work to do this way.
- let token = unicode_chars::check_for_substitution(self, start, c, &mut err);
- if c == '\x00' {
- err.help("source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used");
- }
- err.emit();
- token?
- }
- })
- }
-
fn cook_doc_comment(
&self,
content_start: BytePos,
diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs
index aa70912dc..b2701817d 100644
--- a/compiler/rustc_parse/src/lexer/tokentrees.rs
+++ b/compiler/rustc_parse/src/lexer/tokentrees.rs
@@ -1,31 +1,15 @@
use super::{StringReader, UnmatchedBrace};
-
use rustc_ast::token::{self, Delimiter, Token};
use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenStream, TokenTree};
use rustc_ast_pretty::pprust::token_to_string;
use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::PResult;
+use rustc_errors::{PErr, PResult};
use rustc_span::Span;
-impl<'a> StringReader<'a> {
- pub(super) fn into_token_trees(self) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) {
- let mut tt_reader = TokenTreesReader {
- string_reader: self,
- token: Token::dummy(),
- open_braces: Vec::new(),
- unmatched_braces: Vec::new(),
- matching_delim_spans: Vec::new(),
- last_unclosed_found_span: None,
- last_delim_empty_block_spans: FxHashMap::default(),
- matching_block_spans: Vec::new(),
- };
- let res = tt_reader.parse_all_token_trees();
- (res, tt_reader.unmatched_braces)
- }
-}
-
-struct TokenTreesReader<'a> {
+pub(super) struct TokenTreesReader<'a> {
string_reader: StringReader<'a>,
+ /// The "next" token, which has been obtained from the `StringReader` but
+ /// not yet handled by the `TokenTreesReader`.
token: Token,
/// Stack of open delimiters and their spans. Used for error message.
open_braces: Vec<(Delimiter, Span)>,
@@ -43,254 +27,232 @@ struct TokenTreesReader<'a> {
}
impl<'a> TokenTreesReader<'a> {
- // Parse a stream of tokens into a list of `TokenTree`s, up to an `Eof`.
- fn parse_all_token_trees(&mut self) -> PResult<'a, TokenStream> {
- let mut buf = TokenStreamBuilder::default();
-
- self.bump();
- while self.token != token::Eof {
- buf.push(self.parse_token_tree()?);
- }
-
- Ok(buf.into_token_stream())
+ pub(super) fn parse_all_token_trees(
+ string_reader: StringReader<'a>,
+ ) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) {
+ let mut tt_reader = TokenTreesReader {
+ string_reader,
+ token: Token::dummy(),
+ open_braces: Vec::new(),
+ unmatched_braces: Vec::new(),
+ matching_delim_spans: Vec::new(),
+ last_unclosed_found_span: None,
+ last_delim_empty_block_spans: FxHashMap::default(),
+ matching_block_spans: Vec::new(),
+ };
+ let res = tt_reader.parse_token_trees(/* is_delimited */ false);
+ (res, tt_reader.unmatched_braces)
}
- // Parse a stream of tokens into a list of `TokenTree`s, up to a `CloseDelim`.
- fn parse_token_trees_until_close_delim(&mut self) -> TokenStream {
- let mut buf = TokenStreamBuilder::default();
+ // Parse a stream of tokens into a list of `TokenTree`s.
+ fn parse_token_trees(&mut self, is_delimited: bool) -> PResult<'a, TokenStream> {
+ self.token = self.string_reader.next_token().0;
+ let mut buf = Vec::new();
loop {
- if let token::CloseDelim(..) = self.token.kind {
- return buf.into_token_stream();
- }
-
- match self.parse_token_tree() {
- Ok(tree) => buf.push(tree),
- Err(mut e) => {
- e.emit();
- return buf.into_token_stream();
+ match self.token.kind {
+ token::OpenDelim(delim) => buf.push(self.parse_token_tree_open_delim(delim)),
+ token::CloseDelim(delim) => {
+ return if is_delimited {
+ Ok(TokenStream::new(buf))
+ } else {
+ Err(self.close_delim_err(delim))
+ };
+ }
+ token::Eof => {
+ if is_delimited {
+ self.eof_err().emit();
+ }
+ return Ok(TokenStream::new(buf));
+ }
+ _ => {
+ // Get the next normal token. This might require getting multiple adjacent
+ // single-char tokens and joining them together.
+ let (this_spacing, next_tok) = loop {
+ let (next_tok, is_next_tok_preceded_by_whitespace) =
+ self.string_reader.next_token();
+ if !is_next_tok_preceded_by_whitespace {
+ if let Some(glued) = self.token.glue(&next_tok) {
+ self.token = glued;
+ } else {
+ let this_spacing =
+ if next_tok.is_op() { Spacing::Joint } else { Spacing::Alone };
+ break (this_spacing, next_tok);
+ }
+ } else {
+ break (Spacing::Alone, next_tok);
+ }
+ };
+ let this_tok = std::mem::replace(&mut self.token, next_tok);
+ buf.push(TokenTree::Token(this_tok, this_spacing));
}
}
}
}
- fn parse_token_tree(&mut self) -> PResult<'a, TokenTree> {
- let sm = self.string_reader.sess.source_map();
-
- match self.token.kind {
- token::Eof => {
- let msg = "this file contains an unclosed delimiter";
- let mut err =
- self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, msg);
- for &(_, sp) in &self.open_braces {
- err.span_label(sp, "unclosed delimiter");
- self.unmatched_braces.push(UnmatchedBrace {
- expected_delim: Delimiter::Brace,
- found_delim: None,
- found_span: self.token.span,
- unclosed_span: Some(sp),
- candidate_span: None,
- });
- }
+ fn eof_err(&mut self) -> PErr<'a> {
+ let msg = "this file contains an unclosed delimiter";
+ let mut err = self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, msg);
+ for &(_, sp) in &self.open_braces {
+ err.span_label(sp, "unclosed delimiter");
+ self.unmatched_braces.push(UnmatchedBrace {
+ expected_delim: Delimiter::Brace,
+ found_delim: None,
+ found_span: self.token.span,
+ unclosed_span: Some(sp),
+ candidate_span: None,
+ });
+ }
- if let Some((delim, _)) = self.open_braces.last() {
- if let Some((_, open_sp, close_sp)) =
- self.matching_delim_spans.iter().find(|(d, open_sp, close_sp)| {
- if let Some(close_padding) = sm.span_to_margin(*close_sp) {
- if let Some(open_padding) = sm.span_to_margin(*open_sp) {
- return delim == d && close_padding != open_padding;
- }
- }
- false
- })
- // these are in reverse order as they get inserted on close, but
- {
- // we want the last open/first close
- err.span_label(*open_sp, "this delimiter might not be properly closed...");
- err.span_label(
- *close_sp,
- "...as it matches this but it has different indentation",
- );
+ if let Some((delim, _)) = self.open_braces.last() {
+ if let Some((_, open_sp, close_sp)) =
+ self.matching_delim_spans.iter().find(|(d, open_sp, close_sp)| {
+ let sm = self.string_reader.sess.source_map();
+ if let Some(close_padding) = sm.span_to_margin(*close_sp) {
+ if let Some(open_padding) = sm.span_to_margin(*open_sp) {
+ return delim == d && close_padding != open_padding;
+ }
}
- }
- Err(err)
+ false
+ })
+ // these are in reverse order as they get inserted on close, but
+ {
+ // we want the last open/first close
+ err.span_label(*open_sp, "this delimiter might not be properly closed...");
+ err.span_label(*close_sp, "...as it matches this but it has different indentation");
}
- token::OpenDelim(delim) => {
- // The span for beginning of the delimited section
- let pre_span = self.token.span;
-
- // Parse the open delimiter.
- self.open_braces.push((delim, self.token.span));
- self.bump();
+ }
+ err
+ }
- // Parse the token trees within the delimiters.
- // We stop at any delimiter so we can try to recover if the user
- // uses an incorrect delimiter.
- let tts = self.parse_token_trees_until_close_delim();
+ fn parse_token_tree_open_delim(&mut self, open_delim: Delimiter) -> TokenTree {
+ // The span for beginning of the delimited section
+ let pre_span = self.token.span;
- // Expand to cover the entire delimited token tree
- let delim_span = DelimSpan::from_pair(pre_span, self.token.span);
+ self.open_braces.push((open_delim, self.token.span));
- match self.token.kind {
- // Correct delimiter.
- token::CloseDelim(d) if d == delim => {
- let (open_brace, open_brace_span) = self.open_braces.pop().unwrap();
- let close_brace_span = self.token.span;
+ // Parse the token trees within the delimiters.
+ // We stop at any delimiter so we can try to recover if the user
+ // uses an incorrect delimiter.
+ let tts = self.parse_token_trees(/* is_delimited */ true).unwrap();
- if tts.is_empty() {
- let empty_block_span = open_brace_span.to(close_brace_span);
- if !sm.is_multiline(empty_block_span) {
- // Only track if the block is in the form of `{}`, otherwise it is
- // likely that it was written on purpose.
- self.last_delim_empty_block_spans.insert(delim, empty_block_span);
- }
- }
+ // Expand to cover the entire delimited token tree
+ let delim_span = DelimSpan::from_pair(pre_span, self.token.span);
- //only add braces
- if let (Delimiter::Brace, Delimiter::Brace) = (open_brace, delim) {
- self.matching_block_spans.push((open_brace_span, close_brace_span));
- }
+ match self.token.kind {
+ // Correct delimiter.
+ token::CloseDelim(close_delim) if close_delim == open_delim => {
+ let (open_brace, open_brace_span) = self.open_braces.pop().unwrap();
+ let close_brace_span = self.token.span;
- if self.open_braces.is_empty() {
- // Clear up these spans to avoid suggesting them as we've found
- // properly matched delimiters so far for an entire block.
- self.matching_delim_spans.clear();
- } else {
- self.matching_delim_spans.push((
- open_brace,
- open_brace_span,
- close_brace_span,
- ));
- }
- // Parse the closing delimiter.
- self.bump();
+ if tts.is_empty() {
+ let empty_block_span = open_brace_span.to(close_brace_span);
+ let sm = self.string_reader.sess.source_map();
+ if !sm.is_multiline(empty_block_span) {
+ // Only track if the block is in the form of `{}`, otherwise it is
+ // likely that it was written on purpose.
+ self.last_delim_empty_block_spans.insert(open_delim, empty_block_span);
}
- // Incorrect delimiter.
- token::CloseDelim(other) => {
- let mut unclosed_delimiter = None;
- let mut candidate = None;
-
- if self.last_unclosed_found_span != Some(self.token.span) {
- // do not complain about the same unclosed delimiter multiple times
- self.last_unclosed_found_span = Some(self.token.span);
- // This is a conservative error: only report the last unclosed
- // delimiter. The previous unclosed delimiters could actually be
- // closed! The parser just hasn't gotten to them yet.
- if let Some(&(_, sp)) = self.open_braces.last() {
- unclosed_delimiter = Some(sp);
- };
- if let Some(current_padding) = sm.span_to_margin(self.token.span) {
- for (brace, brace_span) in &self.open_braces {
- if let Some(padding) = sm.span_to_margin(*brace_span) {
- // high likelihood of these two corresponding
- if current_padding == padding && brace == &other {
- candidate = Some(*brace_span);
- }
- }
- }
- }
- let (tok, _) = self.open_braces.pop().unwrap();
- self.unmatched_braces.push(UnmatchedBrace {
- expected_delim: tok,
- found_delim: Some(other),
- found_span: self.token.span,
- unclosed_span: unclosed_delimiter,
- candidate_span: candidate,
- });
- } else {
- self.open_braces.pop();
- }
+ }
- // If the incorrect delimiter matches an earlier opening
- // delimiter, then don't consume it (it can be used to
- // close the earlier one). Otherwise, consume it.
- // E.g., we try to recover from:
- // fn foo() {
- // bar(baz(
- // } // Incorrect delimiter but matches the earlier `{`
- if !self.open_braces.iter().any(|&(b, _)| b == other) {
- self.bump();
- }
- }
- token::Eof => {
- // Silently recover, the EOF token will be seen again
- // and an error emitted then. Thus we don't pop from
- // self.open_braces here.
- }
- _ => {}
+ //only add braces
+ if let (Delimiter::Brace, Delimiter::Brace) = (open_brace, open_delim) {
+ self.matching_block_spans.push((open_brace_span, close_brace_span));
}
- Ok(TokenTree::Delimited(delim_span, delim, tts))
+ if self.open_braces.is_empty() {
+ // Clear up these spans to avoid suggesting them as we've found
+ // properly matched delimiters so far for an entire block.
+ self.matching_delim_spans.clear();
+ } else {
+ self.matching_delim_spans.push((open_brace, open_brace_span, close_brace_span));
+ }
+ // Move past the closing delimiter.
+ self.token = self.string_reader.next_token().0;
}
- token::CloseDelim(delim) => {
- // An unexpected closing delimiter (i.e., there is no
- // matching opening delimiter).
- let token_str = token_to_string(&self.token);
- let msg = format!("unexpected closing delimiter: `{}`", token_str);
- let mut err =
- self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, &msg);
+ // Incorrect delimiter.
+ token::CloseDelim(close_delim) => {
+ let mut unclosed_delimiter = None;
+ let mut candidate = None;
- // Braces are added at the end, so the last element is the biggest block
- if let Some(parent) = self.matching_block_spans.last() {
- if let Some(span) = self.last_delim_empty_block_spans.remove(&delim) {
- // Check if the (empty block) is in the last properly closed block
- if (parent.0.to(parent.1)).contains(span) {
- err.span_label(
- span,
- "block is empty, you might have not meant to close it",
- );
- } else {
- err.span_label(parent.0, "this opening brace...");
-
- err.span_label(parent.1, "...matches this closing brace");
+ if self.last_unclosed_found_span != Some(self.token.span) {
+ // do not complain about the same unclosed delimiter multiple times
+ self.last_unclosed_found_span = Some(self.token.span);
+ // This is a conservative error: only report the last unclosed
+ // delimiter. The previous unclosed delimiters could actually be
+ // closed! The parser just hasn't gotten to them yet.
+ if let Some(&(_, sp)) = self.open_braces.last() {
+ unclosed_delimiter = Some(sp);
+ };
+ let sm = self.string_reader.sess.source_map();
+ if let Some(current_padding) = sm.span_to_margin(self.token.span) {
+ for (brace, brace_span) in &self.open_braces {
+ if let Some(padding) = sm.span_to_margin(*brace_span) {
+ // high likelihood of these two corresponding
+ if current_padding == padding && brace == &close_delim {
+ candidate = Some(*brace_span);
+ }
+ }
}
- } else {
- err.span_label(parent.0, "this opening brace...");
-
- err.span_label(parent.1, "...matches this closing brace");
}
+ let (tok, _) = self.open_braces.pop().unwrap();
+ self.unmatched_braces.push(UnmatchedBrace {
+ expected_delim: tok,
+ found_delim: Some(close_delim),
+ found_span: self.token.span,
+ unclosed_span: unclosed_delimiter,
+ candidate_span: candidate,
+ });
+ } else {
+ self.open_braces.pop();
}
- err.span_label(self.token.span, "unexpected closing delimiter");
- Err(err)
- }
- _ => {
- let tok = self.token.take();
- let mut spacing = self.bump();
- if !self.token.is_op() {
- spacing = Spacing::Alone;
+ // If the incorrect delimiter matches an earlier opening
+ // delimiter, then don't consume it (it can be used to
+ // close the earlier one). Otherwise, consume it.
+ // E.g., we try to recover from:
+ // fn foo() {
+ // bar(baz(
+ // } // Incorrect delimiter but matches the earlier `{`
+ if !self.open_braces.iter().any(|&(b, _)| b == close_delim) {
+ self.token = self.string_reader.next_token().0;
}
- Ok(TokenTree::Token(tok, spacing))
}
+ token::Eof => {
+ // Silently recover, the EOF token will be seen again
+ // and an error emitted then. Thus we don't pop from
+ // self.open_braces here.
+ }
+ _ => unreachable!(),
}
- }
- fn bump(&mut self) -> Spacing {
- let (spacing, token) = self.string_reader.next_token();
- self.token = token;
- spacing
+ TokenTree::Delimited(delim_span, open_delim, tts)
}
-}
-#[derive(Default)]
-struct TokenStreamBuilder {
- buf: Vec<TokenTree>,
-}
+ fn close_delim_err(&mut self, delim: Delimiter) -> PErr<'a> {
+ // An unexpected closing delimiter (i.e., there is no
+ // matching opening delimiter).
+ let token_str = token_to_string(&self.token);
+ let msg = format!("unexpected closing delimiter: `{}`", token_str);
+ let mut err =
+ self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, &msg);
-impl TokenStreamBuilder {
- #[inline(always)]
- fn push(&mut self, tree: TokenTree) {
- if let Some(TokenTree::Token(prev_token, Spacing::Joint)) = self.buf.last()
- && let TokenTree::Token(token, joint) = &tree
- && let Some(glued) = prev_token.glue(token)
- {
- self.buf.pop();
- self.buf.push(TokenTree::Token(glued, *joint));
- } else {
- self.buf.push(tree)
+ // Braces are added at the end, so the last element is the biggest block
+ if let Some(parent) = self.matching_block_spans.last() {
+ if let Some(span) = self.last_delim_empty_block_spans.remove(&delim) {
+ // Check if the (empty block) is in the last properly closed block
+ if (parent.0.to(parent.1)).contains(span) {
+ err.span_label(span, "block is empty, you might have not meant to close it");
+ } else {
+ err.span_label(parent.0, "this opening brace...");
+ err.span_label(parent.1, "...matches this closing brace");
+ }
+ } else {
+ err.span_label(parent.0, "this opening brace...");
+ err.span_label(parent.1, "...matches this closing brace");
+ }
}
- }
- fn into_token_stream(self) -> TokenStream {
- TokenStream::new(self.buf)
+ err.span_label(self.token.span, "unexpected closing delimiter");
+ err
}
}
diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
index 273827864..f075de714 100644
--- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
+++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
@@ -20,13 +20,9 @@ pub(crate) fn emit_unescape_error(
range: Range<usize>,
error: EscapeError,
) {
- tracing::debug!(
+ debug!(
"emit_unescape_error: {:?}, {:?}, {:?}, {:?}, {:?}",
- lit,
- span_with_quotes,
- mode,
- range,
- error
+ lit, span_with_quotes, mode, range, error
);
let last_char = || {
let c = lit[range.clone()].chars().rev().next().unwrap();
@@ -117,11 +113,26 @@ pub(crate) fn emit_unescape_error(
} 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,
);
}
diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs
index 8c087c65c..3dcadb4c9 100644
--- a/compiler/rustc_parse/src/lib.rs
+++ b/compiler/rustc_parse/src/lib.rs
@@ -4,7 +4,6 @@
#![feature(box_patterns)]
#![feature(if_let_guard)]
#![feature(let_chains)]
-#![feature(let_else)]
#![feature(never_type)]
#![feature(rustc_attrs)]
#![recursion_limit = "256"]
@@ -33,12 +32,15 @@ use parser::{emit_unclosed_delims, make_unclosed_delims_error, Parser};
pub mod lexer;
pub mod validate_attr;
+mod errors;
+
// A bunch of utility functions of the form `parse_<thing>_from_<source>`
// where <thing> includes crate, expr, item, stmt, tts, and one that
// 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;
@@ -63,7 +65,7 @@ pub fn parse_crate_from_file<'a>(input: &Path, sess: &'a ParseSess) -> PResult<'
pub fn parse_crate_attrs_from_file<'a>(
input: &Path,
sess: &'a ParseSess,
-) -> PResult<'a, Vec<ast::Attribute>> {
+) -> PResult<'a, ast::AttrVec> {
let mut parser = new_parser_from_file(sess, input, None);
parser.parse_inner_attributes()
}
@@ -80,7 +82,7 @@ pub fn parse_crate_attrs_from_source_str(
name: FileName,
source: String,
sess: &ParseSess,
-) -> PResult<'_, Vec<ast::Attribute>> {
+) -> PResult<'_, ast::AttrVec> {
new_parser_from_source_str(sess, name, source).parse_inner_attributes()
}
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index acdbddf40..9e4565694 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -1,29 +1,26 @@
+use crate::errors::{InvalidMetaItem, SuffixedLiteralInAttribute};
+
use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle};
use rustc_ast as ast;
use rustc_ast::attr;
use rustc_ast::token::{self, Delimiter, Nonterminal};
-use rustc_ast_pretty::pprust;
-use rustc_errors::{error_code, Diagnostic, PResult};
+use rustc_errors::{error_code, fluent, Diagnostic, IntoDiagnostic, PResult};
use rustc_span::{sym, BytePos, Span};
use std::convert::TryInto;
-use tracing::debug;
-
// Public for rustfmt usage
#[derive(Debug)]
-pub enum InnerAttrPolicy<'a> {
+pub enum InnerAttrPolicy {
Permitted,
- Forbidden { reason: &'a str, saw_doc_comment: bool, prev_outer_attr_sp: Option<Span> },
+ Forbidden(Option<InnerAttrForbiddenReason>),
}
-const DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG: &str = "an inner attribute is not \
- permitted in this context";
-
-pub(super) const DEFAULT_INNER_ATTR_FORBIDDEN: InnerAttrPolicy<'_> = InnerAttrPolicy::Forbidden {
- reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG,
- saw_doc_comment: false,
- prev_outer_attr_sp: None,
-};
+#[derive(Clone, Copy, Debug)]
+pub enum InnerAttrForbiddenReason {
+ InCodeBlock,
+ AfterOuterDocComment { prev_doc_comment_span: Span },
+ AfterOuterAttribute { prev_outer_attr_sp: Span },
+}
enum OuterAttributeType {
DocComment,
@@ -34,7 +31,7 @@ enum OuterAttributeType {
impl<'a> Parser<'a> {
/// Parses attributes that appear before an item.
pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, AttrWrapper> {
- let mut outer_attrs: Vec<ast::Attribute> = Vec::new();
+ let mut outer_attrs = ast::AttrVec::new();
let mut just_parsed_doc_comment = false;
let start_pos = self.token_cursor.num_next_calls;
loop {
@@ -42,17 +39,15 @@ impl<'a> Parser<'a> {
let prev_outer_attr_sp = outer_attrs.last().map(|attr| attr.span);
let inner_error_reason = if just_parsed_doc_comment {
- "an inner attribute is not permitted following an outer doc comment"
- } else if prev_outer_attr_sp.is_some() {
- "an inner attribute is not permitted following an outer attribute"
+ Some(InnerAttrForbiddenReason::AfterOuterDocComment {
+ prev_doc_comment_span: prev_outer_attr_sp.unwrap(),
+ })
+ } else if let Some(prev_outer_attr_sp) = prev_outer_attr_sp {
+ Some(InnerAttrForbiddenReason::AfterOuterAttribute { prev_outer_attr_sp })
} else {
- DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG
- };
- let inner_parse_policy = InnerAttrPolicy::Forbidden {
- reason: inner_error_reason,
- saw_doc_comment: just_parsed_doc_comment,
- prev_outer_attr_sp,
+ None
};
+ let inner_parse_policy = InnerAttrPolicy::Forbidden(inner_error_reason);
just_parsed_doc_comment = false;
Some(self.parse_attribute(inner_parse_policy)?)
} else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
@@ -60,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,
- "expected outer doc comment",
+ fluent::parser_inner_doc_comment_not_permitted,
error_code!(E0753),
);
if let Some(replacement_span) = self.annotate_following_item_if_applicable(
@@ -71,13 +66,10 @@ impl<'a> Parser<'a> {
token::CommentKind::Block => OuterAttributeType::DocBlockComment,
},
) {
- err.note(
- "inner doc comments like this (starting with `//!` or `/*!`) can \
- only appear before items",
- );
+ err.note(fluent::note);
err.span_suggestion_verbose(
replacement_span,
- "you might have meant to write a regular comment",
+ fluent::suggestion,
"",
rustc_errors::Applicability::MachineApplicable,
);
@@ -89,6 +81,7 @@ impl<'a> Parser<'a> {
// Always make an outer attribute - this allows us to recover from a misplaced
// inner attribute.
Some(attr::mk_doc_comment(
+ &self.sess.attr_id_generator,
comment_kind,
ast::AttrStyle::Outer,
data,
@@ -106,7 +99,7 @@ impl<'a> Parser<'a> {
break;
}
}
- Ok(AttrWrapper::new(outer_attrs.into(), start_pos))
+ Ok(AttrWrapper::new(outer_attrs, start_pos))
}
/// Matches `attribute = # ! [ meta_item ]`.
@@ -114,7 +107,7 @@ impl<'a> Parser<'a> {
// Public for rustfmt usage.
pub fn parse_attribute(
&mut self,
- inner_parse_policy: InnerAttrPolicy<'_>,
+ inner_parse_policy: InnerAttrPolicy,
) -> PResult<'a, ast::Attribute> {
debug!(
"parse_attribute: inner_parse_policy={:?} self.token={:?}",
@@ -123,29 +116,22 @@ impl<'a> Parser<'a> {
let lo = self.token.span;
// Attributes can't have attributes of their own [Editor's note: not with that attitude]
self.collect_tokens_no_attrs(|this| {
- if this.eat(&token::Pound) {
- let style = if this.eat(&token::Not) {
- ast::AttrStyle::Inner
- } else {
- ast::AttrStyle::Outer
- };
+ assert!(this.eat(&token::Pound), "parse_attribute called in non-attribute position");
- this.expect(&token::OpenDelim(Delimiter::Bracket))?;
- let item = this.parse_attr_item(false)?;
- this.expect(&token::CloseDelim(Delimiter::Bracket))?;
- let attr_sp = lo.to(this.prev_token.span);
+ let style =
+ if this.eat(&token::Not) { ast::AttrStyle::Inner } else { ast::AttrStyle::Outer };
- // Emit error if inner attribute is encountered and forbidden.
- if style == ast::AttrStyle::Inner {
- this.error_on_forbidden_inner_attr(attr_sp, inner_parse_policy);
- }
+ this.expect(&token::OpenDelim(Delimiter::Bracket))?;
+ let item = this.parse_attr_item(false)?;
+ this.expect(&token::CloseDelim(Delimiter::Bracket))?;
+ let attr_sp = lo.to(this.prev_token.span);
- Ok(attr::mk_attr_from_item(item, None, style, attr_sp))
- } else {
- let token_str = pprust::token_to_string(&this.token);
- let msg = &format!("expected `#`, found `{token_str}`");
- Err(this.struct_span_err(this.token.span, msg))
+ // Emit error if inner attribute is encountered and forbidden.
+ if style == ast::AttrStyle::Inner {
+ this.error_on_forbidden_inner_attr(attr_sp, inner_parse_policy);
}
+
+ Ok(attr::mk_attr_from_item(&self.sess.attr_id_generator, item, None, style, attr_sp))
})
}
@@ -185,21 +171,12 @@ impl<'a> Parser<'a> {
ForceCollect::No,
) {
Ok(Some(item)) => {
- let attr_name = match attr_type {
- OuterAttributeType::Attribute => "attribute",
- _ => "doc comment",
- };
- err.span_label(
- item.span,
- &format!("the inner {} doesn't annotate this {}", attr_name, item.kind.descr()),
- );
+ // FIXME(#100717)
+ err.set_arg("item", item.kind.descr());
+ err.span_label(item.span, fluent::label_does_not_annotate_this);
err.span_suggestion_verbose(
replacement_span,
- &format!(
- "to annotate the {}, change the {} from inner to outer style",
- item.kind.descr(),
- attr_name
- ),
+ fluent::sugg_change_inner_to_outer,
match attr_type {
OuterAttributeType::Attribute => "",
OuterAttributeType::DocBlockComment => "*",
@@ -217,22 +194,33 @@ impl<'a> Parser<'a> {
Some(replacement_span)
}
- pub(super) fn error_on_forbidden_inner_attr(&self, attr_sp: Span, policy: InnerAttrPolicy<'_>) {
- if let InnerAttrPolicy::Forbidden { reason, saw_doc_comment, prev_outer_attr_sp } = policy {
- let prev_outer_attr_note =
- if saw_doc_comment { "previous doc comment" } else { "previous outer attribute" };
-
- let mut diag = self.struct_span_err(attr_sp, reason);
-
- if let Some(prev_outer_attr_sp) = prev_outer_attr_sp {
- diag.span_label(attr_sp, "not permitted following an outer attribute")
- .span_label(prev_outer_attr_sp, prev_outer_attr_note);
- }
+ pub(super) fn error_on_forbidden_inner_attr(&self, attr_sp: Span, policy: InnerAttrPolicy) {
+ if let InnerAttrPolicy::Forbidden(reason) = policy {
+ let mut diag = match reason.as_ref().copied() {
+ 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,
+ );
+ 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,
+ );
+ 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)
+ }
+ };
- diag.note(
- "inner attributes, like `#![no_std]`, annotate the item enclosing them, and \
- are usually found at the beginning of source files",
- );
+ diag.note(fluent::parser_inner_attr_explanation);
if self
.annotate_following_item_if_applicable(
&mut diag,
@@ -241,7 +229,7 @@ impl<'a> Parser<'a> {
)
.is_some()
{
- diag.note("outer attributes, like `#[test]`, annotate the item following them");
+ diag.note(fluent::parser_outer_attr_explanation);
};
diag.emit();
}
@@ -283,8 +271,8 @@ impl<'a> Parser<'a> {
/// terminated by a semicolon.
///
/// Matches `inner_attrs*`.
- pub(crate) fn parse_inner_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
- let mut attrs: Vec<ast::Attribute> = vec![];
+ pub(crate) fn parse_inner_attributes(&mut self) -> PResult<'a, ast::AttrVec> {
+ let mut attrs = ast::AttrVec::new();
loop {
let start_pos: u32 = self.token_cursor.num_next_calls.try_into().unwrap();
// Only try to parse if it is an inner attribute (has `!`).
@@ -293,7 +281,13 @@ impl<'a> Parser<'a> {
} else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
if attr_style == ast::AttrStyle::Inner {
self.bump();
- Some(attr::mk_doc_comment(comment_kind, attr_style, data, self.prev_token.span))
+ Some(attr::mk_doc_comment(
+ &self.sess.attr_id_generator,
+ comment_kind,
+ attr_style,
+ data,
+ self.prev_token.span,
+ ))
} else {
None
}
@@ -303,9 +297,9 @@ impl<'a> Parser<'a> {
if let Some(attr) = attr {
let end_pos: u32 = self.token_cursor.num_next_calls.try_into().unwrap();
// If we are currently capturing tokens, mark the location of this inner attribute.
- // If capturing ends up creating a `LazyTokenStream`, we will include
+ // If capturing ends up creating a `LazyAttrTokenStream`, we will include
// this replace range with it, removing the inner attribute from the final
- // `AttrAnnotatedTokenStream`. Inner attributes are stored in the parsed AST note.
+ // `AttrTokenStream`. Inner attributes are stored in the parsed AST note.
// During macro expansion, they are selectively inserted back into the
// token stream (the first inner attribute is removed each time we invoke the
// corresponding macro).
@@ -326,12 +320,7 @@ impl<'a> Parser<'a> {
debug!("checking if {:?} is unusuffixed", lit);
if !lit.kind.is_unsuffixed() {
- self.struct_span_err(lit.span, "suffixed literals are not allowed in attributes")
- .help(
- "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \
- use an unsuffixed version (`1`, `1.0`, etc.)",
- )
- .emit();
+ self.sess.emit_err(SuffixedLiteralInAttribute { span: lit.span });
}
Ok(lit)
@@ -424,9 +413,8 @@ impl<'a> Parser<'a> {
Err(err) => err.cancel(),
}
- let found = pprust::token_to_string(&self.token);
- let msg = format!("expected unsuffixed literal or identifier, found `{found}`");
- Err(self.struct_span_err(self.token.span, &msg))
+ Err(InvalidMetaItem { span: self.token.span, token: self.token.clone() }
+ .into_diagnostic(&self.sess.span_diagnostic))
}
}
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index 6c750ff42..1b16ecb5e 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -1,7 +1,7 @@
use super::{Capturing, FlatToken, ForceCollect, Parser, ReplaceRange, TokenCursor, TrailingToken};
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
-use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttributesData, CreateTokenStream};
-use rustc_ast::tokenstream::{AttrAnnotatedTokenTree, DelimSpan, LazyTokenStream, Spacing};
+use rustc_ast::tokenstream::{AttrTokenStream, AttributesData, ToAttrTokenStream};
+use rustc_ast::tokenstream::{AttrTokenTree, DelimSpan, LazyAttrTokenStream, Spacing};
use rustc_ast::{self as ast};
use rustc_ast::{AttrVec, Attribute, HasAttrs, HasTokens};
use rustc_errors::PResult;
@@ -15,11 +15,11 @@ use std::ops::Range;
/// for the attribute target. This allows us to perform cfg-expansion on
/// a token stream before we invoke a derive proc-macro.
///
-/// This wrapper prevents direct access to the underlying `Vec<ast::Attribute>`.
+/// This wrapper prevents direct access to the underlying `ast::AttrVec>`.
/// Parsing code can only get access to the underlying attributes
/// by passing an `AttrWrapper` to `collect_tokens_trailing_tokens`.
/// This makes it difficult to accidentally construct an AST node
-/// (which stores a `Vec<ast::Attribute>`) without first collecting tokens.
+/// (which stores an `ast::AttrVec`) without first collecting tokens.
///
/// This struct has its own module, to ensure that the parser code
/// cannot directly access the `attrs` field
@@ -32,11 +32,6 @@ pub struct AttrWrapper {
start_pos: usize,
}
-// This struct is passed around very frequently,
-// so make sure it doesn't accidentally get larger
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-rustc_data_structures::static_assert_size!(AttrWrapper, 16);
-
impl AttrWrapper {
pub(super) fn new(attrs: AttrVec, start_pos: usize) -> AttrWrapper {
AttrWrapper { attrs, start_pos }
@@ -49,9 +44,10 @@ impl AttrWrapper {
self.attrs
}
+ // Prepend `self.attrs` to `attrs`.
// FIXME: require passing an NT to prevent misuse of this method
- pub(crate) fn prepend_to_nt_inner(self, attrs: &mut Vec<Attribute>) {
- let mut self_attrs: Vec<_> = self.attrs.into();
+ pub(crate) fn prepend_to_nt_inner(self, attrs: &mut AttrVec) {
+ let mut self_attrs = self.attrs;
std::mem::swap(attrs, &mut self_attrs);
attrs.extend(self_attrs);
}
@@ -87,7 +83,7 @@ fn has_cfg_or_cfg_attr(attrs: &[Attribute]) -> bool {
// This also makes `Parser` very cheap to clone, since
// there is no intermediate collection buffer to clone.
#[derive(Clone)]
-struct LazyTokenStreamImpl {
+struct LazyAttrTokenStreamImpl {
start_token: (Token, Spacing),
cursor_snapshot: TokenCursor,
num_calls: usize,
@@ -95,11 +91,8 @@ struct LazyTokenStreamImpl {
replace_ranges: Box<[ReplaceRange]>,
}
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-rustc_data_structures::static_assert_size!(LazyTokenStreamImpl, 144);
-
-impl CreateTokenStream for LazyTokenStreamImpl {
- fn create_token_stream(&self) -> AttrAnnotatedTokenStream {
+impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
+ fn to_attr_token_stream(&self) -> AttrTokenStream {
// The token produced by the final call to `{,inlined_}next` was not
// actually consumed by the callback. The combination of chaining the
// initial token and using `take` produces the desired result - we
@@ -116,7 +109,7 @@ impl CreateTokenStream for LazyTokenStreamImpl {
if !self.replace_ranges.is_empty() {
let mut tokens: Vec<_> = tokens.collect();
- let mut replace_ranges = self.replace_ranges.clone();
+ let mut replace_ranges = self.replace_ranges.to_vec();
replace_ranges.sort_by_key(|(range, _)| range.start);
#[cfg(debug_assertions)]
@@ -146,7 +139,7 @@ impl CreateTokenStream for LazyTokenStreamImpl {
// start position, we ensure that any replace range which encloses
// another replace range will capture the *replaced* tokens for the inner
// range, not the original tokens.
- for (range, new_tokens) in replace_ranges.iter().rev() {
+ for (range, new_tokens) in replace_ranges.into_iter().rev() {
assert!(!range.is_empty(), "Cannot replace an empty range: {:?}", range);
// Replace ranges are only allowed to decrease the number of tokens.
assert!(
@@ -165,7 +158,7 @@ impl CreateTokenStream for LazyTokenStreamImpl {
tokens.splice(
(range.start as usize)..(range.end as usize),
- new_tokens.clone().into_iter().chain(filler),
+ new_tokens.into_iter().chain(filler),
);
}
make_token_stream(tokens.into_iter(), self.break_last_token)
@@ -178,7 +171,7 @@ impl CreateTokenStream for LazyTokenStreamImpl {
impl<'a> Parser<'a> {
/// Records all tokens consumed by the provided callback,
/// including the current token. These tokens are collected
- /// into a `LazyTokenStream`, and returned along with the result
+ /// into a `LazyAttrTokenStream`, and returned along with the result
/// of the callback.
///
/// Note: If your callback consumes an opening delimiter
@@ -196,7 +189,7 @@ impl<'a> Parser<'a> {
&mut self,
attrs: AttrWrapper,
force_collect: ForceCollect,
- f: impl FnOnce(&mut Self, Vec<ast::Attribute>) -> PResult<'a, (R, TrailingToken)>,
+ f: impl FnOnce(&mut Self, ast::AttrVec) -> PResult<'a, (R, TrailingToken)>,
) -> PResult<'a, R> {
// We only bail out when nothing could possibly observe the collected tokens:
// 1. We cannot be force collecting tokens (since force-collecting requires tokens
@@ -212,7 +205,7 @@ impl<'a> Parser<'a> {
// or `#[cfg_attr]` attributes.
&& !self.capture_cfg
{
- return Ok(f(self, attrs.attrs.into())?.0);
+ return Ok(f(self, attrs.attrs)?.0);
}
let start_token = (self.token.clone(), self.token_spacing);
@@ -222,7 +215,7 @@ impl<'a> Parser<'a> {
let prev_capturing = std::mem::replace(&mut self.capture_state.capturing, Capturing::Yes);
let replace_ranges_start = self.capture_state.replace_ranges.len();
- let ret = f(self, attrs.attrs.into());
+ let ret = f(self, attrs.attrs);
self.capture_state.capturing = prev_capturing;
@@ -280,30 +273,33 @@ 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;
}
}
}
// If we 'broke' the last token (e.g. breaking a '>>' token to two '>' tokens),
// then extend the range of captured tokens to include it, since the parser
- // was not actually bumped past it. When the `LazyTokenStream` gets converted
- // into an `AttrAnnotatedTokenStream`, we will create the proper token.
+ // 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;
}
@@ -315,20 +311,20 @@ impl<'a> Parser<'a> {
Box::new([])
} else {
// Grab any replace ranges that occur *inside* the current AST node.
- // We will perform the actual replacement when we convert the `LazyTokenStream`
- // to an `AttrAnnotatedTokenStream`
+ // We will perform the actual replacement when we convert the `LazyAttrTokenStream`
+ // to an `AttrTokenStream`.
let start_calls: u32 = cursor_snapshot_next_calls.try_into().unwrap();
self.capture_state.replace_ranges[replace_ranges_start..replace_ranges_end]
.iter()
.cloned()
- .chain(inner_attr_replace_ranges.clone().into_iter())
+ .chain(inner_attr_replace_ranges.iter().cloned())
.map(|(range, tokens)| {
((range.start - start_calls)..(range.end - start_calls), tokens)
})
.collect()
};
- let tokens = LazyTokenStream::new(LazyTokenStreamImpl {
+ let tokens = LazyAttrTokenStream::new(LazyAttrTokenStreamImpl {
start_token,
num_calls,
cursor_snapshot,
@@ -352,9 +348,9 @@ impl<'a> Parser<'a> {
// on the captured token stream.
if self.capture_cfg
&& matches!(self.capture_state.capturing, Capturing::Yes)
- && has_cfg_or_cfg_attr(&final_attrs)
+ && has_cfg_or_cfg_attr(final_attrs)
{
- let attr_data = AttributesData { attrs: final_attrs.to_vec().into(), tokens };
+ let attr_data = AttributesData { attrs: final_attrs.iter().cloned().collect(), tokens };
// Replace the entire AST node that we just parsed, including attributes,
// with a `FlatToken::AttrTarget`. If this AST node is inside an item
@@ -391,12 +387,12 @@ impl<'a> Parser<'a> {
fn make_token_stream(
mut iter: impl Iterator<Item = (FlatToken, Spacing)>,
break_last_token: bool,
-) -> AttrAnnotatedTokenStream {
+) -> AttrTokenStream {
#[derive(Debug)]
struct FrameData {
// This is `None` for the first frame, `Some` for all others.
open_delim_sp: Option<(Delimiter, Span)>,
- inner: Vec<(AttrAnnotatedTokenTree, Spacing)>,
+ inner: Vec<AttrTokenTree>,
}
let mut stack = vec![FrameData { open_delim_sp: None, inner: vec![] }];
let mut token_and_spacing = iter.next();
@@ -417,48 +413,57 @@ fn make_token_stream(
open_delim, span
);
let dspan = DelimSpan::from_pair(open_sp, span);
- let stream = AttrAnnotatedTokenStream::new(frame_data.inner);
- let delimited = AttrAnnotatedTokenTree::Delimited(dspan, delim, stream);
+ let stream = AttrTokenStream::new(frame_data.inner);
+ let delimited = AttrTokenTree::Delimited(dspan, delim, stream);
stack
.last_mut()
.unwrap_or_else(|| {
panic!("Bottom token frame is missing for token: {:?}", token)
})
.inner
- .push((delimited, Spacing::Alone));
+ .push(delimited);
}
FlatToken::Token(token) => stack
.last_mut()
.expect("Bottom token frame is missing!")
.inner
- .push((AttrAnnotatedTokenTree::Token(token), spacing)),
+ .push(AttrTokenTree::Token(token, spacing)),
FlatToken::AttrTarget(data) => stack
.last_mut()
.expect("Bottom token frame is missing!")
.inner
- .push((AttrAnnotatedTokenTree::Attributes(data), spacing)),
+ .push(AttrTokenTree::Attributes(data)),
FlatToken::Empty => {}
}
token_and_spacing = iter.next();
}
let mut final_buf = stack.pop().expect("Missing final buf!");
if break_last_token {
- let (last_token, spacing) = final_buf.inner.pop().unwrap();
- if let AttrAnnotatedTokenTree::Token(last_token) = last_token {
+ let last_token = final_buf.inner.pop().unwrap();
+ if let AttrTokenTree::Token(last_token, spacing) = last_token {
let unglued_first = last_token.kind.break_two_token_op().unwrap().0;
// An 'unglued' token is always two ASCII characters
let mut first_span = last_token.span.shrink_to_lo();
first_span = first_span.with_hi(first_span.lo() + rustc_span::BytePos(1));
- final_buf.inner.push((
- AttrAnnotatedTokenTree::Token(Token::new(unglued_first, first_span)),
- spacing,
- ));
+ final_buf
+ .inner
+ .push(AttrTokenTree::Token(Token::new(unglued_first, first_span), spacing));
} else {
panic!("Unexpected last token {:?}", last_token)
}
}
- assert!(stack.is_empty(), "Stack should be empty: final_buf={:?} stack={:?}", final_buf, stack);
- AttrAnnotatedTokenStream::new(final_buf.inner)
+ AttrTokenStream::new(final_buf.inner)
+}
+
+// Some types are used a lot. Make sure they don't unintentionally get bigger.
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+mod size_asserts {
+ use super::*;
+ use rustc_data_structures::static_assert_size;
+ // 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 a2155ac1d..309717350 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -3,6 +3,19 @@ use super::{
BlockMode, CommaRecoveryMode, Parser, PathStyle, Restrictions, SemiColonMode, SeqSep,
TokenExpectType, TokenType,
};
+use crate::errors::{
+ AmbiguousPlus, AttributeOnParamType, BadQPathStage2, BadTypePlus, BadTypePlusSub,
+ ComparisonOperatorsCannotBeChained, ComparisonOperatorsCannotBeChainedSugg,
+ ConstGenericWithoutBraces, ConstGenericWithoutBracesSugg, DocCommentOnParamType,
+ DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg,
+ GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg, InInTypo,
+ IncorrectAwait, IncorrectSemicolon, IncorrectUseOfAwait, ParenthesesInForHead,
+ ParenthesesInForHeadSugg, PatternMethodParamWithoutBody, QuestionMarkInType,
+ QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath,
+ StructLiteralBodyWithoutPathSugg, SuggEscapeToUseAsIdentifier, SuggRemoveComma,
+ UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
+ UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead,
+};
use crate::lexer::UnmatchedBrace;
use rustc_ast as ast;
@@ -10,35 +23,31 @@ use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Lit, LitKind, TokenKind};
use rustc_ast::util::parser::AssocOp;
use rustc_ast::{
- AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, Block,
- BlockCheckMode, Expr, ExprKind, GenericArg, Generics, Item, ItemKind, Mutability, Param, Pat,
- PatKind, Path, PathSegment, QSelf, Ty, TyKind,
+ AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingAnnotation, Block,
+ BlockCheckMode, Expr, ExprKind, GenericArg, Generics, Item, ItemKind, Param, Pat, PatKind,
+ Path, PathSegment, QSelf, Ty, TyKind,
};
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{
fluent, Applicability, DiagnosticBuilder, DiagnosticMessage, Handler, MultiSpan, PResult,
};
-use rustc_errors::{pluralize, struct_span_err, Diagnostic, EmissionGuarantee, ErrorGuaranteed};
-use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
+use rustc_errors::{pluralize, Diagnostic, ErrorGuaranteed, IntoDiagnostic};
+use rustc_session::errors::ExprParenthesesNeeded;
use rustc_span::source_map::Spanned;
-use rustc_span::symbol::{kw, Ident};
+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 tracing::{debug, trace};
-
-const TURBOFISH_SUGGESTION_STR: &str =
- "use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments";
/// Creates a placeholder argument.
pub(super) fn dummy_arg(ident: Ident) -> Param {
let pat = P(Pat {
id: ast::DUMMY_NODE_ID,
- kind: PatKind::Ident(BindingMode::ByValue(Mutability::Not), ident, None),
+ kind: PatKind::Ident(BindingAnnotation::NONE, ident, None),
span: ident.span,
tokens: None,
});
@@ -53,34 +62,6 @@ pub(super) fn dummy_arg(ident: Ident) -> Param {
}
}
-pub enum Error {
- UselessDocComment,
-}
-
-impl Error {
- fn span_err(
- self,
- sp: impl Into<MultiSpan>,
- handler: &Handler,
- ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
- match self {
- Error::UselessDocComment => {
- let mut err = struct_span_err!(
- handler,
- sp,
- E0585,
- "found a documentation comment that doesn't document anything",
- );
- err.help(
- "doc comments must come before what they document, maybe a comment was \
- intended with `//`?",
- );
- err
- }
- }
- }
-}
-
pub(super) trait RecoverQPath: Sized + 'static {
const PATH_STYLE: PathStyle = PathStyle::Expr;
fn to_ty(&self) -> Option<P<Ty>>;
@@ -228,13 +209,13 @@ struct MultiSugg {
}
impl MultiSugg {
- fn emit<G: EmissionGuarantee>(self, err: &mut DiagnosticBuilder<'_, G>) {
+ fn emit(self, err: &mut Diagnostic) {
err.multipart_suggestion(&self.msg, self.patches, self.applicability);
}
/// Overrides individual messages and applicabilities.
- fn emit_many<G: EmissionGuarantee>(
- err: &mut DiagnosticBuilder<'_, G>,
+ fn emit_many(
+ err: &mut Diagnostic,
msg: &str,
applicability: Applicability,
suggestions: impl Iterator<Item = Self>,
@@ -243,97 +224,6 @@ impl MultiSugg {
}
}
-#[derive(SessionDiagnostic)]
-#[error(parser::maybe_report_ambiguous_plus)]
-struct AmbiguousPlus {
- pub sum_ty: String,
- #[primary_span]
- #[suggestion(code = "({sum_ty})")]
- pub span: Span,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(parser::maybe_recover_from_bad_type_plus, code = "E0178")]
-struct BadTypePlus {
- pub ty: String,
- #[primary_span]
- pub span: Span,
- #[subdiagnostic]
- pub sub: BadTypePlusSub,
-}
-
-#[derive(SessionSubdiagnostic)]
-pub enum BadTypePlusSub {
- #[suggestion(
- parser::add_paren,
- code = "{sum_with_parens}",
- applicability = "machine-applicable"
- )]
- AddParen {
- sum_with_parens: String,
- #[primary_span]
- span: Span,
- },
- #[label(parser::forgot_paren)]
- ForgotParen {
- #[primary_span]
- span: Span,
- },
- #[label(parser::expect_path)]
- ExpectPath {
- #[primary_span]
- span: Span,
- },
-}
-
-#[derive(SessionDiagnostic)]
-#[error(parser::maybe_recover_from_bad_qpath_stage_2)]
-struct BadQPathStage2 {
- #[primary_span]
- #[suggestion(applicability = "maybe-incorrect")]
- span: Span,
- ty: String,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(parser::incorrect_semicolon)]
-struct IncorrectSemicolon<'a> {
- #[primary_span]
- #[suggestion_short(applicability = "machine-applicable")]
- span: Span,
- #[help]
- opt_help: Option<()>,
- name: &'a str,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(parser::incorrect_use_of_await)]
-struct IncorrectUseOfAwait {
- #[primary_span]
- #[suggestion(parser::parentheses_suggestion, applicability = "machine-applicable")]
- span: Span,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(parser::incorrect_use_of_await)]
-struct IncorrectAwait {
- #[primary_span]
- span: Span,
- #[suggestion(parser::postfix_suggestion, code = "{expr}.await{question_mark}")]
- sugg_span: (Span, Applicability),
- expr: String,
- question_mark: &'static str,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(parser::in_in_typo)]
-struct InInTypo {
- #[primary_span]
- span: Span,
- #[suggestion(applicability = "machine-applicable")]
- sugg_span: Span,
-}
-
// SnapshotParser is used to create a snapshot of the parser
// without causing duplicate errors being emitted when the `Parser`
// is dropped.
@@ -358,15 +248,6 @@ impl<'a> DerefMut for SnapshotParser<'a> {
impl<'a> Parser<'a> {
#[rustc_lint_diagnostics]
- pub(super) fn span_err<S: Into<MultiSpan>>(
- &self,
- sp: S,
- err: Error,
- ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
- err.span_err(sp, self.diagnostic())
- }
-
- #[rustc_lint_diagnostics]
pub fn struct_span_err<S: Into<MultiSpan>>(
&self,
sp: S,
@@ -387,7 +268,7 @@ impl<'a> Parser<'a> {
/// This is to avoid losing unclosed delims errors `create_snapshot_for_diagnostic` clears.
pub(super) fn restore_snapshot(&mut self, snapshot: SnapshotParser<'a>) {
*self = snapshot.parser;
- self.unclosed_delims.extend(snapshot.unclosed_delims.clone());
+ self.unclosed_delims.extend(snapshot.unclosed_delims);
}
pub fn unclosed_delims(&self) -> &[UnmatchedBrace] {
@@ -411,10 +292,6 @@ impl<'a> Parser<'a> {
}
pub(super) fn expected_ident_found(&self) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
- let mut err = self.struct_span_err(
- self.token.span,
- &format!("expected identifier, found {}", super::token_descr(&self.token)),
- );
let valid_follow = &[
TokenKind::Eq,
TokenKind::Colon,
@@ -426,34 +303,35 @@ impl<'a> Parser<'a> {
TokenKind::CloseDelim(Delimiter::Brace),
TokenKind::CloseDelim(Delimiter::Parenthesis),
];
- match self.token.ident() {
+ let suggest_raw = match self.token.ident() {
Some((ident, false))
if ident.is_raw_guess()
&& self.look_ahead(1, |t| valid_follow.contains(&t.kind)) =>
{
- err.span_suggestion_verbose(
- ident.span.shrink_to_lo(),
- &format!("escape `{}` to use it as an identifier", ident.name),
- "r#",
- Applicability::MaybeIncorrect,
- );
+ Some(SuggEscapeToUseAsIdentifier {
+ span: ident.span.shrink_to_lo(),
+ // `Symbol::to_string()` is different from `Symbol::into_diagnostic_arg()`,
+ // which uses `Symbol::to_ident_string()` and "helpfully" adds an implicit `r#`
+ ident_name: ident.name.to_string(),
+ })
}
- _ => {}
- }
- if let Some(token_descr) = super::token_descr_opt(&self.token) {
- err.span_label(self.token.span, format!("expected identifier, found {}", token_descr));
- } else {
- err.span_label(self.token.span, "expected identifier");
+ _ => None,
+ };
+
+ let suggest_remove_comma =
if self.token == token::Comma && self.look_ahead(1, |t| t.is_ident()) {
- err.span_suggestion(
- self.token.span,
- "remove this comma",
- "",
- Applicability::MachineApplicable,
- );
- }
- }
- err
+ Some(SuggRemoveComma { span: self.token.span })
+ } else {
+ None
+ };
+
+ let err = ExpectedIdentifier {
+ span: self.token.span,
+ token: self.token.clone(),
+ suggest_raw,
+ suggest_remove_comma,
+ };
+ err.into_diagnostic(&self.sess.span_diagnostic)
}
pub(super) fn expected_one_of_not_found(
@@ -518,8 +396,8 @@ impl<'a> Parser<'a> {
expected.dedup();
let sm = self.sess.source_map();
- let msg = format!("expected `;`, found {}", super::token_descr(&self.token));
- let appl = Applicability::MachineApplicable;
+
+ // Special-case "expected `;`" errors
if expected.contains(&TokenType::Token(token::Semi)) {
if self.token.span == DUMMY_SP || self.prev_token.span == DUMMY_SP {
// Likely inside a macro, can't provide meaningful suggestions.
@@ -547,18 +425,22 @@ impl<'a> Parser<'a> {
//
// let x = 32:
// let y = 42;
+ self.sess.emit_err(ExpectedSemi {
+ span: self.token.span,
+ token: self.token.clone(),
+ unexpected_token_label: None,
+ sugg: ExpectedSemiSugg::ChangeToSemi(self.token.span),
+ });
self.bump();
- let sp = self.prev_token.span;
- self.struct_span_err(sp, &msg)
- .span_suggestion_short(sp, "change this to `;`", ";", appl)
- .emit();
return Ok(true);
} else if self.look_ahead(0, |t| {
t == &token::CloseDelim(Delimiter::Brace)
- || (t.can_begin_expr() && t != &token::Semi && t != &token::Pound)
+ || ((t.can_begin_expr() || t.can_begin_item())
+ && t != &token::Semi
+ && t != &token::Pound)
// Avoid triggering with too many trailing `#` in raw string.
|| (sm.is_multiline(
- self.prev_token.span.shrink_to_hi().until(self.token.span.shrink_to_lo())
+ self.prev_token.span.shrink_to_hi().until(self.token.span.shrink_to_lo()),
) && t == &token::Pound)
}) && !expected.contains(&TokenType::Token(token::Comma))
{
@@ -567,15 +449,25 @@ impl<'a> Parser<'a> {
//
// let x = 32
// let y = 42;
- let sp = self.prev_token.span.shrink_to_hi();
- self.struct_span_err(sp, &msg)
- .span_label(self.token.span, "unexpected token")
- .span_suggestion_short(sp, "add `;` here", ";", appl)
- .emit();
+ let span = self.prev_token.span.shrink_to_hi();
+ self.sess.emit_err(ExpectedSemi {
+ span,
+ token: self.token.clone(),
+ unexpected_token_label: Some(self.token.span),
+ sugg: ExpectedSemiSugg::AddSemi(span),
+ });
return Ok(true);
}
}
+ if self.token.kind == TokenKind::EqEq
+ && self.prev_token.is_ident()
+ && expected.iter().any(|tok| matches!(tok, TokenType::Token(TokenKind::Eq)))
+ {
+ // Likely typo: `=` → `==` in let expr or enum item
+ return Err(self.sess.create_err(UseEqInstead { span: self.token.span }));
+ }
+
let expect = tokens_to_string(&expected);
let actual = super::token_descr(&self.token);
let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 {
@@ -590,7 +482,7 @@ impl<'a> Parser<'a> {
)
} else if expected.is_empty() {
(
- format!("unexpected token: {}", actual),
+ format!("unexpected token: {actual}"),
(self.prev_token.span, "unexpected token after this".to_string()),
)
} else {
@@ -600,19 +492,33 @@ impl<'a> Parser<'a> {
)
};
self.last_unexpected_token_span = Some(self.token.span);
+ // FIXME: translation requires list formatting (for `expect`)
let mut err = self.struct_span_err(self.token.span, &msg_exp);
if let TokenKind::Ident(symbol, _) = &self.prev_token.kind {
- if symbol.as_str() == "public" {
+ if ["def", "fun", "func", "function"].contains(&symbol.as_str()) {
err.span_suggestion_short(
self.prev_token.span,
- "write `pub` instead of `public` to make the item public",
- "pub",
- appl,
+ &format!("write `fn` instead of `{symbol}` to declare a function"),
+ "fn",
+ Applicability::MachineApplicable,
);
}
}
+ // `pub` may be used for an item or `pub(crate)`
+ if self.prev_token.is_ident_named(sym::public)
+ && (self.token.can_begin_item()
+ || self.token.kind == TokenKind::OpenDelim(Delimiter::Parenthesis))
+ {
+ err.span_suggestion_short(
+ self.prev_token.span,
+ "write `pub` instead of `public` to make the item public",
+ "pub",
+ Applicability::MachineApplicable,
+ );
+ }
+
// Add suggestion for a missing closing angle bracket if '>' is included in expected_tokens
// there are unclosed angle brackets
if self.unmatched_angle_bracket_count > 0
@@ -734,7 +640,7 @@ impl<'a> Parser<'a> {
let mut snapshot = self.create_snapshot_for_diagnostic();
let path =
Path { segments: vec![], span: self.prev_token.span.shrink_to_lo(), tokens: None };
- let struct_expr = snapshot.parse_struct_expr(None, path, AttrVec::new(), false);
+ 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) {
(Ok(expr), Err(mut err)) => {
@@ -747,19 +653,13 @@ impl<'a> Parser<'a> {
// field: value,
// } }
err.delay_as_bug();
- self.struct_span_err(
- expr.span,
- fluent::parser::struct_literal_body_without_path,
- )
- .multipart_suggestion(
- fluent::parser::suggestion,
- vec![
- (expr.span.shrink_to_lo(), "{ SomeStruct ".to_string()),
- (expr.span.shrink_to_hi(), " }".to_string()),
- ],
- Applicability::MaybeIncorrect,
- )
- .emit();
+ self.sess.emit_err(StructLiteralBodyWithoutPath {
+ span: expr.span,
+ sugg: StructLiteralBodyWithoutPathSugg {
+ before: expr.span.shrink_to_lo(),
+ after: expr.span.shrink_to_hi(),
+ },
+ });
self.restore_snapshot(snapshot);
let mut tail = self.mk_block(
vec![self.mk_stmt_err(expr.span)],
@@ -953,18 +853,8 @@ impl<'a> Parser<'a> {
self.eat_to_tokens(end);
let span = lo.until(self.token.span);
- let total_num_of_gt = number_of_gt + number_of_shr * 2;
- self.struct_span_err(
- span,
- &format!("unmatched angle bracket{}", pluralize!(total_num_of_gt)),
- )
- .span_suggestion(
- span,
- &format!("remove extra angle bracket{}", pluralize!(total_num_of_gt)),
- "",
- Applicability::MachineApplicable,
- )
- .emit();
+ let num_extra_brackets = number_of_gt + number_of_shr * 2;
+ self.sess.emit_err(UnmatchedAngleBrackets { span, num_extra_brackets });
return true;
}
false
@@ -993,19 +883,13 @@ impl<'a> Parser<'a> {
let args = AngleBracketedArgs { args, span }.into();
segment.args = args;
- self.struct_span_err(
+ self.sess.emit_err(GenericParamsWithoutAngleBrackets {
span,
- "generic parameters without surrounding angle brackets",
- )
- .multipart_suggestion(
- "surround the type parameters with angle brackets",
- vec![
- (span.shrink_to_lo(), "<".to_string()),
- (trailing_span, ">".to_string()),
- ],
- Applicability::MachineApplicable,
- )
- .emit();
+ sugg: GenericParamsWithoutAngleBracketsSugg {
+ left: span.shrink_to_lo(),
+ right: trailing_span,
+ },
+ });
} else {
// This doesn't look like an invalid turbofish, can't recover parse state.
self.restore_snapshot(snapshot);
@@ -1042,7 +926,7 @@ impl<'a> Parser<'a> {
if self.eat(&token::Gt) {
e.span_suggestion_verbose(
binop.span.shrink_to_lo(),
- TURBOFISH_SUGGESTION_STR,
+ fluent::parser_sugg_turbofish_syntax,
"::",
Applicability::MaybeIncorrect,
)
@@ -1074,7 +958,7 @@ impl<'a> Parser<'a> {
/// parenthesising the leftmost comparison.
fn attempt_chained_comparison_suggestion(
&mut self,
- err: &mut Diagnostic,
+ err: &mut ComparisonOperatorsCannotBeChained,
inner_op: &Expr,
outer_op: &Spanned<AssocOp>,
) -> bool /* advanced the cursor */ {
@@ -1087,16 +971,6 @@ impl<'a> Parser<'a> {
// suggestion being the only one to apply is high.
return false;
}
- let mut enclose = |left: Span, right: Span| {
- err.multipart_suggestion(
- "parenthesize the comparison",
- vec![
- (left.shrink_to_lo(), "(".to_string()),
- (right.shrink_to_hi(), ")".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- };
return match (op.node, &outer_op.node) {
// `x == y == z`
(BinOpKind::Eq, AssocOp::Equal) |
@@ -1110,12 +984,10 @@ impl<'a> Parser<'a> {
self.span_to_snippet(e.span)
.unwrap_or_else(|_| pprust::expr_to_string(&e))
};
- err.span_suggestion_verbose(
- inner_op.span.shrink_to_hi(),
- "split the comparison into two",
- format!(" && {}", expr_to_str(&r1)),
- Applicability::MaybeIncorrect,
- );
+ err.chaining_sugg = Some(ComparisonOperatorsCannotBeChainedSugg::SplitComparison {
+ span: inner_op.span.shrink_to_hi(),
+ middle_term: expr_to_str(&r1),
+ });
false // Keep the current parse behavior, where the AST is `(x < y) < z`.
}
// `x == y < z`
@@ -1126,7 +998,10 @@ impl<'a> Parser<'a> {
Ok(r2) => {
// We are sure that outer-op-rhs could be consumed, the suggestion is
// likely correct.
- enclose(r1.span, r2.span);
+ err.chaining_sugg = Some(ComparisonOperatorsCannotBeChainedSugg::Parenthesize {
+ left: r1.span.shrink_to_lo(),
+ right: r2.span.shrink_to_hi(),
+ });
true
}
Err(expr_err) => {
@@ -1143,7 +1018,10 @@ impl<'a> Parser<'a> {
// further checks are necessary.
match self.parse_expr() {
Ok(_) => {
- enclose(l1.span, r1.span);
+ err.chaining_sugg = Some(ComparisonOperatorsCannotBeChainedSugg::Parenthesize {
+ left: l1.span.shrink_to_lo(),
+ right: r1.span.shrink_to_hi(),
+ });
true
}
Err(expr_err) => {
@@ -1188,23 +1066,15 @@ impl<'a> Parser<'a> {
outer_op.node,
);
- let mk_err_expr =
- |this: &Self, span| Ok(Some(this.mk_expr(span, ExprKind::Err, AttrVec::new())));
+ let mk_err_expr = |this: &Self, span| Ok(Some(this.mk_expr(span, ExprKind::Err)));
match inner_op.kind {
ExprKind::Binary(op, ref l1, ref r1) if op.node.is_comparison() => {
- let mut err = self.struct_span_err(
- vec![op.span, self.prev_token.span],
- "comparison operators cannot be chained",
- );
-
- let suggest = |err: &mut Diagnostic| {
- err.span_suggestion_verbose(
- op.span.shrink_to_lo(),
- TURBOFISH_SUGGESTION_STR,
- "::",
- Applicability::MaybeIncorrect,
- );
+ let mut err = ComparisonOperatorsCannotBeChained {
+ span: vec![op.span, self.prev_token.span],
+ suggest_turbofish: None,
+ help_turbofish: None,
+ chaining_sugg: None,
};
// Include `<` to provide this recommendation even in a case like
@@ -1231,7 +1101,7 @@ impl<'a> Parser<'a> {
return if token::ModSep == self.token.kind {
// We have some certainty that this was a bad turbofish at this point.
// `foo< bar >::`
- suggest(&mut err);
+ err.suggest_turbofish = Some(op.span.shrink_to_lo());
let snapshot = self.create_snapshot_for_diagnostic();
self.bump(); // `::`
@@ -1240,7 +1110,7 @@ impl<'a> Parser<'a> {
match self.parse_expr() {
Ok(_) => {
// 99% certain that the suggestion is correct, continue parsing.
- err.emit();
+ self.sess.emit_err(err);
// FIXME: actually check that the two expressions in the binop are
// paths and resynthesize new fn call expression instead of using
// `ExprKind::Err` placeholder.
@@ -1251,18 +1121,18 @@ impl<'a> Parser<'a> {
// Not entirely sure now, but we bubble the error up with the
// suggestion.
self.restore_snapshot(snapshot);
- Err(err)
+ Err(err.into_diagnostic(&self.sess.span_diagnostic))
}
}
} else if token::OpenDelim(Delimiter::Parenthesis) == self.token.kind {
// We have high certainty that this was a bad turbofish at this point.
// `foo< bar >(`
- suggest(&mut err);
+ err.suggest_turbofish = Some(op.span.shrink_to_lo());
// Consume the fn call arguments.
match self.consume_fn_args() {
- Err(()) => Err(err),
+ Err(()) => Err(err.into_diagnostic(&self.sess.span_diagnostic)),
Ok(()) => {
- err.emit();
+ self.sess.emit_err(err);
// FIXME: actually check that the two expressions in the binop are
// paths and resynthesize new fn call expression instead of using
// `ExprKind::Err` placeholder.
@@ -1275,25 +1145,24 @@ impl<'a> Parser<'a> {
{
// All we know is that this is `foo < bar >` and *nothing* else. Try to
// be helpful, but don't attempt to recover.
- err.help(TURBOFISH_SUGGESTION_STR);
- err.help("or use `(...)` if you meant to specify fn arguments");
+ err.help_turbofish = Some(());
}
// If it looks like a genuine attempt to chain operators (as opposed to a
// misformatted turbofish, for instance), suggest a correct form.
if self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op)
{
- err.emit();
+ self.sess.emit_err(err);
mk_err_expr(self, inner_op.span.to(self.prev_token.span))
} else {
// These cases cause too many knock-down errors, bail out (#61329).
- Err(err)
+ Err(err.into_diagnostic(&self.sess.span_diagnostic))
}
};
}
let recover =
self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op);
- err.emit();
+ self.sess.emit_err(err);
if recover {
return mk_err_expr(self, inner_op.span.to(self.prev_token.span));
}
@@ -1334,17 +1203,13 @@ impl<'a> Parser<'a> {
pub(super) fn maybe_recover_from_question_mark(&mut self, ty: P<Ty>) -> P<Ty> {
if self.token == token::Question {
self.bump();
- self.struct_span_err(self.prev_token.span, "invalid `?` in type")
- .span_label(self.prev_token.span, "`?` is only allowed on expressions, not types")
- .multipart_suggestion(
- "if you meant to express that the type might not contain a value, use the `Option` wrapper type",
- vec![
- (ty.span.shrink_to_lo(), "Option<".to_string()),
- (self.prev_token.span, ">".to_string()),
- ],
- Applicability::MachineApplicable,
- )
- .emit();
+ self.sess.emit_err(QuestionMarkInType {
+ span: self.prev_token.span,
+ sugg: QuestionMarkInTypeSugg {
+ left: ty.span.shrink_to_lo(),
+ right: self.prev_token.span,
+ },
+ });
self.mk_ty(ty.span.to(self.prev_token.span), TyKind::Err)
} else {
ty
@@ -1497,7 +1362,7 @@ impl<'a> Parser<'a> {
MultiSugg {
msg: format!("use `{}= 1` instead", kind.op.chr()),
patches: vec![
- (pre_span, format!("{{ let {} = ", tmp_var)),
+ (pre_span, format!("{{ let {tmp_var} = ")),
(post_span, format!("; {} {}= 1; {} }}", base_src, kind.op.chr(), tmp_var)),
],
applicability: Applicability::HasPlaceholders,
@@ -1509,9 +1374,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,
}
}
@@ -1596,7 +1469,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.
@@ -1647,7 +1520,6 @@ impl<'a> Parser<'a> {
&mut self,
lo: Span,
await_sp: Span,
- attrs: AttrVec,
) -> PResult<'a, P<Expr>> {
let (hi, expr, is_question) = if self.token == token::Not {
// Handle `await!(<expr>)`.
@@ -1662,7 +1534,7 @@ impl<'a> Parser<'a> {
ExprKind::Try(_) => ExprKind::Err,
_ => ExprKind::Await(expr),
};
- let expr = self.mk_expr(lo.to(sp), kind, attrs);
+ let expr = self.mk_expr(lo.to(sp), kind);
self.maybe_recover_from_bad_qpath(expr)
}
@@ -1680,7 +1552,7 @@ impl<'a> Parser<'a> {
// Handle `await { <expr> }`.
// This needs to be handled separately from the next arm to avoid
// interpreting `await { <expr> }?` as `<expr>?.await`.
- self.parse_block_expr(None, self.token.span, BlockCheckMode::Default, AttrVec::new())
+ self.parse_block_expr(None, self.token.span, BlockCheckMode::Default)
} else {
self.parse_expr()
}
@@ -1769,19 +1641,16 @@ impl<'a> Parser<'a> {
(token::CloseDelim(Delimiter::Parenthesis), Some(begin_par_sp)) => {
self.bump();
- self.struct_span_err(
- MultiSpan::from_spans(vec![begin_par_sp, self.prev_token.span]),
- "unexpected parentheses surrounding `for` loop head",
- )
- .multipart_suggestion(
- "remove parentheses in `for` loop",
- vec![(begin_par_sp, String::new()), (self.prev_token.span, String::new())],
+ self.sess.emit_err(ParenthesesInForHead {
+ span: vec![begin_par_sp, self.prev_token.span],
// 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.
- Applicability::MachineApplicable,
- )
- .emit();
+ sugg: ParenthesesInForHeadSugg {
+ left: begin_par_sp,
+ right: self.prev_token.span,
+ },
+ });
// Unwrap `(pat)` into `pat` to avoid the `unused_parens` lint.
pat.and_then(|pat| match pat.kind {
@@ -1823,7 +1692,7 @@ impl<'a> Parser<'a> {
err.emit();
// Recover from parse error, callers expect the closing delim to be consumed.
self.consume_block(delim, ConsumeClosingDelim::Yes);
- self.mk_expr(lo.to(self.prev_token.span), ExprKind::Err, AttrVec::new())
+ self.mk_expr(lo.to(self.prev_token.span), ExprKind::Err)
}
}
}
@@ -2000,12 +1869,7 @@ impl<'a> Parser<'a> {
pub(super) fn eat_incorrect_doc_comment_for_param_type(&mut self) {
if let token::DocComment(..) = self.token.kind {
- self.struct_span_err(
- self.token.span,
- "documentation comments cannot be applied to a function parameter's type",
- )
- .span_label(self.token.span, "doc comments are not allowed here")
- .emit();
+ self.sess.emit_err(DocCommentOnParamType { span: self.token.span });
self.bump();
} else if self.token == token::Pound
&& self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Bracket))
@@ -2017,9 +1881,7 @@ impl<'a> Parser<'a> {
}
let sp = lo.to(self.token.span);
self.bump();
- self.struct_span_err(sp, "attributes cannot be applied to a function parameter's type")
- .span_label(sp, "attributes are not allowed here")
- .emit();
+ self.sess.emit_err(AttributeOnParamType { span: sp });
}
}
@@ -2140,19 +2002,7 @@ impl<'a> Parser<'a> {
self.expect(&token::Colon)?;
let ty = self.parse_ty()?;
- struct_span_err!(
- self.diagnostic(),
- pat.span,
- E0642,
- "patterns aren't allowed in methods without bodies",
- )
- .span_suggestion_short(
- pat.span,
- "give this argument a name or use an underscore to ignore it",
- "_",
- Applicability::MachineApplicable,
- )
- .emit();
+ self.sess.emit_err(PatternMethodParamWithoutBody { span: pat.span });
// Pretend the pattern is `_`, to avoid duplicate errors from AST validation.
let pat =
@@ -2161,11 +2011,9 @@ impl<'a> Parser<'a> {
}
pub(super) fn recover_bad_self_param(&mut self, mut param: Param) -> PResult<'a, Param> {
- let sp = param.pat.span;
+ let span = param.pat.span;
param.ty.kind = TyKind::Err;
- self.struct_span_err(sp, "unexpected `self` parameter in function")
- .span_label(sp, "must be the first parameter of an associated function")
- .emit();
+ self.sess.emit_err(SelfParamNotFirst { span });
Ok(param)
}
@@ -2199,7 +2047,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}"))
}
_ => (
@@ -2210,7 +2058,7 @@ impl<'a> Parser<'a> {
let mut err = self.struct_span_err(span, &msg);
let sp = self.sess.source_map().start_point(self.token.span);
if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&sp) {
- self.sess.expr_parentheses_needed(&mut err, *sp);
+ err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
}
err.span_label(span, "expected expression");
err
@@ -2314,27 +2162,20 @@ impl<'a> Parser<'a> {
err
})?;
if !self.expr_is_valid_const_arg(&expr) {
- self.struct_span_err(
- expr.span,
- "expressions must be enclosed in braces to be used as const generic \
- arguments",
- )
- .multipart_suggestion(
- "enclose the `const` expression in braces",
- vec![
- (expr.span.shrink_to_lo(), "{ ".to_string()),
- (expr.span.shrink_to_hi(), " }".to_string()),
- ],
- Applicability::MachineApplicable,
- )
- .emit();
+ self.sess.emit_err(ConstGenericWithoutBraces {
+ span: expr.span,
+ sugg: ConstGenericWithoutBracesSugg {
+ left: expr.span.shrink_to_lo(),
+ right: expr.span.shrink_to_hi(),
+ },
+ });
}
Ok(expr)
}
fn recover_const_param_decl(&mut self, ty_generics: Option<&Generics>) -> Option<GenericArg> {
let snapshot = self.create_snapshot_for_diagnostic();
- let param = match self.parse_const_param(vec![]) {
+ let param = match self.parse_const_param(AttrVec::new()) {
Ok(param) => param,
Err(err) => {
err.cancel();
@@ -2342,24 +2183,30 @@ impl<'a> Parser<'a> {
return None;
}
};
- let mut err =
- self.struct_span_err(param.span(), "unexpected `const` parameter declaration");
- err.span_label(param.span(), "expected a `const` expression, not a parameter declaration");
- if let (Some(generics), Ok(snippet)) =
- (ty_generics, self.sess.source_map().span_to_snippet(param.span()))
- {
- let (span, sugg) = match &generics.params[..] {
- [] => (generics.span, format!("<{snippet}>")),
- [.., generic] => (generic.span().shrink_to_hi(), format!(", {snippet}")),
- };
- err.multipart_suggestion(
- "`const` parameters must be declared for the `impl`",
- vec![(span, sugg), (param.span(), param.ident.to_string())],
- Applicability::MachineApplicable,
- );
- }
+
+ let ident = param.ident.to_string();
+ let sugg = match (ty_generics, self.sess.source_map().span_to_snippet(param.span())) {
+ (Some(Generics { params, span: impl_generics, .. }), Ok(snippet)) => {
+ Some(match &params[..] {
+ [] => UnexpectedConstParamDeclarationSugg::AddParam {
+ impl_generics: *impl_generics,
+ incorrect_decl: param.span(),
+ snippet,
+ ident,
+ },
+ [.., generic] => UnexpectedConstParamDeclarationSugg::AppendParam {
+ impl_generics_end: generic.span().shrink_to_hi(),
+ incorrect_decl: param.span(),
+ snippet,
+ ident,
+ },
+ })
+ }
+ _ => None,
+ };
+ self.sess.emit_err(UnexpectedConstParamDeclaration { span: param.span(), sugg });
+
let value = self.mk_expr_err(param.span());
- err.emit();
Some(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }))
}
@@ -2377,20 +2224,15 @@ impl<'a> Parser<'a> {
self.bump(); // `const`
// Detect and recover from the old, pre-RFC2000 syntax for const generics.
- let mut err = self
- .struct_span_err(start, "expected lifetime, type, or constant, found keyword `const`");
+ let mut err = UnexpectedConstInGenericParam { span: start, to_remove: None };
if self.check_const_arg() {
- err.span_suggestion_verbose(
- start.until(self.token.span),
- "the `const` keyword is only needed in the definition of the type",
- "",
- Applicability::MaybeIncorrect,
- );
- err.emit();
+ err.to_remove = Some(start.until(self.token.span));
+ self.sess.emit_err(err);
Ok(Some(GenericArg::Const(self.parse_const_arg()?)))
} else {
let after_kw_const = self.token.span;
- self.recover_const_arg(after_kw_const, err).map(Some)
+ self.recover_const_arg(after_kw_const, err.into_diagnostic(&self.sess.span_diagnostic))
+ .map(Some)
}
}
@@ -2398,7 +2240,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,
@@ -2496,24 +2338,6 @@ impl<'a> Parser<'a> {
GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })
}
- /// Get the diagnostics for the cases where `move async` is found.
- ///
- /// `move_async_span` starts at the 'm' of the move keyword and ends with the 'c' of the async keyword
- pub(super) fn incorrect_move_async_order_found(
- &self,
- move_async_span: Span,
- ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
- let mut err =
- self.struct_span_err(move_async_span, "the order of `move` and `async` is incorrect");
- err.span_suggestion_verbose(
- move_async_span,
- "try switching the order",
- "async move",
- Applicability::MaybeIncorrect,
- );
- err
- }
-
/// Some special error handling for the "top-level" patterns in a match arm,
/// `for` loop, `let`, &c. (in contrast to subpatterns within such).
pub(crate) fn maybe_recover_colon_colon_in_pat_typo(
@@ -2577,7 +2401,7 @@ impl<'a> Parser<'a> {
}
_ => {}
},
- PatKind::Ident(BindingMode::ByValue(Mutability::Not), ident, None) => {
+ PatKind::Ident(BindingAnnotation::NONE, ident, None) => {
match &first_pat.kind {
PatKind::Ident(_, old_ident, _) => {
let path = PatKind::Path(
@@ -2632,11 +2456,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");
@@ -2709,17 +2537,11 @@ impl<'a> Parser<'a> {
let (a_span, b_span) = (a.span(), b.span());
let between_span = a_span.shrink_to_hi().to(b_span.shrink_to_lo());
if self.span_to_snippet(between_span).as_ref().map(|a| &a[..]) == Ok(":: ") {
- let mut err = self.struct_span_err(
- path.span.shrink_to_hi(),
- "expected `:` followed by trait or lifetime",
- );
- err.span_suggestion(
- between_span,
- "use single colon",
- ": ",
- Applicability::MachineApplicable,
- );
- return Err(err);
+ return Err(DoubleColonInBound {
+ span: path.span.shrink_to_hi(),
+ between: between_span,
+ }
+ .into_diagnostic(&self.sess.span_diagnostic));
}
}
}
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 0719a0ef0..a781748ef 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -5,6 +5,28 @@ use super::{
AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions,
SemiColonMode, SeqSep, TokenExpectType, TokenType, TrailingToken,
};
+use crate::errors::{
+ ArrayBracketsInsteadOfSpaces, ArrayBracketsInsteadOfSpacesSugg, AsyncMoveOrderIncorrect,
+ BinaryFloatLiteralNotSupported, BracesForStructLiteral, CatchAfterTry, CommaAfterBaseStruct,
+ ComparisonInterpretedAsGeneric, ComparisonOrShiftInterpretedAsGenericSugg,
+ DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, ExpectedElseBlock, ExpectedExpressionFoundLet,
+ FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart, FoundExprWouldBeStmt,
+ HexadecimalFloatLiteralNotSupported, IfExpressionMissingCondition,
+ IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub, IntLiteralTooLarge,
+ InvalidBlockMacroSegment, InvalidComparisonOperator, InvalidComparisonOperatorSub,
+ InvalidFloatLiteralSuffix, InvalidFloatLiteralWidth, InvalidIntLiteralWidth,
+ InvalidInterpolatedExpression, InvalidLiteralSuffix, InvalidLiteralSuffixOnTupleIndex,
+ InvalidLogicalOperator, InvalidLogicalOperatorSub, InvalidNumLiteralBasePrefix,
+ InvalidNumLiteralSuffix, LabeledLoopInBreak, LeadingPlusNotSupported, LeftArrowOperator,
+ LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, MalformedLoopLabel,
+ MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg, MissingCommaAfterMatchArm,
+ MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray, NoFieldsForFnCall,
+ NotAsNegationOperator, NotAsNegationOperatorSub, OctalFloatLiteralNotSupported,
+ OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields,
+ RequireColonAfterLabeledExpression, ShiftInterpretedAsGeneric, StructLiteralNotAllowedHere,
+ StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator, UnexpectedTokenAfterLabel,
+ UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses,
+};
use crate::maybe_recover_from_interpolated_ty_qpath;
use core::mem;
@@ -20,8 +42,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_data_structures::thin_vec::ThinVec;
-use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, PResult};
+use rustc_errors::{
+ Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, PResult,
+ StashKey,
+};
+use rustc_session::errors::ExprParenthesesNeeded;
use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;
use rustc_session::lint::BuiltinLintDiagnostics;
use rustc_span::source_map::{self, Span, Spanned};
@@ -45,20 +70,12 @@ macro_rules! maybe_whole_expr {
token::NtPath(path) => {
let path = (**path).clone();
$p.bump();
- return Ok($p.mk_expr(
- $p.prev_token.span,
- ExprKind::Path(None, path),
- AttrVec::new(),
- ));
+ return Ok($p.mk_expr($p.prev_token.span, ExprKind::Path(None, path)));
}
token::NtBlock(block) => {
let block = block.clone();
$p.bump();
- return Ok($p.mk_expr(
- $p.prev_token.span,
- ExprKind::Block(block, None),
- AttrVec::new(),
- ));
+ return Ok($p.mk_expr($p.prev_token.span, ExprKind::Block(block, None)));
}
_ => {}
};
@@ -120,7 +137,7 @@ impl<'a> Parser<'a> {
// Special-case handling of `foo(_, _, _)`
err.emit();
self.bump();
- Ok(self.mk_expr(self.prev_token.span, ExprKind::Err, AttrVec::new()))
+ Ok(self.mk_expr(self.prev_token.span, ExprKind::Err))
}
_ => Err(err),
},
@@ -225,15 +242,18 @@ impl<'a> Parser<'a> {
AssocOp::Equal => "==",
AssocOp::NotEqual => "!=",
_ => unreachable!(),
- };
- self.struct_span_err(sp, &format!("invalid comparison operator `{sugg}=`"))
- .span_suggestion_short(
- sp,
- &format!("`{s}=` is not a valid comparison operator, use `{s}`", s = sugg),
- sugg,
- Applicability::MachineApplicable,
- )
- .emit();
+ }
+ .into();
+ let invalid = format!("{}=", &sugg);
+ self.sess.emit_err(InvalidComparisonOperator {
+ span: sp,
+ invalid: invalid.clone(),
+ sub: InvalidComparisonOperatorSub::Correctable {
+ span: sp,
+ invalid,
+ correct: sugg,
+ },
+ });
self.bump();
}
@@ -243,14 +263,15 @@ impl<'a> Parser<'a> {
&& self.prev_token.span.hi() == self.token.span.lo()
{
let sp = op.span.to(self.token.span);
- self.struct_span_err(sp, "invalid comparison operator `<>`")
- .span_suggestion_short(
- sp,
- "`<>` is not a valid comparison operator, use `!=`",
- "!=",
- Applicability::MachineApplicable,
- )
- .emit();
+ self.sess.emit_err(InvalidComparisonOperator {
+ span: sp,
+ invalid: "<>".into(),
+ sub: InvalidComparisonOperatorSub::Correctable {
+ span: sp,
+ invalid: "<>".into(),
+ correct: "!=".into(),
+ },
+ });
self.bump();
}
@@ -260,12 +281,11 @@ impl<'a> Parser<'a> {
&& self.prev_token.span.hi() == self.token.span.lo()
{
let sp = op.span.to(self.token.span);
- self.struct_span_err(sp, "invalid comparison operator `<=>`")
- .span_label(
- sp,
- "`<=>` is not a valid comparison operator, use `std::cmp::Ordering`",
- )
- .emit();
+ self.sess.emit_err(InvalidComparisonOperator {
+ span: sp,
+ invalid: "<=>".into(),
+ sub: InvalidComparisonOperatorSub::Spaceship(sp),
+ });
self.bump();
}
@@ -329,11 +349,9 @@ impl<'a> Parser<'a> {
| AssocOp::GreaterEqual => {
let ast_op = op.to_ast_binop().unwrap();
let binary = self.mk_binary(source_map::respan(cur_op_span, ast_op), lhs, rhs);
- self.mk_expr(span, binary, AttrVec::new())
- }
- AssocOp::Assign => {
- self.mk_expr(span, ExprKind::Assign(lhs, rhs, cur_op_span), AttrVec::new())
+ self.mk_expr(span, binary)
}
+ AssocOp::Assign => self.mk_expr(span, ExprKind::Assign(lhs, rhs, cur_op_span)),
AssocOp::AssignOp(k) => {
let aop = match k {
token::Plus => BinOpKind::Add,
@@ -348,7 +366,7 @@ impl<'a> Parser<'a> {
token::Shr => BinOpKind::Shr,
};
let aopexpr = self.mk_assign_op(source_map::respan(cur_op_span, aop), lhs, rhs);
- self.mk_expr(span, aopexpr, AttrVec::new())
+ self.mk_expr(span, aopexpr)
}
AssocOp::As | AssocOp::Colon | AssocOp::DotDot | AssocOp::DotDotEq => {
self.span_bug(span, "AssocOp should have been handled by special case")
@@ -412,13 +430,11 @@ impl<'a> Parser<'a> {
/// but the next token implies this should be parsed as an expression.
/// For example: `if let Some(x) = x { x } else { 0 } / 2`.
fn error_found_expr_would_be_stmt(&self, lhs: &Expr) {
- let mut err = self.struct_span_err(
- self.token.span,
- &format!("expected expression, found `{}`", pprust::token_to_string(&self.token),),
- );
- err.span_label(self.token.span, "expected expression");
- self.sess.expr_parentheses_needed(&mut err, lhs.span);
- err.emit();
+ self.sess.emit_err(FoundExprWouldBeStmt {
+ span: self.token.span,
+ token: self.token.clone(),
+ suggestion: ExprParenthesesNeeded::surrounding(lhs.span),
+ });
}
/// Possibly translate the current token to an associative operator.
@@ -441,11 +457,19 @@ impl<'a> Parser<'a> {
}
(Some(op), _) => (op, self.token.span),
(None, Some((Ident { name: sym::and, span }, false))) => {
- self.error_bad_logical_op("and", "&&", "conjunction");
+ self.sess.emit_err(InvalidLogicalOperator {
+ span: self.token.span,
+ incorrect: "and".into(),
+ sub: InvalidLogicalOperatorSub::Conjunction(self.token.span),
+ });
(AssocOp::LAnd, span)
}
(None, Some((Ident { name: sym::or, span }, false))) => {
- self.error_bad_logical_op("or", "||", "disjunction");
+ self.sess.emit_err(InvalidLogicalOperator {
+ span: self.token.span,
+ incorrect: "or".into(),
+ sub: InvalidLogicalOperatorSub::Disjunction(self.token.span),
+ });
(AssocOp::LOr, span)
}
_ => return None,
@@ -453,19 +477,6 @@ impl<'a> Parser<'a> {
Some(source_map::respan(span, op))
}
- /// Error on `and` and `or` suggesting `&&` and `||` respectively.
- fn error_bad_logical_op(&self, bad: &str, good: &str, english: &str) {
- self.struct_span_err(self.token.span, &format!("`{bad}` is not a logical operator"))
- .span_suggestion_short(
- self.token.span,
- &format!("use `{good}` to perform logical {english}"),
- good,
- Applicability::MachineApplicable,
- )
- .note("unlike in e.g., python and PHP, `&&` and `||` are used for logical operators")
- .emit();
- }
-
/// Checks if this expression is a successfully parsed statement.
fn expr_is_complete(&self, e: &Expr) -> bool {
self.restrictions.contains(Restrictions::STMT_EXPR)
@@ -491,7 +502,7 @@ impl<'a> Parser<'a> {
let limits =
if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed };
let range = self.mk_range(Some(lhs), rhs, limits);
- Ok(self.mk_expr(span, range, AttrVec::new()))
+ Ok(self.mk_expr(span, range))
}
fn is_at_start_of_range_notation_rhs(&self) -> bool {
@@ -540,7 +551,7 @@ impl<'a> Parser<'a> {
(lo, None)
};
let range = this.mk_range(None, opt_end, limits);
- Ok(this.mk_expr(span, range, attrs.into()))
+ Ok(this.mk_expr_with_attrs(span, range, attrs))
})
}
@@ -553,7 +564,7 @@ impl<'a> Parser<'a> {
($this:ident, $attrs:expr, |this, _| $body:expr) => {
$this.collect_tokens_for_expr($attrs, |$this, attrs| {
let (hi, ex) = $body?;
- Ok($this.mk_expr(lo.to(hi), ex, attrs.into()))
+ Ok($this.mk_expr_with_attrs(lo.to(hi), ex, attrs))
})
};
}
@@ -574,21 +585,16 @@ impl<'a> Parser<'a> {
make_it!(this, attrs, |this, _| this.parse_borrow_expr(lo))
}
token::BinOp(token::Plus) if this.look_ahead(1, |tok| tok.is_numeric_lit()) => {
- let mut err = this.struct_span_err(lo, "leading `+` is not supported");
- err.span_label(lo, "unexpected `+`");
+ let mut err =
+ LeadingPlusNotSupported { span: lo, remove_plus: None, add_parentheses: None };
// a block on the LHS might have been intended to be an expression instead
if let Some(sp) = this.sess.ambiguous_block_expr_parse.borrow().get(&lo) {
- this.sess.expr_parentheses_needed(&mut err, *sp);
+ err.add_parentheses = Some(ExprParenthesesNeeded::surrounding(*sp));
} else {
- err.span_suggestion_verbose(
- lo,
- "try removing the `+`",
- "",
- Applicability::MachineApplicable,
- );
+ err.remove_plus = Some(lo);
}
- err.emit();
+ this.sess.emit_err(err);
this.bump();
this.parse_prefix_expr(None)
@@ -630,14 +636,7 @@ impl<'a> Parser<'a> {
// Recover on `!` suggesting for bitwise negation instead.
fn recover_tilde_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
- self.struct_span_err(lo, "`~` cannot be used as a unary operator")
- .span_suggestion_short(
- lo,
- "use `!` to perform bitwise not",
- "!",
- Applicability::MachineApplicable,
- )
- .emit();
+ self.sess.emit_err(TildeAsUnaryOperator(lo));
self.parse_unary_expr(lo, UnOp::Not)
}
@@ -663,20 +662,25 @@ impl<'a> Parser<'a> {
/// Recover on `not expr` in favor of `!expr`.
fn recover_not_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
// Emit the error...
- let not_token = self.look_ahead(1, |t| t.clone());
- self.struct_span_err(
- not_token.span,
- &format!("unexpected {} after identifier", super::token_descr(&not_token)),
- )
- .span_suggestion_short(
+ let negated_token = self.look_ahead(1, |t| t.clone());
+
+ let sub_diag = if negated_token.is_numeric_lit() {
+ NotAsNegationOperatorSub::SuggestNotBitwise
+ } else if negated_token.is_bool_lit() {
+ NotAsNegationOperatorSub::SuggestNotLogical
+ } else {
+ NotAsNegationOperatorSub::SuggestNotDefault
+ };
+
+ self.sess.emit_err(NotAsNegationOperator {
+ negated: negated_token.span,
+ negated_desc: super::token_descr(&negated_token),
// Span the `not` plus trailing whitespace to avoid
// trailing whitespace after the `!` in our suggestion
- self.sess.source_map().span_until_non_whitespace(lo.to(not_token.span)),
- "use `!` to perform logical negation",
- "!",
- Applicability::MachineApplicable,
- )
- .emit();
+ sub: sub_diag(
+ self.sess.source_map().span_until_non_whitespace(lo.to(negated_token.span)),
+ ),
+ });
// ...and recover!
self.parse_unary_expr(lo, UnOp::Not)
@@ -705,11 +709,7 @@ impl<'a> Parser<'a> {
expr_kind: fn(P<Expr>, P<Ty>) -> ExprKind,
) -> PResult<'a, P<Expr>> {
let mk_expr = |this: &mut Self, lhs: P<Expr>, rhs: P<Ty>| {
- this.mk_expr(
- this.mk_expr_sp(&lhs, lhs_span, rhs.span),
- expr_kind(lhs, rhs),
- AttrVec::new(),
- )
+ this.mk_expr(this.mk_expr_sp(&lhs, lhs_span, rhs.span), expr_kind(lhs, rhs))
};
// Save the state of the parser before parsing type normally, in case there is a
@@ -737,17 +737,13 @@ impl<'a> Parser<'a> {
segments[0].ident.span,
),
};
- match self.parse_labeled_expr(label, AttrVec::new(), false) {
+ match self.parse_labeled_expr(label, false) {
Ok(expr) => {
type_err.cancel();
- self.struct_span_err(label.ident.span, "malformed loop label")
- .span_suggestion(
- label.ident.span,
- "use the correct loop label format",
- label.ident,
- Applicability::MachineApplicable,
- )
- .emit();
+ self.sess.emit_err(MalformedLoopLabel {
+ span: label.ident.span,
+ correct_label: label.ident,
+ });
return Ok(expr);
}
Err(err) => {
@@ -761,9 +757,34 @@ impl<'a> Parser<'a> {
match self.parse_path(PathStyle::Expr) {
Ok(path) => {
- let (op_noun, op_verb) = match self.token.kind {
- token::Lt => ("comparison", "comparing"),
- token::BinOp(token::Shl) => ("shift", "shifting"),
+ let span_after_type = parser_snapshot_after_type.token.span;
+ let expr = mk_expr(
+ self,
+ lhs,
+ self.mk_ty(path.span, TyKind::Path(None, path.clone())),
+ );
+
+ let args_span = self.look_ahead(1, |t| t.span).to(span_after_type);
+ let suggestion = ComparisonOrShiftInterpretedAsGenericSugg {
+ left: expr.span.shrink_to_lo(),
+ right: expr.span.shrink_to_hi(),
+ };
+
+ match self.token.kind {
+ token::Lt => self.sess.emit_err(ComparisonInterpretedAsGeneric {
+ comparison: self.token.span,
+ r#type: path,
+ args: args_span,
+ suggestion,
+ }),
+ token::BinOp(token::Shl) => {
+ self.sess.emit_err(ShiftInterpretedAsGeneric {
+ shift: self.token.span,
+ r#type: path,
+ args: args_span,
+ suggestion,
+ })
+ }
_ => {
// We can end up here even without `<` being the next token, for
// example because `parse_ty_no_plus` returns `Err` on keywords,
@@ -777,33 +798,7 @@ impl<'a> Parser<'a> {
// Successfully parsed the type path leaving a `<` yet to parse.
type_err.cancel();
- // Report non-fatal diagnostics, keep `x as usize` as an expression
- // in AST and continue parsing.
- let msg = format!(
- "`<` is interpreted as a start of generic arguments for `{}`, not a {}",
- pprust::path_to_string(&path),
- op_noun,
- );
- let span_after_type = parser_snapshot_after_type.token.span;
- let expr =
- mk_expr(self, lhs, self.mk_ty(path.span, TyKind::Path(None, path)));
-
- self.struct_span_err(self.token.span, &msg)
- .span_label(
- self.look_ahead(1, |t| t.span).to(span_after_type),
- "interpreted as generic arguments",
- )
- .span_label(self.token.span, format!("not interpreted as {op_noun}"))
- .multipart_suggestion(
- &format!("try {op_verb} the cast value"),
- vec![
- (expr.span.shrink_to_lo(), "(".to_string()),
- (expr.span.shrink_to_hi(), ")".to_string()),
- ],
- Applicability::MachineApplicable,
- )
- .emit();
-
+ // Keep `x as usize` as an expression in AST and continue parsing.
expr
}
Err(path_err) => {
@@ -850,7 +845,7 @@ impl<'a> Parser<'a> {
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),
@@ -859,7 +854,7 @@ impl<'a> Parser<'a> {
);
let mut err = self.struct_span_err(span, &msg);
- let suggest_parens = |err: &mut DiagnosticBuilder<'_, _>| {
+ let suggest_parens = |err: &mut Diagnostic| {
let suggestions = vec![
(span.shrink_to_lo(), "(".to_string()),
(span.shrink_to_hi(), ")".to_string()),
@@ -925,15 +920,7 @@ impl<'a> Parser<'a> {
}
fn error_remove_borrow_lifetime(&self, span: Span, lt_span: Span) {
- self.struct_span_err(span, "borrow expressions cannot be annotated with lifetimes")
- .span_label(lt_span, "annotated with lifetime here")
- .span_suggestion(
- lt_span,
- "remove the lifetime annotation",
- "",
- Applicability::MachineApplicable,
- )
- .emit();
+ self.sess.emit_err(LifetimeInBorrowExpression { span, lifetime_span: lt_span });
}
/// Parse `mut?` or `raw [ const | mut ]`.
@@ -965,18 +952,23 @@ impl<'a> Parser<'a> {
&mut self,
e0: P<Expr>,
lo: Span,
- mut attrs: Vec<ast::Attribute>,
+ mut attrs: ast::AttrVec,
) -> PResult<'a, P<Expr>> {
// Stitch the list of outer attributes onto the return value.
// A little bit ugly, but the best way given the current code
// structure
- self.parse_dot_or_call_expr_with_(e0, lo).map(|expr| {
- expr.map(|mut expr| {
- attrs.extend::<Vec<_>>(expr.attrs.into());
- expr.attrs = attrs.into();
- expr
+ let res = self.parse_dot_or_call_expr_with_(e0, lo);
+ if attrs.is_empty() {
+ res
+ } else {
+ res.map(|expr| {
+ expr.map(|mut expr| {
+ attrs.extend(expr.attrs);
+ expr.attrs = attrs;
+ expr
+ })
})
- })
+ }
}
fn parse_dot_or_call_expr_with_(&mut self, mut e: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> {
@@ -990,7 +982,7 @@ impl<'a> Parser<'a> {
};
if has_question {
// `expr?`
- e = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Try(e), AttrVec::new());
+ e = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Try(e));
continue;
}
let has_dot = if self.prev_token.kind == TokenKind::Ident(kw::Return, false) {
@@ -1167,8 +1159,10 @@ impl<'a> Parser<'a> {
}
let span = self.prev_token.span;
let field = ExprKind::Field(base, Ident::new(field, span));
- self.expect_no_suffix(span, "a tuple index", suffix);
- self.mk_expr(lo.to(span), field, AttrVec::new())
+ if let Some(suffix) = suffix {
+ self.expect_no_tuple_index_suffix(span, suffix);
+ }
+ self.mk_expr(lo.to(span), field)
}
/// Parse a function call expression, `expr(...)`.
@@ -1182,9 +1176,9 @@ impl<'a> Parser<'a> {
};
let open_paren = self.token.span;
- let mut seq = self.parse_paren_expr_seq().map(|args| {
- self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args), AttrVec::new())
- });
+ let mut seq = self
+ .parse_paren_expr_seq()
+ .map(|args| self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args)));
if let Some(expr) =
self.maybe_recover_struct_lit_bad_delims(lo, open_paren, &mut seq, snapshot)
{
@@ -1205,9 +1199,8 @@ impl<'a> Parser<'a> {
) -> Option<P<Expr>> {
match (seq.as_mut(), snapshot) {
(Err(err), Some((mut snapshot, ExprKind::Path(None, path)))) => {
- let name = pprust::path_to_string(&path);
snapshot.bump(); // `(`
- match snapshot.parse_struct_fields(path, false, Delimiter::Parenthesis) {
+ match snapshot.parse_struct_fields(path.clone(), false, Delimiter::Parenthesis) {
Ok((fields, ..))
if snapshot.eat(&token::CloseDelim(Delimiter::Parenthesis)) =>
{
@@ -1217,29 +1210,25 @@ impl<'a> Parser<'a> {
let close_paren = self.prev_token.span;
let span = lo.to(self.prev_token.span);
if !fields.is_empty() {
- let replacement_err = self.struct_span_err(
+ let mut replacement_err = ParenthesesWithStructFields {
span,
- "invalid `struct` delimiters or `fn` call arguments",
- );
- mem::replace(err, replacement_err).cancel();
-
- err.multipart_suggestion(
- &format!("if `{name}` is a struct, use braces as delimiters"),
- vec![
- (open_paren, " { ".to_string()),
- (close_paren, " }".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- err.multipart_suggestion(
- &format!("if `{name}` is a function, use the arguments directly"),
- fields
- .into_iter()
- .map(|field| (field.span.until(field.expr.span), String::new()))
- .collect(),
- Applicability::MaybeIncorrect,
- );
- err.emit();
+ r#type: path,
+ braces_for_struct: BracesForStructLiteral {
+ first: open_paren,
+ second: close_paren,
+ },
+ no_fields_for_fn: NoFieldsForFnCall {
+ fields: fields
+ .into_iter()
+ .map(|field| field.span.until(field.expr.span))
+ .collect(),
+ },
+ }
+ .into_diagnostic(&self.sess.span_diagnostic);
+ replacement_err.emit();
+
+ let old_err = mem::replace(err, replacement_err);
+ old_err.cancel();
} else {
err.emit();
}
@@ -1258,10 +1247,13 @@ impl<'a> Parser<'a> {
/// Parse an indexing expression `expr[...]`.
fn parse_index_expr(&mut self, lo: Span, base: P<Expr>) -> PResult<'a, P<Expr>> {
+ let prev_span = self.prev_token.span;
+ let open_delim_span = self.token.span;
self.bump(); // `[`
let index = self.parse_expr()?;
+ self.suggest_missing_semicolon_before_array(prev_span, open_delim_span)?;
self.expect(&token::CloseDelim(Delimiter::Bracket))?;
- Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_index(base, index), AttrVec::new()))
+ Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_index(base, index)))
}
/// Assuming we have just parsed `.`, continue parsing into an expression.
@@ -1277,24 +1269,18 @@ impl<'a> Parser<'a> {
if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
// Method call `expr.f()`
- let mut args = self.parse_paren_expr_seq()?;
- args.insert(0, self_arg);
-
+ 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, args, fn_span), AttrVec::new()))
+ Ok(self.mk_expr(span, ExprKind::MethodCall(segment, self_arg, args, fn_span)))
} else {
// Field access `expr.f`
if let Some(args) = segment.args {
- self.struct_span_err(
- args.span(),
- "field expressions cannot have generic arguments",
- )
- .emit();
+ 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), AttrVec::new()))
+ Ok(self.mk_expr(span, ExprKind::Field(self_arg, segment.ident)))
}
}
@@ -1309,10 +1295,6 @@ impl<'a> Parser<'a> {
// Outer attributes are already parsed and will be
// added to the return value after the fact.
- //
- // Therefore, prevent sub-parser from parsing
- // attributes by giving them an empty "already-parsed" list.
- let attrs = AttrVec::new();
// Note: when adding new syntax here, don't forget to adjust `TokenKind::can_begin_expr()`.
let lo = self.token.span;
@@ -1320,80 +1302,81 @@ impl<'a> Parser<'a> {
// This match arm is a special-case of the `_` match arm below and
// could be removed without changing functionality, but it's faster
// to have it here, especially for programs with large constants.
- self.parse_lit_expr(attrs)
+ self.parse_lit_expr()
} else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
- self.parse_tuple_parens_expr(attrs)
+ self.parse_tuple_parens_expr()
} else if self.check(&token::OpenDelim(Delimiter::Brace)) {
- self.parse_block_expr(None, lo, BlockCheckMode::Default, attrs)
+ self.parse_block_expr(None, lo, BlockCheckMode::Default)
} else if self.check(&token::BinOp(token::Or)) || self.check(&token::OrOr) {
- self.parse_closure_expr(attrs).map_err(|mut err| {
+ self.parse_closure_expr().map_err(|mut err| {
// If the input is something like `if a { 1 } else { 2 } | if a { 3 } else { 4 }`
// then suggest parens around the lhs.
if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&lo) {
- self.sess.expr_parentheses_needed(&mut err, *sp);
+ err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
}
err
})
} else if self.check(&token::OpenDelim(Delimiter::Bracket)) {
- self.parse_array_or_repeat_expr(attrs, Delimiter::Bracket)
+ self.parse_array_or_repeat_expr(Delimiter::Bracket)
} else if self.check_path() {
- self.parse_path_start_expr(attrs)
+ self.parse_path_start_expr()
} else if self.check_keyword(kw::Move) || self.check_keyword(kw::Static) {
- self.parse_closure_expr(attrs)
+ self.parse_closure_expr()
} else if self.eat_keyword(kw::If) {
- self.parse_if_expr(attrs)
+ self.parse_if_expr()
} else if self.check_keyword(kw::For) {
if self.choose_generics_over_qpath(1) {
- self.parse_closure_expr(attrs)
+ self.parse_closure_expr()
} else {
assert!(self.eat_keyword(kw::For));
- self.parse_for_expr(None, self.prev_token.span, attrs)
+ self.parse_for_expr(None, self.prev_token.span)
}
} else if self.eat_keyword(kw::While) {
- self.parse_while_expr(None, self.prev_token.span, attrs)
+ self.parse_while_expr(None, self.prev_token.span)
} else if let Some(label) = self.eat_label() {
- self.parse_labeled_expr(label, attrs, true)
+ self.parse_labeled_expr(label, true)
} else if self.eat_keyword(kw::Loop) {
let sp = self.prev_token.span;
- self.parse_loop_expr(None, self.prev_token.span, attrs).map_err(|mut err| {
+ self.parse_loop_expr(None, self.prev_token.span).map_err(|mut err| {
err.span_label(sp, "while parsing this `loop` expression");
err
})
} else if self.eat_keyword(kw::Continue) {
let kind = ExprKind::Continue(self.eat_label());
- Ok(self.mk_expr(lo.to(self.prev_token.span), kind, attrs))
+ Ok(self.mk_expr(lo.to(self.prev_token.span), kind))
} else if self.eat_keyword(kw::Match) {
let match_sp = self.prev_token.span;
- self.parse_match_expr(attrs).map_err(|mut err| {
+ self.parse_match_expr().map_err(|mut err| {
err.span_label(match_sp, "while parsing this `match` expression");
err
})
} else if self.eat_keyword(kw::Unsafe) {
let sp = self.prev_token.span;
- self.parse_block_expr(None, lo, BlockCheckMode::Unsafe(ast::UserProvided), attrs)
- .map_err(|mut err| {
+ self.parse_block_expr(None, lo, BlockCheckMode::Unsafe(ast::UserProvided)).map_err(
+ |mut err| {
err.span_label(sp, "while parsing this `unsafe` expression");
err
- })
+ },
+ )
} else if self.check_inline_const(0) {
self.parse_const_block(lo.to(self.token.span), false)
} else if self.is_do_catch_block() {
- self.recover_do_catch(attrs)
+ self.recover_do_catch()
} else if self.is_try_block() {
self.expect_keyword(kw::Try)?;
- self.parse_try_block(lo, attrs)
+ self.parse_try_block(lo)
} else if self.eat_keyword(kw::Return) {
- self.parse_return_expr(attrs)
+ self.parse_return_expr()
} else if self.eat_keyword(kw::Break) {
- self.parse_break_expr(attrs)
+ self.parse_break_expr()
} else if self.eat_keyword(kw::Yield) {
- self.parse_yield_expr(attrs)
+ self.parse_yield_expr()
} else if self.is_do_yeet() {
- self.parse_yeet_expr(attrs)
+ self.parse_yeet_expr()
} else if self.check_keyword(kw::Let) {
- self.parse_let_expr(attrs)
+ self.parse_let_expr()
} else if self.eat_keyword(kw::Underscore) {
- Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore, attrs))
+ Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore))
} else if !self.unclosed_delims.is_empty() && self.check(&token::Semi) {
// Don't complain about bare semicolons after unclosed braces
// recovery in order to keep the error count down. Fixing the
@@ -1412,32 +1395,32 @@ impl<'a> Parser<'a> {
if self.check_keyword(kw::Async) {
if self.is_async_block() {
// Check for `async {` and `async move {`.
- self.parse_async_block(attrs)
+ self.parse_async_block()
} else {
- self.parse_closure_expr(attrs)
+ self.parse_closure_expr()
}
} else if self.eat_keyword(kw::Await) {
- self.recover_incorrect_await_syntax(lo, self.prev_token.span, attrs)
+ self.recover_incorrect_await_syntax(lo, self.prev_token.span)
} else {
- self.parse_lit_expr(attrs)
+ self.parse_lit_expr()
}
} else {
- self.parse_lit_expr(attrs)
+ self.parse_lit_expr()
}
}
- fn parse_lit_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+ 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), attrs);
+ let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Lit(literal));
self.maybe_recover_from_bad_qpath(expr)
}
None => self.try_macro_suggestion(),
}
}
- fn parse_tuple_parens_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+ fn parse_tuple_parens_expr(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.token.span;
self.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
let (es, trailing_comma) = match self.parse_seq_to_end(
@@ -1457,15 +1440,11 @@ impl<'a> Parser<'a> {
// `(e,)` is a tuple with only one field, `e`.
ExprKind::Tup(es)
};
- let expr = self.mk_expr(lo.to(self.prev_token.span), kind, attrs);
+ let expr = self.mk_expr(lo.to(self.prev_token.span), kind);
self.maybe_recover_from_bad_qpath(expr)
}
- fn parse_array_or_repeat_expr(
- &mut self,
- attrs: AttrVec,
- close_delim: Delimiter,
- ) -> PResult<'a, P<Expr>> {
+ fn parse_array_or_repeat_expr(&mut self, close_delim: Delimiter) -> PResult<'a, P<Expr>> {
let lo = self.token.span;
self.bump(); // `[` or other open delim
@@ -1494,81 +1473,94 @@ impl<'a> Parser<'a> {
ExprKind::Array(vec![first_expr])
}
};
- let expr = self.mk_expr(lo.to(self.prev_token.span), kind, attrs);
+ let expr = self.mk_expr(lo.to(self.prev_token.span), kind);
self.maybe_recover_from_bad_qpath(expr)
}
- fn parse_path_start_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+ fn parse_path_start_expr(&mut self) -> PResult<'a, P<Expr>> {
let (qself, path) = if self.eat_lt() {
let (qself, path) = self.parse_qpath(PathStyle::Expr)?;
(Some(qself), path)
} else {
(None, self.parse_path(PathStyle::Expr)?)
};
- let lo = path.span;
// `!`, as an operator, is prefix, so we know this isn't that.
- let (hi, kind) = if self.eat(&token::Not) {
+ let (span, kind) = if self.eat(&token::Not) {
// MACRO INVOCATION expression
if qself.is_some() {
- self.struct_span_err(path.span, "macros cannot use qualified paths").emit();
+ self.sess.emit_err(MacroInvocationWithQualifiedPath(path.span));
}
- let mac = MacCall {
+ let lo = path.span;
+ let mac = P(MacCall {
path,
args: self.parse_mac_args()?,
prior_type_ascription: self.last_type_ascription,
- };
- (self.prev_token.span, ExprKind::MacCall(mac))
- } else if self.check(&token::OpenDelim(Delimiter::Brace)) {
- if let Some(expr) = self.maybe_parse_struct_expr(qself.as_ref(), &path, &attrs) {
+ });
+ (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) {
if qself.is_some() {
self.sess.gated_spans.gate(sym::more_qualified_paths, path.span);
}
return expr;
- } else {
- (path.span, ExprKind::Path(qself, path))
- }
} else {
(path.span, ExprKind::Path(qself, path))
};
- let expr = self.mk_expr(lo.to(hi), kind, attrs);
+ let expr = self.mk_expr(span, kind);
self.maybe_recover_from_bad_qpath(expr)
}
/// Parse `'label: $expr`. The label is already parsed.
fn parse_labeled_expr(
&mut self,
- label: Label,
- attrs: AttrVec,
+ 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, attrs)
+ self.parse_while_expr(label, lo)
} else if self.eat_keyword(kw::For) {
- self.parse_for_expr(label, lo, attrs)
+ self.parse_for_expr(label, lo)
} else if self.eat_keyword(kw::Loop) {
- self.parse_loop_expr(label, lo, attrs)
+ self.parse_loop_expr(label, lo)
} else if self.check_noexpect(&token::OpenDelim(Delimiter::Brace))
|| self.token.is_whole_block()
{
- self.parse_block_expr(label, lo, BlockCheckMode::Default, attrs)
+ self.parse_block_expr(label, lo, BlockCheckMode::Default)
+ } else if !ate_colon
+ && (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)))
} 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
- let msg = "expected `while`, `for`, `loop` or `{` after a label";
- self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit();
+ self.sess.emit_err(UnexpectedTokenAfterLabel {
+ span: self.token.span,
+ remove_label: None,
+ enclose_in_block: None,
+ });
consume_colon = false;
Ok(self.mk_expr_err(lo))
} else {
- let msg = "expected `while`, `for`, `loop` or `{` after a label";
-
- let mut err = self.struct_span_err(self.token.span, msg);
- err.span_label(self.token.span, msg);
+ let mut err = UnexpectedTokenAfterLabel {
+ span: self.token.span,
+ remove_label: None,
+ enclose_in_block: None,
+ };
// Continue as an expression in an effort to recover on `'label: non_block_expr`.
let expr = self.parse_expr().map(|expr| {
@@ -1595,78 +1587,81 @@ impl<'a> Parser<'a> {
// If there are no breaks that may use this label, suggest removing the label and
// recover to the unmodified expression.
if !found_labeled_breaks {
- let msg = "consider removing the label";
- err.span_suggestion_verbose(
- lo.until(span),
- msg,
- "",
- Applicability::MachineApplicable,
- );
+ err.remove_label = Some(lo.until(span));
return expr;
}
- let sugg_msg = "consider enclosing expression in a block";
- let suggestions = vec![
- (span.shrink_to_lo(), "{ ".to_owned()),
- (span.shrink_to_hi(), " }".to_owned()),
- ];
-
- err.multipart_suggestion_verbose(
- sugg_msg,
- suggestions,
- Applicability::MachineApplicable,
- );
+ err.enclose_in_block = Some(UnexpectedTokenAfterLabelSugg {
+ left: span.shrink_to_lo(),
+ right: span.shrink_to_hi(),
+ });
- // Replace `'label: non_block_expr` with `'label: {non_block_expr}` in order to supress future errors about `break 'label`.
+ // Replace `'label: non_block_expr` with `'label: {non_block_expr}` in order to suppress future errors about `break 'label`.
let stmt = self.mk_stmt(span, StmtKind::Expr(expr));
let blk = self.mk_block(vec![stmt], BlockCheckMode::Default, span);
- self.mk_expr(span, ExprKind::Block(blk, label), ThinVec::new())
+ self.mk_expr(span, ExprKind::Block(blk, label))
});
- err.emit();
+ self.sess.emit_err(err);
expr
}?;
if !ate_colon && consume_colon {
- self.error_labeled_expr_must_be_followed_by_colon(lo, expr.span);
+ self.sess.emit_err(RequireColonAfterLabeledExpression {
+ span: expr.span,
+ label: lo,
+ label_end: lo.shrink_to_hi(),
+ });
}
Ok(expr)
}
- fn error_labeled_expr_must_be_followed_by_colon(&self, lo: Span, span: Span) {
- self.struct_span_err(span, "labeled expression must be followed by `:`")
- .span_label(lo, "the label")
- .span_suggestion_short(
- lo.shrink_to_hi(),
- "add `:` after the label",
- ": ",
- Applicability::MachineApplicable,
+ /// Emit an error when a char is parsed as a lifetime because of a missing quote
+ pub(super) fn recover_unclosed_char(
+ &mut self,
+ lifetime: Ident,
+ err: impl FnOnce(&mut 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,
)
- .note("labels are used before loops and blocks, allowing e.g., `break 'label` to them")
.emit();
+ } else {
+ err(self)
+ .span_suggestion_verbose(
+ lifetime.span.shrink_to_hi(),
+ "add `'` to close the char literal",
+ "'",
+ Applicability::MaybeIncorrect,
+ )
+ .emit();
+ }
+ ast::Lit {
+ token_lit: token::Lit::new(token::LitKind::Char, lifetime.name, None),
+ kind: ast::LitKind::Char(lifetime.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, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+ fn recover_do_catch(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.token.span;
self.bump(); // `do`
self.bump(); // `catch`
- let span_dc = lo.to(self.prev_token.span);
- self.struct_span_err(span_dc, "found removed `do catch` syntax")
- .span_suggestion(
- span_dc,
- "replace with the new syntax",
- "try",
- Applicability::MachineApplicable,
- )
- .note("following RFC #2388, the new non-placeholder syntax is `try`")
- .emit();
+ let span = lo.to(self.prev_token.span);
+ self.sess.emit_err(DoCatchSyntaxRemoved { span });
- self.parse_try_block(lo, attrs)
+ self.parse_try_block(lo)
}
/// Parse an expression if the token can begin one.
@@ -1675,15 +1670,15 @@ impl<'a> Parser<'a> {
}
/// Parse `"return" expr?`.
- fn parse_return_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+ fn parse_return_expr(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.prev_token.span;
let kind = ExprKind::Ret(self.parse_expr_opt()?);
- let expr = self.mk_expr(lo.to(self.prev_token.span), kind, attrs);
+ let expr = self.mk_expr(lo.to(self.prev_token.span), kind);
self.maybe_recover_from_bad_qpath(expr)
}
/// Parse `"do" "yeet" expr?`.
- fn parse_yeet_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+ fn parse_yeet_expr(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.token.span;
self.bump(); // `do`
@@ -1693,7 +1688,7 @@ impl<'a> Parser<'a> {
let span = lo.to(self.prev_token.span);
self.sess.gated_spans.gate(sym::yeet_expr, span);
- let expr = self.mk_expr(span, kind, attrs);
+ let expr = self.mk_expr(span, kind);
self.maybe_recover_from_bad_qpath(expr)
}
@@ -1705,26 +1700,20 @@ impl<'a> Parser<'a> {
/// `break 'lbl: loop {}`); a labeled break with an unlabeled loop as its value
/// expression only gets a warning for compatibility reasons; and a labeled break
/// with a labeled loop does not even get a warning because there is no ambiguity.
- fn parse_break_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+ fn parse_break_expr(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.prev_token.span;
let mut label = self.eat_label();
let kind = if label.is_some() && self.token == token::Colon {
// The value expression can be a labeled loop, see issue #86948, e.g.:
// `loop { break 'label: loop { break 'label 42; }; }`
- let lexpr = self.parse_labeled_expr(label.take().unwrap(), AttrVec::new(), true)?;
- self.struct_span_err(
- lexpr.span,
- "parentheses are required around this expression to avoid confusion with a labeled break expression",
- )
- .multipart_suggestion(
- "wrap the expression in parentheses",
- vec![
- (lexpr.span.shrink_to_lo(), "(".to_string()),
- (lexpr.span.shrink_to_hi(), ")".to_string()),
- ],
- Applicability::MachineApplicable,
- )
- .emit();
+ let lexpr = self.parse_labeled_expr(label.take().unwrap(), true)?;
+ self.sess.emit_err(LabeledLoopInBreak {
+ span: lexpr.span,
+ sub: WrapExpressionInParentheses {
+ left: lexpr.span.shrink_to_lo(),
+ right: lexpr.span.shrink_to_hi(),
+ },
+ });
Some(lexpr)
} else if self.token != token::OpenDelim(Delimiter::Brace)
|| !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
@@ -1753,17 +1742,17 @@ impl<'a> Parser<'a> {
} else {
None
};
- let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Break(label, kind), attrs);
+ let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Break(label, kind));
self.maybe_recover_from_bad_qpath(expr)
}
/// Parse `"yield" expr?`.
- fn parse_yield_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+ fn parse_yield_expr(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.prev_token.span;
let kind = ExprKind::Yield(self.parse_expr_opt()?);
let span = lo.to(self.prev_token.span);
self.sess.gated_spans.gate(sym::generators, span);
- let expr = self.mk_expr(span, kind, attrs);
+ let expr = self.mk_expr(span, kind);
self.maybe_recover_from_bad_qpath(expr)
}
@@ -1775,8 +1764,8 @@ impl<'a> Parser<'a> {
Some(lit) => match lit.kind {
ast::LitKind::Str(symbol_unescaped, style) => Ok(ast::StrLit {
style,
- symbol: lit.token.symbol,
- suffix: lit.token.suffix,
+ symbol: lit.token_lit.symbol,
+ suffix: lit.token_lit.suffix,
span: lit.span,
symbol_unescaped,
}),
@@ -1787,7 +1776,7 @@ impl<'a> Parser<'a> {
}
pub(super) fn parse_lit(&mut self) -> PResult<'a, Lit> {
- self.parse_opt_lit().ok_or_else(|| {
+ self.parse_opt_lit().ok_or(()).or_else(|()| {
if let token::Interpolated(inner) = &self.token.kind {
let expr = match inner.as_ref() {
token::NtExpr(expr) => Some(expr),
@@ -1796,16 +1785,25 @@ impl<'a> Parser<'a> {
};
if let Some(expr) = expr {
if matches!(expr.kind, ExprKind::Err) {
- let mut err = self
- .diagnostic()
- .struct_span_err(self.token.span, "invalid interpolated expression");
+ let mut err = InvalidInterpolatedExpression { span: self.token.span }
+ .into_diagnostic(&self.sess.span_diagnostic);
err.downgrade_to_delayed_bug();
- return err;
+ 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_: &mut 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))
+ }
})
}
@@ -1830,7 +1828,10 @@ impl<'a> Parser<'a> {
});
if let Some(token) = &recovered {
self.bump();
- self.error_float_lits_must_have_int_part(&token);
+ self.sess.emit_err(FloatLiteralRequiresIntegerPart {
+ span: token.span,
+ correct: pprust::token_to_string(token).into_owned(),
+ });
}
}
@@ -1853,22 +1854,11 @@ impl<'a> Parser<'a> {
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_lit_token(lit, span).unwrap_or_else(|_| unreachable!()))
+ Some(Lit::from_token_lit(lit, span).unwrap_or_else(|_| unreachable!()))
}
}
}
- fn error_float_lits_must_have_int_part(&self, token: &Token) {
- self.struct_span_err(token.span, "float literals must have an integer part")
- .span_suggestion(
- token.span,
- "must have an integer part",
- pprust::token_to_string(token),
- Applicability::MachineApplicable,
- )
- .emit();
- }
-
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 {
@@ -1897,39 +1887,24 @@ impl<'a> Parser<'a> {
// by lexer, so here we don't report it the second time.
LitError::LexerError => {}
LitError::InvalidSuffix => {
- self.expect_no_suffix(
- span,
- &format!("{} {} literal", kind.article(), kind.descr()),
- suffix,
- );
+ 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.
- let msg = format!("invalid width `{}` for integer literal", &suf[1..]);
- self.struct_span_err(span, &msg)
- .help("valid widths are 8, 16, 32, 64 and 128")
- .emit();
+ self.sess.emit_err(InvalidIntLiteralWidth { span, width: suf[1..].into() });
} else if let Some(fixed) = fix_base_capitalisation(suf) {
- let msg = "invalid base prefix for number literal";
-
- self.struct_span_err(span, msg)
- .note("base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase")
- .span_suggestion(
- span,
- "try making the prefix lowercase",
- fixed,
- Applicability::MaybeIncorrect,
- )
- .emit();
+ self.sess.emit_err(InvalidNumLiteralBasePrefix { span, fixed });
} else {
- let msg = format!("invalid suffix `{suf}` for number literal");
- self.struct_span_err(span, &msg)
- .span_label(span, format!("invalid suffix `{suf}`"))
- .help("the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)")
- .emit();
+ self.sess.emit_err(InvalidNumLiteralSuffix { span, suffix: suf.to_string() });
}
}
LitError::InvalidFloatSuffix => {
@@ -1937,65 +1912,37 @@ impl<'a> Parser<'a> {
let suf = suf.as_str();
if looks_like_width_suffix(&['f'], suf) {
// If it looks like a width, try to be helpful.
- let msg = format!("invalid width `{}` for float literal", &suf[1..]);
- self.struct_span_err(span, &msg).help("valid widths are 32 and 64").emit();
+ self.sess
+ .emit_err(InvalidFloatLiteralWidth { span, width: suf[1..].to_string() });
} else {
- let msg = format!("invalid suffix `{suf}` for float literal");
- self.struct_span_err(span, &msg)
- .span_label(span, format!("invalid suffix `{suf}`"))
- .help("valid suffixes are `f32` and `f64`")
- .emit();
+ self.sess.emit_err(InvalidFloatLiteralSuffix { span, suffix: suf.to_string() });
}
}
LitError::NonDecimalFloat(base) => {
- let descr = match base {
- 16 => "hexadecimal",
- 8 => "octal",
- 2 => "binary",
+ match base {
+ 16 => self.sess.emit_err(HexadecimalFloatLiteralNotSupported { span }),
+ 8 => self.sess.emit_err(OctalFloatLiteralNotSupported { span }),
+ 2 => self.sess.emit_err(BinaryFloatLiteralNotSupported { span }),
_ => unreachable!(),
};
- self.struct_span_err(span, &format!("{descr} float literal is not supported"))
- .span_label(span, "not supported")
- .emit();
}
LitError::IntTooLarge => {
- self.struct_span_err(span, "integer literal is too large").emit();
+ self.sess.emit_err(IntLiteralTooLarge { span });
}
}
}
- pub(super) fn expect_no_suffix(&self, sp: Span, kind: &str, suffix: Option<Symbol>) {
- if let Some(suf) = suffix {
- let mut err = if kind == "a tuple index"
- && [sym::i32, sym::u32, sym::isize, sym::usize].contains(&suf)
- {
- // #59553: warn instead of reject out of hand to allow the fix to percolate
- // through the ecosystem when people fix their macros
- let mut err = self
- .sess
- .span_diagnostic
- .struct_span_warn(sp, &format!("suffixes on {kind} are invalid"));
- err.note(&format!(
- "`{}` is *temporarily* accepted on tuple index fields as it was \
- incorrectly accepted on stable for a few releases",
- suf,
- ));
- err.help(
- "on proc macros, you'll want to use `syn::Index::from` or \
- `proc_macro::Literal::*_unsuffixed` for code that will desugar \
- to tuple field access",
- );
- err.note(
- "see issue #60210 <https://github.com/rust-lang/rust/issues/60210> \
- for more information",
- );
- err
- } else {
- self.struct_span_err(sp, &format!("suffixes on {kind} are invalid"))
- .forget_guarantee()
- };
- err.span_label(sp, format!("invalid suffix `{suf}`"));
- err.emit();
+ pub(super) fn expect_no_tuple_index_suffix(&self, span: Span, suffix: Symbol) {
+ if [sym::i32, sym::u32, sym::isize, sym::usize].contains(&suffix) {
+ // #59553: warn instead of reject out of hand to allow the fix to percolate
+ // through the ecosystem when people fix their macros
+ self.sess.emit_warning(InvalidLiteralSuffixOnTupleIndex {
+ span,
+ suffix,
+ exception: Some(()),
+ });
+ } else {
+ self.sess.emit_err(InvalidLiteralSuffixOnTupleIndex { span, suffix, exception: None });
}
}
@@ -2007,14 +1954,10 @@ 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), AttrVec::new());
+ let expr = self.mk_expr(lit.span, ExprKind::Lit(lit));
if minus_present {
- Ok(self.mk_expr(
- lo.to(self.prev_token.span),
- self.mk_unary(UnOp::Neg, expr),
- AttrVec::new(),
- ))
+ Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_unary(UnOp::Neg, expr)))
} else {
Ok(expr)
}
@@ -2029,22 +1972,17 @@ impl<'a> Parser<'a> {
/// Emits a suggestion if it looks like the user meant an array but
/// accidentally used braces, causing the code to be interpreted as a block
/// expression.
- fn maybe_suggest_brackets_instead_of_braces(
- &mut self,
- lo: Span,
- attrs: AttrVec,
- ) -> Option<P<Expr>> {
+ fn maybe_suggest_brackets_instead_of_braces(&mut self, lo: Span) -> Option<P<Expr>> {
let mut snapshot = self.create_snapshot_for_diagnostic();
- match snapshot.parse_array_or_repeat_expr(attrs, Delimiter::Brace) {
+ match snapshot.parse_array_or_repeat_expr(Delimiter::Brace) {
Ok(arr) => {
- let hi = snapshot.prev_token.span;
- self.struct_span_err(arr.span, "this is a block expression, not an array")
- .multipart_suggestion(
- "to make an array, use square brackets instead of curly braces",
- vec![(lo, "[".to_owned()), (hi, "]".to_owned())],
- Applicability::MaybeIncorrect,
- )
- .emit();
+ self.sess.emit_err(ArrayBracketsInsteadOfSpaces {
+ span: arr.span,
+ sub: ArrayBracketsInsteadOfSpacesSugg {
+ left: lo,
+ right: snapshot.prev_token.span,
+ },
+ });
self.restore_snapshot(snapshot);
Some(self.mk_expr_err(arr.span))
@@ -2056,43 +1994,76 @@ impl<'a> Parser<'a> {
}
}
+ fn suggest_missing_semicolon_before_array(
+ &self,
+ prev_span: Span,
+ open_delim_span: Span,
+ ) -> PResult<'a, ()> {
+ if self.token.kind == token::Comma {
+ if !self.sess.source_map().is_multiline(prev_span.until(self.token.span)) {
+ return Ok(());
+ }
+ let mut snapshot = self.create_snapshot_for_diagnostic();
+ snapshot.bump();
+ match snapshot.parse_seq_to_before_end(
+ &token::CloseDelim(Delimiter::Bracket),
+ SeqSep::trailing_allowed(token::Comma),
+ |p| p.parse_expr(),
+ ) {
+ Ok(_)
+ // When the close delim is `)`, `token.kind` is expected to be `token::CloseDelim(Delimiter::Parenthesis)`,
+ // but the actual `token.kind` is `token::CloseDelim(Delimiter::Bracket)`.
+ // This is because the `token.kind` of the close delim is treated as the same as
+ // that of the open delim in `TokenTreesReader::parse_token_tree`, even if the delimiters of them are different.
+ // Therefore, `token.kind` should not be compared here.
+ if snapshot
+ .span_to_snippet(snapshot.token.span)
+ .map_or(false, |snippet| snippet == "]") =>
+ {
+ return Err(MissingSemicolonBeforeArray {
+ open_delim: open_delim_span,
+ semicolon: prev_span.shrink_to_hi(),
+ }.into_diagnostic(&self.sess.span_diagnostic));
+ }
+ Ok(_) => (),
+ Err(err) => err.cancel(),
+ }
+ }
+ Ok(())
+ }
+
/// Parses a block or unsafe block.
pub(super) fn parse_block_expr(
&mut self,
opt_label: Option<Label>,
lo: Span,
blk_mode: BlockCheckMode,
- mut attrs: AttrVec,
) -> PResult<'a, P<Expr>> {
if self.is_array_like_block() {
- if let Some(arr) = self.maybe_suggest_brackets_instead_of_braces(lo, attrs.clone()) {
+ if let Some(arr) = self.maybe_suggest_brackets_instead_of_braces(lo) {
return Ok(arr);
}
}
- if let Some(label) = opt_label {
- self.sess.gated_spans.gate(sym::label_break_value, label.ident.span);
- }
-
if self.token.is_whole_block() {
- self.struct_span_err(self.token.span, "cannot use a `block` macro fragment here")
- .span_label(lo.to(self.token.span), "the `block` fragment is within this context")
- .emit();
+ self.sess.emit_err(InvalidBlockMacroSegment {
+ span: self.token.span,
+ context: lo.to(self.token.span),
+ });
}
- let (inner_attrs, blk) = self.parse_block_common(lo, blk_mode)?;
- attrs.extend(inner_attrs);
- Ok(self.mk_expr(blk.span, ExprKind::Block(blk, opt_label), attrs))
+ let (attrs, blk) = self.parse_block_common(lo, blk_mode)?;
+ Ok(self.mk_expr_with_attrs(blk.span, ExprKind::Block(blk, opt_label), attrs))
}
/// Parse a block which takes no attributes and has no label
fn parse_simple_block(&mut self) -> PResult<'a, P<Expr>> {
let blk = self.parse_block()?;
- Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None), AttrVec::new()))
+ Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None)))
}
/// Parses a closure expression (e.g., `move |args| expr`).
- fn parse_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+ fn parse_closure_expr(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.token.span;
let binder = if self.check_keyword(kw::For) {
@@ -2127,7 +2098,7 @@ impl<'a> Parser<'a> {
_ => {
// If an explicit return type is given, require a block to appear (RFC 968).
let body_lo = self.token.span;
- self.parse_block_expr(None, body_lo, BlockCheckMode::Default, AttrVec::new())?
+ self.parse_block_expr(None, body_lo, BlockCheckMode::Default)?
}
};
@@ -2138,6 +2109,12 @@ impl<'a> Parser<'a> {
if self.token.kind == TokenKind::Semi
&& matches!(self.token_cursor.frame.delim_sp, Some((Delimiter::Parenthesis, _)))
+ // HACK: This is needed so we can detect whether we're inside a macro,
+ // where regular assumptions about what tokens can follow other tokens
+ // don't necessarily apply.
+ && self.may_recover()
+ // FIXME(Nilstrieb): Remove this check once `may_recover` actually stops recovery
+ && self.subparser_name.is_none()
{
// It is likely that the closure body is a block but where the
// braces have been removed. We will recover and eat the next
@@ -2158,7 +2135,6 @@ impl<'a> Parser<'a> {
body,
lo.to(decl_hi),
),
- attrs,
);
// Disable recovery for closure body
@@ -2175,7 +2151,8 @@ impl<'a> Parser<'a> {
// Check for `move async` and recover
if self.check_keyword(kw::Async) {
let move_async_span = self.token.span.with_lo(self.prev_token.span.data().lo);
- Err(self.incorrect_move_async_order_found(move_async_span))
+ Err(AsyncMoveOrderIncorrect { span: move_async_span }
+ .into_diagnostic(&self.sess.span_diagnostic))
} else {
Ok(CaptureBy::Value)
}
@@ -2221,10 +2198,10 @@ impl<'a> Parser<'a> {
Ok((
Param {
- attrs: attrs.into(),
+ attrs,
ty,
pat,
- span: lo.to(this.token.span),
+ span: lo.to(this.prev_token.span),
id: DUMMY_NODE_ID,
is_placeholder: false,
},
@@ -2234,19 +2211,13 @@ impl<'a> Parser<'a> {
}
/// Parses an `if` expression (`if` token already eaten).
- fn parse_if_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+ fn parse_if_expr(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.prev_token.span;
let cond = self.parse_cond_expr()?;
-
- self.parse_if_after_cond(attrs, lo, cond)
+ self.parse_if_after_cond(lo, cond)
}
- fn parse_if_after_cond(
- &mut self,
- attrs: AttrVec,
- lo: Span,
- mut cond: P<Expr>,
- ) -> PResult<'a, P<Expr>> {
+ fn parse_if_after_cond(&mut self, lo: Span, mut cond: P<Expr>) -> PResult<'a, P<Expr>> {
let cond_span = cond.span;
// Tries to interpret `cond` as either a missing expression if it's a block,
// or as an unfinished expression if it's a binop and the RHS is a block.
@@ -2255,11 +2226,19 @@ impl<'a> Parser<'a> {
let block = match &mut cond.kind {
ExprKind::Binary(Spanned { span: binop_span, .. }, _, right)
if let ExprKind::Block(_, None) = right.kind => {
- this.error_missing_if_then_block(lo, cond_span.shrink_to_lo().to(*binop_span), true).emit();
+ self.sess.emit_err(IfExpressionMissingThenBlock {
+ if_span: lo,
+ sub: IfExpressionMissingThenBlockSub::UnfinishedCondition(
+ cond_span.shrink_to_lo().to(*binop_span)
+ ),
+ });
std::mem::replace(right, this.mk_expr_err(binop_span.shrink_to_hi()))
},
ExprKind::Block(_, None) => {
- this.error_missing_if_cond(lo, cond_span).emit();
+ self.sess.emit_err(IfExpressionMissingCondition {
+ 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()))
}
_ => {
@@ -2277,7 +2256,10 @@ impl<'a> Parser<'a> {
if let Some(block) = recover_block_from_condition(self) {
block
} else {
- self.error_missing_if_then_block(lo, cond_span, false).emit();
+ self.sess.emit_err(IfExpressionMissingThenBlock {
+ if_span: lo,
+ sub: IfExpressionMissingThenBlockSub::AddThenBlock(cond_span.shrink_to_hi()),
+ });
self.mk_block_err(cond_span.shrink_to_hi())
}
} else {
@@ -2302,45 +2284,13 @@ impl<'a> Parser<'a> {
block
};
let els = if self.eat_keyword(kw::Else) { Some(self.parse_else_expr()?) } else { None };
- Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::If(cond, thn, els), attrs))
- }
-
- fn error_missing_if_then_block(
- &self,
- if_span: Span,
- cond_span: Span,
- is_unfinished: bool,
- ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
- let mut err = self.struct_span_err(
- if_span,
- "this `if` expression is missing a block after the condition",
- );
- if is_unfinished {
- err.span_help(cond_span, "this binary operation is possibly unfinished");
- } else {
- err.span_help(cond_span.shrink_to_hi(), "add a block here");
- }
- err
- }
-
- fn error_missing_if_cond(
- &self,
- lo: Span,
- span: Span,
- ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
- let next_span = self.sess.source_map().next_point(lo);
- let mut err = self.struct_span_err(next_span, "missing condition for `if` expression");
- err.span_label(next_span, "expected condition here");
- err.span_label(
- self.sess.source_map().start_point(span),
- "if this block is the condition of the `if` expression, then it must be followed by another block"
- );
- err
+ Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::If(cond, thn, els)))
}
/// Parses the condition of a `if` or `while` expression.
fn parse_cond_expr(&mut self) -> PResult<'a, P<Expr>> {
- let cond = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, None)?;
+ let cond =
+ self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, None)?;
if let ExprKind::Let(..) = cond.kind {
// Remove the last feature gating of a `let` expression since it's stable.
@@ -2351,7 +2301,7 @@ impl<'a> Parser<'a> {
}
/// Parses a `let $pat = $expr` pseudo-expression.
- fn parse_let_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+ fn parse_let_expr(&mut self) -> PResult<'a, P<Expr>> {
// This is a *approximate* heuristic that detects if `let` chains are
// being parsed in the right position. It's approximate because it
// doesn't deny all invalid `let` expressions, just completely wrong usages.
@@ -2360,8 +2310,7 @@ impl<'a> Parser<'a> {
TokenKind::AndAnd | TokenKind::Ident(kw::If, _) | TokenKind::Ident(kw::While, _)
);
if !self.restrictions.contains(Restrictions::ALLOW_LET) || not_in_chain {
- self.struct_span_err(self.token.span, "expected expression, found `let` statement")
- .emit();
+ self.sess.emit_err(ExpectedExpressionFoundLet { span: self.token.span });
}
self.bump(); // Eat `let` token
@@ -2378,7 +2327,7 @@ impl<'a> Parser<'a> {
})?;
let span = lo.to(expr.span);
self.sess.gated_spans.gate(sym::let_chains, span);
- Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span), attrs))
+ Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span)))
}
/// Parses an `else { ... }` expression (`else` token already eaten).
@@ -2386,7 +2335,7 @@ impl<'a> Parser<'a> {
let else_span = self.prev_token.span; // `else`
let attrs = self.parse_outer_attributes()?.take_for_recovery(); // For recovery.
let expr = if self.eat_keyword(kw::If) {
- self.parse_if_expr(AttrVec::new())?
+ self.parse_if_expr()?
} else if self.check(&TokenKind::OpenDelim(Delimiter::Brace)) {
self.parse_simple_block()?
} else {
@@ -2400,16 +2349,13 @@ impl<'a> Parser<'a> {
if self.check(&TokenKind::OpenDelim(Delimiter::Brace))
&& classify::expr_requires_semi_to_be_stmt(&cond) =>
{
- self.struct_span_err(first_tok_span, format!("expected `{{`, found {first_tok}"))
- .span_label(else_span, "expected an `if` or a block after this `else`")
- .span_suggestion(
- cond.span.shrink_to_lo(),
- "add an `if` if this is the condition of a chained `else if` statement",
- "if ",
- Applicability::MaybeIncorrect,
- )
- .emit();
- self.parse_if_after_cond(AttrVec::new(), cond.span.shrink_to_lo(), cond)?
+ self.sess.emit_err(ExpectedElseBlock {
+ first_tok_span,
+ first_tok,
+ else_span,
+ condition_start: cond.span.shrink_to_lo(),
+ });
+ self.parse_if_after_cond(cond.span.shrink_to_lo(), cond)?
}
Err(e) => {
e.cancel();
@@ -2433,25 +2379,22 @@ impl<'a> Parser<'a> {
branch_span: Span,
attrs: &[ast::Attribute],
) {
- let (span, last) = match attrs {
+ let (attributes, last) = match attrs {
[] => return,
[x0 @ xn] | [x0, .., xn] => (x0.span.to(xn.span), xn.span),
};
let ctx = if is_ctx_else { "else" } else { "if" };
- self.struct_span_err(last, "outer attributes are not allowed on `if` and `else` branches")
- .span_label(branch_span, "the attributes are attached to this branch")
- .span_label(ctx_span, format!("the branch belongs to this `{ctx}`"))
- .span_suggestion(span, "remove the attributes", "", Applicability::MachineApplicable)
- .emit();
+ self.sess.emit_err(OuterAttributeNotAllowedOnIfElse {
+ last,
+ branch_span,
+ ctx_span,
+ ctx: ctx.to_string(),
+ attributes,
+ });
}
/// 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,
- mut attrs: AttrVec,
- ) -> PResult<'a, P<Expr>> {
+ fn parse_for_expr(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> {
// Record whether we are about to parse `for (`.
// This is used below for recovery in case of `for ( $stuff ) $block`
// in which case we will suggest `for $stuff $block`.
@@ -2474,63 +2417,51 @@ impl<'a> Parser<'a> {
let pat = self.recover_parens_around_for_head(pat, begin_paren);
- let (iattrs, loop_block) = self.parse_inner_attrs_and_block()?;
- attrs.extend(iattrs);
+ let (attrs, loop_block) = self.parse_inner_attrs_and_block()?;
let kind = ExprKind::ForLoop(pat, expr, loop_block, opt_label);
- Ok(self.mk_expr(lo.to(self.prev_token.span), kind, attrs))
+ Ok(self.mk_expr_with_attrs(lo.to(self.prev_token.span), kind, attrs))
}
fn error_missing_in_for_loop(&mut self) {
- let (span, msg, sugg) = if self.token.is_ident_named(sym::of) {
+ let (span, sub): (_, fn(_) -> _) = if self.token.is_ident_named(sym::of) {
// Possibly using JS syntax (#75311).
let span = self.token.span;
self.bump();
- (span, "try using `in` here instead", "in")
+ (span, MissingInInForLoopSub::InNotOf)
} else {
- (self.prev_token.span.between(self.token.span), "try adding `in` here", " in ")
+ (self.prev_token.span.between(self.token.span), MissingInInForLoopSub::AddIn)
};
- self.struct_span_err(span, "missing `in` in `for` loop")
- .span_suggestion_short(
- span,
- msg,
- sugg,
- // Has been misleading, at least in the past (closed Issue #48492).
- Applicability::MaybeIncorrect,
- )
- .emit();
+
+ self.sess.emit_err(MissingInInForLoop { span, sub: sub(span) });
}
/// Parses a `while` or `while let` expression (`while` token already eaten).
- fn parse_while_expr(
- &mut self,
- opt_label: Option<Label>,
- lo: Span,
- mut attrs: AttrVec,
- ) -> PResult<'a, P<Expr>> {
+ fn parse_while_expr(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> {
let cond = self.parse_cond_expr().map_err(|mut err| {
err.span_label(lo, "while parsing the condition of this `while` expression");
err
})?;
- let (iattrs, body) = self.parse_inner_attrs_and_block().map_err(|mut err| {
+ let (attrs, body) = self.parse_inner_attrs_and_block().map_err(|mut err| {
err.span_label(lo, "while parsing the body of this `while` expression");
err.span_label(cond.span, "this `while` condition successfully parsed");
err
})?;
- attrs.extend(iattrs);
- Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::While(cond, body, opt_label), attrs))
+ Ok(self.mk_expr_with_attrs(
+ lo.to(self.prev_token.span),
+ ExprKind::While(cond, body, opt_label),
+ attrs,
+ ))
}
/// Parses `loop { ... }` (`loop` token already eaten).
- fn parse_loop_expr(
- &mut self,
- opt_label: Option<Label>,
- lo: Span,
- mut attrs: AttrVec,
- ) -> PResult<'a, P<Expr>> {
- let (iattrs, body) = self.parse_inner_attrs_and_block()?;
- attrs.extend(iattrs);
- Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::Loop(body, opt_label), attrs))
+ fn parse_loop_expr(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> {
+ let (attrs, body) = self.parse_inner_attrs_and_block()?;
+ Ok(self.mk_expr_with_attrs(
+ lo.to(self.prev_token.span),
+ ExprKind::Loop(body, opt_label),
+ attrs,
+ ))
}
pub(crate) fn eat_label(&mut self) -> Option<Label> {
@@ -2541,7 +2472,7 @@ impl<'a> Parser<'a> {
}
/// Parses a `match ... { ... }` expression (`match` token already eaten).
- fn parse_match_expr(&mut self, mut attrs: AttrVec) -> PResult<'a, P<Expr>> {
+ fn parse_match_expr(&mut self) -> PResult<'a, P<Expr>> {
let match_span = self.prev_token.span;
let lo = self.prev_token.span;
let scrutinee = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
@@ -2561,7 +2492,7 @@ impl<'a> Parser<'a> {
return Err(e);
}
}
- attrs.extend(self.parse_inner_attributes()?);
+ let attrs = self.parse_inner_attributes()?;
let mut arms: Vec<Arm> = Vec::new();
while self.token != token::CloseDelim(Delimiter::Brace) {
@@ -2575,13 +2506,17 @@ impl<'a> Parser<'a> {
if self.token == token::CloseDelim(Delimiter::Brace) {
self.bump();
}
- return Ok(self.mk_expr(span, ExprKind::Match(scrutinee, arms), attrs));
+ return Ok(self.mk_expr_with_attrs(
+ span,
+ ExprKind::Match(scrutinee, arms),
+ attrs,
+ ));
}
}
}
let hi = self.token.span;
self.bump();
- Ok(self.mk_expr(lo.to(hi), ExprKind::Match(scrutinee, arms), attrs))
+ Ok(self.mk_expr_with_attrs(lo.to(hi), ExprKind::Match(scrutinee, arms), attrs))
}
/// Attempt to recover from match arm body with statements and no surrounding braces.
@@ -2598,39 +2533,22 @@ impl<'a> Parser<'a> {
self.bump(); // `;`
let mut stmts =
vec![self.mk_stmt(first_expr.span, ast::StmtKind::Expr(first_expr.clone()))];
- let err = |this: &mut Parser<'_>, stmts: Vec<ast::Stmt>| {
+ let err = |this: &Parser<'_>, stmts: Vec<ast::Stmt>| {
let span = stmts[0].span.to(stmts[stmts.len() - 1].span);
- let mut err = this.struct_span_err(span, "`match` arm body without braces");
- let (these, s, are) =
- if stmts.len() > 1 { ("these", "s", "are") } else { ("this", "", "is") };
- err.span_label(
- span,
- &format!(
- "{these} statement{s} {are} not surrounded by a body",
- these = these,
- s = s,
- are = are
- ),
- );
- err.span_label(arrow_span, "while parsing the `match` arm starting here");
- if stmts.len() > 1 {
- err.multipart_suggestion(
- &format!("surround the statement{s} with a body"),
- vec![
- (span.shrink_to_lo(), "{ ".to_string()),
- (span.shrink_to_hi(), " }".to_string()),
- ],
- Applicability::MachineApplicable,
- );
- } else {
- err.span_suggestion(
- semi_sp,
- "use a comma to end a `match` arm expression",
- ",",
- Applicability::MachineApplicable,
- );
- }
- err.emit();
+
+ this.sess.emit_err(MatchArmBodyWithoutBraces {
+ statements: span,
+ arrow: arrow_span,
+ num_statements: stmts.len(),
+ sub: if stmts.len() > 1 {
+ MatchArmBodyWithoutBracesSugg::AddBraces {
+ left: span.shrink_to_lo(),
+ right: span.shrink_to_hi(),
+ }
+ } else {
+ MatchArmBodyWithoutBracesSugg::UseComma { semicolon: semi_sp }
+ },
+ });
this.mk_expr_err(span)
};
// We might have either a `,` -> `;` typo, or a block without braces. We need
@@ -2681,7 +2599,7 @@ impl<'a> Parser<'a> {
}
pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> {
- // Used to check the `let_chains` and `if_let_guard` features mostly by scaning
+ // Used to check the `let_chains` and `if_let_guard` features mostly by scanning
// `&&` tokens.
fn check_let_expr(expr: &Expr) -> (bool, bool) {
match expr.kind {
@@ -2756,7 +2674,7 @@ impl<'a> Parser<'a> {
let span = body.span;
return Ok((
ast::Arm {
- attrs: attrs.into(),
+ attrs,
pat,
guard,
body,
@@ -2811,17 +2729,9 @@ impl<'a> Parser<'a> {
.is_ok();
if pattern_follows && snapshot.check(&TokenKind::FatArrow) {
err.cancel();
- this.struct_span_err(
- hi.shrink_to_hi(),
- "expected `,` following `match` arm",
- )
- .span_suggestion(
- hi.shrink_to_hi(),
- "missing a comma here to end this `match` arm",
- ",",
- Applicability::MachineApplicable,
- )
- .emit();
+ this.sess.emit_err(MissingCommaAfterMatchArm {
+ span: hi.shrink_to_hi(),
+ });
return Ok(true);
}
}
@@ -2834,7 +2744,7 @@ impl<'a> Parser<'a> {
Ok((
ast::Arm {
- attrs: attrs.into(),
+ attrs,
pat,
guard,
body: expr,
@@ -2848,21 +2758,15 @@ impl<'a> Parser<'a> {
}
/// Parses a `try {...}` expression (`try` token already eaten).
- fn parse_try_block(&mut self, span_lo: Span, mut attrs: AttrVec) -> PResult<'a, P<Expr>> {
- let (iattrs, body) = self.parse_inner_attrs_and_block()?;
- attrs.extend(iattrs);
+ fn parse_try_block(&mut self, span_lo: Span) -> PResult<'a, P<Expr>> {
+ let (attrs, body) = self.parse_inner_attrs_and_block()?;
if self.eat_keyword(kw::Catch) {
- let mut error = self.struct_span_err(
- self.prev_token.span,
- "keyword `catch` cannot follow a `try` block",
- );
- error.help("try using `match` on the result of the `try` block instead");
- error.emit();
- Err(error)
+ Err(CatchAfterTry { span: self.prev_token.span }
+ .into_diagnostic(&self.sess.span_diagnostic))
} else {
let span = span_lo.to(body.span);
self.sess.gated_spans.gate(sym::try_blocks, span);
- Ok(self.mk_expr(span, ExprKind::TryBlock(body), attrs))
+ Ok(self.mk_expr_with_attrs(span, ExprKind::TryBlock(body), attrs))
}
}
@@ -2884,14 +2788,13 @@ impl<'a> Parser<'a> {
}
/// Parses an `async move? {...}` expression.
- fn parse_async_block(&mut self, mut attrs: AttrVec) -> PResult<'a, P<Expr>> {
+ fn parse_async_block(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.token.span;
self.expect_keyword(kw::Async)?;
let capture_clause = self.parse_capture_clause()?;
- let (iattrs, body) = self.parse_inner_attrs_and_block()?;
- attrs.extend(iattrs);
+ let (attrs, body) = self.parse_inner_attrs_and_block()?;
let kind = ExprKind::Async(capture_clause, DUMMY_NODE_ID, body);
- Ok(self.mk_expr(lo.to(self.prev_token.span), kind, attrs))
+ Ok(self.mk_expr_with_attrs(lo.to(self.prev_token.span), kind, attrs))
}
fn is_async_block(&self) -> bool {
@@ -2925,33 +2828,28 @@ impl<'a> Parser<'a> {
&mut self,
qself: Option<&ast::QSelf>,
path: &ast::Path,
- attrs: &AttrVec,
) -> Option<PResult<'a, P<Expr>>> {
let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL);
if struct_allowed || self.is_certainly_not_a_block() {
if let Err(err) = self.expect(&token::OpenDelim(Delimiter::Brace)) {
return Some(Err(err));
}
- let expr = self.parse_struct_expr(qself.cloned(), path.clone(), attrs.clone(), true);
+ let expr = self.parse_struct_expr(qself.cloned(), 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.error_struct_lit_not_allowed_here(path.span, expr.span);
+ self.sess.emit_err(StructLiteralNotAllowedHere {
+ span: expr.span,
+ sub: StructLiteralNotAllowedHereSugg {
+ left: path.span.shrink_to_lo(),
+ right: expr.span.shrink_to_hi(),
+ },
+ });
}
return Some(expr);
}
None
}
- fn error_struct_lit_not_allowed_here(&self, lo: Span, sp: Span) {
- self.struct_span_err(sp, "struct literals are not allowed here")
- .multipart_suggestion(
- "surround the struct literal with parentheses",
- vec![(lo.shrink_to_lo(), "(".to_string()), (sp.shrink_to_hi(), ")".to_string())],
- Applicability::MachineApplicable,
- )
- .emit();
- }
-
pub(super) fn parse_struct_fields(
&mut self,
pth: ast::Path,
@@ -3069,7 +2967,6 @@ impl<'a> Parser<'a> {
&mut self,
qself: Option<ast::QSelf>,
pth: ast::Path,
- attrs: AttrVec,
recover: bool,
) -> PResult<'a, P<Expr>> {
let lo = pth.span;
@@ -3082,7 +2979,7 @@ impl<'a> Parser<'a> {
} else {
ExprKind::Struct(P(ast::StructExpr { qself, path: pth, fields, rest: base }))
};
- Ok(self.mk_expr(span, expr, attrs))
+ Ok(self.mk_expr(span, expr))
}
/// Use in case of error after field-looking code: `S { foo: () with a }`.
@@ -3110,18 +3007,10 @@ impl<'a> Parser<'a> {
if self.token != token::Comma {
return;
}
- self.struct_span_err(
- span.to(self.prev_token.span),
- "cannot use a comma after the base struct",
- )
- .span_suggestion_short(
- self.token.span,
- "remove this comma",
- "",
- Applicability::MachineApplicable,
- )
- .note("the base struct must always be the last field")
- .emit();
+ self.sess.emit_err(CommaAfterBaseStruct {
+ span: span.to(self.prev_token.span),
+ comma: self.token.span,
+ });
self.recover_stmt();
}
@@ -3137,7 +3026,7 @@ impl<'a> Parser<'a> {
// Mimic `x: x` for the `x` field shorthand.
let ident = this.parse_ident_common(false)?;
let path = ast::Path::from_ident(ident);
- (ident, this.mk_expr(ident.span, ExprKind::Path(None, path), AttrVec::new()))
+ (ident, this.mk_expr(ident.span, ExprKind::Path(None, path)))
} else {
let ident = this.parse_field_name()?;
this.error_on_eq_field_init(ident);
@@ -3151,7 +3040,7 @@ impl<'a> Parser<'a> {
span: lo.to(expr.span),
expr,
is_shorthand,
- attrs: attrs.into(),
+ attrs,
id: DUMMY_NODE_ID,
is_placeholder: false,
},
@@ -3167,43 +3056,18 @@ impl<'a> Parser<'a> {
return;
}
- self.struct_span_err(self.token.span, "expected `:`, found `=`")
- .span_suggestion(
- field_name.span.shrink_to_hi().to(self.token.span),
- "replace equals symbol with a colon",
- ":",
- Applicability::MachineApplicable,
- )
- .emit();
+ self.sess.emit_err(EqFieldInit {
+ span: self.token.span,
+ eq: field_name.span.shrink_to_hi().to(self.token.span),
+ });
}
fn err_dotdotdot_syntax(&self, span: Span) {
- self.struct_span_err(span, "unexpected token: `...`")
- .span_suggestion(
- span,
- "use `..` for an exclusive range",
- "..",
- Applicability::MaybeIncorrect,
- )
- .span_suggestion(
- span,
- "or `..=` for an inclusive range",
- "..=",
- Applicability::MaybeIncorrect,
- )
- .emit();
+ self.sess.emit_err(DotDotDot { span });
}
fn err_larrow_operator(&self, span: Span) {
- self.struct_span_err(span, "unexpected token: `<-`")
- .span_suggestion(
- span,
- "if you meant to write a comparison against a negative value, add a \
- space in between `<` and `-`",
- "< -",
- Applicability::MaybeIncorrect,
- )
- .emit();
+ self.sess.emit_err(LeftArrowOperator { span });
}
fn mk_assign_op(&self, binop: BinOp, lhs: P<Expr>, rhs: P<Expr>) -> ExprKind {
@@ -3242,17 +3106,21 @@ impl<'a> Parser<'a> {
fn mk_await_expr(&mut self, self_arg: P<Expr>, lo: Span) -> P<Expr> {
let span = lo.to(self.prev_token.span);
- let await_expr = self.mk_expr(span, ExprKind::Await(self_arg), AttrVec::new());
+ let await_expr = self.mk_expr(span, ExprKind::Await(self_arg));
self.recover_from_await_method_call();
await_expr
}
- pub(crate) fn mk_expr(&self, span: Span, kind: ExprKind, attrs: AttrVec) -> P<Expr> {
+ pub(crate) fn mk_expr_with_attrs(&self, span: Span, kind: ExprKind, attrs: AttrVec) -> P<Expr> {
P(Expr { kind, span, attrs, id: DUMMY_NODE_ID, tokens: None })
}
+ pub(crate) fn mk_expr(&self, span: Span, kind: ExprKind) -> P<Expr> {
+ P(Expr { kind, span, attrs: AttrVec::new(), id: DUMMY_NODE_ID, tokens: None })
+ }
+
pub(super) fn mk_expr_err(&self, span: Span) -> P<Expr> {
- self.mk_expr(span, ExprKind::Err, AttrVec::new())
+ self.mk_expr(span, ExprKind::Err)
}
/// Create expression span ensuring the span of the parent node
@@ -3268,7 +3136,7 @@ impl<'a> Parser<'a> {
fn collect_tokens_for_expr(
&mut self,
attrs: AttrWrapper,
- f: impl FnOnce(&mut Self, Vec<ast::Attribute>) -> PResult<'a, P<Expr>>,
+ f: impl FnOnce(&mut Self, ast::AttrVec) -> PResult<'a, P<Expr>>,
) -> PResult<'a, P<Expr>> {
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
let res = f(this, attrs)?;
@@ -3276,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 1acfd93d8..fa75670b2 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -2,7 +2,7 @@ use super::{ForceCollect, Parser, TrailingToken};
use rustc_ast::token;
use rustc_ast::{
- self as ast, Attribute, GenericBounds, GenericParam, GenericParamKind, WhereClause,
+ self as ast, AttrVec, GenericBounds, GenericParam, GenericParamKind, TyKind, WhereClause,
};
use rustc_errors::{Applicability, PResult};
use rustc_span::symbol::kw;
@@ -26,24 +26,54 @@ impl<'a> Parser<'a> {
}
/// Matches `typaram = IDENT (`?` unbound)? optbounds ( EQ ty )?`.
- fn parse_ty_param(&mut self, preceding_attrs: Vec<Attribute>) -> PResult<'a, GenericParam> {
+ fn parse_ty_param(&mut self, preceding_attrs: AttrVec) -> PResult<'a, GenericParam> {
let ident = self.parse_ident()?;
// Parse optional colon and param bounds.
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,
- attrs: preceding_attrs.into(),
+ attrs: preceding_attrs,
bounds,
kind: GenericParamKind::Type { default },
is_placeholder: false,
@@ -53,7 +83,7 @@ impl<'a> Parser<'a> {
pub(crate) fn parse_const_param(
&mut self,
- preceding_attrs: Vec<Attribute>,
+ preceding_attrs: AttrVec,
) -> PResult<'a, GenericParam> {
let const_span = self.token.span;
@@ -68,7 +98,7 @@ impl<'a> Parser<'a> {
Ok(GenericParam {
ident,
id: ast::DUMMY_NODE_ID,
- attrs: preceding_attrs.into(),
+ attrs: preceding_attrs,
bounds: Vec::new(),
kind: GenericParamKind::Const { ty, kw_span: const_span, default },
is_placeholder: false,
@@ -109,7 +139,7 @@ impl<'a> Parser<'a> {
Some(ast::GenericParam {
ident: lifetime.ident,
id: lifetime.id,
- attrs: attrs.into(),
+ attrs,
bounds,
kind: ast::GenericParamKind::Lifetime,
is_placeholder: false,
@@ -314,7 +344,6 @@ impl<'a> Parser<'a> {
span: lo.to(self.prev_token.span),
lhs_ty: ty,
rhs_ty,
- id: ast::DUMMY_NODE_ID,
}))
} else {
self.maybe_recover_bounds_doubled_colon(&ty)?;
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 567072925..bda301c52 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -1,4 +1,6 @@
-use super::diagnostics::{dummy_arg, ConsumeClosingDelim, Error};
+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};
@@ -8,12 +10,12 @@ use rustc_ast::token::{self, Delimiter, TokenKind};
use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
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::{BindingMode, Block, FnDecl, FnSig, Param, SelfKind};
+use rustc_ast::{BindingAnnotation, Block, FnDecl, FnSig, Param, SelfKind};
use rustc_ast::{EnumDef, FieldDef, Generics, TraitRef, Ty, TyKind, Variant, VariantData};
use rustc_ast::{FnHeader, ForeignItem, Path, PathSegment, Visibility, VisibilityKind};
use rustc_ast::{MacArgs, MacCall, MacDelimiter};
use rustc_ast_pretty::pprust;
-use rustc_errors::{struct_span_err, Applicability, PResult, StashKey};
+use rustc_errors::{struct_span_err, Applicability, IntoDiagnostic, PResult, StashKey};
use rustc_span::edition::Edition;
use rustc_span::lev_distance::lev_distance;
use rustc_span::source_map::{self, Span};
@@ -22,7 +24,6 @@ use rustc_span::DUMMY_SP;
use std::convert::TryFrom;
use std::mem;
-use tracing::debug;
impl<'a> Parser<'a> {
/// Parses a source module as a crate. This is the main entry point for the parser.
@@ -32,7 +33,7 @@ impl<'a> Parser<'a> {
}
/// Parses a `mod <foo> { ... }` or `mod <foo>;` item.
- fn parse_item_mod(&mut self, attrs: &mut Vec<Attribute>) -> PResult<'a, ItemInfo> {
+ fn parse_item_mod(&mut self, attrs: &mut AttrVec) -> PResult<'a, ItemInfo> {
let unsafety = self.parse_unsafety();
self.expect_keyword(kw::Mod)?;
let id = self.parse_ident()?;
@@ -40,9 +41,9 @@ impl<'a> Parser<'a> {
ModKind::Unloaded
} else {
self.expect(&token::OpenDelim(Delimiter::Brace))?;
- let (mut inner_attrs, items, inner_span) =
+ let (inner_attrs, items, inner_span) =
self.parse_mod(&token::CloseDelim(Delimiter::Brace))?;
- attrs.append(&mut inner_attrs);
+ attrs.extend(inner_attrs);
ModKind::Loaded(items, Inline::Yes, inner_span)
};
Ok((id, ItemKind::Mod(unsafety, mod_kind)))
@@ -52,7 +53,7 @@ impl<'a> Parser<'a> {
pub fn parse_mod(
&mut self,
term: &TokenKind,
- ) -> PResult<'a, (Vec<Attribute>, Vec<P<Item>>, ModSpans)> {
+ ) -> PResult<'a, (AttrVec, Vec<P<Item>>, ModSpans)> {
let lo = self.token.span;
let attrs = self.parse_inner_attributes()?;
@@ -68,7 +69,12 @@ impl<'a> Parser<'a> {
if !self.maybe_consume_incorrect_semicolon(&items) {
let msg = &format!("expected item, found {token_str}");
let mut err = self.struct_span_err(self.token.span, msg);
- err.span_label(self.token.span, "expected item");
+ let label = if self.is_kw_followed_by_ident(kw::Let) {
+ "consider using `const` or `static` instead of `let` for global variables"
+ } else {
+ "expected item"
+ };
+ err.span_label(self.token.span, label);
return Err(err);
}
}
@@ -129,7 +135,7 @@ impl<'a> Parser<'a> {
fn parse_item_common_(
&mut self,
- mut attrs: Vec<Attribute>,
+ mut attrs: AttrVec,
mac_allowed: bool,
attrs_allowed: bool,
fn_parse_mode: FnParseMode,
@@ -193,7 +199,7 @@ impl<'a> Parser<'a> {
/// Parses one of the items allowed by the flags.
fn parse_item_kind(
&mut self,
- attrs: &mut Vec<Attribute>,
+ attrs: &mut AttrVec,
macros_allowed: bool,
lo: Span,
vis: &Visibility,
@@ -271,7 +277,10 @@ impl<'a> Parser<'a> {
// MACRO_RULES ITEM
self.parse_item_macro_rules(vis, has_bang)?
} else if self.isnt_macro_invocation()
- && (self.token.is_ident_named(sym::import) || self.token.is_ident_named(sym::using))
+ && (self.token.is_ident_named(sym::import)
+ || self.token.is_ident_named(sym::using)
+ || self.token.is_ident_named(sym::include)
+ || self.token.is_ident_named(sym::require))
{
return self.recover_import_as_use();
} else if self.isnt_macro_invocation() && vis.kind.is_pub() {
@@ -279,7 +288,7 @@ impl<'a> Parser<'a> {
return Ok(None);
} else if macros_allowed && self.check_path() {
// MACRO INVOCATION ITEM
- (Ident::empty(), ItemKind::MacCall(self.parse_item_macro(vis)?))
+ (Ident::empty(), ItemKind::MacCall(P(self.parse_item_macro(vis)?)))
} else {
return Ok(None);
};
@@ -526,7 +535,7 @@ impl<'a> Parser<'a> {
/// ```
fn parse_item_impl(
&mut self,
- attrs: &mut Vec<Attribute>,
+ attrs: &mut AttrVec,
defaultness: Defaultness,
) -> PResult<'a, ItemInfo> {
let unsafety = self.parse_unsafety();
@@ -653,12 +662,20 @@ impl<'a> Parser<'a> {
fn parse_item_list<T>(
&mut self,
- attrs: &mut Vec<Attribute>,
+ attrs: &mut AttrVec,
mut parse_item: impl FnMut(&mut Parser<'a>) -> PResult<'a, Option<Option<T>>>,
) -> PResult<'a, Vec<T>> {
let open_brace_span = self.token.span;
+
+ // Recover `impl Ty;` instead of `impl Ty {}`
+ if self.token == TokenKind::Semi {
+ self.sess.emit_err(UseEmptyBlockNotSemi { span: self.token.span });
+ self.bump();
+ return Ok(vec![]);
+ }
+
self.expect(&token::OpenDelim(Delimiter::Brace))?;
- attrs.append(&mut self.parse_inner_attributes()?);
+ attrs.extend(self.parse_inner_attributes()?);
let mut items = Vec::new();
while !self.eat(&token::CloseDelim(Delimiter::Brace)) {
@@ -667,14 +684,55 @@ impl<'a> Parser<'a> {
}
match parse_item(self) {
Ok(None) => {
+ let is_unnecessary_semicolon = !items.is_empty()
+ // When the close delim is `)` in a case like the following, `token.kind` is expected to be `token::CloseDelim(Delimiter::Parenthesis)`,
+ // but the actual `token.kind` is `token::CloseDelim(Delimiter::Bracket)`.
+ // This is because the `token.kind` of the close delim is treated as the same as
+ // that of the open delim in `TokenTreesReader::parse_token_tree`, even if the delimiters of them are different.
+ // Therefore, `token.kind` should not be compared here.
+ //
+ // issue-60075.rs
+ // ```
+ // trait T {
+ // fn qux() -> Option<usize> {
+ // let _ = if true {
+ // });
+ // ^ this close delim
+ // Some(4)
+ // }
+ // ```
+ && self
+ .span_to_snippet(self.prev_token.span)
+ .map_or(false, |snippet| snippet == "}")
+ && self.token.kind == token::Semi;
+ let semicolon_span = self.token.span;
// We have to bail or we'll potentially never make progress.
let non_item_span = self.token.span;
+ let is_let = self.token.is_keyword(kw::Let);
+
+ let mut err = self.struct_span_err(non_item_span, "non-item in item list");
self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes);
- self.struct_span_err(non_item_span, "non-item in item list")
- .span_label(open_brace_span, "item list starts here")
- .span_label(non_item_span, "non-item starts here")
- .span_label(self.prev_token.span, "item list ends here")
- .emit();
+ if is_let {
+ err.span_suggestion(
+ non_item_span,
+ "consider using `const` instead of `let` for associated const",
+ "const",
+ Applicability::MachineApplicable,
+ );
+ } else {
+ err.span_label(open_brace_span, "item list starts here")
+ .span_label(non_item_span, "non-item starts here")
+ .span_label(self.prev_token.span, "item list ends here");
+ }
+ if is_unnecessary_semicolon {
+ err.span_suggestion(
+ semicolon_span,
+ "consider removing this semicolon",
+ "",
+ Applicability::MaybeIncorrect,
+ );
+ }
+ err.emit();
break;
}
Ok(Some(item)) => items.extend(item),
@@ -702,8 +760,8 @@ impl<'a> Parser<'a> {
)
.span_label(self.token.span, "this doc comment doesn't document anything")
.help(
- "doc comments must come before what they document, maybe a \
- comment was intended with `//`?",
+ "doc comments must come before what they document, if a comment was \
+ intended use `//`",
)
.emit();
self.bump();
@@ -737,7 +795,7 @@ impl<'a> Parser<'a> {
}
/// Parses `unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`.
- fn parse_item_trait(&mut self, attrs: &mut Vec<Attribute>, lo: Span) -> PResult<'a, ItemInfo> {
+ fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemInfo> {
let unsafety = self.parse_unsafety();
// Parse optional `auto` prefix.
let is_auto = if self.eat_keyword(kw::Auto) { IsAuto::Yes } else { IsAuto::No };
@@ -1023,7 +1081,7 @@ impl<'a> Parser<'a> {
/// ```
fn parse_item_foreign_mod(
&mut self,
- attrs: &mut Vec<Attribute>,
+ attrs: &mut AttrVec,
mut unsafety: Unsafe,
) -> PResult<'a, ItemInfo> {
let abi = self.parse_abi(); // ABI?
@@ -1124,6 +1182,16 @@ impl<'a> Parser<'a> {
Applicability::MaybeIncorrect,
)
.emit();
+ } else if self.eat_keyword(kw::Let) {
+ let span = self.prev_token.span;
+ self.struct_span_err(const_span.to(span), "`const` and `let` are mutually exclusive")
+ .span_suggestion(
+ const_span.to(span),
+ "remove `let`",
+ "const",
+ Applicability::MaybeIncorrect,
+ )
+ .emit();
}
}
@@ -1131,7 +1199,7 @@ impl<'a> Parser<'a> {
fn recover_const_impl(
&mut self,
const_span: Span,
- attrs: &mut Vec<Attribute>,
+ attrs: &mut AttrVec,
defaultness: Defaultness,
) -> PResult<'a, ItemInfo> {
let impl_span = self.token.span;
@@ -1179,10 +1247,11 @@ impl<'a> Parser<'a> {
// Parse the type of a `const` or `static mut?` item.
// That is, the `":" $ty` fragment.
- let ty = if self.eat(&token::Colon) {
- self.parse_ty()?
- } else {
- self.recover_missing_const_type(id, m)
+ let ty = match (self.eat(&token::Colon), self.check(&token::Eq) | self.check(&token::Semi))
+ {
+ // If there wasn't a `:` or the colon was followed by a `=` or `;` recover a missing type.
+ (true, false) => self.parse_ty()?,
+ (colon, _) => self.recover_missing_const_type(colon, m),
};
let expr = if self.eat(&token::Eq) { Some(self.parse_expr()?) } else { None };
@@ -1190,9 +1259,9 @@ impl<'a> Parser<'a> {
Ok((id, ty, expr))
}
- /// We were supposed to parse `:` but the `:` was missing.
+ /// We were supposed to parse `":" $ty` but the `:` or the type was missing.
/// This means that the type is missing.
- fn recover_missing_const_type(&mut self, id: Ident, m: Option<Mutability>) -> P<Ty> {
+ fn recover_missing_const_type(&mut self, colon_present: bool, m: Option<Mutability>) -> P<Ty> {
// Construct the error and stash it away with the hope
// that typeck will later enrich the error with a type.
let kind = match m {
@@ -1200,29 +1269,34 @@ impl<'a> Parser<'a> {
Some(Mutability::Not) => "static",
None => "const",
};
- let mut err = self.struct_span_err(id.span, &format!("missing type for `{kind}` item"));
+
+ let colon = match colon_present {
+ true => "",
+ false => ":",
+ };
+
+ let span = self.prev_token.span.shrink_to_hi();
+ let mut err = self.struct_span_err(span, &format!("missing type for `{kind}` item"));
err.span_suggestion(
- id.span,
+ span,
"provide a type for the item",
- format!("{id}: <type>"),
+ format!("{colon} <type>"),
Applicability::HasPlaceholders,
);
- err.stash(id.span, StashKey::ItemNoType);
+ err.stash(span, StashKey::ItemNoType);
// The user intended that the type be inferred,
// so treat this as if the user wrote e.g. `const A: _ = expr;`.
- P(Ty { kind: TyKind::Infer, span: id.span, id: ast::DUMMY_NODE_ID, tokens: None })
+ P(Ty { kind: TyKind::Infer, span, id: ast::DUMMY_NODE_ID, tokens: None })
}
/// Parses an enum declaration.
fn parse_item_enum(&mut self) -> PResult<'a, ItemInfo> {
if self.token.is_keyword(kw::Struct) {
- let mut err = self.struct_span_err(
- self.prev_token.span.to(self.token.span),
- "`enum` and `struct` are mutually exclusive",
- );
+ let span = self.prev_token.span.to(self.token.span);
+ let mut err = self.struct_span_err(span, "`enum` and `struct` are mutually exclusive");
err.span_suggestion(
- self.prev_token.span.to(self.token.span),
+ span,
"replace `enum struct` with",
"enum",
Applicability::MachineApplicable,
@@ -1239,12 +1313,20 @@ impl<'a> Parser<'a> {
let mut generics = self.parse_generics()?;
generics.where_clause = self.parse_where_clause()?;
- let (variants, _) = self
- .parse_delim_comma_seq(Delimiter::Brace, |p| p.parse_enum_variant())
- .map_err(|e| {
- self.recover_stmt();
- e
- })?;
+ // Possibly recover `enum Foo;` instead of `enum Foo {}`
+ let (variants, _) = if self.token == TokenKind::Semi {
+ self.sess.emit_err(UseEmptyBlockNotSemi { span: self.token.span });
+ self.bump();
+ (vec![], false)
+ } else {
+ self.parse_delim_comma_seq(Delimiter::Brace, |p| p.parse_enum_variant()).map_err(
+ |mut e| {
+ e.span_label(id.span, "while parsing this enum");
+ self.recover_stmt();
+ e
+ },
+ )?
+ };
let enum_definition = EnumDef { variants: variants.into_iter().flatten().collect() };
Ok((id, ItemKind::Enum(enum_definition, generics)))
@@ -1266,7 +1348,8 @@ impl<'a> Parser<'a> {
let struct_def = if this.check(&token::OpenDelim(Delimiter::Brace)) {
// Parse a struct variant.
- let (fields, recovered) = this.parse_record_struct_body("struct", false)?;
+ let (fields, recovered) =
+ this.parse_record_struct_body("struct", ident.span, false)?;
VariantData::Struct(fields, recovered)
} else if this.check(&token::OpenDelim(Delimiter::Parenthesis)) {
VariantData::Tuple(this.parse_tuple_struct_body()?, DUMMY_NODE_ID)
@@ -1281,7 +1364,7 @@ impl<'a> Parser<'a> {
ident,
vis,
id: DUMMY_NODE_ID,
- attrs: variant_attrs.into(),
+ attrs: variant_attrs,
data: struct_def,
disr_expr,
span: vlo.to(this.prev_token.span),
@@ -1320,8 +1403,11 @@ impl<'a> Parser<'a> {
VariantData::Unit(DUMMY_NODE_ID)
} else {
// If we see: `struct Foo<T> where T: Copy { ... }`
- let (fields, recovered) =
- self.parse_record_struct_body("struct", generics.where_clause.has_where_token)?;
+ let (fields, recovered) = self.parse_record_struct_body(
+ "struct",
+ class_name.span,
+ generics.where_clause.has_where_token,
+ )?;
VariantData::Struct(fields, recovered)
}
// No `where` so: `struct Foo<T>;`
@@ -1329,8 +1415,11 @@ impl<'a> Parser<'a> {
VariantData::Unit(DUMMY_NODE_ID)
// Record-style struct definition
} else if self.token == token::OpenDelim(Delimiter::Brace) {
- let (fields, recovered) =
- self.parse_record_struct_body("struct", generics.where_clause.has_where_token)?;
+ let (fields, recovered) = self.parse_record_struct_body(
+ "struct",
+ class_name.span,
+ generics.where_clause.has_where_token,
+ )?;
VariantData::Struct(fields, recovered)
// Tuple-style struct definition with optional where-clause.
} else if self.token == token::OpenDelim(Delimiter::Parenthesis) {
@@ -1359,12 +1448,18 @@ impl<'a> Parser<'a> {
let vdata = if self.token.is_keyword(kw::Where) {
generics.where_clause = self.parse_where_clause()?;
- let (fields, recovered) =
- self.parse_record_struct_body("union", generics.where_clause.has_where_token)?;
+ let (fields, recovered) = self.parse_record_struct_body(
+ "union",
+ class_name.span,
+ generics.where_clause.has_where_token,
+ )?;
VariantData::Struct(fields, recovered)
} else if self.token == token::OpenDelim(Delimiter::Brace) {
- let (fields, recovered) =
- self.parse_record_struct_body("union", generics.where_clause.has_where_token)?;
+ let (fields, recovered) = self.parse_record_struct_body(
+ "union",
+ class_name.span,
+ generics.where_clause.has_where_token,
+ )?;
VariantData::Struct(fields, recovered)
} else {
let token_str = super::token_descr(&self.token);
@@ -1380,6 +1475,7 @@ impl<'a> Parser<'a> {
fn parse_record_struct_body(
&mut self,
adt_ty: &str,
+ ident_span: Span,
parsed_where: bool,
) -> PResult<'a, (Vec<FieldDef>, /* recovered */ bool)> {
let mut fields = Vec::new();
@@ -1394,6 +1490,7 @@ impl<'a> Parser<'a> {
match field {
Ok(field) => fields.push(field),
Err(mut err) => {
+ err.span_label(ident_span, format!("while parsing this {adt_ty}"));
err.emit();
break;
}
@@ -1438,7 +1535,7 @@ impl<'a> Parser<'a> {
ident: None,
id: DUMMY_NODE_ID,
ty,
- attrs: attrs.into(),
+ attrs,
is_placeholder: false,
},
TrailingToken::MaybeComma,
@@ -1464,13 +1561,24 @@ impl<'a> Parser<'a> {
adt_ty: &str,
lo: Span,
vis: Visibility,
- attrs: Vec<Attribute>,
+ attrs: AttrVec,
) -> PResult<'a, FieldDef> {
let mut seen_comma: bool = false;
let a_var = self.parse_name_and_ty(adt_ty, lo, vis, attrs)?;
if self.token == token::Comma {
seen_comma = true;
}
+ if self.eat(&token::Semi) {
+ let sp = self.prev_token.span;
+ let mut err = self.struct_span_err(sp, format!("{adt_ty} fields are separated by `,`"));
+ err.span_suggestion_short(
+ sp,
+ "replace `;` with `,`",
+ ",",
+ Applicability::MachineApplicable,
+ );
+ return Err(err);
+ }
match self.token.kind {
token::Comma => {
self.bump();
@@ -1478,7 +1586,10 @@ impl<'a> Parser<'a> {
token::CloseDelim(Delimiter::Brace) => {}
token::DocComment(..) => {
let previous_span = self.prev_token.span;
- let mut err = self.span_err(self.token.span, Error::UselessDocComment);
+ let mut err = DocCommentDoesNotDocumentAnything {
+ span: self.token.span,
+ missing_comma: None,
+ };
self.bump(); // consume the doc comment
let comma_after_doc_seen = self.eat(&token::Comma);
// `seen_comma` is always false, because we are inside doc block
@@ -1487,18 +1598,13 @@ impl<'a> Parser<'a> {
seen_comma = true;
}
if comma_after_doc_seen || self.token == token::CloseDelim(Delimiter::Brace) {
- err.emit();
+ self.sess.emit_err(err);
} else {
if !seen_comma {
- let sp = self.sess.source_map().next_point(previous_span);
- err.span_suggestion(
- sp,
- "missing comma here",
- ",",
- Applicability::MachineApplicable,
- );
+ let sp = previous_span.shrink_to_hi();
+ err.missing_comma = Some(sp);
}
- return Err(err);
+ return Err(err.into_diagnostic(&self.sess.span_diagnostic));
}
}
_ => {
@@ -1528,8 +1634,12 @@ impl<'a> Parser<'a> {
}
}
- if self.token.is_ident() {
- // This is likely another field; emit the diagnostic and keep going
+ if self.token.is_ident()
+ || (self.token.kind == TokenKind::Pound
+ && (self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Bracket))))
+ {
+ // This is likely another field, TokenKind::Pound is used for `#[..]` attribute for next field,
+ // emit the diagnostic and keep going
err.span_suggestion(
sp,
"try adding a comma",
@@ -1590,7 +1700,7 @@ impl<'a> Parser<'a> {
adt_ty: &str,
lo: Span,
vis: Visibility,
- attrs: Vec<Attribute>,
+ attrs: AttrVec,
) -> PResult<'a, FieldDef> {
let name = self.parse_field_ident(adt_ty, lo)?;
self.expect_field_ty_separator()?;
@@ -1624,7 +1734,7 @@ impl<'a> Parser<'a> {
vis,
id: DUMMY_NODE_ID,
ty,
- attrs: attrs.into(),
+ attrs,
is_placeholder: false,
})
}
@@ -1634,6 +1744,7 @@ impl<'a> Parser<'a> {
fn parse_field_ident(&mut self, adt_ty: &str, lo: Span) -> PResult<'a, Ident> {
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 inherited_vis = Visibility {
span: rustc_span::DUMMY_SP,
@@ -1642,20 +1753,63 @@ impl<'a> Parser<'a> {
};
// We use `parse_fn` to get a span for the function
let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true };
- if let Err(mut db) =
- self.parse_fn(&mut Vec::new(), fn_parse_mode, lo, &inherited_vis)
+ match self.parse_fn(&mut AttrVec::new(), fn_parse_mode, lo, &inherited_vis) {
+ Ok(_) => {
+ let mut err = self.struct_span_err(
+ lo.to(self.prev_token.span),
+ &format!("functions are not allowed in {adt_ty} definitions"),
+ );
+ err.help(
+ "unlike in C++, Java, and C#, functions are declared in `impl` blocks",
+ );
+ err.help("see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information");
+ err
+ }
+ Err(err) => {
+ err.cancel();
+ self.restore_snapshot(snapshot);
+ self.expected_ident_found()
+ }
+ }
+ } else if self.eat_keyword(kw::Struct) {
+ match self.parse_item_struct() {
+ Ok((ident, _)) => {
+ let mut err = self.struct_span_err(
+ lo.with_hi(ident.span.hi()),
+ &format!("structs are not allowed in {adt_ty} definitions"),
+ );
+ err.help("consider creating a new `struct` definition instead of nesting");
+ err
+ }
+ Err(err) => {
+ err.cancel();
+ self.restore_snapshot(snapshot);
+ self.expected_ident_found()
+ }
+ }
+ } else {
+ 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
{
- db.delay_as_bug();
+ 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);
}
- let mut err = self.struct_span_err(
- lo.to(self.prev_token.span),
- &format!("functions are not allowed in {adt_ty} definitions"),
- );
- err.help("unlike in C++, Java, and C#, functions are declared in `impl` blocks");
- err.help("see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information");
err
- } else {
- self.expected_ident_found()
};
return Err(err);
}
@@ -1919,7 +2073,7 @@ impl<'a> Parser<'a> {
/// Parse a function starting from the front matter (`const ...`) to the body `{ ... }` or `;`.
fn parse_fn(
&mut self,
- attrs: &mut Vec<Attribute>,
+ attrs: &mut AttrVec,
fn_parse_mode: FnParseMode,
sig_lo: Span,
vis: &Visibility,
@@ -1942,7 +2096,7 @@ impl<'a> Parser<'a> {
/// or e.g. a block when the function is a provided one.
fn parse_fn_body(
&mut self,
- attrs: &mut Vec<Attribute>,
+ attrs: &mut AttrVec,
ident: &Ident,
sig_hi: &mut Span,
req_body: bool,
@@ -1957,7 +2111,7 @@ impl<'a> Parser<'a> {
// Include the trailing semicolon in the span of the signature
self.expect_semi()?;
*sig_hi = self.prev_token.span;
- (Vec::new(), None)
+ (AttrVec::new(), None)
} else if self.check(&token::OpenDelim(Delimiter::Brace)) || self.token.is_whole_block() {
self.parse_inner_attrs_and_block().map(|(attrs, body)| (attrs, Some(body)))?
} else if self.token.kind == token::Eq {
@@ -1974,7 +2128,7 @@ impl<'a> Parser<'a> {
Applicability::MachineApplicable,
)
.emit();
- (Vec::new(), Some(self.mk_block_err(span)))
+ (AttrVec::new(), Some(self.mk_block_err(span)))
} else {
let expected = if req_body {
&[token::OpenDelim(Delimiter::Brace)][..]
@@ -1991,7 +2145,7 @@ impl<'a> Parser<'a> {
return Err(err);
}
}
- (Vec::new(), None)
+ (AttrVec::new(), None)
};
attrs.extend(inner_attrs);
Ok(body)
@@ -2220,7 +2374,7 @@ impl<'a> Parser<'a> {
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
// Possibly parse `self`. Recover if we parsed it and it wasn't allowed here.
if let Some(mut param) = this.parse_self_param()? {
- param.attrs = attrs.into();
+ param.attrs = attrs;
let res = if first_param { Ok(param) } else { this.recover_bad_self_param(param) };
return Ok((res?, TrailingToken::None));
}
@@ -2249,7 +2403,7 @@ impl<'a> Parser<'a> {
(pat, this.parse_ty_for_param()?)
} else {
debug!("parse_param_general ident_to_pat");
- let parser_snapshot_before_ty = this.clone();
+ let parser_snapshot_before_ty = this.create_snapshot_for_diagnostic();
this.eat_incorrect_doc_comment_for_param_type();
let mut ty = this.parse_ty_for_param();
if ty.is_ok()
@@ -2263,7 +2417,7 @@ impl<'a> Parser<'a> {
match ty {
Ok(ty) => {
let ident = Ident::new(kw::Empty, this.prev_token.span);
- let bm = BindingMode::ByValue(Mutability::Not);
+ let bm = BindingAnnotation::NONE;
let pat = this.mk_pat_ident(ty.span, bm, ident);
(pat, ty)
}
@@ -2272,23 +2426,16 @@ impl<'a> Parser<'a> {
// Recover from attempting to parse the argument as a type without pattern.
Err(err) => {
err.cancel();
- *this = parser_snapshot_before_ty;
+ this.restore_snapshot(parser_snapshot_before_ty);
this.recover_arg_parse()?
}
}
};
- let span = lo.until(this.token.span);
+ let span = lo.to(this.prev_token.span);
Ok((
- Param {
- attrs: attrs.into(),
- id: ast::DUMMY_NODE_ID,
- is_placeholder: false,
- pat,
- span,
- ty,
- },
+ Param { attrs, id: ast::DUMMY_NODE_ID, is_placeholder: false, pat, span, ty },
TrailingToken::None,
))
})
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 0c523ad22..5fe29062b 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -13,7 +13,6 @@ mod ty;
use crate::lexer::UnmatchedBrace;
pub use attr_wrapper::AttrWrapper;
pub use diagnostics::AttemptLocalParseRecovery;
-use diagnostics::Error;
pub(crate) use item::FnParseMode;
pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma};
pub use path::PathStyle;
@@ -32,16 +31,20 @@ use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::PResult;
use rustc_errors::{
- struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, FatalError, MultiSpan,
+ Applicability, DiagnosticBuilder, ErrorGuaranteed, FatalError, IntoDiagnostic, MultiSpan,
};
use rustc_session::parse::ParseSess;
use rustc_span::source_map::{Span, DUMMY_SP};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
-use tracing::debug;
use std::ops::Range;
use std::{cmp, mem, slice};
+use crate::errors::{
+ DocCommentDoesNotDocumentAnything, IncorrectVisibilityRestriction, MismatchedClosingDelimiter,
+ NonStringAbiLiteral,
+};
+
bitflags::bitflags! {
struct Restrictions: u8 {
const STMT_EXPR = 1 << 0;
@@ -76,6 +79,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,
@@ -111,6 +115,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,
@@ -148,12 +158,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)]
@@ -171,7 +184,7 @@ pub struct ClosureSpans {
/// attribute, we parse a nested AST node that has `#[cfg]` or `#[cfg_attr]`
/// In this case, we use a `ReplaceRange` to replace the entire inner AST node
/// with `FlatToken::AttrTarget`, allowing us to perform eager cfg-expansion
-/// on an `AttrAnnotatedTokenStream`
+/// on an `AttrTokenStream`.
///
/// 2. When we parse an inner attribute while collecting tokens. We
/// remove inner attributes from the token stream entirely, and
@@ -184,7 +197,7 @@ pub type ReplaceRange = (Range<u32>, Vec<(FlatToken, Spacing)>);
/// Controls how we capture tokens. Capturing can be expensive,
/// so we try to avoid performing capturing in cases where
-/// we will never need an `AttrAnnotatedTokenStream`
+/// we will never need an `AttrTokenStream`.
#[derive(Copy, Clone)]
pub enum Capturing {
/// We aren't performing any capturing - this is the default mode.
@@ -238,7 +251,7 @@ struct TokenCursor {
// the trailing `>>` token. The `break_last_token`
// field is used to track this token - it gets
// appended to the captured stream when
- // we evaluate a `LazyTokenStream`
+ // we evaluate a `LazyAttrTokenStream`.
break_last_token: bool,
}
@@ -281,7 +294,7 @@ impl TokenCursor {
if delim != Delimiter::Invisible {
return (Token::new(token::OpenDelim(delim), sp.open), Spacing::Alone);
}
- // No open delimeter to return; continue on to the next iteration.
+ // No open delimiter to return; continue on to the next iteration.
}
};
} else if let Some(frame) = self.stack.pop() {
@@ -299,7 +312,10 @@ impl TokenCursor {
fn desugar(&mut self, attr_style: AttrStyle, data: Symbol, span: Span) -> (Token, Spacing) {
// Searches for the occurrences of `"#*` and returns the minimum number of `#`s
- // required to wrap the text.
+ // required to wrap the text. E.g.
+ // - `abc d` is wrapped as `r"abc d"` (num_of_hashes = 0)
+ // - `abc "d"` is wrapped as `r#"abc "d""#` (num_of_hashes = 1)
+ // - `abc "##d##"` is wrapped as `r###"abc "d""###` (num_of_hashes = 3)
let mut num_of_hashes = 0;
let mut count = 0;
for ch in data.as_str().chars() {
@@ -311,6 +327,7 @@ impl TokenCursor {
num_of_hashes = cmp::max(num_of_hashes, count);
}
+ // `/// foo` becomes `doc = r"foo".
let delim_span = DelimSpan::from_single(span);
let body = TokenTree::Delimited(
delim_span,
@@ -407,24 +424,39 @@ pub enum FollowedByType {
No,
}
-fn token_descr_opt(token: &Token) -> Option<&'static str> {
- Some(match token.kind {
- _ if token.is_special_ident() => "reserved identifier",
- _ if token.is_used_keyword() => "keyword",
- _ if token.is_unused_keyword() => "reserved keyword",
- token::DocComment(..) => "doc comment",
- _ => return None,
- })
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub enum TokenDescription {
+ ReservedIdentifier,
+ Keyword,
+ ReservedKeyword,
+ DocComment,
}
-pub(super) fn token_descr(token: &Token) -> String {
- let token_str = pprust::token_to_string(token);
- match token_descr_opt(token) {
- Some(prefix) => format!("{} `{}`", prefix, token_str),
- _ => format!("`{}`", token_str),
+impl TokenDescription {
+ pub fn from_token(token: &Token) -> Option<Self> {
+ match token.kind {
+ _ if token.is_special_ident() => Some(TokenDescription::ReservedIdentifier),
+ _ if token.is_used_keyword() => Some(TokenDescription::Keyword),
+ _ if token.is_unused_keyword() => Some(TokenDescription::ReservedKeyword),
+ token::DocComment(..) => Some(TokenDescription::DocComment),
+ _ => None,
+ }
}
}
+pub(super) fn token_descr(token: &Token) -> String {
+ let name = pprust::token_to_string(token).to_string();
+
+ let kind = TokenDescription::from_token(token).map(|kind| match kind {
+ TokenDescription::ReservedIdentifier => "reserved identifier",
+ TokenDescription::Keyword => "keyword",
+ TokenDescription::ReservedKeyword => "reserved keyword",
+ TokenDescription::DocComment => "doc comment",
+ });
+
+ if let Some(kind) = kind { format!("{} `{}`", kind, name) } else { format!("`{}`", name) }
+}
+
impl<'a> Parser<'a> {
pub fn new(
sess: &'a ParseSess,
@@ -460,6 +492,7 @@ impl<'a> Parser<'a> {
inner_attr_ranges: Default::default(),
},
current_closure: None,
+ recovery: Recovery::Allowed,
};
// Make parser point to the first token.
@@ -468,6 +501,22 @@ impl<'a> Parser<'a> {
parser
}
+ pub fn forbid_recovery(mut self) -> Self {
+ self.recovery = Recovery::Forbidden;
+ 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),
@@ -519,9 +568,11 @@ impl<'a> Parser<'a> {
fn ident_or_err(&mut self) -> PResult<'a, (Ident, /* is_raw */ bool)> {
self.token.ident().ok_or_else(|| match self.prev_token.kind {
- TokenKind::DocComment(..) => {
- self.span_err(self.prev_token.span, Error::UselessDocComment)
+ TokenKind::DocComment(..) => DocCommentDoesNotDocumentAnything {
+ span: self.prev_token.span,
+ missing_comma: None,
}
+ .into_diagnostic(&self.sess.span_diagnostic),
_ => self.expected_ident_found(),
})
}
@@ -1116,10 +1167,14 @@ impl<'a> Parser<'a> {
let (attrs, blk) = self.parse_inner_attrs_and_block()?;
let anon_const = AnonConst {
id: DUMMY_NODE_ID,
- value: self.mk_expr(blk.span, ExprKind::Block(blk, None), AttrVec::new()),
+ value: self.mk_expr(blk.span, ExprKind::Block(blk, None)),
};
let blk_span = anon_const.value.span;
- Ok(self.mk_expr(span.to(blk_span), ExprKind::ConstBlock(anon_const), AttrVec::from(attrs)))
+ Ok(self.mk_expr_with_attrs(
+ span.to(blk_span),
+ ExprKind::ConstBlock(anon_const),
+ AttrVec::from(attrs),
+ ))
}
/// Parses mutability (`mut` or nothing).
@@ -1141,7 +1196,9 @@ impl<'a> Parser<'a> {
fn parse_field_name(&mut self) -> PResult<'a, Ident> {
if let token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) = self.token.kind
{
- self.expect_no_suffix(self.token.span, "a tuple index", suffix);
+ if let Some(suffix) = suffix {
+ self.expect_no_tuple_index_suffix(self.token.span, suffix);
+ }
self.bump();
Ok(Ident::new(symbol, self.prev_token.span))
} else {
@@ -1295,7 +1352,11 @@ impl<'a> Parser<'a> {
self.bump(); // `in`
let path = self.parse_path(PathStyle::Mod)?; // `path`
self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)`
- let vis = VisibilityKind::Restricted { path: P(path), id: ast::DUMMY_NODE_ID };
+ let vis = VisibilityKind::Restricted {
+ path: P(path),
+ id: ast::DUMMY_NODE_ID,
+ shorthand: false,
+ };
return Ok(Visibility {
span: lo.to(self.prev_token.span),
kind: vis,
@@ -1308,7 +1369,11 @@ impl<'a> Parser<'a> {
self.bump(); // `(`
let path = self.parse_path(PathStyle::Mod)?; // `crate`/`super`/`self`
self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)`
- let vis = VisibilityKind::Restricted { path: P(path), id: ast::DUMMY_NODE_ID };
+ let vis = VisibilityKind::Restricted {
+ path: P(path),
+ id: ast::DUMMY_NODE_ID,
+ shorthand: true,
+ };
return Ok(Visibility {
span: lo.to(self.prev_token.span),
kind: vis,
@@ -1331,23 +1396,8 @@ impl<'a> Parser<'a> {
let path = self.parse_path(PathStyle::Mod)?;
self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)`
- let msg = "incorrect visibility restriction";
- let suggestion = r##"some possible visibility restrictions are:
-`pub(crate)`: visible only on the current crate
-`pub(super)`: visible only in the current module's parent
-`pub(in path::to::module)`: visible only on the specified path"##;
-
let path_str = pprust::path_to_string(&path);
-
- struct_span_err!(self.sess.span_diagnostic, path.span, E0704, "{}", msg)
- .help(suggestion)
- .span_suggestion(
- path.span,
- &format!("make this visible only to module `{}` with `in`", path_str),
- format!("in {}", path_str),
- Applicability::MachineApplicable,
- )
- .emit();
+ self.sess.emit_err(IncorrectVisibilityRestriction { span: path.span, inner_str: path_str });
Ok(())
}
@@ -1371,16 +1421,9 @@ impl<'a> Parser<'a> {
match self.parse_str_lit() {
Ok(str_lit) => Some(str_lit),
Err(Some(lit)) => match lit.kind {
- ast::LitKind::Err(_) => None,
+ ast::LitKind::Err => None,
_ => {
- self.struct_span_err(lit.span, "non-string ABI literal")
- .span_suggestion(
- lit.span,
- "specify the ABI with a string literal",
- "\"C\"",
- Applicability::MaybeIncorrect,
- )
- .emit();
+ self.sess.emit_err(NonStringAbiLiteral { span: lit.span });
None
}
},
@@ -1421,25 +1464,18 @@ pub(crate) fn make_unclosed_delims_error(
// `None` here means an `Eof` was found. We already emit those errors elsewhere, we add them to
// `unmatched_braces` only for error recovery in the `Parser`.
let found_delim = unmatched.found_delim?;
- let span: MultiSpan = if let Some(sp) = unmatched.unclosed_span {
- vec![unmatched.found_span, sp].into()
- } else {
- unmatched.found_span.into()
- };
- let mut err = sess.span_diagnostic.struct_span_err(
- span,
- &format!(
- "mismatched closing delimiter: `{}`",
- pprust::token_kind_to_string(&token::CloseDelim(found_delim)),
- ),
- );
- err.span_label(unmatched.found_span, "mismatched closing delimiter");
- if let Some(sp) = unmatched.candidate_span {
- err.span_label(sp, "closing delimiter possibly meant for this");
- }
+ let mut spans = vec![unmatched.found_span];
if let Some(sp) = unmatched.unclosed_span {
- err.span_label(sp, "unclosed delimiter");
- }
+ spans.push(sp);
+ };
+ let err = MismatchedClosingDelimiter {
+ spans,
+ delimiter: pprust::token_kind_to_string(&token::CloseDelim(found_delim)).to_string(),
+ unmatched: unmatched.found_span,
+ opening_candidate: unmatched.candidate_span,
+ unclosed: unmatched.unclosed_span,
+ }
+ .into_diagnostic(&sess.span_diagnostic);
Some(err)
}
@@ -1453,11 +1489,11 @@ pub fn emit_unclosed_delims(unclosed_delims: &mut Vec<UnmatchedBrace>, sess: &Pa
}
}
-/// A helper struct used when building an `AttrAnnotatedTokenStream` from
-/// a `LazyTokenStream`. Both delimiter and non-delimited tokens
+/// A helper struct used when building an `AttrTokenStream` from
+/// a `LazyAttrTokenStream`. Both delimiter and non-delimited tokens
/// are stored as `FlatToken::Token`. A vector of `FlatToken`s
-/// is then 'parsed' to build up an `AttrAnnotatedTokenStream` with nested
-/// `AttrAnnotatedTokenTree::Delimited` tokens
+/// is then 'parsed' to build up an `AttrTokenStream` with nested
+/// `AttrTokenTree::Delimited` tokens.
#[derive(Debug, Clone)]
pub enum FlatToken {
/// A token - this holds both delimiter (e.g. '{' and '}')
@@ -1465,11 +1501,11 @@ pub enum FlatToken {
Token(Token),
/// Holds the `AttributesData` for an AST node. The
/// `AttributesData` is inserted directly into the
- /// constructed `AttrAnnotatedTokenStream` as
- /// an `AttrAnnotatedTokenTree::Attributes`
+ /// constructed `AttrTokenStream` as
+ /// an `AttrTokenTree::Attributes`.
AttrTarget(AttributesData),
/// A special 'empty' token that is ignored during the conversion
- /// to an `AttrAnnotatedTokenStream`. This is used to simplify the
+ /// to an `AttrTokenStream`. This is used to simplify the
/// handling of replace ranges.
Empty,
}
diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs
index e215b6872..103dd8012 100644
--- a/compiler/rustc_parse/src/parser/nonterminal.rs
+++ b/compiler/rustc_parse/src/parser/nonterminal.rs
@@ -66,18 +66,18 @@ impl<'a> Parser<'a> {
},
NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr { .. } => {
match token.kind {
- token::Ident(..) | // box, ref, mut, and other identifiers (can stricten)
- token::OpenDelim(Delimiter::Parenthesis) | // tuple pattern
- token::OpenDelim(Delimiter::Bracket) | // slice pattern
- token::BinOp(token::And) | // reference
- token::BinOp(token::Minus) | // negative literal
- token::AndAnd | // double reference
- token::Literal(..) | // literal
- token::DotDot | // range pattern (future compat)
- token::DotDotDot | // range pattern (future compat)
- token::ModSep | // path
- token::Lt | // path (UFCS constant)
- token::BinOp(token::Shl) => true, // path (double UFCS)
+ token::Ident(..) | // box, ref, mut, and other identifiers (can stricten)
+ token::OpenDelim(Delimiter::Parenthesis) | // tuple pattern
+ token::OpenDelim(Delimiter::Bracket) | // slice pattern
+ token::BinOp(token::And) | // reference
+ token::BinOp(token::Minus) | // negative literal
+ token::AndAnd | // double reference
+ token::Literal(..) | // literal
+ token::DotDot | // range pattern (future compat)
+ token::DotDotDot | // range pattern (future compat)
+ token::ModSep | // path
+ token::Lt | // path (UFCS constant)
+ token::BinOp(token::Shl) => true, // path (double UFCS)
// leading vert `|` or-pattern
token::BinOp(token::Or) => matches!(kind, NonterminalKind::PatWithOr {..}),
token::Interpolated(ref nt) => may_be_ident(nt),
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index ba77a3958..52c11b4e3 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -1,14 +1,16 @@
use super::{ForceCollect, Parser, PathStyle, TrailingToken};
+use crate::errors::RemoveLet;
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor};
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter};
use rustc_ast::{
- self as ast, AttrVec, Attribute, BindingMode, Expr, ExprKind, MacCall, Mutability, Pat,
+ self as ast, AttrVec, BindingAnnotation, ByRef, Expr, ExprKind, MacCall, Mutability, Pat,
PatField, PatKind, Path, QSelf, RangeEnd, RangeSyntax,
};
use rustc_ast_pretty::pprust;
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult};
+use rustc_session::errors::ExprParenthesesNeeded;
use rustc_span::source_map::{respan, Span, Spanned};
use rustc_span::symbol::{kw, sym, Ident};
@@ -320,7 +322,13 @@ impl<'a> Parser<'a> {
maybe_recover_from_interpolated_ty_qpath!(self, true);
maybe_whole!(self, NtPat, |x| x);
- let lo = self.token.span;
+ let mut lo = self.token.span;
+
+ if self.token.is_keyword(kw::Let) && self.look_ahead(1, |tok| tok.can_begin_pattern()) {
+ self.bump();
+ self.sess.emit_err(RemoveLet { span: lo });
+ lo = self.token.span;
+ }
let pat = if self.check(&token::BinOp(token::And)) || self.token.kind == token::AndAnd {
self.parse_pat_deref(expected)?
@@ -353,7 +361,7 @@ impl<'a> Parser<'a> {
} else if self.eat_keyword(kw::Ref) {
// Parse ref ident @ pat / ref mut ident @ pat
let mutbl = self.parse_mutability();
- self.parse_pat_ident(BindingMode::ByRef(mutbl))?
+ self.parse_pat_ident(BindingAnnotation(ByRef::Yes, mutbl))?
} else if self.eat_keyword(kw::Box) {
self.parse_pat_box()?
} else if self.check_inline_const(0) {
@@ -369,7 +377,7 @@ impl<'a> Parser<'a> {
// Parse `ident @ pat`
// This can give false positives and parse nullary enums,
// they are dealt with later in resolve.
- self.parse_pat_ident(BindingMode::ByValue(Mutability::Not))?
+ self.parse_pat_ident(BindingAnnotation::NONE)?
} else if self.is_start_of_pat_with_path() {
// Parse pattern starting with a path
let (qself, path) = if self.eat_lt() {
@@ -385,7 +393,7 @@ impl<'a> Parser<'a> {
if qself.is_none() && self.check(&token::Not) {
self.parse_pat_mac_invoc(path)?
} else if let Some(form) = self.parse_range_end() {
- let begin = self.mk_expr(span, ExprKind::Path(qself, path), AttrVec::new());
+ let begin = self.mk_expr(span, ExprKind::Path(qself, path));
self.parse_pat_range_begin_with(begin, form)?
} else if self.check(&token::OpenDelim(Delimiter::Brace)) {
self.parse_pat_struct(qself, path)?
@@ -394,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)))
} else {
// Try to parse everything else as literal with optional minus
match self.parse_literal_maybe_minus() {
@@ -578,7 +605,8 @@ impl<'a> Parser<'a> {
let mut pat = self.parse_pat_no_top_alt(Some("identifier"))?;
// If we don't have `mut $ident (@ pat)?`, error.
- if let PatKind::Ident(BindingMode::ByValue(m @ Mutability::Not), ..) = &mut pat.kind {
+ if let PatKind::Ident(BindingAnnotation(ByRef::No, m @ Mutability::Not), ..) = &mut pat.kind
+ {
// Don't recurse into the subpattern.
// `mut` on the outer binding doesn't affect the inner bindings.
*m = Mutability::Mut;
@@ -604,7 +632,7 @@ impl<'a> Parser<'a> {
)
.emit();
- self.parse_pat_ident(BindingMode::ByRef(Mutability::Mut))
+ self.parse_pat_ident(BindingAnnotation::REF_MUT)
}
/// Turn all by-value immutable bindings in a pattern into mutable bindings.
@@ -613,7 +641,8 @@ impl<'a> Parser<'a> {
struct AddMut(bool);
impl MutVisitor for AddMut {
fn visit_pat(&mut self, pat: &mut P<Pat>) {
- if let PatKind::Ident(BindingMode::ByValue(m @ Mutability::Not), ..) = &mut pat.kind
+ if let PatKind::Ident(BindingAnnotation(ByRef::No, m @ Mutability::Not), ..) =
+ &mut pat.kind
{
self.0 = true;
*m = Mutability::Mut;
@@ -665,7 +694,7 @@ impl<'a> Parser<'a> {
fn parse_pat_mac_invoc(&mut self, path: Path) -> PResult<'a, PatKind> {
self.bump();
let args = self.parse_mac_args()?;
- let mac = MacCall { path, args, prior_type_ascription: self.last_type_ascription };
+ let mac = P(MacCall { path, args, prior_type_ascription: self.last_type_ascription });
Ok(PatKind::MacCall(mac))
}
@@ -684,7 +713,7 @@ impl<'a> Parser<'a> {
let sp = self.sess.source_map().start_point(self.token.span);
if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&sp) {
- self.sess.expr_parentheses_needed(&mut err, *sp);
+ err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
}
Err(err)
@@ -767,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")
@@ -790,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'`
})
}
@@ -807,7 +836,7 @@ impl<'a> Parser<'a> {
(None, self.parse_path(PathStyle::Expr)?)
};
let hi = self.prev_token.span;
- Ok(self.mk_expr(lo.to(hi), ExprKind::Path(qself, path), AttrVec::new()))
+ Ok(self.mk_expr(lo.to(hi), ExprKind::Path(qself, path)))
} else {
self.parse_literal_maybe_minus()
}
@@ -838,7 +867,7 @@ impl<'a> Parser<'a> {
/// Parses `ident` or `ident @ pat`.
/// Used by the copy foo and ref foo patterns to give a good
/// error message when parsing mistakes like `ref foo(a, b)`.
- fn parse_pat_ident(&mut self, binding_mode: BindingMode) -> PResult<'a, PatKind> {
+ fn parse_pat_ident(&mut self, binding_annotation: BindingAnnotation) -> PResult<'a, PatKind> {
let ident = self.parse_ident()?;
let sub = if self.eat(&token::At) {
Some(self.parse_pat_no_top_alt(Some("binding pattern"))?)
@@ -856,7 +885,7 @@ impl<'a> Parser<'a> {
.struct_span_err(self.prev_token.span, "expected identifier, found enum pattern"));
}
- Ok(PatKind::Ident(binding_mode, ident, sub))
+ Ok(PatKind::Ident(binding_annotation, ident, sub))
}
/// Parse a struct ("record") pattern (e.g. `Foo { ... }` or `Foo::Bar { ... }`).
@@ -936,11 +965,7 @@ impl<'a> Parser<'a> {
None
};
- Ok(PatKind::Ident(
- BindingMode::ByValue(Mutability::Not),
- Ident::new(kw::Box, box_span),
- sub,
- ))
+ Ok(PatKind::Ident(BindingAnnotation::NONE, Ident::new(kw::Box, box_span), sub))
} else {
let pat = self.parse_pat_with_range_pat(false, None)?;
self.sess.gated_spans.gate(sym::box_patterns, box_span.to(self.prev_token.span));
@@ -1093,7 +1118,7 @@ impl<'a> Parser<'a> {
.emit();
}
- fn parse_pat_field(&mut self, lo: Span, attrs: Vec<Attribute>) -> PResult<'a, PatField> {
+ fn parse_pat_field(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, PatField> {
// Check if a colon exists one ahead. This means we're parsing a fieldname.
let hi;
let (subpat, fieldname, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) {
@@ -1117,14 +1142,12 @@ impl<'a> Parser<'a> {
let fieldname = self.parse_field_name()?;
hi = self.prev_token.span;
- let bind_type = match (is_ref, is_mut) {
- (true, true) => BindingMode::ByRef(Mutability::Mut),
- (true, false) => BindingMode::ByRef(Mutability::Not),
- (false, true) => BindingMode::ByValue(Mutability::Mut),
- (false, false) => BindingMode::ByValue(Mutability::Not),
+ let mutability = match is_mut {
+ false => Mutability::Not,
+ true => Mutability::Mut,
};
-
- let fieldpat = self.mk_pat_ident(boxed_span.to(hi), bind_type, fieldname);
+ let ann = BindingAnnotation(ByRef::from(is_ref), mutability);
+ let fieldpat = self.mk_pat_ident(boxed_span.to(hi), ann, fieldname);
let subpat =
if is_box { self.mk_pat(lo.to(hi), PatKind::Box(fieldpat)) } else { fieldpat };
(subpat, fieldname, true)
@@ -1134,15 +1157,15 @@ impl<'a> Parser<'a> {
ident: fieldname,
pat: subpat,
is_shorthand,
- attrs: attrs.into(),
+ attrs,
id: ast::DUMMY_NODE_ID,
span: lo.to(hi),
is_placeholder: false,
})
}
- pub(super) fn mk_pat_ident(&self, span: Span, bm: BindingMode, ident: Ident) -> P<Pat> {
- self.mk_pat(span, PatKind::Ident(bm, ident, None))
+ pub(super) fn mk_pat_ident(&self, span: Span, ann: BindingAnnotation, ident: Ident) -> P<Pat> {
+ self.mk_pat(span, PatKind::Ident(ann, ident, None))
}
pub(super) fn mk_pat(&self, span: Span, kind: PatKind) -> P<Pat> {
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index 5cf1758c3..fdc1af27f 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -13,7 +13,6 @@ use rustc_span::source_map::{BytePos, Span};
use rustc_span::symbol::{kw, sym, Ident};
use std::mem;
-use tracing::debug;
/// Specifies how to parse a path.
#[derive(Copy, Clone, PartialEq)]
@@ -527,7 +526,7 @@ impl<'a> Parser<'a> {
Ok(ident_gen_args) => ident_gen_args,
Err(()) => return Ok(Some(AngleBracketedArg::Arg(arg))),
};
- if binder.is_some() {
+ if binder {
// FIXME(compiler-errors): this could be improved by suggesting lifting
// this up to the trait, at least before this becomes real syntax.
// e.g. `Trait<for<'a> Assoc = Ty>` -> `for<'a> Trait<Assoc = Ty>`
@@ -652,12 +651,7 @@ impl<'a> Parser<'a> {
pub(super) fn parse_const_arg(&mut self) -> PResult<'a, AnonConst> {
// Parse const argument.
let value = if let token::OpenDelim(Delimiter::Brace) = self.token.kind {
- self.parse_block_expr(
- None,
- self.token.span,
- BlockCheckMode::Default,
- ast::AttrVec::new(),
- )?
+ self.parse_block_expr(None, self.token.span, BlockCheckMode::Default)?
} else {
self.handle_unambiguous_unbraced_const_arg()?
};
@@ -725,28 +719,24 @@ impl<'a> Parser<'a> {
/// Given a arg inside of generics, we try to destructure it as if it were the LHS in
/// `LHS = ...`, i.e. an associated type binding.
- /// This returns (optionally, if they are present) any `for<'a, 'b>` binder args, the
+ /// This returns a bool indicating if there are any `for<'a, 'b>` binder args, the
/// identifier, and any GAT arguments.
fn get_ident_from_generic_arg(
&self,
gen_arg: &GenericArg,
- ) -> Result<(Option<Vec<ast::GenericParam>>, Ident, Option<GenericArgs>), ()> {
+ ) -> Result<(bool, Ident, Option<GenericArgs>), ()> {
if let GenericArg::Type(ty) = gen_arg {
if let ast::TyKind::Path(qself, path) = &ty.kind
&& qself.is_none()
&& let [seg] = path.segments.as_slice()
{
- return Ok((None, seg.ident, seg.args.as_deref().cloned()));
+ return Ok((false, seg.ident, seg.args.as_deref().cloned()));
} else if let ast::TyKind::TraitObject(bounds, ast::TraitObjectSyntax::None) = &ty.kind
&& let [ast::GenericBound::Trait(trait_ref, ast::TraitBoundModifier::None)] =
bounds.as_slice()
&& let [seg] = trait_ref.trait_ref.path.segments.as_slice()
{
- return Ok((
- Some(trait_ref.bound_generic_params.clone()),
- seg.ident,
- seg.args.as_deref().cloned(),
- ));
+ return Ok((true, seg.ident, seg.args.as_deref().cloned()));
}
}
Err(())
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 51bd9d2d3..12753c678 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -1,5 +1,5 @@
-use super::attr::DEFAULT_INNER_ATTR_FORBIDDEN;
-use super::diagnostics::{AttemptLocalParseRecovery, Error};
+use super::attr::InnerAttrForbiddenReason;
+use super::diagnostics::AttemptLocalParseRecovery;
use super::expr::LhsExpr;
use super::pat::RecoverComma;
use super::path::PathStyle;
@@ -7,6 +7,12 @@ use super::TrailingToken;
use super::{
AttrWrapper, BlockMode, FnParseMode, ForceCollect, Parser, Restrictions, SemiColonMode,
};
+use crate::errors::{
+ AssignmentElseNotAllowed, CompoundAssignmentExpressionInLet, ConstLetMutuallyExclusive,
+ DocCommentDoesNotDocumentAnything, ExpectedStatementAfterOuterAttr, InvalidCurlyInLetElse,
+ InvalidExpressionInLetElse, InvalidVariableDeclaration, InvalidVariableDeclarationSub,
+ WrapExpressionInParentheses,
+};
use crate::maybe_whole;
use rustc_ast as ast;
@@ -34,7 +40,7 @@ impl<'a> Parser<'a> {
}))
}
- /// If `force_capture` is true, forces collection of tokens regardless of whether
+ /// If `force_collect` is [`ForceCollect::Yes`], forces collection of tokens regardless of whether
/// or not we have attributes
pub(crate) fn parse_stmt_without_recovery(
&mut self,
@@ -55,18 +61,25 @@ impl<'a> Parser<'a> {
return Ok(Some(stmt.into_inner()));
}
+ if self.token.is_keyword(kw::Mut) && self.is_keyword_ahead(1, &[kw::Let]) {
+ self.bump();
+ let mut_let_span = lo.to(self.token.span);
+ self.sess.emit_err(InvalidVariableDeclaration {
+ span: mut_let_span,
+ sub: InvalidVariableDeclarationSub::SwitchMutLetOrder(mut_let_span),
+ });
+ }
+
Ok(Some(if self.token.is_keyword(kw::Let) {
self.parse_local_mk(lo, attrs, capture_semi, force_collect)?
} else if self.is_kw_followed_by_ident(kw::Mut) {
- self.recover_stmt_local(lo, attrs, "missing keyword", "let mut")?
+ self.recover_stmt_local(lo, attrs, InvalidVariableDeclarationSub::MissingLet)?
} else if self.is_kw_followed_by_ident(kw::Auto) {
self.bump(); // `auto`
- let msg = "write `let` instead of `auto` to introduce a new variable";
- self.recover_stmt_local(lo, attrs, msg, "let")?
+ self.recover_stmt_local(lo, attrs, InvalidVariableDeclarationSub::UseLetNotAuto)?
} else if self.is_kw_followed_by_ident(sym::var) {
self.bump(); // `var`
- let msg = "write `let` instead of `var` to introduce a new variable";
- self.recover_stmt_local(lo, attrs, msg, "let")?
+ self.recover_stmt_local(lo, attrs, InvalidVariableDeclarationSub::UseLetNotVar)?
} else if self.check_path() && !self.token.is_qpath_start() && !self.is_path_start_item() {
// We have avoided contextual keywords like `union`, items with `crate` visibility,
// or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something
@@ -103,11 +116,7 @@ impl<'a> Parser<'a> {
let bl = self.parse_block()?;
// Destructuring assignment ... else.
// This is not allowed, but point it out in a nice way.
- let mut err = self.struct_span_err(
- e.span.to(bl.span),
- "<assignment> ... else { ... } is not allowed",
- );
- err.emit();
+ self.sess.emit_err(AssignmentElseNotAllowed { span: e.span.to(bl.span) });
}
self.mk_stmt(lo.to(e.span), StmtKind::Expr(e))
} else {
@@ -121,7 +130,7 @@ impl<'a> Parser<'a> {
let path = this.parse_path(PathStyle::Expr)?;
if this.eat(&token::Not) {
- let stmt_mac = this.parse_stmt_mac(lo, attrs.into(), path)?;
+ let stmt_mac = this.parse_stmt_mac(lo, attrs, path)?;
if this.token == token::Semi {
return Ok((stmt_mac, TrailingToken::Semi));
} else {
@@ -130,10 +139,10 @@ impl<'a> Parser<'a> {
}
let expr = if this.eat(&token::OpenDelim(Delimiter::Brace)) {
- this.parse_struct_expr(None, path, AttrVec::new(), true)?
+ this.parse_struct_expr(None, path, true)?
} else {
let hi = this.prev_token.span;
- this.mk_expr(lo.to(hi), ExprKind::Path(None, path), AttrVec::new())
+ this.mk_expr(lo.to(hi), ExprKind::Path(None, path))
};
let expr = this.with_res(Restrictions::STMT_EXPR, |this| {
@@ -168,7 +177,7 @@ impl<'a> Parser<'a> {
None => unreachable!(),
};
- let mac = MacCall { path, args, prior_type_ascription: self.last_type_ascription };
+ let mac = P(MacCall { path, args, prior_type_ascription: self.last_type_ascription });
let kind = if (style == MacStmtStyle::Braces
&& self.token != token::Dot
@@ -179,9 +188,9 @@ impl<'a> Parser<'a> {
StmtKind::MacCall(P(MacCallStmt { mac, style, attrs, tokens: None }))
} else {
// Since none of the above applied, this is an expression statement macro.
- let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac), AttrVec::new());
+ let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac));
let e = self.maybe_recover_from_bad_qpath(e)?;
- let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?;
+ let e = self.parse_dot_or_call_expr_with(e, lo, attrs)?;
let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
StmtKind::Expr(e)
};
@@ -193,9 +202,12 @@ impl<'a> Parser<'a> {
fn error_outer_attrs(&self, attrs: &[Attribute]) {
if let [.., last] = attrs {
if last.is_doc_comment() {
- self.span_err(last.span, Error::UselessDocComment).emit();
+ self.sess.emit_err(DocCommentDoesNotDocumentAnything {
+ span: last.span,
+ missing_comma: None,
+ });
} else if attrs.iter().any(|a| a.style == AttrStyle::Outer) {
- self.struct_span_err(last.span, "expected statement after outer attribute").emit();
+ self.sess.emit_err(ExpectedStatementAfterOuterAttr { span: last.span });
}
}
}
@@ -204,13 +216,10 @@ impl<'a> Parser<'a> {
&mut self,
lo: Span,
attrs: AttrWrapper,
- msg: &str,
- sugg: &str,
+ subdiagnostic: fn(Span) -> InvalidVariableDeclarationSub,
) -> PResult<'a, Stmt> {
let stmt = self.recover_local_after_let(lo, attrs)?;
- self.struct_span_err(lo, "invalid variable declaration")
- .span_suggestion(lo, msg, sugg, Applicability::MachineApplicable)
- .emit();
+ self.sess.emit_err(InvalidVariableDeclaration { span: lo, sub: subdiagnostic(lo) });
Ok(stmt)
}
@@ -223,7 +232,7 @@ impl<'a> Parser<'a> {
) -> PResult<'a, Stmt> {
self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| {
this.expect_keyword(kw::Let)?;
- let local = this.parse_local(attrs.into())?;
+ let local = this.parse_local(attrs)?;
let trailing = if capture_semi && this.token.kind == token::Semi {
TrailingToken::Semi
} else {
@@ -235,7 +244,7 @@ impl<'a> Parser<'a> {
fn recover_local_after_let(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> {
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
- let local = this.parse_local(attrs.into())?;
+ let local = this.parse_local(attrs)?;
// FIXME - maybe capture semicolon in recovery?
Ok((
this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Local(local)),
@@ -247,6 +256,12 @@ impl<'a> Parser<'a> {
/// Parses a local variable declaration.
fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P<Local>> {
let lo = self.prev_token.span;
+
+ if self.token.is_keyword(kw::Const) && self.look_ahead(1, |t| t.is_ident()) {
+ self.sess.emit_err(ConstLetMutuallyExclusive { span: lo.to(self.token.span) });
+ self.bump();
+ }
+
let (pat, colon) = self.parse_pat_before_ty(None, RecoverComma::Yes, "`let` bindings")?;
let (err, ty) = if colon {
@@ -341,44 +356,27 @@ impl<'a> Parser<'a> {
fn check_let_else_init_bool_expr(&self, init: &ast::Expr) {
if let ast::ExprKind::Binary(op, ..) = init.kind {
if op.node.lazy() {
- let suggs = vec![
- (init.span.shrink_to_lo(), "(".to_string()),
- (init.span.shrink_to_hi(), ")".to_string()),
- ];
- self.struct_span_err(
- init.span,
- &format!(
- "a `{}` expression cannot be directly assigned in `let...else`",
- op.node.to_string()
- ),
- )
- .multipart_suggestion(
- "wrap the expression in parentheses",
- suggs,
- Applicability::MachineApplicable,
- )
- .emit();
+ self.sess.emit_err(InvalidExpressionInLetElse {
+ span: init.span,
+ operator: op.node.to_string(),
+ sugg: WrapExpressionInParentheses {
+ left: init.span.shrink_to_lo(),
+ right: init.span.shrink_to_hi(),
+ },
+ });
}
}
}
fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) {
if let Some(trailing) = classify::expr_trailing_brace(init) {
- let err_span = trailing.span.with_lo(trailing.span.hi() - BytePos(1));
- let suggs = vec![
- (trailing.span.shrink_to_lo(), "(".to_string()),
- (trailing.span.shrink_to_hi(), ")".to_string()),
- ];
- self.struct_span_err(
- err_span,
- "right curly brace `}` before `else` in a `let...else` statement not allowed",
- )
- .multipart_suggestion(
- "try wrapping the expression in parentheses",
- suggs,
- Applicability::MachineApplicable,
- )
- .emit();
+ self.sess.emit_err(InvalidCurlyInLetElse {
+ span: trailing.span.with_lo(trailing.span.hi() - BytePos(1)),
+ sugg: WrapExpressionInParentheses {
+ left: trailing.span.shrink_to_lo(),
+ right: trailing.span.shrink_to_hi(),
+ },
+ });
}
}
@@ -387,18 +385,7 @@ impl<'a> Parser<'a> {
let eq_consumed = match self.token.kind {
token::BinOpEq(..) => {
// Recover `let x <op>= 1` as `let x = 1`
- self.struct_span_err(
- self.token.span,
- "can't reassign to an uninitialized variable",
- )
- .span_suggestion_short(
- self.token.span,
- "initialize the variable",
- "=",
- Applicability::MaybeIncorrect,
- )
- .help("if you meant to overwrite, remove the `let` binding")
- .emit();
+ self.sess.emit_err(CompoundAssignmentExpressionInLet { span: self.token.span });
self.bump();
true
}
@@ -412,7 +399,12 @@ impl<'a> Parser<'a> {
pub(super) fn parse_block(&mut self) -> PResult<'a, P<Block>> {
let (attrs, block) = self.parse_inner_attrs_and_block()?;
if let [.., last] = &*attrs {
- self.error_on_forbidden_inner_attr(last.span, DEFAULT_INNER_ATTR_FORBIDDEN);
+ self.error_on_forbidden_inner_attr(
+ last.span,
+ super::attr::InnerAttrPolicy::Forbidden(Some(
+ InnerAttrForbiddenReason::InCodeBlock,
+ )),
+ );
}
Ok(block)
}
@@ -487,9 +479,7 @@ impl<'a> Parser<'a> {
}
/// Parses a block. Inner attributes are allowed.
- pub(super) fn parse_inner_attrs_and_block(
- &mut self,
- ) -> PResult<'a, (Vec<Attribute>, P<Block>)> {
+ pub(super) fn parse_inner_attrs_and_block(&mut self) -> PResult<'a, (AttrVec, P<Block>)> {
self.parse_block_common(self.token.span, BlockCheckMode::Default)
}
@@ -498,8 +488,8 @@ impl<'a> Parser<'a> {
&mut self,
lo: Span,
blk_mode: BlockCheckMode,
- ) -> PResult<'a, (Vec<Attribute>, P<Block>)> {
- maybe_whole!(self, NtBlock, |x| (Vec::new(), x));
+ ) -> PResult<'a, (AttrVec, P<Block>)> {
+ maybe_whole!(self, NtBlock, |x| (AttrVec::new(), x));
self.maybe_recover_unexpected_block_label();
if !self.eat(&token::OpenDelim(Delimiter::Brace)) {
@@ -563,39 +553,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 31b40a83e..2a8512acf 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -397,10 +397,13 @@ impl<'a> Parser<'a> {
fn parse_ty_ptr(&mut self) -> PResult<'a, TyKind> {
let mutbl = self.parse_const_or_mut().unwrap_or_else(|| {
let span = self.prev_token.span;
- let msg = "expected mut or const in raw pointer type";
- self.struct_span_err(span, msg)
- .span_label(span, msg)
- .help("use `*mut T` or `*const T` as appropriate")
+ self.struct_span_err(span, "expected `mut` or `const` keyword in raw pointer type")
+ .span_suggestions(
+ span.shrink_to_hi(),
+ "add `mut` or `const` here",
+ ["mut ".to_string(), "const ".to_string()].into_iter(),
+ Applicability::HasPlaceholders,
+ )
.emit();
Mutability::Not
});
@@ -567,7 +570,8 @@ impl<'a> Parser<'a> {
self.check_keyword(kw::Dyn)
&& (!self.token.uninterpolated_span().rust_2015()
|| self.look_ahead(1, |t| {
- t.can_begin_bound() && !can_continue_type_after_non_fn_ident(t)
+ (t.can_begin_bound() || t.kind == TokenKind::BinOp(token::Star))
+ && !can_continue_type_after_non_fn_ident(t)
}))
}
@@ -576,10 +580,18 @@ impl<'a> Parser<'a> {
/// Note that this does *not* parse bare trait objects.
fn parse_dyn_ty(&mut self, impl_dyn_multi: &mut bool) -> PResult<'a, TyKind> {
self.bump(); // `dyn`
+
+ // parse dyn* types
+ let syntax = if self.eat(&TokenKind::BinOp(token::Star)) {
+ TraitObjectSyntax::DynStar
+ } else {
+ TraitObjectSyntax::Dyn
+ };
+
// Always parse bounds greedily for better error recovery.
let bounds = self.parse_generic_bounds(None)?;
*impl_dyn_multi = bounds.len() > 1 || self.prev_token.kind == TokenKind::BinOp(token::Plus);
- Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::Dyn))
+ Ok(TyKind::TraitObject(bounds, syntax))
}
/// Parses a type starting with a path.
@@ -598,11 +610,11 @@ impl<'a> Parser<'a> {
let path = self.parse_path_inner(PathStyle::Type, ty_generics)?;
if self.eat(&token::Not) {
// Macro invocation in type position
- Ok(TyKind::MacCall(MacCall {
+ Ok(TyKind::MacCall(P(MacCall {
path,
args: self.parse_mac_args()?,
prior_type_ascription: self.last_type_ascription,
- }))
+ })))
} else if allow_plus == AllowPlus::Yes && self.check_plus() {
// `Trait1 + Trait2 + 'a`
self.parse_remaining_bounds_path(Vec::new(), path, lo, true)
@@ -640,7 +652,13 @@ impl<'a> Parser<'a> {
let mut bounds = Vec::new();
let mut negative_bounds = Vec::new();
- while self.can_begin_bound() || self.token.is_keyword(kw::Dyn) {
+ while self.can_begin_bound()
+ // Continue even if we find a keyword.
+ // This is necessary for error recover on, for example, `impl fn()`.
+ //
+ // The only keyword that can go after generic bounds is `where`, so stop if it's it.
+ || (self.token.is_reserved_ident() && !self.token.is_keyword(kw::Where))
+ {
if self.token.is_keyword(kw::Dyn) {
// Account for `&dyn Trait + dyn Other`.
self.struct_span_err(self.token.span, "invalid `dyn` keyword")
@@ -804,6 +822,20 @@ impl<'a> Parser<'a> {
let span = tilde.to(self.prev_token.span);
self.sess.gated_spans.gate(sym::const_trait_impl, span);
Some(span)
+ } else if self.eat_keyword(kw::Const) {
+ let span = self.prev_token.span;
+ self.sess.gated_spans.gate(sym::const_trait_impl, span);
+
+ self.struct_span_err(span, "const bounds must start with `~`")
+ .span_suggestion(
+ span.shrink_to_lo(),
+ "add `~`",
+ "~",
+ Applicability::MachineApplicable,
+ )
+ .emit();
+
+ Some(span)
} else {
None
};
diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs
index 4890fade5..1394993ab 100644
--- a/compiler/rustc_parse_format/src/lib.rs
+++ b/compiler/rustc_parse_format/src/lib.rs
@@ -9,6 +9,8 @@
html_playground_url = "https://play.rust-lang.org/",
test(attr(deny(warnings)))
)]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
// We want to be able to build this crate with a stable compiler, so no
// `#![feature]` attributes should be added.
@@ -165,6 +167,8 @@ pub enum Count<'a> {
CountIsName(&'a str, InnerSpan),
/// The count is specified by the argument at the given index.
CountIsParam(usize),
+ /// The count is specified by a star (like in `{:.*}`) that refers to the argument at the given index.
+ CountIsStar(usize),
/// The count is implied and cannot be explicitly specified.
CountImplied,
}
@@ -220,7 +224,7 @@ impl<'a> Iterator for Parser<'a> {
'{' => {
let curr_last_brace = self.last_opening_brace;
let byte_pos = self.to_span_index(pos);
- let lbrace_end = InnerOffset(byte_pos.0 + 1);
+ let lbrace_end = self.to_span_index(pos + 1);
self.last_opening_brace = Some(byte_pos.to(lbrace_end));
self.cur.next();
if self.consume('{') {
@@ -262,9 +266,7 @@ impl<'a> Iterator for Parser<'a> {
}
} else {
if self.is_literal {
- let start = self.to_span_index(self.cur_line_start);
- let end = self.to_span_index(self.input.len());
- let span = start.to(end);
+ let span = self.span(self.cur_line_start, self.input.len());
if self.line_spans.last() != Some(&span) {
self.line_spans.push(span);
}
@@ -382,6 +384,12 @@ impl<'a> Parser<'a> {
InnerOffset(raw + pos + 1)
}
+ fn span(&self, start_pos: usize, end_pos: usize) -> InnerSpan {
+ let start = self.to_span_index(start_pos);
+ let end = self.to_span_index(end_pos);
+ start.to(end)
+ }
+
/// Forces consumption of the specified character. If the character is not
/// found, an error is emitted.
fn must_consume(&mut self, c: char) -> Option<usize> {
@@ -470,9 +478,7 @@ impl<'a> Parser<'a> {
return &self.input[start..pos];
}
'\n' if self.is_literal => {
- let start = self.to_span_index(self.cur_line_start);
- let end = self.to_span_index(pos);
- self.line_spans.push(start.to(end));
+ self.line_spans.push(self.span(self.cur_line_start, pos));
self.cur_line_start = pos + 1;
self.cur.next();
}
@@ -535,6 +541,10 @@ impl<'a> Parser<'a> {
}
}
+ fn current_pos(&mut self) -> usize {
+ if let Some(&(pos, _)) = self.cur.peek() { pos } else { self.input.len() }
+ }
+
/// Parses a format specifier at the current position, returning all of the
/// relevant information in the `FormatSpec` struct.
fn format(&mut self) -> FormatSpec<'a> {
@@ -588,39 +598,37 @@ impl<'a> Parser<'a> {
// no '0' flag and '0$' as the width instead.
if let Some(end) = self.consume_pos('$') {
spec.width = CountIsParam(0);
-
- if let Some((pos, _)) = self.cur.peek().cloned() {
- spec.width_span = Some(self.to_span_index(pos - 2).to(self.to_span_index(pos)));
- }
+ spec.width_span = Some(self.span(end - 1, end + 1));
havewidth = true;
- spec.width_span = Some(self.to_span_index(end - 1).to(self.to_span_index(end + 1)));
} else {
spec.flags |= 1 << (FlagSignAwareZeroPad as u32);
}
}
+
if !havewidth {
- let width_span_start = if let Some((pos, _)) = self.cur.peek() { *pos } else { 0 };
- let (w, sp) = self.count(width_span_start);
- spec.width = w;
- spec.width_span = sp;
+ let start = self.current_pos();
+ spec.width = self.count(start);
+ if spec.width != CountImplied {
+ let end = self.current_pos();
+ spec.width_span = Some(self.span(start, end));
+ }
}
if let Some(start) = self.consume_pos('.') {
- if let Some(end) = self.consume_pos('*') {
+ if self.consume('*') {
// Resolve `CountIsNextParam`.
// We can do this immediately as `position` is resolved later.
let i = self.curarg;
self.curarg += 1;
- spec.precision = CountIsParam(i);
- spec.precision_span =
- Some(self.to_span_index(start).to(self.to_span_index(end + 1)));
+ spec.precision = CountIsStar(i);
} else {
- let (p, sp) = self.count(start);
- spec.precision = p;
- spec.precision_span = sp;
+ spec.precision = self.count(start + 1);
}
+ let end = self.current_pos();
+ spec.precision_span = Some(self.span(start, end));
}
- let ty_span_start = self.cur.peek().map(|(pos, _)| *pos);
+
+ let ty_span_start = self.current_pos();
// Optional radix followed by the actual format specifier
if self.consume('x') {
if self.consume('?') {
@@ -640,11 +648,9 @@ impl<'a> Parser<'a> {
spec.ty = "?";
} else {
spec.ty = self.word();
- let ty_span_end = self.cur.peek().map(|(pos, _)| *pos);
if !spec.ty.is_empty() {
- spec.ty_span = ty_span_start
- .and_then(|s| ty_span_end.map(|e| (s, e)))
- .map(|(start, end)| self.to_span_index(start).to(self.to_span_index(end)));
+ let ty_span_end = self.current_pos();
+ spec.ty_span = Some(self.span(ty_span_start, ty_span_end));
}
}
spec
@@ -668,13 +674,11 @@ impl<'a> Parser<'a> {
return spec;
}
- let ty_span_start = self.cur.peek().map(|(pos, _)| *pos);
+ let ty_span_start = self.current_pos();
spec.ty = self.word();
- let ty_span_end = self.cur.peek().map(|(pos, _)| *pos);
if !spec.ty.is_empty() {
- spec.ty_span = ty_span_start
- .and_then(|s| ty_span_end.map(|e| (s, e)))
- .map(|(start, end)| self.to_span_index(start).to(self.to_span_index(end)));
+ let ty_span_end = self.current_pos();
+ spec.ty_span = Some(self.span(ty_span_start, ty_span_end));
}
spec
@@ -683,26 +687,21 @@ impl<'a> Parser<'a> {
/// Parses a `Count` parameter at the current position. This does not check
/// for 'CountIsNextParam' because that is only used in precision, not
/// width.
- fn count(&mut self, start: usize) -> (Count<'a>, Option<InnerSpan>) {
+ fn count(&mut self, start: usize) -> Count<'a> {
if let Some(i) = self.integer() {
- if let Some(end) = self.consume_pos('$') {
- let span = self.to_span_index(start).to(self.to_span_index(end + 1));
- (CountIsParam(i), Some(span))
- } else {
- (CountIs(i), None)
- }
+ if self.consume('$') { CountIsParam(i) } else { CountIs(i) }
} else {
let tmp = self.cur.clone();
let word = self.word();
if word.is_empty() {
self.cur = tmp;
- (CountImplied, None)
+ CountImplied
} else if let Some(end) = self.consume_pos('$') {
- let span = self.to_span_index(start + 1).to(self.to_span_index(end));
- (CountIsName(word, span), None)
+ let name_span = self.span(start, end);
+ CountIsName(word, name_span)
} else {
self.cur = tmp;
- (CountImplied, None)
+ CountImplied
}
}
}
@@ -735,26 +734,46 @@ impl<'a> Parser<'a> {
"invalid argument name `_`",
"invalid argument name",
"argument name cannot be a single underscore",
- self.to_span_index(start).to(self.to_span_index(end)),
+ self.span(start, end),
);
}
word
}
- /// Optionally parses an integer at the current position. This doesn't deal
- /// with overflow at all, it's just accumulating digits.
fn integer(&mut self) -> Option<usize> {
- let mut cur = 0;
+ let mut cur: usize = 0;
let mut found = false;
+ let mut overflow = false;
+ let start = self.current_pos();
while let Some(&(_, c)) = self.cur.peek() {
if let Some(i) = c.to_digit(10) {
- cur = cur * 10 + i as usize;
+ let (tmp, mul_overflow) = cur.overflowing_mul(10);
+ let (tmp, add_overflow) = tmp.overflowing_add(i as usize);
+ if mul_overflow || add_overflow {
+ overflow = true;
+ }
+ cur = tmp;
found = true;
self.cur.next();
} else {
break;
}
}
+
+ if overflow {
+ let end = self.current_pos();
+ let overflowed_int = &self.input[start..end];
+ self.err(
+ format!(
+ "integer `{}` does not fit into the type `usize` whose range is `0..={}`",
+ overflowed_int,
+ usize::MAX
+ ),
+ "integer out of range for `usize`",
+ self.span(start, end),
+ );
+ }
+
if found { Some(cur) } else { None }
}
diff --git a/compiler/rustc_parse_format/src/tests.rs b/compiler/rustc_parse_format/src/tests.rs
index 578530696..3f9cb149b 100644
--- a/compiler/rustc_parse_format/src/tests.rs
+++ b/compiler/rustc_parse_format/src/tests.rs
@@ -1,5 +1,6 @@
use super::*;
+#[track_caller]
fn same(fmt: &'static str, p: &[Piece<'static>]) {
let parser = Parser::new(fmt, None, None, false, ParseMode::Format);
assert_eq!(parser.collect::<Vec<Piece<'static>>>(), p);
@@ -57,6 +58,21 @@ fn invalid06() {
}
#[test]
+fn invalid_position() {
+ musterr("{18446744073709551616}");
+}
+
+#[test]
+fn invalid_width() {
+ musterr("{:18446744073709551616}");
+}
+
+#[test]
+fn invalid_precision() {
+ musterr("{:.18446744073709551616}");
+}
+
+#[test]
fn format_nothing() {
same(
"{}",
@@ -190,9 +206,9 @@ fn format_counts() {
align: AlignUnknown,
flags: 0,
precision: CountImplied,
- width: CountIs(10),
precision_span: None,
- width_span: None,
+ width: CountIs(10),
+ width_span: Some(InnerSpan { start: 3, end: 5 }),
ty: "x",
ty_span: None,
},
@@ -208,9 +224,9 @@ fn format_counts() {
align: AlignUnknown,
flags: 0,
precision: CountIs(10),
+ precision_span: Some(InnerSpan { start: 6, end: 9 }),
width: CountIsParam(10),
- precision_span: None,
- width_span: Some(InnerSpan::new(3, 6)),
+ width_span: Some(InnerSpan { start: 3, end: 6 }),
ty: "x",
ty_span: None,
},
@@ -226,9 +242,9 @@ fn format_counts() {
align: AlignUnknown,
flags: 0,
precision: CountIs(10),
+ precision_span: Some(InnerSpan { start: 6, end: 9 }),
width: CountIsParam(0),
- precision_span: None,
- width_span: Some(InnerSpan::new(4, 6)),
+ width_span: Some(InnerSpan { start: 4, end: 6 }),
ty: "x",
ty_span: None,
},
@@ -243,9 +259,9 @@ fn format_counts() {
fill: None,
align: AlignUnknown,
flags: 0,
- precision: CountIsParam(0),
+ precision: CountIsStar(0),
+ precision_span: Some(InnerSpan { start: 3, end: 5 }),
width: CountImplied,
- precision_span: Some(InnerSpan::new(3, 5)),
width_span: None,
ty: "x",
ty_span: None,
@@ -279,15 +295,33 @@ fn format_counts() {
fill: None,
align: AlignUnknown,
flags: 0,
- precision: CountIsName("b", InnerSpan::new(6, 7)),
- width: CountIsName("a", InnerSpan::new(4, 4)),
- precision_span: None,
- width_span: None,
+ precision: CountIsName("b", InnerSpan { start: 6, end: 7 }),
+ precision_span: Some(InnerSpan { start: 5, end: 8 }),
+ width: CountIsName("a", InnerSpan { start: 3, end: 4 }),
+ width_span: Some(InnerSpan { start: 3, end: 5 }),
ty: "?",
ty_span: None,
},
})],
);
+ same(
+ "{:.4}",
+ &[NextArgument(Argument {
+ position: ArgumentImplicitlyIs(0),
+ position_span: InnerSpan { start: 2, end: 2 },
+ format: FormatSpec {
+ fill: None,
+ align: AlignUnknown,
+ flags: 0,
+ precision: CountIs(4),
+ precision_span: Some(InnerSpan { start: 3, end: 5 }),
+ width: CountImplied,
+ width_span: None,
+ ty: "",
+ ty_span: None,
+ },
+ })],
+ )
}
#[test]
fn format_flags() {