@@ -3,6 +3,7 @@ use rustc_hir::intravisit::{self, Visitor};
3
3
use rustc_hir:: { self as hir, LifetimeSource } ;
4
4
use rustc_session:: { declare_lint, declare_lint_pass} ;
5
5
use rustc_span:: Span ;
6
+ use rustc_span:: def_id:: LocalDefId ;
6
7
use tracing:: instrument;
7
8
8
9
use crate :: { LateContext , LateLintPass , LintContext , lints} ;
@@ -78,11 +79,11 @@ impl<'tcx> LateLintPass<'tcx> for LifetimeSyntax {
78
79
fn check_fn (
79
80
& mut self ,
80
81
cx : & LateContext < ' tcx > ,
81
- _: hir :: intravisit:: FnKind < ' tcx > ,
82
+ _: intravisit:: FnKind < ' tcx > ,
82
83
fd : & ' tcx hir:: FnDecl < ' tcx > ,
83
84
_: & ' tcx hir:: Body < ' tcx > ,
84
- _: rustc_span :: Span ,
85
- _: rustc_span :: def_id :: LocalDefId ,
85
+ _: Span ,
86
+ _: LocalDefId ,
86
87
) {
87
88
check_fn_like ( cx, fd) ;
88
89
}
@@ -97,11 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for LifetimeSyntax {
97
98
}
98
99
99
100
#[ 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 > ) {
105
102
match fi. kind {
106
103
hir:: ForeignItemKind :: Fn ( fn_sig, _idents, _generics) => check_fn_like ( cx, fn_sig. decl ) ,
107
104
hir:: ForeignItemKind :: Static ( ..) => { }
@@ -111,35 +108,47 @@ impl<'tcx> LateLintPass<'tcx> for LifetimeSyntax {
111
108
}
112
109
113
110
fn 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 ;
119
113
}
114
+ let hir:: FnRetTy :: Return ( output) = fd. output else {
115
+ return ;
116
+ } ;
120
117
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 ( ) ;
124
119
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
+ }
127
127
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) ;
138
132
}
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) ;
139
142
}
140
143
}
141
144
}
142
145
146
+ #[ derive( Default ) ]
147
+ struct LifetimeGroup < ' tcx > {
148
+ inputs : Vec < Info < ' tcx > > ,
149
+ outputs : Vec < Info < ' tcx > > ,
150
+ }
151
+
143
152
#[ derive( Debug , Copy , Clone , PartialEq ) ]
144
153
enum LifetimeSyntaxCategory {
145
154
Hidden ,
@@ -148,11 +157,11 @@ enum LifetimeSyntaxCategory {
148
157
}
149
158
150
159
impl LifetimeSyntaxCategory {
151
- fn new ( syntax_source : ( hir:: LifetimeSyntax , LifetimeSource ) ) -> Option < Self > {
160
+ fn new ( lifetime : & hir:: Lifetime ) -> Option < Self > {
152
161
use LifetimeSource :: * ;
153
162
use hir:: LifetimeSyntax :: * ;
154
163
155
- match syntax_source {
164
+ match ( lifetime . syntax , lifetime . source ) {
156
165
// E.g. `&T`.
157
166
( Implicit , Reference ) |
158
167
// E.g. `&'_ T`.
@@ -216,7 +225,7 @@ impl<T> LifetimeSyntaxCategories<Vec<T>> {
216
225
217
226
pub fn iter_unnamed ( & self ) -> impl Iterator < Item = & T > {
218
227
let Self { hidden, elided, named : _ } = self ;
219
- [ hidden . iter ( ) , elided. iter ( ) ] . into_iter ( ) . flatten ( )
228
+ std :: iter:: chain ( hidden , elided)
220
229
}
221
230
}
222
231
@@ -233,22 +242,8 @@ impl std::ops::Add for LifetimeSyntaxCategories<usize> {
233
242
}
234
243
235
244
fn 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 )
252
247
}
253
248
254
249
fn emit_mismatch_diagnostic < ' tcx > (
@@ -310,18 +305,13 @@ fn emit_mismatch_diagnostic<'tcx>(
310
305
use LifetimeSource :: * ;
311
306
use hir:: LifetimeSyntax :: * ;
312
307
313
- let syntax_source = info. syntax_source ( ) ;
308
+ let lifetime = info. lifetime ;
314
309
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 {
321
311
bound_lifetime = Some ( info) ;
322
312
}
323
313
324
- match syntax_source {
314
+ match ( lifetime . syntax , lifetime . source ) {
325
315
// E.g. `&T`.
326
316
( Implicit , Reference ) => {
327
317
suggest_change_to_explicit_anonymous. push ( info) ;
@@ -341,8 +331,8 @@ fn emit_mismatch_diagnostic<'tcx>(
341
331
suggest_change_to_explicit_bound. push ( info) ;
342
332
}
343
333
344
- // E.g. `ContainsLifetime<'_>`.
345
- ( ExplicitAnonymous , Path { .. } ) => {
334
+ // E.g. `ContainsLifetime<'_>`, `+ '_`, `+ use<'_>` .
335
+ ( ExplicitAnonymous , Path { .. } | OutlivesBound | PreciseCapturing ) => {
346
336
suggest_change_to_explicit_bound. push ( info) ;
347
337
}
348
338
@@ -353,8 +343,8 @@ fn emit_mismatch_diagnostic<'tcx>(
353
343
suggest_change_to_explicit_anonymous. push ( info) ;
354
344
}
355
345
356
- // E.g. `ContainsLifetime<'a>`.
357
- ( ExplicitBound , Path { .. } ) => {
346
+ // E.g. `ContainsLifetime<'a>`, `+ 'a`, `+ use<'a>` .
347
+ ( ExplicitBound , Path { .. } | OutlivesBound | PreciseCapturing ) => {
358
348
suggest_change_to_mixed_explicit_anonymous. push ( info) ;
359
349
suggest_change_to_explicit_anonymous. push ( info) ;
360
350
}
@@ -363,39 +353,26 @@ fn emit_mismatch_diagnostic<'tcx>(
363
353
panic ! ( "This syntax / source combination is not possible" ) ;
364
354
}
365
355
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
-
377
356
( _, Other ) => {
378
357
panic ! ( "This syntax / source combination has already been skipped" ) ;
379
358
}
380
359
}
381
360
382
- if matches ! ( syntax_source , ( _ , Path { .. } | OutlivesBound | PreciseCapturing ) ) {
361
+ if matches ! ( lifetime . source , Path { .. } | OutlivesBound | PreciseCapturing ) {
383
362
allow_suggesting_implicit = false ;
384
363
}
385
364
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 ,
389
368
_ => { }
390
369
}
391
370
}
392
371
393
372
let categorize = |infos : & [ Info < ' _ > ] | {
394
373
let mut categories = LifetimeSyntaxCategories :: < Vec < _ > > :: default ( ) ;
395
374
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 ( ) ) ;
399
376
}
400
377
categories
401
378
} ;
@@ -407,10 +384,10 @@ fn emit_mismatch_diagnostic<'tcx>(
407
384
|infos : & [ & Info < ' _ > ] | infos. iter ( ) . map ( |i| i. removing_span ( ) ) . collect :: < Vec < _ > > ( ) ;
408
385
409
386
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)
411
388
} ) ;
412
389
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 ( ) ) ;
414
391
415
392
tracing:: debug!( ?bound_lifetime, ?explicit_bound_suggestion, ?is_bound_static) ;
416
393
@@ -517,33 +494,17 @@ fn build_mismatch_suggestion(
517
494
518
495
#[ derive( Debug ) ]
519
496
struct Info < ' tcx > {
520
- type_span : Span ,
521
- referenced_type_span : Option < Span > ,
522
497
lifetime : & ' tcx hir:: Lifetime ,
498
+ syntax_category : LifetimeSyntaxCategory ,
499
+ ty : & ' tcx hir:: Ty < ' tcx > ,
523
500
}
524
501
525
502
impl < ' 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
-
542
503
/// When reporting a lifetime that is implicit, we expand the span
543
504
/// to include the type. Otherwise we end up pointing at nothing,
544
505
/// which is a bit confusing.
545
506
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 }
547
508
}
548
509
549
510
/// When removing an explicit lifetime from a reference,
@@ -560,12 +521,10 @@ impl<'tcx> Info<'tcx> {
560
521
/// ```
561
522
// FIXME: Ideally, we'd also remove the lifetime declaration.
562
523
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 ) ;
567
527
}
568
-
569
528
span
570
529
}
571
530
@@ -574,46 +533,38 @@ impl<'tcx> Info<'tcx> {
574
533
}
575
534
}
576
535
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 > ,
583
539
}
584
540
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 } ;
588
547
589
548
intravisit:: walk_unambig_ty ( & mut this, ty) ;
590
549
}
591
550
}
592
551
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
+ {
594
556
#[ instrument( skip( self ) ) ]
595
557
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
+ }
602
562
}
603
563
604
564
#[ instrument( skip( self ) ) ]
605
565
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 ( ) ) ;
614
567
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;
618
569
}
619
570
}
0 commit comments