Skip to content

Commit e1523fb

Browse files
tommilliganThibsG
authored andcommitted
Working basic dereference clip
1 parent 2734e4e commit e1523fb

File tree

4 files changed

+178
-1
lines changed

4 files changed

+178
-1
lines changed

clippy_lints/src/dereference.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
use crate::rustc::hir::{Expr, ExprKind, QPath};
2+
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
3+
use crate::rustc::{declare_tool_lint, lint_array};
4+
use if_chain::if_chain;
5+
use crate::utils::span_lint_and_sugg;
6+
7+
/// **What it does:** Checks for explicit deref() or deref_mut() method calls.
8+
///
9+
/// **Why is this bad?** Derefencing by &*x or &mut *x is clearer and more concise,
10+
/// when not part of a method chain.
11+
///
12+
/// **Example:**
13+
/// ```rust
14+
/// let b = a.deref();
15+
/// let c = a.deref_mut();
16+
///
17+
/// // excludes
18+
/// let e = d.deref().unwrap();
19+
/// let f = a.deref().unwrap();
20+
/// ```
21+
declare_clippy_lint! {
22+
pub DEREF_METHOD_EXPLICIT,
23+
complexity,
24+
"Explicit use of deref or deref_mut method while not in a method chain."
25+
}
26+
27+
pub struct Pass;
28+
29+
impl LintPass for Pass {
30+
fn get_lints(&self) -> LintArray {
31+
lint_array!(DEREF_METHOD_EXPLICIT)
32+
}
33+
}
34+
35+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
36+
fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr) {
37+
if_chain! {
38+
// if this is a method call
39+
if let ExprKind::MethodCall(ref method_name, _, ref args) = &expr.node;
40+
// on a Path (i.e. a variable/name, not another method)
41+
if let ExprKind::Path(QPath::Resolved(None, path)) = &args[0].node;
42+
then {
43+
let name = method_name.ident.as_str();
44+
// alter help slightly to account for _mut
45+
match &*name {
46+
"deref" => {
47+
span_lint_and_sugg(
48+
cx,
49+
DEREF_METHOD_EXPLICIT,
50+
expr.span,
51+
"explicit deref method call",
52+
"try this",
53+
format!("&*{}", path),
54+
);
55+
},
56+
"deref_mut" => {
57+
span_lint_and_sugg(
58+
cx,
59+
DEREF_METHOD_EXPLICIT,
60+
expr.span,
61+
"explicit deref_mut method call",
62+
"try this",
63+
format!("&mut *{}", path),
64+
);
65+
},
66+
_ => ()
67+
};
68+
}
69+
}
70+
}
71+
}

clippy_lints/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ pub mod copies;
186186
pub mod copy_iterator;
187187
pub mod dbg_macro;
188188
pub mod default_trait_access;
189+
pub mod dereference;
189190
pub mod derive;
190191
pub mod doc;
191192
pub mod double_comparison;
@@ -383,7 +384,7 @@ pub fn read_conf(args: &[syntax::ast::NestedMetaItem], sess: &Session) -> Conf {
383384
}
384385

385386
conf
386-
},
387+
}
387388
Err((err, span)) => {
388389
sess.struct_span_err(span, err)
389390
.span_note(span, "Clippy will use default configuration")
@@ -501,6 +502,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
501502
&copy_iterator::COPY_ITERATOR,
502503
&dbg_macro::DBG_MACRO,
503504
&default_trait_access::DEFAULT_TRAIT_ACCESS,
505+
&dereference::DEREF_METHOD_EXPLICIT,
504506
&derive::DERIVE_HASH_XOR_EQ,
505507
&derive::EXPL_IMPL_CLONE_ON_COPY,
506508
&doc::DOC_MARKDOWN,
@@ -1011,6 +1013,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10111013
store.register_early_pass(move || box excessive_bools::ExcessiveBools::new(max_struct_bools, max_fn_params_bools));
10121014
store.register_early_pass(|| box option_env_unwrap::OptionEnvUnwrap);
10131015
store.register_late_pass(|| box wildcard_imports::WildcardImports);
1016+
store.register_late_pass(|| box dereference::DerefMethodExplicit);
10141017

10151018
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
10161019
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -1143,6 +1146,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
11431146
LintId::of(&comparison_chain::COMPARISON_CHAIN),
11441147
LintId::of(&copies::IFS_SAME_COND),
11451148
LintId::of(&copies::IF_SAME_THEN_ELSE),
1149+
LintId::of(&dereference::DEREF_METHOD_EXPLICIT),
11461150
LintId::of(&derive::DERIVE_HASH_XOR_EQ),
11471151
LintId::of(&doc::MISSING_SAFETY_DOC),
11481152
LintId::of(&doc::NEEDLESS_DOCTEST_MAIN),

tests/ui/dereference.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#![feature(tool_lints)]
2+
3+
use std::ops::{Deref, DerefMut};
4+
5+
#[allow(clippy::many_single_char_names, clippy::clone_double_ref)]
6+
#[allow(unused_variables)]
7+
#[warn(clippy::deref_method_explicit)]
8+
fn main() {
9+
let a: &mut String = &mut String::from("foo");
10+
11+
// these should require linting
12+
{
13+
let b: &str = a.deref();
14+
}
15+
16+
{
17+
let b: &mut str = a.deref_mut();
18+
}
19+
20+
{
21+
let b: String = a.deref().clone();
22+
}
23+
24+
{
25+
let b: usize = a.deref_mut().len();
26+
}
27+
28+
{
29+
let b: &usize = &a.deref().len();
30+
}
31+
32+
{
33+
// only first deref should get linted here
34+
let b: &str = a.deref().deref();
35+
}
36+
37+
{
38+
// both derefs should get linted here
39+
let b: String = format!("{}, {}", a.deref(), a.deref());
40+
}
41+
42+
// these should not require linting
43+
{
44+
let b: &str = &*a;
45+
}
46+
47+
{
48+
let b: &mut str = &mut *a;
49+
}
50+
}

tests/ui/dereference.stderr

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
error: explicit deref method call
2+
--> $DIR/dereference.rs:13:23
3+
|
4+
13 | let b: &str = a.deref();
5+
| ^^^^^^^^^ help: try this: `&*a`
6+
|
7+
= note: `-D clippy::deref-method-explicit` implied by `-D warnings`
8+
9+
error: explicit deref_mut method call
10+
--> $DIR/dereference.rs:17:27
11+
|
12+
17 | let b: &mut str = a.deref_mut();
13+
| ^^^^^^^^^^^^^ help: try this: `&mut *a`
14+
15+
error: explicit deref method call
16+
--> $DIR/dereference.rs:21:25
17+
|
18+
21 | let b: String = a.deref().clone();
19+
| ^^^^^^^^^ help: try this: `&*a`
20+
21+
error: explicit deref_mut method call
22+
--> $DIR/dereference.rs:25:24
23+
|
24+
25 | let b: usize = a.deref_mut().len();
25+
| ^^^^^^^^^^^^^ help: try this: `&mut *a`
26+
27+
error: explicit deref method call
28+
--> $DIR/dereference.rs:29:26
29+
|
30+
29 | let b: &usize = &a.deref().len();
31+
| ^^^^^^^^^ help: try this: `&*a`
32+
33+
error: explicit deref method call
34+
--> $DIR/dereference.rs:34:23
35+
|
36+
34 | let b: &str = a.deref().deref();
37+
| ^^^^^^^^^ help: try this: `&*a`
38+
39+
error: explicit deref method call
40+
--> $DIR/dereference.rs:39:43
41+
|
42+
39 | let b: String = format!("{}, {}", a.deref(), a.deref());
43+
| ^^^^^^^^^ help: try this: `&*a`
44+
45+
error: explicit deref method call
46+
--> $DIR/dereference.rs:39:54
47+
|
48+
39 | let b: String = format!("{}, {}", a.deref(), a.deref());
49+
| ^^^^^^^^^ help: try this: `&*a`
50+
51+
error: aborting due to 8 previous errors
52+

0 commit comments

Comments
 (0)