diff options
Diffstat (limited to 'compiler/rustc_parse/src/parser')
| -rw-r--r-- | compiler/rustc_parse/src/parser/attr.rs | 159 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/attr_wrapper.rs | 18 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/diagnostics.rs | 942 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 403 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/item.rs | 140 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/mod.rs | 120 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/pat.rs | 5 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/stmt.rs | 104 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/ty.rs | 11 |
9 files changed, 608 insertions, 1294 deletions
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 5fd69b15ecc..58be348883c 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -1,27 +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; // 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, @@ -40,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 { @@ -58,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( @@ -69,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::parser::note); err.span_suggestion_verbose( replacement_span, - "you might have meant to write a regular comment", + fluent::parser::suggestion, "", rustc_errors::Applicability::MachineApplicable, ); @@ -113,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={:?}", @@ -122,35 +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( - &self.sess.attr_id_generator, - 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)) }) } @@ -190,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::parser::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::parser::sugg_change_inner_to_outer, match attr_type { OuterAttributeType::Attribute => "", OuterAttributeType::DocBlockComment => "*", @@ -222,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::parser::label_attr) + .span_label(prev_doc_comment_span, fluent::parser::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::parser::label_attr) + .span_label(prev_outer_attr_sp, fluent::parser::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, @@ -246,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(); } @@ -337,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) @@ -435,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 86c386b94c8..0dc05475ce9 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -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 } @@ -96,9 +91,6 @@ struct LazyAttrTokenStreamImpl { replace_ranges: Box<[ReplaceRange]>, } -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(LazyAttrTokenStreamImpl, 144); - impl ToAttrTokenStream for LazyAttrTokenStreamImpl { fn to_attr_token_stream(&self) -> AttrTokenStream { // The token produced by the final call to `{,inlined_}next` was not @@ -461,3 +453,13 @@ fn make_token_stream( } 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; + // These are in alphabetical order, which is easy to maintain. + static_assert_size!(AttrWrapper, 16); + static_assert_size!(LazyAttrTokenStreamImpl, 144); +} diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index dcea11eadcb..f57bd9cec19 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; @@ -19,8 +32,8 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ fluent, Applicability, DiagnosticBuilder, DiagnosticMessage, Handler, MultiSpan, PResult, }; -use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed}; -use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_errors::{pluralize, Diagnostic, ErrorGuaranteed, IntoDiagnostic}; +use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{Span, SpanSnippetError, DUMMY_SP}; @@ -30,9 +43,6 @@ use std::mem::take; use crate::parser; -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 { @@ -52,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>>; @@ -242,509 +224,6 @@ impl MultiSugg { } } -#[derive(Diagnostic)] -#[diag(parser::maybe_report_ambiguous_plus)] -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")] -struct BadTypePlus { - pub ty: String, - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub sub: BadTypePlusSub, -} - -#[derive(Subdiagnostic)] -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(Diagnostic)] -#[diag(parser::maybe_recover_from_bad_qpath_stage_2)] -struct BadQPathStage2 { - #[primary_span] - #[suggestion(applicability = "maybe-incorrect")] - span: Span, - ty: String, -} - -#[derive(Diagnostic)] -#[diag(parser::incorrect_semicolon)] -struct IncorrectSemicolon<'a> { - #[primary_span] - #[suggestion_short(applicability = "machine-applicable")] - span: Span, - #[help] - opt_help: Option<()>, - name: &'a str, -} - -#[derive(Diagnostic)] -#[diag(parser::incorrect_use_of_await)] -struct IncorrectUseOfAwait { - #[primary_span] - #[suggestion(parser::parentheses_suggestion, applicability = "machine-applicable")] - span: Span, -} - -#[derive(Diagnostic)] -#[diag(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(Diagnostic)] -#[diag(parser::in_in_typo)] -struct InInTypo { - #[primary_span] - span: Span, - #[suggestion(applicability = "machine-applicable")] - sugg_span: Span, -} - -#[derive(Diagnostic)] -#[diag(parser::invalid_variable_declaration)] -pub struct InvalidVariableDeclaration { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub sub: InvalidVariableDeclarationSub, -} - -#[derive(Subdiagnostic)] -pub 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( - parser::use_instead, - applicability = "machine-applicable", - code = "{correct}" - )] - Correctable { - #[primary_span] - span: Span, - invalid: String, - correct: String, - }, - #[label(parser::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( - parser::use_amp_amp_for_conjunction, - applicability = "machine-applicable", - code = "&&" - )] - Conjunction(#[primary_span] Span), - #[suggestion_short( - parser::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, -); - -#[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(parser::condition_possibly_unfinished)] - UnfinishedCondition(#[primary_span] Span), - #[help(parser::add_then_block)] - AddThenBlock(#[primary_span] Span), -} - -#[derive(Diagnostic)] -#[diag(parser::if_expression_missing_condition)] -pub(crate) struct IfExpressionMissingCondition { - #[primary_span] - #[label(parser::condition_label)] - pub if_span: Span, - #[label(parser::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(parser::branch_label)] - pub branch_span: Span, - - #[label(parser::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(parser::use_in_not_of, applicability = "maybe-incorrect", code = "in")] - InNotOf(#[primary_span] Span), - #[suggestion_short(parser::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(parser::suggest_exclusive_range, applicability = "maybe-incorrect", code = "..")] - #[suggestion(parser::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, -} - // SnapshotParser is used to create a snapshot of the parser // without causing duplicate errors being emitted when the `Parser` // is dropped. @@ -769,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, @@ -822,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, @@ -837,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( @@ -929,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. @@ -958,11 +425,13 @@ 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) @@ -980,11 +449,13 @@ 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); } } @@ -1021,6 +492,7 @@ 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 { @@ -1029,7 +501,7 @@ impl<'a> Parser<'a> { self.prev_token.span, &format!("write `fn` instead of `{symbol}` to declare a function"), "fn", - appl, + Applicability::MachineApplicable, ); } } @@ -1043,7 +515,7 @@ impl<'a> Parser<'a> { self.prev_token.span, "write `pub` instead of `public` to make the item public", "pub", - appl, + Applicability::MachineApplicable, ); } @@ -1181,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)], @@ -1387,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 @@ -1427,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); @@ -1476,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, ) @@ -1508,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 */ { @@ -1521,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) | @@ -1544,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` @@ -1560,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) => { @@ -1577,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) => { @@ -1626,18 +1070,11 @@ impl<'a> Parser<'a> { 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 @@ -1664,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(); // `::` @@ -1673,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. @@ -1684,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. @@ -1708,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)); } @@ -1767,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 @@ -2201,19 +1633,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 { @@ -2432,12 +1861,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)) @@ -2449,9 +1873,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 }); } } @@ -2572,19 +1994,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 = @@ -2593,11 +2003,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) } @@ -2642,7 +2050,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 @@ -2746,20 +2154,13 @@ 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) } @@ -2774,24 +2175,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 ¶ms[..] { + [] => 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 })) } @@ -2809,20 +2216,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) } } @@ -2928,24 +2330,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( @@ -3141,17 +2525,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 ae77961b7bc..11301f03e48 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1,27 +1,33 @@ -use super::diagnostics::{ - CatchAfterTry, CommaAfterBaseStruct, DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, - ExpectedElseBlock, ExpectedExpressionFoundLet, FieldExpressionWithGeneric, - FloatLiteralRequiresIntegerPart, IfExpressionMissingCondition, IfExpressionMissingThenBlock, - IfExpressionMissingThenBlockSub, InvalidBlockMacroSegment, InvalidComparisonOperator, - InvalidComparisonOperatorSub, InvalidLogicalOperator, InvalidLogicalOperatorSub, - LeftArrowOperator, LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, - MalformedLoopLabel, MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray, - NotAsNegationOperator, NotAsNegationOperatorSub, OuterAttributeNotAllowedOnIfElse, - RequireColonAfterLabeledExpression, SnapshotParser, TildeAsUnaryOperator, - UnexpectedTokenAfterLabel, -}; +use super::diagnostics::SnapshotParser; use super::pat::{CommaRecoveryMode, RecoverColon, RecoverComma, PARAM_EXPECTED}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{ AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions, SemiColonMode, SeqSep, TokenExpectType, TokenType, TrailingToken, }; -use crate::maybe_recover_from_interpolated_ty_qpath; -use crate::parser::diagnostics::{ - IntLiteralTooLarge, InvalidFloatLiteralSuffix, InvalidFloatLiteralWidth, - InvalidIntLiteralWidth, InvalidNumLiteralBasePrefix, InvalidNumLiteralSuffix, - MissingCommaAfterMatchArm, +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; use rustc_ast::ptr::P; @@ -38,6 +44,7 @@ use rustc_ast::{ClosureBinder, StmtKind}; use rustc_ast_pretty::pprust; use rustc_errors::IntoDiagnostic; use rustc_errors::{Applicability, Diagnostic, PResult}; +use rustc_session::errors::ExprParenthesesNeeded; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_span::source_map::{self, Span, Spanned}; @@ -421,13 +428,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. @@ -578,21 +583,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) @@ -755,9 +755,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, @@ -771,33 +796,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) => { @@ -1158,7 +1157,9 @@ 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); + if let Some(suffix) = suffix { + self.expect_no_tuple_index_suffix(span, suffix); + } self.mk_expr(lo.to(span), field) } @@ -1196,9 +1197,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)) => { @@ -1208,29 +1208,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(); } @@ -1314,7 +1310,7 @@ impl<'a> Parser<'a> { // 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 }) @@ -1537,15 +1533,19 @@ impl<'a> Parser<'a> { && (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt)) { // We're probably inside of a `Path<'a>` that needs a turbofish - self.sess.emit_err(UnexpectedTokenAfterLabel(self.token.span)); + 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 { - // FIXME: use UnexpectedTokenAfterLabel, needs multipart suggestions - 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| { @@ -1572,28 +1572,15 @@ 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 suppress future errors about `break 'label`. let stmt = self.mk_stmt(span, StmtKind::Expr(expr)); @@ -1601,7 +1588,7 @@ impl<'a> Parser<'a> { self.mk_expr(span, ExprKind::Block(blk, label)) }); - err.emit(); + self.sess.emit_err(err); expr }?; @@ -1672,19 +1659,13 @@ impl<'a> Parser<'a> { // 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(), 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(); + 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) @@ -1756,9 +1737,8 @@ 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; } @@ -1790,7 +1770,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(), + }); } } @@ -1818,13 +1801,6 @@ impl<'a> Parser<'a> { } } - fn error_float_lits_must_have_int_part(&self, token: &Token) { - self.sess.emit_err(FloatLiteralRequiresIntegerPart { - span: token.span, - correct: pprust::token_to_string(token).into_owned(), - }); - } - 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 { @@ -1853,11 +1829,13 @@ 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"); @@ -1883,15 +1861,12 @@ impl<'a> Parser<'a> { } } 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.sess.emit_err(IntLiteralTooLarge { span }); @@ -1899,38 +1874,17 @@ impl<'a> Parser<'a> { } } - 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 }); } } @@ -1964,14 +1918,13 @@ impl<'a> Parser<'a> { let mut snapshot = self.create_snapshot_for_diagnostic(); 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)) @@ -2134,7 +2087,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) } @@ -2515,39 +2469,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 @@ -2836,23 +2773,19 @@ impl<'a> Parser<'a> { 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, diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index e55b5ce71cd..25425fbb2c6 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}; @@ -13,7 +15,7 @@ use rustc_ast::{EnumDef, FieldDef, Generics, TraitRef, Ty, TyKind, Variant, Vari 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}; @@ -664,6 +666,14 @@ impl<'a> Parser<'a> { 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.extend(self.parse_inner_attributes()?); @@ -750,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(); @@ -1283,12 +1293,10 @@ impl<'a> Parser<'a> { /// 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, @@ -1305,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))) @@ -1332,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) @@ -1386,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>;` @@ -1395,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) { @@ -1425,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); @@ -1446,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(); @@ -1460,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; } @@ -1555,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 @@ -1564,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, - ); + err.missing_comma = Some(sp); } - return Err(err); + return Err(err.into_diagnostic(&self.sess.span_diagnostic)); } } _ => { @@ -1715,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, @@ -1723,18 +1753,40 @@ 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 AttrVec::new(), fn_parse_mode, lo, &inherited_vis) - { - db.delay_as_bug(); + 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() + } } - 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() }; diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 4cb198561e0..b934e087608 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,7 +31,7 @@ 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}; @@ -41,6 +40,11 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol}; 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; @@ -298,7 +302,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() { @@ -310,6 +317,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, @@ -406,24 +414,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, @@ -518,9 +541,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(), }) } @@ -1144,7 +1169,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 { @@ -1342,23 +1369,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(()) } @@ -1384,14 +1396,7 @@ impl<'a> Parser<'a> { Err(Some(lit)) => match lit.kind { 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 } }, @@ -1432,25 +1437,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) } diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 120a3c267f1..0250b518243 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -1,5 +1,5 @@ use super::{ForceCollect, Parser, PathStyle, TrailingToken}; -use crate::parser::diagnostics::RemoveLet; +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; @@ -10,6 +10,7 @@ use rustc_ast::{ }; 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}; @@ -693,7 +694,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) diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 3d957406b19..a61e77b7c3b 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -1,7 +1,5 @@ -use super::attr::DEFAULT_INNER_ATTR_FORBIDDEN; -use super::diagnostics::{ - AttemptLocalParseRecovery, Error, InvalidVariableDeclaration, InvalidVariableDeclarationSub, -}; +use super::attr::InnerAttrForbiddenReason; +use super::diagnostics::AttemptLocalParseRecovery; use super::expr::LhsExpr; use super::pat::RecoverComma; use super::path::PathStyle; @@ -9,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; @@ -112,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 { @@ -202,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 }); } } } @@ -255,17 +258,7 @@ impl<'a> Parser<'a> { let lo = self.prev_token.span; if self.token.is_keyword(kw::Const) && self.look_ahead(1, |t| t.is_ident()) { - self.struct_span_err( - lo.to(self.token.span), - "`const` and `let` are mutually exclusive", - ) - .span_suggestion( - lo.to(self.token.span), - "remove `let`", - "const", - Applicability::MaybeIncorrect, - ) - .emit(); + self.sess.emit_err(ConstLetMutuallyExclusive { span: lo.to(self.token.span) }); self.bump(); } @@ -363,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(), + }, + }); } } @@ -409,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 } @@ -434,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) } diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index b47f0c09783..2a8512acf8c 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 }); |
