about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libsyntax/parse/diagnostics.rs100
-rw-r--r--src/libsyntax/parse/parser/expr.rs4
-rw-r--r--src/test/ui/did_you_mean/issue-40396.stderr16
-rw-r--r--src/test/ui/parser/require-parens-for-chained-comparison.rs5
-rw-r--r--src/test/ui/parser/require-parens-for-chained-comparison.stderr23
5 files changed, 121 insertions, 27 deletions
diff --git a/src/libsyntax/parse/diagnostics.rs b/src/libsyntax/parse/diagnostics.rs
index e8d7b7663ed..0e3d873b252 100644
--- a/src/libsyntax/parse/diagnostics.rs
+++ b/src/libsyntax/parse/diagnostics.rs
@@ -543,16 +543,25 @@ impl<'a> Parser<'a> {
     }
 
     /// Produces an error if comparison operators are chained (RFC #558).
-    /// We only need to check the LHS, not the RHS, because all comparison ops
-    /// have same precedence and are left-associative.
-    crate fn check_no_chained_comparison(&self, lhs: &Expr, outer_op: &AssocOp) -> PResult<'a, ()> {
-        debug_assert!(outer_op.is_comparison(),
-                      "check_no_chained_comparison: {:?} is not comparison",
-                      outer_op);
+    /// We only need to check the LHS, not the RHS, because all comparison ops have same
+    /// precedence and are left-associative.
+    ///
+    /// This can also be hit if someone incorrectly writes `foo<bar>()` when they should have used
+    /// the turbofish syntax. We attempt some heuristic recovery if that is the case.
+    crate fn check_no_chained_comparison(
+        &mut self,
+        lhs: &Expr,
+        outer_op: &AssocOp,
+    ) -> PResult<'a, Option<P<Expr>>> {
+        debug_assert!(
+            outer_op.is_comparison(),
+            "check_no_chained_comparison: {:?} is not comparison",
+            outer_op,
+        );
         match lhs.kind {
             ExprKind::Binary(op, _, _) if op.node.is_comparison() => {
                 // Respan to include both operators.
-                let op_span = op.span.to(self.token.span);
+                let op_span = op.span.to(self.prev_span);
                 let mut err = self.struct_span_err(
                     op_span,
                     "chained comparison operators require parentheses",
@@ -561,17 +570,84 @@ impl<'a> Parser<'a> {
                     *outer_op == AssocOp::Less ||  // Include `<` to provide this recommendation
                     *outer_op == AssocOp::Greater  // even in a case like the following:
                 {                                  //     Foo<Bar<Baz<Qux, ()>>>
-                    err.help(
-                        "use `::<...>` instead of `<...>` if you meant to specify type arguments");
-                    err.help("or use `(...)` if you meant to specify fn arguments");
-                    // These cases cause too many knock-down errors, bail out (#61329).
+                    let msg = "use `::<...>` instead of `<...>` if you meant to specify type \
+                               arguments";
+                    if *outer_op == AssocOp::Less {
+                    // if self.look_ahead(1, |t| t.kind == token::Lt || t.kind == token::ModSep) {
+                        let snapshot = self.clone();
+                        self.bump();
+                        // So far we have parsed `foo<bar<`
+                        let mut acc = 1;
+                        while acc > 0 {
+                            match &self.token.kind {
+                                token::Lt => {
+                                    acc += 1;
+                                }
+                                token::Gt => {
+                                    acc -= 1;
+                                }
+                                token::BinOp(token::Shr) => {
+                                    acc -= 2;
+                                }
+                                token::Eof => {
+                                    break;
+                                }
+                                _ => {}
+                            }
+                            self.bump();
+                        }
+                        if self.token.kind != token::OpenDelim(token::Paren) {
+                            mem::replace(self, snapshot.clone());
+                        }
+                    }
+                    if self.token.kind == token::OpenDelim(token::Paren) {
+                        err.span_suggestion(
+                            op_span.shrink_to_lo(),
+                            msg,
+                            "::".to_string(),
+                            Applicability::MaybeIncorrect,
+                        );
+                        let snapshot = self.clone();
+                        self.bump();
+                        let mut acc = 1;
+                        while acc > 0 {
+                            match &self.token.kind {
+                                token::OpenDelim(token::Paren) => {
+                                    acc += 1;
+                                }
+                                token::CloseDelim(token::Paren) => {
+                                    acc -= 1;
+                                }
+                                token::Eof => {
+                                    break;
+                                }
+                                _ => {}
+                            }
+                            self.bump();
+                        }
+                        if self.token.kind == token::Eof {
+                            mem::replace(self, snapshot);
+                            return Err(err);
+                        } else {
+                            err.emit();
+                            return Ok(Some(self.mk_expr(
+                                lhs.span.to(self.prev_span),
+                                ExprKind::Err,
+                                ThinVec::new(),
+                            )));
+                        }
+                    } else {
+                        err.help(msg);
+                        err.help("or use `(...)` if you meant to specify fn arguments");
+                        // These cases cause too many knock-down errors, bail out (#61329).
+                    }
                     return Err(err);
                 }
                 err.emit();
             }
             _ => {}
         }
-        Ok(())
+        Ok(None)
     }
 
     crate fn maybe_report_ambiguous_plus(
diff --git a/src/libsyntax/parse/parser/expr.rs b/src/libsyntax/parse/parser/expr.rs
index 23674ad589d..b459782d237 100644
--- a/src/libsyntax/parse/parser/expr.rs
+++ b/src/libsyntax/parse/parser/expr.rs
@@ -238,7 +238,9 @@ impl<'a> Parser<'a> {
 
             self.bump();
             if op.is_comparison() {
-                self.check_no_chained_comparison(&lhs, &op)?;
+                if let Some(expr) = self.check_no_chained_comparison(&lhs, &op)? {
+                    return Ok(expr);
+                }
             }
             // Special cases:
             if op == AssocOp::As {
diff --git a/src/test/ui/did_you_mean/issue-40396.stderr b/src/test/ui/did_you_mean/issue-40396.stderr
index 7a08fda27e3..d0448cc265c 100644
--- a/src/test/ui/did_you_mean/issue-40396.stderr
+++ b/src/test/ui/did_you_mean/issue-40396.stderr
@@ -2,16 +2,17 @@ error: chained comparison operators require parentheses
   --> $DIR/issue-40396.rs:2:20
    |
 LL |     (0..13).collect<Vec<i32>>();
-   |                    ^^^^^^^^
+   |                    ^^^^^
+help: use `::<...>` instead of `<...>` if you meant to specify type arguments
    |
-   = help: use `::<...>` instead of `<...>` if you meant to specify type arguments
-   = help: or use `(...)` if you meant to specify fn arguments
+LL |     (0..13).collect::<Vec<i32>>();
+   |                    ^^
 
 error: chained comparison operators require parentheses
   --> $DIR/issue-40396.rs:7:8
    |
 LL |     Vec<i32>::new();
-   |        ^^^^^^^
+   |        ^^^^^
    |
    = help: use `::<...>` instead of `<...>` if you meant to specify type arguments
    = help: or use `(...)` if you meant to specify fn arguments
@@ -20,10 +21,11 @@ error: chained comparison operators require parentheses
   --> $DIR/issue-40396.rs:12:20
    |
 LL |     (0..13).collect<Vec<i32>();
-   |                    ^^^^^^^^
+   |                    ^^^^^
+help: use `::<...>` instead of `<...>` if you meant to specify type arguments
    |
-   = help: use `::<...>` instead of `<...>` if you meant to specify type arguments
-   = help: or use `(...)` if you meant to specify fn arguments
+LL |     (0..13).collect::<Vec<i32>();
+   |                    ^^
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/parser/require-parens-for-chained-comparison.rs b/src/test/ui/parser/require-parens-for-chained-comparison.rs
index 3dcc0c8f3d4..11839938cb0 100644
--- a/src/test/ui/parser/require-parens-for-chained-comparison.rs
+++ b/src/test/ui/parser/require-parens-for-chained-comparison.rs
@@ -13,5 +13,8 @@ fn main() {
     f<X>();
     //~^ ERROR chained comparison operators require parentheses
     //~| HELP: use `::<...>` instead of `<...>`
-    //~| HELP: or use `(...)`
+
+    f<Result<Option<X>, Option<Option<X>>>(1, 2);
+    //~^ ERROR chained comparison operators require parentheses
+    //~| HELP: use `::<...>` instead of `<...>`
 }
diff --git a/src/test/ui/parser/require-parens-for-chained-comparison.stderr b/src/test/ui/parser/require-parens-for-chained-comparison.stderr
index e927f4c3248..02fb56a7f9b 100644
--- a/src/test/ui/parser/require-parens-for-chained-comparison.stderr
+++ b/src/test/ui/parser/require-parens-for-chained-comparison.stderr
@@ -2,22 +2,33 @@ error: chained comparison operators require parentheses
   --> $DIR/require-parens-for-chained-comparison.rs:5:11
    |
 LL |     false == false == false;
-   |           ^^^^^^^^^^^^^^^^^
+   |           ^^^^^^^^^^^
 
 error: chained comparison operators require parentheses
   --> $DIR/require-parens-for-chained-comparison.rs:8:11
    |
 LL |     false == 0 < 2;
-   |           ^^^^^^^^
+   |           ^^^^^^
 
 error: chained comparison operators require parentheses
   --> $DIR/require-parens-for-chained-comparison.rs:13:6
    |
 LL |     f<X>();
-   |      ^^^^
+   |      ^^^
+help: use `::<...>` instead of `<...>` if you meant to specify type arguments
    |
-   = help: use `::<...>` instead of `<...>` if you meant to specify type arguments
-   = help: or use `(...)` if you meant to specify fn arguments
+LL |     f::<X>();
+   |      ^^
+
+error: chained comparison operators require parentheses
+  --> $DIR/require-parens-for-chained-comparison.rs:17:6
+   |
+LL |     f<Result<Option<X>, Option<Option<X>>>(1, 2);
+   |      ^^^^^^^^
+help: use `::<...>` instead of `<...>` if you meant to specify type arguments
+   |
+LL |     f::<Result<Option<X>, Option<Option<X>>>(1, 2);
+   |      ^^
 
 error[E0308]: mismatched types
   --> $DIR/require-parens-for-chained-comparison.rs:8:14
@@ -37,6 +48,6 @@ LL |     false == 0 < 2;
    = note: expected type `bool`
               found type `{integer}`
 
-error: aborting due to 5 previous errors
+error: aborting due to 6 previous errors
 
 For more information about this error, try `rustc --explain E0308`.