Skip to content

Commit 9b2e423

Browse files
committed
Lint more cases when the explicit dereference can be kept in place
1 parent ca9e429 commit 9b2e423

File tree

4 files changed

+99
-19
lines changed

4 files changed

+99
-19
lines changed

clippy_lints/src/reference.rs

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
22
use clippy_utils::source::{SpanRangeExt, snippet_with_applicability};
33
use clippy_utils::ty::adjust_derefs_manually_drop;
44
use rustc_errors::Applicability;
5-
use rustc_hir::{Expr, ExprKind, Mutability, Node, UnOp};
5+
use rustc_hir::{Expr, ExprKind, HirId, Mutability, Node, UnOp};
66
use rustc_lint::{LateContext, LateLintPass};
77
use rustc_session::declare_lint_pass;
88
use 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 {
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 {
110137
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) {
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

tests/ui/deref_addrof.fixed

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,15 +112,23 @@ fn issue14386() {
112112
*a.prim = 0;
113113
//~^ deref_addrof
114114

115-
(*&mut a.data).num = 42;
116-
(*&mut a.tup).0.num = 42;
117-
(*&mut a.indirect.md)[3] = 1;
118-
(*&mut a.indirect_arr[1].md)[3] = 1;
119-
(*&mut a.indirect_ref.md)[3] = 1;
115+
(*a.data).num = 42;
116+
//~^ deref_addrof
117+
(*a.indirect.md)[3] = 1;
118+
//~^ deref_addrof
119+
(*a.indirect_arr[1].md)[3] = 1;
120+
//~^ deref_addrof
121+
(*a.indirect_ref.md)[3] = 1;
122+
//~^ deref_addrof
120123

121124
// Check that raw pointers are properly considered as well
122125
*a.prim = 0;
123126
//~^ deref_addrof
124-
(*&raw mut a.data).num = 42;
127+
(*a.data).num = 42;
128+
//~^ deref_addrof
129+
130+
// Do not lint, as the dereference happens later, we cannot
131+
// just remove `&mut`
132+
(*&mut a.tup).0.num = 42;
125133
}
126134
}

tests/ui/deref_addrof.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,22 @@ fn issue14386() {
113113
//~^ deref_addrof
114114

115115
(*&mut a.data).num = 42;
116-
(*&mut a.tup).0.num = 42;
116+
//~^ deref_addrof
117117
(*&mut a.indirect.md)[3] = 1;
118+
//~^ deref_addrof
118119
(*&mut a.indirect_arr[1].md)[3] = 1;
120+
//~^ deref_addrof
119121
(*&mut a.indirect_ref.md)[3] = 1;
122+
//~^ deref_addrof
120123

121124
// Check that raw pointers are properly considered as well
122125
**&raw mut a.prim = 0;
123126
//~^ deref_addrof
124127
(*&raw mut a.data).num = 42;
128+
//~^ deref_addrof
129+
130+
// Do not lint, as the dereference happens later, we cannot
131+
// just remove `&mut`
132+
(*&mut a.tup).0.num = 42;
125133
}
126134
}

tests/ui/deref_addrof.stderr

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,40 @@ LL | **&mut a.prim = 0;
9090
| ^^^^^^^^^^^^ help: try: `a.prim`
9191

9292
error: immediately dereferencing a reference
93-
--> tests/ui/deref_addrof.rs:122:10
93+
--> tests/ui/deref_addrof.rs:115:9
94+
|
95+
LL | (*&mut a.data).num = 42;
96+
| ^^^^^^^^^^^^^^ help: try: `(*a.data)`
97+
98+
error: immediately dereferencing a reference
99+
--> tests/ui/deref_addrof.rs:117:9
100+
|
101+
LL | (*&mut a.indirect.md)[3] = 1;
102+
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `(*a.indirect.md)`
103+
104+
error: immediately dereferencing a reference
105+
--> tests/ui/deref_addrof.rs:119:9
106+
|
107+
LL | (*&mut a.indirect_arr[1].md)[3] = 1;
108+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*a.indirect_arr[1].md)`
109+
110+
error: immediately dereferencing a reference
111+
--> tests/ui/deref_addrof.rs:121:9
112+
|
113+
LL | (*&mut a.indirect_ref.md)[3] = 1;
114+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*a.indirect_ref.md)`
115+
116+
error: immediately dereferencing a reference
117+
--> tests/ui/deref_addrof.rs:125:10
94118
|
95119
LL | **&raw mut a.prim = 0;
96120
| ^^^^^^^^^^^^^^^^ help: try: `a.prim`
97121

98-
error: aborting due to 15 previous errors
122+
error: immediately dereferencing a reference
123+
--> tests/ui/deref_addrof.rs:127:9
124+
|
125+
LL | (*&raw mut a.data).num = 42;
126+
| ^^^^^^^^^^^^^^^^^^ help: try: `(*a.data)`
127+
128+
error: aborting due to 20 previous errors
99129

0 commit comments

Comments
 (0)