Skip to content

Commit c530608

Browse files
committed
Merge needless_borrow checks into Dereferencing lint pass
`needless_borrow` will now check for multiple borrows `needless_borrow` will now consider mutable references
1 parent d1912cf commit c530608

File tree

6 files changed

+150
-59
lines changed

6 files changed

+150
-59
lines changed

clippy_lints/src/dereference.rs

+87-11
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1+
use crate::needless_borrow::NEEDLESS_BORROW;
12
use crate::utils::{
23
get_node_span, get_parent_node, in_macro, is_allowed, peel_mid_ty_refs, snippet_with_context, span_lint_and_sugg,
34
};
45
use rustc_ast::util::parser::PREC_PREFIX;
56
use rustc_errors::Applicability;
67
use rustc_hir::{BorrowKind, Destination, Expr, ExprKind, HirId, MatchSource, Mutability, Node, UnOp};
78
use rustc_lint::{LateContext, LateLintPass};
8-
use rustc_middle::ty::{self, adjustment::Adjustment, Ty, TyCtxt, TyS, TypeckResults};
9+
use rustc_middle::ty::{
10+
self,
11+
adjustment::{Adjust, Adjustment},
12+
Ty, TyCtxt, TyS, TypeckResults,
13+
};
914
use rustc_session::{declare_tool_lint, impl_lint_pass};
1015
use rustc_span::{symbol::sym, Span};
1116

@@ -38,6 +43,7 @@ declare_clippy_lint! {
3843

3944
impl_lint_pass!(Dereferencing => [
4045
EXPLICIT_DEREF_METHODS,
46+
NEEDLESS_BORROW,
4147
]);
4248

4349
#[derive(Default)]
@@ -64,6 +70,10 @@ enum State {
6470
ty_changed_count: usize,
6571
is_final_ufcs: bool,
6672
},
73+
NeedlessBorrow {
74+
// The number of borrows remaining
75+
remaining: usize,
76+
},
6777
}
6878

6979
// A reference operation considered by this lint pass
@@ -111,15 +121,50 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
111121

112122
let expr_adjustments = find_adjustments(cx.tcx, typeck, expr);
113123
let expr_ty = typeck.expr_ty(expr);
114-
let target_mut =
115-
if let ty::Ref(_, _, mutability) = *expr_adjustments.last().map_or(expr_ty, |a| a.target).kind() {
124+
let data = StateData {
125+
span: expr.span,
126+
target_mut: if let ty::Ref(_, _, mutability) =
127+
*expr_adjustments.last().map_or(expr_ty, |a| a.target).kind()
128+
{
116129
mutability
117130
} else {
118131
Mutability::Not
119-
};
132+
},
133+
};
120134

121-
match kind {
122-
RefOp::Method
135+
match (kind, expr_adjustments) {
136+
(
137+
RefOp::AddrOf,
138+
[Adjustment {
139+
kind: Adjust::Deref(None),
140+
target,
141+
}, Adjustment {
142+
kind: Adjust::Deref(None),
143+
..
144+
}, adjustments @ ..],
145+
) if target.is_ref() => {
146+
let count = adjustments
147+
.iter()
148+
.take_while(|&a| matches!(a.kind, Adjust::Deref(None)) && a.target.is_ref())
149+
.count();
150+
self.state = Some((
151+
State::NeedlessBorrow {
152+
remaining: if matches!(
153+
adjustments.get(count),
154+
Some(Adjustment {
155+
kind: Adjust::Borrow(_),
156+
..
157+
})
158+
) {
159+
count
160+
} else {
161+
count + 1
162+
},
163+
},
164+
data,
165+
));
166+
},
167+
(RefOp::Method, _)
123168
if !is_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
124169
&& is_linted_explicit_deref_position(parent, expr.hir_id) =>
125170
{
@@ -132,10 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
132177
},
133178
is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
134179
},
135-
StateData {
136-
span: expr.span,
137-
target_mut,
138-
},
180+
data,
139181
));
140182
}
141183
_ => (),
@@ -154,7 +196,14 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
154196
data,
155197
));
156198
},
157-
199+
(Some((State::NeedlessBorrow { remaining }, data)), RefOp::AddrOf) if remaining != 0 => {
200+
self.state = Some((
201+
State::NeedlessBorrow {
202+
remaining: remaining - 1,
203+
},
204+
data,
205+
))
206+
},
158207
(Some((state, data)), _) => report(cx, expr, state, data),
159208
}
160209
}
@@ -350,5 +399,32 @@ fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: Stat
350399
app,
351400
);
352401
},
402+
State::NeedlessBorrow { .. } => {
403+
let ty = cx.typeck_results().expr_ty(expr);
404+
let mut app = Applicability::MachineApplicable;
405+
let (expr_str, _) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
406+
407+
// let reported_ty = if ty.is_ref() {
408+
// ty
409+
// } else if let Some(Node::Expr(e)) = get_parent_node(cx.tcx, expr.hir_id) {
410+
// cx.typeck_results().expr_ty(e)
411+
// } else {
412+
// ty
413+
// };
414+
415+
span_lint_and_sugg(
416+
cx,
417+
NEEDLESS_BORROW,
418+
data.span,
419+
&format!(
420+
"this expression borrows a reference (`{}`) that is immediately dereferenced \
421+
by the compiler",
422+
ty,
423+
),
424+
"change this to",
425+
expr_str.into(),
426+
app,
427+
);
428+
},
353429
}
354430
}

clippy_lints/src/needless_borrow.rs

+1-43
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@
55
use crate::utils::{is_automatically_derived, snippet_opt, span_lint_and_then};
66
use if_chain::if_chain;
77
use rustc_errors::Applicability;
8-
use rustc_hir::{BindingAnnotation, BorrowKind, Expr, ExprKind, Item, Mutability, Pat, PatKind};
8+
use rustc_hir::{BindingAnnotation, Item, Mutability, Pat, PatKind};
99
use rustc_lint::{LateContext, LateLintPass};
1010
use rustc_middle::ty;
11-
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
1211
use rustc_session::{declare_tool_lint, impl_lint_pass};
1312
use rustc_span::def_id::LocalDefId;
1413

@@ -42,47 +41,6 @@ pub struct NeedlessBorrow {
4241
impl_lint_pass!(NeedlessBorrow => [NEEDLESS_BORROW]);
4342

4443
impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow {
45-
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
46-
if e.span.from_expansion() || self.derived_item.is_some() {
47-
return;
48-
}
49-
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = e.kind {
50-
if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(inner).kind() {
51-
for adj3 in cx.typeck_results().expr_adjustments(e).windows(3) {
52-
if let [Adjustment {
53-
kind: Adjust::Deref(_), ..
54-
}, Adjustment {
55-
kind: Adjust::Deref(_), ..
56-
}, Adjustment {
57-
kind: Adjust::Borrow(_),
58-
..
59-
}] = *adj3
60-
{
61-
span_lint_and_then(
62-
cx,
63-
NEEDLESS_BORROW,
64-
e.span,
65-
&format!(
66-
"this expression borrows a reference (`&{}`) that is immediately dereferenced \
67-
by the compiler",
68-
ty
69-
),
70-
|diag| {
71-
if let Some(snippet) = snippet_opt(cx, inner.span) {
72-
diag.span_suggestion(
73-
e.span,
74-
"change this to",
75-
snippet,
76-
Applicability::MachineApplicable,
77-
);
78-
}
79-
},
80-
);
81-
}
82-
}
83-
}
84-
}
85-
}
8644
fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
8745
if pat.span.from_expansion() || self.derived_item.is_some() {
8846
return;

clippy_utils/src/lib.rs

+13
Original file line numberDiff line numberDiff line change
@@ -1788,6 +1788,19 @@ pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>,
17881788
f(expr, 0, count)
17891789
}
17901790

1791+
/// Peels off all references on the type. Returns the underlying type and the number of references
1792+
/// removed.
1793+
pub fn peel_hir_ty_refs(ty: &'tcx hir::Ty<'_>) -> (&'tcx hir::Ty<'tcx>, usize) {
1794+
fn peel(ty: &'tcx hir::Ty<'_>, count: usize) -> (&'tcx hir::Ty<'tcx>, usize) {
1795+
if let TyKind::Rptr(_, ty) = &ty.kind {
1796+
peel(ty.ty, count + 1)
1797+
} else {
1798+
(ty, count)
1799+
}
1800+
}
1801+
peel(ty, 0)
1802+
}
1803+
17911804
/// Peels off all references on the expression. Returns the underlying expression and the number of
17921805
/// references removed.
17931806
pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {

tests/ui/needless_borrow.fixed

+14-1
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,28 @@ fn main() {
1919
let vec_val = g(&vec); // should not error, because `&Vec<T>` derefs to `&[T]`
2020
h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait`
2121
if let Some(cake) = Some(&5) {}
22+
let ref_a = &a;
2223
let garbl = match 42 {
2324
44 => &a,
2425
45 => {
2526
println!("foo");
26-
&&a // FIXME: this should lint, too
27+
&a
2728
},
2829
46 => &a,
30+
#[allow(clippy::never_loop)]
31+
47 => loop {
32+
break ref_a;
33+
},
34+
48 =>
35+
{
36+
#[allow(clippy::never_loop)]
37+
loop {
38+
break ref_a;
39+
}
40+
},
2941
_ => panic!(),
3042
};
43+
x(&Box::new(0)); // shouldn't error.
3144
}
3245

3346
fn f<T: Copy>(y: &T) -> T {

tests/ui/needless_borrow.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,28 @@ fn main() {
1919
let vec_val = g(&vec); // should not error, because `&Vec<T>` derefs to `&[T]`
2020
h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait`
2121
if let Some(ref cake) = Some(&5) {}
22+
let ref_a = &a;
2223
let garbl = match 42 {
2324
44 => &a,
2425
45 => {
2526
println!("foo");
26-
&&a // FIXME: this should lint, too
27+
&&a
2728
},
2829
46 => &&a,
30+
#[allow(clippy::never_loop)]
31+
47 => loop {
32+
break &ref_a;
33+
},
34+
48 =>
35+
{
36+
#[allow(clippy::never_loop)]
37+
loop {
38+
break &ref_a;
39+
}
40+
},
2941
_ => panic!(),
3042
};
43+
x(&Box::new(0)); // shouldn't error.
3144
}
3245

3346
fn f<T: Copy>(y: &T) -> T {

tests/ui/needless_borrow.stderr

+21-3
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,34 @@ LL | if let Some(ref cake) = Some(&5) {}
1313
| ^^^^^^^^ help: change this to: `cake`
1414

1515
error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
16-
--> $DIR/needless_borrow.rs:28:15
16+
--> $DIR/needless_borrow.rs:27:13
17+
|
18+
LL | &&a
19+
| ^^^ help: change this to: `&a`
20+
21+
error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
22+
--> $DIR/needless_borrow.rs:29:15
1723
|
1824
LL | 46 => &&a,
1925
| ^^^ help: change this to: `&a`
2026

27+
error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
28+
--> $DIR/needless_borrow.rs:32:19
29+
|
30+
LL | break &ref_a;
31+
| ^^^^^^ help: change this to: `ref_a`
32+
33+
error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
34+
--> $DIR/needless_borrow.rs:38:23
35+
|
36+
LL | break &ref_a;
37+
| ^^^^^^ help: change this to: `ref_a`
38+
2139
error: this pattern creates a reference to a reference
22-
--> $DIR/needless_borrow.rs:51:31
40+
--> $DIR/needless_borrow.rs:64:31
2341
|
2442
LL | let _ = v.iter().filter(|&ref a| a.is_empty());
2543
| ^^^^^ help: change this to: `a`
2644

27-
error: aborting due to 4 previous errors
45+
error: aborting due to 7 previous errors
2846

0 commit comments

Comments
 (0)