@@ -2,17 +2,18 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
2
2
use clippy_utils:: ty:: same_type_and_consts;
3
3
use clippy_utils:: { in_macro, meets_msrv, msrvs} ;
4
4
use if_chain:: if_chain;
5
+ use rustc_data_structures:: fx:: FxHashSet ;
5
6
use rustc_errors:: Applicability ;
6
7
use rustc_hir:: {
7
8
self as hir,
8
9
def:: { CtorOf , DefKind , Res } ,
9
10
def_id:: LocalDefId ,
10
11
intravisit:: { walk_ty, NestedVisitorMap , Visitor } ,
11
- Expr , ExprKind , FnRetTy , FnSig , GenericArg , HirId , Impl , ImplItemKind , Item , ItemKind , Node , Path , QPath , TyKind ,
12
+ Expr , ExprKind , FnRetTy , FnSig , GenericArg , HirId , Impl , ImplItemKind , Item , ItemKind , Path , QPath , TyKind ,
12
13
} ;
13
14
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
14
15
use rustc_middle:: hir:: map:: Map ;
15
- use rustc_middle:: ty:: { AssocKind , Ty } ;
16
+ use rustc_middle:: ty:: AssocKind ;
16
17
use rustc_semver:: RustcVersion ;
17
18
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
18
19
use rustc_span:: Span ;
@@ -73,10 +74,9 @@ impl UseSelf {
73
74
#[ derive( Debug ) ]
74
75
enum StackItem {
75
76
Check {
76
- hir_id : HirId ,
77
- impl_trait_ref_def_id : Option < LocalDefId > ,
78
- types_to_skip : Vec < HirId > ,
79
- types_to_lint : Vec < HirId > ,
77
+ impl_id : LocalDefId ,
78
+ in_body : u32 ,
79
+ types_to_skip : FxHashSet < HirId > ,
80
80
} ,
81
81
NoCheck ,
82
82
}
@@ -86,60 +86,41 @@ impl_lint_pass!(UseSelf => [USE_SELF]);
86
86
const SEGMENTS_MSG : & str = "segments should be composed of at least 1 element" ;
87
87
88
88
impl < ' tcx > LateLintPass < ' tcx > for UseSelf {
89
- fn check_item ( & mut self , cx : & LateContext < ' _ > , item : & Item < ' _ > ) {
89
+ fn check_item ( & mut self , _cx : & LateContext < ' _ > , item : & Item < ' _ > ) {
90
+ if !is_item_interesting ( item) {
91
+ // This does two things:
92
+ // 1) Reduce needless churn on `self.stack`
93
+ // 2) Don't push `StackItem::NoCheck` when entering `ItemKind::OpaqueTy`,
94
+ // in order to lint `foo() -> impl <..>`
95
+ return ;
96
+ }
90
97
// We push the self types of `impl`s on a stack here. Only the top type on the stack is
91
98
// relevant for linting, since this is the self type of the `impl` we're currently in. To
92
99
// avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that
93
100
// we're in an `impl` or nested item, that we don't want to lint
94
- //
95
- // NB: If you push something on the stack in this method, remember to also pop it in the
96
- // `check_item_post` method.
97
- match & item. kind {
98
- ItemKind :: Impl ( Impl {
99
- self_ty : hir_self_ty,
100
- of_trait,
101
- ..
102
- } ) => {
103
- let should_check = if let TyKind :: Path ( QPath :: Resolved ( _, item_path) ) = hir_self_ty. kind {
104
- let parameters = & item_path. segments . last ( ) . expect ( SEGMENTS_MSG ) . args ;
105
- parameters. as_ref ( ) . map_or ( true , |params| {
106
- !params. parenthesized && !params. args . iter ( ) . any ( |arg| matches ! ( arg, GenericArg :: Lifetime ( _) ) )
107
- } )
108
- } else {
109
- false
110
- } ;
111
- let impl_trait_ref_def_id = of_trait. as_ref ( ) . map ( |_| cx. tcx . hir ( ) . local_def_id ( item. hir_id ( ) ) ) ;
112
- if should_check {
113
- self . stack . push ( StackItem :: Check {
114
- hir_id : hir_self_ty. hir_id ,
115
- impl_trait_ref_def_id,
116
- types_to_lint : Vec :: new ( ) ,
117
- types_to_skip : Vec :: new ( ) ,
118
- } ) ;
119
- } else {
120
- self . stack . push ( StackItem :: NoCheck ) ;
101
+ let stack_item = if_chain ! {
102
+ if let ItemKind :: Impl ( Impl { self_ty, .. } ) = item. kind;
103
+ if let TyKind :: Path ( QPath :: Resolved ( _, item_path) ) = self_ty. kind;
104
+ let parameters = & item_path. segments. last( ) . expect( SEGMENTS_MSG ) . args;
105
+ if parameters. as_ref( ) . map_or( true , |params| {
106
+ !params. parenthesized && !params. args. iter( ) . any( |arg| matches!( arg, GenericArg :: Lifetime ( _) ) )
107
+ } ) ;
108
+ then {
109
+ StackItem :: Check {
110
+ impl_id: item. def_id,
111
+ in_body: 0 ,
112
+ types_to_skip: std:: iter:: once( self_ty. hir_id) . collect( ) ,
121
113
}
122
- } ,
123
- ItemKind :: Static ( ..)
124
- | ItemKind :: Const ( ..)
125
- | ItemKind :: Fn ( ..)
126
- | ItemKind :: Enum ( ..)
127
- | ItemKind :: Struct ( ..)
128
- | ItemKind :: Union ( ..)
129
- | ItemKind :: Trait ( ..) => {
130
- self . stack . push ( StackItem :: NoCheck ) ;
131
- } ,
132
- _ => ( ) ,
133
- }
114
+ } else {
115
+ StackItem :: NoCheck
116
+ }
117
+ } ;
118
+ self . stack . push ( stack_item) ;
134
119
}
135
120
136
121
fn check_item_post ( & mut self , _: & LateContext < ' _ > , item : & Item < ' _ > ) {
137
- use ItemKind :: { Const , Enum , Fn , Impl , Static , Struct , Trait , Union } ;
138
- match item. kind {
139
- Impl { .. } | Static ( ..) | Const ( ..) | Fn ( ..) | Enum ( ..) | Struct ( ..) | Union ( ..) | Trait ( ..) => {
140
- self . stack . pop ( ) ;
141
- } ,
142
- _ => ( ) ,
122
+ if is_item_interesting ( item) {
123
+ self . stack . pop ( ) ;
143
124
}
144
125
}
145
126
@@ -149,11 +130,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
149
130
if_chain ! {
150
131
if let ImplItemKind :: Fn ( FnSig { decl, .. } , ..) = impl_item. kind;
151
132
if let Some ( & mut StackItem :: Check {
152
- impl_trait_ref_def_id : Some ( def_id ) ,
133
+ impl_id ,
153
134
ref mut types_to_skip,
154
135
..
155
136
} ) = self . stack. last_mut( ) ;
156
- if let Some ( impl_trait_ref) = cx. tcx. impl_trait_ref( def_id ) ;
137
+ if let Some ( impl_trait_ref) = cx. tcx. impl_trait_ref( impl_id ) ;
157
138
then {
158
139
// `self_ty` is the semantic self type of `impl <trait> for <type>`. This cannot be
159
140
// `Self`.
@@ -201,52 +182,39 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
201
182
}
202
183
}
203
184
204
- fn check_body ( & mut self , cx : & LateContext < ' tcx > , body : & ' tcx hir:: Body < ' _ > ) {
185
+ fn check_body ( & mut self , _ : & LateContext < ' _ > , _ : & hir:: Body < ' _ > ) {
205
186
// `hir_ty_to_ty` cannot be called in `Body`s or it will panic (sometimes). But in bodies
206
187
// we can use `cx.typeck_results.node_type(..)` to get the `ty::Ty` from a `hir::Ty`.
207
188
// However the `node_type()` method can *only* be called in bodies.
208
- //
209
- // This method implementation determines which types should get linted in a `Body` and
210
- // which shouldn't, with a visitor. We could directly lint in the visitor, but then we
211
- // could only allow this lint on item scope. And we would have to check if those types are
212
- // already dealt with in `check_ty` anyway.
213
- if let Some ( StackItem :: Check {
214
- hir_id,
215
- types_to_lint,
216
- types_to_skip,
217
- ..
218
- } ) = self . stack . last_mut ( )
219
- {
220
- let self_ty = ty_from_hir_id ( cx, * hir_id) ;
189
+ if let Some ( & mut StackItem :: Check { ref mut in_body, .. } ) = self . stack . last_mut ( ) {
190
+ * in_body = in_body. saturating_add ( 1 ) ;
191
+ }
192
+ }
221
193
222
- let mut visitor = LintTyCollector {
223
- cx,
224
- self_ty,
225
- types_to_lint : vec ! [ ] ,
226
- types_to_skip : vec ! [ ] ,
227
- } ;
228
- visitor. visit_expr ( & body. value ) ;
229
- types_to_lint. extend ( visitor. types_to_lint ) ;
230
- types_to_skip. extend ( visitor. types_to_skip ) ;
194
+ fn check_body_post ( & mut self , _: & LateContext < ' _ > , _: & hir:: Body < ' _ > ) {
195
+ if let Some ( & mut StackItem :: Check { ref mut in_body, .. } ) = self . stack . last_mut ( ) {
196
+ * in_body = in_body. saturating_sub ( 1 ) ;
231
197
}
232
198
}
233
199
234
200
fn check_ty ( & mut self , cx : & LateContext < ' _ > , hir_ty : & hir:: Ty < ' _ > ) {
235
201
if_chain ! {
236
- if !in_macro( hir_ty. span) && !in_impl ( cx , hir_ty ) ;
202
+ if !in_macro( hir_ty. span) ;
237
203
if meets_msrv( self . msrv. as_ref( ) , & msrvs:: TYPE_ALIAS_ENUM_VARIANTS ) ;
238
- if let Some ( StackItem :: Check {
239
- hir_id,
240
- types_to_lint,
241
- types_to_skip,
242
- ..
204
+ if let Some ( & StackItem :: Check {
205
+ impl_id,
206
+ in_body,
207
+ ref types_to_skip,
243
208
} ) = self . stack. last( ) ;
209
+ if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = hir_ty. kind;
210
+ if !matches!( path. res, Res :: SelfTy ( ..) | Res :: Def ( DefKind :: TyParam , _) ) ;
244
211
if !types_to_skip. contains( & hir_ty. hir_id) ;
245
- if types_to_lint. contains( & hir_ty. hir_id)
246
- || {
247
- let self_ty = ty_from_hir_id( cx, * hir_id) ;
248
- should_lint_ty( hir_ty, hir_ty_to_ty( cx. tcx, hir_ty) , self_ty)
249
- } ;
212
+ let ty = if in_body > 0 {
213
+ cx. typeck_results( ) . node_type( hir_ty. hir_id)
214
+ } else {
215
+ hir_ty_to_ty( cx. tcx, hir_ty)
216
+ } ;
217
+ if same_type_and_consts( ty, cx. tcx. type_of( impl_id) ) ;
250
218
let hir = cx. tcx. hir( ) ;
251
219
let id = hir. get_parent_node( hir_ty. hir_id) ;
252
220
if !hir. opt_span( id) . map_or( false , in_macro) ;
@@ -260,8 +228,8 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
260
228
if_chain ! {
261
229
if !in_macro( expr. span) ;
262
230
if meets_msrv( self . msrv. as_ref( ) , & msrvs:: TYPE_ALIAS_ENUM_VARIANTS ) ;
263
- if let Some ( StackItem :: Check { hir_id , .. } ) = self . stack. last( ) ;
264
- if cx. typeck_results( ) . expr_ty( expr) == ty_from_hir_id ( cx , * hir_id ) ;
231
+ if let Some ( & StackItem :: Check { impl_id , .. } ) = self . stack. last( ) ;
232
+ if cx. typeck_results( ) . expr_ty( expr) == cx . tcx . type_of ( impl_id ) ;
265
233
then { } else { return ; }
266
234
}
267
235
match expr. kind {
@@ -309,35 +277,6 @@ impl<'tcx> Visitor<'tcx> for SkipTyCollector {
309
277
}
310
278
}
311
279
312
- struct LintTyCollector < ' a , ' tcx > {
313
- cx : & ' a LateContext < ' tcx > ,
314
- self_ty : Ty < ' tcx > ,
315
- types_to_lint : Vec < HirId > ,
316
- types_to_skip : Vec < HirId > ,
317
- }
318
-
319
- impl < ' a , ' tcx > Visitor < ' tcx > for LintTyCollector < ' a , ' tcx > {
320
- type Map = Map < ' tcx > ;
321
-
322
- fn visit_ty ( & mut self , hir_ty : & ' tcx hir:: Ty < ' _ > ) {
323
- if_chain ! {
324
- if let Some ( ty) = self . cx. typeck_results( ) . node_type_opt( hir_ty. hir_id) ;
325
- if should_lint_ty( hir_ty, ty, self . self_ty) ;
326
- then {
327
- self . types_to_lint. push( hir_ty. hir_id) ;
328
- } else {
329
- self . types_to_skip. push( hir_ty. hir_id) ;
330
- }
331
- }
332
-
333
- walk_ty ( self , hir_ty) ;
334
- }
335
-
336
- fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
337
- NestedVisitorMap :: None
338
- }
339
- }
340
-
341
280
fn span_lint ( cx : & LateContext < ' _ > , span : Span ) {
342
281
span_lint_and_sugg (
343
282
cx,
@@ -359,36 +298,10 @@ fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) {
359
298
}
360
299
}
361
300
362
- fn ty_from_hir_id < ' tcx > ( cx : & LateContext < ' tcx > , hir_id : HirId ) -> Ty < ' tcx > {
363
- if let Some ( Node :: Ty ( hir_ty) ) = cx. tcx . hir ( ) . find ( hir_id) {
364
- hir_ty_to_ty ( cx. tcx , hir_ty)
365
- } else {
366
- unreachable ! ( "This function should only be called with `HirId`s that are for sure `Node::Ty`" )
367
- }
368
- }
369
-
370
- fn in_impl ( cx : & LateContext < ' tcx > , hir_ty : & hir:: Ty < ' _ > ) -> bool {
371
- let map = cx. tcx . hir ( ) ;
372
- let parent = map. get_parent_node ( hir_ty. hir_id ) ;
373
- if_chain ! {
374
- if let Some ( Node :: Item ( item) ) = map. find( parent) ;
375
- if let ItemKind :: Impl { .. } = item. kind;
376
- then {
377
- true
378
- } else {
379
- false
380
- }
381
- }
382
- }
383
-
384
- fn should_lint_ty ( hir_ty : & hir:: Ty < ' _ > , ty : Ty < ' _ > , self_ty : Ty < ' _ > ) -> bool {
385
- if_chain ! {
386
- if same_type_and_consts( ty, self_ty) ;
387
- if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = hir_ty. kind;
388
- then {
389
- !matches!( path. res, Res :: SelfTy ( ..) | Res :: Def ( DefKind :: TyParam , _) )
390
- } else {
391
- false
392
- }
393
- }
301
+ fn is_item_interesting ( item : & Item < ' _ > ) -> bool {
302
+ use rustc_hir:: ItemKind :: { Const , Enum , Fn , Impl , Static , Struct , Trait , Union } ;
303
+ matches ! (
304
+ item. kind,
305
+ Impl { .. } | Static ( ..) | Const ( ..) | Fn ( ..) | Enum ( ..) | Struct ( ..) | Union ( ..) | Trait ( ..)
306
+ )
394
307
}
0 commit comments