Skip to content

Implement 'Re-implementing PartialEq::ne' lint #1307

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

Merged
merged 4 commits into from
Oct 31, 2016
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -319,6 +319,7 @@ All notable changes to this project will be documented in this file.
[`out_of_bounds_indexing`]: https://github.com/Manishearth/rust-clippy/wiki#out_of_bounds_indexing
[`overflow_check_conditional`]: https://github.com/Manishearth/rust-clippy/wiki#overflow_check_conditional
[`panic_params`]: https://github.com/Manishearth/rust-clippy/wiki#panic_params
[`partialeq_ne_impl`]: https://github.com/Manishearth/rust-clippy/wiki#partialeq_ne_impl
[`precedence`]: https://github.com/Manishearth/rust-clippy/wiki#precedence
[`print_stdout`]: https://github.com/Manishearth/rust-clippy/wiki#print_stdout
[`print_with_newline`]: https://github.com/Manishearth/rust-clippy/wiki#print_with_newline
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ You can check out this great service at [clippy.bashy.io](https://clippy.bashy.i

## Lints

There are 175 lints included in this crate:
There are 176 lints included in this crate:

name | default | triggers on
-----------------------------------------------------------------------------------------------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -293,6 +293,7 @@ name
[out_of_bounds_indexing](https://github.com/Manishearth/rust-clippy/wiki#out_of_bounds_indexing) | deny | out of bounds constant indexing
[overflow_check_conditional](https://github.com/Manishearth/rust-clippy/wiki#overflow_check_conditional) | warn | overflow checks inspired by C which are likely to panic
[panic_params](https://github.com/Manishearth/rust-clippy/wiki#panic_params) | warn | missing parameters in `panic!` calls
[partialeq_ne_impl](https://github.com/Manishearth/rust-clippy/wiki#partialeq_ne_impl) | warn | re-implementing `PartialEq::ne`
[precedence](https://github.com/Manishearth/rust-clippy/wiki#precedence) | warn | operations where precedence may be unclear
[print_stdout](https://github.com/Manishearth/rust-clippy/wiki#print_stdout) | allow | printing on stdout
[print_with_newline](https://github.com/Manishearth/rust-clippy/wiki#print_with_newline) | warn | using `print!()` with a format string that ends in a newline
Expand Down
16 changes: 3 additions & 13 deletions clippy_lints/src/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ use rustc::ty::subst::Subst;
use rustc::ty::TypeVariants;
use rustc::ty;
use rustc::hir::*;
use syntax::ast::{Attribute, MetaItemKind};
use syntax::codemap::Span;
use utils::paths;
use utils::{match_path, span_lint_and_then};
use utils::{is_automatically_derived, match_path, span_lint_and_then};

/// **What it does:** Checks for deriving `Hash` but implementing `PartialEq`
/// explicitly.
Expand Down Expand Up @@ -75,7 +74,7 @@ impl LateLintPass for Derive {
fn check_item(&mut self, cx: &LateContext, item: &Item) {
if let ItemImpl(_, _, _, Some(ref trait_ref), _, _) = item.node {
let ty = cx.tcx.lookup_item_type(cx.tcx.map.local_def_id(item.id)).ty;
let is_automatically_derived = item.attrs.iter().any(is_automatically_derived);
let is_automatically_derived = is_automatically_derived(&*item.attrs);

check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);

Expand All @@ -97,7 +96,7 @@ fn check_hash_peq<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, span: Span, trait_re

// Look for the PartialEq implementations for `ty`
peq_trait_def.for_each_relevant_impl(cx.tcx, ty, |impl_id| {
let peq_is_automatically_derived = cx.tcx.get_attrs(impl_id).iter().any(is_automatically_derived);
let peq_is_automatically_derived = is_automatically_derived(&cx.tcx.get_attrs(impl_id));

if peq_is_automatically_derived == hash_is_automatically_derived {
return;
Expand Down Expand Up @@ -174,12 +173,3 @@ fn check_copy_clone<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, item: &Item, trait_ref
});
}
}

/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d implementations have.
fn is_automatically_derived(attr: &Attribute) -> bool {
if let MetaItemKind::Word(ref word) = attr.node.value.node {
word == &"automatically_derived"
} else {
false
}
}
3 changes: 3 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ pub mod ok_if_let;
pub mod open_options;
pub mod overflow_check_conditional;
pub mod panic;
pub mod partialeq_ne_impl;
pub mod precedence;
pub mod print;
pub mod ptr;
Expand Down Expand Up @@ -259,6 +260,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
reg.register_late_lint_pass(box missing_doc::MissingDoc::new());
reg.register_late_lint_pass(box ok_if_let::Pass);
reg.register_late_lint_pass(box if_let_redundant_pattern_matching::Pass);
reg.register_late_lint_pass(box partialeq_ne_impl::Pass);

reg.register_lint_group("clippy_restrictions", vec![
arithmetic::FLOAT_ARITHMETIC,
Expand Down Expand Up @@ -415,6 +417,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
open_options::NONSENSICAL_OPEN_OPTIONS,
overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
panic::PANIC_PARAMS,
partialeq_ne_impl::PARTIALEQ_NE_IMPL,
precedence::PRECEDENCE,
print::PRINT_WITH_NEWLINE,
ptr::CMP_NULL,
Expand Down
55 changes: 55 additions & 0 deletions clippy_lints/src/partialeq_ne_impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use rustc::lint::*;
use rustc::hir::*;
use utils::{is_automatically_derived, span_lint};

/// **What it does:** Checks for manual re-implementations of `PartialEq::ne`.
///
/// **Why is this bad?** `PartialEq::ne` is required to always return the
/// negated result of `PartialEq::eq`, which is exactly what the default
/// implementation does. Therefore, there should never be any need to
/// re-implement it.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// struct Foo;
///
/// impl PartialEq for Foo {
/// fn eq(&self, other: &Foo) -> bool { ... }
/// fn ne(&self, other: &Foo) -> bool { !(self == other) }
/// }
/// ```
declare_lint! {
pub PARTIALEQ_NE_IMPL,
Warn,
"re-implementing `PartialEq::ne`"
}

#[derive(Clone, Copy)]
pub struct Pass;

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

impl LateLintPass for Pass {
fn check_item(&mut self, cx: &LateContext, item: &Item) {
if_let_chain! {[
let ItemImpl(_, _, _, Some(ref trait_ref), _, ref impl_items) = item.node,
!is_automatically_derived(&*item.attrs),
cx.tcx.expect_def(trait_ref.ref_id).def_id() == cx.tcx.lang_items.eq_trait().unwrap(),
], {
for impl_item in impl_items {
if &*impl_item.name.as_str() == "ne" {
span_lint(cx,
PARTIALEQ_NE_IMPL,
impl_item.span,
"re-implementing `PartialEq::ne` is unnecessary")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This should also give a help message telling the user what to do( remove the impl)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Any suggestion for wording? Maybe just appending ... is unnecessary?

}
}};
}
}
16 changes: 7 additions & 9 deletions clippy_lints/src/utils/inspector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

use rustc::lint::*;
use rustc::hir;
use syntax::ast::{Attribute, MetaItemKind};
use syntax::ast::Attribute;
use syntax::attr;

/// **What it does:** Dumps every ast/hir node which has the `#[clippy_dump]` attribute
///
Expand Down Expand Up @@ -128,10 +129,7 @@ impl LateLintPass for Pass {
}

fn has_attr(attrs: &[Attribute]) -> bool {
attrs.iter().any(|attr| match attr.node.value.node {
MetaItemKind::Word(ref word) => word == "clippy_dump",
_ => false,
})
attr::contains_name(attrs, "clippy_dump")
Copy link
Contributor

Choose a reason for hiding this comment

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

Neat cleanup, didn't know about this function

}

fn print_decl(cx: &LateContext, decl: &hir::Decl) {
Expand Down Expand Up @@ -381,12 +379,12 @@ fn print_item(cx: &LateContext, item: &hir::Item) {
}
},
hir::ItemDefaultImpl(_, ref trait_ref) => {
let trait_did = cx.tcx.map.local_def_id(trait_ref.ref_id);
println!("default impl for `{:?}`", cx.tcx.item_path_str(trait_did));
let trait_did = cx.tcx.expect_def(trait_ref.ref_id).def_id();
println!("default impl for `{}`", cx.tcx.item_path_str(trait_did));
},
hir::ItemImpl(_, _, _, Some(ref trait_ref), _, _) => {
let trait_did = cx.tcx.map.local_def_id(trait_ref.ref_id);
println!("impl of trait `{:?}`", cx.tcx.item_path_str(trait_did));
let trait_did = cx.tcx.expect_def(trait_ref.ref_id).def_id();
println!("impl of trait `{}`", cx.tcx.item_path_str(trait_did));
},
hir::ItemImpl(_, _, _, None, _, _) => {
println!("impl");
Expand Down
6 changes: 6 additions & 0 deletions clippy_lints/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use std::env;
use std::mem;
use std::str::FromStr;
use syntax::ast::{self, LitKind};
use syntax::attr;
use syntax::codemap::{ExpnFormat, ExpnInfo, MultiSpan, Span, DUMMY_SP};
use syntax::errors::DiagnosticBuilder;
use syntax::ptr::P;
Expand Down Expand Up @@ -761,3 +762,8 @@ pub fn is_refutable(cx: &LateContext, pat: &Pat) -> bool {
}
}
}

/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d implementations have.
pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
attr::contains_name(attrs, "automatically_derived")
}
15 changes: 15 additions & 0 deletions tests/compile-fail/partialeq_ne_impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#![feature(plugin)]
#![plugin(clippy)]

#![deny(warnings)]
#![allow(dead_code)]

struct Foo;

impl PartialEq for Foo {
fn eq(&self, _: &Foo) -> bool { true }
fn ne(&self, _: &Foo) -> bool { false }
//~^ ERROR re-implementing `PartialEq::ne` is unnecessary
}

fn main() {}
2 changes: 1 addition & 1 deletion util/dogfood.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/sh
rm -rf target*/*so
cargo build --lib && cp -R target target_recur && cargo rustc -- -Zextra-plugins=clippy -Ltarget_recur/debug -Dclippy_pedantic -Dclippy || exit 1
cargo build --lib && cp -R target target_recur && cargo rustc --lib -- -Zextra-plugins=clippy -Ltarget_recur/debug -Dclippy_pedantic -Dclippy || exit 1
Copy link
Contributor

Choose a reason for hiding this comment

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

Huh, how did this ever work?

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh.... This is dogfood.sh, nevermind.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, seems like nobody's run this ever since cargo-clippy appeared 😄 .

Copy link
Contributor

Choose a reason for hiding this comment

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

Cargo test will actually run a separate implementation of dogfood, so there wasn't any need for that script as far as I knew

rm -rf target_recur