2015-08-16 06:54:43 +00:00
use rustc ::lint ::* ;
2015-08-26 15:09:37 +00:00
use rustc ::middle ::ty ;
2016-02-24 16:38:57 +00:00
use rustc_front ::hir ::* ;
2015-12-08 16:28:35 +00:00
use utils ::{ snippet_opt , span_lint_and_then , is_adjusted } ;
2015-07-26 14:53:11 +00:00
2015-05-10 05:09:04 +00:00
#[ allow(missing_copy_implementations) ]
pub struct EtaPass ;
2016-02-05 23:41:54 +00: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.
2015-12-11 00:22:27 +00:00
///
/// **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`.
2016-02-05 23:13:29 +00:00
declare_lint! {
pub REDUNDANT_CLOSURE , Warn ,
" using redundant closures, i.e. `|a| foo(a)` (which can be written as just `foo`) "
}
2015-05-10 05:09:04 +00:00
impl LintPass for EtaPass {
fn get_lints ( & self ) -> LintArray {
lint_array! ( REDUNDANT_CLOSURE )
}
2015-09-19 02:53:04 +00:00
}
2015-05-10 05:09:04 +00:00
2015-09-19 02:53:04 +00:00
impl LateLintPass for EtaPass {
fn check_expr ( & mut self , cx : & LateContext , expr : & Expr ) {
2015-08-21 18:44:48 +00:00
match expr . node {
ExprCall ( _ , ref args ) |
ExprMethodCall ( _ , _ , ref args ) = > {
2015-08-14 12:21:05 +00:00
for arg in args {
2015-08-25 12:41:35 +00:00
check_closure ( cx , arg )
2015-08-14 12:21:05 +00:00
}
2015-11-17 04:39:42 +00:00
}
2015-08-14 12:21:05 +00:00
_ = > ( ) ,
}
}
}
2015-09-19 02:53:04 +00:00
fn check_closure ( cx : & LateContext , expr : & Expr ) {
2015-08-14 12:21:05 +00:00
if let ExprClosure ( _ , ref decl , ref blk ) = expr . node {
if ! blk . stmts . is_empty ( ) {
// || {foo(); bar()}; can't be reduced here
return ;
}
2016-01-18 18:28:06 +00:00
2015-08-14 12:21:05 +00:00
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 ;
}
2016-01-18 18:28:06 +00:00
if is_adjusted ( cx , ex ) | | args . iter ( ) . any ( | arg | is_adjusted ( cx , arg ) ) {
// Are the expression or the arguments type-adjusted? Then we need the closure
2015-08-26 15:09:37 +00:00
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 12:21:05 +00:00
for ( ref a1 , ref a2 ) in decl . inputs . iter ( ) . zip ( args ) {
2016-02-18 20:16:39 +00:00
if let PatKind ::Ident ( _ , ident , _ ) = a1 . pat . node {
2015-08-14 12:21:05 +00:00
// 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-04 04:26:12 +00:00
return ;
2015-05-10 05:09:04 +00:00
}
} else {
2016-01-04 04:26:12 +00:00
return ;
2015-05-10 05:09:04 +00:00
}
2015-08-14 12:21:05 +00:00
} else {
2016-01-04 04:26:12 +00:00
return ;
2015-05-10 05:09:04 +00:00
}
}
2016-01-04 04:26:12 +00:00
span_lint_and_then ( cx , REDUNDANT_CLOSURE , expr . span , " redundant closure found " , | db | {
2015-12-08 16:28:35 +00:00
if let Some ( snippet ) = snippet_opt ( cx , caller . span ) {
2016-01-04 04:26:12 +00:00
db . span_suggestion ( expr . span , " remove closure as shown: " , snippet ) ;
2015-12-08 16:28:35 +00:00
}
} ) ;
2015-05-10 05:09:04 +00:00
}
}
}
}