about summary refs log tree commit diff
path: root/compiler/rustc_parse/src/parser
diff options
context:
space:
mode:
authorMatthew Kelly <matthew.kelly2@gmail.com>2022-08-31 19:39:39 -0400
committerMatthew Kelly <matthew.kelly2@gmail.com>2022-08-31 19:39:39 -0400
commiteda2a401457ba645a32bdc5b9e7e90214e3e4e24 (patch)
tree76c4a12cb26666f03aa37a81abe27782def16f1d /compiler/rustc_parse/src/parser
parent4a443dfb8227d407ff3f0542cb6e688833708ba9 (diff)
parent9243168fa5615ec8ebe9164c6bc2fdcccffd08b6 (diff)
downloadrust-eda2a401457ba645a32bdc5b9e7e90214e3e4e24.tar.gz
rust-eda2a401457ba645a32bdc5b9e7e90214e3e4e24.zip
Merge remote-tracking branch 'origin/master' into mpk/add-long-error-message-for-E0311
Diffstat (limited to 'compiler/rustc_parse/src/parser')
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs8
-rw-r--r--compiler/rustc_parse/src/parser/attr_wrapper.rs25
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs417
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs486
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs14
-rw-r--r--compiler/rustc_parse/src/parser/item.rs59
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs2
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs10
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs49
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs26
10 files changed, 678 insertions, 418 deletions
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index acdbddf4099..72ab96b5ca6 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -34,7 +34,7 @@ enum OuterAttributeType {
 impl<'a> Parser<'a> {
     /// Parses attributes that appear before an item.
     pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, AttrWrapper> {
-        let mut outer_attrs: Vec<ast::Attribute> = Vec::new();
+        let mut outer_attrs = ast::AttrVec::new();
         let mut just_parsed_doc_comment = false;
         let start_pos = self.token_cursor.num_next_calls;
         loop {
@@ -106,7 +106,7 @@ impl<'a> Parser<'a> {
                 break;
             }
         }
-        Ok(AttrWrapper::new(outer_attrs.into(), start_pos))
+        Ok(AttrWrapper::new(outer_attrs, start_pos))
     }
 
     /// Matches `attribute = # ! [ meta_item ]`.
@@ -283,8 +283,8 @@ impl<'a> Parser<'a> {
     /// terminated by a semicolon.
     ///
     /// Matches `inner_attrs*`.
-    pub(crate) fn parse_inner_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
-        let mut attrs: Vec<ast::Attribute> = vec![];
+    pub(crate) fn parse_inner_attributes(&mut self) -> PResult<'a, ast::AttrVec> {
+        let mut attrs = ast::AttrVec::new();
         loop {
             let start_pos: u32 = self.token_cursor.num_next_calls.try_into().unwrap();
             // Only try to parse if it is an inner attribute (has `!`).
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index 6c750ff428f..b564f4ad92c 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -15,11 +15,11 @@ use std::ops::Range;
 /// for the attribute target. This allows us to perform cfg-expansion on
 /// a token stream before we invoke a derive proc-macro.
 ///
-/// This wrapper prevents direct access to the underlying `Vec<ast::Attribute>`.
+/// This wrapper prevents direct access to the underlying `ast::AttrVec>`.
 /// Parsing code can only get access to the underlying attributes
 /// by passing an `AttrWrapper` to `collect_tokens_trailing_tokens`.
 /// This makes it difficult to accidentally construct an AST node
-/// (which stores a `Vec<ast::Attribute>`) without first collecting tokens.
+/// (which stores an `ast::AttrVec`) without first collecting tokens.
 ///
 /// This struct has its own module, to ensure that the parser code
 /// cannot directly access the `attrs` field
@@ -49,9 +49,10 @@ impl AttrWrapper {
         self.attrs
     }
 
+    // Prepend `self.attrs` to `attrs`.
     // FIXME: require passing an NT to prevent misuse of this method
-    pub(crate) fn prepend_to_nt_inner(self, attrs: &mut Vec<Attribute>) {
-        let mut self_attrs: Vec<_> = self.attrs.into();
+    pub(crate) fn prepend_to_nt_inner(self, attrs: &mut AttrVec) {
+        let mut self_attrs = self.attrs.clone();
         std::mem::swap(attrs, &mut self_attrs);
         attrs.extend(self_attrs);
     }
@@ -116,7 +117,7 @@ impl CreateTokenStream for LazyTokenStreamImpl {
 
         if !self.replace_ranges.is_empty() {
             let mut tokens: Vec<_> = tokens.collect();
-            let mut replace_ranges = self.replace_ranges.clone();
+            let mut replace_ranges = self.replace_ranges.to_vec();
             replace_ranges.sort_by_key(|(range, _)| range.start);
 
             #[cfg(debug_assertions)]
@@ -146,7 +147,7 @@ impl CreateTokenStream for LazyTokenStreamImpl {
             // start position, we ensure that any replace range which encloses
             // another replace range will capture the *replaced* tokens for the inner
             // range, not the original tokens.
-            for (range, new_tokens) in replace_ranges.iter().rev() {
+            for (range, new_tokens) in replace_ranges.into_iter().rev() {
                 assert!(!range.is_empty(), "Cannot replace an empty range: {:?}", range);
                 // Replace ranges are only allowed to decrease the number of tokens.
                 assert!(
@@ -165,7 +166,7 @@ impl CreateTokenStream for LazyTokenStreamImpl {
 
                 tokens.splice(
                     (range.start as usize)..(range.end as usize),
-                    new_tokens.clone().into_iter().chain(filler),
+                    new_tokens.into_iter().chain(filler),
                 );
             }
             make_token_stream(tokens.into_iter(), self.break_last_token)
@@ -196,7 +197,7 @@ impl<'a> Parser<'a> {
         &mut self,
         attrs: AttrWrapper,
         force_collect: ForceCollect,
-        f: impl FnOnce(&mut Self, Vec<ast::Attribute>) -> PResult<'a, (R, TrailingToken)>,
+        f: impl FnOnce(&mut Self, ast::AttrVec) -> PResult<'a, (R, TrailingToken)>,
     ) -> PResult<'a, R> {
         // We only bail out when nothing could possibly observe the collected tokens:
         // 1. We cannot be force collecting tokens (since force-collecting requires tokens
@@ -212,7 +213,7 @@ impl<'a> Parser<'a> {
             // or `#[cfg_attr]` attributes.
             && !self.capture_cfg
         {
-            return Ok(f(self, attrs.attrs.into())?.0);
+            return Ok(f(self, attrs.attrs)?.0);
         }
 
         let start_token = (self.token.clone(), self.token_spacing);
@@ -222,7 +223,7 @@ impl<'a> Parser<'a> {
         let prev_capturing = std::mem::replace(&mut self.capture_state.capturing, Capturing::Yes);
         let replace_ranges_start = self.capture_state.replace_ranges.len();
 
-        let ret = f(self, attrs.attrs.into());
+        let ret = f(self, attrs.attrs);
 
         self.capture_state.capturing = prev_capturing;
 
@@ -321,7 +322,7 @@ impl<'a> Parser<'a> {
             self.capture_state.replace_ranges[replace_ranges_start..replace_ranges_end]
                 .iter()
                 .cloned()
-                .chain(inner_attr_replace_ranges.clone().into_iter())
+                .chain(inner_attr_replace_ranges.iter().cloned())
                 .map(|(range, tokens)| {
                     ((range.start - start_calls)..(range.end - start_calls), tokens)
                 })
@@ -352,7 +353,7 @@ impl<'a> Parser<'a> {
         // on the captured token stream.
         if self.capture_cfg
             && matches!(self.capture_state.capturing, Capturing::Yes)
-            && has_cfg_or_cfg_attr(&final_attrs)
+            && has_cfg_or_cfg_attr(final_attrs)
         {
             let attr_data = AttributesData { attrs: final_attrs.to_vec().into(), tokens };
 
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 93e70e9abda..f0ea1dfe297 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -22,7 +22,7 @@ use rustc_errors::{
 use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed};
 use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::{kw, Ident};
+use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::{Span, SpanSnippetError, DUMMY_SP};
 use std::ops::{Deref, DerefMut};
 
@@ -244,7 +244,7 @@ impl MultiSugg {
 }
 
 #[derive(SessionDiagnostic)]
-#[error(parser::maybe_report_ambiguous_plus)]
+#[diag(parser::maybe_report_ambiguous_plus)]
 struct AmbiguousPlus {
     pub sum_ty: String,
     #[primary_span]
@@ -253,7 +253,7 @@ struct AmbiguousPlus {
 }
 
 #[derive(SessionDiagnostic)]
-#[error(parser::maybe_recover_from_bad_type_plus, code = "E0178")]
+#[diag(parser::maybe_recover_from_bad_type_plus, code = "E0178")]
 struct BadTypePlus {
     pub ty: String,
     #[primary_span]
@@ -287,7 +287,7 @@ pub enum BadTypePlusSub {
 }
 
 #[derive(SessionDiagnostic)]
-#[error(parser::maybe_recover_from_bad_qpath_stage_2)]
+#[diag(parser::maybe_recover_from_bad_qpath_stage_2)]
 struct BadQPathStage2 {
     #[primary_span]
     #[suggestion(applicability = "maybe-incorrect")]
@@ -296,7 +296,7 @@ struct BadQPathStage2 {
 }
 
 #[derive(SessionDiagnostic)]
-#[error(parser::incorrect_semicolon)]
+#[diag(parser::incorrect_semicolon)]
 struct IncorrectSemicolon<'a> {
     #[primary_span]
     #[suggestion_short(applicability = "machine-applicable")]
@@ -307,7 +307,7 @@ struct IncorrectSemicolon<'a> {
 }
 
 #[derive(SessionDiagnostic)]
-#[error(parser::incorrect_use_of_await)]
+#[diag(parser::incorrect_use_of_await)]
 struct IncorrectUseOfAwait {
     #[primary_span]
     #[suggestion(parser::parentheses_suggestion, applicability = "machine-applicable")]
@@ -315,7 +315,7 @@ struct IncorrectUseOfAwait {
 }
 
 #[derive(SessionDiagnostic)]
-#[error(parser::incorrect_use_of_await)]
+#[diag(parser::incorrect_use_of_await)]
 struct IncorrectAwait {
     #[primary_span]
     span: Span,
@@ -326,7 +326,7 @@ struct IncorrectAwait {
 }
 
 #[derive(SessionDiagnostic)]
-#[error(parser::in_in_typo)]
+#[diag(parser::in_in_typo)]
 struct InInTypo {
     #[primary_span]
     span: Span,
@@ -334,6 +334,378 @@ struct InInTypo {
     sugg_span: Span,
 }
 
+#[derive(SessionDiagnostic)]
+#[diag(parser::invalid_variable_declaration)]
+pub struct InvalidVariableDeclaration {
+    #[primary_span]
+    pub span: Span,
+    #[subdiagnostic]
+    pub sub: InvalidVariableDeclarationSub,
+}
+
+#[derive(SessionSubdiagnostic)]
+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(SessionDiagnostic)]
+#[diag(parser::invalid_comparison_operator)]
+pub(crate) struct InvalidComparisonOperator {
+    #[primary_span]
+    pub span: Span,
+    pub invalid: String,
+    #[subdiagnostic]
+    pub sub: InvalidComparisonOperatorSub,
+}
+
+#[derive(SessionSubdiagnostic)]
+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(SessionDiagnostic)]
+#[diag(parser::invalid_logical_operator)]
+#[note]
+pub(crate) struct InvalidLogicalOperator {
+    #[primary_span]
+    pub span: Span,
+    pub incorrect: String,
+    #[subdiagnostic]
+    pub sub: InvalidLogicalOperatorSub,
+}
+
+#[derive(SessionSubdiagnostic)]
+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(SessionDiagnostic)]
+#[diag(parser::tilde_is_not_unary_operator)]
+pub(crate) struct TildeAsUnaryOperator(
+    #[primary_span]
+    #[suggestion_short(applicability = "machine-applicable", code = "!")]
+    pub Span,
+);
+
+#[derive(SessionDiagnostic)]
+#[diag(parser::unexpected_token_after_not)]
+pub(crate) struct NotAsNegationOperator {
+    #[primary_span]
+    pub negated: Span,
+    pub negated_desc: String,
+    #[suggestion_short(applicability = "machine-applicable", code = "!")]
+    pub not: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[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(SessionDiagnostic)]
+#[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(SessionDiagnostic)]
+#[diag(parser::field_expression_with_generic)]
+pub(crate) struct FieldExpressionWithGeneric(#[primary_span] pub Span);
+
+#[derive(SessionDiagnostic)]
+#[diag(parser::macro_invocation_with_qualified_path)]
+pub(crate) struct MacroInvocationWithQualifiedPath(#[primary_span] pub Span);
+
+#[derive(SessionDiagnostic)]
+#[diag(parser::unexpected_token_after_label)]
+pub(crate) struct UnexpectedTokenAfterLabel(
+    #[primary_span]
+    #[label(parser::unexpected_token_after_label)]
+    pub Span,
+);
+
+#[derive(SessionDiagnostic)]
+#[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(SessionDiagnostic)]
+#[diag(parser::do_catch_syntax_removed)]
+#[note]
+pub(crate) struct DoCatchSyntaxRemoved {
+    #[primary_span]
+    #[suggestion(applicability = "machine-applicable", code = "try")]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[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(SessionDiagnostic)]
+#[diag(parser::invalid_int_literal_width)]
+#[help]
+pub(crate) struct InvalidIntLiteralWidth {
+    #[primary_span]
+    pub span: Span,
+    pub width: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[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(SessionDiagnostic)]
+#[diag(parser::invalid_num_literal_suffix)]
+#[help]
+pub(crate) struct InvalidNumLiteralSuffix {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub suffix: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(parser::invalid_float_literal_width)]
+#[help]
+pub(crate) struct InvalidFloatLiteralWidth {
+    #[primary_span]
+    pub span: Span,
+    pub width: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(parser::invalid_float_literal_suffix)]
+#[help]
+pub(crate) struct InvalidFloatLiteralSuffix {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub suffix: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(parser::int_literal_too_large)]
+pub(crate) struct IntLiteralTooLarge {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[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(SessionDiagnostic)]
+#[diag(parser::invalid_block_macro_segment)]
+pub(crate) struct InvalidBlockMacroSegment {
+    #[primary_span]
+    pub span: Span,
+    #[label]
+    pub context: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(parser::if_expression_missing_then_block)]
+pub(crate) struct IfExpressionMissingThenBlock {
+    #[primary_span]
+    pub if_span: Span,
+    #[subdiagnostic]
+    pub sub: IfExpressionMissingThenBlockSub,
+}
+
+#[derive(SessionSubdiagnostic)]
+pub(crate) enum IfExpressionMissingThenBlockSub {
+    #[help(parser::condition_possibly_unfinished)]
+    UnfinishedCondition(#[primary_span] Span),
+    #[help(parser::add_then_block)]
+    AddThenBlock(#[primary_span] Span),
+}
+
+#[derive(SessionDiagnostic)]
+#[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(SessionDiagnostic)]
+#[diag(parser::expected_expression_found_let)]
+pub(crate) struct ExpectedExpressionFoundLet {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[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(SessionDiagnostic)]
+#[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(SessionDiagnostic)]
+#[diag(parser::missing_in_in_for_loop)]
+pub(crate) struct MissingInInForLoop {
+    #[primary_span]
+    pub span: Span,
+    #[subdiagnostic]
+    pub sub: MissingInInForLoopSub,
+}
+
+#[derive(SessionSubdiagnostic)]
+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(SessionDiagnostic)]
+#[diag(parser::missing_comma_after_match_arm)]
+pub(crate) struct MissingCommaAfterMatchArm {
+    #[primary_span]
+    #[suggestion(applicability = "machine-applicable", code = ",")]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(parser::catch_after_try)]
+#[help]
+pub(crate) struct CatchAfterTry {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[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(SessionDiagnostic)]
+#[diag(parser::eq_field_init)]
+pub(crate) struct EqFieldInit {
+    #[primary_span]
+    pub span: Span,
+    #[suggestion(applicability = "machine-applicable", code = ":")]
+    pub eq: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[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(SessionDiagnostic)]
+#[diag(parser::left_arrow_operator)]
+pub(crate) struct LeftArrowOperator {
+    #[primary_span]
+    #[suggestion(applicability = "maybe-incorrect", 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.
@@ -387,7 +759,7 @@ impl<'a> Parser<'a> {
     /// This is to avoid losing unclosed delims errors `create_snapshot_for_diagnostic` clears.
     pub(super) fn restore_snapshot(&mut self, snapshot: SnapshotParser<'a>) {
         *self = snapshot.parser;
-        self.unclosed_delims.extend(snapshot.unclosed_delims.clone());
+        self.unclosed_delims.extend(snapshot.unclosed_delims);
     }
 
     pub fn unclosed_delims(&self) -> &[UnmatchedBrace] {
@@ -555,10 +927,12 @@ impl<'a> Parser<'a> {
                 return Ok(true);
             } else if self.look_ahead(0, |t| {
                 t == &token::CloseDelim(Delimiter::Brace)
-                    || (t.can_begin_expr() && t != &token::Semi && t != &token::Pound)
+                    || ((t.can_begin_expr() || t.can_begin_item())
+                        && t != &token::Semi
+                        && t != &token::Pound)
                     // Avoid triggering with too many trailing `#` in raw string.
                     || (sm.is_multiline(
-                        self.prev_token.span.shrink_to_hi().until(self.token.span.shrink_to_lo())
+                        self.prev_token.span.shrink_to_hi().until(self.token.span.shrink_to_lo()),
                     ) && t == &token::Pound)
             }) && !expected.contains(&TokenType::Token(token::Comma))
             {
@@ -603,16 +977,29 @@ impl<'a> Parser<'a> {
         let mut err = self.struct_span_err(self.token.span, &msg_exp);
 
         if let TokenKind::Ident(symbol, _) = &self.prev_token.kind {
-            if symbol.as_str() == "public" {
+            if ["def", "fun", "func", "function"].contains(&symbol.as_str()) {
                 err.span_suggestion_short(
                     self.prev_token.span,
-                    "write `pub` instead of `public` to make the item public",
-                    "pub",
+                    &format!("write `fn` instead of `{symbol}` to declare a function"),
+                    "fn",
                     appl,
                 );
             }
         }
 
+        // `pub` may be used for an item or `pub(crate)`
+        if self.prev_token.is_ident_named(sym::public)
+            && (self.token.can_begin_item()
+                || self.token.kind == TokenKind::OpenDelim(Delimiter::Parenthesis))
+        {
+            err.span_suggestion_short(
+                self.prev_token.span,
+                "write `pub` instead of `public` to make the item public",
+                "pub",
+                appl,
+            );
+        }
+
         // Add suggestion for a missing closing angle bracket if '>' is included in expected_tokens
         // there are unclosed angle brackets
         if self.unmatched_angle_bracket_count > 0
@@ -2332,7 +2719,7 @@ impl<'a> Parser<'a> {
 
     fn recover_const_param_decl(&mut self, ty_generics: Option<&Generics>) -> Option<GenericArg> {
         let snapshot = self.create_snapshot_for_diagnostic();
-        let param = match self.parse_const_param(vec![]) {
+        let param = match self.parse_const_param(AttrVec::new()) {
             Ok(param) => param,
             Err(err) => {
                 err.cancel();
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index c824566c35f..c8541609514 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -1,4 +1,14 @@
-use super::diagnostics::SnapshotParser;
+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, OuterAttributeNotAllowedOnIfElse, RequireColonAfterLabeledExpression,
+    SnapshotParser, TildeAsUnaryOperator, UnexpectedTokenAfterLabel,
+};
 use super::pat::{CommaRecoveryMode, RecoverColon, RecoverComma, PARAM_EXPECTED};
 use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
 use super::{
@@ -6,6 +16,11 @@ use super::{
     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 core::mem;
 use rustc_ast::ptr::P;
@@ -20,9 +35,10 @@ use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty
 use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
 use rustc_ast::{ClosureBinder, StmtKind};
 use rustc_ast_pretty::pprust;
-use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, PResult};
+use rustc_errors::{Applicability, Diagnostic, PResult};
 use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;
 use rustc_session::lint::BuiltinLintDiagnostics;
+use rustc_session::SessionDiagnostic;
 use rustc_span::source_map::{self, Span, Spanned};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{BytePos, Pos};
@@ -216,15 +232,18 @@ impl<'a> Parser<'a> {
                     AssocOp::Equal => "==",
                     AssocOp::NotEqual => "!=",
                     _ => unreachable!(),
-                };
-                self.struct_span_err(sp, &format!("invalid comparison operator `{sugg}=`"))
-                    .span_suggestion_short(
-                        sp,
-                        &format!("`{s}=` is not a valid comparison operator, use `{s}`", s = sugg),
-                        sugg,
-                        Applicability::MachineApplicable,
-                    )
-                    .emit();
+                }
+                .into();
+                let invalid = format!("{}=", &sugg);
+                self.sess.emit_err(InvalidComparisonOperator {
+                    span: sp,
+                    invalid: invalid.clone(),
+                    sub: InvalidComparisonOperatorSub::Correctable {
+                        span: sp,
+                        invalid,
+                        correct: sugg,
+                    },
+                });
                 self.bump();
             }
 
@@ -234,14 +253,15 @@ impl<'a> Parser<'a> {
                 && self.prev_token.span.hi() == self.token.span.lo()
             {
                 let sp = op.span.to(self.token.span);
-                self.struct_span_err(sp, "invalid comparison operator `<>`")
-                    .span_suggestion_short(
-                        sp,
-                        "`<>` is not a valid comparison operator, use `!=`",
-                        "!=",
-                        Applicability::MachineApplicable,
-                    )
-                    .emit();
+                self.sess.emit_err(InvalidComparisonOperator {
+                    span: sp,
+                    invalid: "<>".into(),
+                    sub: InvalidComparisonOperatorSub::Correctable {
+                        span: sp,
+                        invalid: "<>".into(),
+                        correct: "!=".into(),
+                    },
+                });
                 self.bump();
             }
 
@@ -251,12 +271,11 @@ impl<'a> Parser<'a> {
                 && self.prev_token.span.hi() == self.token.span.lo()
             {
                 let sp = op.span.to(self.token.span);
-                self.struct_span_err(sp, "invalid comparison operator `<=>`")
-                    .span_label(
-                        sp,
-                        "`<=>` is not a valid comparison operator, use `std::cmp::Ordering`",
-                    )
-                    .emit();
+                self.sess.emit_err(InvalidComparisonOperator {
+                    span: sp,
+                    invalid: "<=>".into(),
+                    sub: InvalidComparisonOperatorSub::Spaceship(sp),
+                });
                 self.bump();
             }
 
@@ -430,11 +449,19 @@ impl<'a> Parser<'a> {
             }
             (Some(op), _) => (op, self.token.span),
             (None, Some((Ident { name: sym::and, span }, false))) => {
-                self.error_bad_logical_op("and", "&&", "conjunction");
+                self.sess.emit_err(InvalidLogicalOperator {
+                    span: self.token.span,
+                    incorrect: "and".into(),
+                    sub: InvalidLogicalOperatorSub::Conjunction(self.token.span),
+                });
                 (AssocOp::LAnd, span)
             }
             (None, Some((Ident { name: sym::or, span }, false))) => {
-                self.error_bad_logical_op("or", "||", "disjunction");
+                self.sess.emit_err(InvalidLogicalOperator {
+                    span: self.token.span,
+                    incorrect: "or".into(),
+                    sub: InvalidLogicalOperatorSub::Disjunction(self.token.span),
+                });
                 (AssocOp::LOr, span)
             }
             _ => return None,
@@ -442,19 +469,6 @@ impl<'a> Parser<'a> {
         Some(source_map::respan(span, op))
     }
 
-    /// Error on `and` and `or` suggesting `&&` and `||` respectively.
-    fn error_bad_logical_op(&self, bad: &str, good: &str, english: &str) {
-        self.struct_span_err(self.token.span, &format!("`{bad}` is not a logical operator"))
-            .span_suggestion_short(
-                self.token.span,
-                &format!("use `{good}` to perform logical {english}"),
-                good,
-                Applicability::MachineApplicable,
-            )
-            .note("unlike in e.g., python and PHP, `&&` and `||` are used for logical operators")
-            .emit();
-    }
-
     /// Checks if this expression is a successfully parsed statement.
     fn expr_is_complete(&self, e: &Expr) -> bool {
         self.restrictions.contains(Restrictions::STMT_EXPR)
@@ -619,14 +633,7 @@ impl<'a> Parser<'a> {
 
     // Recover on `!` suggesting for bitwise negation instead.
     fn recover_tilde_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
-        self.struct_span_err(lo, "`~` cannot be used as a unary operator")
-            .span_suggestion_short(
-                lo,
-                "use `!` to perform bitwise not",
-                "!",
-                Applicability::MachineApplicable,
-            )
-            .emit();
+        self.sess.emit_err(TildeAsUnaryOperator(lo));
 
         self.parse_unary_expr(lo, UnOp::Not)
     }
@@ -652,20 +659,14 @@ impl<'a> Parser<'a> {
     /// Recover on `not expr` in favor of `!expr`.
     fn recover_not_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
         // Emit the error...
-        let not_token = self.look_ahead(1, |t| t.clone());
-        self.struct_span_err(
-            not_token.span,
-            &format!("unexpected {} after identifier", super::token_descr(&not_token)),
-        )
-        .span_suggestion_short(
+        let negated_token = self.look_ahead(1, |t| t.clone());
+        self.sess.emit_err(NotAsNegationOperator {
+            negated: negated_token.span,
+            negated_desc: super::token_descr(&negated_token),
             // Span the `not` plus trailing whitespace to avoid
             // trailing whitespace after the `!` in our suggestion
-            self.sess.source_map().span_until_non_whitespace(lo.to(not_token.span)),
-            "use `!` to perform logical negation",
-            "!",
-            Applicability::MachineApplicable,
-        )
-        .emit();
+            not: self.sess.source_map().span_until_non_whitespace(lo.to(negated_token.span)),
+        });
 
         // ...and recover!
         self.parse_unary_expr(lo, UnOp::Not)
@@ -725,14 +726,10 @@ impl<'a> Parser<'a> {
                         match self.parse_labeled_expr(label, false) {
                             Ok(expr) => {
                                 type_err.cancel();
-                                self.struct_span_err(label.ident.span, "malformed loop label")
-                                    .span_suggestion(
-                                        label.ident.span,
-                                        "use the correct loop label format",
-                                        label.ident,
-                                        Applicability::MachineApplicable,
-                                    )
-                                    .emit();
+                                self.sess.emit_err(MalformedLoopLabel {
+                                    span: label.ident.span,
+                                    correct_label: label.ident,
+                                });
                                 return Ok(expr);
                             }
                             Err(err) => {
@@ -910,15 +907,7 @@ impl<'a> Parser<'a> {
     }
 
     fn error_remove_borrow_lifetime(&self, span: Span, lt_span: Span) {
-        self.struct_span_err(span, "borrow expressions cannot be annotated with lifetimes")
-            .span_label(lt_span, "annotated with lifetime here")
-            .span_suggestion(
-                lt_span,
-                "remove the lifetime annotation",
-                "",
-                Applicability::MachineApplicable,
-            )
-            .emit();
+        self.sess.emit_err(LifetimeInBorrowExpression { span, lifetime_span: lt_span });
     }
 
     /// Parse `mut?` or `raw [ const | mut ]`.
@@ -950,15 +939,15 @@ impl<'a> Parser<'a> {
         &mut self,
         e0: P<Expr>,
         lo: Span,
-        mut attrs: Vec<ast::Attribute>,
+        mut attrs: ast::AttrVec,
     ) -> PResult<'a, P<Expr>> {
         // Stitch the list of outer attributes onto the return value.
         // A little bit ugly, but the best way given the current code
         // structure
         self.parse_dot_or_call_expr_with_(e0, lo).map(|expr| {
             expr.map(|mut expr| {
-                attrs.extend::<Vec<_>>(expr.attrs.into());
-                expr.attrs = attrs.into();
+                attrs.extend(expr.attrs);
+                expr.attrs = attrs;
                 expr
             })
         })
@@ -1272,11 +1261,7 @@ impl<'a> Parser<'a> {
         } else {
             // Field access `expr.f`
             if let Some(args) = segment.args {
-                self.struct_span_err(
-                    args.span(),
-                    "field expressions cannot have generic arguments",
-                )
-                .emit();
+                self.sess.emit_err(FieldExpressionWithGeneric(args.span()));
             }
 
             let span = lo.to(self.prev_token.span);
@@ -1489,14 +1474,14 @@ impl<'a> Parser<'a> {
         let (span, kind) = if self.eat(&token::Not) {
             // MACRO INVOCATION expression
             if qself.is_some() {
-                self.struct_span_err(path.span, "macros cannot use qualified paths").emit();
+                self.sess.emit_err(MacroInvocationWithQualifiedPath(path.span));
             }
             let lo = path.span;
-            let mac = MacCall {
+            let mac = P(MacCall {
                 path,
                 args: self.parse_mac_args()?,
                 prior_type_ascription: self.last_type_ascription,
-            };
+            });
             (lo.to(self.prev_token.span), ExprKind::MacCall(mac))
         } else if self.check(&token::OpenDelim(Delimiter::Brace)) &&
             let Some(expr) = self.maybe_parse_struct_expr(qself.as_ref(), &path) {
@@ -1535,11 +1520,11 @@ 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
-            let msg = "expected `while`, `for`, `loop` or `{` after a label";
-            self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit();
+            self.sess.emit_err(UnexpectedTokenAfterLabel(self.token.span));
             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);
@@ -1604,25 +1589,16 @@ impl<'a> Parser<'a> {
         }?;
 
         if !ate_colon && consume_colon {
-            self.error_labeled_expr_must_be_followed_by_colon(lo, expr.span);
+            self.sess.emit_err(RequireColonAfterLabeledExpression {
+                span: expr.span,
+                label: lo,
+                label_end: lo.shrink_to_hi(),
+            });
         }
 
         Ok(expr)
     }
 
-    fn error_labeled_expr_must_be_followed_by_colon(&self, lo: Span, span: Span) {
-        self.struct_span_err(span, "labeled expression must be followed by `:`")
-            .span_label(lo, "the label")
-            .span_suggestion_short(
-                lo.shrink_to_hi(),
-                "add `:` after the label",
-                ": ",
-                Applicability::MachineApplicable,
-            )
-            .note("labels are used before loops and blocks, allowing e.g., `break 'label` to them")
-            .emit();
-    }
-
     /// Recover on the syntax `do catch { ... }` suggesting `try { ... }` instead.
     fn recover_do_catch(&mut self) -> PResult<'a, P<Expr>> {
         let lo = self.token.span;
@@ -1630,16 +1606,8 @@ impl<'a> Parser<'a> {
         self.bump(); // `do`
         self.bump(); // `catch`
 
-        let span_dc = lo.to(self.prev_token.span);
-        self.struct_span_err(span_dc, "found removed `do catch` syntax")
-            .span_suggestion(
-                span_dc,
-                "replace with the new syntax",
-                "try",
-                Applicability::MachineApplicable,
-            )
-            .note("following RFC #2388, the new non-placeholder syntax is `try`")
-            .emit();
+        let span = lo.to(self.prev_token.span);
+        self.sess.emit_err(DoCatchSyntaxRemoved { span });
 
         self.parse_try_block(lo)
     }
@@ -1834,14 +1802,10 @@ impl<'a> Parser<'a> {
     }
 
     fn error_float_lits_must_have_int_part(&self, token: &Token) {
-        self.struct_span_err(token.span, "float literals must have an integer part")
-            .span_suggestion(
-                token.span,
-                "must have an integer part",
-                pprust::token_to_string(token),
-                Applicability::MachineApplicable,
-            )
-            .emit();
+        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) {
@@ -1883,28 +1847,11 @@ impl<'a> Parser<'a> {
                 let suf = suf.as_str();
                 if looks_like_width_suffix(&['i', 'u'], &suf) {
                     // If it looks like a width, try to be helpful.
-                    let msg = format!("invalid width `{}` for integer literal", &suf[1..]);
-                    self.struct_span_err(span, &msg)
-                        .help("valid widths are 8, 16, 32, 64 and 128")
-                        .emit();
+                    self.sess.emit_err(InvalidIntLiteralWidth { span, width: suf[1..].into() });
                 } else if let Some(fixed) = fix_base_capitalisation(suf) {
-                    let msg = "invalid base prefix for number literal";
-
-                    self.struct_span_err(span, msg)
-                        .note("base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase")
-                        .span_suggestion(
-                            span,
-                            "try making the prefix lowercase",
-                            fixed,
-                            Applicability::MaybeIncorrect,
-                        )
-                        .emit();
+                    self.sess.emit_err(InvalidNumLiteralBasePrefix { span, fixed });
                 } else {
-                    let msg = format!("invalid suffix `{suf}` for number literal");
-                    self.struct_span_err(span, &msg)
-                        .span_label(span, format!("invalid suffix `{suf}`"))
-                        .help("the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)")
-                        .emit();
+                    self.sess.emit_err(InvalidNumLiteralSuffix { span, suffix: suf.to_string() });
                 }
             }
             LitError::InvalidFloatSuffix => {
@@ -1912,14 +1859,10 @@ impl<'a> Parser<'a> {
                 let suf = suf.as_str();
                 if looks_like_width_suffix(&['f'], suf) {
                     // If it looks like a width, try to be helpful.
-                    let msg = format!("invalid width `{}` for float literal", &suf[1..]);
-                    self.struct_span_err(span, &msg).help("valid widths are 32 and 64").emit();
+                    self.sess
+                        .emit_err(InvalidFloatLiteralWidth { span, width: suf[1..].to_string() });
                 } else {
-                    let msg = format!("invalid suffix `{suf}` for float literal");
-                    self.struct_span_err(span, &msg)
-                        .span_label(span, format!("invalid suffix `{suf}`"))
-                        .help("valid suffixes are `f32` and `f64`")
-                        .emit();
+                    self.sess.emit_err(InvalidFloatLiteralSuffix { span, suffix: suf.to_string() });
                 }
             }
             LitError::NonDecimalFloat(base) => {
@@ -1934,7 +1877,7 @@ impl<'a> Parser<'a> {
                     .emit();
             }
             LitError::IntTooLarge => {
-                self.struct_span_err(span, "integer literal is too large").emit();
+                self.sess.emit_err(IntLiteralTooLarge { span });
             }
         }
     }
@@ -2046,14 +1989,10 @@ impl<'a> Parser<'a> {
                         .span_to_snippet(snapshot.token.span)
                         .map_or(false, |snippet| snippet == "]") =>
                 {
-                    let mut err = self.struct_span_err(open_delim_span, "expected `;`, found `[`");
-                    err.span_suggestion_verbose(
-                        prev_span.shrink_to_hi(),
-                        "consider adding `;` here",
-                        ';',
-                        Applicability::MaybeIncorrect,
-                    );
-                    return Err(err);
+                    return Err(MissingSemicolonBeforeArray {
+                        open_delim: open_delim_span,
+                        semicolon: prev_span.shrink_to_hi(),
+                    }.into_diagnostic(self.sess));
                 }
                 Ok(_) => (),
                 Err(err) => err.cancel(),
@@ -2075,14 +2014,11 @@ impl<'a> Parser<'a> {
             }
         }
 
-        if let Some(label) = opt_label {
-            self.sess.gated_spans.gate(sym::label_break_value, label.ident.span);
-        }
-
         if self.token.is_whole_block() {
-            self.struct_span_err(self.token.span, "cannot use a `block` macro fragment here")
-                .span_label(lo.to(self.token.span), "the `block` fragment is within this context")
-                .emit();
+            self.sess.emit_err(InvalidBlockMacroSegment {
+                span: self.token.span,
+                context: lo.to(self.token.span),
+            });
         }
 
         let (attrs, blk) = self.parse_block_common(lo, blk_mode)?;
@@ -2224,7 +2160,7 @@ impl<'a> Parser<'a> {
 
             Ok((
                 Param {
-                    attrs: attrs.into(),
+                    attrs,
                     ty,
                     pat,
                     span: lo.to(this.prev_token.span),
@@ -2252,11 +2188,19 @@ impl<'a> Parser<'a> {
             let block = match &mut cond.kind {
                 ExprKind::Binary(Spanned { span: binop_span, .. }, _, right)
                     if let ExprKind::Block(_, None) = right.kind => {
-                        this.error_missing_if_then_block(lo, cond_span.shrink_to_lo().to(*binop_span), true).emit();
+                        self.sess.emit_err(IfExpressionMissingThenBlock {
+                            if_span: lo,
+                            sub: IfExpressionMissingThenBlockSub::UnfinishedCondition(
+                                cond_span.shrink_to_lo().to(*binop_span)
+                            ),
+                        });
                         std::mem::replace(right, this.mk_expr_err(binop_span.shrink_to_hi()))
                     },
                 ExprKind::Block(_, None) => {
-                    this.error_missing_if_cond(lo, cond_span).emit();
+                    self.sess.emit_err(IfExpressionMissingCondition {
+                        if_span: self.sess.source_map().next_point(lo),
+                        block_span: self.sess.source_map().start_point(cond_span),
+                    });
                     std::mem::replace(&mut cond, this.mk_expr_err(cond_span.shrink_to_hi()))
                 }
                 _ => {
@@ -2274,7 +2218,10 @@ impl<'a> Parser<'a> {
             if let Some(block) = recover_block_from_condition(self) {
                 block
             } else {
-                self.error_missing_if_then_block(lo, cond_span, false).emit();
+                self.sess.emit_err(IfExpressionMissingThenBlock {
+                    if_span: lo,
+                    sub: IfExpressionMissingThenBlockSub::AddThenBlock(cond_span.shrink_to_hi()),
+                });
                 self.mk_block_err(cond_span.shrink_to_hi())
             }
         } else {
@@ -2302,42 +2249,17 @@ impl<'a> Parser<'a> {
         Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::If(cond, thn, els)))
     }
 
-    fn error_missing_if_then_block(
-        &self,
-        if_span: Span,
-        cond_span: Span,
-        is_unfinished: bool,
-    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
-        let mut err = self.struct_span_err(
-            if_span,
-            "this `if` expression is missing a block after the condition",
-        );
-        if is_unfinished {
-            err.span_help(cond_span, "this binary operation is possibly unfinished");
-        } else {
-            err.span_help(cond_span.shrink_to_hi(), "add a block here");
-        }
-        err
-    }
-
-    fn error_missing_if_cond(
-        &self,
-        lo: Span,
-        span: Span,
-    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
-        let next_span = self.sess.source_map().next_point(lo);
-        let mut err = self.struct_span_err(next_span, "missing condition for `if` expression");
-        err.span_label(next_span, "expected condition here");
-        err.span_label(
-            self.sess.source_map().start_point(span),
-            "if this block is the condition of the `if` expression, then it must be followed by another block"
-        );
-        err
-    }
-
     /// Parses the condition of a `if` or `while` expression.
     fn parse_cond_expr(&mut self) -> PResult<'a, P<Expr>> {
-        self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, None)
+        let cond =
+            self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, None)?;
+
+        if let ExprKind::Let(..) = cond.kind {
+            // Remove the last feature gating of a `let` expression since it's stable.
+            self.sess.gated_spans.ungate_last(sym::let_chains, cond.span);
+        }
+
+        Ok(cond)
     }
 
     /// Parses a `let $pat = $expr` pseudo-expression.
@@ -2350,8 +2272,7 @@ impl<'a> Parser<'a> {
             TokenKind::AndAnd | TokenKind::Ident(kw::If, _) | TokenKind::Ident(kw::While, _)
         );
         if !self.restrictions.contains(Restrictions::ALLOW_LET) || not_in_chain {
-            self.struct_span_err(self.token.span, "expected expression, found `let` statement")
-                .emit();
+            self.sess.emit_err(ExpectedExpressionFoundLet { span: self.token.span });
         }
 
         self.bump(); // Eat `let` token
@@ -2367,6 +2288,7 @@ impl<'a> Parser<'a> {
             this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into())
         })?;
         let span = lo.to(expr.span);
+        self.sess.gated_spans.gate(sym::let_chains, span);
         Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span)))
     }
 
@@ -2389,15 +2311,12 @@ impl<'a> Parser<'a> {
                     if self.check(&TokenKind::OpenDelim(Delimiter::Brace))
                         && classify::expr_requires_semi_to_be_stmt(&cond) =>
                 {
-                    self.struct_span_err(first_tok_span, format!("expected `{{`, found {first_tok}"))
-                        .span_label(else_span, "expected an `if` or a block after this `else`")
-                        .span_suggestion(
-                            cond.span.shrink_to_lo(),
-                            "add an `if` if this is the condition of a chained `else if` statement",
-                            "if ",
-                            Applicability::MaybeIncorrect,
-                        )
-                        .emit();
+                    self.sess.emit_err(ExpectedElseBlock {
+                        first_tok_span,
+                        first_tok,
+                        else_span,
+                        condition_start: cond.span.shrink_to_lo(),
+                    });
                     self.parse_if_after_cond(cond.span.shrink_to_lo(), cond)?
                 }
                 Err(e) => {
@@ -2422,16 +2341,18 @@ impl<'a> Parser<'a> {
         branch_span: Span,
         attrs: &[ast::Attribute],
     ) {
-        let (span, last) = match attrs {
+        let (attributes, last) = match attrs {
             [] => return,
             [x0 @ xn] | [x0, .., xn] => (x0.span.to(xn.span), xn.span),
         };
         let ctx = if is_ctx_else { "else" } else { "if" };
-        self.struct_span_err(last, "outer attributes are not allowed on `if` and `else` branches")
-            .span_label(branch_span, "the attributes are attached to this branch")
-            .span_label(ctx_span, format!("the branch belongs to this `{ctx}`"))
-            .span_suggestion(span, "remove the attributes", "", Applicability::MachineApplicable)
-            .emit();
+        self.sess.emit_err(OuterAttributeNotAllowedOnIfElse {
+            last,
+            branch_span,
+            ctx_span,
+            ctx: ctx.to_string(),
+            attributes,
+        });
     }
 
     /// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
@@ -2465,23 +2386,16 @@ impl<'a> Parser<'a> {
     }
 
     fn error_missing_in_for_loop(&mut self) {
-        let (span, msg, sugg) = if self.token.is_ident_named(sym::of) {
+        let (span, sub): (_, fn(_) -> _) = if self.token.is_ident_named(sym::of) {
             // Possibly using JS syntax (#75311).
             let span = self.token.span;
             self.bump();
-            (span, "try using `in` here instead", "in")
+            (span, MissingInInForLoopSub::InNotOf)
         } else {
-            (self.prev_token.span.between(self.token.span), "try adding `in` here", " in ")
+            (self.prev_token.span.between(self.token.span), MissingInInForLoopSub::AddIn)
         };
-        self.struct_span_err(span, "missing `in` in `for` loop")
-            .span_suggestion_short(
-                span,
-                msg,
-                sugg,
-                // Has been misleading, at least in the past (closed Issue #48492).
-                Applicability::MaybeIncorrect,
-            )
-            .emit();
+
+        self.sess.emit_err(MissingInInForLoop { span, sub: sub(span) });
     }
 
     /// Parses a `while` or `while let` expression (`while` token already eaten).
@@ -2666,13 +2580,15 @@ impl<'a> Parser<'a> {
     pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> {
         // Used to check the `let_chains` and `if_let_guard` features mostly by scaning
         // `&&` tokens.
-        fn check_let_expr(expr: &Expr) -> bool {
+        fn check_let_expr(expr: &Expr) -> (bool, bool) {
             match expr.kind {
                 ExprKind::Binary(BinOp { node: BinOpKind::And, .. }, ref lhs, ref rhs) => {
-                    check_let_expr(lhs) || check_let_expr(rhs)
+                    let lhs_rslt = check_let_expr(lhs);
+                    let rhs_rslt = check_let_expr(rhs);
+                    (lhs_rslt.0 || rhs_rslt.0, false)
                 }
-                ExprKind::Let(..) => true,
-                _ => false,
+                ExprKind::Let(..) => (true, true),
+                _ => (false, true),
             }
         }
         let attrs = self.parse_outer_attributes()?;
@@ -2687,7 +2603,12 @@ impl<'a> Parser<'a> {
             let guard = if this.eat_keyword(kw::If) {
                 let if_span = this.prev_token.span;
                 let cond = this.parse_expr_res(Restrictions::ALLOW_LET, None)?;
-                if check_let_expr(&cond) {
+                let (has_let_expr, does_not_have_bin_op) = check_let_expr(&cond);
+                if has_let_expr {
+                    if does_not_have_bin_op {
+                        // Remove the last feature gating of a `let` expression since it's stable.
+                        this.sess.gated_spans.ungate_last(sym::let_chains, cond.span);
+                    }
                     let span = if_span.to(cond.span);
                     this.sess.gated_spans.gate(sym::if_let_guard, span);
                 }
@@ -2732,7 +2653,7 @@ impl<'a> Parser<'a> {
                     let span = body.span;
                     return Ok((
                         ast::Arm {
-                            attrs: attrs.into(),
+                            attrs,
                             pat,
                             guard,
                             body,
@@ -2787,17 +2708,9 @@ impl<'a> Parser<'a> {
                                 .is_ok();
                             if pattern_follows && snapshot.check(&TokenKind::FatArrow) {
                                 err.cancel();
-                                this.struct_span_err(
-                                    hi.shrink_to_hi(),
-                                    "expected `,` following `match` arm",
-                                )
-                                .span_suggestion(
-                                    hi.shrink_to_hi(),
-                                    "missing a comma here to end this `match` arm",
-                                    ",",
-                                    Applicability::MachineApplicable,
-                                )
-                                .emit();
+                                this.sess.emit_err(MissingCommaAfterMatchArm {
+                                    span: hi.shrink_to_hi(),
+                                });
                                 return Ok(true);
                             }
                         }
@@ -2810,7 +2723,7 @@ impl<'a> Parser<'a> {
 
             Ok((
                 ast::Arm {
-                    attrs: attrs.into(),
+                    attrs,
                     pat,
                     guard,
                     body: expr,
@@ -2827,13 +2740,7 @@ impl<'a> Parser<'a> {
     fn parse_try_block(&mut self, span_lo: Span) -> PResult<'a, P<Expr>> {
         let (attrs, body) = self.parse_inner_attrs_and_block()?;
         if self.eat_keyword(kw::Catch) {
-            let mut error = self.struct_span_err(
-                self.prev_token.span,
-                "keyword `catch` cannot follow a `try` block",
-            );
-            error.help("try using `match` on the result of the `try` block instead");
-            error.emit();
-            Err(error)
+            Err(CatchAfterTry { span: self.prev_token.span }.into_diagnostic(self.sess))
         } else {
             let span = span_lo.to(body.span);
             self.sess.gated_spans.gate(sym::try_blocks, span);
@@ -3082,18 +2989,10 @@ impl<'a> Parser<'a> {
         if self.token != token::Comma {
             return;
         }
-        self.struct_span_err(
-            span.to(self.prev_token.span),
-            "cannot use a comma after the base struct",
-        )
-        .span_suggestion_short(
-            self.token.span,
-            "remove this comma",
-            "",
-            Applicability::MachineApplicable,
-        )
-        .note("the base struct must always be the last field")
-        .emit();
+        self.sess.emit_err(CommaAfterBaseStruct {
+            span: span.to(self.prev_token.span),
+            comma: self.token.span,
+        });
         self.recover_stmt();
     }
 
@@ -3123,7 +3022,7 @@ impl<'a> Parser<'a> {
                     span: lo.to(expr.span),
                     expr,
                     is_shorthand,
-                    attrs: attrs.into(),
+                    attrs,
                     id: DUMMY_NODE_ID,
                     is_placeholder: false,
                 },
@@ -3139,43 +3038,18 @@ impl<'a> Parser<'a> {
             return;
         }
 
-        self.struct_span_err(self.token.span, "expected `:`, found `=`")
-            .span_suggestion(
-                field_name.span.shrink_to_hi().to(self.token.span),
-                "replace equals symbol with a colon",
-                ":",
-                Applicability::MachineApplicable,
-            )
-            .emit();
+        self.sess.emit_err(EqFieldInit {
+            span: self.token.span,
+            eq: field_name.span.shrink_to_hi().to(self.token.span),
+        });
     }
 
     fn err_dotdotdot_syntax(&self, span: Span) {
-        self.struct_span_err(span, "unexpected token: `...`")
-            .span_suggestion(
-                span,
-                "use `..` for an exclusive range",
-                "..",
-                Applicability::MaybeIncorrect,
-            )
-            .span_suggestion(
-                span,
-                "or `..=` for an inclusive range",
-                "..=",
-                Applicability::MaybeIncorrect,
-            )
-            .emit();
+        self.sess.emit_err(DotDotDot { span });
     }
 
     fn err_larrow_operator(&self, span: Span) {
-        self.struct_span_err(span, "unexpected token: `<-`")
-            .span_suggestion(
-                span,
-                "if you meant to write a comparison against a negative value, add a \
-             space in between `<` and `-`",
-                "< -",
-                Applicability::MaybeIncorrect,
-            )
-            .emit();
+        self.sess.emit_err(LeftArrowOperator { span });
     }
 
     fn mk_assign_op(&self, binop: BinOp, lhs: P<Expr>, rhs: P<Expr>) -> ExprKind {
@@ -3219,14 +3093,10 @@ impl<'a> Parser<'a> {
         await_expr
     }
 
-    pub(crate) fn mk_expr_with_attrs<A>(&self, span: Span, kind: ExprKind, attrs: A) -> P<Expr>
-    where
-        A: Into<AttrVec>,
-    {
-        P(Expr { kind, span, attrs: attrs.into(), id: DUMMY_NODE_ID, tokens: None })
+    pub(crate) fn mk_expr_with_attrs(&self, span: Span, kind: ExprKind, attrs: AttrVec) -> P<Expr> {
+        P(Expr { kind, span, attrs, id: DUMMY_NODE_ID, tokens: None })
     }
 
-    // njn: rename
     pub(crate) fn mk_expr(&self, span: Span, kind: ExprKind) -> P<Expr> {
         P(Expr { kind, span, attrs: AttrVec::new(), id: DUMMY_NODE_ID, tokens: None })
     }
@@ -3248,7 +3118,7 @@ impl<'a> Parser<'a> {
     fn collect_tokens_for_expr(
         &mut self,
         attrs: AttrWrapper,
-        f: impl FnOnce(&mut Self, Vec<ast::Attribute>) -> PResult<'a, P<Expr>>,
+        f: impl FnOnce(&mut Self, ast::AttrVec) -> PResult<'a, P<Expr>>,
     ) -> PResult<'a, P<Expr>> {
         self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
             let res = f(this, attrs)?;
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index 5e5f2fd7d9f..4d0a8b05eb0 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -1,9 +1,7 @@
 use super::{ForceCollect, Parser, TrailingToken};
 
 use rustc_ast::token;
-use rustc_ast::{
-    self as ast, Attribute, GenericBounds, GenericParam, GenericParamKind, WhereClause,
-};
+use rustc_ast::{self as ast, AttrVec, GenericBounds, GenericParam, GenericParamKind, WhereClause};
 use rustc_errors::{Applicability, PResult};
 use rustc_span::symbol::kw;
 
@@ -26,7 +24,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Matches `typaram = IDENT (`?` unbound)? optbounds ( EQ ty )?`.
-    fn parse_ty_param(&mut self, preceding_attrs: Vec<Attribute>) -> PResult<'a, GenericParam> {
+    fn parse_ty_param(&mut self, preceding_attrs: AttrVec) -> PResult<'a, GenericParam> {
         let ident = self.parse_ident()?;
 
         // Parse optional colon and param bounds.
@@ -43,7 +41,7 @@ impl<'a> Parser<'a> {
         Ok(GenericParam {
             ident,
             id: ast::DUMMY_NODE_ID,
-            attrs: preceding_attrs.into(),
+            attrs: preceding_attrs,
             bounds,
             kind: GenericParamKind::Type { default },
             is_placeholder: false,
@@ -53,7 +51,7 @@ impl<'a> Parser<'a> {
 
     pub(crate) fn parse_const_param(
         &mut self,
-        preceding_attrs: Vec<Attribute>,
+        preceding_attrs: AttrVec,
     ) -> PResult<'a, GenericParam> {
         let const_span = self.token.span;
 
@@ -68,7 +66,7 @@ impl<'a> Parser<'a> {
         Ok(GenericParam {
             ident,
             id: ast::DUMMY_NODE_ID,
-            attrs: preceding_attrs.into(),
+            attrs: preceding_attrs,
             bounds: Vec::new(),
             kind: GenericParamKind::Const { ty, kw_span: const_span, default },
             is_placeholder: false,
@@ -109,7 +107,7 @@ impl<'a> Parser<'a> {
                         Some(ast::GenericParam {
                             ident: lifetime.ident,
                             id: lifetime.id,
-                            attrs: attrs.into(),
+                            attrs,
                             bounds,
                             kind: ast::GenericParamKind::Lifetime,
                             is_placeholder: false,
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index f3f070e6eb0..b743162a7e4 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -32,7 +32,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parses a `mod <foo> { ... }` or `mod <foo>;` item.
-    fn parse_item_mod(&mut self, attrs: &mut Vec<Attribute>) -> PResult<'a, ItemInfo> {
+    fn parse_item_mod(&mut self, attrs: &mut AttrVec) -> PResult<'a, ItemInfo> {
         let unsafety = self.parse_unsafety();
         self.expect_keyword(kw::Mod)?;
         let id = self.parse_ident()?;
@@ -40,9 +40,9 @@ impl<'a> Parser<'a> {
             ModKind::Unloaded
         } else {
             self.expect(&token::OpenDelim(Delimiter::Brace))?;
-            let (mut inner_attrs, items, inner_span) =
+            let (inner_attrs, items, inner_span) =
                 self.parse_mod(&token::CloseDelim(Delimiter::Brace))?;
-            attrs.append(&mut inner_attrs);
+            attrs.extend(inner_attrs);
             ModKind::Loaded(items, Inline::Yes, inner_span)
         };
         Ok((id, ItemKind::Mod(unsafety, mod_kind)))
@@ -52,7 +52,7 @@ impl<'a> Parser<'a> {
     pub fn parse_mod(
         &mut self,
         term: &TokenKind,
-    ) -> PResult<'a, (Vec<Attribute>, Vec<P<Item>>, ModSpans)> {
+    ) -> PResult<'a, (AttrVec, Vec<P<Item>>, ModSpans)> {
         let lo = self.token.span;
         let attrs = self.parse_inner_attributes()?;
 
@@ -134,7 +134,7 @@ impl<'a> Parser<'a> {
 
     fn parse_item_common_(
         &mut self,
-        mut attrs: Vec<Attribute>,
+        mut attrs: AttrVec,
         mac_allowed: bool,
         attrs_allowed: bool,
         fn_parse_mode: FnParseMode,
@@ -198,7 +198,7 @@ impl<'a> Parser<'a> {
     /// Parses one of the items allowed by the flags.
     fn parse_item_kind(
         &mut self,
-        attrs: &mut Vec<Attribute>,
+        attrs: &mut AttrVec,
         macros_allowed: bool,
         lo: Span,
         vis: &Visibility,
@@ -287,7 +287,7 @@ impl<'a> Parser<'a> {
             return Ok(None);
         } else if macros_allowed && self.check_path() {
             // MACRO INVOCATION ITEM
-            (Ident::empty(), ItemKind::MacCall(self.parse_item_macro(vis)?))
+            (Ident::empty(), ItemKind::MacCall(P(self.parse_item_macro(vis)?)))
         } else {
             return Ok(None);
         };
@@ -534,7 +534,7 @@ impl<'a> Parser<'a> {
     /// ```
     fn parse_item_impl(
         &mut self,
-        attrs: &mut Vec<Attribute>,
+        attrs: &mut AttrVec,
         defaultness: Defaultness,
     ) -> PResult<'a, ItemInfo> {
         let unsafety = self.parse_unsafety();
@@ -661,12 +661,12 @@ impl<'a> Parser<'a> {
 
     fn parse_item_list<T>(
         &mut self,
-        attrs: &mut Vec<Attribute>,
+        attrs: &mut AttrVec,
         mut parse_item: impl FnMut(&mut Parser<'a>) -> PResult<'a, Option<Option<T>>>,
     ) -> PResult<'a, Vec<T>> {
         let open_brace_span = self.token.span;
         self.expect(&token::OpenDelim(Delimiter::Brace))?;
-        attrs.append(&mut self.parse_inner_attributes()?);
+        attrs.extend(self.parse_inner_attributes()?);
 
         let mut items = Vec::new();
         while !self.eat(&token::CloseDelim(Delimiter::Brace)) {
@@ -775,7 +775,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parses `unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`.
-    fn parse_item_trait(&mut self, attrs: &mut Vec<Attribute>, lo: Span) -> PResult<'a, ItemInfo> {
+    fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemInfo> {
         let unsafety = self.parse_unsafety();
         // Parse optional `auto` prefix.
         let is_auto = if self.eat_keyword(kw::Auto) { IsAuto::Yes } else { IsAuto::No };
@@ -1061,7 +1061,7 @@ impl<'a> Parser<'a> {
     /// ```
     fn parse_item_foreign_mod(
         &mut self,
-        attrs: &mut Vec<Attribute>,
+        attrs: &mut AttrVec,
         mut unsafety: Unsafe,
     ) -> PResult<'a, ItemInfo> {
         let abi = self.parse_abi(); // ABI?
@@ -1179,7 +1179,7 @@ impl<'a> Parser<'a> {
     fn recover_const_impl(
         &mut self,
         const_span: Span,
-        attrs: &mut Vec<Attribute>,
+        attrs: &mut AttrVec,
         defaultness: Defaultness,
     ) -> PResult<'a, ItemInfo> {
         let impl_span = self.token.span;
@@ -1337,7 +1337,7 @@ impl<'a> Parser<'a> {
                     ident,
                     vis,
                     id: DUMMY_NODE_ID,
-                    attrs: variant_attrs.into(),
+                    attrs: variant_attrs,
                     data: struct_def,
                     disr_expr,
                     span: vlo.to(this.prev_token.span),
@@ -1494,7 +1494,7 @@ impl<'a> Parser<'a> {
                         ident: None,
                         id: DUMMY_NODE_ID,
                         ty,
-                        attrs: attrs.into(),
+                        attrs,
                         is_placeholder: false,
                     },
                     TrailingToken::MaybeComma,
@@ -1520,7 +1520,7 @@ impl<'a> Parser<'a> {
         adt_ty: &str,
         lo: Span,
         vis: Visibility,
-        attrs: Vec<Attribute>,
+        attrs: AttrVec,
     ) -> PResult<'a, FieldDef> {
         let mut seen_comma: bool = false;
         let a_var = self.parse_name_and_ty(adt_ty, lo, vis, attrs)?;
@@ -1650,7 +1650,7 @@ impl<'a> Parser<'a> {
         adt_ty: &str,
         lo: Span,
         vis: Visibility,
-        attrs: Vec<Attribute>,
+        attrs: AttrVec,
     ) -> PResult<'a, FieldDef> {
         let name = self.parse_field_ident(adt_ty, lo)?;
         self.expect_field_ty_separator()?;
@@ -1684,7 +1684,7 @@ impl<'a> Parser<'a> {
             vis,
             id: DUMMY_NODE_ID,
             ty,
-            attrs: attrs.into(),
+            attrs,
             is_placeholder: false,
         })
     }
@@ -1703,7 +1703,7 @@ impl<'a> Parser<'a> {
                 // We use `parse_fn` to get a span for the function
                 let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true };
                 if let Err(mut db) =
-                    self.parse_fn(&mut Vec::new(), fn_parse_mode, lo, &inherited_vis)
+                    self.parse_fn(&mut AttrVec::new(), fn_parse_mode, lo, &inherited_vis)
                 {
                     db.delay_as_bug();
                 }
@@ -1979,7 +1979,7 @@ impl<'a> Parser<'a> {
     /// Parse a function starting from the front matter (`const ...`) to the body `{ ... }` or `;`.
     fn parse_fn(
         &mut self,
-        attrs: &mut Vec<Attribute>,
+        attrs: &mut AttrVec,
         fn_parse_mode: FnParseMode,
         sig_lo: Span,
         vis: &Visibility,
@@ -2002,7 +2002,7 @@ impl<'a> Parser<'a> {
     /// or e.g. a block when the function is a provided one.
     fn parse_fn_body(
         &mut self,
-        attrs: &mut Vec<Attribute>,
+        attrs: &mut AttrVec,
         ident: &Ident,
         sig_hi: &mut Span,
         req_body: bool,
@@ -2017,7 +2017,7 @@ impl<'a> Parser<'a> {
             // Include the trailing semicolon in the span of the signature
             self.expect_semi()?;
             *sig_hi = self.prev_token.span;
-            (Vec::new(), None)
+            (AttrVec::new(), None)
         } else if self.check(&token::OpenDelim(Delimiter::Brace)) || self.token.is_whole_block() {
             self.parse_inner_attrs_and_block().map(|(attrs, body)| (attrs, Some(body)))?
         } else if self.token.kind == token::Eq {
@@ -2034,7 +2034,7 @@ impl<'a> Parser<'a> {
                     Applicability::MachineApplicable,
                 )
                 .emit();
-            (Vec::new(), Some(self.mk_block_err(span)))
+            (AttrVec::new(), Some(self.mk_block_err(span)))
         } else {
             let expected = if req_body {
                 &[token::OpenDelim(Delimiter::Brace)][..]
@@ -2051,7 +2051,7 @@ impl<'a> Parser<'a> {
                     return Err(err);
                 }
             }
-            (Vec::new(), None)
+            (AttrVec::new(), None)
         };
         attrs.extend(inner_attrs);
         Ok(body)
@@ -2280,7 +2280,7 @@ impl<'a> Parser<'a> {
         self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
             // Possibly parse `self`. Recover if we parsed it and it wasn't allowed here.
             if let Some(mut param) = this.parse_self_param()? {
-                param.attrs = attrs.into();
+                param.attrs = attrs;
                 let res = if first_param { Ok(param) } else { this.recover_bad_self_param(param) };
                 return Ok((res?, TrailingToken::None));
             }
@@ -2341,14 +2341,7 @@ impl<'a> Parser<'a> {
             let span = lo.to(this.prev_token.span);
 
             Ok((
-                Param {
-                    attrs: attrs.into(),
-                    id: ast::DUMMY_NODE_ID,
-                    is_placeholder: false,
-                    pat,
-                    span,
-                    ty,
-                },
+                Param { attrs, id: ast::DUMMY_NODE_ID, is_placeholder: false, pat, span, ty },
                 TrailingToken::None,
             ))
         })
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index d61da7b6cc0..f6516d3bd45 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -1383,7 +1383,7 @@ impl<'a> Parser<'a> {
         match self.parse_str_lit() {
             Ok(str_lit) => Some(str_lit),
             Err(Some(lit)) => match lit.kind {
-                ast::LitKind::Err(_) => None,
+                ast::LitKind::Err => None,
                 _ => {
                     self.struct_span_err(lit.span, "non-string ABI literal")
                         .span_suggestion(
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index 98c974420eb..8b3200d45fc 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -4,8 +4,8 @@ use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor};
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, Delimiter};
 use rustc_ast::{
-    self as ast, Attribute, BindingMode, Expr, ExprKind, MacCall, Mutability, Pat, PatField,
-    PatKind, Path, QSelf, RangeEnd, RangeSyntax,
+    self as ast, AttrVec, BindingMode, Expr, ExprKind, MacCall, Mutability, Pat, PatField, PatKind,
+    Path, QSelf, RangeEnd, RangeSyntax,
 };
 use rustc_ast_pretty::pprust;
 use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult};
@@ -665,7 +665,7 @@ impl<'a> Parser<'a> {
     fn parse_pat_mac_invoc(&mut self, path: Path) -> PResult<'a, PatKind> {
         self.bump();
         let args = self.parse_mac_args()?;
-        let mac = MacCall { path, args, prior_type_ascription: self.last_type_ascription };
+        let mac = P(MacCall { path, args, prior_type_ascription: self.last_type_ascription });
         Ok(PatKind::MacCall(mac))
     }
 
@@ -1093,7 +1093,7 @@ impl<'a> Parser<'a> {
             .emit();
     }
 
-    fn parse_pat_field(&mut self, lo: Span, attrs: Vec<Attribute>) -> PResult<'a, PatField> {
+    fn parse_pat_field(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, PatField> {
         // Check if a colon exists one ahead. This means we're parsing a fieldname.
         let hi;
         let (subpat, fieldname, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) {
@@ -1134,7 +1134,7 @@ impl<'a> Parser<'a> {
             ident: fieldname,
             pat: subpat,
             is_shorthand,
-            attrs: attrs.into(),
+            attrs,
             id: ast::DUMMY_NODE_ID,
             span: lo.to(hi),
             is_placeholder: false,
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index cac39f8f25f..3d957406b19 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -1,5 +1,7 @@
 use super::attr::DEFAULT_INNER_ATTR_FORBIDDEN;
-use super::diagnostics::{AttemptLocalParseRecovery, Error};
+use super::diagnostics::{
+    AttemptLocalParseRecovery, Error, InvalidVariableDeclaration, InvalidVariableDeclarationSub,
+};
 use super::expr::LhsExpr;
 use super::pat::RecoverComma;
 use super::path::PathStyle;
@@ -58,28 +60,22 @@ impl<'a> Parser<'a> {
         if self.token.is_keyword(kw::Mut) && self.is_keyword_ahead(1, &[kw::Let]) {
             self.bump();
             let mut_let_span = lo.to(self.token.span);
-            self.struct_span_err(mut_let_span, "invalid variable declaration")
-                .span_suggestion(
-                    mut_let_span,
-                    "switch the order of `mut` and `let`",
-                    "let mut",
-                    Applicability::MaybeIncorrect,
-                )
-                .emit();
+            self.sess.emit_err(InvalidVariableDeclaration {
+                span: mut_let_span,
+                sub: InvalidVariableDeclarationSub::SwitchMutLetOrder(mut_let_span),
+            });
         }
 
         Ok(Some(if self.token.is_keyword(kw::Let) {
             self.parse_local_mk(lo, attrs, capture_semi, force_collect)?
         } else if self.is_kw_followed_by_ident(kw::Mut) {
-            self.recover_stmt_local(lo, attrs, "missing keyword", "let mut")?
+            self.recover_stmt_local(lo, attrs, InvalidVariableDeclarationSub::MissingLet)?
         } else if self.is_kw_followed_by_ident(kw::Auto) {
             self.bump(); // `auto`
-            let msg = "write `let` instead of `auto` to introduce a new variable";
-            self.recover_stmt_local(lo, attrs, msg, "let")?
+            self.recover_stmt_local(lo, attrs, InvalidVariableDeclarationSub::UseLetNotAuto)?
         } else if self.is_kw_followed_by_ident(sym::var) {
             self.bump(); // `var`
-            let msg = "write `let` instead of `var` to introduce a new variable";
-            self.recover_stmt_local(lo, attrs, msg, "let")?
+            self.recover_stmt_local(lo, attrs, InvalidVariableDeclarationSub::UseLetNotVar)?
         } else if self.check_path() && !self.token.is_qpath_start() && !self.is_path_start_item() {
             // We have avoided contextual keywords like `union`, items with `crate` visibility,
             // or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something
@@ -134,7 +130,7 @@ impl<'a> Parser<'a> {
             let path = this.parse_path(PathStyle::Expr)?;
 
             if this.eat(&token::Not) {
-                let stmt_mac = this.parse_stmt_mac(lo, attrs.into(), path)?;
+                let stmt_mac = this.parse_stmt_mac(lo, attrs, path)?;
                 if this.token == token::Semi {
                     return Ok((stmt_mac, TrailingToken::Semi));
                 } else {
@@ -181,7 +177,7 @@ impl<'a> Parser<'a> {
             None => unreachable!(),
         };
 
-        let mac = MacCall { path, args, prior_type_ascription: self.last_type_ascription };
+        let mac = P(MacCall { path, args, prior_type_ascription: self.last_type_ascription });
 
         let kind = if (style == MacStmtStyle::Braces
             && self.token != token::Dot
@@ -194,7 +190,7 @@ impl<'a> Parser<'a> {
             // Since none of the above applied, this is an expression statement macro.
             let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac));
             let e = self.maybe_recover_from_bad_qpath(e)?;
-            let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?;
+            let e = self.parse_dot_or_call_expr_with(e, lo, attrs)?;
             let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
             StmtKind::Expr(e)
         };
@@ -217,13 +213,10 @@ impl<'a> Parser<'a> {
         &mut self,
         lo: Span,
         attrs: AttrWrapper,
-        msg: &str,
-        sugg: &str,
+        subdiagnostic: fn(Span) -> InvalidVariableDeclarationSub,
     ) -> PResult<'a, Stmt> {
         let stmt = self.recover_local_after_let(lo, attrs)?;
-        self.struct_span_err(lo, "invalid variable declaration")
-            .span_suggestion(lo, msg, sugg, Applicability::MachineApplicable)
-            .emit();
+        self.sess.emit_err(InvalidVariableDeclaration { span: lo, sub: subdiagnostic(lo) });
         Ok(stmt)
     }
 
@@ -236,7 +229,7 @@ impl<'a> Parser<'a> {
     ) -> PResult<'a, Stmt> {
         self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| {
             this.expect_keyword(kw::Let)?;
-            let local = this.parse_local(attrs.into())?;
+            let local = this.parse_local(attrs)?;
             let trailing = if capture_semi && this.token.kind == token::Semi {
                 TrailingToken::Semi
             } else {
@@ -248,7 +241,7 @@ impl<'a> Parser<'a> {
 
     fn recover_local_after_let(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> {
         self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
-            let local = this.parse_local(attrs.into())?;
+            let local = this.parse_local(attrs)?;
             // FIXME - maybe capture semicolon in recovery?
             Ok((
                 this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Local(local)),
@@ -516,9 +509,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parses a block. Inner attributes are allowed.
-    pub(super) fn parse_inner_attrs_and_block(
-        &mut self,
-    ) -> PResult<'a, (Vec<Attribute>, P<Block>)> {
+    pub(super) fn parse_inner_attrs_and_block(&mut self) -> PResult<'a, (AttrVec, P<Block>)> {
         self.parse_block_common(self.token.span, BlockCheckMode::Default)
     }
 
@@ -527,8 +518,8 @@ impl<'a> Parser<'a> {
         &mut self,
         lo: Span,
         blk_mode: BlockCheckMode,
-    ) -> PResult<'a, (Vec<Attribute>, P<Block>)> {
-        maybe_whole!(self, NtBlock, |x| (Vec::new(), x));
+    ) -> PResult<'a, (AttrVec, P<Block>)> {
+        maybe_whole!(self, NtBlock, |x| (AttrVec::new(), x));
 
         self.maybe_recover_unexpected_block_label();
         if !self.eat(&token::OpenDelim(Delimiter::Brace)) {
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 31b40a83e60..4a2cf74905b 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -598,11 +598,11 @@ impl<'a> Parser<'a> {
         let path = self.parse_path_inner(PathStyle::Type, ty_generics)?;
         if self.eat(&token::Not) {
             // Macro invocation in type position
-            Ok(TyKind::MacCall(MacCall {
+            Ok(TyKind::MacCall(P(MacCall {
                 path,
                 args: self.parse_mac_args()?,
                 prior_type_ascription: self.last_type_ascription,
-            }))
+            })))
         } else if allow_plus == AllowPlus::Yes && self.check_plus() {
             // `Trait1 + Trait2 + 'a`
             self.parse_remaining_bounds_path(Vec::new(), path, lo, true)
@@ -640,7 +640,13 @@ impl<'a> Parser<'a> {
         let mut bounds = Vec::new();
         let mut negative_bounds = Vec::new();
 
-        while self.can_begin_bound() || self.token.is_keyword(kw::Dyn) {
+        while self.can_begin_bound()
+            // Continue even if we find a keyword.
+            // This is necessary for error recover on, for example, `impl fn()`.
+            //
+            // The only keyword that can go after generic bounds is `where`, so stop if it's it.
+            || (self.token.is_reserved_ident() && !self.token.is_keyword(kw::Where))
+        {
             if self.token.is_keyword(kw::Dyn) {
                 // Account for `&dyn Trait + dyn Other`.
                 self.struct_span_err(self.token.span, "invalid `dyn` keyword")
@@ -804,6 +810,20 @@ impl<'a> Parser<'a> {
             let span = tilde.to(self.prev_token.span);
             self.sess.gated_spans.gate(sym::const_trait_impl, span);
             Some(span)
+        } else if self.eat_keyword(kw::Const) {
+            let span = self.prev_token.span;
+            self.sess.gated_spans.gate(sym::const_trait_impl, span);
+
+            self.struct_span_err(span, "const bounds must start with `~`")
+                .span_suggestion(
+                    span.shrink_to_lo(),
+                    "add `~`",
+                    "~",
+                    Applicability::MachineApplicable,
+                )
+                .emit();
+
+            Some(span)
         } else {
             None
         };