use syntax::ast;
use syntax::ast::*;
-use syntax::codemap::Span;
+use syntax::codemap::{Span, Spanned};
use syntax::visit::FnKind;
-use rustc::lint::{Context, LintPass, LintArray};
+use rustc::lint::{Context, LintPass, LintArray, Level};
-use utils::{span_lint, snippet};
+use utils::{span_lint, snippet, match_path};
declare_lint!(pub NEEDLESS_RETURN, Warn,
"Warn on using a return statement where an expression would be enough");
+declare_lint!(pub LET_AND_RETURN, Warn,
+ "Warn on creating a let-binding and then immediately returning it");
#[derive(Copy,Clone)]
pub struct ReturnPass;
} else if let Some(stmt) = block.stmts.last() {
if let StmtSemi(ref expr, _) = stmt.node {
if let ExprRet(Some(ref inner)) = expr.node {
- self.emit_lint(cx, (expr.span, inner.span));
+ self.emit_return_lint(cx, (expr.span, inner.span));
}
}
}
match expr.node {
// simple return is always "bad"
ExprRet(Some(ref inner)) => {
- self.emit_lint(cx, (expr.span, inner.span));
+ self.emit_return_lint(cx, (expr.span, inner.span));
}
// a whole block? check it!
ExprBlock(ref block) => {
}
}
- fn emit_lint(&mut self, cx: &Context, spans: (Span, Span)) {
+ fn emit_return_lint(&mut self, cx: &Context, spans: (Span, Span)) {
span_lint(cx, NEEDLESS_RETURN, spans.0, &format!(
"unneeded return statement. Consider using {} \
without the trailing semicolon",
snippet(cx, spans.1, "..")))
}
+
+ // Check for "let x = EXPR; x"
+ fn check_let_return(&mut self, cx: &Context, block: &Block) {
+ // we need both a let-binding stmt and an expr
+ if let Some(stmt) = block.stmts.last() {
+ if let StmtDecl(ref decl, _) = stmt.node {
+ if let DeclLocal(ref local) = decl.node {
+ if let Some(ref initexpr) = local.init {
+ if let PatIdent(_, Spanned { node: id, .. }, _) = local.pat.node {
+ if let Some(ref retexpr) = block.expr {
+ if let ExprPath(_, ref path) = retexpr.node {
+ if match_path(path, &[&*id.name.as_str()]) {
+ self.emit_let_lint(cx, retexpr.span, initexpr.span);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ fn emit_let_lint(&mut self, cx: &Context, lint_span: Span, note_span: Span) {
+ span_lint(cx, LET_AND_RETURN, lint_span,
+ "returning the result of a let binding. \
+ Consider returning the expression directly.");
+ if cx.current_level(LET_AND_RETURN) != Level::Allow {
+ cx.sess().span_note(note_span,
+ "this expression can be directly returned");
+ }
+ }
}
impl LintPass for ReturnPass {
fn get_lints(&self) -> LintArray {
- lint_array!(NEEDLESS_RETURN)
+ lint_array!(NEEDLESS_RETURN, LET_AND_RETURN)
}
fn check_fn(&mut self, cx: &Context, _: FnKind, _: &FnDecl,
block: &Block, _: Span, _: ast::NodeId) {
self.check_block_return(cx, block);
+ self.check_let_return(cx, block);
}
}