2015-08-16 01:54:43 -05:00
use rustc ::lint ::* ;
2015-09-03 09:42:17 -05:00
use rustc_front ::hir ::* ;
2015-08-26 10:09:37 -05:00
use rustc ::middle ::ty ;
2015-05-10 00:09:04 -05:00
2015-12-08 10:28:35 -06:00
use utils ::{ snippet_opt , span_lint_and_then , is_adjusted } ;
2015-07-26 09:53:11 -05:00
2015-05-10 00:09:04 -05:00
#[ allow(missing_copy_implementations) ]
pub struct EtaPass ;
2015-12-10 18:22:27 -06:00
/// **What it does:** This lint checks for closures which just call another function where the function can be called directly. `unsafe` functions or calls where types get adjusted are ignored. It is `Warn` by default.
///
/// **Why is this bad?** Needlessly creating a closure just costs heap space and adds code for no benefit.
///
/// **Known problems:** None
///
/// **Example:** `xs.map(|x| foo(x))` where `foo(_)` is a plain function that takes the exact argument type of `x`.
2015-05-10 00:09:04 -05:00
declare_lint! ( pub REDUNDANT_CLOSURE , Warn ,
2015-08-13 03:32:35 -05:00
" using redundant closures, i.e. `|a| foo(a)` (which can be written as just `foo`) " ) ;
2015-05-10 00:09:04 -05:00
impl LintPass for EtaPass {
fn get_lints ( & self ) -> LintArray {
lint_array! ( REDUNDANT_CLOSURE )
}
2015-09-18 21:53:04 -05:00
}
2015-05-10 00:09:04 -05:00
2015-09-18 21:53:04 -05:00
impl LateLintPass for EtaPass {
fn check_expr ( & mut self , cx : & LateContext , expr : & Expr ) {
2015-08-21 13:44:48 -05:00
match expr . node {
ExprCall ( _ , ref args ) |
ExprMethodCall ( _ , _ , ref args ) = > {
2015-08-14 07:21:05 -05:00
for arg in args {
2015-08-25 07:41:35 -05:00
check_closure ( cx , arg )
2015-08-14 07:21:05 -05:00
}
2015-11-16 22:39:42 -06:00
}
2015-08-14 07:21:05 -05:00
_ = > ( ) ,
}
}
}
2015-09-18 21:53:04 -05:00
fn check_closure ( cx : & LateContext , expr : & Expr ) {
2015-08-14 07:21:05 -05:00
if let ExprClosure ( _ , ref decl , ref blk ) = expr . node {
if ! blk . stmts . is_empty ( ) {
// || {foo(); bar()}; can't be reduced here
return ;
}
if let Some ( ref ex ) = blk . expr {
if let ExprCall ( ref caller , ref args ) = ex . node {
if args . len ( ) ! = decl . inputs . len ( ) {
// Not the same number of arguments, there
// is no way the closure is the same as the function
return ;
}
2015-08-26 10:09:37 -05:00
if args . iter ( ) . any ( | arg | is_adjusted ( cx , arg ) ) {
// Are the arguments type-adjusted? Then we need the closure
return ;
}
let fn_ty = cx . tcx . expr_ty ( caller ) ;
if let ty ::TyBareFn ( _ , fn_ty ) = fn_ty . sty {
// Is it an unsafe function? They don't implement the closure traits
if fn_ty . unsafety = = Unsafety ::Unsafe {
return ;
}
}
2015-08-14 07:21:05 -05:00
for ( ref a1 , ref a2 ) in decl . inputs . iter ( ) . zip ( args ) {
if let PatIdent ( _ , ident , _ ) = a1 . pat . node {
// XXXManishearth Should I be checking the binding mode here?
if let ExprPath ( None , ref p ) = a2 . node {
if p . segments . len ( ) ! = 1 {
// If it's a proper path, it can't be a local variable
return ;
}
if p . segments [ 0 ] . identifier ! = ident . node {
// The two idents should be the same
2016-01-03 22:26:12 -06:00
return ;
2015-05-10 00:09:04 -05:00
}
} else {
2016-01-03 22:26:12 -06:00
return ;
2015-05-10 00:09:04 -05:00
}
2015-08-14 07:21:05 -05:00
} else {
2016-01-03 22:26:12 -06:00
return ;
2015-05-10 00:09:04 -05:00
}
}
2016-01-03 22:26:12 -06:00
span_lint_and_then ( cx , REDUNDANT_CLOSURE , expr . span , " redundant closure found " , | db | {
2015-12-08 10:28:35 -06:00
if let Some ( snippet ) = snippet_opt ( cx , caller . span ) {
2016-01-03 22:26:12 -06:00
db . span_suggestion ( expr . span , " remove closure as shown: " , snippet ) ;
2015-12-08 10:28:35 -06:00
}
} ) ;
2015-05-10 00:09:04 -05:00
}
}
}
}