@@ -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,46 @@ 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+ emit_lint( cx, span) ;
125+ }
126+ } ) ;
127+ }
128+ }
129+ }
130+ }
87131}
88132
89133fn get_trait_res_span_from_bound ( bound : & GenericBound < ' _ > ) -> Option < ( Res , Span ) > {
@@ -198,17 +242,21 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
198242 if let Some ( ( _, span_direct) ) = trait_resolutions_direct
199243 . iter( )
200244 . find( |( res_direct, _) | * res_direct == res_where) {
201- span_lint_and_help(
202- cx,
203- TRAIT_DUPLICATION_IN_BOUNDS ,
204- * span_direct,
205- "this trait bound is already specified in the where clause" ,
206- None ,
207- "consider removing this trait bound" ,
208- ) ;
245+ emit_lint( cx, * span_direct) ;
209246 }
210247 }
211248 }
212249 }
213250 }
214251}
252+
253+ fn emit_lint ( cx : & LateContext < ' _ > , span : Span ) {
254+ span_lint_and_help (
255+ cx,
256+ TRAIT_DUPLICATION_IN_BOUNDS ,
257+ span,
258+ "this trait bound is already specified in the where clause" ,
259+ None ,
260+ "consider removing this trait bound" ,
261+ ) ;
262+ }
0 commit comments