@@ -3,10 +3,14 @@ use clippy_utils::source::{snippet, snippet_with_applicability};
33use clippy_utils:: { SpanlessEq , SpanlessHash } ;
44use core:: hash:: { Hash , Hasher } ;
55use if_chain:: if_chain;
6- use rustc_data_structures:: fx:: FxHashMap ;
6+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
77use rustc_data_structures:: unhash:: UnhashMap ;
88use rustc_errors:: Applicability ;
9- use rustc_hir:: { def:: Res , GenericBound , Generics , ParamName , Path , QPath , Ty , TyKind , WherePredicate } ;
9+ use rustc_hir:: def:: Res ;
10+ use rustc_hir:: {
11+ GenericBound , Generics , Item , ItemKind , Node , ParamName , Path , PathSegment , QPath , TraitItem , Ty , TyKind ,
12+ WherePredicate ,
13+ } ;
1014use rustc_lint:: { LateContext , LateLintPass } ;
1115use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
1216use rustc_span:: Span ;
@@ -84,6 +88,53 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
8488 self . check_type_repetition ( cx, gen) ;
8589 check_trait_bound_duplication ( cx, gen) ;
8690 }
91+
92+ fn check_trait_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx TraitItem < ' tcx > ) {
93+ let Generics { where_clause, .. } = & item. generics ;
94+ let mut self_bounds_set = FxHashSet :: default ( ) ;
95+
96+ for predicate in where_clause. predicates {
97+ if_chain ! {
98+ if let WherePredicate :: BoundPredicate ( ref bound_predicate) = predicate;
99+ if !bound_predicate. span. from_expansion( ) ;
100+ if let TyKind :: Path ( QPath :: Resolved ( _, Path { segments, .. } ) ) = bound_predicate. bounded_ty. kind;
101+ if let Some ( PathSegment { res: Some ( Res :: SelfTy ( Some ( def_id) , _) ) , .. } ) = segments. first( ) ;
102+
103+ if let Some (
104+ Node :: Item (
105+ Item {
106+ kind: ItemKind :: Trait ( _, _, _, self_bounds, _) ,
107+ .. }
108+ )
109+ ) = cx. tcx. hir( ) . get_if_local( * def_id) ;
110+ then {
111+ if self_bounds_set. is_empty( ) {
112+ for bound in self_bounds. iter( ) {
113+ let Some ( ( self_res, _) ) = get_trait_res_span_from_bound( bound) else { continue } ;
114+ self_bounds_set. insert( self_res) ;
115+ }
116+ }
117+
118+ bound_predicate
119+ . bounds
120+ . iter( )
121+ . filter_map( get_trait_res_span_from_bound)
122+ . for_each( |( trait_item_res, span) | {
123+ if self_bounds_set. get( & trait_item_res) . is_some( ) {
124+ span_lint_and_help(
125+ cx,
126+ TRAIT_DUPLICATION_IN_BOUNDS ,
127+ span,
128+ "this trait bound is already specified in trait declaration" ,
129+ None ,
130+ "consider removing this trait bound" ,
131+ ) ;
132+ }
133+ } ) ;
134+ }
135+ }
136+ }
137+ }
87138}
88139
89140fn get_trait_res_span_from_bound ( bound : & GenericBound < ' _ > ) -> Option < ( Res , Span ) > {
0 commit comments