When encountering chained operators use heuristics to recover from bad turbofish

This commit is contained in:
Esteban Küber 2019-09-29 19:07:26 -07:00
parent f2023ac599
commit ed60cf2475
5 changed files with 121 additions and 27 deletions

View file

@ -543,16 +543,25 @@ impl<'a> Parser<'a> {
} }
/// Produces an error if comparison operators are chained (RFC #558). /// Produces an error if comparison operators are chained (RFC #558).
/// We only need to check the LHS, not the RHS, because all comparison ops /// We only need to check the LHS, not the RHS, because all comparison ops have same
/// have same precedence and are left-associative. /// 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(), /// This can also be hit if someone incorrectly writes `foo<bar>()` when they should have used
"check_no_chained_comparison: {:?} is not comparison", /// the turbofish syntax. We attempt some heuristic recovery if that is the case.
outer_op); 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 { match lhs.kind {
ExprKind::Binary(op, _, _) if op.node.is_comparison() => { ExprKind::Binary(op, _, _) if op.node.is_comparison() => {
// Respan to include both operators. // 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( let mut err = self.struct_span_err(
op_span, op_span,
"chained comparison operators require parentheses", "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::Less || // Include `<` to provide this recommendation
*outer_op == AssocOp::Greater // even in a case like the following: *outer_op == AssocOp::Greater // even in a case like the following:
{ // Foo<Bar<Baz<Qux, ()>>> { // Foo<Bar<Baz<Qux, ()>>>
err.help( let msg = "use `::<...>` instead of `<...>` if you meant to specify type \
"use `::<...>` instead of `<...>` if you meant to specify type arguments"); arguments";
err.help("or use `(...)` if you meant to specify fn arguments"); if *outer_op == AssocOp::Less {
// These cases cause too many knock-down errors, bail out (#61329). // 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); return Err(err);
} }
err.emit(); err.emit();
} }
_ => {} _ => {}
} }
Ok(()) Ok(None)
} }
crate fn maybe_report_ambiguous_plus( crate fn maybe_report_ambiguous_plus(

View file

@ -238,7 +238,9 @@ impl<'a> Parser<'a> {
self.bump(); self.bump();
if op.is_comparison() { 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: // Special cases:
if op == AssocOp::As { if op == AssocOp::As {

View file

@ -2,16 +2,17 @@ error: chained comparison operators require parentheses
--> $DIR/issue-40396.rs:2:20 --> $DIR/issue-40396.rs:2:20
| |
LL | (0..13).collect<Vec<i32>>(); 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 LL | (0..13).collect::<Vec<i32>>();
= help: or use `(...)` if you meant to specify fn arguments | ^^
error: chained comparison operators require parentheses error: chained comparison operators require parentheses
--> $DIR/issue-40396.rs:7:8 --> $DIR/issue-40396.rs:7:8
| |
LL | Vec<i32>::new(); LL | Vec<i32>::new();
| ^^^^^^^ | ^^^^^
| |
= 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 = 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 --> $DIR/issue-40396.rs:12:20
| |
LL | (0..13).collect<Vec<i32>(); 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 LL | (0..13).collect::<Vec<i32>();
= help: or use `(...)` if you meant to specify fn arguments | ^^
error: aborting due to 3 previous errors error: aborting due to 3 previous errors

View file

@ -13,5 +13,8 @@ fn main() {
f<X>(); f<X>();
//~^ ERROR chained comparison operators require parentheses //~^ ERROR chained comparison operators require parentheses
//~| HELP: use `::<...>` instead of `<...>` //~| 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 `<...>`
} }

View file

@ -2,22 +2,33 @@ error: chained comparison operators require parentheses
--> $DIR/require-parens-for-chained-comparison.rs:5:11 --> $DIR/require-parens-for-chained-comparison.rs:5:11
| |
LL | false == false == false; LL | false == false == false;
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^
error: chained comparison operators require parentheses error: chained comparison operators require parentheses
--> $DIR/require-parens-for-chained-comparison.rs:8:11 --> $DIR/require-parens-for-chained-comparison.rs:8:11
| |
LL | false == 0 < 2; LL | false == 0 < 2;
| ^^^^^^^^ | ^^^^^^
error: chained comparison operators require parentheses error: chained comparison operators require parentheses
--> $DIR/require-parens-for-chained-comparison.rs:13:6 --> $DIR/require-parens-for-chained-comparison.rs:13:6
| |
LL | f<X>(); 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 LL | f::<X>();
= help: or use `(...)` if you meant to specify fn arguments | ^^
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 error[E0308]: mismatched types
--> $DIR/require-parens-for-chained-comparison.rs:8:14 --> $DIR/require-parens-for-chained-comparison.rs:8:14
@ -37,6 +48,6 @@ LL | false == 0 < 2;
= note: expected type `bool` = note: expected type `bool`
found type `{integer}` 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`. For more information about this error, try `rustc --explain E0308`.