about summary refs log tree commit diff
path: root/compiler/rustc_parse/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-09-15 10:14:52 +0000
committerbors <bors@rust-lang.org>2020-09-15 10:14:52 +0000
commit90b1f5ae59291dd69d72fad41a22277df19dc953 (patch)
tree48be81f25a8a51daf3a29911988d58044341f67f /compiler/rustc_parse/src
parentc1589cc819ba7cf289c3ccbab30c215f0a6ba7d7 (diff)
parent62effcbd5bfaf74e99def3e9a660dba9728b0b47 (diff)
downloadrust-90b1f5ae59291dd69d72fad41a22277df19dc953.tar.gz
rust-90b1f5ae59291dd69d72fad41a22277df19dc953.zip
Auto merge of #76171 - estebank:turbofish-the-revenge, r=davidtwco
Detect turbofish with multiple type params missing leading `::`

Fix #76072.
Diffstat (limited to 'compiler/rustc_parse/src')
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs46
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs26
2 files changed, 66 insertions, 6 deletions
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<Expr>,
+    ) -> 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<bar, baz>;`. Try to recover.
+                    match &mut local.init {
+                        Some(ref mut expr) => {
+                            self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?;
+                            // We found `foo<bar, baz>`, have we fully recovered?
+                            self.expect_semi()?;
+                        }
+                        None => return Err(e),
+                    }
+                }
                 eat_semi = false;
             }
             StmtKind::Empty => eat_semi = false,