Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(lint): deref and deref_mut method explicit calls #3258

Closed
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,7 @@ All notable changes to this project will be documented in this file.
[`expect_fun_call`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#expect_fun_call
[`expl_impl_clone_on_copy`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy
[`explicit_counter_loop`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#explicit_counter_loop
[`explicit_deref_method`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#explicit_deref_method
[`explicit_into_iter_loop`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#explicit_into_iter_loop
[`explicit_iter_loop`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#explicit_iter_loop
[`explicit_write`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#explicit_write
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ We are currently in the process of discussing Clippy 1.0 via the RFC process in

A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.

[There are 279 lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html)
[There are 280 lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html)

We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:

Expand Down
71 changes: 71 additions & 0 deletions clippy_lints/src/dereference.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use crate::rustc::hir::{Expr, ExprKind, QPath};
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use crate::rustc::{declare_tool_lint, lint_array};
use if_chain::if_chain;
use crate::utils::span_lint_and_sugg;

/// **What it does:** Checks for explicit deref() or deref_mut() method calls.
///
/// **Why is this bad?** Derefencing by &*x or &mut *x is clearer and more concise,
/// when not part of a method chain.
///
/// **Example:**
/// ```rust
/// let b = a.deref();
/// let c = a.deref_mut();
///
/// // excludes
/// let e = d.deref().unwrap();
Copy link
Member

@flip1995 flip1995 Oct 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does your lint exclude these cases? Since _.deref().len() gets linted, I have a feeling, that also _.unwrap() will get linted.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quite correct - should be the other way round. d.unwrap().deref() would not be linted.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also add a test for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah, I missed that, thanks!

/// let f = a.deref().unwrap();
/// ```
declare_clippy_lint! {
pub EXPLICIT_DEREF_METHOD,
pedantic,
"Explicit use of deref or deref_mut method while not in a method chain."
}

pub struct Pass;

impl LintPass for Pass {
fn get_lints(&self) -> LintArray {
lint_array!(EXPLICIT_DEREF_METHOD)
}
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr) {
if_chain! {
// if this is a method call
if let ExprKind::MethodCall(ref method_name, _, ref args) = &expr.node;
// on a Path (i.e. a variable/name, not another method)
if let ExprKind::Path(QPath::Resolved(None, path)) = &args[0].node;
then {
let name = method_name.ident.as_str();
// alter help slightly to account for _mut
match &*name {
"deref" => {
span_lint_and_sugg(
cx,
EXPLICIT_DEREF_METHOD,
expr.span,
"explicit deref method call",
"try this",
format!("&*{}", path),
);
},
"deref_mut" => {
span_lint_and_sugg(
cx,
EXPLICIT_DEREF_METHOD,
expr.span,
"explicit deref_mut method call",
"try this",
format!("&mut *{}", path),
);
},
_ => ()
};
}
}
}
}
3 changes: 3 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pub mod copies;
pub mod copy_iterator;
pub mod cyclomatic_complexity;
pub mod default_trait_access;
pub mod dereference;
pub mod derive;
pub mod doc;
pub mod double_comparison;
Expand Down Expand Up @@ -356,6 +357,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
reg.register_early_lint_pass(box misc_early::MiscEarly);
reg.register_late_lint_pass(box panic_unimplemented::Pass);
reg.register_late_lint_pass(box strings::StringLitAsBytes);
reg.register_late_lint_pass(box dereference::Pass);
reg.register_late_lint_pass(box derive::Derive);
reg.register_late_lint_pass(box types::CharLitAsU8);
reg.register_late_lint_pass(box vec::Pass);
Expand Down Expand Up @@ -460,6 +462,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
copies::MATCH_SAME_ARMS,
copy_iterator::COPY_ITERATOR,
default_trait_access::DEFAULT_TRAIT_ACCESS,
dereference::EXPLICIT_DEREF_METHOD,
derive::EXPL_IMPL_CLONE_ON_COPY,
doc::DOC_MARKDOWN,
empty_enum::EMPTY_ENUM,
Expand Down
50 changes: 50 additions & 0 deletions tests/ui/dereference.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#![feature(tool_lints)]

use std::ops::{Deref, DerefMut};

#[allow(clippy::many_single_char_names, clippy::clone_double_ref)]
#[allow(unused_variables)]
#[warn(clippy::explicit_deref_method)]
fn main() {
let a: &mut String = &mut String::from("foo");

// these should require linting
{
let b: &str = a.deref();
}

{
let b: &mut str = a.deref_mut();
}

{
let b: String = a.deref().clone();
}

{
let b: usize = a.deref_mut().len();
}

{
let b: &usize = &a.deref().len();
}

{
// only first deref should get linted here
let b: &str = a.deref().deref();
}

{
// both derefs should get linted here
let b: String = format!("{}, {}", a.deref(), a.deref());
}

// these should not require linting
{
let b: &str = &*a;
}

{
let b: &mut str = &mut *a;
}
}
52 changes: 52 additions & 0 deletions tests/ui/dereference.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
error: explicit deref method call
--> $DIR/dereference.rs:13:23
|
13 | let b: &str = a.deref();
| ^^^^^^^^^ help: try this: `&*a`
|
= note: `-D clippy::explicit-deref-method` implied by `-D warnings`

error: explicit deref_mut method call
--> $DIR/dereference.rs:17:27
|
17 | let b: &mut str = a.deref_mut();
| ^^^^^^^^^^^^^ help: try this: `&mut *a`

error: explicit deref method call
--> $DIR/dereference.rs:21:25
|
21 | let b: String = a.deref().clone();
| ^^^^^^^^^ help: try this: `&*a`

error: explicit deref_mut method call
--> $DIR/dereference.rs:25:24
|
25 | let b: usize = a.deref_mut().len();
| ^^^^^^^^^^^^^ help: try this: `&mut *a`

error: explicit deref method call
--> $DIR/dereference.rs:29:26
|
29 | let b: &usize = &a.deref().len();
| ^^^^^^^^^ help: try this: `&*a`

error: explicit deref method call
--> $DIR/dereference.rs:34:23
|
34 | let b: &str = a.deref().deref();
| ^^^^^^^^^ help: try this: `&*a`

error: explicit deref method call
--> $DIR/dereference.rs:39:43
|
39 | let b: String = format!("{}, {}", a.deref(), a.deref());
| ^^^^^^^^^ help: try this: `&*a`

error: explicit deref method call
--> $DIR/dereference.rs:39:54
|
39 | let b: String = format!("{}, {}", a.deref(), a.deref());
| ^^^^^^^^^ help: try this: `&*a`

error: aborting due to 8 previous errors