Do not suggest `;` if expression is side effect free

When a tail expression isn't unit, we previously always suggested adding
a trailing `;` to turn it into a statement. This suggestion isn't
appropriate for any expression that doesn't have side-effects, as the
user will have likely wanted to call something else or do something with
the resulting value, instead of just discarding it.
This commit is contained in:
Esteban Küber 2021-02-04 17:36:06 -08:00
parent 020edd91a9
commit d669882f38
11 changed files with 92 additions and 51 deletions

View File

@ -1,5 +1,6 @@
// ignore-tidy-filelength
use crate::def::{DefKind, Namespace, Res};
use crate::def::{CtorKind, DefKind, Namespace, Res};
use crate::def_id::DefId;
crate use crate::hir_id::HirId;
use crate::{itemlikevisit, LangItem};
@ -1554,6 +1555,63 @@ impl Expr<'_> {
}
expr
}
pub fn can_have_side_effects(&self) -> bool {
match self.peel_drop_temps().kind {
ExprKind::Path(_) | ExprKind::Lit(_) => false,
ExprKind::Type(base, _)
| ExprKind::Unary(_, base)
| ExprKind::Field(base, _)
| ExprKind::Index(base, _)
| ExprKind::AddrOf(.., base)
| ExprKind::Cast(base, _) => {
// This isn't exactly true for `Index` and all `Unnary`, but we are using this
// method exclusively for diagnostics and there's a *cultural* pressure against
// them being used only for its side-effects.
base.can_have_side_effects()
}
ExprKind::Struct(_, fields, init) => fields
.iter()
.map(|field| field.expr)
.chain(init.into_iter())
.all(|e| e.can_have_side_effects()),
ExprKind::Array(args)
| ExprKind::Tup(args)
| ExprKind::Call(
Expr {
kind:
ExprKind::Path(QPath::Resolved(
None,
Path { res: Res::Def(DefKind::Ctor(_, CtorKind::Fn), _), .. },
)),
..
},
args,
) => args.iter().all(|arg| arg.can_have_side_effects()),
ExprKind::If(..)
| ExprKind::Match(..)
| ExprKind::MethodCall(..)
| ExprKind::Call(..)
| ExprKind::Closure(..)
| ExprKind::Block(..)
| ExprKind::Repeat(..)
| ExprKind::Break(..)
| ExprKind::Continue(..)
| ExprKind::Ret(..)
| ExprKind::Loop(..)
| ExprKind::Assign(..)
| ExprKind::InlineAsm(..)
| ExprKind::LlvmInlineAsm(..)
| ExprKind::AssignOp(..)
| ExprKind::ConstBlock(..)
| ExprKind::Box(..)
| ExprKind::Binary(..)
| ExprKind::Yield(..)
| ExprKind::DropTemps(..)
| ExprKind::Err => true,
}
}
}
/// Checks if the specified expression is a built-in range literal.

View File

@ -1450,7 +1450,9 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
) {
if cond_expr.span.desugaring_kind().is_none() {
err.span_label(cond_expr.span, "expected this to be `()`");
fcx.suggest_semicolon_at_end(cond_expr.span, &mut err);
if expr.can_have_side_effects() {
fcx.suggest_semicolon_at_end(cond_expr.span, &mut err);
}
}
}
fcx.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main))

View File

@ -561,7 +561,9 @@ 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.mk_unit(), |err| {
self.suggest_semicolon_at_end(expr.span, err);
if expr.can_have_side_effects() {
self.suggest_semicolon_at_end(expr.span, err);
}
});
}
hir::StmtKind::Semi(ref expr) => {

View File

@ -44,11 +44,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
blk_id: hir::HirId,
) -> bool {
let expr = expr.peel_drop_temps();
self.suggest_missing_semicolon(err, expr, expected, cause_span);
if expr.can_have_side_effects() {
self.suggest_missing_semicolon(err, expr, expected, cause_span);
}
let mut pointing_at_return_type = false;
if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
pointing_at_return_type =
self.suggest_missing_return_type(err, &fn_decl, expected, found, can_suggest);
self.suggest_missing_return_expr(err, expr, &fn_decl, expected, found);
}
pointing_at_return_type
}
@ -392,7 +395,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| ExprKind::Loop(..)
| ExprKind::If(..)
| ExprKind::Match(..)
| ExprKind::Block(..) => {
| ExprKind::Block(..)
if expression.can_have_side_effects() =>
{
err.span_suggestion(
cause_span.shrink_to_hi(),
"consider using a semicolon here",

View File

@ -14,9 +14,7 @@ LL | | true
| | ^^^^ expected `()`, found `bool`
LL | |
LL | | }
| | -- help: consider using a semicolon here
| |_____|
| expected this to be `()`
| |_____- expected this to be `()`
error: aborting due to previous error; 1 warning emitted

View File

@ -7,10 +7,6 @@ LL | if let Some(x) = a { true } else { false }
| | expected `()`, found `bool`
| expected this to be `()`
|
help: consider using a semicolon here
|
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 }
@ -25,10 +21,6 @@ LL | if let Some(x) = a { true } else { false }
| | expected `()`, found `bool`
| expected this to be `()`
|
help: consider using a semicolon here
|
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; }

View File

@ -57,7 +57,7 @@ error[E0308]: mismatched types
--> $DIR/struct-literal-variant-in-if.rs:10:20
|
LL | if x == E::V { field } {}
| ---------------^^^^^--- help: consider using a semicolon here
| ---------------^^^^^--
| | |
| | expected `()`, found `bool`
| expected this to be `()`

View File

@ -9,14 +9,6 @@ LL | | }
|
= note: expected unit type `()`
found enum `std::result::Result<_, {integer}>`
help: consider using a semicolon here
|
LL | Err(42);
| ^
help: consider using a semicolon here
|
LL | };
| ^
help: you might have meant to return this value
|
LL | return Err(42);

View File

@ -1,18 +0,0 @@
// check-only
// run-rustfix
fn main() {
match 3 {
4 => 1,
3 => {
2 //~ ERROR mismatched types
}
_ => 2
};
match 3 { //~ ERROR mismatched types
4 => 1,
3 => 2,
_ => 2
};
let _ = ();
}

View File

@ -1,11 +1,10 @@
// check-only
// run-rustfix
fn main() {
match 3 {
4 => 1,
3 => {
2 //~ ERROR mismatched types
foo() //~ ERROR mismatched types
}
_ => 2
}
@ -16,3 +15,7 @@ fn main() {
}
let _ = ();
}
fn foo() -> i32 {
42
}

View File

@ -1,20 +1,27 @@
error[E0308]: mismatched types
--> $DIR/match-needing-semi.rs:8:13
--> $DIR/match-needing-semi.rs:7:13
|
LL | / match 3 {
LL | | 4 => 1,
LL | | 3 => {
LL | | 2
| | ^ expected `()`, found integer
LL | | foo()
| | ^^^^^ expected `()`, found `i32`
LL | | }
LL | | _ => 2
LL | | }
| | -- help: consider using a semicolon here
| |_____|
| expected this to be `()`
| |_____- expected this to be `()`
|
help: consider using a semicolon here
|
LL | foo();
| ^
help: consider using a semicolon here
|
LL | };
| ^
error[E0308]: mismatched types
--> $DIR/match-needing-semi.rs:12:5
--> $DIR/match-needing-semi.rs:11:5
|
LL | / match 3 {
LL | | 4 => 1,