@@ -140,43 +140,115 @@ fn report_mismatches<'tcx>(
140140 }
141141}
142142
143- fn lifetimes_use_matched_syntax ( input_info : & [ Info < ' _ > ] , output_info : & [ Info < ' _ > ] ) -> bool {
144- // Categorize lifetimes into source/syntax buckets.
145- let mut n_hidden = 0 ;
146- let mut n_elided = 0 ;
147- let mut n_named = 0 ;
143+ #[ derive( Debug , Copy , Clone , PartialEq ) ]
144+ enum LifetimeSyntaxCategory {
145+ Hidden ,
146+ Elided ,
147+ Named ,
148+ }
148149
149- for info in input_info. iter ( ) . chain ( output_info) {
150+ impl LifetimeSyntaxCategory {
151+ fn new ( syntax_source : ( hir:: LifetimeSyntax , LifetimeSource ) ) -> Option < Self > {
150152 use LifetimeSource :: * ;
151153 use hir:: LifetimeSyntax :: * ;
152154
153- let syntax_source = ( info. lifetime . syntax , info. lifetime . source ) ;
154-
155155 match syntax_source {
156- // Ignore any other kind of lifetime.
157- ( _, Other ) => continue ,
158-
159156 // E.g. `&T`.
160- ( Implicit , Reference | OutlivesBound | PreciseCapturing ) |
157+ ( Implicit , Reference ) |
161158 // E.g. `&'_ T`.
162- ( ExplicitAnonymous , Reference | OutlivesBound | PreciseCapturing ) |
159+ ( ExplicitAnonymous , Reference ) |
163160 // E.g. `ContainsLifetime<'_>`.
164- ( ExplicitAnonymous , Path { .. } ) => n_elided += 1 ,
161+ ( ExplicitAnonymous , Path { .. } ) |
162+ // E.g. `+ '_`, `+ use<'_>`.
163+ ( ExplicitAnonymous , OutlivesBound | PreciseCapturing ) => {
164+ Some ( Self :: Elided )
165+ }
165166
166167 // E.g. `ContainsLifetime`.
167- ( Implicit , Path { .. } ) => n_hidden += 1 ,
168+ ( Implicit , Path { .. } ) => {
169+ Some ( Self :: Hidden )
170+ }
168171
169172 // E.g. `&'a T`.
170- ( ExplicitBound , Reference | OutlivesBound | PreciseCapturing ) |
173+ ( ExplicitBound , Reference ) |
171174 // E.g. `ContainsLifetime<'a>`.
172- ( ExplicitBound , Path { .. } ) => n_named += 1 ,
173- } ;
175+ ( ExplicitBound , Path { .. } ) |
176+ // E.g. `+ 'a`, `+ use<'a>`.
177+ ( ExplicitBound , OutlivesBound | PreciseCapturing ) => {
178+ Some ( Self :: Named )
179+ }
180+
181+ ( Implicit , OutlivesBound | PreciseCapturing ) |
182+ ( _, Other ) => {
183+ None
184+ }
185+ }
186+ }
187+ }
188+
189+ #[ derive( Debug , Default ) ]
190+ pub struct LifetimeSyntaxCategories < T > {
191+ pub hidden : T ,
192+ pub elided : T ,
193+ pub named : T ,
194+ }
195+
196+ impl < T > LifetimeSyntaxCategories < T > {
197+ fn select ( & mut self , category : LifetimeSyntaxCategory ) -> & mut T {
198+ use LifetimeSyntaxCategory :: * ;
199+
200+ match category {
201+ Elided => & mut self . elided ,
202+ Hidden => & mut self . hidden ,
203+ Named => & mut self . named ,
204+ }
205+ }
206+ }
207+
208+ impl < T > LifetimeSyntaxCategories < Vec < T > > {
209+ pub fn len ( & self ) -> LifetimeSyntaxCategories < usize > {
210+ LifetimeSyntaxCategories {
211+ hidden : self . hidden . len ( ) ,
212+ elided : self . elided . len ( ) ,
213+ named : self . named . len ( ) ,
214+ }
215+ }
216+
217+ pub fn flatten ( & self ) -> impl Iterator < Item = & T > {
218+ let Self { hidden, elided, named } = self ;
219+ [ hidden. iter ( ) , elided. iter ( ) , named. iter ( ) ] . into_iter ( ) . flatten ( )
220+ }
221+ }
222+
223+ impl std:: ops:: Add for LifetimeSyntaxCategories < usize > {
224+ type Output = Self ;
225+
226+ fn add ( self , rhs : Self ) -> Self :: Output {
227+ Self {
228+ hidden : self . hidden + rhs. hidden ,
229+ elided : self . elided + rhs. elided ,
230+ named : self . named + rhs. named ,
231+ }
232+ }
233+ }
234+
235+ 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+ }
174242 }
175243
176- let syntax_counts = ( n_hidden, n_elided, n_named) ;
177244 tracing:: debug!( ?syntax_counts) ;
178245
179- matches ! ( syntax_counts, ( _, 0 , 0 ) | ( 0 , _, 0 ) | ( 0 , 0 , _) )
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+ )
180252}
181253
182254fn emit_mismatch_diagnostic < ' tcx > (
@@ -238,7 +310,7 @@ fn emit_mismatch_diagnostic<'tcx>(
238310 use LifetimeSource :: * ;
239311 use hir:: LifetimeSyntax :: * ;
240312
241- let syntax_source = ( info. lifetime . syntax , info . lifetime . source ) ;
313+ let syntax_source = info. syntax_source ( ) ;
242314
243315 if let ( _, Other ) = syntax_source {
244316 // Ignore any other kind of lifetime.
@@ -259,7 +331,6 @@ fn emit_mismatch_diagnostic<'tcx>(
259331 // E.g. `&'_ T`.
260332 ( ExplicitAnonymous , Reference ) => {
261333 suggest_change_to_implicit. push ( info) ;
262- suggest_change_to_mixed_implicit. push ( info) ;
263334 suggest_change_to_explicit_bound. push ( info) ;
264335 }
265336
@@ -319,12 +390,22 @@ fn emit_mismatch_diagnostic<'tcx>(
319390 }
320391 }
321392
393+ let categorize = |infos : & [ Info < ' _ > ] | {
394+ let mut categories = LifetimeSyntaxCategories :: < Vec < _ > > :: default ( ) ;
395+ for info in infos {
396+ if let Some ( category) = info. lifetime_syntax_category ( ) {
397+ categories. select ( category) . push ( info. reporting_span ( ) ) ;
398+ }
399+ }
400+ categories
401+ } ;
402+
403+ let inputs = categorize ( input_info) ;
404+ let outputs = categorize ( output_info) ;
405+
322406 let make_implicit_suggestions =
323407 |infos : & [ & Info < ' _ > ] | infos. iter ( ) . map ( |i| i. removing_span ( ) ) . collect :: < Vec < _ > > ( ) ;
324408
325- let inputs = input_info. iter ( ) . map ( |info| info. reporting_span ( ) ) . collect ( ) ;
326- let outputs = output_info. iter ( ) . map ( |info| info. reporting_span ( ) ) . collect ( ) ;
327-
328409 let explicit_bound_suggestion = bound_lifetime. map ( |info| {
329410 build_mismatch_suggestion ( info. lifetime_name ( ) , & suggest_change_to_explicit_bound)
330411 } ) ;
@@ -399,8 +480,6 @@ fn emit_mismatch_diagnostic<'tcx>(
399480 ?explicit_anonymous_suggestion,
400481 ) ;
401482
402- let lifetime_name = bound_lifetime. map ( |info| info. lifetime_name ( ) ) . unwrap_or ( "'_" ) . to_owned ( ) ;
403-
404483 // We can produce a number of suggestions which may overwhelm
405484 // the user. Instead, we order the suggestions based on Rust
406485 // idioms. The "best" choice is shown to the user and the
@@ -413,21 +492,21 @@ fn emit_mismatch_diagnostic<'tcx>(
413492
414493 cx. emit_span_lint (
415494 MISMATCHED_LIFETIME_SYNTAXES ,
416- Vec :: clone ( & inputs ) ,
417- lints:: MismatchedLifetimeSyntaxes { lifetime_name , inputs, outputs, suggestions } ,
495+ inputs . flatten ( ) . copied ( ) . collect :: < Vec < _ > > ( ) ,
496+ lints:: MismatchedLifetimeSyntaxes { inputs, outputs, suggestions } ,
418497 ) ;
419498}
420499
421500fn build_mismatch_suggestion (
422501 lifetime_name : & str ,
423502 infos : & [ & Info < ' _ > ] ,
424503) -> lints:: MismatchedLifetimeSyntaxesSuggestion {
425- let lifetime_name_sugg = lifetime_name. to_owned ( ) ;
504+ let lifetime_name = lifetime_name. to_owned ( ) ;
426505
427506 let suggestions = infos. iter ( ) . map ( |info| info. suggestion ( & lifetime_name) ) . collect ( ) ;
428507
429508 lints:: MismatchedLifetimeSyntaxesSuggestion :: Explicit {
430- lifetime_name_sugg ,
509+ lifetime_name ,
431510 suggestions,
432511 tool_only : false ,
433512 }
@@ -441,6 +520,14 @@ struct Info<'tcx> {
441520}
442521
443522impl < ' tcx > Info < ' tcx > {
523+ fn syntax_source ( & self ) -> ( hir:: LifetimeSyntax , LifetimeSource ) {
524+ ( self . lifetime . syntax , self . lifetime . source )
525+ }
526+
527+ fn lifetime_syntax_category ( & self ) -> Option < LifetimeSyntaxCategory > {
528+ LifetimeSyntaxCategory :: new ( self . syntax_source ( ) )
529+ }
530+
444531 fn lifetime_name ( & self ) -> & str {
445532 self . lifetime . ident . as_str ( )
446533 }
0 commit comments