-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathreference.rs
More file actions
132 lines (123 loc) · 4.7 KB
/
reference.rs
File metadata and controls
132 lines (123 loc) · 4.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::sugg::{Sugg, has_enclosing_paren};
use clippy_utils::ty::adjust_derefs_manually_drop;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, HirId, Node, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `*&` and `*&mut` in expressions.
///
/// ### Why is this bad?
/// Immediately dereferencing a reference is no-op and
/// makes the code less clear.
///
/// ### Known problems
/// Multiple dereference/addrof pairs are not handled so
/// the suggested fix for `x = **&&y` is `x = *&y`, which is still incorrect.
///
/// ### Example
/// ```rust,ignore
/// let a = f(*&mut b);
/// let c = *&d;
/// ```
///
/// Use instead:
/// ```rust,ignore
/// let a = f(b);
/// let c = d;
/// ```
#[clippy::version = "pre 1.29.0"]
pub DEREF_ADDROF,
complexity,
"use of `*&` or `*&mut` in an expression"
}
declare_lint_pass!(DerefAddrOf => [DEREF_ADDROF]);
impl LateLintPass<'_> for DerefAddrOf {
fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) {
if !e.span.from_expansion()
&& let ExprKind::Unary(UnOp::Deref, deref_target) = e.kind
&& !deref_target.span.from_expansion()
&& let ExprKind::AddrOf(_, _, addrof_target) = deref_target.kind
// NOTE(tesuji): `*&` forces rustc to const-promote the array to `.rodata` section.
// See #12854 for details.
&& !matches!(addrof_target.kind, ExprKind::Array(_))
&& deref_target.span.eq_ctxt(e.span)
&& !addrof_target.span.from_expansion()
{
let mut applicability = Applicability::MachineApplicable;
let mut sugg = || Sugg::hir_with_applicability(cx, addrof_target, "_", &mut applicability);
// If this expression is an explicit `DerefMut` of a `ManuallyDrop` reached through a
// union, we may remove the reference if we are at the point where the implicit
// dereference would take place. Otherwise, we should not lint.
let sugg = match is_manually_drop_through_union(cx, e.hir_id, addrof_target) {
ManuallyDropThroughUnion::Directly => sugg().deref(),
ManuallyDropThroughUnion::Indirect => return,
ManuallyDropThroughUnion::No => sugg(),
};
let sugg = if has_enclosing_paren(snippet(cx, e.span, "")) {
sugg.maybe_paren()
} else {
sugg
};
span_lint_and_sugg(
cx,
DEREF_ADDROF,
e.span,
"immediately dereferencing a reference",
"try",
sugg.to_string(),
applicability,
);
}
}
}
/// Is this a `ManuallyDrop` reached through a union, and when is `DerefMut` called on it?
enum ManuallyDropThroughUnion {
/// `ManuallyDrop` reached through a union and immediately explicitely dereferenced
Directly,
/// `ManuallyDrop` reached through a union, and dereferenced later on
Indirect,
/// Any other situation
No,
}
/// Check if `addrof_target` is part of an access to a `ManuallyDrop` entity reached through a
/// union, and when it is dereferenced using `DerefMut` starting from `expr_id` and going up.
fn is_manually_drop_through_union(
cx: &LateContext<'_>,
expr_id: HirId,
addrof_target: &Expr<'_>,
) -> ManuallyDropThroughUnion {
if is_reached_through_union(cx, addrof_target) {
let typeck = cx.typeck_results();
for (idx, id) in std::iter::once(expr_id)
.chain(cx.tcx.hir_parent_id_iter(expr_id))
.enumerate()
{
if let Node::Expr(expr) = cx.tcx.hir_node(id) {
if adjust_derefs_manually_drop(typeck.expr_adjustments(expr), typeck.expr_ty(expr)) {
return if idx == 0 {
ManuallyDropThroughUnion::Directly
} else {
ManuallyDropThroughUnion::Indirect
};
}
} else {
break;
}
}
}
ManuallyDropThroughUnion::No
}
/// Checks whether `expr` denotes an object reached through a union
fn is_reached_through_union(cx: &LateContext<'_>, mut expr: &Expr<'_>) -> bool {
while let ExprKind::Field(parent, _) | ExprKind::Index(parent, _, _) = expr.kind {
if cx.typeck_results().expr_ty_adjusted(parent).is_union() {
return true;
}
expr = parent;
}
false
}