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:
parent
020edd91a9
commit
d669882f38
11 changed files with 92 additions and 51 deletions
|
@ -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.
|
||||||
|
|
|
@ -1450,9 +1450,11 @@ 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 `()`");
|
||||||
|
if expr.can_have_side_effects() {
|
||||||
fcx.suggest_semicolon_at_end(cond_expr.span, &mut err);
|
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))
|
||||||
} else {
|
} else {
|
||||||
fcx.get_fn_decl(parent_id)
|
fcx.get_fn_decl(parent_id)
|
||||||
|
|
|
@ -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| {
|
||||||
|
if expr.can_have_side_effects() {
|
||||||
self.suggest_semicolon_at_end(expr.span, err);
|
self.suggest_semicolon_at_end(expr.span, err);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
hir::StmtKind::Semi(ref expr) => {
|
hir::StmtKind::Semi(ref expr) => {
|
||||||
|
|
|
@ -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();
|
||||||
|
if expr.can_have_side_effects() {
|
||||||
self.suggest_missing_semicolon(err, expr, expected, cause_span);
|
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",
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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 `()`
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 _ = ();
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue