Skip to main content

rustc_lint/
lifetime_syntax.rs

1use rustc_data_structures::fx::FxIndexMap;
2use rustc_hir::intravisit::{self, Visitor};
3use rustc_hir::{self as hir, LifetimeSource};
4use rustc_session::{declare_lint, declare_lint_pass};
5use rustc_span::Span;
6use rustc_span::def_id::LocalDefId;
7use tracing::instrument;
8
9use crate::{LateContext, LateLintPass, LintContext, lints};
10
11#[doc = r" The `mismatched_lifetime_syntaxes` lint detects when the same"]
#[doc = r" lifetime is referred to by different syntaxes between function"]
#[doc = r" arguments and return values."]
#[doc = r""]
#[doc = r" The three kinds of syntaxes are:"]
#[doc = r""]
#[doc = r" 1. Named lifetimes. These are references (`&'a str`) or paths"]
#[doc = r"    (`Person<'a>`) that use a lifetime with a name, such as"]
#[doc = r"    `'static` or `'a`."]
#[doc = r""]
#[doc = r" 2. Elided lifetimes. These are references with no explicit"]
#[doc = r"    lifetime (`&str`), references using the anonymous lifetime"]
#[doc = r"    (`&'_ str`), and paths using the anonymous lifetime"]
#[doc = r"    (`Person<'_>`)."]
#[doc = r""]
#[doc = r" 3. Hidden lifetimes. These are paths that do not contain any"]
#[doc = r"    visual indication that it contains a lifetime (`Person`)."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust,compile_fail"]
#[doc = r" #![deny(mismatched_lifetime_syntaxes)]"]
#[doc = r""]
#[doc = r" pub fn mixing_named_with_elided(v: &'static u8) -> &u8 {"]
#[doc = r"     v"]
#[doc = r" }"]
#[doc = r""]
#[doc = r" struct Person<'a> {"]
#[doc = r"     name: &'a str,"]
#[doc = r" }"]
#[doc = r""]
#[doc = r" pub fn mixing_hidden_with_elided(v: Person) -> Person<'_> {"]
#[doc = r"     v"]
#[doc = r" }"]
#[doc = r""]
#[doc = r" struct Foo;"]
#[doc = r""]
#[doc = r" impl Foo {"]
#[doc = r"     // Lifetime elision results in the output lifetime becoming"]
#[doc = r"     // `'static`, which is not what was intended."]
#[doc = r"     pub fn get_mut(&'static self, x: &mut u8) -> &mut u8 {"]
#[doc = r"         unsafe { &mut *(x as *mut _) }"]
#[doc = r"     }"]
#[doc = r" }"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc = r" Lifetime elision is useful because it frees you from having to"]
#[doc = r" give each lifetime its own name and show the relation of input"]
#[doc = r" and output lifetimes for common cases. However, a lifetime"]
#[doc = r" that uses inconsistent syntax between related arguments and"]
#[doc = r" return values is more confusing."]
#[doc = r""]
#[doc = r" In certain `unsafe` code, lifetime elision combined with"]
#[doc = r" inconsistent lifetime syntax may result in unsound code."]
pub static MISMATCHED_LIFETIME_SYNTAXES: &::rustc_lint_defs::Lint =
    &::rustc_lint_defs::Lint {
            name: "MISMATCHED_LIFETIME_SYNTAXES",
            default_level: ::rustc_lint_defs::Warn,
            desc: "detects when a lifetime uses different syntax between arguments and return values",
            is_externally_loaded: false,
            ..::rustc_lint_defs::Lint::default_fields_for_macro()
        };declare_lint! {
12    /// The `mismatched_lifetime_syntaxes` lint detects when the same
13    /// lifetime is referred to by different syntaxes between function
14    /// arguments and return values.
15    ///
16    /// The three kinds of syntaxes are:
17    ///
18    /// 1. Named lifetimes. These are references (`&'a str`) or paths
19    ///    (`Person<'a>`) that use a lifetime with a name, such as
20    ///    `'static` or `'a`.
21    ///
22    /// 2. Elided lifetimes. These are references with no explicit
23    ///    lifetime (`&str`), references using the anonymous lifetime
24    ///    (`&'_ str`), and paths using the anonymous lifetime
25    ///    (`Person<'_>`).
26    ///
27    /// 3. Hidden lifetimes. These are paths that do not contain any
28    ///    visual indication that it contains a lifetime (`Person`).
29    ///
30    /// ### Example
31    ///
32    /// ```rust,compile_fail
33    /// #![deny(mismatched_lifetime_syntaxes)]
34    ///
35    /// pub fn mixing_named_with_elided(v: &'static u8) -> &u8 {
36    ///     v
37    /// }
38    ///
39    /// struct Person<'a> {
40    ///     name: &'a str,
41    /// }
42    ///
43    /// pub fn mixing_hidden_with_elided(v: Person) -> Person<'_> {
44    ///     v
45    /// }
46    ///
47    /// struct Foo;
48    ///
49    /// impl Foo {
50    ///     // Lifetime elision results in the output lifetime becoming
51    ///     // `'static`, which is not what was intended.
52    ///     pub fn get_mut(&'static self, x: &mut u8) -> &mut u8 {
53    ///         unsafe { &mut *(x as *mut _) }
54    ///     }
55    /// }
56    /// ```
57    ///
58    /// {{produces}}
59    ///
60    /// ### Explanation
61    ///
62    /// Lifetime elision is useful because it frees you from having to
63    /// give each lifetime its own name and show the relation of input
64    /// and output lifetimes for common cases. However, a lifetime
65    /// that uses inconsistent syntax between related arguments and
66    /// return values is more confusing.
67    ///
68    /// In certain `unsafe` code, lifetime elision combined with
69    /// inconsistent lifetime syntax may result in unsound code.
70    pub MISMATCHED_LIFETIME_SYNTAXES,
71    Warn,
72    "detects when a lifetime uses different syntax between arguments and return values"
73}
74
75pub struct LifetimeSyntax;
#[automatically_derived]
impl ::core::marker::Copy for LifetimeSyntax { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for LifetimeSyntax { }
#[automatically_derived]
impl ::core::clone::Clone for LifetimeSyntax {
    #[inline]
    fn clone(&self) -> LifetimeSyntax { *self }
}
impl ::rustc_lint_defs::LintPass for LifetimeSyntax {
    fn name(&self) -> &'static str { "LifetimeSyntax" }
    fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                [MISMATCHED_LIFETIME_SYNTAXES]))
    }
}
impl LifetimeSyntax {
    #[allow(unused)]
    pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                [MISMATCHED_LIFETIME_SYNTAXES]))
    }
}declare_lint_pass!(LifetimeSyntax => [MISMATCHED_LIFETIME_SYNTAXES]);
76
77impl<'tcx> LateLintPass<'tcx> for LifetimeSyntax {
78    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL &&
                ::tracing::Level::INFO <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("check_fn",
                                    "rustc_lint::lifetime_syntax", ::tracing::Level::INFO,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/lifetime_syntax.rs"),
                                    ::tracing_core::__macro_support::Option::Some(78u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_lint::lifetime_syntax"),
                                    ::tracing_core::field::FieldSet::new(&[],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::INFO <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::INFO <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{ meta.fields().value_set(&[]) })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: () = loop {};
            return __tracing_attr_fake_return;
        }
        { check_fn_like(cx, fd); }
    }
}#[instrument(skip_all)]
79    fn check_fn(
80        &mut self,
81        cx: &LateContext<'tcx>,
82        _: intravisit::FnKind<'tcx>,
83        fd: &'tcx hir::FnDecl<'tcx>,
84        _: &'tcx hir::Body<'tcx>,
85        _: Span,
86        _: LocalDefId,
87    ) {
88        check_fn_like(cx, fd);
89    }
90
91    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL &&
                ::tracing::Level::INFO <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("check_trait_item",
                                    "rustc_lint::lifetime_syntax", ::tracing::Level::INFO,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/lifetime_syntax.rs"),
                                    ::tracing_core::__macro_support::Option::Some(91u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_lint::lifetime_syntax"),
                                    ::tracing_core::field::FieldSet::new(&[],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::INFO <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::INFO <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{ meta.fields().value_set(&[]) })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: () = loop {};
            return __tracing_attr_fake_return;
        }
        {
            match ti.kind {
                hir::TraitItemKind::Const(..) => {}
                hir::TraitItemKind::Fn(fn_sig, _trait_fn) =>
                    check_fn_like(cx, fn_sig.decl),
                hir::TraitItemKind::Type(..) => {}
            }
        }
    }
}#[instrument(skip_all)]
92    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, ti: &'tcx hir::TraitItem<'tcx>) {
93        match ti.kind {
94            hir::TraitItemKind::Const(..) => {}
95            hir::TraitItemKind::Fn(fn_sig, _trait_fn) => check_fn_like(cx, fn_sig.decl),
96            hir::TraitItemKind::Type(..) => {}
97        }
98    }
99
100    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL &&
                ::tracing::Level::INFO <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("check_foreign_item",
                                    "rustc_lint::lifetime_syntax", ::tracing::Level::INFO,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/lifetime_syntax.rs"),
                                    ::tracing_core::__macro_support::Option::Some(100u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_lint::lifetime_syntax"),
                                    ::tracing_core::field::FieldSet::new(&[],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::INFO <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::INFO <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{ meta.fields().value_set(&[]) })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: () = loop {};
            return __tracing_attr_fake_return;
        }
        {
            match fi.kind {
                hir::ForeignItemKind::Fn(fn_sig, _idents, _generics) =>
                    check_fn_like(cx, fn_sig.decl),
                hir::ForeignItemKind::Static(..) => {}
                hir::ForeignItemKind::Type => {}
            }
        }
    }
}#[instrument(skip_all)]
101    fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, fi: &'tcx hir::ForeignItem<'tcx>) {
102        match fi.kind {
103            hir::ForeignItemKind::Fn(fn_sig, _idents, _generics) => check_fn_like(cx, fn_sig.decl),
104            hir::ForeignItemKind::Static(..) => {}
105            hir::ForeignItemKind::Type => {}
106        }
107    }
108}
109
110fn check_fn_like<'tcx>(cx: &LateContext<'tcx>, fd: &'tcx hir::FnDecl<'tcx>) {
111    if fd.inputs.is_empty() {
112        return;
113    }
114    let hir::FnRetTy::Return(output) = fd.output else {
115        return;
116    };
117
118    let mut map: FxIndexMap<hir::LifetimeKind, LifetimeGroup<'_>> = FxIndexMap::default();
119
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
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);
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);
142        }
143    }
144}
145
146#[derive(#[automatically_derived]
impl<'tcx> ::core::default::Default for LifetimeGroup<'tcx> {
    #[inline]
    fn default() -> LifetimeGroup<'tcx> {
        LifetimeGroup {
            inputs: ::core::default::Default::default(),
            outputs: ::core::default::Default::default(),
        }
    }
}Default)]
147struct LifetimeGroup<'tcx> {
148    inputs: Vec<Info<'tcx>>,
149    outputs: Vec<Info<'tcx>>,
150}
151
152#[derive(#[automatically_derived]
impl ::core::fmt::Debug for LifetimeSyntaxCategory {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                LifetimeSyntaxCategory::Hidden => "Hidden",
                LifetimeSyntaxCategory::Elided => "Elided",
                LifetimeSyntaxCategory::Named => "Named",
            })
    }
}Debug, #[automatically_derived]
impl ::core::marker::Copy for LifetimeSyntaxCategory { }Copy, #[automatically_derived]
impl ::core::clone::Clone for LifetimeSyntaxCategory {
    #[inline]
    fn clone(&self) -> LifetimeSyntaxCategory { *self }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for LifetimeSyntaxCategory {
    #[inline]
    fn eq(&self, other: &LifetimeSyntaxCategory) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq)]
153enum LifetimeSyntaxCategory {
154    Hidden,
155    Elided,
156    Named,
157}
158
159impl LifetimeSyntaxCategory {
160    fn new(lifetime: &hir::Lifetime) -> Option<Self> {
161        use LifetimeSource::*;
162        use hir::LifetimeSyntax::*;
163
164        match (lifetime.syntax, lifetime.source) {
165            // E.g. `&T`.
166            (Implicit, Reference) |
167            // E.g. `&'_ T`.
168            (ExplicitAnonymous, Reference) |
169            // E.g. `ContainsLifetime<'_>`.
170            (ExplicitAnonymous, Path { .. }) |
171            // E.g. `+ '_`, `+ use<'_>`.
172            (ExplicitAnonymous, OutlivesBound | PreciseCapturing) => Some(Self::Elided),
173
174            // E.g. `ContainsLifetime`.
175            (Implicit, Path { .. }) => Some(Self::Hidden),
176
177            // E.g. `&'a T`.
178            (ExplicitBound, Reference) |
179            // E.g. `ContainsLifetime<'a>`.
180            (ExplicitBound, Path { .. }) |
181            // E.g. `+ 'a`, `+ use<'a>`.
182            (ExplicitBound, OutlivesBound | PreciseCapturing) => Some(Self::Named),
183
184            (Implicit, OutlivesBound | PreciseCapturing) | (_, Other) => None,
185        }
186    }
187}
188
189#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug> ::core::fmt::Debug for LifetimeSyntaxCategories<T>
    {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f,
            "LifetimeSyntaxCategories", "hidden", &self.hidden, "elided",
            &self.elided, "named", &&self.named)
    }
}Debug, #[automatically_derived]
impl<T: ::core::default::Default> ::core::default::Default for
    LifetimeSyntaxCategories<T> {
    #[inline]
    fn default() -> LifetimeSyntaxCategories<T> {
        LifetimeSyntaxCategories {
            hidden: ::core::default::Default::default(),
            elided: ::core::default::Default::default(),
            named: ::core::default::Default::default(),
        }
    }
}Default)]
190pub struct LifetimeSyntaxCategories<T> {
191    pub hidden: T,
192    pub elided: T,
193    pub named: T,
194}
195
196impl<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
208impl<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 iter_unnamed(&self) -> impl Iterator<Item = &T> {
218        let Self { hidden, elided, named: _ } = self;
219        std::iter::chain(hidden, elided)
220    }
221}
222
223impl 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
235fn lifetimes_use_matched_syntax(input_info: &[Info<'_>], output_info: &[Info<'_>]) -> bool {
236    let (first, inputs) = input_info.split_first().unwrap();
237    std::iter::chain(inputs, output_info).all(|info| info.syntax_category == first.syntax_category)
238}
239
240fn emit_mismatch_diagnostic<'tcx>(
241    cx: &LateContext<'tcx>,
242    input_info: &[Info<'_>],
243    output_info: &[Info<'_>],
244) {
245    // There can only ever be zero or one bound lifetime
246    // for a given lifetime resolution.
247    let mut bound_lifetime = None;
248
249    // We offer the following kinds of suggestions (when appropriate
250    // such that the suggestion wouldn't violate the lint):
251    //
252    // 1. Every lifetime becomes named, when there is already a
253    //    user-provided name.
254    //
255    // 2. A "mixed" signature, where references become implicit
256    //    and paths become explicitly anonymous.
257    //
258    // 3. Every lifetime becomes implicit.
259    //
260    // 4. Every lifetime becomes explicitly anonymous.
261    //
262    // Number 2 is arguably the most common pattern and the one we
263    // should push strongest. Number 3 is likely the next most common,
264    // followed by number 1. Coming in at a distant last would be
265    // number 4.
266    //
267    // Beyond these, there are variants of acceptable signatures that
268    // we won't suggest because they are very low-value. For example,
269    // we will never suggest `fn(&T1, &'_ T2) -> &T3` even though that
270    // would pass the lint.
271    //
272    // The following collections are the lifetime instances that we
273    // suggest changing to a given alternate style.
274
275    // 1. Convert all to named.
276    let mut suggest_change_to_explicit_bound = Vec::new();
277
278    // 2. Convert to mixed. We track each kind of change separately.
279    let mut suggest_change_to_mixed_implicit = Vec::new();
280    let mut suggest_change_to_mixed_explicit_anonymous = Vec::new();
281
282    // 3. Convert all to implicit.
283    let mut suggest_change_to_implicit = Vec::new();
284
285    // 4. Convert all to explicit anonymous.
286    let mut suggest_change_to_explicit_anonymous = Vec::new();
287
288    // Some styles prevent using implicit syntax at all.
289    let mut allow_suggesting_implicit = true;
290
291    // It only makes sense to suggest mixed if we have both sources.
292    let mut saw_a_reference = false;
293    let mut saw_a_path = false;
294
295    for info in input_info.iter().chain(output_info) {
296        use LifetimeSource::*;
297        use hir::LifetimeSyntax::*;
298
299        let lifetime = info.lifetime;
300
301        if lifetime.syntax == ExplicitBound {
302            bound_lifetime = Some(info);
303        }
304
305        match (lifetime.syntax, lifetime.source) {
306            // E.g. `&T`.
307            (Implicit, Reference) => {
308                suggest_change_to_explicit_anonymous.push(info);
309                suggest_change_to_explicit_bound.push(info);
310            }
311
312            // E.g. `&'_ T`.
313            (ExplicitAnonymous, Reference) => {
314                suggest_change_to_implicit.push(info);
315                suggest_change_to_explicit_bound.push(info);
316            }
317
318            // E.g. `ContainsLifetime`.
319            (Implicit, Path { .. }) => {
320                suggest_change_to_mixed_explicit_anonymous.push(info);
321                suggest_change_to_explicit_anonymous.push(info);
322                suggest_change_to_explicit_bound.push(info);
323            }
324
325            // E.g. `ContainsLifetime<'_>`, `+ '_`, `+ use<'_>`.
326            (ExplicitAnonymous, Path { .. } | OutlivesBound | PreciseCapturing) => {
327                suggest_change_to_explicit_bound.push(info);
328            }
329
330            // E.g. `&'a T`.
331            (ExplicitBound, Reference) => {
332                suggest_change_to_implicit.push(info);
333                suggest_change_to_mixed_implicit.push(info);
334                suggest_change_to_explicit_anonymous.push(info);
335            }
336
337            // E.g. `ContainsLifetime<'a>`, `+ 'a`, `+ use<'a>`.
338            (ExplicitBound, Path { .. } | OutlivesBound | PreciseCapturing) => {
339                suggest_change_to_mixed_explicit_anonymous.push(info);
340                suggest_change_to_explicit_anonymous.push(info);
341            }
342
343            (Implicit, OutlivesBound | PreciseCapturing) => {
344                {
    ::core::panicking::panic_fmt(format_args!("This syntax / source combination is not possible"));
};panic!("This syntax / source combination is not possible");
345            }
346
347            (_, Other) => {
348                {
    ::core::panicking::panic_fmt(format_args!("This syntax / source combination has already been skipped"));
};panic!("This syntax / source combination has already been skipped");
349            }
350        }
351
352        if #[allow(non_exhaustive_omitted_patterns)] match lifetime.source {
    Path { .. } | OutlivesBound | PreciseCapturing => true,
    _ => false,
}matches!(lifetime.source, Path { .. } | OutlivesBound | PreciseCapturing) {
353            allow_suggesting_implicit = false;
354        }
355
356        match lifetime.source {
357            Reference => saw_a_reference = true,
358            Path { .. } => saw_a_path = true,
359            _ => {}
360        }
361    }
362
363    let categorize = |infos: &[Info<'_>]| {
364        let mut categories = LifetimeSyntaxCategories::<Vec<_>>::default();
365        for info in infos {
366            categories.select(info.syntax_category).push(info.reporting_span());
367        }
368        categories
369    };
370
371    let inputs = categorize(input_info);
372    let outputs = categorize(output_info);
373
374    let make_implicit_suggestions =
375        |infos: &[&Info<'_>]| infos.iter().map(|i| i.removing_span()).collect::<Vec<_>>();
376
377    let explicit_bound_suggestion = bound_lifetime.map(|info| {
378        build_mismatch_suggestion(info.lifetime.ident.as_str(), &suggest_change_to_explicit_bound)
379    });
380
381    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_lint/src/lifetime_syntax.rs:381",
                        "rustc_lint::lifetime_syntax", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/lifetime_syntax.rs"),
                        ::tracing_core::__macro_support::Option::Some(381u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_lint::lifetime_syntax"),
                        ::tracing_core::field::FieldSet::new(&["bound_lifetime",
                                        "explicit_bound_suggestion"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&bound_lifetime)
                                            as &dyn Value)),
                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&explicit_bound_suggestion)
                                            as &dyn Value))])
            });
    } else { ; }
};tracing::debug!(?bound_lifetime, ?explicit_bound_suggestion);
382
383    let should_suggest_mixed =
384        // Do we have a mixed case?
385        (saw_a_reference && saw_a_path) &&
386        // Is there anything to change?
387        (!suggest_change_to_mixed_implicit.is_empty() ||
388         !suggest_change_to_mixed_explicit_anonymous.is_empty()) &&
389        // If we have a named lifetime, prefer consistent naming.
390        bound_lifetime.is_none();
391
392    let mixed_suggestion = should_suggest_mixed.then(|| {
393        let implicit_suggestions = make_implicit_suggestions(&suggest_change_to_mixed_implicit);
394
395        let explicit_anonymous_suggestions = build_mismatch_suggestions_for_lifetime(
396            "'_",
397            &suggest_change_to_mixed_explicit_anonymous,
398        );
399
400        lints::MismatchedLifetimeSyntaxesSuggestion::Mixed {
401            implicit_suggestions,
402            explicit_anonymous_suggestions,
403            optional_alternative: false,
404        }
405    });
406
407    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_lint/src/lifetime_syntax.rs:407",
                        "rustc_lint::lifetime_syntax", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/lifetime_syntax.rs"),
                        ::tracing_core::__macro_support::Option::Some(407u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_lint::lifetime_syntax"),
                        ::tracing_core::field::FieldSet::new(&["suggest_change_to_mixed_implicit",
                                        "suggest_change_to_mixed_explicit_anonymous",
                                        "mixed_suggestion"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&suggest_change_to_mixed_implicit)
                                            as &dyn Value)),
                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&suggest_change_to_mixed_explicit_anonymous)
                                            as &dyn Value)),
                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&mixed_suggestion)
                                            as &dyn Value))])
            });
    } else { ; }
};tracing::debug!(
408        ?suggest_change_to_mixed_implicit,
409        ?suggest_change_to_mixed_explicit_anonymous,
410        ?mixed_suggestion,
411    );
412
413    let should_suggest_implicit =
414        // Is there anything to change?
415        !suggest_change_to_implicit.is_empty() &&
416        // We never want to hide the lifetime in a path (or similar).
417        allow_suggesting_implicit &&
418        // If we have a named lifetime, prefer consistent naming.
419        bound_lifetime.is_none();
420
421    let implicit_suggestion = should_suggest_implicit.then(|| {
422        let suggestions = make_implicit_suggestions(&suggest_change_to_implicit);
423
424        lints::MismatchedLifetimeSyntaxesSuggestion::Implicit {
425            suggestions,
426            optional_alternative: false,
427        }
428    });
429
430    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_lint/src/lifetime_syntax.rs:430",
                        "rustc_lint::lifetime_syntax", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/lifetime_syntax.rs"),
                        ::tracing_core::__macro_support::Option::Some(430u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_lint::lifetime_syntax"),
                        ::tracing_core::field::FieldSet::new(&["should_suggest_implicit",
                                        "suggest_change_to_implicit", "allow_suggesting_implicit",
                                        "implicit_suggestion"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&should_suggest_implicit)
                                            as &dyn Value)),
                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&suggest_change_to_implicit)
                                            as &dyn Value)),
                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&allow_suggesting_implicit
                                            as &dyn Value)),
                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&implicit_suggestion)
                                            as &dyn Value))])
            });
    } else { ; }
};tracing::debug!(
431        ?should_suggest_implicit,
432        ?suggest_change_to_implicit,
433        allow_suggesting_implicit,
434        ?implicit_suggestion,
435    );
436
437    let should_suggest_explicit_anonymous =
438        // Is there anything to change?
439        !suggest_change_to_explicit_anonymous.is_empty() &&
440        // If we already have a mixed suggestion, avoid overlapping alternatives.
441        mixed_suggestion.is_none() &&
442        // If we have a named lifetime, prefer consistent naming.
443        bound_lifetime.is_none();
444
445    let explicit_anonymous_suggestion = should_suggest_explicit_anonymous
446        .then(|| build_mismatch_suggestion("'_", &suggest_change_to_explicit_anonymous));
447
448    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_lint/src/lifetime_syntax.rs:448",
                        "rustc_lint::lifetime_syntax", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/lifetime_syntax.rs"),
                        ::tracing_core::__macro_support::Option::Some(448u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_lint::lifetime_syntax"),
                        ::tracing_core::field::FieldSet::new(&["should_suggest_explicit_anonymous",
                                        "suggest_change_to_explicit_anonymous",
                                        "explicit_anonymous_suggestion"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&should_suggest_explicit_anonymous)
                                            as &dyn Value)),
                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&suggest_change_to_explicit_anonymous)
                                            as &dyn Value)),
                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&explicit_anonymous_suggestion)
                                            as &dyn Value))])
            });
    } else { ; }
};tracing::debug!(
449        ?should_suggest_explicit_anonymous,
450        ?suggest_change_to_explicit_anonymous,
451        ?explicit_anonymous_suggestion,
452    );
453
454    // We can produce a number of suggestions which may overwhelm
455    // the user. Instead, we order the suggestions based on Rust
456    // idioms. The "best" choice is shown to the user and the
457    // remaining choices are shown to tools only.
458    let mut suggestions = Vec::new();
459    suggestions.extend(explicit_bound_suggestion);
460    suggestions.extend(mixed_suggestion);
461    suggestions.extend(implicit_suggestion);
462    suggestions.extend(explicit_anonymous_suggestion);
463
464    cx.emit_span_lint(
465        MISMATCHED_LIFETIME_SYNTAXES,
466        inputs.iter_unnamed().chain(outputs.iter_unnamed()).copied().collect::<Vec<_>>(),
467        lints::MismatchedLifetimeSyntaxes { inputs, outputs, suggestions },
468    );
469}
470
471fn build_mismatch_suggestion(
472    lifetime_name: &str,
473    infos: &[&Info<'_>],
474) -> lints::MismatchedLifetimeSyntaxesSuggestion {
475    let lifetime_name = lifetime_name.to_owned();
476
477    let suggestions = build_mismatch_suggestions_for_lifetime(&lifetime_name, infos);
478
479    lints::MismatchedLifetimeSyntaxesSuggestion::Explicit {
480        lifetime_name,
481        suggestions,
482        optional_alternative: false,
483    }
484}
485
486fn build_mismatch_suggestions_for_lifetime(
487    lifetime_name: &str,
488    infos: &[&Info<'_>],
489) -> Vec<(Span, String)> {
490    use hir::{AngleBrackets, LifetimeSource, LifetimeSyntax};
491
492    #[derive(#[automatically_derived]
impl ::core::clone::Clone for PathSuggestionKind {
    #[inline]
    fn clone(&self) -> PathSuggestionKind { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for PathSuggestionKind { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for PathSuggestionKind {
    #[inline]
    fn eq(&self, other: &PathSuggestionKind) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for PathSuggestionKind {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {}
}Eq, #[automatically_derived]
impl ::core::hash::Hash for PathSuggestionKind {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        ::core::hash::Hash::hash(&__self_discr, state)
    }
}Hash)]
493    enum PathSuggestionKind {
494        Missing,
495        Empty,
496        Full,
497    }
498
499    let mut suggestions = Vec::new();
500    let mut path_counts: FxIndexMap<(hir::HirId, PathSuggestionKind), (Span, usize)> =
501        FxIndexMap::default();
502
503    for info in infos {
504        let lifetime = info.lifetime;
505        if #[allow(non_exhaustive_omitted_patterns)] match lifetime.syntax {
    LifetimeSyntax::Implicit => true,
    _ => false,
}matches!(lifetime.syntax, LifetimeSyntax::Implicit) {
506            if let LifetimeSource::Path { angle_brackets } = lifetime.source {
507                let (span, kind) = match angle_brackets {
508                    AngleBrackets::Missing => {
509                        (lifetime.ident.span.shrink_to_hi(), PathSuggestionKind::Missing)
510                    }
511                    AngleBrackets::Empty => (lifetime.ident.span, PathSuggestionKind::Empty),
512                    AngleBrackets::Full => (lifetime.ident.span, PathSuggestionKind::Full),
513                };
514                let entry = path_counts.entry((info.ty.hir_id, kind)).or_insert((span, 0));
515                entry.1 += 1;
516                continue;
517            }
518        }
519        suggestions.push(info.suggestion(lifetime_name));
520    }
521
522    for ((_ty_hir_id, kind), (span, count)) in path_counts {
523        let repeated = std::iter::repeat(lifetime_name).take(count).collect::<Vec<_>>().join(", ");
524
525        let suggestion = match kind {
526            PathSuggestionKind::Missing => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("<{0}>", repeated))
    })format!("<{repeated}>"),
527            PathSuggestionKind::Empty => repeated,
528            PathSuggestionKind::Full => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}, ", repeated))
    })format!("{repeated}, "),
529        };
530
531        suggestions.push((span, suggestion));
532    }
533
534    suggestions
535}
536
537#[derive(#[automatically_derived]
impl<'tcx> ::core::fmt::Debug for Info<'tcx> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f, "Info",
            "lifetime", &self.lifetime, "syntax_category",
            &self.syntax_category, "ty", &&self.ty)
    }
}Debug)]
538struct Info<'tcx> {
539    lifetime: &'tcx hir::Lifetime,
540    syntax_category: LifetimeSyntaxCategory,
541    ty: &'tcx hir::Ty<'tcx>,
542}
543
544impl<'tcx> Info<'tcx> {
545    /// When reporting a lifetime that is implicit, we expand the span
546    /// to include the type. Otherwise we end up pointing at nothing,
547    /// which is a bit confusing.
548    fn reporting_span(&self) -> Span {
549        if self.lifetime.is_implicit() { self.ty.span } else { self.lifetime.ident.span }
550    }
551
552    /// When removing an explicit lifetime from a reference,
553    /// we want to remove the whitespace after the lifetime.
554    ///
555    /// ```rust
556    /// fn x(a: &'_ u8) {}
557    /// ```
558    ///
559    /// Should become:
560    ///
561    /// ```rust
562    /// fn x(a: &u8) {}
563    /// ```
564    // FIXME: Ideally, we'd also remove the lifetime declaration.
565    fn removing_span(&self) -> Span {
566        let mut span = self.lifetime.ident.span;
567        if let hir::TyKind::Ref(_, mut_ty) = self.ty.kind {
568            span = span.until(mut_ty.ty.span);
569        }
570        span
571    }
572
573    fn suggestion(&self, lifetime_name: &str) -> (Span, String) {
574        self.lifetime.suggestion(lifetime_name)
575    }
576}
577
578struct LifetimeInfoCollector<'tcx, F> {
579    info_func: F,
580    ty: &'tcx hir::Ty<'tcx>,
581}
582
583impl<'tcx, F> LifetimeInfoCollector<'tcx, F>
584where
585    F: FnMut(Info<'tcx>),
586{
587    fn collect(ty: &'tcx hir::Ty<'tcx>, info_func: F) {
588        let mut this = Self { info_func, ty };
589
590        intravisit::walk_unambig_ty(&mut this, ty);
591    }
592}
593
594impl<'tcx, F> Visitor<'tcx> for LifetimeInfoCollector<'tcx, F>
595where
596    F: FnMut(Info<'tcx>),
597{
598    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL &&
                ::tracing::Level::INFO <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("visit_lifetime",
                                    "rustc_lint::lifetime_syntax", ::tracing::Level::INFO,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/lifetime_syntax.rs"),
                                    ::tracing_core::__macro_support::Option::Some(598u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_lint::lifetime_syntax"),
                                    ::tracing_core::field::FieldSet::new(&["lifetime"],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::INFO <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::INFO <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                let mut iter = meta.fields().iter();
                                meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&lifetime)
                                                            as &dyn Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: () = loop {};
            return __tracing_attr_fake_return;
        }
        {
            if let Some(syntax_category) =
                    LifetimeSyntaxCategory::new(lifetime) {
                let info = Info { lifetime, syntax_category, ty: self.ty };
                (self.info_func)(info);
            }
        }
    }
}#[instrument(skip(self))]
599    fn visit_lifetime(&mut self, lifetime: &'tcx hir::Lifetime) {
600        if let Some(syntax_category) = LifetimeSyntaxCategory::new(lifetime) {
601            let info = Info { lifetime, syntax_category, ty: self.ty };
602            (self.info_func)(info);
603        }
604    }
605
606    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL &&
                ::tracing::Level::INFO <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("visit_ty",
                                    "rustc_lint::lifetime_syntax", ::tracing::Level::INFO,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/lifetime_syntax.rs"),
                                    ::tracing_core::__macro_support::Option::Some(606u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_lint::lifetime_syntax"),
                                    ::tracing_core::field::FieldSet::new(&["ty"],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::INFO <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::INFO <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                let mut iter = meta.fields().iter();
                                meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&ty)
                                                            as &dyn Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: Self::Result = loop {};
            return __tracing_attr_fake_return;
        }
        {
            let old_ty = std::mem::replace(&mut self.ty, ty.as_unambig_ty());
            intravisit::walk_ty(self, ty);
            self.ty = old_ty;
        }
    }
}#[instrument(skip(self))]
607    fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) -> Self::Result {
608        let old_ty = std::mem::replace(&mut self.ty, ty.as_unambig_ty());
609        intravisit::walk_ty(self, ty);
610        self.ty = old_ty;
611    }
612}