Skip to content

Commit 6d89798

Browse files
authored
Merge pull request #2163 from HMPerson1/useless_asref
Add lint for useless `as_ref` calls
2 parents 2f225d8 + bfa7a9b commit 6d89798

File tree

3 files changed

+243
-2
lines changed

3 files changed

+243
-2
lines changed

clippy_lints/src/methods.rs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,29 @@ declare_lint! {
581581
"using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char"
582582
}
583583

584+
/// **What it does:** Checks for usage of `.as_ref()` or `.as_mut()` where the
585+
/// types before and after the call are the same.
586+
///
587+
/// **Why is this bad?** The call is unnecessary.
588+
///
589+
/// **Known problems:** None.
590+
///
591+
/// **Example:**
592+
/// ```rust
593+
/// let x: &[i32] = &[1,2,3,4,5];
594+
/// do_stuff(x.as_ref());
595+
/// ```
596+
/// The correct use would be:
597+
/// ```rust
598+
/// let x: &[i32] = &[1,2,3,4,5];
599+
/// do_stuff(x);
600+
/// ```
601+
declare_lint! {
602+
pub USELESS_ASREF,
603+
Warn,
604+
"using `as_ref` where the types before and after the call are the same"
605+
}
606+
584607
impl LintPass for Pass {
585608
fn get_lints(&self) -> LintArray {
586609
lint_array!(
@@ -609,8 +632,8 @@ impl LintPass for Pass {
609632
ITER_SKIP_NEXT,
610633
GET_UNWRAP,
611634
STRING_EXTEND_CHARS,
612-
ITER_CLONED_COLLECT
613-
)
635+
ITER_CLONED_COLLECT,
636+
USELESS_ASREF)
614637
}
615638
}
616639

@@ -669,6 +692,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
669692
lint_iter_skip_next(cx, expr);
670693
} else if let Some(arglists) = method_chain_args(expr, &["cloned", "collect"]) {
671694
lint_iter_cloned_collect(cx, expr, arglists[0]);
695+
} else if let Some(arglists) = method_chain_args(expr, &["as_ref"]) {
696+
lint_asref(cx, expr, "as_ref", arglists[0]);
697+
} else if let Some(arglists) = method_chain_args(expr, &["as_mut"]) {
698+
lint_asref(cx, expr, "as_mut", arglists[0]);
672699
}
673700

674701
lint_or_fun_call(cx, expr, &method_call.name.as_str(), args);
@@ -1504,6 +1531,30 @@ fn lint_single_char_pattern<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hi
15041531
}
15051532
}
15061533

1534+
/// Checks for the `USELESS_ASREF` lint.
1535+
fn lint_asref(cx: &LateContext, expr: &hir::Expr, call_name: &str, as_ref_args: &[hir::Expr]) {
1536+
// when we get here, we've already checked that the call name is "as_ref" or "as_mut"
1537+
// check if the call is to the actual `AsRef` or `AsMut` trait
1538+
if match_trait_method(cx, expr, &paths::ASREF_TRAIT) || match_trait_method(cx, expr, &paths::ASMUT_TRAIT) {
1539+
// check if the type after `as_ref` or `as_mut` is the same as before
1540+
let recvr = &as_ref_args[0];
1541+
let rcv_ty = cx.tables.expr_ty(recvr);
1542+
let res_ty = cx.tables.expr_ty(expr);
1543+
let (base_res_ty, res_depth) = walk_ptrs_ty_depth(res_ty);
1544+
let (base_rcv_ty, rcv_depth) = walk_ptrs_ty_depth(rcv_ty);
1545+
if base_rcv_ty == base_res_ty && rcv_depth >= res_depth {
1546+
span_lint_and_sugg(
1547+
cx,
1548+
USELESS_ASREF,
1549+
expr.span,
1550+
&format!("this call to `{}` does nothing", call_name),
1551+
"try this",
1552+
snippet(cx, recvr.span, "_").into_owned(),
1553+
);
1554+
}
1555+
}
1556+
}
1557+
15071558
/// Given a `Result<T, E>` type, return its error type (`E`).
15081559
fn get_error_type<'a>(cx: &LateContext, ty: Ty<'a>) -> Option<Ty<'a>> {
15091560
if let ty::TyAdt(_, substs) = ty.sty {

tests/ui/useless_asref.rs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
#![deny(useless_asref)]
2+
3+
use std::fmt::Debug;
4+
5+
struct FakeAsRef;
6+
7+
#[allow(should_implement_trait)]
8+
impl FakeAsRef {
9+
fn as_ref(&self) -> &Self { self }
10+
}
11+
12+
struct MoreRef;
13+
14+
impl<'a, 'b, 'c> AsRef<&'a &'b &'c MoreRef> for MoreRef {
15+
fn as_ref(&self) -> &&'a &'b &'c MoreRef {
16+
&&&&MoreRef
17+
}
18+
}
19+
20+
fn foo_rstr(x: &str) { println!("{:?}", x); }
21+
fn foo_rslice(x: &[i32]) { println!("{:?}", x); }
22+
fn foo_mrslice(x: &mut [i32]) { println!("{:?}", x); }
23+
fn foo_rrrrmr(_: &&&&MoreRef) { println!("so many refs"); }
24+
25+
fn not_ok() {
26+
let rstr: &str = "hello";
27+
let mut mrslice: &mut [i32] = &mut [1,2,3];
28+
29+
{
30+
let rslice: &[i32] = &*mrslice;
31+
foo_rstr(rstr.as_ref());
32+
foo_rstr(rstr);
33+
foo_rslice(rslice.as_ref());
34+
foo_rslice(rslice);
35+
}
36+
{
37+
foo_mrslice(mrslice.as_mut());
38+
foo_mrslice(mrslice);
39+
foo_rslice(mrslice.as_ref());
40+
foo_rslice(mrslice);
41+
}
42+
43+
{
44+
let rrrrrstr = &&&&rstr;
45+
let rrrrrslice = &&&&&*mrslice;
46+
foo_rslice(rrrrrslice.as_ref());
47+
foo_rslice(rrrrrslice);
48+
foo_rstr(rrrrrstr.as_ref());
49+
foo_rstr(rrrrrstr);
50+
}
51+
{
52+
let mrrrrrslice = &mut &mut &mut &mut mrslice;
53+
foo_mrslice(mrrrrrslice.as_mut());
54+
foo_mrslice(mrrrrrslice);
55+
foo_rslice(mrrrrrslice.as_ref());
56+
foo_rslice(mrrrrrslice);
57+
}
58+
foo_rrrrmr((&&&&MoreRef).as_ref());
59+
60+
generic_not_ok(mrslice);
61+
generic_ok(mrslice);
62+
}
63+
64+
fn ok() {
65+
let string = "hello".to_owned();
66+
let mut arr = [1,2,3];
67+
let mut vec = vec![1,2,3];
68+
69+
{
70+
foo_rstr(string.as_ref());
71+
foo_rslice(arr.as_ref());
72+
foo_rslice(vec.as_ref());
73+
}
74+
{
75+
foo_mrslice(arr.as_mut());
76+
foo_mrslice(vec.as_mut());
77+
}
78+
79+
{
80+
let rrrrstring = &&&&string;
81+
let rrrrarr = &&&&arr;
82+
let rrrrvec = &&&&vec;
83+
foo_rstr(rrrrstring.as_ref());
84+
foo_rslice(rrrrarr.as_ref());
85+
foo_rslice(rrrrvec.as_ref());
86+
}
87+
{
88+
let mrrrrarr = &mut &mut &mut &mut arr;
89+
let mrrrrvec = &mut &mut &mut &mut vec;
90+
foo_mrslice(mrrrrarr.as_mut());
91+
foo_mrslice(mrrrrvec.as_mut());
92+
}
93+
FakeAsRef.as_ref();
94+
foo_rrrrmr(MoreRef.as_ref());
95+
96+
generic_not_ok(arr.as_mut());
97+
generic_ok(&mut arr);
98+
}
99+
100+
fn foo_mrt<T: Debug + ?Sized>(t: &mut T) { println!("{:?}", t); }
101+
fn foo_rt<T: Debug + ?Sized>(t: &T) { println!("{:?}", t); }
102+
103+
fn generic_not_ok<T: AsMut<T> + AsRef<T> + Debug + ?Sized>(mrt: &mut T) {
104+
foo_mrt(mrt.as_mut());
105+
foo_mrt(mrt);
106+
foo_rt(mrt.as_ref());
107+
foo_rt(mrt);
108+
}
109+
110+
fn generic_ok<U: AsMut<T> + AsRef<T> + ?Sized, T: Debug + ?Sized>(mru: &mut U) {
111+
foo_mrt(mru.as_mut());
112+
foo_rt(mru.as_ref());
113+
}
114+
115+
fn main() {
116+
not_ok();
117+
ok();
118+
}

tests/ui/useless_asref.stderr

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
error: this call to `as_ref` does nothing
2+
--> $DIR/useless_asref.rs:31:18
3+
|
4+
31 | foo_rstr(rstr.as_ref());
5+
| ^^^^^^^^^^^^^ help: try this: `rstr`
6+
|
7+
note: lint level defined here
8+
--> $DIR/useless_asref.rs:1:9
9+
|
10+
1 | #![deny(useless_asref)]
11+
| ^^^^^^^^^^^^^
12+
13+
error: this call to `as_ref` does nothing
14+
--> $DIR/useless_asref.rs:33:20
15+
|
16+
33 | foo_rslice(rslice.as_ref());
17+
| ^^^^^^^^^^^^^^^ help: try this: `rslice`
18+
19+
error: this call to `as_mut` does nothing
20+
--> $DIR/useless_asref.rs:37:21
21+
|
22+
37 | foo_mrslice(mrslice.as_mut());
23+
| ^^^^^^^^^^^^^^^^ help: try this: `mrslice`
24+
25+
error: this call to `as_ref` does nothing
26+
--> $DIR/useless_asref.rs:39:20
27+
|
28+
39 | foo_rslice(mrslice.as_ref());
29+
| ^^^^^^^^^^^^^^^^ help: try this: `mrslice`
30+
31+
error: this call to `as_ref` does nothing
32+
--> $DIR/useless_asref.rs:46:20
33+
|
34+
46 | foo_rslice(rrrrrslice.as_ref());
35+
| ^^^^^^^^^^^^^^^^^^^ help: try this: `rrrrrslice`
36+
37+
error: this call to `as_ref` does nothing
38+
--> $DIR/useless_asref.rs:48:18
39+
|
40+
48 | foo_rstr(rrrrrstr.as_ref());
41+
| ^^^^^^^^^^^^^^^^^ help: try this: `rrrrrstr`
42+
43+
error: this call to `as_mut` does nothing
44+
--> $DIR/useless_asref.rs:53:21
45+
|
46+
53 | foo_mrslice(mrrrrrslice.as_mut());
47+
| ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice`
48+
49+
error: this call to `as_ref` does nothing
50+
--> $DIR/useless_asref.rs:55:20
51+
|
52+
55 | foo_rslice(mrrrrrslice.as_ref());
53+
| ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice`
54+
55+
error: this call to `as_ref` does nothing
56+
--> $DIR/useless_asref.rs:58:16
57+
|
58+
58 | foo_rrrrmr((&&&&MoreRef).as_ref());
59+
| ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(&&&&MoreRef)`
60+
61+
error: this call to `as_mut` does nothing
62+
--> $DIR/useless_asref.rs:104:13
63+
|
64+
104 | foo_mrt(mrt.as_mut());
65+
| ^^^^^^^^^^^^ help: try this: `mrt`
66+
67+
error: this call to `as_ref` does nothing
68+
--> $DIR/useless_asref.rs:106:12
69+
|
70+
106 | foo_rt(mrt.as_ref());
71+
| ^^^^^^^^^^^^ help: try this: `mrt`
72+

0 commit comments

Comments
 (0)