Skip to content

Commit

Permalink
Lint duplicate methods of trait bounds
Browse files Browse the repository at this point in the history
Fixes #5777
  • Loading branch information
wiomoc committed Jul 28, 2020
1 parent f5d429c commit 48900a2
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1723,6 +1723,7 @@ Released 2018-09-13
[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
[`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
[`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg
[`trait_duplication_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds
[`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str
[`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int
[`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool
Expand Down
2 changes: 2 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS,
&temporary_assignment::TEMPORARY_ASSIGNMENT,
&to_digit_is_some::TO_DIGIT_IS_SOME,
&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS,
&trait_bounds::TYPE_REPETITION_IN_BOUNDS,
&transmute::CROSSPOINTER_TRANSMUTE,
&transmute::TRANSMUTE_BYTES_TO_STR,
Expand Down Expand Up @@ -1174,6 +1175,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&ranges::RANGE_PLUS_ONE),
LintId::of(&shadow::SHADOW_UNRELATED),
LintId::of(&strings::STRING_ADD_ASSIGN),
LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS),
LintId::of(&trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF),
LintId::of(&types::CAST_LOSSLESS),
Expand Down
92 changes: 90 additions & 2 deletions clippy_lints/src/trait_bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use rustc_hir::{GenericBound, Generics, WherePredicate};
use rustc_hir::{def::Res, GenericBound, Generics, ParamName, Path, QPath, TyKind, WherePredicate};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;

declare_clippy_lint! {
/// **What it does:** This lint warns about unnecessary type repetitions in trait bounds
Expand All @@ -29,6 +30,35 @@ declare_clippy_lint! {
"Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
}

declare_clippy_lint! {
/// **What it does:** Checks for cases where generics are being used and multiple
/// syntax specifications for trait bounds are used simultaneously.
///
/// **Why is this bad?** Duplicate bounds makes the code
/// less readable than specifing them only once.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// fn func<T: Clone + Default>(arg: T) where T: Clone + Default {}
/// ```
///
/// Could be written as:
///
/// ```rust
/// fn func<T: Clone + Default>(arg: T) {}
/// ```
/// or
/// ///
/// ```rust
/// fn func<T>(arg: T) where T: Clone + Default {}
/// ```
pub TRAIT_DUPLICATION_IN_BOUNDS,
pedantic,
"Check if the same trait bounds are specifed twice during a function declaration"
}

#[derive(Copy, Clone)]
pub struct TraitBounds {
max_trait_bounds: u64,
Expand All @@ -41,10 +71,25 @@ impl TraitBounds {
}
}

impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS]);
impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS, TRAIT_DUPLICATION_IN_BOUNDS]);

impl<'tcx> LateLintPass<'tcx> for TraitBounds {
fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
self.check_type_repetition(cx, gen);
check_trait_bound_duplication(cx, gen);
}
}

fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span)> {
if let GenericBound::Trait(t, _) = bound {
Some((t.trait_ref.path.res, t.span))
} else {
None
}
}

impl TraitBounds {
fn check_type_repetition(self, cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
if in_macro(gen.span) {
return;
}
Expand Down Expand Up @@ -101,3 +146,46 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
}
}
}

fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
if in_macro(gen.span) {
return;
}

let mut map = FxHashMap::default();
for param in gen.params {
if let ParamName::Plain(ref ident) = param.name {
let res = param
.bounds
.iter()
.filter_map(get_trait_res_span_from_bound)
.collect::<Vec<_>>();
map.insert(*ident, res);
}
}

for predicate in gen.where_clause.predicates {
if_chain! {
if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
if !in_macro(bound_predicate.span);
if let TyKind::Path(ref path) = bound_predicate.bounded_ty.kind;
if let QPath::Resolved(_, Path { ref segments, .. }) = path;
if let Some(segment) = segments.first();
if let Some(trait_resolutions_direct) = map.get(&segment.ident);
then {
for (res_where, _) in bound_predicate.bounds.iter().filter_map(get_trait_res_span_from_bound) {
if let Some((_, span_direct)) = trait_resolutions_direct.iter().find(|(res_direct, _)| *res_direct == res_where) {
span_lint_and_help(
cx,
TRAIT_DUPLICATION_IN_BOUNDS,
*span_direct,
"this trait bound is already specified in the where clause",
None,
"consider removing this trait bound",
);
}
}
}
}
}
}
7 changes: 7 additions & 0 deletions src/lintlist/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2166,6 +2166,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "misc",
},
Lint {
name: "trait_duplication_in_bounds",
group: "pedantic",
desc: "Check if the same trait bounds are specifed twice during a function declaration",
deprecation: None,
module: "trait_bounds",
},
Lint {
name: "transmute_bytes_to_str",
group: "complexity",
Expand Down
31 changes: 31 additions & 0 deletions tests/ui/trait_duplication_in_bounds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#![deny(clippy::trait_duplication_in_bounds)]

use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};

fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
where
T: Clone,
T: Default,
{
unimplemented!();
}

fn good_bar<T: Clone + Default>(arg: T) {
unimplemented!();
}

fn good_foo<T>(arg: T)
where
T: Clone + Default,
{
unimplemented!();
}

fn good_foobar<T: Default>(arg: T)
where
T: Clone,
{
unimplemented!();
}

fn main() {}
23 changes: 23 additions & 0 deletions tests/ui/trait_duplication_in_bounds.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
error: this trait bound is already specified in the where clause
--> $DIR/trait_duplication_in_bounds.rs:5:15
|
LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
| ^^^^^
|
note: the lint level is defined here
--> $DIR/trait_duplication_in_bounds.rs:1:9
|
LL | #![deny(clippy::trait_duplication_in_bounds)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: consider removing this trait bound

error: this trait bound is already specified in the where clause
--> $DIR/trait_duplication_in_bounds.rs:5:23
|
LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
| ^^^^^^^
|
= help: consider removing this trait bound

error: aborting due to 2 previous errors

0 comments on commit 48900a2

Please sign in to comment.