2015-08-16 01:54:43 -05:00
use rustc ::lint ::* ;
2016-03-27 13:59:02 -05:00
use rustc ::ty ;
2016-04-07 10:46:48 -05:00
use rustc ::hir ::* ;
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 ;
2016-02-05 17:41:54 -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.
2015-12-10 18:22:27 -06: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 17:13:29 -06: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 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 ) {
2016-04-25 15:31:49 -05:00
if let ExprClosure ( _ , ref decl , ref blk , _ ) = expr . node {
2015-08-14 07:21:05 -05:00
if ! blk . stmts . is_empty ( ) {
// || {foo(); bar()}; can't be reduced here
return ;
}
2016-01-18 12:28:06 -06:00
2015-08-14 07:21:05 -05: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 12:28:06 -06: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 10:09:37 -05:00
return ;
}
let fn_ty = cx . tcx . expr_ty ( caller ) ;
2016-03-10 11:13:49 -06:00
match fn_ty . sty {
2015-08-26 10:09:37 -05:00
// Is it an unsafe function? They don't implement the closure traits
2016-04-14 13:14:03 -05:00
ty ::TyFnDef ( _ , _ , fn_ty ) |
ty ::TyFnPtr ( fn_ty ) = > {
2016-03-30 16:07:21 -05:00
if fn_ty . unsafety = = Unsafety ::Unsafe | |
2016-04-14 13:14:03 -05:00
fn_ty . sig . skip_binder ( ) . output = = ty ::FnOutput ::FnDiverging {
2016-03-10 11:13:49 -06:00
return ;
}
2015-08-26 10:09:37 -05:00
}
2016-03-10 11:13:49 -06:00
_ = > ( ) ,
2015-08-26 10:09:37 -05:00
}
2015-08-14 07:21:05 -05:00
for ( ref a1 , ref a2 ) in decl . inputs . iter ( ) . zip ( args ) {
2016-02-18 14:16:39 -06:00
if let PatKind ::Ident ( _ , ident , _ ) = a1 . pat . node {
2015-08-14 07:21:05 -05: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 ;
}
2016-05-19 16:14:34 -05:00
if p . segments [ 0 ] . name ! = ident . node {
2015-08-14 07:21:05 -05:00
// 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
}
}
}
}