2015-08-16 01:54:43 -05:00
use rustc ::lint ::* ;
2015-10-11 16:12:21 -05:00
use syntax ::ast ::* ;
2015-08-11 15:06:30 -05:00
use syntax ::codemap ::{ Span , Spanned } ;
2015-10-11 16:12:21 -05:00
use syntax ::visit ::FnKind ;
2015-08-11 14:47:34 -05:00
2015-12-11 01:28:05 -06:00
use utils ::{ span_lint , span_lint_and_then , snippet_opt , match_path_ast , in_external_macro } ;
2015-08-11 14:47:34 -05:00
2016-02-05 17:41:54 -06:00
/// **What it does:** This lint checks for return statements at the end of a block.
2015-12-10 18:22:27 -06:00
///
/// **Why is this bad?** Removing the `return` and semicolon will make the code more rusty.
///
2016-04-11 16:59:52 -05:00
/// **Known problems:** Following this lint's advice may currently run afoul of Rust issue [#31439](https://github.com/rust-lang/rust/issues/31439), so if you get lifetime errors, please roll back the change until that issue is fixed.
2015-12-10 18:22:27 -06:00
///
/// **Example:** `fn foo(x: usize) { return x; }`
2016-02-05 17:13:29 -06:00
declare_lint! {
pub NEEDLESS_RETURN , Warn ,
" using a return statement like `return expr;` where an expression would suffice "
}
2016-02-05 17:41:54 -06:00
/// **What it does:** This lint checks for `let`-bindings, which are subsequently returned.
2015-12-10 18:22:27 -06:00
///
/// **Why is this bad?** It is just extraneous code. Remove it to make your code more rusty.
///
2016-04-11 16:59:52 -05:00
/// **Known problems:** Following this lint's advice may currently run afoul of Rust issue [#31439](https://github.com/rust-lang/rust/issues/31439), so if you get lifetime errors, please roll back the change until that issue is fixed.
2015-12-10 18:22:27 -06:00
///
/// **Example:** `{ let x = ..; x }`
2016-02-05 17:13:29 -06:00
declare_lint! {
pub LET_AND_RETURN , Warn ,
" creating a let-binding and then immediately returning it like `let x = expr; x` at \
the end of a block "
}
2015-08-11 14:47:34 -05:00
2015-08-21 10:11:34 -05:00
#[ derive(Copy, Clone) ]
2015-08-11 14:47:34 -05:00
pub struct ReturnPass ;
impl ReturnPass {
// Check the final stmt or expr in a block for unnecessary return.
2015-10-11 16:12:21 -05:00
fn check_block_return ( & mut self , cx : & EarlyContext , block : & Block ) {
2015-08-11 14:47:34 -05:00
if let Some ( ref expr ) = block . expr {
self . check_final_expr ( cx , expr ) ;
} else if let Some ( stmt ) = block . stmts . last ( ) {
2016-02-12 11:35:44 -06:00
if let StmtKind ::Semi ( ref expr , _ ) = stmt . node {
if let ExprKind ::Ret ( Some ( ref inner ) ) = expr . node {
2015-12-11 01:28:05 -06:00
self . emit_return_lint ( cx , ( stmt . span , inner . span ) ) ;
2015-08-11 14:47:34 -05:00
}
}
}
}
// Check a the final expression in a block if it's a return.
2015-10-11 16:12:21 -05:00
fn check_final_expr ( & mut self , cx : & EarlyContext , expr : & Expr ) {
2015-08-11 14:47:34 -05:00
match expr . node {
// simple return is always "bad"
2016-02-12 11:35:44 -06:00
ExprKind ::Ret ( Some ( ref inner ) ) = > {
2015-08-11 15:06:30 -05:00
self . emit_return_lint ( cx , ( expr . span , inner . span ) ) ;
2015-08-11 14:47:34 -05:00
}
// a whole block? check it!
2016-02-12 11:35:44 -06:00
ExprKind ::Block ( ref block ) = > {
2015-08-11 14:47:34 -05:00
self . check_block_return ( cx , block ) ;
}
// an if/if let expr, check both exprs
// note, if without else is going to be a type checking error anyways
// (except for unit type functions) so we don't match it
2016-02-12 11:35:44 -06:00
ExprKind ::If ( _ , ref ifblock , Some ( ref elsexpr ) ) = > {
2015-08-11 14:47:34 -05:00
self . check_block_return ( cx , ifblock ) ;
self . check_final_expr ( cx , elsexpr ) ;
}
// a match expr, check all arms
2016-02-12 11:35:44 -06:00
ExprKind ::Match ( _ , ref arms ) = > {
2015-08-11 14:47:34 -05:00
for arm in arms {
2015-08-25 07:41:35 -05:00
self . check_final_expr ( cx , & arm . body ) ;
2015-08-11 14:47:34 -05:00
}
}
2016-04-14 13:14:03 -05:00
_ = > ( ) ,
2015-08-11 14:47:34 -05:00
}
}
2015-10-11 16:12:21 -05:00
fn emit_return_lint ( & mut self , cx : & EarlyContext , spans : ( Span , Span ) ) {
2016-01-03 22:26:12 -06:00
if in_external_macro ( cx , spans . 1 ) {
return ;
}
span_lint_and_then ( cx , NEEDLESS_RETURN , spans . 0 , " unneeded return statement " , | db | {
2015-12-11 01:28:05 -06:00
if let Some ( snippet ) = snippet_opt ( cx , spans . 1 ) {
2016-01-03 22:26:12 -06:00
db . span_suggestion ( spans . 0 , " remove `return` as shown: " , snippet ) ;
2015-12-11 01:28:05 -06:00
}
} ) ;
2015-08-11 14:47:34 -05:00
}
2015-08-11 15:06:30 -05:00
// Check for "let x = EXPR; x"
2015-10-11 16:12:21 -05:00
fn check_let_return ( & mut self , cx : & EarlyContext , block : & Block ) {
2015-08-11 15:06:30 -05:00
// we need both a let-binding stmt and an expr
2015-08-12 00:48:00 -05:00
if_let_chain! {
[
2015-08-12 14:11:32 -05:00
let Some ( stmt ) = block . stmts . last ( ) ,
2015-09-20 06:57:27 -05:00
let Some ( ref retexpr ) = block . expr ,
2016-02-12 11:35:44 -06:00
let StmtKind ::Decl ( ref decl , _ ) = stmt . node ,
let DeclKind ::Local ( ref local ) = decl . node ,
2015-08-12 14:11:32 -05:00
let Some ( ref initexpr ) = local . init ,
2016-02-17 06:38:44 -06:00
let PatKind ::Ident ( _ , Spanned { node : id , .. } , _ ) = local . pat . node ,
2016-02-12 11:35:44 -06:00
let ExprKind ::Path ( _ , ref path ) = retexpr . node ,
2015-10-11 16:12:21 -05:00
match_path_ast ( path , & [ & id . name . as_str ( ) ] )
2015-08-12 00:48:00 -05:00
] , {
2015-08-12 14:11:32 -05:00
self . emit_let_lint ( cx , retexpr . span , initexpr . span ) ;
2015-08-11 15:06:30 -05:00
}
}
}
2015-10-11 16:12:21 -05:00
fn emit_let_lint ( & mut self , cx : & EarlyContext , lint_span : Span , note_span : Span ) {
2016-01-03 22:26:12 -06:00
if in_external_macro ( cx , note_span ) {
return ;
}
let mut db = span_lint ( cx ,
LET_AND_RETURN ,
lint_span ,
" returning the result of a let binding from a block. Consider returning the \
expression directly . " );
2015-08-11 15:06:30 -05:00
if cx . current_level ( LET_AND_RETURN ) ! = Level ::Allow {
2016-01-03 22:26:12 -06:00
db . span_note ( note_span , " this expression can be directly returned " ) ;
2015-08-11 15:06:30 -05:00
}
}
2015-08-11 14:47:34 -05:00
}
impl LintPass for ReturnPass {
fn get_lints ( & self ) -> LintArray {
2015-08-11 15:06:30 -05:00
lint_array! ( NEEDLESS_RETURN , LET_AND_RETURN )
2015-08-11 14:47:34 -05:00
}
2015-09-18 21:53:04 -05:00
}
2015-08-11 14:47:34 -05:00
2015-10-11 16:12:21 -05:00
impl EarlyLintPass for ReturnPass {
2016-01-03 22:26:12 -06:00
fn check_fn ( & mut self , cx : & EarlyContext , _ : FnKind , _ : & FnDecl , block : & Block , _ : Span , _ : NodeId ) {
2015-08-11 14:47:34 -05:00
self . check_block_return ( cx , block ) ;
2015-09-20 06:57:27 -05:00
}
2015-10-11 16:12:21 -05:00
fn check_block ( & mut self , cx : & EarlyContext , block : & Block ) {
2015-08-11 15:06:30 -05:00
self . check_let_return ( cx , block ) ;
2015-08-11 14:47:34 -05:00
}
}