@@ -3,6 +3,7 @@ use rustc_hir::intravisit::{self, Visitor};
33use rustc_hir:: { self as hir, LifetimeSource } ;
44use rustc_session:: { declare_lint, declare_lint_pass} ;
55use rustc_span:: Span ;
6+ use rustc_span:: def_id:: LocalDefId ;
67use tracing:: instrument;
78
89use crate :: { LateContext , LateLintPass , LintContext , lints} ;
@@ -78,11 +79,11 @@ impl<'tcx> LateLintPass<'tcx> for LifetimeSyntax {
7879 fn check_fn (
7980 & mut self ,
8081 cx : & LateContext < ' tcx > ,
81- _: hir :: intravisit:: FnKind < ' tcx > ,
82+ _: intravisit:: FnKind < ' tcx > ,
8283 fd : & ' tcx hir:: FnDecl < ' tcx > ,
8384 _: & ' tcx hir:: Body < ' tcx > ,
84- _: rustc_span :: Span ,
85- _: rustc_span :: def_id :: LocalDefId ,
85+ _: Span ,
86+ _: LocalDefId ,
8687 ) {
8788 check_fn_like ( cx, fd) ;
8889 }
@@ -97,11 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for LifetimeSyntax {
9798 }
9899
99100 #[ instrument( skip_all) ]
100- fn check_foreign_item (
101- & mut self ,
102- cx : & LateContext < ' tcx > ,
103- fi : & ' tcx rustc_hir:: ForeignItem < ' tcx > ,
104- ) {
101+ fn check_foreign_item ( & mut self , cx : & LateContext < ' tcx > , fi : & ' tcx hir:: ForeignItem < ' tcx > ) {
105102 match fi. kind {
106103 hir:: ForeignItemKind :: Fn ( fn_sig, _idents, _generics) => check_fn_like ( cx, fn_sig. decl ) ,
107104 hir:: ForeignItemKind :: Static ( ..) => { }
@@ -111,35 +108,47 @@ impl<'tcx> LateLintPass<'tcx> for LifetimeSyntax {
111108}
112109
113110fn check_fn_like < ' tcx > ( cx : & LateContext < ' tcx > , fd : & ' tcx hir:: FnDecl < ' tcx > ) {
114- let mut input_map = Default :: default ( ) ;
115- let mut output_map = Default :: default ( ) ;
116-
117- for input in fd. inputs {
118- LifetimeInfoCollector :: collect ( input, & mut input_map) ;
111+ if fd. inputs . is_empty ( ) {
112+ return ;
119113 }
114+ let hir:: FnRetTy :: Return ( output) = fd. output else {
115+ return ;
116+ } ;
120117
121- if let hir:: FnRetTy :: Return ( output) = fd. output {
122- LifetimeInfoCollector :: collect ( output, & mut output_map) ;
123- }
118+ let mut map: FxIndexMap < hir:: LifetimeKind , LifetimeGroup < ' _ > > = FxIndexMap :: default ( ) ;
124119
125- report_mismatches ( cx, & input_map, & output_map) ;
126- }
120+ LifetimeInfoCollector :: collect ( output, |info| {
121+ let group = map. entry ( info. lifetime . kind ) . or_default ( ) ;
122+ group. outputs . push ( info) ;
123+ } ) ;
124+ if map. is_empty ( ) {
125+ return ;
126+ }
127127
128- #[ instrument( skip_all) ]
129- fn report_mismatches < ' tcx > (
130- cx : & LateContext < ' tcx > ,
131- inputs : & LifetimeInfoMap < ' tcx > ,
132- outputs : & LifetimeInfoMap < ' tcx > ,
133- ) {
134- for ( resolved_lifetime, output_info) in outputs {
135- if let Some ( input_info) = inputs. get ( resolved_lifetime) {
136- if !lifetimes_use_matched_syntax ( input_info, output_info) {
137- emit_mismatch_diagnostic ( cx, input_info, output_info) ;
128+ for input in fd. inputs {
129+ LifetimeInfoCollector :: collect ( input, |info| {
130+ if let Some ( group) = map. get_mut ( & info. lifetime . kind ) {
131+ group. inputs . push ( info) ;
138132 }
133+ } ) ;
134+ }
135+
136+ for LifetimeGroup { ref inputs, ref outputs } in map. into_values ( ) {
137+ if inputs. is_empty ( ) {
138+ continue ;
139+ }
140+ if !lifetimes_use_matched_syntax ( inputs, outputs) {
141+ emit_mismatch_diagnostic ( cx, inputs, outputs) ;
139142 }
140143 }
141144}
142145
146+ #[ derive( Default ) ]
147+ struct LifetimeGroup < ' tcx > {
148+ inputs : Vec < Info < ' tcx > > ,
149+ outputs : Vec < Info < ' tcx > > ,
150+ }
151+
143152#[ derive( Debug , Copy , Clone , PartialEq ) ]
144153enum LifetimeSyntaxCategory {
145154 Hidden ,
@@ -148,11 +157,11 @@ enum LifetimeSyntaxCategory {
148157}
149158
150159impl LifetimeSyntaxCategory {
151- fn new ( syntax_source : ( hir:: LifetimeSyntax , LifetimeSource ) ) -> Option < Self > {
160+ fn new ( lifetime : & hir:: Lifetime ) -> Option < Self > {
152161 use LifetimeSource :: * ;
153162 use hir:: LifetimeSyntax :: * ;
154163
155- match syntax_source {
164+ match ( lifetime . syntax , lifetime . source ) {
156165 // E.g. `&T`.
157166 ( Implicit , Reference ) |
158167 // E.g. `&'_ T`.
@@ -216,7 +225,7 @@ impl<T> LifetimeSyntaxCategories<Vec<T>> {
216225
217226 pub fn iter_unnamed ( & self ) -> impl Iterator < Item = & T > {
218227 let Self { hidden, elided, named : _ } = self ;
219- [ hidden . iter ( ) , elided. iter ( ) ] . into_iter ( ) . flatten ( )
228+ std :: iter:: chain ( hidden , elided)
220229 }
221230}
222231
@@ -233,22 +242,8 @@ impl std::ops::Add for LifetimeSyntaxCategories<usize> {
233242}
234243
235244fn lifetimes_use_matched_syntax ( input_info : & [ Info < ' _ > ] , output_info : & [ Info < ' _ > ] ) -> bool {
236- let mut syntax_counts = LifetimeSyntaxCategories :: < usize > :: default ( ) ;
237-
238- for info in input_info. iter ( ) . chain ( output_info) {
239- if let Some ( category) = info. lifetime_syntax_category ( ) {
240- * syntax_counts. select ( category) += 1 ;
241- }
242- }
243-
244- tracing:: debug!( ?syntax_counts) ;
245-
246- matches ! (
247- syntax_counts,
248- LifetimeSyntaxCategories { hidden: _, elided: 0 , named: 0 }
249- | LifetimeSyntaxCategories { hidden: 0 , elided: _, named: 0 }
250- | LifetimeSyntaxCategories { hidden: 0 , elided: 0 , named: _ }
251- )
245+ let ( first, inputs) = input_info. split_first ( ) . unwrap ( ) ;
246+ std:: iter:: chain ( inputs, output_info) . all ( |info| info. syntax_category == first. syntax_category )
252247}
253248
254249fn emit_mismatch_diagnostic < ' tcx > (
@@ -310,18 +305,13 @@ fn emit_mismatch_diagnostic<'tcx>(
310305 use LifetimeSource :: * ;
311306 use hir:: LifetimeSyntax :: * ;
312307
313- let syntax_source = info. syntax_source ( ) ;
308+ let lifetime = info. lifetime ;
314309
315- if let ( _, Other ) = syntax_source {
316- // Ignore any other kind of lifetime.
317- continue ;
318- }
319-
320- if let ( ExplicitBound , _) = syntax_source {
310+ if lifetime. syntax == ExplicitBound {
321311 bound_lifetime = Some ( info) ;
322312 }
323313
324- match syntax_source {
314+ match ( lifetime . syntax , lifetime . source ) {
325315 // E.g. `&T`.
326316 ( Implicit , Reference ) => {
327317 suggest_change_to_explicit_anonymous. push ( info) ;
@@ -341,8 +331,8 @@ fn emit_mismatch_diagnostic<'tcx>(
341331 suggest_change_to_explicit_bound. push ( info) ;
342332 }
343333
344- // E.g. `ContainsLifetime<'_>`.
345- ( ExplicitAnonymous , Path { .. } ) => {
334+ // E.g. `ContainsLifetime<'_>`, `+ '_`, `+ use<'_>` .
335+ ( ExplicitAnonymous , Path { .. } | OutlivesBound | PreciseCapturing ) => {
346336 suggest_change_to_explicit_bound. push ( info) ;
347337 }
348338
@@ -353,8 +343,8 @@ fn emit_mismatch_diagnostic<'tcx>(
353343 suggest_change_to_explicit_anonymous. push ( info) ;
354344 }
355345
356- // E.g. `ContainsLifetime<'a>`.
357- ( ExplicitBound , Path { .. } ) => {
346+ // E.g. `ContainsLifetime<'a>`, `+ 'a`, `+ use<'a>` .
347+ ( ExplicitBound , Path { .. } | OutlivesBound | PreciseCapturing ) => {
358348 suggest_change_to_mixed_explicit_anonymous. push ( info) ;
359349 suggest_change_to_explicit_anonymous. push ( info) ;
360350 }
@@ -363,39 +353,26 @@ fn emit_mismatch_diagnostic<'tcx>(
363353 panic ! ( "This syntax / source combination is not possible" ) ;
364354 }
365355
366- // E.g. `+ '_`, `+ use<'_>`.
367- ( ExplicitAnonymous , OutlivesBound | PreciseCapturing ) => {
368- suggest_change_to_explicit_bound. push ( info) ;
369- }
370-
371- // E.g. `+ 'a`, `+ use<'a>`.
372- ( ExplicitBound , OutlivesBound | PreciseCapturing ) => {
373- suggest_change_to_mixed_explicit_anonymous. push ( info) ;
374- suggest_change_to_explicit_anonymous. push ( info) ;
375- }
376-
377356 ( _, Other ) => {
378357 panic ! ( "This syntax / source combination has already been skipped" ) ;
379358 }
380359 }
381360
382- if matches ! ( syntax_source , ( _ , Path { .. } | OutlivesBound | PreciseCapturing ) ) {
361+ if matches ! ( lifetime . source , Path { .. } | OutlivesBound | PreciseCapturing ) {
383362 allow_suggesting_implicit = false ;
384363 }
385364
386- match syntax_source {
387- ( _ , Reference ) => saw_a_reference = true ,
388- ( _ , Path { .. } ) => saw_a_path = true ,
365+ match lifetime . source {
366+ Reference => saw_a_reference = true ,
367+ Path { .. } => saw_a_path = true ,
389368 _ => { }
390369 }
391370 }
392371
393372 let categorize = |infos : & [ Info < ' _ > ] | {
394373 let mut categories = LifetimeSyntaxCategories :: < Vec < _ > > :: default ( ) ;
395374 for info in infos {
396- if let Some ( category) = info. lifetime_syntax_category ( ) {
397- categories. select ( category) . push ( info. reporting_span ( ) ) ;
398- }
375+ categories. select ( info. syntax_category ) . push ( info. reporting_span ( ) ) ;
399376 }
400377 categories
401378 } ;
@@ -407,10 +384,10 @@ fn emit_mismatch_diagnostic<'tcx>(
407384 |infos : & [ & Info < ' _ > ] | infos. iter ( ) . map ( |i| i. removing_span ( ) ) . collect :: < Vec < _ > > ( ) ;
408385
409386 let explicit_bound_suggestion = bound_lifetime. map ( |info| {
410- build_mismatch_suggestion ( info. lifetime_name ( ) , & suggest_change_to_explicit_bound)
387+ build_mismatch_suggestion ( info. lifetime . ident . as_str ( ) , & suggest_change_to_explicit_bound)
411388 } ) ;
412389
413- let is_bound_static = bound_lifetime. is_some_and ( |info| info. is_static ( ) ) ;
390+ let is_bound_static = bound_lifetime. is_some_and ( |info| info. lifetime . is_static ( ) ) ;
414391
415392 tracing:: debug!( ?bound_lifetime, ?explicit_bound_suggestion, ?is_bound_static) ;
416393
@@ -517,33 +494,17 @@ fn build_mismatch_suggestion(
517494
518495#[ derive( Debug ) ]
519496struct Info < ' tcx > {
520- type_span : Span ,
521- referenced_type_span : Option < Span > ,
522497 lifetime : & ' tcx hir:: Lifetime ,
498+ syntax_category : LifetimeSyntaxCategory ,
499+ ty : & ' tcx hir:: Ty < ' tcx > ,
523500}
524501
525502impl < ' tcx > Info < ' tcx > {
526- fn syntax_source ( & self ) -> ( hir:: LifetimeSyntax , LifetimeSource ) {
527- ( self . lifetime . syntax , self . lifetime . source )
528- }
529-
530- fn lifetime_syntax_category ( & self ) -> Option < LifetimeSyntaxCategory > {
531- LifetimeSyntaxCategory :: new ( self . syntax_source ( ) )
532- }
533-
534- fn lifetime_name ( & self ) -> & str {
535- self . lifetime . ident . as_str ( )
536- }
537-
538- fn is_static ( & self ) -> bool {
539- self . lifetime . is_static ( )
540- }
541-
542503 /// When reporting a lifetime that is implicit, we expand the span
543504 /// to include the type. Otherwise we end up pointing at nothing,
544505 /// which is a bit confusing.
545506 fn reporting_span ( & self ) -> Span {
546- if self . lifetime . is_implicit ( ) { self . type_span } else { self . lifetime . ident . span }
507+ if self . lifetime . is_implicit ( ) { self . ty . span } else { self . lifetime . ident . span }
547508 }
548509
549510 /// When removing an explicit lifetime from a reference,
@@ -560,12 +521,10 @@ impl<'tcx> Info<'tcx> {
560521 /// ```
561522 // FIXME: Ideally, we'd also remove the lifetime declaration.
562523 fn removing_span ( & self ) -> Span {
563- let mut span = self . suggestion ( "'dummy" ) . 0 ;
564-
565- if let Some ( referenced_type_span) = self . referenced_type_span {
566- span = span. until ( referenced_type_span) ;
524+ let mut span = self . lifetime . ident . span ;
525+ if let hir:: TyKind :: Ref ( _, mut_ty) = self . ty . kind {
526+ span = span. until ( mut_ty. ty . span ) ;
567527 }
568-
569528 span
570529 }
571530
@@ -574,46 +533,38 @@ impl<'tcx> Info<'tcx> {
574533 }
575534}
576535
577- type LifetimeInfoMap < ' tcx > = FxIndexMap < & ' tcx hir:: LifetimeKind , Vec < Info < ' tcx > > > ;
578-
579- struct LifetimeInfoCollector < ' a , ' tcx > {
580- type_span : Span ,
581- referenced_type_span : Option < Span > ,
582- map : & ' a mut LifetimeInfoMap < ' tcx > ,
536+ struct LifetimeInfoCollector < ' tcx , F > {
537+ info_func : F ,
538+ ty : & ' tcx hir:: Ty < ' tcx > ,
583539}
584540
585- impl < ' a , ' tcx > LifetimeInfoCollector < ' a , ' tcx > {
586- fn collect ( ty : & ' tcx hir:: Ty < ' tcx > , map : & ' a mut LifetimeInfoMap < ' tcx > ) {
587- let mut this = Self { type_span : ty. span , referenced_type_span : None , map } ;
541+ impl < ' tcx , F > LifetimeInfoCollector < ' tcx , F >
542+ where
543+ F : FnMut ( Info < ' tcx > ) ,
544+ {
545+ fn collect ( ty : & ' tcx hir:: Ty < ' tcx > , info_func : F ) {
546+ let mut this = Self { info_func, ty } ;
588547
589548 intravisit:: walk_unambig_ty ( & mut this, ty) ;
590549 }
591550}
592551
593- impl < ' a , ' tcx > Visitor < ' tcx > for LifetimeInfoCollector < ' a , ' tcx > {
552+ impl < ' tcx , F > Visitor < ' tcx > for LifetimeInfoCollector < ' tcx , F >
553+ where
554+ F : FnMut ( Info < ' tcx > ) ,
555+ {
594556 #[ instrument( skip( self ) ) ]
595557 fn visit_lifetime ( & mut self , lifetime : & ' tcx hir:: Lifetime ) {
596- let type_span = self . type_span ;
597- let referenced_type_span = self . referenced_type_span ;
598-
599- let info = Info { type_span, referenced_type_span, lifetime } ;
600-
601- self . map . entry ( & lifetime. kind ) . or_default ( ) . push ( info) ;
558+ if let Some ( syntax_category) = LifetimeSyntaxCategory :: new ( lifetime) {
559+ let info = Info { lifetime, syntax_category, ty : self . ty } ;
560+ ( self . info_func ) ( info) ;
561+ }
602562 }
603563
604564 #[ instrument( skip( self ) ) ]
605565 fn visit_ty ( & mut self , ty : & ' tcx hir:: Ty < ' tcx , hir:: AmbigArg > ) -> Self :: Result {
606- let old_type_span = self . type_span ;
607- let old_referenced_type_span = self . referenced_type_span ;
608-
609- self . type_span = ty. span ;
610- if let hir:: TyKind :: Ref ( _, ty) = ty. kind {
611- self . referenced_type_span = Some ( ty. ty . span ) ;
612- }
613-
566+ let old_ty = std:: mem:: replace ( & mut self . ty , ty. as_unambig_ty ( ) ) ;
614567 intravisit:: walk_ty ( self , ty) ;
615-
616- self . type_span = old_type_span;
617- self . referenced_type_span = old_referenced_type_span;
568+ self . ty = old_ty;
618569 }
619570}
0 commit comments