diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index e2a735188f9..9ab13db4b5f 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -553,6 +553,52 @@ impl<'a> Parser<'a> { } } + /// When writing a turbofish with multiple type parameters missing the leading `::`, we will + /// encounter a parse error when encountering the first `,`. + pub(super) fn check_mistyped_turbofish_with_multiple_type_params( + &mut self, + mut e: DiagnosticBuilder<'a>, + expr: &mut P, + ) -> PResult<'a, ()> { + if let ExprKind::Binary(binop, _, _) = &expr.kind { + if let ast::BinOpKind::Lt = binop.node { + if self.eat(&token::Comma) { + let x = self.parse_seq_to_before_end( + &token::Gt, + SeqSep::trailing_allowed(token::Comma), + |p| p.parse_ty(), + ); + match x { + Ok((_, _, false)) => { + self.bump(); // `>` + match self.parse_expr() { + Ok(_) => { + e.span_suggestion_verbose( + binop.span.shrink_to_lo(), + "use `::<...>` instead of `<...>` to specify type arguments", + "::".to_string(), + Applicability::MaybeIncorrect, + ); + e.emit(); + *expr = self.mk_expr_err(expr.span.to(self.prev_token.span)); + return Ok(()); + } + Err(mut err) => { + err.cancel(); + } + } + } + Err(mut err) => { + err.cancel(); + } + _ => {} + } + } + } + } + Err(e) + } + /// Check to see if a pair of chained operators looks like an attempt at chained comparison, /// e.g. `1 < x <= 3`. If so, suggest either splitting the comparison into two, or /// parenthesising the leftmost comparison. diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 64b959e8325..fd1c6b25aec 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -364,7 +364,7 @@ impl<'a> Parser<'a> { let mut eat_semi = true; match stmt.kind { // Expression without semicolon. - StmtKind::Expr(ref expr) + StmtKind::Expr(ref mut expr) if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) => { // Just check for errors and recover; do not eat semicolon yet. @@ -388,15 +388,29 @@ impl<'a> Parser<'a> { ); } } - e.emit(); - self.recover_stmt(); + if let Err(mut e) = + self.check_mistyped_turbofish_with_multiple_type_params(e, expr) + { + e.emit(); + self.recover_stmt(); + } // Don't complain about type errors in body tail after parse error (#57383). let sp = expr.span.to(self.prev_token.span); - stmt.kind = StmtKind::Expr(self.mk_expr_err(sp)); + *expr = self.mk_expr_err(sp); } } - StmtKind::Local(..) => { - self.expect_semi()?; + StmtKind::Local(ref mut local) => { + if let Err(e) = self.expect_semi() { + // We might be at the `,` in `let x = foo;`. Try to recover. + match &mut local.init { + Some(ref mut expr) => { + self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?; + // We found `foo`, have we fully recovered? + self.expect_semi()?; + } + None => return Err(e), + } + } eat_semi = false; } StmtKind::Empty => eat_semi = false, diff --git a/src/test/ui/did_you_mean/issue-40396.rs b/src/test/ui/did_you_mean/issue-40396.rs index e4e94bb9492..5497ba2e11f 100644 --- a/src/test/ui/did_you_mean/issue-40396.rs +++ b/src/test/ui/did_you_mean/issue-40396.rs @@ -1,8 +1,29 @@ fn main() { (0..13).collect>(); //~^ ERROR comparison operators cannot be chained + //~| HELP use `::<...>` instead Vec::new(); //~^ ERROR comparison operators cannot be chained + //~| HELP use `::<...>` instead (0..13).collect(); //~^ ERROR comparison operators cannot be chained + //~| HELP use `::<...>` instead + let x = std::collections::HashMap::new(); //~ ERROR expected one of + //~^ HELP use `::<...>` instead + let x: () = 42; //~ ERROR mismatched types + let x = { + std::collections::HashMap::new() //~ ERROR expected one of + //~^ HELP use `::<...>` instead + }; + let x: () = 42; //~ ERROR mismatched types + let x = { + std::collections::HashMap::new(); //~ ERROR expected one of + //~^ HELP use `::<...>` instead + let x: () = 42; //~ ERROR mismatched types + }; + { + std::collections::HashMap::new(1, 2); //~ ERROR expected one of + //~^ HELP use `::<...>` instead + let x: () = 32; //~ ERROR mismatched types + }; } diff --git a/src/test/ui/did_you_mean/issue-40396.stderr b/src/test/ui/did_you_mean/issue-40396.stderr index 10972697f9f..184bcf0c74b 100644 --- a/src/test/ui/did_you_mean/issue-40396.stderr +++ b/src/test/ui/did_you_mean/issue-40396.stderr @@ -10,7 +10,7 @@ LL | (0..13).collect::>(); | ^^ error: comparison operators cannot be chained - --> $DIR/issue-40396.rs:4:8 + --> $DIR/issue-40396.rs:5:8 | LL | Vec::new(); | ^ ^ @@ -21,7 +21,7 @@ LL | Vec::::new(); | ^^ error: comparison operators cannot be chained - --> $DIR/issue-40396.rs:6:20 + --> $DIR/issue-40396.rs:8:20 | LL | (0..13).collect(); | ^ ^ @@ -31,5 +31,82 @@ help: use `::<...>` instead of `<...>` to specify type arguments LL | (0..13).collect::(); | ^^ -error: aborting due to 3 previous errors +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,` + --> $DIR/issue-40396.rs:11:43 + | +LL | let x = std::collections::HashMap::new(); + | ^ expected one of 7 possible tokens + | +help: use `::<...>` instead of `<...>` to specify type arguments + | +LL | let x = std::collections::HashMap::::new(); + | ^^ +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `,` + --> $DIR/issue-40396.rs:15:39 + | +LL | std::collections::HashMap::new() + | ^ expected one of 8 possible tokens + | +help: use `::<...>` instead of `<...>` to specify type arguments + | +LL | std::collections::HashMap::::new() + | ^^ + +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `,` + --> $DIR/issue-40396.rs:20:39 + | +LL | std::collections::HashMap::new(); + | ^ expected one of 8 possible tokens + | +help: use `::<...>` instead of `<...>` to specify type arguments + | +LL | std::collections::HashMap::::new(); + | ^^ + +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `,` + --> $DIR/issue-40396.rs:25:39 + | +LL | std::collections::HashMap::new(1, 2); + | ^ expected one of 8 possible tokens + | +help: use `::<...>` instead of `<...>` to specify type arguments + | +LL | std::collections::HashMap::::new(1, 2); + | ^^ + +error[E0308]: mismatched types + --> $DIR/issue-40396.rs:13:17 + | +LL | let x: () = 42; + | -- ^^ expected `()`, found integer + | | + | expected due to this + +error[E0308]: mismatched types + --> $DIR/issue-40396.rs:18:17 + | +LL | let x: () = 42; + | -- ^^ expected `()`, found integer + | | + | expected due to this + +error[E0308]: mismatched types + --> $DIR/issue-40396.rs:22:21 + | +LL | let x: () = 42; + | -- ^^ expected `()`, found integer + | | + | expected due to this + +error[E0308]: mismatched types + --> $DIR/issue-40396.rs:27:21 + | +LL | let x: () = 32; + | -- ^^ expected `()`, found integer + | | + | expected due to this + +error: aborting due to 11 previous errors + +For more information about this error, try `rustc --explain E0308`.