Skip to content

Commit d509b5a

Browse files
committed
Auto merge of #4274 - daxpedda:implicit_return_fixes, r=flip1995
false positives fixes of `implicit_return` - Handle returning macro statements properly (remove "this error originates in a macro outside of the current crate") - Handle functions that return never type - Handle functions that panic but do not return never type changelog: Fix false positives in `implicit_return` lint pertaining to macros and panics
2 parents e9ff319 + 9e5e0f8 commit d509b5a

File tree

3 files changed

+118
-74
lines changed

3 files changed

+118
-74
lines changed

clippy_lints/src/implicit_return.rs

Lines changed: 91 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1-
use crate::utils::{in_macro_or_desugar, is_expn_of, snippet_opt, span_lint_and_then};
2-
use rustc::hir::{intravisit::FnKind, Body, ExprKind, FnDecl, HirId, MatchSource};
3-
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
4-
use rustc::{declare_lint_pass, declare_tool_lint};
1+
use crate::utils::{
2+
in_macro_or_desugar, match_def_path,
3+
paths::{BEGIN_PANIC, BEGIN_PANIC_FMT},
4+
resolve_node, snippet_opt, span_lint_and_then,
5+
};
6+
use if_chain::if_chain;
7+
use rustc::{
8+
declare_lint_pass, declare_tool_lint,
9+
hir::{intravisit::FnKind, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind},
10+
lint::{LateContext, LateLintPass, LintArray, LintPass},
11+
};
512
use rustc_errors::Applicability;
613
use syntax::source_map::Span;
714

@@ -35,71 +42,92 @@ declare_clippy_lint! {
3542

3643
declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]);
3744

38-
impl ImplicitReturn {
39-
fn lint(cx: &LateContext<'_, '_>, outer_span: syntax_pos::Span, inner_span: syntax_pos::Span, msg: &str) {
40-
span_lint_and_then(cx, IMPLICIT_RETURN, outer_span, "missing return statement", |db| {
41-
if let Some(snippet) = snippet_opt(cx, inner_span) {
42-
db.span_suggestion(
43-
outer_span,
44-
msg,
45-
format!("return {}", snippet),
46-
Applicability::MachineApplicable,
47-
);
48-
}
49-
});
45+
static LINT_BREAK: &str = "change `break` to `return` as shown";
46+
static LINT_RETURN: &str = "add `return` as shown";
47+
48+
fn lint(cx: &LateContext<'_, '_>, outer_span: Span, inner_span: Span, msg: &str) {
49+
let outer_span = span_to_outer_expn(outer_span);
50+
let inner_span = span_to_outer_expn(inner_span);
51+
52+
span_lint_and_then(cx, IMPLICIT_RETURN, outer_span, "missing return statement", |db| {
53+
if let Some(snippet) = snippet_opt(cx, inner_span) {
54+
db.span_suggestion(
55+
outer_span,
56+
msg,
57+
format!("return {}", snippet),
58+
Applicability::MachineApplicable,
59+
);
60+
}
61+
});
62+
}
63+
64+
fn span_to_outer_expn(span: Span) -> Span {
65+
if let Some(expr) = span.ctxt().outer_expn_info() {
66+
span_to_outer_expn(expr.call_site)
67+
} else {
68+
span
5069
}
70+
}
5171

52-
fn expr_match(cx: &LateContext<'_, '_>, expr: &rustc::hir::Expr) {
53-
match &expr.node {
54-
// loops could be using `break` instead of `return`
55-
ExprKind::Block(block, ..) | ExprKind::Loop(block, ..) => {
56-
if let Some(expr) = &block.expr {
57-
Self::expr_match(cx, expr);
58-
}
59-
// only needed in the case of `break` with `;` at the end
60-
else if let Some(stmt) = block.stmts.last() {
61-
if let rustc::hir::StmtKind::Semi(expr, ..) = &stmt.node {
62-
// make sure it's a break, otherwise we want to skip
63-
if let ExprKind::Break(.., break_expr) = &expr.node {
64-
if let Some(break_expr) = break_expr {
65-
Self::lint(cx, expr.span, break_expr.span, "change `break` to `return` as shown");
66-
}
67-
}
72+
fn expr_match(cx: &LateContext<'_, '_>, expr: &Expr) {
73+
match &expr.node {
74+
// loops could be using `break` instead of `return`
75+
ExprKind::Block(block, ..) | ExprKind::Loop(block, ..) => {
76+
if let Some(expr) = &block.expr {
77+
expr_match(cx, expr);
78+
}
79+
// only needed in the case of `break` with `;` at the end
80+
else if let Some(stmt) = block.stmts.last() {
81+
if_chain! {
82+
if let StmtKind::Semi(expr, ..) = &stmt.node;
83+
// make sure it's a break, otherwise we want to skip
84+
if let ExprKind::Break(.., break_expr) = &expr.node;
85+
if let Some(break_expr) = break_expr;
86+
then {
87+
lint(cx, expr.span, break_expr.span, LINT_BREAK);
6888
}
6989
}
70-
},
71-
// use `return` instead of `break`
72-
ExprKind::Break(.., break_expr) => {
73-
if let Some(break_expr) = break_expr {
74-
Self::lint(cx, expr.span, break_expr.span, "change `break` to `return` as shown");
75-
}
76-
},
77-
ExprKind::Match(.., arms, source) => {
78-
let check_all_arms = match source {
79-
MatchSource::IfLetDesugar {
80-
contains_else_clause: has_else,
81-
} => *has_else,
82-
_ => true,
83-
};
90+
}
91+
},
92+
// use `return` instead of `break`
93+
ExprKind::Break(.., break_expr) => {
94+
if let Some(break_expr) = break_expr {
95+
lint(cx, expr.span, break_expr.span, LINT_BREAK);
96+
}
97+
},
98+
ExprKind::Match(.., arms, source) => {
99+
let check_all_arms = match source {
100+
MatchSource::IfLetDesugar {
101+
contains_else_clause: has_else,
102+
} => *has_else,
103+
_ => true,
104+
};
84105

85-
if check_all_arms {
86-
for arm in arms {
87-
Self::expr_match(cx, &arm.body);
88-
}
89-
} else {
90-
Self::expr_match(cx, &arms.first().expect("if let doesn't have a single arm").body);
106+
if check_all_arms {
107+
for arm in arms {
108+
expr_match(cx, &arm.body);
91109
}
92-
},
93-
// skip if it already has a return statement
94-
ExprKind::Ret(..) => (),
95-
// everything else is missing `return`
96-
_ => {
97-
// make sure it's not just an unreachable expression
98-
if is_expn_of(expr.span, "unreachable").is_none() {
99-
Self::lint(cx, expr.span, expr.span, "add `return` as shown")
110+
} else {
111+
expr_match(cx, &arms.first().expect("if let doesn't have a single arm").body);
112+
}
113+
},
114+
// skip if it already has a return statement
115+
ExprKind::Ret(..) => (),
116+
// make sure it's not a call that panics
117+
ExprKind::Call(expr, ..) => {
118+
if_chain! {
119+
if let ExprKind::Path(qpath) = &expr.node;
120+
if let Some(path_def_id) = resolve_node(cx, qpath, expr.hir_id).opt_def_id();
121+
if match_def_path(cx, path_def_id, &BEGIN_PANIC) ||
122+
match_def_path(cx, path_def_id, &BEGIN_PANIC_FMT);
123+
then { }
124+
else {
125+
lint(cx, expr.span, expr.span, LINT_RETURN)
100126
}
101-
},
102-
}
127+
}
128+
},
129+
// everything else is missing `return`
130+
_ => lint(cx, expr.span, expr.span, LINT_RETURN),
103131
}
104132
}
105133

@@ -119,7 +147,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitReturn {
119147
// checking return type through MIR, HIR is not able to determine inferred closure return types
120148
// make sure it's not a macro
121149
if !mir.return_ty().is_unit() && !in_macro_or_desugar(span) {
122-
Self::expr_match(cx, &body.value);
150+
expr_match(cx, &body.value);
123151
}
124152
}
125153
}

tests/ui/implicit_return.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ fn test_end_of_fn() -> bool {
55
// no error!
66
return true;
77
}
8+
89
true
910
}
1011

@@ -76,6 +77,14 @@ fn test_closure() {
7677
let _ = || true;
7778
}
7879

80+
fn test_panic() -> bool {
81+
panic!()
82+
}
83+
84+
fn test_return_macro() -> String {
85+
format!("test {}", "test")
86+
}
87+
7988
fn main() {
8089
let _ = test_end_of_fn();
8190
let _ = test_if_block();
@@ -86,4 +95,5 @@ fn main() {
8695
let _ = test_loop_with_nests();
8796
let _ = test_loop_with_if_let();
8897
test_closure();
98+
let _ = test_return_macro();
8999
}

tests/ui/implicit_return.stderr

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,70 @@
11
error: missing return statement
2-
--> $DIR/implicit_return.rs:8:5
2+
--> $DIR/implicit_return.rs:9:5
33
|
44
LL | true
55
| ^^^^ help: add `return` as shown: `return true`
66
|
77
= note: `-D clippy::implicit-return` implied by `-D warnings`
88

99
error: missing return statement
10-
--> $DIR/implicit_return.rs:14:9
10+
--> $DIR/implicit_return.rs:15:9
1111
|
1212
LL | true
1313
| ^^^^ help: add `return` as shown: `return true`
1414

1515
error: missing return statement
16-
--> $DIR/implicit_return.rs:16:9
16+
--> $DIR/implicit_return.rs:17:9
1717
|
1818
LL | false
1919
| ^^^^^ help: add `return` as shown: `return false`
2020

2121
error: missing return statement
22-
--> $DIR/implicit_return.rs:24:17
22+
--> $DIR/implicit_return.rs:25:17
2323
|
2424
LL | true => false,
2525
| ^^^^^ help: add `return` as shown: `return false`
2626

2727
error: missing return statement
28-
--> $DIR/implicit_return.rs:25:20
28+
--> $DIR/implicit_return.rs:26:20
2929
|
3030
LL | false => { true },
3131
| ^^^^ help: add `return` as shown: `return true`
3232

3333
error: missing return statement
34-
--> $DIR/implicit_return.rs:40:9
34+
--> $DIR/implicit_return.rs:41:9
3535
|
3636
LL | break true;
3737
| ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
3838

3939
error: missing return statement
40-
--> $DIR/implicit_return.rs:48:13
40+
--> $DIR/implicit_return.rs:49:13
4141
|
4242
LL | break true;
4343
| ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
4444

4545
error: missing return statement
46-
--> $DIR/implicit_return.rs:57:13
46+
--> $DIR/implicit_return.rs:58:13
4747
|
4848
LL | break true;
4949
| ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
5050

5151
error: missing return statement
52-
--> $DIR/implicit_return.rs:75:18
52+
--> $DIR/implicit_return.rs:76:18
5353
|
5454
LL | let _ = || { true };
5555
| ^^^^ help: add `return` as shown: `return true`
5656

5757
error: missing return statement
58-
--> $DIR/implicit_return.rs:76:16
58+
--> $DIR/implicit_return.rs:77:16
5959
|
6060
LL | let _ = || true;
6161
| ^^^^ help: add `return` as shown: `return true`
6262

63-
error: aborting due to 10 previous errors
63+
error: missing return statement
64+
--> $DIR/implicit_return.rs:85:5
65+
|
66+
LL | format!("test {}", "test")
67+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `return` as shown: `return format!("test {}", "test")`
68+
69+
error: aborting due to 11 previous errors
6470

0 commit comments

Comments
 (0)