@@ -646,7 +646,24 @@ struct DiagMetadata<'ast> {
646646}
647647
648648#[ derive( Debug ) ]
649- struct ResolvedNestedElisionTarget {
649+ enum ResolvedElisionTarget {
650+ /// Elision in `&u8` -> `&'_ u8`
651+ TopLevel ( NodeId ) ,
652+ /// Elision in `Foo` -> `Foo<'_>`
653+ Nested ( NestedResolvedElisionTarget ) ,
654+ }
655+
656+ impl ResolvedElisionTarget {
657+ fn node_id ( & self ) -> NodeId {
658+ match * self {
659+ Self :: TopLevel ( n) => n,
660+ Self :: Nested ( NestedResolvedElisionTarget { segment_id, .. } ) => segment_id,
661+ }
662+ }
663+ }
664+
665+ #[ derive( Debug ) ]
666+ struct NestedResolvedElisionTarget {
650667 segment_id : NodeId ,
651668 elided_lifetime_span : Span ,
652669 diagnostic : lint:: BuiltinLintDiag ,
@@ -694,7 +711,7 @@ struct LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
694711 lifetime_uses : FxHashMap < LocalDefId , LifetimeUseSet > ,
695712
696713 /// Track which types participated in lifetime elision
697- resolved_lifetime_elisions : Vec < ResolvedNestedElisionTarget > ,
714+ resolved_lifetime_elisions : Vec < ResolvedElisionTarget > ,
698715}
699716
700717/// Walks the whole crate in DFS order, visiting each item, resolving names as it goes.
@@ -1742,6 +1759,8 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
17421759 LifetimeElisionCandidate :: Ignore ,
17431760 ) ;
17441761 self . resolve_anonymous_lifetime ( & lt, true ) ;
1762+
1763+ self . resolved_lifetime_elisions . push ( ResolvedElisionTarget :: TopLevel ( anchor_id) ) ;
17451764 }
17461765
17471766 #[ instrument( level = "debug" , skip( self ) ) ]
@@ -1953,16 +1972,18 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
19531972 }
19541973
19551974 if should_lint {
1956- self . resolved_lifetime_elisions . push ( ResolvedNestedElisionTarget {
1957- segment_id,
1958- elided_lifetime_span,
1959- diagnostic : lint:: BuiltinLintDiag :: ElidedLifetimesInPaths (
1960- expected_lifetimes,
1961- path_span,
1962- !segment. has_generic_args ,
1975+ self . resolved_lifetime_elisions . push ( ResolvedElisionTarget :: Nested (
1976+ NestedResolvedElisionTarget {
1977+ segment_id,
19631978 elided_lifetime_span,
1964- ) ,
1965- } ) ;
1979+ diagnostic : lint:: BuiltinLintDiag :: ElidedLifetimesInPaths (
1980+ expected_lifetimes,
1981+ path_span,
1982+ !segment. has_generic_args ,
1983+ elided_lifetime_span,
1984+ ) ,
1985+ } ,
1986+ ) ) ;
19661987 }
19671988 }
19681989 }
@@ -2024,22 +2045,80 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
20242045
20252046 let elision_failures =
20262047 replace ( & mut self . diag_metadata . current_elision_failures , outer_failures) ;
2027- if !elision_failures. is_empty ( ) {
2028- let Err ( failure_info) = elision_lifetime else { bug ! ( ) } ;
2029- self . report_missing_lifetime_specifiers ( elision_failures, Some ( failure_info) ) ;
2030- }
2048+
2049+ let elision_lifetime = match ( elision_failures. is_empty ( ) , elision_lifetime) {
2050+ ( true , Ok ( lifetime) ) => Some ( lifetime) ,
2051+
2052+ ( true , Err ( _) ) => None ,
2053+
2054+ ( false , Ok ( _) ) => bug ! ( ) ,
2055+
2056+ ( false , Err ( failure_info) ) => {
2057+ self . report_missing_lifetime_specifiers ( elision_failures, Some ( failure_info) ) ;
2058+ None
2059+ }
2060+ } ;
2061+
2062+ // We've recorded all elisions that occurred in the params and
2063+ // outputs, categorized by top-level or nested.
2064+ //
2065+ // Our primary lint case is when an output lifetime is tied to
2066+ // an input lifetime. In that case, we want to warn about any
2067+ // nested hidden lifetimes in those params or outputs.
2068+ //
2069+ // The secondary case is for nested elisions that are not part
2070+ // of the tied lifetime relationship.
20312071
20322072 let output_resolved_lifetime_elisions =
20332073 replace ( & mut self . resolved_lifetime_elisions , outer_resolved_lifetime_elisions) ;
20342074
2035- let resolved_lifetime_elisions =
2036- param_resolved_lifetime_elisions. into_iter ( ) . chain ( output_resolved_lifetime_elisions) ;
2075+ match ( output_resolved_lifetime_elisions. is_empty ( ) , elision_lifetime) {
2076+ ( true , _) | ( _, None ) => {
2077+ // Treat all parameters as untied
2078+ self . report_elided_lifetimes_in_paths (
2079+ param_resolved_lifetime_elisions,
2080+ lint:: builtin:: ELIDED_LIFETIMES_IN_PATHS_UNTIED ,
2081+ ) ;
2082+ }
2083+ ( false , Some ( elision_lifetime) ) => {
2084+ let ( primary, secondary) : ( Vec < _ > , Vec < _ > ) =
2085+ param_resolved_lifetime_elisions. into_iter ( ) . partition ( |re| {
2086+ // Recover the `NodeId` of an elided lifetime
2087+ let lvl1 = & self . r . lifetimes_res_map [ & re. node_id ( ) ] ;
2088+ let lvl2 = match lvl1 {
2089+ LifetimeRes :: ElidedAnchor { start, .. } => {
2090+ & self . r . lifetimes_res_map [ & start]
2091+ }
2092+ o => o,
2093+ } ;
2094+
2095+ lvl2 == & elision_lifetime
2096+ } ) ;
2097+
2098+ self . report_elided_lifetimes_in_paths (
2099+ primary. into_iter ( ) . chain ( output_resolved_lifetime_elisions) ,
2100+ lint:: builtin:: ELIDED_LIFETIMES_IN_PATHS_TIED ,
2101+ ) ;
2102+ self . report_elided_lifetimes_in_paths (
2103+ secondary,
2104+ lint:: builtin:: ELIDED_LIFETIMES_IN_PATHS_UNTIED ,
2105+ ) ;
2106+ }
2107+ }
2108+ }
2109+
2110+ fn report_elided_lifetimes_in_paths (
2111+ & mut self ,
2112+ resolved_elisions : impl IntoIterator < Item = ResolvedElisionTarget > ,
2113+ lint : & ' static lint:: Lint ,
2114+ ) {
2115+ for re in resolved_elisions {
2116+ let ResolvedElisionTarget :: Nested ( d) = re else { continue } ;
20372117
2038- for re in resolved_lifetime_elisions {
2039- let ResolvedNestedElisionTarget { segment_id, elided_lifetime_span, diagnostic } = re;
2118+ let NestedResolvedElisionTarget { segment_id, elided_lifetime_span, diagnostic } = d;
20402119
20412120 self . r . lint_buffer . buffer_lint_with_diagnostic (
2042- lint:: builtin :: ELIDED_LIFETIMES_IN_PATHS ,
2121+ lint,
20432122 segment_id,
20442123 elided_lifetime_span,
20452124 "hidden lifetime parameters in types are deprecated" ,
0 commit comments