@@ -2,6 +2,7 @@ use rustc::lint::*;
2
2
use rustc:: hir:: def_id:: DefId ;
3
3
use rustc:: ty;
4
4
use rustc:: hir:: * ;
5
+ use std:: collections:: HashSet ;
5
6
use syntax:: ast:: { Lit , LitKind , Name } ;
6
7
use syntax:: codemap:: { Span , Spanned } ;
7
8
use utils:: { get_item_name, in_macro, snippet, span_lint, span_lint_and_sugg, walk_ptrs_ty} ;
@@ -88,7 +89,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LenZero {
88
89
}
89
90
}
90
91
91
- fn check_trait_items ( cx : & LateContext , item : & Item , trait_items : & [ TraitItemRef ] ) {
92
+ fn check_trait_items ( cx : & LateContext , visited_trait : & Item , trait_items : & [ TraitItemRef ] ) {
92
93
fn is_named_self ( cx : & LateContext , item : & TraitItemRef , name : & str ) -> bool {
93
94
item. name == name &&
94
95
if let AssociatedItemKind :: Method { has_self } = item. kind {
@@ -102,18 +103,52 @@ fn check_trait_items(cx: &LateContext, item: &Item, trait_items: &[TraitItemRef]
102
103
}
103
104
}
104
105
105
- if !trait_items. iter ( ) . any ( |i| is_named_self ( cx, i, "is_empty" ) ) {
106
- if let Some ( i) = trait_items. iter ( ) . find ( |i| is_named_self ( cx, i, "len" ) ) {
107
- if cx. access_levels . is_exported ( i. id . node_id ) {
108
- span_lint (
109
- cx,
110
- LEN_WITHOUT_IS_EMPTY ,
111
- item. span ,
112
- & format ! ( "trait `{}` has a `len` method but no `is_empty` method" , item. name) ,
113
- ) ;
106
+ // fill the set with current and super traits
107
+ fn fill_trait_set < ' a , ' b : ' a > ( traitt : & ' b Item , set : & ' a mut HashSet < & ' b Item > , cx : & ' b LateContext ) {
108
+ if set. insert ( traitt) {
109
+ if let ItemTrait ( .., ref ty_param_bounds, _) = traitt. node {
110
+ for ty_param_bound in ty_param_bounds {
111
+ if let TraitTyParamBound ( ref poly_trait_ref, _) = * ty_param_bound {
112
+ let super_trait_node_id = cx. tcx
113
+ . hir
114
+ . as_local_node_id ( poly_trait_ref. trait_ref . path . def . def_id ( ) )
115
+ . expect ( "the DefId is local, the NodeId should be available" ) ;
116
+ let super_trait = cx. tcx . hir . expect_item ( super_trait_node_id) ;
117
+ fill_trait_set ( super_trait, set, cx) ;
118
+ }
119
+ }
114
120
}
115
121
}
116
122
}
123
+
124
+ if cx. access_levels . is_exported ( visited_trait. id ) &&
125
+ trait_items
126
+ . iter ( )
127
+ . any ( |i| is_named_self ( cx, i, "len" ) )
128
+ {
129
+ let mut current_and_super_traits = HashSet :: new ( ) ;
130
+ fill_trait_set ( visited_trait, & mut current_and_super_traits, cx) ;
131
+
132
+ let is_empty_method_found = current_and_super_traits
133
+ . iter ( )
134
+ . flat_map ( |i| match i. node {
135
+ ItemTrait ( .., ref trait_items) => trait_items. iter ( ) ,
136
+ _ => bug ! ( "should only handle traits" ) ,
137
+ } )
138
+ . any ( |i| is_named_self ( cx, i, "is_empty" ) ) ;
139
+
140
+ if !is_empty_method_found {
141
+ span_lint (
142
+ cx,
143
+ LEN_WITHOUT_IS_EMPTY ,
144
+ visited_trait. span ,
145
+ & format ! (
146
+ "trait `{}` has a `len` method but no (possibly inherited) `is_empty` method" ,
147
+ visited_trait. name
148
+ ) ,
149
+ ) ;
150
+ }
151
+ }
117
152
}
118
153
119
154
fn check_impl_items ( cx : & LateContext , item : & Item , impl_items : & [ ImplItemRef ] ) {
0 commit comments