@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
2
2
use clippy_utils:: source:: { SpanRangeExt , snippet_with_applicability} ;
3
3
use clippy_utils:: ty:: adjust_derefs_manually_drop;
4
4
use rustc_errors:: Applicability ;
5
- use rustc_hir:: { Expr , ExprKind , Mutability , Node , UnOp } ;
5
+ use rustc_hir:: { Expr , ExprKind , HirId , Mutability , Node , UnOp } ;
6
6
use rustc_lint:: { LateContext , LateLintPass } ;
7
7
use rustc_session:: declare_lint_pass;
8
8
use rustc_span:: { BytePos , Span } ;
@@ -45,10 +45,18 @@ impl LateLintPass<'_> for DerefAddrOf {
45
45
// NOTE(tesuji): `*&` forces rustc to const-promote the array to `.rodata` section.
46
46
// See #12854 for details.
47
47
&& !matches ! ( addrof_target. kind, ExprKind :: Array ( _) )
48
- && !is_manually_drop_through_union ( cx, addrof_target)
49
48
&& deref_target. span . eq_ctxt ( e. span )
50
49
&& !addrof_target. span . from_expansion ( )
51
50
{
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
+
52
60
let mut applicability = Applicability :: MachineApplicable ;
53
61
let sugg = if e. span . from_expansion ( ) {
54
62
if let Some ( macro_source) = e. span . get_source_text ( cx) {
@@ -97,29 +105,55 @@ impl LateLintPass<'_> for DerefAddrOf {
97
105
e. span ,
98
106
"immediately dereferencing a reference" ,
99
107
"try" ,
100
- sugg. to_string ( ) ,
108
+ if keep_deref {
109
+ format ! ( "(*{sugg})" )
110
+ } else {
111
+ sugg. to_string ( )
112
+ } ,
101
113
applicability,
102
114
) ;
103
115
}
104
116
}
105
117
}
106
118
}
107
119
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 {
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 {
110
137
let typeck = cx. typeck_results ( ) ;
111
- if is_reached_through_union ( cx, expr) {
112
- for ( _, node) in cx. tcx . hir_parent_iter ( expr. hir_id ) {
113
- if let Node :: Expr ( expr) = node {
138
+ if is_reached_through_union ( cx, addrof_target) {
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) {
114
144
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
+ } ;
116
150
}
117
151
} else {
118
152
break ;
119
153
}
120
154
}
121
155
}
122
- false
156
+ ManuallyDropThroughUnion :: No
123
157
}
124
158
125
159
/// Checks whether `expr` denotes an object reached through a union
0 commit comments