Suggest parentheses around if-expressions
```
error[E0308]: mismatched types
--> $DIR/expr-as-stmt-2.rs:15:15
|
LL | if true { true } else { false } && true;
| ----------^^^^-----------------
| | |
| | expected `()`, found `bool`
| expected this to be `()`
|
help: parentheses are required to parse this as an expression
|
LL | (if true { true } else { false }) && true;
| + +
```
This commit is contained in:
parent
f5b5e67f0f
commit
ff60e5c3f3
5 changed files with 303 additions and 78 deletions
|
|
@ -1897,7 +1897,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
|
|||
fcx.suggest_semicolon_at_end(cond_expr.span, &mut err);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// If this is due to an explicit `return`, suggest adding a return type.
|
||||
if let Some((fn_id, fn_decl)) = fcx.get_fn_decl(block_or_return_id)
|
||||
|
|
|
|||
|
|
@ -1912,39 +1912,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
hir::StmtKind::Expr(ref expr) => {
|
||||
// Check with expected type of `()`.
|
||||
self.check_expr_has_type_or_error(expr, self.tcx.types.unit, |err| {
|
||||
if expr.can_have_side_effects() {
|
||||
let hir_id = stmt.hir_id;
|
||||
if let hir::ExprKind::Match(..) = expr.kind
|
||||
&& let hir::Node::Block(b) = self.tcx.parent_hir_node(hir_id)
|
||||
&& let mut stmts = b.stmts.iter().skip_while(|s| s.hir_id != hir_id)
|
||||
&& let Some(_) = stmts.next() // The statement from the `match`
|
||||
&& let Some(next) = match (stmts.next(), b.expr) {
|
||||
(Some(next), _) => match next.kind {
|
||||
hir::StmtKind::Expr(next) | hir::StmtKind::Semi(next) => Some(next),
|
||||
_ => None,
|
||||
},
|
||||
(None, Some(next)) => Some(next),
|
||||
_ => None,
|
||||
}
|
||||
&& let hir::ExprKind::AddrOf(..)
|
||||
| hir::ExprKind::Unary(..)
|
||||
| hir::ExprKind::Err(_) = next.kind
|
||||
{
|
||||
// We have something like `match () { _ => true } && true`. Suggest
|
||||
// wrapping in parentheses. We find the statement or expression
|
||||
// following the `match` (`&& true`) and see if it is something that
|
||||
// can reasonably be interpreted as a binop following an expression.
|
||||
err.multipart_suggestion(
|
||||
"parentheses are required to parse this as an expression",
|
||||
vec![
|
||||
(expr.span.shrink_to_lo(), "(".to_string()),
|
||||
(expr.span.shrink_to_hi(), ")".to_string()),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
self.suggest_semicolon_at_end(expr.span, err);
|
||||
}
|
||||
if self.is_next_stmt_expr_continuation(stmt.hir_id)
|
||||
&& let hir::ExprKind::Match(..) | hir::ExprKind::If(..) = expr.kind
|
||||
{
|
||||
// We have something like `match () { _ => true } && true`. Suggest
|
||||
// wrapping in parentheses. We find the statement or expression
|
||||
// following the `match` (`&& true`) and see if it is something that
|
||||
// can reasonably be interpreted as a binop following an expression.
|
||||
err.multipart_suggestion(
|
||||
"parentheses are required to parse this as an expression",
|
||||
vec![
|
||||
(expr.span.shrink_to_lo(), "(".to_string()),
|
||||
(expr.span.shrink_to_hi(), ")".to_string()),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if expr.can_have_side_effects() {
|
||||
self.suggest_semicolon_at_end(expr.span, err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// ignore-tidy-filelength
|
||||
use core::cmp::min;
|
||||
use core::iter;
|
||||
|
||||
|
|
@ -766,53 +767,118 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
needs_block: bool,
|
||||
parent_is_closure: bool,
|
||||
) {
|
||||
if expected.is_unit() {
|
||||
// `BlockTailExpression` only relevant if the tail expr would be
|
||||
// useful on its own.
|
||||
match expression.kind {
|
||||
ExprKind::Call(..)
|
||||
| ExprKind::MethodCall(..)
|
||||
| ExprKind::Loop(..)
|
||||
| ExprKind::If(..)
|
||||
| ExprKind::Match(..)
|
||||
| ExprKind::Block(..)
|
||||
if expression.can_have_side_effects()
|
||||
// If the expression is from an external macro, then do not suggest
|
||||
// adding a semicolon, because there's nowhere to put it.
|
||||
// See issue #81943.
|
||||
&& !expression.span.in_external_macro(self.tcx.sess.source_map()) =>
|
||||
if !expected.is_unit() {
|
||||
return;
|
||||
}
|
||||
// `BlockTailExpression` only relevant if the tail expr would be
|
||||
// useful on its own.
|
||||
match expression.kind {
|
||||
ExprKind::Call(..)
|
||||
| ExprKind::MethodCall(..)
|
||||
| ExprKind::Loop(..)
|
||||
| ExprKind::If(..)
|
||||
| ExprKind::Match(..)
|
||||
| ExprKind::Block(..)
|
||||
if expression.can_have_side_effects()
|
||||
// If the expression is from an external macro, then do not suggest
|
||||
// adding a semicolon, because there's nowhere to put it.
|
||||
// See issue #81943.
|
||||
&& !expression.span.in_external_macro(self.tcx.sess.source_map()) =>
|
||||
{
|
||||
if needs_block {
|
||||
err.multipart_suggestion(
|
||||
"consider using a semicolon here",
|
||||
vec![
|
||||
(expression.span.shrink_to_lo(), "{ ".to_owned()),
|
||||
(expression.span.shrink_to_hi(), "; }".to_owned()),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if let hir::Node::Block(block) = self.tcx.parent_hir_node(expression.hir_id)
|
||||
&& let hir::Node::Expr(expr) = self.tcx.parent_hir_node(block.hir_id)
|
||||
&& let hir::Node::Expr(if_expr) = self.tcx.parent_hir_node(expr.hir_id)
|
||||
&& let hir::ExprKind::If(_cond, _then, Some(_else)) = if_expr.kind
|
||||
&& let hir::Node::Stmt(stmt) = self.tcx.parent_hir_node(if_expr.hir_id)
|
||||
&& let hir::StmtKind::Expr(_) = stmt.kind
|
||||
&& self.is_next_stmt_expr_continuation(stmt.hir_id)
|
||||
{
|
||||
if needs_block {
|
||||
err.multipart_suggestion(
|
||||
"consider using a semicolon here",
|
||||
vec![
|
||||
(expression.span.shrink_to_lo(), "{ ".to_owned()),
|
||||
(expression.span.shrink_to_hi(), "; }".to_owned()),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
err.span_suggestion(
|
||||
expression.span.shrink_to_hi(),
|
||||
"consider using a semicolon here",
|
||||
";",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
ExprKind::Path(..) | ExprKind::Lit(_)
|
||||
if parent_is_closure
|
||||
&& !expression.span.in_external_macro(self.tcx.sess.source_map()) =>
|
||||
{
|
||||
err.span_suggestion_verbose(
|
||||
expression.span.shrink_to_lo(),
|
||||
"consider ignoring the value",
|
||||
"_ = ",
|
||||
err.multipart_suggestion(
|
||||
"parentheses are required to parse this as an expression",
|
||||
vec![
|
||||
(stmt.span.shrink_to_lo(), "(".to_string()),
|
||||
(stmt.span.shrink_to_hi(), ")".to_string()),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
err.span_suggestion(
|
||||
expression.span.shrink_to_hi(),
|
||||
"consider using a semicolon here",
|
||||
";",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
ExprKind::Path(..) | ExprKind::Lit(_)
|
||||
if parent_is_closure
|
||||
&& !expression.span.in_external_macro(self.tcx.sess.source_map()) =>
|
||||
{
|
||||
err.span_suggestion_verbose(
|
||||
expression.span.shrink_to_lo(),
|
||||
"consider ignoring the value",
|
||||
"_ = ",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
if let hir::Node::Block(block) = self.tcx.parent_hir_node(expression.hir_id)
|
||||
&& let hir::Node::Expr(expr) = self.tcx.parent_hir_node(block.hir_id)
|
||||
&& let hir::Node::Expr(if_expr) = self.tcx.parent_hir_node(expr.hir_id)
|
||||
&& let hir::ExprKind::If(_cond, _then, Some(_else)) = if_expr.kind
|
||||
&& let hir::Node::Stmt(stmt) = self.tcx.parent_hir_node(if_expr.hir_id)
|
||||
&& let hir::StmtKind::Expr(_) = stmt.kind
|
||||
&& self.is_next_stmt_expr_continuation(stmt.hir_id)
|
||||
{
|
||||
// The error is pointing at an arm of an if-expression, and we want to get the
|
||||
// `Span` of the whole if-expression for the suggestion. This only works for a
|
||||
// single level of nesting, which is fine.
|
||||
// We have something like `if true { false } else { true } && true`. Suggest
|
||||
// wrapping in parentheses. We find the statement or expression following the
|
||||
// `if` (`&& true`) and see if it is something that can reasonably be
|
||||
// interpreted as a binop following an expression.
|
||||
err.multipart_suggestion(
|
||||
"parentheses are required to parse this as an expression",
|
||||
vec![
|
||||
(stmt.span.shrink_to_lo(), "(".to_string()),
|
||||
(stmt.span.shrink_to_hi(), ")".to_string()),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_next_stmt_expr_continuation(&self, hir_id: HirId) -> bool {
|
||||
if let hir::Node::Block(b) = self.tcx.parent_hir_node(hir_id)
|
||||
&& let mut stmts = b.stmts.iter().skip_while(|s| s.hir_id != hir_id)
|
||||
&& let Some(_) = stmts.next() // The statement the statement that was passed in
|
||||
&& let Some(next) = match (stmts.next(), b.expr) { // The following statement
|
||||
(Some(next), _) => match next.kind {
|
||||
hir::StmtKind::Expr(next) | hir::StmtKind::Semi(next) => Some(next),
|
||||
_ => None,
|
||||
},
|
||||
(None, Some(next)) => Some(next),
|
||||
_ => None,
|
||||
}
|
||||
&& let hir::ExprKind::AddrOf(..) // prev_stmt && next
|
||||
| hir::ExprKind::Unary(..) // prev_stmt * next
|
||||
| hir::ExprKind::Err(_) = next.kind
|
||||
// prev_stmt + next
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,4 +7,25 @@ fn foo(a: Option<u32>, b: Option<u32>) -> bool {
|
|||
if let Some(y) = a { true } else { false }
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
fn bar() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if true { true } else { false } && true;
|
||||
//~^ ERROR mismatched types
|
||||
//~| ERROR mismatched types
|
||||
if true { true } else { false } && if true { true } else { false };
|
||||
//~^ ERROR mismatched types
|
||||
//~| ERROR mismatched types
|
||||
if true { true } else { false } if true { true } else { false };
|
||||
//~^ ERROR mismatched types
|
||||
//~| ERROR mismatched types
|
||||
if true { bar() } else { bar() } && if true { bar() } else { bar() };
|
||||
//~^ ERROR mismatched types
|
||||
//~| ERROR mismatched types
|
||||
if true { bar() } else { bar() } if true { bar() } else { bar() };
|
||||
//~^ ERROR mismatched types
|
||||
//~| ERROR mismatched types
|
||||
let _ = if true { true } else { false } && true; // ok
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@ LL | if let Some(x) = a { true } else { false }
|
|||
| | expected `()`, found `bool`
|
||||
| expected this to be `()`
|
||||
|
|
||||
help: parentheses are required to parse this as an expression
|
||||
|
|
||||
LL | (if let Some(x) = a { true } else { false })
|
||||
| + +
|
||||
help: you might have meant to return this value
|
||||
|
|
||||
LL | if let Some(x) = a { return true; } else { false }
|
||||
|
|
@ -21,6 +25,10 @@ LL | if let Some(x) = a { true } else { false }
|
|||
| | expected `()`, found `bool`
|
||||
| expected this to be `()`
|
||||
|
|
||||
help: parentheses are required to parse this as an expression
|
||||
|
|
||||
LL | (if let Some(x) = a { true } else { false })
|
||||
| + +
|
||||
help: you might have meant to return this value
|
||||
|
|
||||
LL | if let Some(x) = a { true } else { return false; }
|
||||
|
|
@ -41,6 +49,152 @@ help: parentheses are required to parse this as an expression
|
|||
LL | (if let Some(x) = a { true } else { false })
|
||||
| + +
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/expr-as-stmt-2.rs:15:15
|
||||
|
|
||||
LL | if true { true } else { false } && true;
|
||||
| ----------^^^^-----------------
|
||||
| | |
|
||||
| | expected `()`, found `bool`
|
||||
| expected this to be `()`
|
||||
|
|
||||
help: parentheses are required to parse this as an expression
|
||||
|
|
||||
LL | (if true { true } else { false }) && true;
|
||||
| + +
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/expr-as-stmt-2.rs:15:29
|
||||
|
|
||||
LL | if true { true } else { false } && true;
|
||||
| ------------------------^^^^^--
|
||||
| | |
|
||||
| | expected `()`, found `bool`
|
||||
| expected this to be `()`
|
||||
|
|
||||
help: parentheses are required to parse this as an expression
|
||||
|
|
||||
LL | (if true { true } else { false }) && true;
|
||||
| + +
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/expr-as-stmt-2.rs:18:15
|
||||
|
|
||||
LL | if true { true } else { false } && if true { true } else { false };
|
||||
| ----------^^^^-----------------
|
||||
| | |
|
||||
| | expected `()`, found `bool`
|
||||
| expected this to be `()`
|
||||
|
|
||||
help: parentheses are required to parse this as an expression
|
||||
|
|
||||
LL | (if true { true } else { false }) && if true { true } else { false };
|
||||
| + +
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/expr-as-stmt-2.rs:18:29
|
||||
|
|
||||
LL | if true { true } else { false } && if true { true } else { false };
|
||||
| ------------------------^^^^^--
|
||||
| | |
|
||||
| | expected `()`, found `bool`
|
||||
| expected this to be `()`
|
||||
|
|
||||
help: parentheses are required to parse this as an expression
|
||||
|
|
||||
LL | (if true { true } else { false }) && if true { true } else { false };
|
||||
| + +
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/expr-as-stmt-2.rs:21:15
|
||||
|
|
||||
LL | if true { true } else { false } if true { true } else { false };
|
||||
| ----------^^^^-----------------
|
||||
| | |
|
||||
| | expected `()`, found `bool`
|
||||
| expected this to be `()`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/expr-as-stmt-2.rs:21:29
|
||||
|
|
||||
LL | if true { true } else { false } if true { true } else { false };
|
||||
| ------------------------^^^^^--
|
||||
| | |
|
||||
| | expected `()`, found `bool`
|
||||
| expected this to be `()`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/expr-as-stmt-2.rs:24:15
|
||||
|
|
||||
LL | if true { bar() } else { bar() } && if true { bar() } else { bar() };
|
||||
| ----------^^^^^-----------------
|
||||
| | |
|
||||
| | expected `()`, found `bool`
|
||||
| expected this to be `()`
|
||||
|
|
||||
help: parentheses are required to parse this as an expression
|
||||
|
|
||||
LL | (if true { bar() } else { bar() }) && if true { bar() } else { bar() };
|
||||
| + +
|
||||
help: consider using a semicolon here
|
||||
|
|
||||
LL | if true { bar() } else { bar() }; && if true { bar() } else { bar() };
|
||||
| +
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/expr-as-stmt-2.rs:24:30
|
||||
|
|
||||
LL | if true { bar() } else { bar() } && if true { bar() } else { bar() };
|
||||
| -------------------------^^^^^--
|
||||
| | |
|
||||
| | expected `()`, found `bool`
|
||||
| expected this to be `()`
|
||||
|
|
||||
help: parentheses are required to parse this as an expression
|
||||
|
|
||||
LL | (if true { bar() } else { bar() }) && if true { bar() } else { bar() };
|
||||
| + +
|
||||
help: consider using a semicolon here
|
||||
|
|
||||
LL | if true { bar() } else { bar() }; && if true { bar() } else { bar() };
|
||||
| +
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/expr-as-stmt-2.rs:27:15
|
||||
|
|
||||
LL | if true { bar() } else { bar() } if true { bar() } else { bar() };
|
||||
| ----------^^^^^-----------------
|
||||
| | |
|
||||
| | expected `()`, found `bool`
|
||||
| expected this to be `()`
|
||||
|
|
||||
help: consider using a semicolon here
|
||||
|
|
||||
LL | if true { bar(); } else { bar() } if true { bar() } else { bar() };
|
||||
| +
|
||||
help: consider using a semicolon here
|
||||
|
|
||||
LL | if true { bar() } else { bar() }; if true { bar() } else { bar() };
|
||||
| +
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/expr-as-stmt-2.rs:27:30
|
||||
|
|
||||
LL | if true { bar() } else { bar() } if true { bar() } else { bar() };
|
||||
| -------------------------^^^^^--
|
||||
| | |
|
||||
| | expected `()`, found `bool`
|
||||
| expected this to be `()`
|
||||
|
|
||||
help: consider using a semicolon here
|
||||
|
|
||||
LL | if true { bar() } else { bar(); } if true { bar() } else { bar() };
|
||||
| +
|
||||
help: consider using a semicolon here
|
||||
|
|
||||
LL | if true { bar() } else { bar() }; if true { bar() } else { bar() };
|
||||
| +
|
||||
|
||||
error: aborting due to 13 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue