diff options
Diffstat (limited to 'compiler/rustc_parse/src')
| -rw-r--r-- | compiler/rustc_parse/src/errors.rs | 622 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/attr_wrapper.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/diagnostics.rs | 11 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 53 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/generics.rs | 29 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/item.rs | 378 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/mod.rs | 123 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/nonterminal.rs | 22 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/pat.rs | 346 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/stmt.rs | 5 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/ty.rs | 190 |
11 files changed, 1070 insertions, 711 deletions
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 054b41b478d..fd4333dbbec 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1,8 +1,11 @@ +use std::borrow::Cow; + use rustc_ast::token::Token; -use rustc_ast::Path; +use rustc_ast::{Path, Visibility}; use rustc_errors::{fluent, AddToDiagnostic, Applicability, EmissionGuarantee, IntoDiagnostic}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_session::errors::ExprParenthesesNeeded; +use rustc_span::edition::{Edition, LATEST_STABLE_EDITION}; use rustc_span::symbol::Ident; use rustc_span::{Span, Symbol}; @@ -351,7 +354,7 @@ pub(crate) enum IfExpressionMissingThenBlockSub { } #[derive(Subdiagnostic)] -#[help(parse_extra_if_in_let_else)] +#[suggestion(parse_extra_if_in_let_else, applicability = "maybe-incorrect", code = "")] pub(crate) struct IfExpressionLetSomeSub { #[primary_span] pub if_span: Span, @@ -667,13 +670,10 @@ pub(crate) struct InclusiveRangeExtraEquals { #[diag(parse_inclusive_range_match_arrow)] pub(crate) struct InclusiveRangeMatchArrow { #[primary_span] + pub arrow: Span, + #[label] pub span: Span, - #[suggestion( - suggestion_add_space, - style = "verbose", - code = " ", - applicability = "machine-applicable" - )] + #[suggestion(style = "verbose", code = " ", applicability = "machine-applicable")] pub after_pat: Span, } @@ -1330,3 +1330,609 @@ pub(crate) struct WhereClauseBeforeTupleStructBodySugg { #[suggestion_part(code = "")] pub right: Span, } + +#[derive(Diagnostic)] +#[diag(parse_async_fn_in_2015, code = "E0670")] +pub(crate) struct AsyncFnIn2015 { + #[primary_span] + #[label] + pub span: Span, + #[subdiagnostic] + pub help: HelpUseLatestEdition, +} + +#[derive(Subdiagnostic)] +#[label(parse_async_block_in_2015)] +pub(crate) struct AsyncBlockIn2015 { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_self_argument_pointer)] +pub(crate) struct SelfArgumentPointer { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_visibility_not_followed_by_item)] +#[help] +pub(crate) struct VisibilityNotFollowedByItem { + #[primary_span] + #[label] + pub span: Span, + pub vis: Visibility, +} + +#[derive(Diagnostic)] +#[diag(parse_default_not_followed_by_item)] +#[note] +pub(crate) struct DefaultNotFollowedByItem { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +pub(crate) enum MissingKeywordForItemDefinition { + #[diag(parse_missing_struct_for_struct_definition)] + Struct { + #[primary_span] + #[suggestion(style = "short", applicability = "maybe-incorrect", code = " struct ")] + span: Span, + ident: Ident, + }, + #[diag(parse_missing_fn_for_function_definition)] + Function { + #[primary_span] + #[suggestion(style = "short", applicability = "maybe-incorrect", code = " fn ")] + span: Span, + ident: Ident, + }, + #[diag(parse_missing_fn_for_method_definition)] + Method { + #[primary_span] + #[suggestion(style = "short", applicability = "maybe-incorrect", code = " fn ")] + span: Span, + ident: Ident, + }, + #[diag(parse_ambiguous_missing_keyword_for_item_definition)] + Ambiguous { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiag: Option<AmbiguousMissingKwForItemSub>, + }, +} + +#[derive(Subdiagnostic)] +pub(crate) enum AmbiguousMissingKwForItemSub { + #[suggestion(suggestion, applicability = "maybe-incorrect", code = "{snippet}!")] + SuggestMacro { + #[primary_span] + span: Span, + snippet: String, + }, + #[help(help)] + HelpMacro, +} + +#[derive(Diagnostic)] +#[diag(parse_missing_trait_in_trait_impl)] +pub(crate) struct MissingTraitInTraitImpl { + #[primary_span] + #[suggestion(suggestion_add_trait, code = " Trait ", applicability = "has-placeholders")] + pub span: Span, + #[suggestion(suggestion_remove_for, code = "", applicability = "maybe-incorrect")] + pub for_span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_missing_for_in_trait_impl)] +pub(crate) struct MissingForInTraitImpl { + #[primary_span] + #[suggestion(style = "short", code = " for ", applicability = "machine-applicable")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_expected_trait_in_trait_impl_found_type)] +pub(crate) struct ExpectedTraitInTraitImplFoundType { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_bounds_not_allowed_on_trait_aliases)] +pub(crate) struct BoundsNotAllowedOnTraitAliases { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_trait_alias_cannot_be_auto)] +pub(crate) struct TraitAliasCannotBeAuto { + #[primary_span] + #[label(parse_trait_alias_cannot_be_auto)] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_trait_alias_cannot_be_unsafe)] +pub(crate) struct TraitAliasCannotBeUnsafe { + #[primary_span] + #[label(parse_trait_alias_cannot_be_unsafe)] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_associated_static_item_not_allowed)] +pub(crate) struct AssociatedStaticItemNotAllowed { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_extern_crate_name_with_dashes)] +pub(crate) struct ExternCrateNameWithDashes { + #[primary_span] + #[label] + pub span: Span, + #[subdiagnostic] + pub sugg: ExternCrateNameWithDashesSugg, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(suggestion, applicability = "machine-applicable")] +pub(crate) struct ExternCrateNameWithDashesSugg { + #[suggestion_part(code = "_")] + pub dashes: Vec<Span>, +} + +#[derive(Diagnostic)] +#[diag(parse_extern_item_cannot_be_const)] +#[note] +pub(crate) struct ExternItemCannotBeConst { + #[primary_span] + pub ident_span: Span, + #[suggestion(code = "static ", applicability = "machine-applicable")] + pub const_span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_const_global_cannot_be_mutable)] +pub(crate) struct ConstGlobalCannotBeMutable { + #[primary_span] + #[label] + pub ident_span: Span, + #[suggestion(code = "static", applicability = "maybe-incorrect")] + pub const_span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_missing_const_type)] +pub(crate) struct MissingConstType { + #[primary_span] + #[suggestion(code = "{colon} <type>", applicability = "has-placeholders")] + pub span: Span, + + pub kind: &'static str, + pub colon: &'static str, +} + +#[derive(Diagnostic)] +#[diag(parse_enum_struct_mutually_exclusive)] +pub(crate) struct EnumStructMutuallyExclusive { + #[primary_span] + #[suggestion(code = "enum", applicability = "machine-applicable")] + pub span: Span, +} + +#[derive(Diagnostic)] +pub(crate) enum UnexpectedTokenAfterStructName { + #[diag(parse_unexpected_token_after_struct_name_found_reserved_identifier)] + ReservedIdentifier { + #[primary_span] + #[label(parse_unexpected_token_after_struct_name)] + span: Span, + token: Token, + }, + #[diag(parse_unexpected_token_after_struct_name_found_keyword)] + Keyword { + #[primary_span] + #[label(parse_unexpected_token_after_struct_name)] + span: Span, + token: Token, + }, + #[diag(parse_unexpected_token_after_struct_name_found_reserved_keyword)] + ReservedKeyword { + #[primary_span] + #[label(parse_unexpected_token_after_struct_name)] + span: Span, + token: Token, + }, + #[diag(parse_unexpected_token_after_struct_name_found_doc_comment)] + DocComment { + #[primary_span] + #[label(parse_unexpected_token_after_struct_name)] + span: Span, + token: Token, + }, + #[diag(parse_unexpected_token_after_struct_name_found_other)] + Other { + #[primary_span] + #[label(parse_unexpected_token_after_struct_name)] + span: Span, + token: Token, + }, +} + +impl UnexpectedTokenAfterStructName { + pub fn new(span: Span, token: Token) -> Self { + match TokenDescription::from_token(&token) { + Some(TokenDescription::ReservedIdentifier) => Self::ReservedIdentifier { span, token }, + Some(TokenDescription::Keyword) => Self::Keyword { span, token }, + Some(TokenDescription::ReservedKeyword) => Self::ReservedKeyword { span, token }, + Some(TokenDescription::DocComment) => Self::DocComment { span, token }, + None => Self::Other { span, token }, + } + } +} + +#[derive(Diagnostic)] +#[diag(parse_unexpected_self_in_generic_parameters)] +#[note] +pub(crate) struct UnexpectedSelfInGenericParameters { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_multiple_where_clauses)] +pub(crate) struct MultipleWhereClauses { + #[primary_span] + pub span: Span, + #[label] + pub previous: Span, + #[suggestion(style = "verbose", code = ",", applicability = "maybe-incorrect")] + pub between: Span, +} + +#[derive(Diagnostic)] +pub(crate) enum UnexpectedNonterminal { + #[diag(parse_nonterminal_expected_item_keyword)] + Item(#[primary_span] Span), + #[diag(parse_nonterminal_expected_statement)] + Statement(#[primary_span] Span), + #[diag(parse_nonterminal_expected_ident)] + Ident { + #[primary_span] + span: Span, + token: Token, + }, + #[diag(parse_nonterminal_expected_lifetime)] + Lifetime { + #[primary_span] + span: Span, + token: Token, + }, +} + +#[derive(Diagnostic)] +pub(crate) enum TopLevelOrPatternNotAllowed { + #[diag(parse_or_pattern_not_allowed_in_let_binding)] + LetBinding { + #[primary_span] + span: Span, + #[subdiagnostic] + sub: Option<TopLevelOrPatternNotAllowedSugg>, + }, + #[diag(parse_or_pattern_not_allowed_in_fn_parameters)] + FunctionParameter { + #[primary_span] + span: Span, + #[subdiagnostic] + sub: Option<TopLevelOrPatternNotAllowedSugg>, + }, +} + +#[derive(Subdiagnostic)] +pub(crate) enum TopLevelOrPatternNotAllowedSugg { + #[suggestion( + parse_sugg_remove_leading_vert_in_pattern, + code = "{pat}", + applicability = "machine-applicable" + )] + RemoveLeadingVert { + #[primary_span] + span: Span, + pat: String, + }, + #[suggestion( + parse_sugg_wrap_pattern_in_parens, + code = "({pat})", + applicability = "machine-applicable" + )] + WrapInParens { + #[primary_span] + span: Span, + pat: String, + }, +} + +#[derive(Diagnostic)] +#[diag(parse_unexpected_vert_vert_before_function_parameter)] +#[note(parse_note_pattern_alternatives_use_single_vert)] +pub(crate) struct UnexpectedVertVertBeforeFunctionParam { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_unexpected_vert_vert_in_pattern)] +pub(crate) struct UnexpectedVertVertInPattern { + #[primary_span] + #[suggestion(code = "|", applicability = "machine-applicable")] + pub span: Span, + #[label(parse_label_while_parsing_or_pattern_here)] + pub start: Option<Span>, +} + +#[derive(Diagnostic)] +#[diag(parse_trailing_vert_not_allowed)] +pub(crate) struct TrailingVertNotAllowed { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub span: Span, + #[label(parse_label_while_parsing_or_pattern_here)] + pub start: Option<Span>, + pub token: Token, + #[note(parse_note_pattern_alternatives_use_single_vert)] + pub note_double_vert: Option<()>, +} + +#[derive(Diagnostic)] +#[diag(parse_dotdotdot_rest_pattern)] +pub(crate) struct DotDotDotRestPattern { + #[primary_span] + #[suggestion(style = "short", code = "..", applicability = "machine-applicable")] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_pattern_on_wrong_side_of_at)] +pub(crate) struct PatternOnWrongSideOfAt { + #[primary_span] + #[suggestion(code = "{whole_pat}", applicability = "machine-applicable")] + pub whole_span: Span, + pub whole_pat: String, + #[label(label_pattern)] + pub pattern: Span, + #[label(label_binding)] + pub binding: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_expected_binding_left_of_at)] +#[note] +pub(crate) struct ExpectedBindingLeftOfAt { + #[primary_span] + pub whole_span: Span, + #[label(label_lhs)] + pub lhs: Span, + #[label(label_rhs)] + pub rhs: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_ambiguous_range_pattern)] +pub(crate) struct AmbiguousRangePattern { + #[primary_span] + #[suggestion(code = "({pat})", applicability = "maybe-incorrect")] + pub span: Span, + pub pat: String, +} + +#[derive(Diagnostic)] +#[diag(parse_unexpected_lifetime_in_pattern)] +pub(crate) struct UnexpectedLifetimeInPattern { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub span: Span, + pub symbol: Symbol, +} + +#[derive(Diagnostic)] +#[diag(parse_ref_mut_order_incorrect)] +pub(crate) struct RefMutOrderIncorrect { + #[primary_span] + #[suggestion(code = "ref mut", applicability = "machine-applicable")] + pub span: Span, +} + +#[derive(Diagnostic)] +pub(crate) enum InvalidMutInPattern { + #[diag(parse_mut_on_nested_ident_pattern)] + #[note(parse_note_mut_pattern_usage)] + NestedIdent { + #[primary_span] + #[suggestion(code = "{pat}", applicability = "machine-applicable")] + span: Span, + pat: String, + }, + #[diag(parse_mut_on_non_ident_pattern)] + #[note(parse_note_mut_pattern_usage)] + NonIdent { + #[primary_span] + #[suggestion(code = "{pat}", applicability = "machine-applicable")] + span: Span, + pat: String, + }, +} + +#[derive(Diagnostic)] +#[diag(parse_repeated_mut_in_pattern)] +pub(crate) struct RepeatedMutInPattern { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_dot_dot_dot_range_to_pattern_not_allowed)] +pub(crate) struct DotDotDotRangeToPatternNotAllowed { + #[primary_span] + #[suggestion(style = "short", code = "..=", applicability = "machine-applicable")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_enum_pattern_instead_of_identifier)] +pub(crate) struct EnumPatternInsteadOfIdentifier { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_dot_dot_dot_for_remaining_fields)] +pub(crate) struct DotDotDotForRemainingFields { + #[primary_span] + #[suggestion(code = "..", style = "verbose", applicability = "machine-applicable")] + pub span: Span, + pub token_str: Cow<'static, str>, +} + +#[derive(Diagnostic)] +#[diag(parse_expected_comma_after_pattern_field)] +pub(crate) struct ExpectedCommaAfterPatternField { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_return_types_use_thin_arrow)] +pub(crate) struct ReturnTypesUseThinArrow { + #[primary_span] + #[suggestion(style = "short", code = "->", applicability = "machine-applicable")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_need_plus_after_trait_object_lifetime)] +pub(crate) struct NeedPlusAfterTraitObjectLifetime { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_expected_mut_or_const_in_raw_pointer_type)] +pub(crate) struct ExpectedMutOrConstInRawPointerType { + #[primary_span] + pub span: Span, + #[suggestion(code("mut ", "const "), applicability = "has-placeholders")] + pub after_asterisk: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_lifetime_after_mut)] +pub(crate) struct LifetimeAfterMut { + #[primary_span] + pub span: Span, + #[suggestion(code = "&{snippet} mut", applicability = "maybe-incorrect")] + pub suggest_lifetime: Option<Span>, + pub snippet: String, +} + +#[derive(Diagnostic)] +#[diag(parse_dyn_after_mut)] +pub(crate) struct DynAfterMut { + #[primary_span] + #[suggestion(code = "&mut dyn", applicability = "machine-applicable")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_fn_pointer_cannot_be_const)] +pub(crate) struct FnPointerCannotBeConst { + #[primary_span] + pub span: Span, + #[suggestion(code = "", applicability = "maybe-incorrect")] + #[label] + pub qualifier: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_fn_pointer_cannot_be_async)] +pub(crate) struct FnPointerCannotBeAsync { + #[primary_span] + pub span: Span, + #[suggestion(code = "", applicability = "maybe-incorrect")] + #[label] + pub qualifier: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_nested_c_variadic_type, code = "E0743")] +pub(crate) struct NestedCVariadicType { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_invalid_dyn_keyword)] +#[help] +pub(crate) struct InvalidDynKeyword { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_negative_bounds_not_supported)] +pub(crate) struct NegativeBoundsNotSupported { + #[primary_span] + pub negative_bounds: Vec<Span>, + #[label] + pub last_span: Span, + #[subdiagnostic] + pub sub: Option<NegativeBoundsNotSupportedSugg>, +} + +#[derive(Subdiagnostic)] +#[suggestion( + suggestion, + style = "tool-only", + code = "{fixed}", + applicability = "machine-applicable" +)] +pub(crate) struct NegativeBoundsNotSupportedSugg { + #[primary_span] + pub bound_list: Span, + pub num_bounds: usize, + pub fixed: String, +} + +#[derive(Subdiagnostic)] +pub enum HelpUseLatestEdition { + #[help(parse_help_set_edition_cargo)] + #[note(parse_note_edition_guide)] + Cargo { edition: Edition }, + #[help(parse_help_set_edition_standalone)] + #[note(parse_note_edition_guide)] + Standalone { edition: Edition }, +} + +impl HelpUseLatestEdition { + pub fn new() -> Self { + let edition = LATEST_STABLE_EDITION; + if std::env::var_os("CARGO").is_some() { + Self::Cargo { edition } + } else { + Self::Standalone { edition } + } + } +} diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index b97f22417cb..dbd3b76786f 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -469,6 +469,6 @@ mod size_asserts { use rustc_data_structures::static_assert_size; // tidy-alphabetical-start static_assert_size!(AttrWrapper, 16); - static_assert_size!(LazyAttrTokenStreamImpl, 144); + static_assert_size!(LazyAttrTokenStreamImpl, 120); // tidy-alphabetical-end } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index f4c08031bcc..cd9d85b1d91 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2030,7 +2030,7 @@ impl<'a> Parser<'a> { } pub(super) fn recover_arg_parse(&mut self) -> PResult<'a, (P<ast::Pat>, P<ast::Ty>)> { - let pat = self.parse_pat_no_top_alt(Some("argument name"))?; + let pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName))?; self.expect(&token::Colon)?; let ty = self.parse_ty()?; @@ -2394,10 +2394,10 @@ impl<'a> Parser<'a> { /// 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_or_anon_enum( + pub(crate) fn maybe_recover_colon_colon_in_pat_typo( &mut self, mut first_pat: P<Pat>, - expected: Expected, + expected: Option<Expected>, ) -> P<Pat> { if token::Colon != self.token.kind { return first_pat; @@ -2435,8 +2435,9 @@ impl<'a> Parser<'a> { // Create error for "unexpected `:`". match self.expected_one_of_not_found(&[], &[]) { Err(mut err) => { - snapshot_pat.bump(); // Skip the `:`. - snapshot_type.bump(); // Skip the `:`. + // Skip the `:`. + snapshot_pat.bump(); + snapshot_type.bump(); match snapshot_pat.parse_pat_no_top_alt(expected) { Err(inner_err) => { inner_err.cancel(); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 701a2a3efcf..28347b137f3 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1,25 +1,26 @@ use super::diagnostics::SnapshotParser; -use super::pat::{CommaRecoveryMode, RecoverColon, RecoverComma, PARAM_EXPECTED}; +use super::pat::{CommaRecoveryMode, Expected, RecoverColon, RecoverComma}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{ AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions, SemiColonMode, SeqSep, TokenExpectType, TokenType, TrailingToken, }; use crate::errors::{ - ArrayBracketsInsteadOfSpaces, ArrayBracketsInsteadOfSpacesSugg, AsyncMoveOrderIncorrect, - BracesForStructLiteral, CatchAfterTry, CommaAfterBaseStruct, ComparisonInterpretedAsGeneric, - ComparisonOrShiftInterpretedAsGenericSugg, DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, - ExpectedElseBlock, ExpectedEqForLetExpr, ExpectedExpressionFoundLet, - FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart, FoundExprWouldBeStmt, - IfExpressionLetSomeSub, IfExpressionMissingCondition, IfExpressionMissingThenBlock, - IfExpressionMissingThenBlockSub, InvalidBlockMacroSegment, InvalidComparisonOperator, - InvalidComparisonOperatorSub, InvalidInterpolatedExpression, InvalidLiteralSuffixOnTupleIndex, - InvalidLogicalOperator, InvalidLogicalOperatorSub, LabeledLoopInBreak, LeadingPlusNotSupported, - LeftArrowOperator, LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, - MalformedLoopLabel, MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg, - MissingCommaAfterMatchArm, MissingDotDot, MissingInInForLoop, MissingInInForLoopSub, - MissingSemicolonBeforeArray, NoFieldsForFnCall, NotAsNegationOperator, - NotAsNegationOperatorSub, OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields, + ArrayBracketsInsteadOfSpaces, ArrayBracketsInsteadOfSpacesSugg, AsyncBlockIn2015, + AsyncMoveOrderIncorrect, BracesForStructLiteral, CatchAfterTry, CommaAfterBaseStruct, + ComparisonInterpretedAsGeneric, ComparisonOrShiftInterpretedAsGenericSugg, + DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, ExpectedElseBlock, ExpectedEqForLetExpr, + ExpectedExpressionFoundLet, FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart, + FoundExprWouldBeStmt, HelpUseLatestEdition, IfExpressionLetSomeSub, + IfExpressionMissingCondition, IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub, + InvalidBlockMacroSegment, InvalidComparisonOperator, InvalidComparisonOperatorSub, + InvalidInterpolatedExpression, InvalidLiteralSuffixOnTupleIndex, InvalidLogicalOperator, + InvalidLogicalOperatorSub, LabeledLoopInBreak, LeadingPlusNotSupported, LeftArrowOperator, + LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, MalformedLoopLabel, + MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg, MissingCommaAfterMatchArm, + MissingDotDot, MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray, + NoFieldsForFnCall, NotAsNegationOperator, NotAsNegationOperatorSub, + OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields, RequireColonAfterLabeledExpression, ShiftInterpretedAsGeneric, StructLiteralNotAllowedHere, StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator, UnexpectedIfWithIf, UnexpectedTokenAfterLabel, UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses, @@ -39,8 +40,8 @@ use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, R use rustc_ast::{ClosureBinder, MetaItemLit, StmtKind}; use rustc_ast_pretty::pprust; use rustc_errors::{ - Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, PResult, - StashKey, + AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, + PResult, StashKey, }; use rustc_session::errors::{report_lit_error, ExprParenthesesNeeded}; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; @@ -2140,7 +2141,7 @@ impl<'a> Parser<'a> { } if self.token.kind == TokenKind::Semi - && matches!(self.token_cursor.frame.delim_sp, Some((Delimiter::Parenthesis, _))) + && matches!(self.token_cursor.stack.last(), Some((_, Delimiter::Parenthesis, _))) && self.may_recover() { // It is likely that the closure body is a block but where the @@ -2221,7 +2222,7 @@ impl<'a> Parser<'a> { let lo = self.token.span; let attrs = self.parse_outer_attributes()?; self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { - let pat = this.parse_pat_no_top_alt(PARAM_EXPECTED)?; + let pat = this.parse_pat_no_top_alt(Some(Expected::ParameterName))?; let ty = if this.eat(&token::Colon) { this.parse_ty()? } else { @@ -2290,7 +2291,7 @@ impl<'a> Parser<'a> { block } else { let let_else_sub = matches!(cond.kind, ExprKind::Let(..)) - .then(|| IfExpressionLetSomeSub { if_span: lo }); + .then(|| IfExpressionLetSomeSub { if_span: lo.until(cond_span) }); self.sess.emit_err(IfExpressionMissingThenBlock { if_span: lo, @@ -2716,6 +2717,14 @@ impl<'a> Parser<'a> { ); err.emit(); this.bump(); + } else if matches!( + (&this.prev_token.kind, &this.token.kind), + (token::DotDotEq, token::Gt) + ) { + // `error_inclusive_range_match_arrow` handles cases like `0..=> {}`, + // so we supress the error here + err.delay_as_bug(); + this.bump(); } else { return Err(err); } @@ -2926,8 +2935,8 @@ impl<'a> Parser<'a> { let mut async_block_err = |e: &mut Diagnostic, span: Span| { recover_async = true; - e.span_label(span, "`async` blocks are only allowed in Rust 2018 or later"); - e.help_use_latest_edition(); + AsyncBlockIn2015 { span }.add_to_diagnostic(e); + HelpUseLatestEdition::new().add_to_diagnostic(e); }; while self.token != token::CloseDelim(close_delim) { diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 8ba811715d8..585dfc518b3 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -1,4 +1,7 @@ -use crate::errors::{WhereClauseBeforeTupleStructBody, WhereClauseBeforeTupleStructBodySugg}; +use crate::errors::{ + MultipleWhereClauses, UnexpectedSelfInGenericParameters, WhereClauseBeforeTupleStructBody, + WhereClauseBeforeTupleStructBodySugg, +}; use super::{ForceCollect, Parser, TrailingToken}; @@ -127,12 +130,9 @@ impl<'a> Parser<'a> { if this.eat_keyword_noexpect(kw::SelfUpper) { // `Self` as a generic param is invalid. Here we emit the diagnostic and continue parsing // as if `Self` never existed. - this.struct_span_err( - this.prev_token.span, - "unexpected keyword `Self` in generic parameters", - ) - .note("you cannot use `Self` as a generic parameter because it is reserved for associated items") - .emit(); + this.sess.emit_err(UnexpectedSelfInGenericParameters { + span: this.prev_token.span, + }); this.eat(&token::Comma); } @@ -329,16 +329,11 @@ impl<'a> Parser<'a> { let ate_comma = self.eat(&token::Comma); if self.eat_keyword_noexpect(kw::Where) { - let msg = "cannot define duplicate `where` clauses on an item"; - let mut err = self.struct_span_err(self.token.span, msg); - err.span_label(pred_lo, "previous `where` clause starts here"); - err.span_suggestion_verbose( - prev_token.shrink_to_hi().to(self.prev_token.span), - "consider joining the two `where` clauses into one", - ",", - Applicability::MaybeIncorrect, - ); - err.emit(); + self.sess.emit_err(MultipleWhereClauses { + span: self.token.span, + previous: pred_lo, + between: prev_token.shrink_to_hi().to(self.prev_token.span), + }); } else if !ate_comma { break; } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 53680a82bdc..628e9d88cf1 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1,4 +1,13 @@ -use crate::errors::{DocCommentDoesNotDocumentAnything, UseEmptyBlockNotSemi}; +use crate::errors::{ + AmbiguousMissingKwForItemSub, AssociatedStaticItemNotAllowed, AsyncFnIn2015, + BoundsNotAllowedOnTraitAliases, ConstGlobalCannotBeMutable, ConstLetMutuallyExclusive, + DefaultNotFollowedByItem, DocCommentDoesNotDocumentAnything, EnumStructMutuallyExclusive, + ExpectedTraitInTraitImplFoundType, ExternCrateNameWithDashes, ExternCrateNameWithDashesSugg, + ExternItemCannotBeConst, HelpUseLatestEdition, MissingConstType, MissingForInTraitImpl, + MissingKeywordForItemDefinition, MissingTraitInTraitImpl, SelfArgumentPointer, + TraitAliasCannotBeAuto, TraitAliasCannotBeUnsafe, UnexpectedTokenAfterStructName, + UseEmptyBlockNotSemi, VisibilityNotFollowedByItem, +}; use super::diagnostics::{dummy_arg, ConsumeClosingDelim}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; @@ -16,12 +25,16 @@ use rustc_ast::{EnumDef, FieldDef, Generics, TraitRef, Ty, TyKind, Variant, Vari use rustc_ast::{FnHeader, ForeignItem, Path, PathSegment, Visibility, VisibilityKind}; use rustc_ast::{MacCall, MacDelimiter}; use rustc_ast_pretty::pprust; -use rustc_errors::{struct_span_err, Applicability, IntoDiagnostic, PResult, StashKey}; +use rustc_errors::{ + struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, PResult, + StashKey, +}; use rustc_span::edition::Edition; use rustc_span::lev_distance::lev_distance; use rustc_span::source_map::{self, Span}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::DUMMY_SP; +use std::fmt::Write; use std::mem; use thin_vec::ThinVec; @@ -163,35 +176,18 @@ impl<'a> Parser<'a> { } // At this point, we have failed to parse an item. - self.error_on_unmatched_vis(&vis); - self.error_on_unmatched_defaultness(def); - if !attrs_allowed { - self.recover_attrs_no_item(&attrs)?; + if !matches!(vis.kind, VisibilityKind::Inherited) { + self.sess.emit_err(VisibilityNotFollowedByItem { span: vis.span, vis }); } - Ok(None) - } - /// Error in-case a non-inherited visibility was parsed but no item followed. - fn error_on_unmatched_vis(&self, vis: &Visibility) { - if let VisibilityKind::Inherited = vis.kind { - return; + if let Defaultness::Default(span) = def { + self.sess.emit_err(DefaultNotFollowedByItem { span }); } - let vs = pprust::vis_to_string(&vis); - let vs = vs.trim_end(); - self.struct_span_err(vis.span, &format!("visibility `{vs}` is not followed by an item")) - .span_label(vis.span, "the visibility") - .help(&format!("you likely meant to define an item, e.g., `{vs} fn foo() {{}}`")) - .emit(); - } - /// Error in-case a `default` was parsed but no item followed. - fn error_on_unmatched_defaultness(&self, def: Defaultness) { - if let Defaultness::Default(sp) = def { - self.struct_span_err(sp, "`default` is not followed by an item") - .span_label(sp, "the `default` qualifier") - .note("only `fn`, `const`, `type`, or `impl` items may be prefixed by `default`") - .emit(); + if !attrs_allowed { + self.recover_attrs_no_item(&attrs)?; } + Ok(None) } /// Error in-case `default` was parsed in an in-appropriate context. @@ -384,86 +380,72 @@ impl<'a> Parser<'a> { let sp = self.prev_token.span.between(self.token.span); let full_sp = self.prev_token.span.to(self.token.span); let ident_sp = self.token.span; - if self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace)) { + + let ident = if self.look_ahead(1, |t| { + [ + token::Lt, + token::OpenDelim(Delimiter::Brace), + token::OpenDelim(Delimiter::Parenthesis), + ] + .contains(&t.kind) + }) { + self.parse_ident().unwrap() + } else { + return Ok(()); + }; + + let mut found_generics = false; + if self.check(&token::Lt) { + found_generics = true; + self.eat_to_tokens(&[&token::Gt]); + self.bump(); // `>` + } + + let err = if self.check(&token::OpenDelim(Delimiter::Brace)) { // possible public struct definition where `struct` was forgotten - let ident = self.parse_ident().unwrap(); - let msg = format!("add `struct` here to parse `{ident}` as a public struct"); - let mut err = self.struct_span_err(sp, "missing `struct` for struct definition"); - err.span_suggestion_short( - sp, - &msg, - " struct ", - Applicability::MaybeIncorrect, // speculative - ); - Err(err) - } else if self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Parenthesis)) { - let ident = self.parse_ident().unwrap(); + Some(MissingKeywordForItemDefinition::Struct { span: sp, ident }) + } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) { + // possible public function or tuple struct definition where `fn`/`struct` was + // forgotten self.bump(); // `(` - let kw_name = self.recover_first_param(); + let is_method = self.recover_self_param(); + self.consume_block(Delimiter::Parenthesis, ConsumeClosingDelim::Yes); - let (kw, kw_name, ambiguous) = if self.check(&token::RArrow) { + + let err = if self.check(&token::RArrow) + || self.check(&token::OpenDelim(Delimiter::Brace)) + { self.eat_to_tokens(&[&token::OpenDelim(Delimiter::Brace)]); self.bump(); // `{` - ("fn", kw_name, false) - } else if self.check(&token::OpenDelim(Delimiter::Brace)) { - self.bump(); // `{` - ("fn", kw_name, false) - } else if self.check(&token::Colon) { - let kw = "struct"; - (kw, kw, false) - } else { - ("fn` or `struct", "function or struct", true) - }; - - let msg = format!("missing `{kw}` for {kw_name} definition"); - let mut err = self.struct_span_err(sp, &msg); - if !ambiguous { self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes); - let suggestion = - format!("add `{kw}` here to parse `{ident}` as a public {kw_name}"); - err.span_suggestion_short( - sp, - &suggestion, - format!(" {kw} "), - Applicability::MachineApplicable, - ); - } else if let Ok(snippet) = self.span_to_snippet(ident_sp) { - err.span_suggestion( - full_sp, - "if you meant to call a macro, try", - format!("{}!", snippet), - // this is the `ambiguous` conditional branch - Applicability::MaybeIncorrect, - ); - } else { - err.help( - "if you meant to call a macro, remove the `pub` \ - and add a trailing `!` after the identifier", - ); - } - Err(err) - } else if self.look_ahead(1, |t| *t == token::Lt) { - let ident = self.parse_ident().unwrap(); - self.eat_to_tokens(&[&token::Gt]); - self.bump(); // `>` - let (kw, kw_name, ambiguous) = if self.eat(&token::OpenDelim(Delimiter::Parenthesis)) { - ("fn", self.recover_first_param(), false) - } else if self.check(&token::OpenDelim(Delimiter::Brace)) { - ("struct", "struct", false) + if is_method { + MissingKeywordForItemDefinition::Method { span: sp, ident } + } else { + MissingKeywordForItemDefinition::Function { span: sp, ident } + } + } else if self.check(&token::Semi) { + MissingKeywordForItemDefinition::Struct { span: sp, ident } } else { - ("fn` or `struct", "function or struct", true) + MissingKeywordForItemDefinition::Ambiguous { + span: sp, + subdiag: if found_generics { + None + } else if let Ok(snippet) = self.span_to_snippet(ident_sp) { + Some(AmbiguousMissingKwForItemSub::SuggestMacro { span: full_sp, snippet }) + } else { + Some(AmbiguousMissingKwForItemSub::HelpMacro) + }, + } }; - let msg = format!("missing `{kw}` for {kw_name} definition"); - let mut err = self.struct_span_err(sp, &msg); - if !ambiguous { - err.span_suggestion_short( - sp, - &format!("add `{kw}` here to parse `{ident}` as a public {kw_name}"), - format!(" {} ", kw), - Applicability::MachineApplicable, - ); - } - Err(err) + Some(err) + } else if found_generics { + Some(MissingKeywordForItemDefinition::Ambiguous { span: sp, subdiag: None }) + } else { + None + }; + + if let Some(err) = err { + Err(err.into_diagnostic(&self.sess.span_diagnostic)) } else { Ok(()) } @@ -512,16 +494,13 @@ impl<'a> Parser<'a> { let mut err = self.struct_span_err(end.span, msg); if end.is_doc_comment() { err.span_label(end.span, "this doc comment doesn't document anything"); - } - if end.meta_kind().is_some() { - if self.token.kind == TokenKind::Semi { - err.span_suggestion_verbose( - self.token.span, - "consider removing this semicolon", - "", - Applicability::MaybeIncorrect, - ); - } + } else if self.token.kind == TokenKind::Semi { + err.span_suggestion_verbose( + self.token.span, + "consider removing this semicolon", + "", + Applicability::MaybeIncorrect, + ); } if let [.., penultimate, _] = attrs { err.span_label(start.span.to(penultimate.span), "other attributes here"); @@ -588,20 +567,9 @@ impl<'a> Parser<'a> { let ty_first = if self.token.is_keyword(kw::For) && self.look_ahead(1, |t| t != &token::Lt) { let span = self.prev_token.span.between(self.token.span); - self.struct_span_err(span, "missing trait in a trait impl") - .span_suggestion( - span, - "add a trait here", - " Trait ", - Applicability::HasPlaceholders, - ) - .span_suggestion( - span.to(self.token.span), - "for an inherent impl, drop this `for`", - "", - Applicability::MaybeIncorrect, - ) - .emit(); + self.sess + .emit_err(MissingTraitInTraitImpl { span, for_span: span.to(self.token.span) }); + P(Ty { kind: TyKind::Path(None, err_path(span)), span, @@ -634,14 +602,7 @@ impl<'a> Parser<'a> { Some(ty_second) => { // impl Trait for Type if !has_for { - self.struct_span_err(missing_for_span, "missing `for` in a trait impl") - .span_suggestion_short( - missing_for_span, - "add `for` here", - " for ", - Applicability::MachineApplicable, - ) - .emit(); + self.sess.emit_err(MissingForInTraitImpl { span: missing_for_span }); } let ty_first = ty_first.into_inner(); @@ -649,7 +610,8 @@ impl<'a> Parser<'a> { // This notably includes paths passed through `ty` macro fragments (#46438). TyKind::Path(None, path) => path, _ => { - self.struct_span_err(ty_first.span, "expected a trait, found type").emit(); + self.sess + .emit_err(ExpectedTraitInTraitImplFoundType { span: ty_first.span }); err_path(ty_first.span) } }; @@ -783,6 +745,7 @@ impl<'a> Parser<'a> { fn recover_doc_comment_before_brace(&mut self) -> bool { if let token::DocComment(..) = self.token.kind { if self.look_ahead(1, |tok| tok == &token::CloseDelim(Delimiter::Brace)) { + // FIXME: merge with `DocCommentDoesNotDocumentAnything` (E0585) struct_span_err!( self.diagnostic(), self.token.span, @@ -849,7 +812,7 @@ impl<'a> Parser<'a> { // It's a trait alias. if had_colon { let span = span_at_colon.to(span_before_eq); - self.struct_span_err(span, "bounds are not allowed on trait aliases").emit(); + self.sess.emit_err(BoundsNotAllowedOnTraitAliases { span }); } let bounds = self.parse_generic_bounds(None)?; @@ -858,12 +821,10 @@ impl<'a> Parser<'a> { let whole_span = lo.to(self.prev_token.span); if is_auto == IsAuto::Yes { - let msg = "trait aliases cannot be `auto`"; - self.struct_span_err(whole_span, msg).span_label(whole_span, msg).emit(); + self.sess.emit_err(TraitAliasCannotBeAuto { span: whole_span }); } if let Unsafe::Yes(_) = unsafety { - let msg = "trait aliases cannot be `unsafe`"; - self.struct_span_err(whole_span, msg).span_label(whole_span, msg).emit(); + self.sess.emit_err(TraitAliasCannotBeUnsafe { span: whole_span }); } self.sess.gated_spans.gate(sym::trait_alias, whole_span); @@ -909,8 +870,7 @@ impl<'a> Parser<'a> { Ok(kind) => kind, Err(kind) => match kind { ItemKind::Static(a, _, b) => { - self.struct_span_err(span, "associated `static` items are not allowed") - .emit(); + self.sess.emit_err(AssociatedStaticItemNotAllowed { span }); AssocItemKind::Const(Defaultness::Final, a, b) } _ => return self.error_bad_item_kind(span, &kind, "`trait`s or `impl`s"), @@ -1084,41 +1044,37 @@ impl<'a> Parser<'a> { } fn parse_crate_name_with_dashes(&mut self) -> PResult<'a, Ident> { - let error_msg = "crate name using dashes are not valid in `extern crate` statements"; - let suggestion_msg = "if the original crate name uses dashes you need to use underscores \ - in the code"; - let mut ident = if self.token.is_keyword(kw::SelfLower) { + let ident = if self.token.is_keyword(kw::SelfLower) { self.parse_path_segment_ident() } else { self.parse_ident() }?; - let mut idents = vec![]; - let mut replacement = vec![]; - let mut fixed_crate_name = false; - // Accept `extern crate name-like-this` for better diagnostics. + let dash = token::BinOp(token::BinOpToken::Minus); - if self.token == dash { - // Do not include `-` as part of the expected tokens list. - while self.eat(&dash) { - fixed_crate_name = true; - replacement.push((self.prev_token.span, "_".to_string())); - idents.push(self.parse_ident()?); - } + if self.token != dash { + return Ok(ident); } - if fixed_crate_name { - let fixed_name_sp = ident.span.to(idents.last().unwrap().span); - let mut fixed_name = ident.name.to_string(); - for part in idents { - fixed_name.push_str(&format!("_{}", part.name)); - } - ident = Ident::from_str_and_span(&fixed_name, fixed_name_sp); - self.struct_span_err(fixed_name_sp, error_msg) - .span_label(fixed_name_sp, "dash-separated idents are not valid") - .multipart_suggestion(suggestion_msg, replacement, Applicability::MachineApplicable) - .emit(); + // Accept `extern crate name-like-this` for better diagnostics. + let mut dashes = vec![]; + let mut idents = vec![]; + while self.eat(&dash) { + dashes.push(self.prev_token.span); + idents.push(self.parse_ident()?); } - Ok(ident) + + let fixed_name_sp = ident.span.to(idents.last().unwrap().span); + let mut fixed_name = ident.name.to_string(); + for part in idents { + write!(fixed_name, "_{}", part.name).unwrap(); + } + + self.sess.emit_err(ExternCrateNameWithDashes { + span: fixed_name_sp, + sugg: ExternCrateNameWithDashesSugg { dashes }, + }); + + Ok(Ident::from_str_and_span(&fixed_name, fixed_name_sp)) } /// Parses `extern` for foreign ABIs modules. @@ -1166,7 +1122,10 @@ impl<'a> Parser<'a> { Ok(kind) => kind, Err(kind) => match kind { ItemKind::Const(_, a, b) => { - self.error_on_foreign_const(span, ident); + self.sess.emit_err(ExternItemCannotBeConst { + ident_span: ident.span, + const_span: span.with_hi(ident.span.lo()), + }); ForeignItemKind::Static(a, Mutability::Not, b) } _ => return self.error_bad_item_kind(span, &kind, "`extern` blocks"), @@ -1178,6 +1137,7 @@ impl<'a> Parser<'a> { } fn error_bad_item_kind<T>(&self, span: Span, kind: &ItemKind, ctx: &str) -> Option<T> { + // FIXME(#100717): needs variant for each `ItemKind` (instead of using `ItemKind::descr()`) let span = self.sess.source_map().guess_head_span(span); let descr = kind.descr(); self.struct_span_err(span, &format!("{descr} is not supported in {ctx}")) @@ -1186,18 +1146,6 @@ impl<'a> Parser<'a> { None } - fn error_on_foreign_const(&self, span: Span, ident: Ident) { - self.struct_span_err(ident.span, "extern items cannot be `const`") - .span_suggestion( - span.with_hi(ident.span.lo()), - "try using a static value", - "static ", - Applicability::MachineApplicable, - ) - .note("for more information, visit https://doc.rust-lang.org/std/keyword.extern.html") - .emit(); - } - fn is_unsafe_foreign_mod(&self) -> bool { self.token.is_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Extern]) @@ -1225,25 +1173,10 @@ impl<'a> Parser<'a> { fn recover_const_mut(&mut self, const_span: Span) { if self.eat_keyword(kw::Mut) { let span = self.prev_token.span; - self.struct_span_err(span, "const globals cannot be mutable") - .span_label(span, "cannot be mutable") - .span_suggestion( - const_span, - "you might want to declare a static instead", - "static", - Applicability::MaybeIncorrect, - ) - .emit(); + self.sess.emit_err(ConstGlobalCannotBeMutable { ident_span: span, const_span }); } 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(); + self.sess.emit_err(ConstLetMutuallyExclusive { span: const_span.to(span) }); } } @@ -1328,13 +1261,8 @@ impl<'a> Parser<'a> { }; 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( - span, - "provide a type for the item", - format!("{colon} <type>"), - Applicability::HasPlaceholders, - ); + let err: DiagnosticBuilder<'_, ErrorGuaranteed> = + MissingConstType { span, colon, kind }.into_diagnostic(&self.sess.span_diagnostic); err.stash(span, StashKey::ItemNoType); // The user intended that the type be inferred, @@ -1346,18 +1274,12 @@ impl<'a> Parser<'a> { fn parse_item_enum(&mut self) -> PResult<'a, ItemInfo> { if self.token.is_keyword(kw::Struct) { 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( - span, - "replace `enum struct` with", - "enum", - Applicability::MachineApplicable, - ); + let err = EnumStructMutuallyExclusive { span }; if self.look_ahead(1, |t| t.is_ident()) { self.bump(); - err.emit(); + self.sess.emit_err(err); } else { - return Err(err); + return Err(err.into_diagnostic(&self.sess.span_diagnostic)); } } @@ -1493,13 +1415,8 @@ impl<'a> Parser<'a> { self.expect_semi()?; body } else { - let token_str = super::token_descr(&self.token); - let msg = &format!( - "expected `where`, `{{`, `(`, or `;` after struct name, found {token_str}" - ); - let mut err = self.struct_span_err(self.token.span, msg); - err.span_label(self.token.span, "expected `where`, `{`, `(`, or `;` after struct name"); - return Err(err); + let err = UnexpectedTokenAfterStructName::new(self.token.span, self.token.clone()); + return Err(err.into_diagnostic(&self.sess.span_diagnostic)); }; Ok((class_name, ItemKind::Struct(vdata, generics))) @@ -2330,7 +2247,9 @@ impl<'a> Parser<'a> { let ext = self.parse_extern(case); if let Async::Yes { span, .. } = asyncness { - self.ban_async_in_2015(span); + if span.is_rust_2015() { + self.sess.emit_err(AsyncFnIn2015 { span, help: HelpUseLatestEdition::new() }); + } } if !self.eat_keyword_case(kw::Fn, case) { @@ -2440,17 +2359,6 @@ impl<'a> Parser<'a> { Ok(FnHeader { constness, unsafety, asyncness, ext }) } - /// We are parsing `async fn`. If we are on Rust 2015, emit an error. - fn ban_async_in_2015(&self, span: Span) { - if span.rust_2015() { - let diag = self.diagnostic(); - struct_span_err!(diag, span, E0670, "`async fn` is not permitted in Rust 2015") - .span_label(span, "to use `async fn`, switch to Rust 2018 or later") - .help_use_latest_edition() - .emit(); - } - } - /// Parses the parameter list and result type of a function declaration. pub(super) fn parse_fn_decl( &mut self, @@ -2593,9 +2501,7 @@ impl<'a> Parser<'a> { }; // Recover for the grammar `*self`, `*const self`, and `*mut self`. let recover_self_ptr = |this: &mut Self| { - let msg = "cannot pass `self` by raw pointer"; - let span = this.token.span; - this.struct_span_err(span, msg).span_label(span, msg).emit(); + self.sess.emit_err(SelfArgumentPointer { span: this.token.span }); Ok((SelfKind::Value(Mutability::Not), expect_self_ident(this), this.prev_token.span)) }; @@ -2676,14 +2582,14 @@ impl<'a> Parser<'a> { && self.look_ahead(offset + 1, |t| t == &token::Colon) } - fn recover_first_param(&mut self) -> &'static str { + fn recover_self_param(&mut self) -> bool { match self .parse_outer_attributes() .and_then(|_| self.parse_self_param()) .map_err(|e| e.cancel()) { - Ok(Some(_)) => "method", - _ => "function", + Ok(Some(_)) => true, + _ => false, } } } diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index bb06ad20118..0cb88f3c3a9 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -19,9 +19,8 @@ pub use path::PathStyle; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Nonterminal, Token, TokenKind}; -use rustc_ast::tokenstream::AttributesData; -use rustc_ast::tokenstream::{self, DelimSpan, Spacing}; -use rustc_ast::tokenstream::{TokenStream, TokenTree}; +use rustc_ast::tokenstream::{AttributesData, DelimSpan, Spacing}; +use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor}; use rustc_ast::util::case::Case; use rustc_ast::AttrId; use rustc_ast::DUMMY_NODE_ID; @@ -168,7 +167,7 @@ pub struct Parser<'a> { // This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure // it doesn't unintentionally get bigger. #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(Parser<'_>, 336); +rustc_data_structures::static_assert_size!(Parser<'_>, 312); /// Stores span information about a closure. #[derive(Clone)] @@ -221,18 +220,27 @@ impl<'a> Drop for Parser<'a> { } } +/// Iterator over a `TokenStream` that produces `Token`s. It's a bit odd that +/// we (a) lex tokens into a nice tree structure (`TokenStream`), and then (b) +/// use this type to emit them as a linear sequence. But a linear sequence is +/// what the parser expects, for the most part. #[derive(Clone)] struct TokenCursor { - // The current (innermost) frame. `frame` and `stack` could be combined, - // but it's faster to have them separately to access `frame` directly - // rather than via something like `stack.last().unwrap()` or - // `stack[stack.len() - 1]`. - frame: TokenCursorFrame, - // Additional frames that enclose `frame`. - stack: Vec<TokenCursorFrame>, + // Cursor for the current (innermost) token stream. The delimiters for this + // token stream are found in `self.stack.last()`; when that is `None` then + // we are in the outermost token stream which never has delimiters. + tree_cursor: TokenTreeCursor, + + // Token streams surrounding the current one. The delimiters for stack[n]'s + // tokens are in `stack[n-1]`. `stack[0]` (when present) has no delimiters + // because it's the outermost token stream which never has delimiters. + stack: Vec<(TokenTreeCursor, Delimiter, DelimSpan)>, + desugar_doc_comments: bool, + // Counts the number of calls to `{,inlined_}next`. num_next_calls: usize, + // During parsing, we may sometimes need to 'unglue' a // glued token into two component tokens // (e.g. '>>' into '>' and '>), so that the parser @@ -257,18 +265,6 @@ struct TokenCursor { break_last_token: bool, } -#[derive(Clone)] -struct TokenCursorFrame { - delim_sp: Option<(Delimiter, DelimSpan)>, - tree_cursor: tokenstream::Cursor, -} - -impl TokenCursorFrame { - fn new(delim_sp: Option<(Delimiter, DelimSpan)>, tts: TokenStream) -> Self { - TokenCursorFrame { delim_sp, tree_cursor: tts.into_trees() } - } -} - impl TokenCursor { fn next(&mut self, desugar_doc_comments: bool) -> (Token, Spacing) { self.inlined_next(desugar_doc_comments) @@ -281,38 +277,47 @@ impl TokenCursor { // FIXME: we currently don't return `Delimiter` open/close delims. To fix #67062 we will // need to, whereupon the `delim != Delimiter::Invisible` conditions below can be // removed. - if let Some(tree) = self.frame.tree_cursor.next_ref() { + if let Some(tree) = self.tree_cursor.next_ref() { match tree { &TokenTree::Token(ref token, spacing) => match (desugar_doc_comments, token) { (true, &Token { kind: token::DocComment(_, attr_style, data), span }) => { - return self.desugar(attr_style, data, span); + let desugared = self.desugar(attr_style, data, span); + self.tree_cursor.replace_prev_and_rewind(desugared); + // Continue to get the first token of the desugared doc comment. + } + _ => { + debug_assert!(!matches!( + token.kind, + token::OpenDelim(_) | token::CloseDelim(_) + )); + return (token.clone(), spacing); } - _ => return (token.clone(), spacing), }, &TokenTree::Delimited(sp, delim, ref tts) => { - // Set `open_delim` to true here because we deal with it immediately. - let frame = TokenCursorFrame::new(Some((delim, sp)), tts.clone()); - self.stack.push(mem::replace(&mut self.frame, frame)); + let trees = tts.clone().into_trees(); + self.stack.push((mem::replace(&mut self.tree_cursor, trees), delim, sp)); if delim != Delimiter::Invisible { return (Token::new(token::OpenDelim(delim), sp.open), Spacing::Alone); } // No open delimiter to return; continue on to the next iteration. } }; - } else if let Some(frame) = self.stack.pop() { - if let Some((delim, span)) = self.frame.delim_sp && delim != Delimiter::Invisible { - self.frame = frame; + } else if let Some((tree_cursor, delim, span)) = self.stack.pop() { + // We have exhausted this token stream. Move back to its parent token stream. + self.tree_cursor = tree_cursor; + if delim != Delimiter::Invisible { return (Token::new(token::CloseDelim(delim), span.close), Spacing::Alone); } - self.frame = frame; // No close delimiter to return; continue on to the next iteration. } else { + // We have exhausted the outermost token stream. return (Token::new(token::Eof, DUMMY_SP), Spacing::Alone); } } } - fn desugar(&mut self, attr_style: AttrStyle, data: Symbol, span: Span) -> (Token, Spacing) { + // Desugar a doc comment into something like `#[doc = r"foo"]`. + fn desugar(&mut self, attr_style: AttrStyle, data: Symbol, span: Span) -> Vec<TokenTree> { // Searches for the occurrences of `"#*` and returns the minimum number of `#`s // required to wrap the text. E.g. // - `abc d` is wrapped as `r"abc d"` (num_of_hashes = 0) @@ -346,27 +351,15 @@ impl TokenCursor { .collect::<TokenStream>(), ); - self.stack.push(mem::replace( - &mut self.frame, - TokenCursorFrame::new( - None, - if attr_style == AttrStyle::Inner { - [ - TokenTree::token_alone(token::Pound, span), - TokenTree::token_alone(token::Not, span), - body, - ] - .into_iter() - .collect::<TokenStream>() - } else { - [TokenTree::token_alone(token::Pound, span), body] - .into_iter() - .collect::<TokenStream>() - }, - ), - )); - - self.next(/* desugar_doc_comments */ false) + if attr_style == AttrStyle::Inner { + vec![ + TokenTree::token_alone(token::Pound, span), + TokenTree::token_alone(token::Not, span), + body, + ] + } else { + vec![TokenTree::token_alone(token::Pound, span), body] + } } } @@ -475,7 +468,7 @@ impl<'a> Parser<'a> { restrictions: Restrictions::empty(), expected_tokens: Vec::new(), token_cursor: TokenCursor { - frame: TokenCursorFrame::new(None, tokens), + tree_cursor: tokens.into_trees(), stack: Vec::new(), num_next_calls: 0, desugar_doc_comments, @@ -1143,14 +1136,16 @@ impl<'a> Parser<'a> { return looker(&self.token); } - let frame = &self.token_cursor.frame; - if let Some((delim, span)) = frame.delim_sp && delim != Delimiter::Invisible { + let tree_cursor = &self.token_cursor.tree_cursor; + if let Some(&(_, delim, span)) = self.token_cursor.stack.last() + && delim != Delimiter::Invisible + { let all_normal = (0..dist).all(|i| { - let token = frame.tree_cursor.look_ahead(i); + let token = tree_cursor.look_ahead(i); !matches!(token, Some(TokenTree::Delimited(_, Delimiter::Invisible, _))) }); if all_normal { - return match frame.tree_cursor.look_ahead(dist - 1) { + return match tree_cursor.look_ahead(dist - 1) { Some(tree) => match tree { TokenTree::Token(token, _) => looker(token), TokenTree::Delimited(dspan, delim, _) => { @@ -1321,10 +1316,10 @@ impl<'a> Parser<'a> { pub(crate) fn parse_token_tree(&mut self) -> TokenTree { match self.token.kind { token::OpenDelim(..) => { - // Grab the tokens from this frame. - let frame = &self.token_cursor.frame; - let stream = frame.tree_cursor.stream.clone(); - let (delim, span) = frame.delim_sp.unwrap(); + // Grab the tokens within the delimiters. + let tree_cursor = &self.token_cursor.tree_cursor; + let stream = tree_cursor.stream.clone(); + let (_, delim, span) = *self.token_cursor.stack.last().unwrap(); // Advance the token cursor through the entire delimited // sequence. After getting the `OpenDelim` we are *within* the diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 239ed79ce2f..7a4d53ed8bb 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -2,9 +2,11 @@ use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, NonterminalKind, Token}; use rustc_ast::HasTokens; use rustc_ast_pretty::pprust; +use rustc_errors::IntoDiagnostic; use rustc_errors::PResult; use rustc_span::symbol::{kw, Ident}; +use crate::errors::UnexpectedNonterminal; use crate::parser::pat::{CommaRecoveryMode, RecoverColon, RecoverComma}; use crate::parser::{FollowedByType, ForceCollect, NtOrTt, Parser, PathStyle}; @@ -113,7 +115,8 @@ impl<'a> Parser<'a> { NonterminalKind::Item => match self.parse_item(ForceCollect::Yes)? { Some(item) => token::NtItem(item), None => { - return Err(self.struct_span_err(self.token.span, "expected an item keyword")); + return Err(UnexpectedNonterminal::Item(self.token.span) + .into_diagnostic(&self.sess.span_diagnostic)); } }, NonterminalKind::Block => { @@ -124,7 +127,8 @@ impl<'a> Parser<'a> { NonterminalKind::Stmt => match self.parse_stmt(ForceCollect::Yes)? { Some(s) => token::NtStmt(P(s)), None => { - return Err(self.struct_span_err(self.token.span, "expected a statement")); + return Err(UnexpectedNonterminal::Statement(self.token.span) + .into_diagnostic(&self.sess.span_diagnostic)); } }, NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr { .. } => { @@ -160,9 +164,10 @@ impl<'a> Parser<'a> { token::NtIdent(ident, is_raw) } NonterminalKind::Ident => { - let token_str = pprust::token_to_string(&self.token); - let msg = &format!("expected ident, found {}", &token_str); - return Err(self.struct_span_err(self.token.span, msg)); + return Err(UnexpectedNonterminal::Ident { + span: self.token.span, + token: self.token.clone(), + }.into_diagnostic(&self.sess.span_diagnostic)); } NonterminalKind::Path => token::NtPath( P(self.collect_tokens_no_attrs(|this| this.parse_path(PathStyle::Type))?), @@ -175,9 +180,10 @@ impl<'a> Parser<'a> { if self.check_lifetime() { token::NtLifetime(self.expect_lifetime().ident) } else { - let token_str = pprust::token_to_string(&self.token); - let msg = &format!("expected a lifetime, found `{}`", &token_str); - return Err(self.struct_span_err(self.token.span, msg)); + return Err(UnexpectedNonterminal::Lifetime { + span: self.token.span, + token: self.token.clone(), + }.into_diagnostic(&self.sess.span_diagnostic)); } } }; diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 912f7cc14f6..b054dc59a0c 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -1,6 +1,12 @@ use super::{ForceCollect, Parser, PathStyle, TrailingToken}; use crate::errors::{ - InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, RemoveLet, + AmbiguousRangePattern, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed, + DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt, + ExpectedCommaAfterPatternField, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, + InclusiveRangeNoEnd, InvalidMutInPattern, PatternOnWrongSideOfAt, RefMutOrderIncorrect, + RemoveLet, RepeatedMutInPattern, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, + TrailingVertNotAllowed, UnexpectedLifetimeInPattern, UnexpectedVertVertBeforeFunctionParam, + UnexpectedVertVertInPattern, }; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor}; @@ -11,15 +17,33 @@ use rustc_ast::{ PatField, PatKind, Path, QSelf, RangeEnd, RangeSyntax, }; use rustc_ast_pretty::pprust; -use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult}; +use rustc_errors::{ + fluent, Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, PResult, +}; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::{respan, Span, Spanned}; use rustc_span::symbol::{kw, sym, Ident}; -pub(super) type Expected = Option<&'static str>; +#[derive(PartialEq, Copy, Clone)] +pub enum Expected { + ParameterName, + ArgumentName, + Identifier, + BindingPattern, +} -/// `Expected` for function and lambda parameter patterns. -pub(super) const PARAM_EXPECTED: Expected = Some("parameter name"); +impl Expected { + // FIXME(#100717): migrate users of this to proper localization + fn to_string_or_fallback(expected: Option<Expected>) -> &'static str { + match expected { + Some(Expected::ParameterName) => "parameter name", + Some(Expected::ArgumentName) => "argument name", + Some(Expected::Identifier) => "identifier", + Some(Expected::BindingPattern) => "binding pattern", + None => "pattern", + } + } +} const WHILE_PARSING_OR_MSG: &str = "while parsing this or-pattern starting here"; @@ -56,13 +80,19 @@ enum EatOrResult { None, } +/// The syntax location of a given pattern. Used for diagnostics. +pub(super) enum PatternLocation { + LetBinding, + FunctionParameter, +} + impl<'a> Parser<'a> { /// Parses a pattern. /// /// Corresponds to `pat<no_top_alt>` in RFC 2535 and does not admit or-patterns /// at the top level. Used when parsing the parameters of lambda expressions, /// functions, function pointers, and `pat` macro fragments. - pub fn parse_pat_no_top_alt(&mut self, expected: Expected) -> PResult<'a, P<Pat>> { + pub fn parse_pat_no_top_alt(&mut self, expected: Option<Expected>) -> PResult<'a, P<Pat>> { self.parse_pat_with_range_pat(true, expected) } @@ -76,7 +106,7 @@ impl<'a> Parser<'a> { /// simplify the grammar somewhat. pub fn parse_pat_allow_top_alt( &mut self, - expected: Expected, + expected: Option<Expected>, rc: RecoverComma, ra: RecoverColon, rt: CommaRecoveryMode, @@ -88,7 +118,7 @@ impl<'a> Parser<'a> { /// recovered). fn parse_pat_allow_top_alt_inner( &mut self, - expected: Expected, + expected: Option<Expected>, rc: RecoverComma, ra: RecoverColon, rt: CommaRecoveryMode, @@ -118,8 +148,7 @@ impl<'a> Parser<'a> { // Check if the user wrote `foo:bar` instead of `foo::bar`. if ra == RecoverColon::Yes { - first_pat = - self.maybe_recover_colon_colon_in_pat_typo_or_anon_enum(first_pat, expected); + first_pat = self.maybe_recover_colon_colon_in_pat_typo(first_pat, expected); } if let Some(leading_vert_span) = leading_vert_span { @@ -168,9 +197,9 @@ impl<'a> Parser<'a> { /// otherwise). pub(super) fn parse_pat_before_ty( &mut self, - expected: Expected, + expected: Option<Expected>, rc: RecoverComma, - syntax_loc: &str, + syntax_loc: PatternLocation, ) -> PResult<'a, (P<Pat>, bool)> { // We use `parse_pat_allow_top_alt` regardless of whether we actually want top-level // or-patterns so that we can detect when a user tries to use it. This allows us to print a @@ -184,27 +213,41 @@ impl<'a> Parser<'a> { let colon = self.eat(&token::Colon); if let PatKind::Or(pats) = &pat.kind { - let msg = format!("top-level or-patterns are not allowed in {}", syntax_loc); - let (help, fix) = if pats.len() == 1 { - // If all we have is a leading vert, then print a special message. This is the case - // if `parse_pat_allow_top_alt` returns an or-pattern with one variant. - let msg = "remove the `|`"; - let fix = pprust::pat_to_string(&pat); - (msg, fix) - } else { - let msg = "wrap the pattern in parentheses"; - let fix = format!("({})", pprust::pat_to_string(&pat)); - (msg, fix) - }; + let span = pat.span; if trailing_vert { // We already emitted an error and suggestion to remove the trailing vert. Don't // emit again. - self.sess.span_diagnostic.delay_span_bug(pat.span, &msg); + + // FIXME(#100717): pass `TopLevelOrPatternNotAllowed::* { sub: None }` to + // `delay_span_bug()` instead of fluent message + self.sess.span_diagnostic.delay_span_bug( + span, + match syntax_loc { + PatternLocation::LetBinding => { + fluent::parse_or_pattern_not_allowed_in_let_binding + } + PatternLocation::FunctionParameter => { + fluent::parse_or_pattern_not_allowed_in_fn_parameters + } + }, + ); } else { - self.struct_span_err(pat.span, &msg) - .span_suggestion(pat.span, help, fix, Applicability::MachineApplicable) - .emit(); + let pat = pprust::pat_to_string(&pat); + let sub = if pats.len() == 1 { + Some(TopLevelOrPatternNotAllowedSugg::RemoveLeadingVert { span, pat }) + } else { + Some(TopLevelOrPatternNotAllowedSugg::WrapInParens { span, pat }) + }; + + self.sess.emit_err(match syntax_loc { + PatternLocation::LetBinding => { + TopLevelOrPatternNotAllowed::LetBinding { span, sub } + } + PatternLocation::FunctionParameter => { + TopLevelOrPatternNotAllowed::FunctionParameter { span, sub } + } + }); } } @@ -221,15 +264,15 @@ impl<'a> Parser<'a> { // a leading `||` probably doesn't indicate an or-pattern attempt, so we handle that // separately. if let token::OrOr = self.token.kind { - let span = self.token.span; - let mut err = self.struct_span_err(span, "unexpected `||` before function parameter"); - err.span_suggestion(span, "remove the `||`", "", Applicability::MachineApplicable); - err.note("alternatives in or-patterns are separated with `|`, not `||`"); - err.emit(); + self.sess.emit_err(UnexpectedVertVertBeforeFunctionParam { span: self.token.span }); self.bump(); } - self.parse_pat_before_ty(PARAM_EXPECTED, RecoverComma::No, "function parameters") + self.parse_pat_before_ty( + Some(Expected::ParameterName), + RecoverComma::No, + PatternLocation::FunctionParameter, + ) } /// Eat the or-pattern `|` separator. @@ -239,7 +282,7 @@ impl<'a> Parser<'a> { EatOrResult::TrailingVert } else if matches!(self.token.kind, token::OrOr) { // Found `||`; Recover and pretend we parsed `|`. - self.ban_unexpected_or_or(lo); + self.sess.emit_err(UnexpectedVertVertInPattern { span: self.token.span, start: lo }); self.bump(); EatOrResult::AteOr } else if self.eat(&token::BinOp(token::Or)) { @@ -273,7 +316,13 @@ impl<'a> Parser<'a> { }); match (is_end_ahead, &self.token.kind) { (true, token::BinOp(token::Or) | token::OrOr) => { - self.ban_illegal_vert(lo, "trailing", "not allowed in an or-pattern"); + // A `|` or possibly `||` token shouldn't be here. Ban it. + self.sess.emit_err(TrailingVertNotAllowed { + span: self.token.span, + start: lo, + token: self.token.clone(), + note_double_vert: matches!(self.token.kind, token::OrOr).then_some(()), + }); self.bump(); true } @@ -281,46 +330,12 @@ impl<'a> Parser<'a> { } } - /// We have parsed `||` instead of `|`. Error and suggest `|` instead. - fn ban_unexpected_or_or(&mut self, lo: Option<Span>) { - let mut err = self.struct_span_err(self.token.span, "unexpected token `||` in pattern"); - err.span_suggestion( - self.token.span, - "use a single `|` to separate multiple alternative patterns", - "|", - Applicability::MachineApplicable, - ); - if let Some(lo) = lo { - err.span_label(lo, WHILE_PARSING_OR_MSG); - } - err.emit(); - } - - /// A `|` or possibly `||` token shouldn't be here. Ban it. - fn ban_illegal_vert(&mut self, lo: Option<Span>, pos: &str, ctx: &str) { - let span = self.token.span; - let mut err = self.struct_span_err(span, &format!("a {} `|` is {}", pos, ctx)); - err.span_suggestion( - span, - &format!("remove the `{}`", pprust::token_to_string(&self.token)), - "", - Applicability::MachineApplicable, - ); - if let Some(lo) = lo { - err.span_label(lo, WHILE_PARSING_OR_MSG); - } - if let token::OrOr = self.token.kind { - err.note("alternatives in or-patterns are separated with `|`, not `||`"); - } - err.emit(); - } - /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are /// allowed). fn parse_pat_with_range_pat( &mut self, allow_range_pat: bool, - expected: Expected, + expected: Option<Expected>, ) -> PResult<'a, P<Pat>> { maybe_recover_from_interpolated_ty_qpath!(self, true); maybe_whole!(self, NtPat, |x| x); @@ -416,7 +431,7 @@ impl<'a> Parser<'a> { let lt = self.expect_lifetime(); let (lit, _) = self.recover_unclosed_char(lt.ident, Parser::mk_token_lit_char, |self_| { - let expected = expected.unwrap_or("pattern"); + let expected = Expected::to_string_or_fallback(expected); let msg = format!( "expected {}, found {}", expected, @@ -457,15 +472,7 @@ impl<'a> Parser<'a> { self.bump(); // `...` // The user probably mistook `...` for a rest pattern `..`. - self.struct_span_err(lo, "unexpected `...`") - .span_label(lo, "not a valid pattern") - .span_suggestion_short( - lo, - "for a rest pattern, use `..` instead of `...`", - "..", - Applicability::MachineApplicable, - ) - .emit(); + self.sess.emit_err(DotDotDotRestPattern { span: lo }); PatKind::Rest } @@ -490,7 +497,7 @@ impl<'a> Parser<'a> { // At this point we attempt to parse `@ $pat_rhs` and emit an error. self.bump(); // `@` let mut rhs = self.parse_pat_no_top_alt(None)?; - let sp = lhs.span.to(rhs.span); + let whole_span = lhs.span.to(rhs.span); if let PatKind::Ident(_, _, sub @ None) = &mut rhs.kind { // The user inverted the order, so help them fix that. @@ -499,27 +506,23 @@ impl<'a> Parser<'a> { // The RHS is now the full pattern. *sub = Some(lhs); - self.struct_span_err(sp, "pattern on wrong side of `@`") - .span_label(lhs_span, "pattern on the left, should be on the right") - .span_label(rhs.span, "binding on the right, should be on the left") - .span_suggestion( - sp, - "switch the order", - pprust::pat_to_string(&rhs), - Applicability::MachineApplicable, - ) - .emit(); + self.sess.emit_err(PatternOnWrongSideOfAt { + whole_span, + whole_pat: pprust::pat_to_string(&rhs), + pattern: lhs_span, + binding: rhs.span, + }); } else { // The special case above doesn't apply so we may have e.g. `A(x) @ B(y)`. rhs.kind = PatKind::Wild; - self.struct_span_err(sp, "left-hand side of `@` must be a binding") - .span_label(lhs.span, "interpreted as a pattern, not a binding") - .span_label(rhs.span, "also a pattern") - .note("bindings are `x`, `mut x`, `ref x`, and `ref mut x`") - .emit(); + self.sess.emit_err(ExpectedBindingLeftOfAt { + whole_span, + lhs: lhs.span, + rhs: rhs.span, + }); } - rhs.span = sp; + rhs.span = whole_span; Ok(rhs) } @@ -534,35 +537,23 @@ impl<'a> Parser<'a> { _ => return, } - self.struct_span_err(pat.span, "the range pattern here has ambiguous interpretation") - .span_suggestion( - pat.span, - "add parentheses to clarify the precedence", - format!("({})", pprust::pat_to_string(&pat)), - // "ambiguous interpretation" implies that we have to be guessing - Applicability::MaybeIncorrect, - ) - .emit(); + self.sess + .emit_err(AmbiguousRangePattern { span: pat.span, pat: pprust::pat_to_string(&pat) }); } /// Parse `&pat` / `&mut pat`. - fn parse_pat_deref(&mut self, expected: Expected) -> PResult<'a, PatKind> { + fn parse_pat_deref(&mut self, expected: Option<Expected>) -> PResult<'a, PatKind> { self.expect_and()?; - self.recover_lifetime_in_deref_pat(); - let mutbl = self.parse_mutability(); - let subpat = self.parse_pat_with_range_pat(false, expected)?; - Ok(PatKind::Ref(subpat, mutbl)) - } - - fn recover_lifetime_in_deref_pat(&mut self) { if let token::Lifetime(name) = self.token.kind { self.bump(); // `'a` - let span = self.prev_token.span; - self.struct_span_err(span, &format!("unexpected lifetime `{}` in pattern", name)) - .span_suggestion(span, "remove the lifetime", "", Applicability::MachineApplicable) - .emit(); + self.sess + .emit_err(UnexpectedLifetimeInPattern { span: self.prev_token.span, symbol: name }); } + + let mutbl = self.parse_mutability(); + let subpat = self.parse_pat_with_range_pat(false, expected)?; + Ok(PatKind::Ref(subpat, mutbl)) } /// Parse a tuple or parenthesis pattern. @@ -590,7 +581,8 @@ impl<'a> Parser<'a> { let mut_span = self.prev_token.span; if self.eat_keyword(kw::Ref) { - return self.recover_mut_ref_ident(mut_span); + self.sess.emit_err(RefMutOrderIncorrect { span: mut_span.to(self.prev_token.span) }); + return self.parse_pat_ident(BindingAnnotation::REF_MUT); } self.recover_additional_muts(); @@ -603,7 +595,7 @@ impl<'a> Parser<'a> { } // Parse the pattern we hope to be an identifier. - let mut pat = self.parse_pat_no_top_alt(Some("identifier"))?; + let mut pat = self.parse_pat_no_top_alt(Some(Expected::Identifier))?; // If we don't have `mut $ident (@ pat)?`, error. if let PatKind::Ident(BindingAnnotation(ByRef::No, m @ Mutability::Not), ..) = &mut pat.kind @@ -620,22 +612,6 @@ impl<'a> Parser<'a> { Ok(pat.into_inner().kind) } - /// Recover on `mut ref? ident @ pat` and suggest - /// that the order of `mut` and `ref` is incorrect. - fn recover_mut_ref_ident(&mut self, lo: Span) -> PResult<'a, PatKind> { - let mutref_span = lo.to(self.prev_token.span); - self.struct_span_err(mutref_span, "the order of `mut` and `ref` is incorrect") - .span_suggestion( - mutref_span, - "try switching the order", - "ref mut", - Applicability::MachineApplicable, - ) - .emit(); - - self.parse_pat_ident(BindingAnnotation::REF_MUT) - } - /// Turn all by-value immutable bindings in a pattern into mutable bindings. /// Returns `true` if any change was made. fn make_all_value_bindings_mutable(pat: &mut P<Pat>) -> bool { @@ -660,16 +636,13 @@ impl<'a> Parser<'a> { /// Error on `mut $pat` where `$pat` is not an ident. fn ban_mut_general_pat(&self, lo: Span, pat: &Pat, changed_any_binding: bool) { let span = lo.to(pat.span); - let fix = pprust::pat_to_string(&pat); - let (problem, suggestion) = if changed_any_binding { - ("`mut` must be attached to each individual binding", "add `mut` to each binding") + let pat = pprust::pat_to_string(&pat); + + self.sess.emit_err(if changed_any_binding { + InvalidMutInPattern::NestedIdent { span, pat } } else { - ("`mut` must be followed by a named binding", "remove the `mut` prefix") - }; - self.struct_span_err(span, problem) - .span_suggestion(span, suggestion, fix, Applicability::MachineApplicable) - .note("`mut` may be followed by `variable` and `variable @ pattern`") - .emit(); + InvalidMutInPattern::NonIdent { span, pat } + }); } /// Eat any extraneous `mut`s and error + recover if we ate any. @@ -680,15 +653,7 @@ impl<'a> Parser<'a> { return; } - let span = lo.to(self.prev_token.span); - self.struct_span_err(span, "`mut` on a binding may not be repeated") - .span_suggestion( - span, - "remove the additional `mut`s", - "", - Applicability::MachineApplicable, - ) - .emit(); + self.sess.emit_err(RepeatedMutInPattern { span: lo.to(self.prev_token.span) }); } /// Parse macro invocation @@ -702,11 +667,11 @@ impl<'a> Parser<'a> { fn fatal_unexpected_non_pat( &mut self, err: DiagnosticBuilder<'a, ErrorGuaranteed>, - expected: Expected, + expected: Option<Expected>, ) -> PResult<'a, P<Pat>> { err.cancel(); - let expected = expected.unwrap_or("pattern"); + let expected = Expected::to_string_or_fallback(expected); let msg = format!("expected {}, found {}", expected, super::token_descr(&self.token)); let mut err = self.struct_span_err(self.token.span, &msg); @@ -774,28 +739,18 @@ impl<'a> Parser<'a> { let _ = self.parse_pat_range_end().map_err(|e| e.cancel()); } - self.error_inclusive_range_with_extra_equals(span_with_eq); + self.sess.emit_err(InclusiveRangeExtraEquals { span: span_with_eq }); } token::Gt if no_space => { - self.error_inclusive_range_match_arrow(span); + let after_pat = span.with_hi(span.hi() - rustc_span::BytePos(1)).shrink_to_hi(); + self.sess.emit_err(InclusiveRangeMatchArrow { span, arrow: tok.span, after_pat }); + } + _ => { + self.sess.emit_err(InclusiveRangeNoEnd { span }); } - _ => self.error_inclusive_range_with_no_end(span), } } - fn error_inclusive_range_with_extra_equals(&self, span: Span) { - self.sess.emit_err(InclusiveRangeExtraEquals { span }); - } - - fn error_inclusive_range_match_arrow(&self, span: Span) { - let after_pat = span.with_hi(span.hi() - rustc_span::BytePos(1)).shrink_to_hi(); - self.sess.emit_err(InclusiveRangeMatchArrow { span, after_pat }); - } - - fn error_inclusive_range_with_no_end(&self, span: Span) { - self.sess.emit_err(InclusiveRangeNoEnd { span }); - } - /// Parse a range-to pattern, `..X` or `..=X` where `X` remains to be parsed. /// /// The form `...X` is prohibited to reduce confusion with the potential @@ -804,14 +759,7 @@ impl<'a> Parser<'a> { let end = self.parse_pat_range_end()?; if let RangeEnd::Included(syn @ RangeSyntax::DotDotDot) = &mut re.node { *syn = RangeSyntax::DotDotEq; - self.struct_span_err(re.span, "range-to patterns with `...` are not allowed") - .span_suggestion_short( - re.span, - "use `..=` instead", - "..=", - Applicability::MachineApplicable, - ) - .emit(); + self.sess.emit_err(DotDotDotRangeToPatternNotAllowed { span: re.span }); } Ok(PatKind::Range(None, Some(end), re)) } @@ -876,7 +824,7 @@ impl<'a> Parser<'a> { 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"))?) + Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern))?) } else { None }; @@ -887,8 +835,8 @@ impl<'a> Parser<'a> { // binding mode then we do not end up here, because the lookahead // will direct us over to `parse_enum_variant()`. if self.token == token::OpenDelim(Delimiter::Parenthesis) { - return Err(self - .struct_span_err(self.prev_token.span, "expected identifier, found enum pattern")); + return Err(EnumPatternInsteadOfIdentifier { span: self.prev_token.span } + .into_diagnostic(&self.sess.span_diagnostic)); } Ok(PatKind::Ident(binding_annotation, ident, sub)) @@ -970,7 +918,7 @@ impl<'a> Parser<'a> { // We cannot use `parse_pat_ident()` since it will complain `box` // is not an identifier. let sub = if self.eat(&token::At) { - Some(self.parse_pat_no_top_alt(Some("binding pattern"))?) + Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern))?) } else { None }; @@ -1005,7 +953,8 @@ impl<'a> Parser<'a> { // check that a comma comes after every field if !ate_comma { - let err = self.struct_span_err(self.token.span, "expected `,`"); + let err = ExpectedCommaAfterPatternField { span: self.token.span } + .into_diagnostic(&self.sess.span_diagnostic); if let Some(mut delayed) = delayed_err { delayed.emit(); } @@ -1013,12 +962,15 @@ impl<'a> Parser<'a> { } ate_comma = false; - if self.check(&token::DotDot) || self.token == token::DotDotDot { + if self.check(&token::DotDot) + || self.check_noexpect(&token::DotDotDot) + || self.check_keyword(kw::Underscore) + { etc = true; let mut etc_sp = self.token.span; - self.recover_one_fewer_dotdot(); - self.bump(); // `..` || `...` + self.recover_bad_dot_dot(); + self.bump(); // `..` || `...` || `_` if self.token == token::CloseDelim(Delimiter::Brace) { etc_span = Some(etc_sp); @@ -1111,21 +1063,15 @@ impl<'a> Parser<'a> { Ok((fields, etc)) } - /// Recover on `...` as if it were `..` to avoid further errors. + /// Recover on `...` or `_` as if it were `..` to avoid further errors. /// See issue #46718. - fn recover_one_fewer_dotdot(&self) { - if self.token != token::DotDotDot { + fn recover_bad_dot_dot(&self) { + if self.token == token::DotDot { return; } - self.struct_span_err(self.token.span, "expected field pattern, found `...`") - .span_suggestion( - self.token.span, - "to omit remaining fields, use one fewer `.`", - "..", - Applicability::MachineApplicable, - ) - .emit(); + let token_str = pprust::token_to_string(&self.token); + self.sess.emit_err(DotDotDotForRemainingFields { span: self.token.span, token_str }); } fn parse_pat_field(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, PatField> { diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 58c7a398f14..647639b9b62 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -1,7 +1,7 @@ use super::attr::InnerAttrForbiddenReason; use super::diagnostics::AttemptLocalParseRecovery; use super::expr::LhsExpr; -use super::pat::RecoverComma; +use super::pat::{PatternLocation, RecoverComma}; use super::path::PathStyle; use super::TrailingToken; use super::{ @@ -275,7 +275,8 @@ impl<'a> Parser<'a> { } self.report_invalid_identifier_error()?; - let (pat, colon) = self.parse_pat_before_ty(None, RecoverComma::Yes, "`let` bindings")?; + let (pat, colon) = + self.parse_pat_before_ty(None, RecoverComma::Yes, PatternLocation::LetBinding)?; let (err, ty) = if colon { // Save the state of the parser before parsing type normally, in case there is a `:` diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 8b4f0ab8feb..a19ea04fa5e 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -1,6 +1,12 @@ use super::{Parser, PathStyle, TokenType}; -use crate::errors::{ExpectedFnPathFoundFnKeyword, FnPtrWithGenerics, FnPtrWithGenericsSugg}; +use crate::errors::{ + DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType, + FnPointerCannotBeAsync, FnPointerCannotBeConst, FnPtrWithGenerics, FnPtrWithGenericsSugg, + InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, + NegativeBoundsNotSupported, NegativeBoundsNotSupportedSugg, NestedCVariadicType, + ReturnTypesUseThinArrow, +}; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use ast::DUMMY_NODE_ID; @@ -11,8 +17,7 @@ use rustc_ast::{ self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind, }; -use rustc_ast_pretty::pprust; -use rustc_errors::{pluralize, struct_span_err, Applicability, PResult}; +use rustc_errors::{Applicability, PResult}; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Symbol; @@ -44,24 +49,17 @@ pub(super) enum AllowPlus { No, } -#[derive(PartialEq, Clone, Copy)] +#[derive(PartialEq)] pub(super) enum RecoverQPath { Yes, No, } -#[derive(PartialEq, Clone, Copy)] pub(super) enum RecoverQuestionMark { Yes, No, } -#[derive(PartialEq, Clone, Copy)] -pub(super) enum RecoverAnonEnum { - Yes, - No, -} - /// Signals whether parsing a type should recover `->`. /// /// More specifically, when parsing a function like: @@ -94,7 +92,7 @@ impl RecoverReturnSign { } // Is `...` (`CVarArgs`) legal at this level of type parsing? -#[derive(PartialEq, Clone, Copy)] +#[derive(PartialEq)] enum AllowCVariadic { Yes, No, @@ -119,7 +117,6 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, - RecoverAnonEnum::No, ) } @@ -134,7 +131,6 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, Some(ty_params), RecoverQuestionMark::Yes, - RecoverAnonEnum::No, ) } @@ -149,7 +145,6 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, - RecoverAnonEnum::Yes, ) } @@ -167,7 +162,6 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, - RecoverAnonEnum::No, ) } @@ -181,7 +175,6 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::No, - RecoverAnonEnum::No, ) } @@ -193,7 +186,6 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::No, - RecoverAnonEnum::No, ) } @@ -206,7 +198,6 @@ impl<'a> Parser<'a> { RecoverReturnSign::OnlyFatArrow, None, RecoverQuestionMark::Yes, - RecoverAnonEnum::No, ) } @@ -226,21 +217,13 @@ impl<'a> Parser<'a> { recover_return_sign, None, RecoverQuestionMark::Yes, - RecoverAnonEnum::Yes, )?; FnRetTy::Ty(ty) } else if recover_return_sign.can_recover(&self.token.kind) { // Don't `eat` to prevent `=>` from being added as an expected token which isn't // actually expected and could only confuse users self.bump(); - self.struct_span_err(self.prev_token.span, "return types are denoted using `->`") - .span_suggestion_short( - self.prev_token.span, - "use `->` instead", - "->", - Applicability::MachineApplicable, - ) - .emit(); + self.sess.emit_err(ReturnTypesUseThinArrow { span: self.prev_token.span }); let ty = self.parse_ty_common( allow_plus, AllowCVariadic::No, @@ -248,7 +231,6 @@ impl<'a> Parser<'a> { recover_return_sign, None, RecoverQuestionMark::Yes, - RecoverAnonEnum::Yes, )?; FnRetTy::Ty(ty) } else { @@ -264,7 +246,6 @@ impl<'a> Parser<'a> { recover_return_sign: RecoverReturnSign, ty_generics: Option<&Generics>, recover_question_mark: RecoverQuestionMark, - recover_anon_enum: RecoverAnonEnum, ) -> PResult<'a, P<Ty>> { let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); @@ -328,7 +309,7 @@ impl<'a> Parser<'a> { AllowCVariadic::No => { // FIXME(Centril): Should we just allow `...` syntactically // anywhere in a type and use semantic restrictions instead? - self.error_illegal_c_varadic_ty(lo); + self.sess.emit_err(NestedCVariadicType { span: lo.to(self.prev_token.span) }); TyKind::Err } } @@ -348,50 +329,9 @@ impl<'a> Parser<'a> { AllowPlus::Yes => self.maybe_recover_from_bad_type_plus(&ty)?, AllowPlus::No => self.maybe_report_ambiguous_plus(impl_dyn_multi, &ty), } - if RecoverQuestionMark::Yes == recover_question_mark { + if let RecoverQuestionMark::Yes = recover_question_mark { ty = self.maybe_recover_from_question_mark(ty); } - if recover_anon_enum == RecoverAnonEnum::Yes - && self.check_noexpect(&token::BinOp(token::Or)) - && self.look_ahead(1, |t| t.can_begin_type()) - { - let mut pipes = vec![self.token.span]; - let mut types = vec![ty]; - loop { - if !self.eat(&token::BinOp(token::Or)) { - break; - } - pipes.push(self.prev_token.span); - types.push(self.parse_ty_common( - allow_plus, - allow_c_variadic, - recover_qpath, - recover_return_sign, - ty_generics, - recover_question_mark, - RecoverAnonEnum::No, - )?); - } - let mut err = self.struct_span_err(pipes, "anonymous enums are not supported"); - for ty in &types { - err.span_label(ty.span, ""); - } - err.help(&format!( - "create a named `enum` and use it here instead:\nenum Name {{\n{}\n}}", - types - .iter() - .enumerate() - .map(|(i, t)| format!( - " Variant{}({}),", - i + 1, // Lets not confuse people with zero-indexing :) - pprust::to_string(|s| s.print_type(&t)), - )) - .collect::<Vec<_>>() - .join("\n"), - )); - err.emit(); - return Ok(self.mk_ty(lo.to(self.prev_token.span), TyKind::Err)); - } if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) } } @@ -431,8 +371,7 @@ impl<'a> Parser<'a> { let lt_no_plus = self.check_lifetime() && !self.look_ahead(1, |t| t.is_like_plus()); let bounds = self.parse_generic_bounds_common(allow_plus, None)?; if lt_no_plus { - self.struct_span_err(lo, "lifetime in trait object type must be followed by `+`") - .emit(); + self.sess.emit_err(NeedPlusAfterTraitObjectLifetime { span: lo }); } Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None)) } @@ -466,14 +405,10 @@ 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; - 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()], - Applicability::HasPlaceholders, - ) - .emit(); + self.sess.emit_err(ExpectedMutOrConstInRawPointerType { + span, + after_asterisk: span.shrink_to_hi(), + }); Mutability::Not }); let ty = self.parse_ty_no_plus()?; @@ -528,16 +463,13 @@ impl<'a> Parser<'a> { let lifetime_span = self.token.span; let span = and_span.to(lifetime_span); - let mut err = self.struct_span_err(span, "lifetime must precede `mut`"); - if let Ok(lifetime_src) = self.span_to_snippet(lifetime_span) { - err.span_suggestion( - span, - "place the lifetime before `mut`", - format!("&{} mut", lifetime_src), - Applicability::MaybeIncorrect, - ); - } - err.emit(); + let (suggest_lifetime, snippet) = + if let Ok(lifetime_src) = self.span_to_snippet(lifetime_span) { + (Some(span), lifetime_src) + } else { + (None, String::new()) + }; + self.sess.emit_err(LifetimeAfterMut { span, suggest_lifetime, snippet }); opt_lifetime = Some(self.expect_lifetime()); } @@ -547,14 +479,7 @@ impl<'a> Parser<'a> { { // We have `&dyn mut ...`, which is invalid and should be `&mut dyn ...`. let span = and_span.to(self.look_ahead(1, |t| t.span)); - let mut err = self.struct_span_err(span, "`mut` must precede `dyn`"); - err.span_suggestion( - span, - "place `mut` before `dyn`", - "&mut dyn", - Applicability::MachineApplicable, - ); - err.emit(); + self.sess.emit_err(DynAfterMut { span }); // Recovery mutbl = Mutability::Mut; @@ -608,10 +533,10 @@ impl<'a> Parser<'a> { // If we ever start to allow `const fn()`, then update // feature gating for `#![feature(const_extern_fn)]` to // cover it. - self.error_fn_ptr_bad_qualifier(whole_span, span, "const"); + self.sess.emit_err(FnPointerCannotBeConst { span: whole_span, qualifier: span }); } if let ast::Async::Yes { span, .. } = asyncness { - self.error_fn_ptr_bad_qualifier(whole_span, span, "async"); + self.sess.emit_err(FnPointerCannotBeAsync { span: whole_span, qualifier: span }); } let decl_span = span_start.to(self.token.span); Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params: params, decl, decl_span }))) @@ -659,19 +584,6 @@ impl<'a> Parser<'a> { Ok(()) } - /// Emit an error for the given bad function pointer qualifier. - fn error_fn_ptr_bad_qualifier(&self, span: Span, qual_span: Span, qual: &str) { - self.struct_span_err(span, &format!("an `fn` pointer type cannot be `{}`", qual)) - .span_label(qual_span, format!("`{}` because of this", qual)) - .span_suggestion_short( - qual_span, - &format!("remove the `{}` qualifier", qual), - "", - Applicability::MaybeIncorrect, - ) - .emit(); - } - /// Parses an `impl B0 + ... + Bn` type. fn parse_impl_ty(&mut self, impl_dyn_multi: &mut bool) -> PResult<'a, TyKind> { // Always parse bounds greedily for better error recovery. @@ -702,7 +614,7 @@ impl<'a> Parser<'a> { /// Is a `dyn B0 + ... + Bn` type allowed here? fn is_explicit_dyn_type(&mut self) -> bool { self.check_keyword(kw::Dyn) - && (!self.token.uninterpolated_span().rust_2015() + && (self.token.uninterpolated_span().rust_2018() || self.look_ahead(1, |t| { (t.can_begin_bound() || t.kind == TokenKind::BinOp(token::Star)) && !can_continue_type_after_non_fn_ident(t) @@ -758,16 +670,6 @@ impl<'a> Parser<'a> { } } - fn error_illegal_c_varadic_ty(&self, lo: Span) { - struct_span_err!( - self.sess.span_diagnostic, - lo.to(self.prev_token.span), - E0743, - "C-variadic type `...` may not be nested inside another type", - ) - .emit(); - } - pub(super) fn parse_generic_bounds( &mut self, colon_span: Option<Span>, @@ -797,15 +699,7 @@ impl<'a> Parser<'a> { { if self.token.is_keyword(kw::Dyn) { // Account for `&dyn Trait + dyn Other`. - self.struct_span_err(self.token.span, "invalid `dyn` keyword") - .help("`dyn` is only needed at the start of a trait `+`-separated list") - .span_suggestion( - self.token.span, - "remove this keyword", - "", - Applicability::MachineApplicable, - ) - .emit(); + self.sess.emit_err(InvalidDynKeyword { span: self.token.span }); self.bump(); } match self.parse_generic_bound()? { @@ -842,11 +736,7 @@ impl<'a> Parser<'a> { bounds: &[GenericBound], negative_bounds: Vec<Span>, ) { - let negative_bounds_len = negative_bounds.len(); - let last_span = *negative_bounds.last().expect("no negative bounds, but still error?"); - let mut err = self.struct_span_err(negative_bounds, "negative bounds are not supported"); - err.span_label(last_span, "negative bounds are not supported"); - if let Some(bound_list) = colon_span { + let sub = if let Some(bound_list) = colon_span { let bound_list = bound_list.to(self.prev_token.span); let mut new_bound_list = String::new(); if !bounds.is_empty() { @@ -857,14 +747,18 @@ impl<'a> Parser<'a> { } new_bound_list = new_bound_list.replacen(" +", ":", 1); } - err.tool_only_span_suggestion( + + Some(NegativeBoundsNotSupportedSugg { bound_list, - &format!("remove the bound{}", pluralize!(negative_bounds_len)), - new_bound_list, - Applicability::MachineApplicable, - ); - } - err.emit(); + num_bounds: negative_bounds.len(), + fixed: new_bound_list, + }) + } else { + None + }; + + let last_span = *negative_bounds.last().expect("no negative bounds, but still error?"); + self.sess.emit_err(NegativeBoundsNotSupported { negative_bounds, last_span, sub }); } /// Parses a bound according to the grammar: |
