When encountering chained operators use heuristics to recover from bad turbofish
This commit is contained in:
parent
f2023ac599
commit
ed60cf2475
5 changed files with 121 additions and 27 deletions
|
@ -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(
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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 `<...>`
|
||||||
}
|
}
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue