]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/returns.rs
Make lint descriptions short and to the point; always fitting the column "triggers...
[rust.git] / clippy_lints / src / returns.rs
1 use rustc::lint::*;
2 use syntax::ast::*;
3 use syntax::codemap::{Span, Spanned};
4 use syntax::visit::FnKind;
5
6 use utils::{span_note_and_lint, span_lint_and_then, snippet_opt, match_path_ast, in_external_macro};
7
8 /// **What it does:** Checks for return statements at the end of a block.
9 ///
10 /// **Why is this bad?** Removing the `return` and semicolon will make the code
11 /// more rusty.
12 ///
13 /// **Known problems:** Following this lint's advice may currently run afoul of
14 /// Rust issue [#31439](https://github.com/rust-lang/rust/issues/31439), so if
15 /// you get lifetime errors, please roll back the change until that issue is
16 /// fixed.
17 ///
18 /// **Example:**
19 /// ```rust
20 /// fn foo(x: usize) { return x; }
21 /// ```
22 declare_lint! {
23     pub NEEDLESS_RETURN,
24     Warn,
25     "using a return statement like `return expr;` where an expression would suffice"
26 }
27
28 /// **What it does:** Checks for `let`-bindings, which are subsequently returned.
29 ///
30 /// **Why is this bad?** It is just extraneous code. Remove it to make your code
31 /// more rusty.
32 ///
33 /// **Known problems:** Following this lint's advice may currently run afoul of
34 /// Rust issue [#31439](https://github.com/rust-lang/rust/issues/31439), so if
35 /// you get lifetime errors, please roll back the change until that issue is
36 /// fixed.
37 ///
38 /// **Example:**
39 /// ```rust
40 /// { let x = ..; x }
41 /// ```
42 declare_lint! {
43     pub LET_AND_RETURN,
44     Warn,
45     "creating a let-binding and then immediately returning it like `let x = expr; x` at \
46      the end of a block"
47 }
48
49 #[derive(Copy, Clone)]
50 pub struct ReturnPass;
51
52 impl ReturnPass {
53     // Check the final stmt or expr in a block for unnecessary return.
54     fn check_block_return(&mut self, cx: &EarlyContext, block: &Block) {
55         if let Some(stmt) = block.stmts.last() {
56             match stmt.node {
57                 StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => {
58                     self.check_final_expr(cx, expr, Some(stmt.span));
59                 }
60                 _ => (),
61             }
62         }
63     }
64
65     // Check a the final expression in a block if it's a return.
66     fn check_final_expr(&mut self, cx: &EarlyContext, expr: &Expr, span: Option<Span>) {
67         match expr.node {
68             // simple return is always "bad"
69             ExprKind::Ret(Some(ref inner)) => {
70                 self.emit_return_lint(cx, span.expect("`else return` is not possible"), inner.span);
71             }
72             // a whole block? check it!
73             ExprKind::Block(ref block) => {
74                 self.check_block_return(cx, block);
75             }
76             // an if/if let expr, check both exprs
77             // note, if without else is going to be a type checking error anyways
78             // (except for unit type functions) so we don't match it
79             ExprKind::If(_, ref ifblock, Some(ref elsexpr)) => {
80                 self.check_block_return(cx, ifblock);
81                 self.check_final_expr(cx, elsexpr, None);
82             }
83             // a match expr, check all arms
84             ExprKind::Match(_, ref arms) => {
85                 for arm in arms {
86                     self.check_final_expr(cx, &arm.body, Some(arm.body.span));
87                 }
88             }
89             _ => (),
90         }
91     }
92
93     fn emit_return_lint(&mut self, cx: &EarlyContext, ret_span: Span, inner_span: Span) {
94         if in_external_macro(cx, inner_span) {
95             return;
96         }
97         span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded return statement", |db| {
98             if let Some(snippet) = snippet_opt(cx, inner_span) {
99                 db.span_suggestion(ret_span, "remove `return` as shown:", snippet);
100             }
101         });
102     }
103
104     // Check for "let x = EXPR; x"
105     fn check_let_return(&mut self, cx: &EarlyContext, block: &Block) {
106         let mut it = block.stmts.iter();
107
108         // we need both a let-binding stmt and an expr
109         if_let_chain! {[
110             let Some(ref retexpr) = it.next_back(),
111             let StmtKind::Expr(ref retexpr) = retexpr.node,
112             let Some(stmt) = it.next_back(),
113             let StmtKind::Local(ref local) = stmt.node,
114             let Some(ref initexpr) = local.init,
115             let PatKind::Ident(_, Spanned { node: id, .. }, _) = local.pat.node,
116             let ExprKind::Path(_, ref path) = retexpr.node,
117             match_path_ast(path, &[&id.name.as_str()]),
118             !in_external_macro(cx, initexpr.span),
119         ], {
120                 span_note_and_lint(cx,
121                                    LET_AND_RETURN,
122                                    retexpr.span,
123                                    "returning the result of a let binding from a block. \
124                                    Consider returning the expression directly.",
125                                    initexpr.span,
126                                    "this expression can be directly returned");
127         }}
128     }
129 }
130
131 impl LintPass for ReturnPass {
132     fn get_lints(&self) -> LintArray {
133         lint_array!(NEEDLESS_RETURN, LET_AND_RETURN)
134     }
135 }
136
137 impl EarlyLintPass for ReturnPass {
138     fn check_fn(&mut self, cx: &EarlyContext, _: FnKind, _: &FnDecl, block: &Block, _: Span, _: NodeId) {
139         self.check_block_return(cx, block);
140     }
141
142     fn check_block(&mut self, cx: &EarlyContext, block: &Block) {
143         self.check_let_return(cx, block);
144     }
145 }