From 62effcbd5bfaf74e99def3e9a660dba9728b0b47 Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Mon, 31 Aug 2020 10:24:37 -0700 Subject: Detect turbofish with multiple type params missing leading `::` Fix #76072. --- compiler/rustc_parse/src/parser/diagnostics.rs | 46 ++++++++++++++++++++++++++ compiler/rustc_parse/src/parser/stmt.rs | 26 +++++++++++---- 2 files changed, 66 insertions(+), 6 deletions(-) (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 12efe391fb9..002d8baf03f 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -548,6 +548,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 ac067cb0eab..d8a2e106dc0 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -363,7 +363,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. @@ -387,15 +387,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, -- cgit 1.4.1-3-g733a5