2015-08-16 08:54:43 +02:00
use rustc ::lint ::* ;
2015-09-03 20:12:17 +05:30
use rustc_front ::hir ::* ;
2015-08-26 17:09:37 +02:00
use rustc ::middle ::ty ;
2015-05-10 10:39:04 +05:30
2015-12-09 01:28:35 +09:00
use utils ::{ snippet_opt , span_lint_and_then , is_adjusted } ;
2015-07-26 20:23:11 +05:30
2015-05-10 10:39:04 +05:30
#[ allow(missing_copy_implementations) ]
pub struct EtaPass ;
2016-02-06 00:41:54 +01: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 01:22:27 +01: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-06 00:13:29 +01: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 10:39:04 +05:30
impl LintPass for EtaPass {
fn get_lints ( & self ) -> LintArray {
lint_array! ( REDUNDANT_CLOSURE )
}
2015-09-19 08:23:04 +05:30
}
2015-05-10 10:39:04 +05:30
2015-09-19 08:23:04 +05:30
impl LateLintPass for EtaPass {
fn check_expr ( & mut self , cx : & LateContext , expr : & Expr ) {
2015-08-21 20:44:48 +02:00
match expr . node {
ExprCall ( _ , ref args ) |
ExprMethodCall ( _ , _ , ref args ) = > {
2015-08-14 14:21:05 +02:00
for arg in args {
2015-08-25 14:41:35 +02:00
check_closure ( cx , arg )
2015-08-14 14:21:05 +02:00
}
2015-11-17 13:39:42 +09:00
}
2015-08-14 14:21:05 +02:00
_ = > ( ) ,
}
}
}
2015-09-19 08:23:04 +05:30
fn check_closure ( cx : & LateContext , expr : & Expr ) {
2015-08-14 14:21:05 +02: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 19:28:06 +01:00
2015-08-14 14:21:05 +02: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 19:28:06 +01: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 17:09:37 +02: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 14:21:05 +02: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-04 09:56:12 +05:30
return ;
2015-05-10 10:39:04 +05:30
}
} else {
2016-01-04 09:56:12 +05:30
return ;
2015-05-10 10:39:04 +05:30
}
2015-08-14 14:21:05 +02:00
} else {
2016-01-04 09:56:12 +05:30
return ;
2015-05-10 10:39:04 +05:30
}
}
2016-01-04 09:56:12 +05:30
span_lint_and_then ( cx , REDUNDANT_CLOSURE , expr . span , " redundant closure found " , | db | {
2015-12-09 01:28:35 +09:00
if let Some ( snippet ) = snippet_opt ( cx , caller . span ) {
2016-01-04 09:56:12 +05:30
db . span_suggestion ( expr . span , " remove closure as shown: " , snippet ) ;
2015-12-09 01:28:35 +09:00
}
} ) ;
2015-05-10 10:39:04 +05:30
}
}
}
}