3 use syntax::codemap::{Span, Spanned};
4 use syntax::visit::FnKind;
6 use utils::{span_note_and_lint, span_lint_and_then, snippet_opt, match_path_ast, in_external_macro};
8 /// **What it does:** Checks for return statements at the end of a block.
10 /// **Why is this bad?** Removing the `return` and semicolon will make the code
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
20 /// fn foo(x: usize) { return x; }
25 "using a return statement like `return expr;` where an expression would suffice"
28 /// **What it does:** Checks for `let`-bindings, which are subsequently returned.
30 /// **Why is this bad?** It is just extraneous code. Remove it to make your code
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
45 "creating a let-binding and then immediately returning it like `let x = expr; x` at \
49 #[derive(Copy, Clone)]
50 pub struct 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() {
57 StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => {
58 self.check_final_expr(cx, expr, Some(stmt.span));
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>) {
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);
72 // a whole block? check it!
73 ExprKind::Block(ref block) => {
74 self.check_block_return(cx, block);
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);
83 // a match expr, check all arms
84 ExprKind::Match(_, ref arms) => {
86 self.check_final_expr(cx, &arm.body, Some(arm.body.span));
93 fn emit_return_lint(&mut self, cx: &EarlyContext, ret_span: Span, inner_span: Span) {
94 if in_external_macro(cx, inner_span) {
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);
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();
108 // we need both a let-binding stmt and an expr
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),
120 span_note_and_lint(cx,
123 "returning the result of a let binding from a block. \
124 Consider returning the expression directly.",
126 "this expression can be directly returned");
131 impl LintPass for ReturnPass {
132 fn get_lints(&self) -> LintArray {
133 lint_array!(NEEDLESS_RETURN, LET_AND_RETURN)
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);
142 fn check_block(&mut self, cx: &EarlyContext, block: &Block) {
143 self.check_let_return(cx, block);