Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggest returning tail expressions that match return type #81769

Merged
merged 5 commits into from
Feb 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 58 additions & 1 deletion compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// 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};
Expand Down Expand Up @@ -1554,6 +1554,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.
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_typeck/src/check/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if call_is_multiline {
err.span_suggestion(
callee.span.shrink_to_hi(),
"try adding a semicolon",
"consider using a semicolon here",
";".to_owned(),
Applicability::MaybeIncorrect,
);
Expand Down
16 changes: 14 additions & 2 deletions compiler/rustc_typeck/src/check/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1450,15 +1450,17 @@ 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))
} else {
fcx.get_fn_decl(parent_id)
};

if let (Some((fn_decl, can_suggest)), _) = (fn_decl, pointing_at_return_type) {
if let Some((fn_decl, can_suggest)) = fn_decl {
if expression.is_none() {
pointing_at_return_type |= fcx.suggest_missing_return_type(
&mut err,
Expand All @@ -1472,6 +1474,16 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
fn_output = Some(&fn_decl.output); // `impl Trait` return type
}
}

let parent_id = fcx.tcx.hir().get_parent_item(id);
let parent_item = fcx.tcx.hir().get(parent_id);

if let (Some((expr, _)), Some((fn_decl, _, _))) =
(expression, fcx.get_node_fn_decl(parent_item))
{
fcx.suggest_missing_return_expr(&mut err, expr, fn_decl, expected, found);
}

if let (Some(sp), Some(fn_output)) = (fcx.ret_coercion_span.get(), fn_output) {
self.add_impl_trait_explanation(&mut err, cause, fcx, expected, sp, fn_output);
}
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
39 changes: 36 additions & 3 deletions compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -392,10 +395,12 @@ 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(),
"try adding a semicolon",
"consider using a semicolon here",
";".to_string(),
Applicability::MachineApplicable,
);
Expand Down Expand Up @@ -464,6 +469,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

pub(in super::super) fn suggest_missing_return_expr(
&self,
err: &mut DiagnosticBuilder<'_>,
expr: &'tcx hir::Expr<'tcx>,
fn_decl: &hir::FnDecl<'_>,
expected: Ty<'tcx>,
found: Ty<'tcx>,
) {
if !expected.is_unit() {
return;
}
let found = self.resolve_vars_with_obligations(found);
if let hir::FnRetTy::Return(ty) = fn_decl.output {
let ty = AstConv::ast_ty_to_ty(self, ty);
let ty = self.normalize_associated_types_in(expr.span, ty);
if self.can_coerce(found, ty) {
err.multipart_suggestion(
"you might have meant to return this value",
vec![
(expr.span.shrink_to_lo(), "return ".to_string()),
(expr.span.shrink_to_hi(), ";".to_string()),
],
Applicability::MaybeIncorrect,
);
}
}
}

pub(in super::super) fn suggest_missing_parentheses(
&self,
err: &mut DiagnosticBuilder<'_>,
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/async-await/suggest-missing-await.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ async fn dummy() {}
async fn suggest_await_in_async_fn_return() {
dummy()
//~^ ERROR mismatched types [E0308]
//~| HELP try adding a semicolon
//~| HELP consider using a semicolon here
//~| HELP consider `await`ing on the `Future`
//~| SUGGESTION .await
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/async-await/suggest-missing-await.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ help: consider `await`ing on the `Future`
|
LL | dummy().await
| ^^^^^^
help: try adding a semicolon
help: consider using a semicolon here
|
LL | dummy();
| ^
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/block-result/unexpected-return-on-unit.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ error[E0308]: mismatched types
LL | foo()
| ^^^^^ expected `()`, found `usize`
|
help: try adding a semicolon
help: consider using a semicolon here
|
LL | foo();
| ^
Expand Down
5 changes: 5 additions & 0 deletions src/test/ui/macros/empty-trailing-stmt.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ error[E0308]: mismatched types
|
LL | { true }
| ^^^^ expected `()`, found `bool`
|
help: you might have meant to return this value
|
LL | { return true; }
| ^^^^^^ ^

error[E0308]: mismatched types
--> $DIR/empty-trailing-stmt.rs:5:13
Expand Down
14 changes: 12 additions & 2 deletions src/test/ui/parser/expr-as-stmt-2.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,29 @@ error[E0308]: mismatched types
--> $DIR/expr-as-stmt-2.rs:3:26
|
LL | if let Some(x) = a { true } else { false }
| ---------------------^^^^------------------ help: consider using a semicolon here
| ---------------------^^^^-----------------
| | |
| | expected `()`, found `bool`
| expected this to be `()`
|
help: you might have meant to return this value
|
LL | if let Some(x) = a { return true; } else { false }
| ^^^^^^ ^

error[E0308]: mismatched types
--> $DIR/expr-as-stmt-2.rs:3:40
|
LL | if let Some(x) = a { true } else { false }
| -----------------------------------^^^^^--- help: consider using a semicolon here
| -----------------------------------^^^^^--
| | |
| | expected `()`, found `bool`
| expected this to be `()`
|
help: you might have meant to return this value
|
LL | if let Some(x) = a { true } else { return false; }
| ^^^^^^ ^

error[E0308]: mismatched types
--> $DIR/expr-as-stmt-2.rs:6:5
Expand Down
20 changes: 20 additions & 0 deletions src/test/ui/parser/expr-as-stmt.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,44 @@ error[E0308]: mismatched types
|
LL | {2} + {2}
| ^ expected `()`, found integer
|
help: you might have meant to return this value
|
LL | {return 2;} + {2}
| ^^^^^^ ^

error[E0308]: mismatched types
--> $DIR/expr-as-stmt.rs:12:6
|
LL | {2} + 2
| ^ expected `()`, found integer
|
help: you might have meant to return this value
|
LL | {return 2;} + 2
| ^^^^^^ ^

error[E0308]: mismatched types
--> $DIR/expr-as-stmt.rs:18:7
|
LL | { 42 } + foo;
| ^^ expected `()`, found integer
|
help: you might have meant to return this value
|
LL | { return 42; } + foo;
| ^^^^^^ ^

error[E0308]: mismatched types
--> $DIR/expr-as-stmt.rs:24:7
|
LL | { 3 } * 3
| ^ expected `()`, found integer
|
help: you might have meant to return this value
|
LL | { return 3; } * 3
| ^^^^^^ ^

error[E0614]: type `{integer}` cannot be dereferenced
--> $DIR/expr-as-stmt.rs:24:11
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/parser/struct-literal-variant-in-if.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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 `()`
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/proc-macro/issue-37788.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ LL | fn main() {
| - expected `()` because of default return type
LL | // Test that constructing the `visible_parent_map` (in `cstore_impl.rs`) does not ICE.
LL | std::cell::Cell::new(0)
| ^^^^^^^^^^^^^^^^^^^^^^^- help: try adding a semicolon: `;`
| ^^^^^^^^^^^^^^^^^^^^^^^- help: consider using a semicolon here: `;`
| |
| expected `()`, found struct `Cell`
|
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/return/return-type.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ LL | foo(4 as usize)
|
= note: expected unit type `()`
found struct `S<usize>`
help: try adding a semicolon
help: consider using a semicolon here
|
LL | foo(4 as usize);
| ^
Expand Down
10 changes: 10 additions & 0 deletions src/test/ui/return/tail-expr-as-potential-return.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
fn main() {
let _ = foo(true);
}

fn foo(x: bool) -> Result<f64, i32> {
if x {
Err(42) //~ ERROR mismatched types
}
Ok(42.0)
}
19 changes: 19 additions & 0 deletions src/test/ui/return/tail-expr-as-potential-return.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
error[E0308]: mismatched types
--> $DIR/tail-expr-as-potential-return.rs:7:9
|
LL | / if x {
LL | | Err(42)
| | ^^^^^^^ expected `()`, found enum `Result`
LL | | }
| |_____- expected this to be `()`
|
= note: expected unit type `()`
found enum `Result<_, {integer}>`
help: you might have meant to return this value
|
LL | return Err(42);
| ^^^^^^ ^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ LL | fn monomorphic() -> () {
| -- expected `()` because of return type
...
LL | generic::<()>()
| ^^^^^^^^^^^^^^^- help: try adding a semicolon: `;`
| ^^^^^^^^^^^^^^^- help: consider using a semicolon here: `;`
| |
| expected `()`, found associated type
|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ LL | fn vindictive() -> bool { true }
| ----------------------- `vindictive` defined here returns `bool`
...
LL | vindictive()
| -^^^^^^^^^^^- help: try adding a semicolon: `;`
| -^^^^^^^^^^^- help: consider using a semicolon here: `;`
| _____|
| |
LL | | (1, 2)
Expand Down
Loading