1
Fork 0

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 // ignore-tidy-filelength
use crate::def::{DefKind, Namespace, Res}; use crate::def::{DefKind, Namespace, Res};
use crate::def::{CtorKind, DefKind, Namespace, Res};
use crate::def_id::DefId; use crate::def_id::DefId;
crate use crate::hir_id::HirId; crate use crate::hir_id::HirId;
use crate::{itemlikevisit, LangItem}; use crate::{itemlikevisit, LangItem};
@ -1554,6 +1555,63 @@ impl Expr<'_> {
} }
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. /// 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() { if cond_expr.span.desugaring_kind().is_none() {
err.span_label(cond_expr.span, "expected this to be `()`"); 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)) 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) => { hir::StmtKind::Expr(ref expr) => {
// Check with expected type of `()`. // Check with expected type of `()`.
self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| { 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) => { hir::StmtKind::Semi(ref expr) => {

View file

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

View file

@ -14,9 +14,7 @@ LL | | true
| | ^^^^ expected `()`, found `bool` | | ^^^^ expected `()`, found `bool`
LL | | LL | |
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 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 `()`, found `bool`
| expected this to be `()` | 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 help: you might have meant to return this value
| |
LL | if let Some(x) = a { return true; } else { false } 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 `()`, found `bool`
| expected this to be `()` | 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 help: you might have meant to return this value
| |
LL | if let Some(x) = a { true } else { return false; } 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 --> $DIR/struct-literal-variant-in-if.rs:10:20
| |
LL | if x == E::V { field } {} LL | if x == E::V { field } {}
| ---------------^^^^^--- help: consider using a semicolon here | ---------------^^^^^--
| | | | | |
| | expected `()`, found `bool` | | expected `()`, found `bool`
| expected this to be `()` | expected this to be `()`

View file

@ -9,14 +9,6 @@ LL | | }
| |
= note: expected unit type `()` = note: expected unit type `()`
found enum `std::result::Result<_, {integer}>` 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 help: you might have meant to return this value
| |
LL | return Err(42); 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 // check-only
// run-rustfix
fn main() { fn main() {
match 3 { match 3 {
4 => 1, 4 => 1,
3 => { 3 => {
2 //~ ERROR mismatched types foo() //~ ERROR mismatched types
} }
_ => 2 _ => 2
} }
@ -16,3 +15,7 @@ fn main() {
} }
let _ = (); let _ = ();
} }
fn foo() -> i32 {
42
}

View file

@ -1,20 +1,27 @@
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/match-needing-semi.rs:8:13 --> $DIR/match-needing-semi.rs:7:13
| |
LL | / match 3 { LL | / match 3 {
LL | | 4 => 1, LL | | 4 => 1,
LL | | 3 => { LL | | 3 => {
LL | | 2 LL | | foo()
| | ^ expected `()`, found integer | | ^^^^^ expected `()`, found `i32`
LL | | } LL | | }
LL | | _ => 2 LL | | _ => 2
LL | | } 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 error[E0308]: mismatched types
--> $DIR/match-needing-semi.rs:12:5 --> $DIR/match-needing-semi.rs:11:5
| |
LL | / match 3 { LL | / match 3 {
LL | | 4 => 1, LL | | 4 => 1,