@@ -3,10 +3,15 @@ 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:: FxHashMap ;
67use rustc_errors:: Applicability ;
7- use rustc_hir:: { Expr , ExprKind , LangItem , QPath } ;
8+ use rustc_hir:: def_id:: LocalDefId ;
9+ use rustc_hir:: { Expr , ExprKind , HirId , HirIdSet , LangItem , Node , 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;
12+ use rustc_middle:: mir:: FakeReadCause ;
13+ use rustc_middle:: ty;
14+ use rustc_session:: impl_lint_pass;
1015use rustc_span:: sym;
1116
1217declare_clippy_lint ! {
@@ -33,7 +38,13 @@ declare_clippy_lint! {
3338 perf,
3439 "assigning a newly created box to `Box<T>` is inefficient"
3540}
36- declare_lint_pass ! ( ReplaceBox => [ REPLACE_BOX ] ) ;
41+
42+ #[ derive( Default ) ]
43+ pub struct ReplaceBox {
44+ moved_vars : FxHashMap < LocalDefId , HirIdSet > ,
45+ }
46+
47+ impl_lint_pass ! ( ReplaceBox => [ REPLACE_BOX ] ) ;
3748
3849impl LateLintPass < ' _ > for ReplaceBox {
3950 fn check_expr ( & mut self , cx : & LateContext < ' _ > , expr : & ' _ Expr < ' _ > ) {
@@ -42,7 +53,30 @@ impl LateLintPass<'_> for ReplaceBox {
4253 && !rhs. span . from_expansion ( )
4354 && let lhs_ty = cx. typeck_results ( ) . expr_ty ( lhs)
4455 // No diagnostic for late-initialized locals
45- && lhs. res_local_id ( ) . is_none_or ( |local| local_is_initialized ( cx, local) )
56+ && let local = lhs. res_local_id ( )
57+ && local. is_none_or ( |local| {
58+ if !local_is_initialized ( cx, local) {
59+ return false ;
60+ }
61+
62+ let Some ( body) = cx. enclosing_body . map ( |b| cx. tcx . hir_body ( b) ) else {
63+ return true ;
64+ } ;
65+
66+ let owner_id = cx. tcx . hir_enclosing_body_owner ( expr. hir_id ) ;
67+ !self . moved_vars
68+ . entry ( owner_id)
69+ . or_insert_with ( || {
70+ let mut ctx = MovedVariablesCtxt {
71+ cx,
72+ moved_vars : HirIdSet :: default ( ) ,
73+ } ;
74+ ExprUseVisitor :: for_clippy ( cx, owner_id, & mut ctx)
75+ . consume_body ( body)
76+ . into_ok ( ) ;
77+ ctx. moved_vars
78+ } ) . contains ( & local)
79+ } )
4680 && let Some ( inner_ty) = lhs_ty. boxed_ty ( )
4781 {
4882 if let Some ( default_trait_id) = cx. tcx . get_diagnostic_item ( sym:: Default )
@@ -109,3 +143,27 @@ fn get_box_new_payload<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<
109143 None
110144 }
111145}
146+
147+ struct MovedVariablesCtxt < ' a , ' tcx > {
148+ cx : & ' a LateContext < ' tcx > ,
149+ moved_vars : HirIdSet ,
150+ }
151+
152+ impl < ' tcx > Delegate < ' tcx > for MovedVariablesCtxt < ' _ , ' tcx > {
153+ fn consume ( & mut self , cmt : & PlaceWithHirId < ' tcx > , _: HirId ) {
154+ if let PlaceBase :: Local ( vid) = cmt. place . base
155+ && let Node :: Expr ( expr) = self . cx . tcx . hir_node ( cmt. hir_id )
156+ && expr. res_local_id ( ) . is_some_and ( |hir_id| hir_id == vid)
157+ {
158+ self . moved_vars . insert ( vid) ;
159+ }
160+ }
161+
162+ fn use_cloned ( & mut self , _: & PlaceWithHirId < ' tcx > , _: HirId ) { }
163+
164+ fn borrow ( & mut self , _: & PlaceWithHirId < ' tcx > , _: HirId , _: ty:: BorrowKind ) { }
165+
166+ fn mutate ( & mut self , _: & PlaceWithHirId < ' tcx > , _: HirId ) { }
167+
168+ fn fake_read ( & mut self , _: & PlaceWithHirId < ' tcx > , _: FakeReadCause , _: HirId ) { }
169+ }
0 commit comments