@@ -3,11 +3,17 @@ use clippy_utils::res::{MaybeDef, MaybeResPath};
33use  clippy_utils:: sugg:: Sugg ; 
44use  clippy_utils:: ty:: implements_trait; 
55use  clippy_utils:: { is_default_equivalent_call,  local_is_initialized} ; 
6+ use  rustc_data_structures:: fx:: FxHashSet ; 
7+ use  rustc_data_structures:: smallvec:: SmallVec ; 
68use  rustc_errors:: Applicability ; 
7- use  rustc_hir:: { Expr ,  ExprKind ,  LangItem ,  QPath } ; 
9+ use  rustc_hir:: { Body ,  BodyId ,  Expr ,  ExprKind ,  HirId ,  LangItem ,  QPath } ; 
10+ use  rustc_hir_typeck:: expr_use_visitor:: { Delegate ,  ExprUseVisitor ,  PlaceBase ,  PlaceWithHirId } ; 
811use  rustc_lint:: { LateContext ,  LateLintPass } ; 
9- use  rustc_session:: declare_lint_pass; 
10- use  rustc_span:: sym; 
12+ use  rustc_middle:: hir:: place:: ProjectionKind ; 
13+ use  rustc_middle:: mir:: FakeReadCause ; 
14+ use  rustc_middle:: ty; 
15+ use  rustc_session:: impl_lint_pass; 
16+ use  rustc_span:: { Symbol ,  sym} ; 
1117
1218declare_clippy_lint !  { 
1319    /// ### What it does 
@@ -33,17 +39,57 @@ declare_clippy_lint! {
3339    perf, 
3440    "assigning a newly created box to `Box<T>` is inefficient" 
3541} 
36- declare_lint_pass ! ( ReplaceBox  => [ REPLACE_BOX ] ) ; 
42+ 
43+ #[ derive( Default ) ]  
44+ pub  struct  ReplaceBox  { 
45+     consumed_locals :  FxHashSet < HirId > , 
46+     loaded_bodies :  SmallVec < [ BodyId ;  2 ] > , 
47+ } 
48+ 
49+ impl  ReplaceBox  { 
50+     fn  get_consumed_locals ( & mut  self ,  cx :  & LateContext < ' _ > )  -> & FxHashSet < HirId >  { 
51+         if  let  Some ( body_id)  = cx. enclosing_body 
52+             && !self . loaded_bodies . contains ( & body_id) 
53+         { 
54+             self . loaded_bodies . push ( body_id) ; 
55+             ExprUseVisitor :: for_clippy ( 
56+                 cx, 
57+                 cx. tcx . hir_body_owner_def_id ( body_id) , 
58+                 MovedVariablesCtxt  { 
59+                     consumed_locals :  & mut  self . consumed_locals , 
60+                 } , 
61+             ) 
62+             . consume_body ( cx. tcx . hir_body ( body_id) ) 
63+             . into_ok ( ) ; 
64+         } 
65+ 
66+         & self . consumed_locals 
67+     } 
68+ } 
69+ 
70+ impl_lint_pass ! ( ReplaceBox  => [ REPLACE_BOX ] ) ; 
3771
3872impl  LateLintPass < ' _ >  for  ReplaceBox  { 
73+     fn  check_body_post ( & mut  self ,  _:  & LateContext < ' _ > ,  body :  & Body < ' _ > )  { 
74+         if  self . loaded_bodies . first ( ) . is_some_and ( |& x| x == body. id ( ) )  { 
75+             self . consumed_locals . clear ( ) ; 
76+             self . loaded_bodies . clear ( ) ; 
77+         } 
78+     } 
79+ 
3980    fn  check_expr ( & mut  self ,  cx :  & LateContext < ' _ > ,  expr :  & ' _  Expr < ' _ > )  { 
4081        if  let  ExprKind :: Assign ( lhs,  rhs,  _)  = & expr. kind 
4182            && !lhs. span . from_expansion ( ) 
4283            && !rhs. span . from_expansion ( ) 
4384            && let  lhs_ty = cx. typeck_results ( ) . expr_ty ( lhs) 
85+             && let  Some ( inner_ty)  = lhs_ty. boxed_ty ( ) 
4486            // No diagnostic for late-initialized locals 
4587            && lhs. res_local_id ( ) . is_none_or ( |local| local_is_initialized ( cx,  local) ) 
46-             && let  Some ( inner_ty)  = lhs_ty. boxed_ty ( ) 
88+             // No diagnostic if this is a local that has been moved, or the field 
89+             // of a local that has been moved, or several chained field accesses of a local 
90+             && local_base ( lhs) . is_none_or ( |( base_id,  _) | { 
91+                 !self . get_consumed_locals ( cx) . contains ( & base_id) 
92+             } ) 
4793        { 
4894            if  let  Some ( default_trait_id)  = cx. tcx . get_diagnostic_item ( sym:: Default ) 
4995                && implements_trait ( cx,  inner_ty,  default_trait_id,  & [ ] ) 
@@ -109,3 +155,46 @@ fn get_box_new_payload<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<
109155        None 
110156    } 
111157} 
158+ 
159+ struct  MovedVariablesCtxt < ' a >  { 
160+     consumed_locals :  & ' a  mut  FxHashSet < HirId > , 
161+ } 
162+ 
163+ impl < ' tcx >  Delegate < ' tcx >  for  MovedVariablesCtxt < ' _ >  { 
164+     fn  consume ( & mut  self ,  cmt :  & PlaceWithHirId < ' tcx > ,  _:  HirId )  { 
165+         if  let  PlaceBase :: Local ( id)  = cmt. place . base 
166+             && let  mut  projections = cmt
167+                 . place 
168+                 . projections 
169+                 . iter ( ) 
170+                 . filter ( |x| matches ! ( x. kind,  ProjectionKind :: Deref ) ) 
171+             // Either no deref or multiple derefs 
172+             && ( projections. next ( ) . is_none ( )  || projections. next ( ) . is_some ( ) ) 
173+         { 
174+             self . consumed_locals . insert ( id) ; 
175+         } 
176+     } 
177+ 
178+     fn  use_cloned ( & mut  self ,  _:  & PlaceWithHirId < ' tcx > ,  _:  HirId )  { } 
179+ 
180+     fn  borrow ( & mut  self ,  _:  & PlaceWithHirId < ' tcx > ,  _:  HirId ,  _:  ty:: BorrowKind )  { } 
181+ 
182+     fn  mutate ( & mut  self ,  _:  & PlaceWithHirId < ' tcx > ,  _:  HirId )  { } 
183+ 
184+     fn  fake_read ( & mut  self ,  _:  & PlaceWithHirId < ' tcx > ,  _:  FakeReadCause ,  _:  HirId )  { } 
185+ } 
186+ 
187+ /// A local place followed by optional fields 
188+ type  IdFields  = ( HirId ,  Vec < Symbol > ) ; 
189+ 
190+ /// If `expr` is a local variable with optional field accesses, return it. 
191+ fn  local_base ( expr :  & Expr < ' _ > )  -> Option < IdFields >  { 
192+     match  expr. kind  { 
193+         ExprKind :: Path ( qpath)  => qpath. res_local_id ( ) . map ( |id| ( id,  Vec :: new ( ) ) ) , 
194+         ExprKind :: Field ( expr,  field)  => local_base ( expr) . map ( |( id,  mut  fields) | { 
195+             fields. push ( field. name ) ; 
196+             ( id,  fields) 
197+         } ) , 
198+         _ => None , 
199+     } 
200+ } 
0 commit comments