]> git.lizzy.rs Git - rust.git/blob - src/returns.rs
d6a4b33b6d1f7ad58bd13df146d7b460e64d2d78
[rust.git] / src / returns.rs
1 use syntax::ast;
2 use syntax::ast::*;
3 use syntax::codemap::Span;
4 use syntax::visit::FnKind;
5 use rustc::lint::{Context, LintPass, LintArray};
6
7 use utils::{span_lint, snippet};
8
9 declare_lint!(pub NEEDLESS_RETURN, Warn,
10               "Warn on using a return statement where an expression would be enough");
11
12 #[derive(Copy,Clone)]
13 pub struct ReturnPass;
14
15 impl ReturnPass {
16     // Check the final stmt or expr in a block for unnecessary return.
17     fn check_block_return(&mut self, cx: &Context, block: &Block) {
18         if let Some(ref expr) = block.expr {
19             self.check_final_expr(cx, expr);
20         } else if let Some(stmt) = block.stmts.last() {
21             if let StmtSemi(ref expr, _) = stmt.node {
22                 if let ExprRet(Some(ref inner)) = expr.node {
23                     self.emit_lint(cx, (expr.span, inner.span));
24                 }
25             }
26         }
27     }
28
29     // Check a the final expression in a block if it's a return.
30     fn check_final_expr(&mut self, cx: &Context, expr: &Expr) {
31         match expr.node {
32             // simple return is always "bad"
33             ExprRet(Some(ref inner)) => {
34                 self.emit_lint(cx, (expr.span, inner.span));
35             }
36             // a whole block? check it!
37             ExprBlock(ref block) => {
38                 self.check_block_return(cx, block);
39             }
40             // an if/if let expr, check both exprs
41             // note, if without else is going to be a type checking error anyways
42             // (except for unit type functions) so we don't match it
43             ExprIf(_, ref ifblock, Some(ref elsexpr)) |
44             ExprIfLet(_, _, ref ifblock, Some(ref elsexpr)) => {
45                 self.check_block_return(cx, ifblock);
46                 self.check_final_expr(cx, elsexpr);
47             }
48             // a match expr, check all arms
49             ExprMatch(_, ref arms, _) => {
50                 for arm in arms {
51                     self.check_final_expr(cx, &*arm.body);
52                 }
53             }
54             _ => { }
55         }
56     }
57
58     fn emit_lint(&mut self, cx: &Context, spans: (Span, Span)) {
59         span_lint(cx, NEEDLESS_RETURN, spans.0, &format!(
60             "unneeded return statement. Consider using {} \
61              without the trailing semicolon",
62             snippet(cx, spans.1, "..")))
63     }
64 }
65
66 impl LintPass for ReturnPass {
67     fn get_lints(&self) -> LintArray {
68         lint_array!(NEEDLESS_RETURN)
69     }
70
71     fn check_fn(&mut self, cx: &Context, _: FnKind, _: &FnDecl,
72                 block: &Block, _: Span, _: ast::NodeId) {
73         self.check_block_return(cx, block);
74     }
75 }