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 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 (Implicit, Reference) |
167 (ExplicitAnonymous, Reference) |
169 (ExplicitAnonymous, Path { .. }) |
171 (ExplicitAnonymous, OutlivesBound | PreciseCapturing) => Some(Self::Elided),
173
174 (Implicit, Path { .. }) => Some(Self::Hidden),
176
177 (ExplicitBound, Reference) |
179 (ExplicitBound, Path { .. }) |
181 (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 let mut bound_lifetime = None;
248
249 let mut suggest_change_to_explicit_bound = Vec::new();
277
278 let mut suggest_change_to_mixed_implicit = Vec::new();
280 let mut suggest_change_to_mixed_explicit_anonymous = Vec::new();
281
282 let mut suggest_change_to_implicit = Vec::new();
284
285 let mut suggest_change_to_explicit_anonymous = Vec::new();
287
288 let mut allow_suggesting_implicit = true;
290
291 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 (Implicit, Reference) => {
308 suggest_change_to_explicit_anonymous.push(info);
309 suggest_change_to_explicit_bound.push(info);
310 }
311
312 (ExplicitAnonymous, Reference) => {
314 suggest_change_to_implicit.push(info);
315 suggest_change_to_explicit_bound.push(info);
316 }
317
318 (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 (ExplicitAnonymous, Path { .. } | OutlivesBound | PreciseCapturing) => {
327 suggest_change_to_explicit_bound.push(info);
328 }
329
330 (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 (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 (saw_a_reference && saw_a_path) &&
386 (!suggest_change_to_mixed_implicit.is_empty() ||
388 !suggest_change_to_mixed_explicit_anonymous.is_empty()) &&
389 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 !suggest_change_to_implicit.is_empty() &&
416 allow_suggesting_implicit &&
418 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 !suggest_change_to_explicit_anonymous.is_empty() &&
440 mixed_suggestion.is_none() &&
442 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 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 fn reporting_span(&self) -> Span {
549 if self.lifetime.is_implicit() { self.ty.span } else { self.lifetime.ident.span }
550 }
551
552 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}