diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 31dd358ad51f4..18888dd2be5a2 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -671,6 +671,16 @@ impl Pat { }); contains_never_pattern } + + /// Return a name suitable for diagnostics. + pub fn descr(&self) -> Option { + match &self.kind { + PatKind::Wild => Some("_".to_string()), + PatKind::Ident(BindingAnnotation::NONE, ident, None) => Some(format!("{ident}")), + PatKind::Ref(pat, mutbl) => pat.descr().map(|d| format!("&{}{d}", mutbl.prefix_str())), + _ => None, + } + } } /// A single field in a struct pattern. @@ -1053,6 +1063,7 @@ pub struct Local { pub ty: Option>, pub kind: LocalKind, pub span: Span, + pub colon_sp: Option, pub attrs: AttrVec, pub tokens: Option, } @@ -3337,7 +3348,7 @@ mod size_asserts { static_assert_size!(Item, 136); static_assert_size!(ItemKind, 64); static_assert_size!(LitKind, 24); - static_assert_size!(Local, 72); + static_assert_size!(Local, 80); static_assert_size!(MetaItemLit, 40); static_assert_size!(Param, 40); static_assert_size!(Pat, 72); diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 2faf3228d551e..bbbb34f7f2f48 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -609,7 +609,7 @@ pub fn noop_visit_parenthesized_parameter_data( } pub fn noop_visit_local(local: &mut P, vis: &mut T) { - let Local { id, pat, ty, kind, span, attrs, tokens } = local.deref_mut(); + let Local { id, pat, ty, kind, span, colon_sp, attrs, tokens } = local.deref_mut(); vis.visit_id(id); vis.visit_pat(pat); visit_opt(ty, |ty| vis.visit_ty(ty)); @@ -624,6 +624,7 @@ pub fn noop_visit_local(local: &mut P, vis: &mut T) { } } vis.visit_span(span); + visit_opt(colon_sp, |sp| vis.visit_span(sp)); visit_attrs(attrs, vis); visit_lazy_tts(tokens, vis); } diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 5879e025e7dbd..9ce2084c84704 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -165,6 +165,7 @@ impl<'a> ExtCtxt<'a> { id: ast::DUMMY_NODE_ID, kind: LocalKind::Init(ex), span: sp, + colon_sp: None, attrs: AttrVec::new(), tokens: None, }); @@ -194,6 +195,7 @@ impl<'a> ExtCtxt<'a> { id: ast::DUMMY_NODE_ID, kind: LocalKind::Init(ex), span: sp, + colon_sp: None, attrs: AttrVec::new(), tokens: None, }); @@ -208,6 +210,7 @@ impl<'a> ExtCtxt<'a> { id: ast::DUMMY_NODE_ID, kind: LocalKind::Decl, span, + colon_sp: None, attrs: AttrVec::new(), tokens: None, }); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 316a9c4f8dfc2..54771cccd75a1 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -430,7 +430,7 @@ impl<'a> Parser<'a> { /// The method does not advance the current token. /// /// Also performs recovery for `and` / `or` which are mistaken for `&&` and `||` respectively. - fn check_assoc_op(&self) -> Option> { + pub fn check_assoc_op(&self) -> Option> { let (op, span) = match (AssocOp::from_token(&self.token), self.token.ident()) { // When parsing const expressions, stop parsing when encountering `>`. ( @@ -994,7 +994,7 @@ impl<'a> Parser<'a> { } } - fn parse_dot_suffix_expr(&mut self, lo: Span, base: P) -> PResult<'a, P> { + pub fn parse_dot_suffix_expr(&mut self, lo: Span, base: P) -> PResult<'a, P> { match self.token.uninterpolate().kind { token::Ident(..) => self.parse_dot_suffix(base, lo), token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => { diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 9fe74e80a2b5e..65c0f7d49c153 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -294,17 +294,22 @@ impl<'a> Parser<'a> { let (pat, colon) = self.parse_pat_before_ty(None, RecoverComma::Yes, PatternLocation::LetBinding)?; - let (err, ty) = if colon { + let (err, ty, colon_sp) = if colon { // Save the state of the parser before parsing type normally, in case there is a `:` // instead of an `=` typo. let parser_snapshot_before_type = self.clone(); let colon_sp = self.prev_token.span; match self.parse_ty() { - Ok(ty) => (None, Some(ty)), + Ok(ty) => (None, Some(ty), Some(colon_sp)), Err(mut err) => { - if let Ok(snip) = self.span_to_snippet(pat.span) { - err.span_label(pat.span, format!("while parsing the type for `{snip}`")); - } + err.span_label( + colon_sp, + format!( + "while parsing the type for {}", + pat.descr() + .map_or_else(|| "the binding".to_string(), |n| format!("`{n}`")) + ), + ); // we use noexpect here because we don't actually expect Eq to be here // but we are still checking for it in order to be able to handle it if // it is there @@ -317,11 +322,11 @@ impl<'a> Parser<'a> { mem::replace(self, parser_snapshot_before_type); Some((parser_snapshot_after_type, colon_sp, err)) }; - (err, None) + (err, None, Some(colon_sp)) } } } else { - (None, None) + (None, None, None) }; let init = match (self.parse_initializer(err.is_some()), err) { (Ok(init), None) => { @@ -380,7 +385,16 @@ impl<'a> Parser<'a> { } }; let hi = if self.token == token::Semi { self.token.span } else { self.prev_token.span }; - Ok(P(ast::Local { ty, pat, kind, id: DUMMY_NODE_ID, span: lo.to(hi), attrs, tokens: None })) + Ok(P(ast::Local { + ty, + pat, + kind, + id: DUMMY_NODE_ID, + span: lo.to(hi), + colon_sp, + attrs, + tokens: None, + })) } fn check_let_else_init_bool_expr(&self, init: &ast::Expr) { @@ -750,7 +764,7 @@ impl<'a> Parser<'a> { } } StmtKind::Expr(_) | StmtKind::MacCall(_) => {} - StmtKind::Local(local) if let Err(e) = self.expect_semi() => { + StmtKind::Local(local) if let Err(mut e) = self.expect_semi() => { // We might be at the `,` in `let x = foo;`. Try to recover. match &mut local.kind { LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => { @@ -758,7 +772,47 @@ impl<'a> Parser<'a> { // We found `foo`, have we fully recovered? self.expect_semi()?; } - LocalKind::Decl => return Err(e), + LocalKind::Decl => { + if let Some(colon_sp) = local.colon_sp { + e.span_label( + colon_sp, + format!( + "while parsing the type for {}", + local.pat.descr().map_or_else( + || "the binding".to_string(), + |n| format!("`{n}`") + ) + ), + ); + let suggest_eq = if self.token.kind == token::Dot + && let _ = self.bump() + && let mut snapshot = self.create_snapshot_for_diagnostic() + && let Ok(_) = snapshot.parse_dot_suffix_expr( + colon_sp, + self.mk_expr_err( + colon_sp, + self.dcx().delayed_bug("error during `:` -> `=` recovery"), + ), + ) { + true + } else if let Some(op) = self.check_assoc_op() + && op.node.can_continue_expr_unambiguously() + { + true + } else { + false + }; + if suggest_eq { + e.span_suggestion_short( + colon_sp, + "use `=` if you meant to assign", + "=", + Applicability::MaybeIncorrect, + ); + } + } + return Err(e); + } } eat_semi = false; } diff --git a/tests/ui/const-generics/bad-const-generic-exprs.stderr b/tests/ui/const-generics/bad-const-generic-exprs.stderr index 17a63a96fe4fe..6d308bdc90708 100644 --- a/tests/ui/const-generics/bad-const-generic-exprs.stderr +++ b/tests/ui/const-generics/bad-const-generic-exprs.stderr @@ -146,9 +146,9 @@ error: expected one of `,` or `>`, found `0` --> $DIR/bad-const-generic-exprs.rs:43:17 | LL | let _: Wow; - | - ^ expected one of `,` or `>` - | | - | while parsing the type for `_` + | - ^ expected one of `,` or `>` + | | + | while parsing the type for `_` | help: you might have meant to end the type parameters here | diff --git a/tests/ui/issues/issue-34334.stderr b/tests/ui/issues/issue-34334.stderr index ac2e63eca3ede..e14629f5f3aa2 100644 --- a/tests/ui/issues/issue-34334.stderr +++ b/tests/ui/issues/issue-34334.stderr @@ -2,9 +2,9 @@ error: expected one of `,`, `:`, or `>`, found `=` --> $DIR/issue-34334.rs:2:29 | LL | let sr: Vec<(u32, _, _) = vec![]; - | -- ^ expected one of `,`, `:`, or `>` - | | - | while parsing the type for `sr` + | - ^ expected one of `,`, `:`, or `>` + | | + | while parsing the type for `sr` | help: you might have meant to end the type parameters here | diff --git a/tests/ui/parser/better-expected.stderr b/tests/ui/parser/better-expected.stderr index 6cb9a49605ff4..f4ec933be1643 100644 --- a/tests/ui/parser/better-expected.stderr +++ b/tests/ui/parser/better-expected.stderr @@ -2,9 +2,9 @@ error: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `]`, found `3` --> $DIR/better-expected.rs:2:19 | LL | let x: [isize 3]; - | - ^ expected one of 7 possible tokens - | | - | while parsing the type for `x` + | - ^ expected one of 7 possible tokens + | | + | while parsing the type for `x` error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-84117.stderr b/tests/ui/parser/issues/issue-84117.stderr index 958f3b40bb0eb..9f603b84434bb 100644 --- a/tests/ui/parser/issues/issue-84117.stderr +++ b/tests/ui/parser/issues/issue-84117.stderr @@ -2,9 +2,9 @@ error: expected one of `>`, a const expression, lifetime, or type, found `}` --> $DIR/issue-84117.rs:2:67 | LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, } - | ----------- ^ expected one of `>`, a const expression, lifetime, or type - | | - | while parsing the type for `inner_local` + | - ^ expected one of `>`, a const expression, lifetime, or type + | | + | while parsing the type for `inner_local` | help: you might have meant to end the type parameters here | @@ -25,7 +25,7 @@ error: expected one of `,` or `>`, found `}` --> $DIR/issue-84117.rs:8:1 | LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, } - | ----------- while parsing the type for `outer_local` - expected one of `,` or `>` + | - while parsing the type for `outer_local` - expected one of `,` or `>` ... LL | } | ^ unexpected token @@ -43,9 +43,9 @@ error: expected one of `>`, a const expression, lifetime, or type, found `}` --> $DIR/issue-84117.rs:2:67 | LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, } - | ----------- ^ expected one of `>`, a const expression, lifetime, or type - | | - | while parsing the type for `inner_local` + | - ^ expected one of `>`, a const expression, lifetime, or type + | | + | while parsing the type for `inner_local` | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: you might have meant to end the type parameters here diff --git a/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.stderr b/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.stderr index b21e788aa7300..f01da7a38f3e1 100644 --- a/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.stderr +++ b/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.stderr @@ -2,9 +2,9 @@ error: expected one of `,`, `:`, or `>`, found `=` --> $DIR/missing-closing-angle-bracket-eq-constraint.rs:7:23 | LL | let v : Vec<(u32,_) = vec![]; - | - ^ expected one of `,`, `:`, or `>` - | | - | while parsing the type for `v` + | - ^ expected one of `,`, `:`, or `>` + | | + | while parsing the type for `v` | help: you might have meant to end the type parameters here | @@ -15,9 +15,9 @@ error: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `{` --> $DIR/missing-closing-angle-bracket-eq-constraint.rs:13:32 | LL | let foo : Foo::`, found `=` --> $DIR/missing-closing-angle-bracket-eq-constraint.rs:18:18 | LL | let v : Vec<'a = vec![]; - | - ^ expected one of `,`, `:`, or `>` - | | - | while parsing the type for `v` + | - ^ expected one of `,`, `:`, or `>` + | | + | while parsing the type for `v` | help: you might have meant to end the type parameters here | diff --git a/tests/ui/parser/nested-missing-closing-angle-bracket.stderr b/tests/ui/parser/nested-missing-closing-angle-bracket.stderr index 8d69125f8e957..2541d9af1842a 100644 --- a/tests/ui/parser/nested-missing-closing-angle-bracket.stderr +++ b/tests/ui/parser/nested-missing-closing-angle-bracket.stderr @@ -2,7 +2,7 @@ error: expected one of `,` or `>`, found `;` --> $DIR/nested-missing-closing-angle-bracket.rs:2:46 | LL | let v : Vec:: = vec![vec![]]; - | - while parsing the type for `v` ^ expected one of `,` or `>` + | - while parsing the type for `v` ^ expected one of `,` or `>` error: aborting due to 1 previous error diff --git a/tests/ui/parser/recover/recover-colon-instead-of-eq-in-local.rs b/tests/ui/parser/recover/recover-colon-instead-of-eq-in-local.rs new file mode 100644 index 0000000000000..ed4de58cd23ce --- /dev/null +++ b/tests/ui/parser/recover/recover-colon-instead-of-eq-in-local.rs @@ -0,0 +1,3 @@ +fn main() { + let _: std::env::temp_dir().join("foo"); //~ ERROR expected one of +} diff --git a/tests/ui/parser/recover/recover-colon-instead-of-eq-in-local.stderr b/tests/ui/parser/recover/recover-colon-instead-of-eq-in-local.stderr new file mode 100644 index 0000000000000..d03f3ae02830b --- /dev/null +++ b/tests/ui/parser/recover/recover-colon-instead-of-eq-in-local.stderr @@ -0,0 +1,11 @@ +error: expected one of `!`, `+`, `->`, `::`, `;`, or `=`, found `.` + --> $DIR/recover-colon-instead-of-eq-in-local.rs:2:32 + | +LL | let _: std::env::temp_dir().join("foo"); + | - ^ expected one of `!`, `+`, `->`, `::`, `;`, or `=` + | | + | while parsing the type for `_` + | help: use `=` if you meant to assign + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/removed-syntax/removed-syntax-fn-sigil.stderr b/tests/ui/parser/removed-syntax/removed-syntax-fn-sigil.stderr index 0d37741670066..c089e0ba9693c 100644 --- a/tests/ui/parser/removed-syntax/removed-syntax-fn-sigil.stderr +++ b/tests/ui/parser/removed-syntax/removed-syntax-fn-sigil.stderr @@ -8,7 +8,9 @@ error: expected one of `->`, `;`, or `=`, found `~` --> $DIR/removed-syntax-fn-sigil.rs:2:14 | LL | let x: fn~() = || (); - | ^ expected one of `->`, `;`, or `=` + | - ^ expected one of `->`, `;`, or `=` + | | + | while parsing the type for `x` error: aborting due to 2 previous errors diff --git a/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr b/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr index 0c109ff6bbad1..da8f4ca5f0cd7 100644 --- a/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr +++ b/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr @@ -2,7 +2,9 @@ error: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `=`, found `@` --> $DIR/nested-type-ascription-syntactically-invalid.rs:18:15 | LL | let a: u8 @ b = 0; - | ^ expected one of 7 possible tokens + | - ^ expected one of 7 possible tokens + | | + | while parsing the type for `a` error: expected one of `)`, `,`, `@`, or `|`, found `:` --> $DIR/nested-type-ascription-syntactically-invalid.rs:24:15 @@ -16,7 +18,9 @@ error: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `=`, found `@` --> $DIR/nested-type-ascription-syntactically-invalid.rs:30:15 | LL | let a: T1 @ Outer(b: T2); - | ^ expected one of 7 possible tokens + | - ^ expected one of 7 possible tokens + | | + | while parsing the type for `a` error: aborting due to 3 previous errors diff --git a/tests/ui/stats/hir-stats.stderr b/tests/ui/stats/hir-stats.stderr index 2c21c25d7c77e..579a5d5180ef6 100644 --- a/tests/ui/stats/hir-stats.stderr +++ b/tests/ui/stats/hir-stats.stderr @@ -10,16 +10,16 @@ ast-stats-1 - BoundPredicate 56 ( 0.8%) 1 ast-stats-1 Attribute 64 ( 1.0%) 2 32 ast-stats-1 - Normal 32 ( 0.5%) 1 ast-stats-1 - DocComment 32 ( 0.5%) 1 -ast-stats-1 Local 72 ( 1.1%) 1 72 -ast-stats-1 Arm 96 ( 1.5%) 2 48 -ast-stats-1 ForeignItem 96 ( 1.5%) 1 96 -ast-stats-1 - Fn 96 ( 1.5%) 1 +ast-stats-1 Local 80 ( 1.2%) 1 80 +ast-stats-1 Arm 96 ( 1.4%) 2 48 +ast-stats-1 ForeignItem 96 ( 1.4%) 1 96 +ast-stats-1 - Fn 96 ( 1.4%) 1 ast-stats-1 FnDecl 120 ( 1.8%) 5 24 ast-stats-1 FieldDef 160 ( 2.4%) 2 80 ast-stats-1 Stmt 160 ( 2.4%) 5 32 ast-stats-1 - Local 32 ( 0.5%) 1 ast-stats-1 - MacCall 32 ( 0.5%) 1 -ast-stats-1 - Expr 96 ( 1.5%) 3 +ast-stats-1 - Expr 96 ( 1.4%) 3 ast-stats-1 Param 160 ( 2.4%) 4 40 ast-stats-1 Block 192 ( 2.9%) 6 32 ast-stats-1 Variant 208 ( 3.1%) 2 104 @@ -28,7 +28,7 @@ ast-stats-1 - Trait 352 ( 5.3%) 4 ast-stats-1 AssocItem 352 ( 5.3%) 4 88 ast-stats-1 - Type 176 ( 2.7%) 2 ast-stats-1 - Fn 176 ( 2.7%) 2 -ast-stats-1 GenericParam 480 ( 7.3%) 5 96 +ast-stats-1 GenericParam 480 ( 7.2%) 5 96 ast-stats-1 Pat 504 ( 7.6%) 7 72 ast-stats-1 - Struct 72 ( 1.1%) 1 ast-stats-1 - Wild 72 ( 1.1%) 1 @@ -53,7 +53,7 @@ ast-stats-1 - Impl 136 ( 2.1%) 1 ast-stats-1 - Fn 272 ( 4.1%) 2 ast-stats-1 - Use 408 ( 6.2%) 3 ast-stats-1 ---------------------------------------------------------------- -ast-stats-1 Total 6_616 +ast-stats-1 Total 6_624 ast-stats-1 ast-stats-2 POST EXPANSION AST STATS ast-stats-2 Name Accumulated Size Count Item Size @@ -64,7 +64,7 @@ ast-stats-2 Crate 40 ( 0.6%) 1 40 ast-stats-2 ExprField 48 ( 0.7%) 1 48 ast-stats-2 WherePredicate 56 ( 0.8%) 1 56 ast-stats-2 - BoundPredicate 56 ( 0.8%) 1 -ast-stats-2 Local 72 ( 1.0%) 1 72 +ast-stats-2 Local 80 ( 1.1%) 1 80 ast-stats-2 Arm 96 ( 1.3%) 2 48 ast-stats-2 ForeignItem 96 ( 1.3%) 1 96 ast-stats-2 - Fn 96 ( 1.3%) 1 @@ -86,7 +86,7 @@ ast-stats-2 - Trait 352 ( 4.9%) 4 ast-stats-2 AssocItem 352 ( 4.9%) 4 88 ast-stats-2 - Type 176 ( 2.4%) 2 ast-stats-2 - Fn 176 ( 2.4%) 2 -ast-stats-2 GenericParam 480 ( 6.7%) 5 96 +ast-stats-2 GenericParam 480 ( 6.6%) 5 96 ast-stats-2 Pat 504 ( 7.0%) 7 72 ast-stats-2 - Struct 72 ( 1.0%) 1 ast-stats-2 - Wild 72 ( 1.0%) 1 @@ -113,7 +113,7 @@ ast-stats-2 - Impl 136 ( 1.9%) 1 ast-stats-2 - Fn 272 ( 3.8%) 2 ast-stats-2 - Use 544 ( 7.5%) 4 ast-stats-2 ---------------------------------------------------------------- -ast-stats-2 Total 7_216 +ast-stats-2 Total 7_224 ast-stats-2 hir-stats HIR STATS hir-stats Name Accumulated Size Count Item Size diff --git a/tests/ui/type/type-ascription-instead-of-initializer.stderr b/tests/ui/type/type-ascription-instead-of-initializer.stderr index 429501c2762fc..efa917334bf0c 100644 --- a/tests/ui/type/type-ascription-instead-of-initializer.stderr +++ b/tests/ui/type/type-ascription-instead-of-initializer.stderr @@ -2,10 +2,10 @@ error: expected type, found `10` --> $DIR/type-ascription-instead-of-initializer.rs:2:31 | LL | let x: Vec::with_capacity(10, 20); - | -- ^^ expected type - | || - | |help: use `=` if you meant to assign - | while parsing the type for `x` + | - ^^ expected type + | | + | while parsing the type for `x` + | help: use `=` if you meant to assign error[E0061]: this function takes 1 argument but 2 arguments were supplied --> $DIR/type-ascription-instead-of-initializer.rs:2:12