@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
22use clippy_utils:: source:: { SpanRangeExt , snippet_with_applicability} ;
33use clippy_utils:: ty:: adjust_derefs_manually_drop;
44use rustc_errors:: Applicability ;
5- use rustc_hir:: { Expr , ExprKind , Mutability , Node , UnOp } ;
5+ use rustc_hir:: { Expr , ExprKind , HirId , Mutability , Node , UnOp } ;
66use rustc_lint:: { LateContext , LateLintPass } ;
77use rustc_session:: declare_lint_pass;
88use rustc_span:: { BytePos , Span } ;
@@ -45,10 +45,18 @@ impl LateLintPass<'_> for DerefAddrOf {
4545 // NOTE(tesuji): `*&` forces rustc to const-promote the array to `.rodata` section.
4646 // See #12854 for details.
4747 && !matches ! ( addrof_target. kind, ExprKind :: Array ( _) )
48- && !is_manually_drop_through_union ( cx, addrof_target)
4948 && deref_target. span . eq_ctxt ( e. span )
5049 && !addrof_target. span . from_expansion ( )
5150 {
51+ // If this expression is an explicit `DerefMut` of a `ManuallyDrop` reached through a
52+ // union, we may remove the reference if we are at the point where the implicit
53+ // dereference would take place. Otherwise, we should not lint.
54+ let keep_deref = match is_manually_drop_through_union ( cx, e. hir_id , addrof_target) {
55+ ManuallyDropThroughUnion :: Directly => true ,
56+ ManuallyDropThroughUnion :: Indirect => return ,
57+ ManuallyDropThroughUnion :: No => false ,
58+ } ;
59+
5260 let mut applicability = Applicability :: MachineApplicable ;
5361 let sugg = if e. span . from_expansion ( ) {
5462 if let Some ( macro_source) = e. span . get_source_text ( cx) {
@@ -97,29 +105,55 @@ impl LateLintPass<'_> for DerefAddrOf {
97105 e. span ,
98106 "immediately dereferencing a reference" ,
99107 "try" ,
100- sugg. to_string ( ) ,
108+ if keep_deref {
109+ format ! ( "(*{sugg})" )
110+ } else {
111+ sugg. to_string ( )
112+ } ,
101113 applicability,
102114 ) ;
103115 }
104116 }
105117 }
106118}
107119
108- /// Check if `expr` is part of an access to a `ManuallyDrop` entity reached through a union
109- fn is_manually_drop_through_union ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
110- if is_reached_through_union ( cx, expr) {
120+ /// Is this a `ManuallyDrop` reached through a union, and when is `DerefMut` called on it?
121+ enum ManuallyDropThroughUnion {
122+ /// `ManuallyDrop` reached through a union and immediately explicitely dereferenced
123+ Directly ,
124+ /// `ManuallyDrop` reached through a union, and dereferenced later on
125+ Indirect ,
126+ /// Any other situation
127+ No ,
128+ }
129+
130+ /// Check if `addrof_target` is part of an access to a `ManuallyDrop` entity reached through a
131+ /// union, and when it is dereferenced using `DerefMut` starting from `expr_id` and going up.
132+ fn is_manually_drop_through_union (
133+ cx : & LateContext < ' _ > ,
134+ expr_id : HirId ,
135+ addrof_target : & Expr < ' _ > ,
136+ ) -> ManuallyDropThroughUnion {
137+ if is_reached_through_union ( cx, addrof_target) {
111138 let typeck = cx. typeck_results ( ) ;
112- for ( _, node) in cx. tcx . hir_parent_iter ( expr. hir_id ) {
113- if let Node :: Expr ( expr) = node {
139+ for ( idx, id) in std:: iter:: once ( expr_id)
140+ . chain ( cx. tcx . hir_parent_id_iter ( expr_id) )
141+ . enumerate ( )
142+ {
143+ if let Node :: Expr ( expr) = cx. tcx . hir_node ( id) {
114144 if adjust_derefs_manually_drop ( typeck. expr_adjustments ( expr) , typeck. expr_ty ( expr) ) {
115- return true ;
145+ return if idx == 0 {
146+ ManuallyDropThroughUnion :: Directly
147+ } else {
148+ ManuallyDropThroughUnion :: Indirect
149+ } ;
116150 }
117151 } else {
118152 break ;
119153 }
120154 }
121155 }
122- false
156+ ManuallyDropThroughUnion :: No
123157}
124158
125159/// Checks whether `expr` denotes an object reached through a union
0 commit comments