@@ -2,17 +2,18 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
22use clippy_utils:: ty:: same_type_and_consts;
33use clippy_utils:: { in_macro, meets_msrv, msrvs} ;
44use if_chain:: if_chain;
5+ use rustc_data_structures:: fx:: FxHashSet ;
56use rustc_errors:: Applicability ;
67use rustc_hir:: {
78 self as hir,
89 def:: { CtorOf , DefKind , Res } ,
910 def_id:: LocalDefId ,
1011 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 ,
1213} ;
1314use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
1415use rustc_middle:: hir:: map:: Map ;
15- use rustc_middle:: ty:: { AssocKind , Ty } ;
16+ use rustc_middle:: ty:: AssocKind ;
1617use rustc_semver:: RustcVersion ;
1718use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
1819use rustc_span:: Span ;
@@ -73,10 +74,9 @@ impl UseSelf {
7374#[ derive( Debug ) ]
7475enum StackItem {
7576 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 > ,
8080 } ,
8181 NoCheck ,
8282}
@@ -86,60 +86,41 @@ impl_lint_pass!(UseSelf => [USE_SELF]);
8686const SEGMENTS_MSG : & str = "segments should be composed of at least 1 element" ;
8787
8888impl < ' 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+ }
9097 // We push the self types of `impl`s on a stack here. Only the top type on the stack is
9198 // relevant for linting, since this is the self type of the `impl` we're currently in. To
9299 // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that
93100 // 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( ) ,
121113 }
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) ;
134119 }
135120
136121 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 ( ) ;
143124 }
144125 }
145126
@@ -149,11 +130,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
149130 if_chain ! {
150131 if let ImplItemKind :: Fn ( FnSig { decl, .. } , ..) = impl_item. kind;
151132 if let Some ( & mut StackItem :: Check {
152- impl_trait_ref_def_id : Some ( def_id ) ,
133+ impl_id ,
153134 ref mut types_to_skip,
154135 ..
155136 } ) = 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 ) ;
157138 then {
158139 // `self_ty` is the semantic self type of `impl <trait> for <type>`. This cannot be
159140 // `Self`.
@@ -201,52 +182,39 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
201182 }
202183 }
203184
204- fn check_body ( & mut self , cx : & LateContext < ' tcx > , body : & ' tcx hir:: Body < ' _ > ) {
185+ fn check_body ( & mut self , _ : & LateContext < ' _ > , _ : & hir:: Body < ' _ > ) {
205186 // `hir_ty_to_ty` cannot be called in `Body`s or it will panic (sometimes). But in bodies
206187 // we can use `cx.typeck_results.node_type(..)` to get the `ty::Ty` from a `hir::Ty`.
207188 // 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+ }
221193
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 ) ;
231197 }
232198 }
233199
234200 fn check_ty ( & mut self , cx : & LateContext < ' _ > , hir_ty : & hir:: Ty < ' _ > ) {
235201 if_chain ! {
236- if !in_macro( hir_ty. span) && !in_impl ( cx , hir_ty ) ;
202+ if !in_macro( hir_ty. span) ;
237203 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,
243208 } ) = 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 , _) ) ;
244211 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) ) ;
250218 let hir = cx. tcx. hir( ) ;
251219 let id = hir. get_parent_node( hir_ty. hir_id) ;
252220 if !hir. opt_span( id) . map_or( false , in_macro) ;
@@ -260,8 +228,8 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
260228 if_chain ! {
261229 if !in_macro( expr. span) ;
262230 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 ) ;
265233 then { } else { return ; }
266234 }
267235 match expr. kind {
@@ -309,35 +277,6 @@ impl<'tcx> Visitor<'tcx> for SkipTyCollector {
309277 }
310278}
311279
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-
341280fn span_lint ( cx : & LateContext < ' _ > , span : Span ) {
342281 span_lint_and_sugg (
343282 cx,
@@ -359,36 +298,10 @@ fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) {
359298 }
360299}
361300
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+ )
394307}
0 commit comments