1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use rustc::lint::*;
use syntax::ast;
use syntax::codemap::{Span, Spanned};
use syntax::visit::FnKind;
use utils::{in_external_macro, in_macro, match_path_ast, snippet_opt, span_lint_and_then, span_note_and_lint};
declare_clippy_lint! {
pub NEEDLESS_RETURN,
style,
"using a return statement like `return expr;` where an expression would suffice"
}
declare_clippy_lint! {
pub LET_AND_RETURN,
style,
"creating a let-binding and then immediately returning it like `let x = expr; x` at \
the end of a block"
}
#[derive(Copy, Clone)]
pub struct ReturnPass;
impl ReturnPass {
fn check_block_return(&mut self, cx: &EarlyContext, block: &ast::Block) {
if let Some(stmt) = block.stmts.last() {
match stmt.node {
ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => {
self.check_final_expr(cx, expr, Some(stmt.span));
},
_ => (),
}
}
}
fn check_final_expr(&mut self, cx: &EarlyContext, expr: &ast::Expr, span: Option<Span>) {
match expr.node {
ast::ExprKind::Ret(Some(ref inner)) => {
if !expr.attrs.iter().any(attr_is_cfg) {
self.emit_return_lint(cx, span.expect("`else return` is not possible"), inner.span);
}
},
ast::ExprKind::Block(ref block) => {
self.check_block_return(cx, block);
},
ast::ExprKind::If(_, ref ifblock, Some(ref elsexpr)) => {
self.check_block_return(cx, ifblock);
self.check_final_expr(cx, elsexpr, None);
},
ast::ExprKind::Match(_, ref arms) => for arm in arms {
self.check_final_expr(cx, &arm.body, Some(arm.body.span));
},
_ => (),
}
}
fn emit_return_lint(&mut self, cx: &EarlyContext, ret_span: Span, inner_span: Span) {
if in_external_macro(cx, inner_span) || in_macro(inner_span) {
return;
}
span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded return statement", |db| {
if let Some(snippet) = snippet_opt(cx, inner_span) {
db.span_suggestion(ret_span, "remove `return` as shown", snippet);
}
});
}
fn check_let_return(&mut self, cx: &EarlyContext, block: &ast::Block) {
let mut it = block.stmts.iter();
if_chain! {
if let Some(retexpr) = it.next_back();
if let ast::StmtKind::Expr(ref retexpr) = retexpr.node;
if let Some(stmt) = it.next_back();
if let ast::StmtKind::Local(ref local) = stmt.node;
if local.ty.is_none();
if !local.attrs.iter().any(attr_is_cfg);
if let Some(ref initexpr) = local.init;
if let ast::PatKind::Ident(_, Spanned { node: id, .. }, _) = local.pat.node;
if let ast::ExprKind::Path(_, ref path) = retexpr.node;
if match_path_ast(path, &[&id.name.as_str()]);
if !in_external_macro(cx, initexpr.span);
then {
span_note_and_lint(cx,
LET_AND_RETURN,
retexpr.span,
"returning the result of a let binding from a block. \
Consider returning the expression directly.",
initexpr.span,
"this expression can be directly returned");
}
}
}
}
impl LintPass for ReturnPass {
fn get_lints(&self) -> LintArray {
lint_array!(NEEDLESS_RETURN, LET_AND_RETURN)
}
}
impl EarlyLintPass for ReturnPass {
fn check_fn(&mut self, cx: &EarlyContext, kind: FnKind, _: &ast::FnDecl, _: Span, _: ast::NodeId) {
match kind {
FnKind::ItemFn(.., block) | FnKind::Method(.., block) => self.check_block_return(cx, block),
FnKind::Closure(body) => self.check_final_expr(cx, body, Some(body.span)),
}
}
fn check_block(&mut self, cx: &EarlyContext, block: &ast::Block) {
self.check_let_return(cx, block);
}
}
fn attr_is_cfg(attr: &ast::Attribute) -> bool {
attr.meta_item_list().is_some() && attr.name().map_or(false, |n| n == "cfg")
}