2016-04-07 17:46:48 +02:00
use rustc ::hir ::* ;
use rustc ::hir ::intravisit as visit ;
use rustc ::hir ::map ::Node ::{ NodeExpr , NodeStmt } ;
2016-02-24 17:38:57 +01:00
use rustc ::lint ::* ;
2015-12-04 11:12:53 +01:00
use rustc ::middle ::expr_use_visitor ::* ;
use rustc ::middle ::mem_categorization ::{ cmt , Categorization } ;
2016-03-27 20:59:02 +02:00
use rustc ::ty ::adjustment ::AutoAdjustment ;
use rustc ::ty ;
2015-12-04 11:12:53 +01:00
use rustc ::util ::nodemap ::NodeSet ;
use syntax ::ast ::NodeId ;
use syntax ::codemap ::Span ;
use utils ::span_lint ;
2016-06-10 16:17:20 +02:00
pub struct Pass ;
2015-12-04 11:12:53 +01:00
2016-02-06 00:41:54 +01:00
/// **What it does:** This lint checks for usage of `Box<T>` where an unboxed `T` would work fine.
2015-12-13 04:38:58 +01:00
///
2016-01-01 17:49:01 +01:00
/// **Why is this bad?** This is an unnecessary allocation, and bad for performance. It is only necessary to allocate if you wish to move the box into something.
2015-12-13 04:38:58 +01:00
///
2016-01-01 17:49:01 +01:00
/// **Known problems:** None
2015-12-13 04:38:58 +01:00
///
/// **Example:**
///
/// ```rust
/// fn main() {
/// let x = Box::new(1);
/// foo(*x);
/// println!("{}", *x);
/// }
2015-12-14 21:17:11 +01:00
/// ```
2016-02-06 00:13:29 +01:00
declare_lint! {
2016-05-06 16:09:05 +02:00
pub BOXED_LOCAL , Warn , " using `Box<T>` where unnecessary "
2016-02-06 00:13:29 +01:00
}
2015-12-04 11:12:53 +01:00
2016-02-01 20:37:07 +01:00
fn is_non_trait_box ( ty : ty ::Ty ) -> bool {
2015-12-28 15:12:57 +01:00
match ty . sty {
2016-02-01 20:37:07 +01:00
ty ::TyBox ( ref inner ) = > ! inner . is_trait ( ) ,
2016-01-30 13:48:39 +01:00
_ = > false ,
2015-12-28 15:12:57 +01:00
}
}
2015-12-04 11:12:53 +01:00
struct EscapeDelegate < ' a , ' tcx : ' a > {
2016-05-12 19:11:13 +02:00
tcx : ty ::TyCtxt < ' a , ' tcx , ' tcx > ,
2015-12-04 11:12:53 +01:00
set : NodeSet ,
}
2016-06-10 16:17:20 +02:00
impl LintPass for Pass {
2015-12-04 11:12:53 +01:00
fn get_lints ( & self ) -> LintArray {
lint_array! ( BOXED_LOCAL )
}
}
2016-06-10 16:17:20 +02:00
impl LateLintPass for Pass {
2016-01-04 05:26:12 +01:00
fn check_fn ( & mut self , cx : & LateContext , _ : visit ::FnKind , decl : & FnDecl , body : & Block , _ : Span , id : NodeId ) {
2015-12-04 11:12:53 +01:00
let param_env = ty ::ParameterEnvironment ::for_item ( cx . tcx , id ) ;
2016-05-12 19:11:13 +02:00
let infcx = cx . tcx . borrowck_fake_infer_ctxt ( param_env ) ;
2015-12-04 11:12:53 +01:00
let mut v = EscapeDelegate {
2016-05-12 19:11:13 +02:00
tcx : cx . tcx ,
2015-12-04 11:12:53 +01:00
set : NodeSet ( ) ,
} ;
2016-05-12 19:11:13 +02:00
2015-12-04 11:12:53 +01:00
{
let mut vis = ExprUseVisitor ::new ( & mut v , & infcx ) ;
vis . walk_fn ( decl , body ) ;
}
2016-05-12 19:11:13 +02:00
2015-12-04 11:12:53 +01:00
for node in v . set {
span_lint ( cx ,
BOXED_LOCAL ,
cx . tcx . map . span ( node ) ,
" local variable doesn't need to be boxed here " ) ;
}
}
}
impl < ' a , ' tcx : ' a > Delegate < ' tcx > for EscapeDelegate < ' a , ' tcx > {
2016-01-04 05:26:12 +01:00
fn consume ( & mut self , _ : NodeId , _ : Span , cmt : cmt < ' tcx > , mode : ConsumeMode ) {
2015-12-04 11:12:53 +01:00
if let Categorization ::Local ( lid ) = cmt . cat {
if self . set . contains ( & lid ) {
if let Move ( DirectRefMove ) = mode {
// moved out or in. clearly can't be localized
self . set . remove ( & lid ) ;
}
}
}
}
fn matched_pat ( & mut self , _ : & Pat , _ : cmt < ' tcx > , _ : MatchMode ) { }
fn consume_pat ( & mut self , consume_pat : & Pat , cmt : cmt < ' tcx > , _ : ConsumeMode ) {
2016-05-12 19:11:13 +02:00
let map = & self . tcx . map ;
2016-02-01 12:35:01 +01:00
if map . is_argument ( consume_pat . id ) {
// Skip closure arguments
if let Some ( NodeExpr ( .. ) ) = map . find ( map . get_parent_node ( consume_pat . id ) ) {
return ;
}
2016-02-01 20:37:07 +01:00
if is_non_trait_box ( cmt . ty ) {
2015-12-28 15:12:57 +01:00
self . set . insert ( consume_pat . id ) ;
}
return ;
}
2015-12-04 11:12:53 +01:00
if let Categorization ::Rvalue ( .. ) = cmt . cat {
2016-02-01 12:35:01 +01:00
if let Some ( NodeStmt ( st ) ) = map . find ( map . get_parent_node ( cmt . id ) ) {
2015-12-04 11:12:53 +01:00
if let StmtDecl ( ref decl , _ ) = st . node {
if let DeclLocal ( ref loc ) = decl . node {
if let Some ( ref ex ) = loc . init {
if let ExprBox ( .. ) = ex . node {
2016-02-01 20:37:07 +01:00
if is_non_trait_box ( cmt . ty ) {
2015-12-04 11:12:53 +01:00
// let x = box (...)
self . set . insert ( consume_pat . id ) ;
}
// TODO Box::new
// TODO vec![]
// TODO "foo".to_owned() and friends
}
}
}
}
}
}
if let Categorization ::Local ( lid ) = cmt . cat {
if self . set . contains ( & lid ) {
// let y = x where x is known
// remove x, insert y
self . set . insert ( consume_pat . id ) ;
self . set . remove ( & lid ) ;
}
}
}
2016-01-04 05:26:12 +01:00
fn borrow ( & mut self , borrow_id : NodeId , _ : Span , cmt : cmt < ' tcx > , _ : ty ::Region , _ : ty ::BorrowKind ,
2015-12-04 11:12:53 +01:00
loan_cause : LoanCause ) {
if let Categorization ::Local ( lid ) = cmt . cat {
if self . set . contains ( & lid ) {
2016-05-12 19:11:13 +02:00
if let Some ( & AutoAdjustment ::AdjustDerefRef ( adj ) ) = self . tcx
2015-12-04 11:12:53 +01:00
. tables
. borrow ( )
. adjustments
. get ( & borrow_id ) {
if LoanCause ::AutoRef = = loan_cause {
// x.foo()
2016-01-29 07:39:13 +01:00
if adj . autoderefs = = 0 {
2015-12-04 11:12:53 +01:00
self . set . remove ( & lid ) ; // Used without autodereffing (i.e. x.clone())
}
} else {
2016-04-03 17:16:53 +02:00
span_bug! ( cmt . span , " Unknown adjusted AutoRef " ) ;
2015-12-04 11:12:53 +01:00
}
} else if LoanCause ::AddrOf = = loan_cause {
// &x
2016-05-12 19:11:13 +02:00
if let Some ( & AutoAdjustment ::AdjustDerefRef ( adj ) ) = self . tcx
2016-01-04 05:26:12 +01:00
. tables
. borrow ( )
. adjustments
2016-05-12 19:11:13 +02:00
. get ( & self . tcx
2016-01-04 05:26:12 +01:00
. map
. get_parent_node ( borrow_id ) ) {
2015-12-04 11:12:53 +01:00
if adj . autoderefs < = 1 {
// foo(&x) where no extra autoreffing is happening
self . set . remove ( & lid ) ;
}
}
} else if LoanCause ::MatchDiscriminant = = loan_cause {
self . set . remove ( & lid ) ; // `match x` can move
}
// do nothing for matches, etc. These can't escape
}
}
}
fn decl_without_init ( & mut self , _ : NodeId , _ : Span ) { }
2016-01-04 05:26:12 +01:00
fn mutate ( & mut self , _ : NodeId , _ : Span , _ : cmt < ' tcx > , _ : MutateMode ) { }
2015-12-04 11:12:53 +01:00
}