Skip to content

Commit 9989ccf

Browse files
tommilliganThibsG
authored andcommitted
Working basic dereference clip
1 parent 610bcea commit 9989ccf

File tree

4 files changed

+187
-1
lines changed

4 files changed

+187
-1
lines changed

clippy_lints/src/dereference.rs

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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 crate::utils::{in_macro, span_lint_and_sugg};
5+
use if_chain::if_chain;
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.unwrap().deref();
19+
/// ```
20+
declare_clippy_lint! {
21+
pub EXPLICIT_DEREF_METHOD,
22+
pedantic,
23+
"Explicit use of deref or deref_mut method while not in a method chain."
24+
}
25+
26+
pub struct Pass;
27+
28+
impl LintPass for Pass {
29+
fn get_lints(&self) -> LintArray {
30+
lint_array!(EXPLICIT_DEREF_METHOD)
31+
}
32+
}
33+
34+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
35+
fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr) {
36+
if in_macro(expr.span) {
37+
return;
38+
}
39+
40+
if_chain! {
41+
// if this is a method call
42+
if let ExprKind::MethodCall(ref method_name, _, ref args) = &expr.node;
43+
// on a Path (i.e. a variable/name, not another method)
44+
if let ExprKind::Path(QPath::Resolved(None, path)) = &args[0].node;
45+
then {
46+
let name = method_name.ident.as_str();
47+
// alter help slightly to account for _mut
48+
match &*name {
49+
"deref" => {
50+
span_lint_and_sugg(
51+
cx,
52+
EXPLICIT_DEREF_METHOD,
53+
expr.span,
54+
"explicit deref method call",
55+
"try this",
56+
format!("&*{}", path),
57+
);
58+
},
59+
"deref_mut" => {
60+
span_lint_and_sugg(
61+
cx,
62+
EXPLICIT_DEREF_METHOD,
63+
expr.span,
64+
"explicit deref_mut method call",
65+
"try this",
66+
format!("&mut *{}", path),
67+
);
68+
},
69+
_ => ()
70+
};
71+
}
72+
}
73+
}
74+
}

clippy_lints/src/lib.rs

+6-1
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),
@@ -1059,6 +1062,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10591062
LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION),
10601063
LintId::of(&copy_iterator::COPY_ITERATOR),
10611064
LintId::of(&default_trait_access::DEFAULT_TRAIT_ACCESS),
1065+
LintId::of(&dereference::EXPLICIT_DEREF_METHOD),
10621066
LintId::of(&derive::EXPL_IMPL_CLONE_ON_COPY),
10631067
LintId::of(&doc::DOC_MARKDOWN),
10641068
LintId::of(&doc::MISSING_ERRORS_DOC),
@@ -1143,6 +1147,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
11431147
LintId::of(&comparison_chain::COMPARISON_CHAIN),
11441148
LintId::of(&copies::IFS_SAME_COND),
11451149
LintId::of(&copies::IF_SAME_THEN_ELSE),
1150+
LintId::of(&dereference::EXPLICIT_DEREF_METHOD),
11461151
LintId::of(&derive::DERIVE_HASH_XOR_EQ),
11471152
LintId::of(&doc::MISSING_SAFETY_DOC),
11481153
LintId::of(&doc::NEEDLESS_DOCTEST_MAIN),

tests/ui/dereference.rs

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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::explicit_deref_method)]
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+
51+
{
52+
macro_rules! expr_deref { ($body:expr) => { $body.deref() } }
53+
let b: &str = expr_deref!(a);
54+
}
55+
}

tests/ui/dereference.stderr

+52
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::explicit-deref-method` 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)