Skip to content

Commit 3481bf4

Browse files
authored
Rollup merge of #5226 - ThibsG:DerefExplicit1566, r=flip1995
Add lint for explicit deref and deref_mut method calls This PR adds the lint `explicit_deref_method` that suggests replacing `deref()` and `deref_mut()` with `&*a` and `&mut *a`. It doesn't lint inside macros. This PR is the continuation of #3258. changelog: Add lint `explicit_deref_method`. Fixes: #1566
2 parents a9a4c8e + 3c2bbcf commit 3481bf4

File tree

7 files changed

+381
-0
lines changed

7 files changed

+381
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1256,6 +1256,7 @@ Released 2018-09-13
12561256
[`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call
12571257
[`expl_impl_clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy
12581258
[`explicit_counter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_counter_loop
1259+
[`explicit_deref_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods
12591260
[`explicit_into_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_into_iter_loop
12601261
[`explicit_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop
12611262
[`explicit_write`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_write

clippy_lints/src/dereference.rs

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use crate::utils::{get_parent_expr, implements_trait, snippet, span_lint_and_sugg};
2+
use if_chain::if_chain;
3+
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX, PREC_PREFIX};
4+
use rustc_errors::Applicability;
5+
use rustc_hir::{Expr, ExprKind};
6+
use rustc_lint::{LateContext, LateLintPass};
7+
use rustc_session::{declare_lint_pass, declare_tool_lint};
8+
use rustc_span::source_map::Span;
9+
10+
declare_clippy_lint! {
11+
/// **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls.
12+
///
13+
/// **Why is this bad?** Derefencing by `&*x` or `&mut *x` is clearer and more concise,
14+
/// when not part of a method chain.
15+
///
16+
/// **Example:**
17+
/// ```rust
18+
/// use std::ops::Deref;
19+
/// let a: &mut String = &mut String::from("foo");
20+
/// let b: &str = a.deref();
21+
/// ```
22+
/// Could be written as:
23+
/// ```rust
24+
/// let a: &mut String = &mut String::from("foo");
25+
/// let b = &*a;
26+
/// ```
27+
///
28+
/// This lint excludes
29+
/// ```rust,ignore
30+
/// let _ = d.unwrap().deref();
31+
/// ```
32+
pub EXPLICIT_DEREF_METHODS,
33+
pedantic,
34+
"Explicit use of deref or deref_mut method while not in a method chain."
35+
}
36+
37+
declare_lint_pass!(Dereferencing => [
38+
EXPLICIT_DEREF_METHODS
39+
]);
40+
41+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Dereferencing {
42+
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
43+
if_chain! {
44+
if !expr.span.from_expansion();
45+
if let ExprKind::MethodCall(ref method_name, _, ref args) = &expr.kind;
46+
if args.len() == 1;
47+
48+
then {
49+
if let Some(parent_expr) = get_parent_expr(cx, expr) {
50+
// Check if we have the whole call chain here
51+
if let ExprKind::MethodCall(..) = parent_expr.kind {
52+
return;
53+
}
54+
// Check for Expr that we don't want to be linted
55+
let precedence = parent_expr.precedence();
56+
match precedence {
57+
// Lint a Call is ok though
58+
ExprPrecedence::Call | ExprPrecedence::AddrOf => (),
59+
_ => {
60+
if precedence.order() >= PREC_PREFIX && precedence.order() <= PREC_POSTFIX {
61+
return;
62+
}
63+
}
64+
}
65+
}
66+
let name = method_name.ident.as_str();
67+
lint_deref(cx, &*name, &args[0], args[0].span, expr.span);
68+
}
69+
}
70+
}
71+
}
72+
73+
fn lint_deref(cx: &LateContext<'_, '_>, method_name: &str, call_expr: &Expr<'_>, var_span: Span, expr_span: Span) {
74+
match method_name {
75+
"deref" => {
76+
if cx
77+
.tcx
78+
.lang_items()
79+
.deref_trait()
80+
.map_or(false, |id| implements_trait(cx, cx.tables.expr_ty(&call_expr), id, &[]))
81+
{
82+
span_lint_and_sugg(
83+
cx,
84+
EXPLICIT_DEREF_METHODS,
85+
expr_span,
86+
"explicit deref method call",
87+
"try this",
88+
format!("&*{}", &snippet(cx, var_span, "..")),
89+
Applicability::MachineApplicable,
90+
);
91+
}
92+
},
93+
"deref_mut" => {
94+
if cx
95+
.tcx
96+
.lang_items()
97+
.deref_mut_trait()
98+
.map_or(false, |id| implements_trait(cx, cx.tables.expr_ty(&call_expr), id, &[]))
99+
{
100+
span_lint_and_sugg(
101+
cx,
102+
EXPLICIT_DEREF_METHODS,
103+
expr_span,
104+
"explicit deref_mut method call",
105+
"try this",
106+
format!("&mut *{}", &snippet(cx, var_span, "..")),
107+
Applicability::MachineApplicable,
108+
);
109+
}
110+
},
111+
_ => (),
112+
}
113+
}

clippy_lints/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ mod copies;
191191
mod copy_iterator;
192192
mod dbg_macro;
193193
mod default_trait_access;
194+
mod dereference;
194195
mod derive;
195196
mod doc;
196197
mod double_comparison;
@@ -513,6 +514,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
513514
&copy_iterator::COPY_ITERATOR,
514515
&dbg_macro::DBG_MACRO,
515516
&default_trait_access::DEFAULT_TRAIT_ACCESS,
517+
&dereference::EXPLICIT_DEREF_METHODS,
516518
&derive::DERIVE_HASH_XOR_EQ,
517519
&derive::EXPL_IMPL_CLONE_ON_COPY,
518520
&doc::DOC_MARKDOWN,
@@ -1039,6 +1041,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10391041
store.register_late_pass(|| box verbose_file_reads::VerboseFileReads);
10401042
store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default());
10411043
store.register_late_pass(|| box unnamed_address::UnnamedAddress);
1044+
store.register_late_pass(|| box dereference::Dereferencing);
10421045

10431046
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
10441047
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -1089,6 +1092,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10891092
LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION),
10901093
LintId::of(&copy_iterator::COPY_ITERATOR),
10911094
LintId::of(&default_trait_access::DEFAULT_TRAIT_ACCESS),
1095+
LintId::of(&dereference::EXPLICIT_DEREF_METHODS),
10921096
LintId::of(&derive::EXPL_IMPL_CLONE_ON_COPY),
10931097
LintId::of(&doc::DOC_MARKDOWN),
10941098
LintId::of(&doc::MISSING_ERRORS_DOC),

src/lintlist/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
528528
deprecation: None,
529529
module: "loops",
530530
},
531+
Lint {
532+
name: "explicit_deref_methods",
533+
group: "pedantic",
534+
desc: "Explicit use of deref or deref_mut method while not in a method chain.",
535+
deprecation: None,
536+
module: "dereference",
537+
},
531538
Lint {
532539
name: "explicit_into_iter_loop",
533540
group: "pedantic",

tests/ui/dereference.fixed

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// run-rustfix
2+
3+
#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)]
4+
#![warn(clippy::explicit_deref_methods)]
5+
6+
use std::ops::{Deref, DerefMut};
7+
8+
fn concat(deref_str: &str) -> String {
9+
format!("{}bar", deref_str)
10+
}
11+
12+
fn just_return(deref_str: &str) -> &str {
13+
deref_str
14+
}
15+
16+
struct CustomVec(Vec<u8>);
17+
impl Deref for CustomVec {
18+
type Target = Vec<u8>;
19+
20+
fn deref(&self) -> &Vec<u8> {
21+
&self.0
22+
}
23+
}
24+
25+
fn main() {
26+
let a: &mut String = &mut String::from("foo");
27+
28+
// these should require linting
29+
30+
let b: &str = &*a;
31+
32+
let b: &mut str = &mut *a;
33+
34+
// both derefs should get linted here
35+
let b: String = format!("{}, {}", &*a, &*a);
36+
37+
println!("{}", &*a);
38+
39+
#[allow(clippy::match_single_binding)]
40+
match &*a {
41+
_ => (),
42+
}
43+
44+
let b: String = concat(&*a);
45+
46+
let b = &*just_return(a);
47+
48+
let b: String = concat(&*just_return(a));
49+
50+
let b: &str = &*a.deref();
51+
52+
let opt_a = Some(a.clone());
53+
let b = &*opt_a.unwrap();
54+
55+
// following should not require linting
56+
57+
let cv = CustomVec(vec![0, 42]);
58+
let c = cv.deref()[0];
59+
60+
let b: &str = &*a.deref();
61+
62+
let b: String = a.deref().clone();
63+
64+
let b: usize = a.deref_mut().len();
65+
66+
let b: &usize = &a.deref().len();
67+
68+
let b: &str = &*a;
69+
70+
let b: &mut str = &mut *a;
71+
72+
macro_rules! expr_deref {
73+
($body:expr) => {
74+
$body.deref()
75+
};
76+
}
77+
let b: &str = expr_deref!(a);
78+
79+
// The struct does not implement Deref trait
80+
#[derive(Copy, Clone)]
81+
struct NoLint(u32);
82+
impl NoLint {
83+
pub fn deref(self) -> u32 {
84+
self.0
85+
}
86+
pub fn deref_mut(self) -> u32 {
87+
self.0
88+
}
89+
}
90+
let no_lint = NoLint(42);
91+
let b = no_lint.deref();
92+
let b = no_lint.deref_mut();
93+
}

tests/ui/dereference.rs

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// run-rustfix
2+
3+
#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)]
4+
#![warn(clippy::explicit_deref_methods)]
5+
6+
use std::ops::{Deref, DerefMut};
7+
8+
fn concat(deref_str: &str) -> String {
9+
format!("{}bar", deref_str)
10+
}
11+
12+
fn just_return(deref_str: &str) -> &str {
13+
deref_str
14+
}
15+
16+
struct CustomVec(Vec<u8>);
17+
impl Deref for CustomVec {
18+
type Target = Vec<u8>;
19+
20+
fn deref(&self) -> &Vec<u8> {
21+
&self.0
22+
}
23+
}
24+
25+
fn main() {
26+
let a: &mut String = &mut String::from("foo");
27+
28+
// these should require linting
29+
30+
let b: &str = a.deref();
31+
32+
let b: &mut str = a.deref_mut();
33+
34+
// both derefs should get linted here
35+
let b: String = format!("{}, {}", a.deref(), a.deref());
36+
37+
println!("{}", a.deref());
38+
39+
#[allow(clippy::match_single_binding)]
40+
match a.deref() {
41+
_ => (),
42+
}
43+
44+
let b: String = concat(a.deref());
45+
46+
let b = just_return(a).deref();
47+
48+
let b: String = concat(just_return(a).deref());
49+
50+
let b: &str = a.deref().deref();
51+
52+
let opt_a = Some(a.clone());
53+
let b = opt_a.unwrap().deref();
54+
55+
// following should not require linting
56+
57+
let cv = CustomVec(vec![0, 42]);
58+
let c = cv.deref()[0];
59+
60+
let b: &str = &*a.deref();
61+
62+
let b: String = a.deref().clone();
63+
64+
let b: usize = a.deref_mut().len();
65+
66+
let b: &usize = &a.deref().len();
67+
68+
let b: &str = &*a;
69+
70+
let b: &mut str = &mut *a;
71+
72+
macro_rules! expr_deref {
73+
($body:expr) => {
74+
$body.deref()
75+
};
76+
}
77+
let b: &str = expr_deref!(a);
78+
79+
// The struct does not implement Deref trait
80+
#[derive(Copy, Clone)]
81+
struct NoLint(u32);
82+
impl NoLint {
83+
pub fn deref(self) -> u32 {
84+
self.0
85+
}
86+
pub fn deref_mut(self) -> u32 {
87+
self.0
88+
}
89+
}
90+
let no_lint = NoLint(42);
91+
let b = no_lint.deref();
92+
let b = no_lint.deref_mut();
93+
}

0 commit comments

Comments
 (0)