Introduce the mismatched_lifetime_syntaxes lint

This commit is contained in:
Jake Goulding 2025-03-28 16:12:07 -04:00
parent d2bf16ad6d
commit 9a50cb4a0c
8 changed files with 1447 additions and 3 deletions

View file

@ -206,7 +206,7 @@ impl ParamName {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable_Generic)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable_Generic)]
pub enum LifetimeKind {
/// User-given names or fresh (synthetic) names.
Param(LocalDefId),

View file

@ -518,6 +518,28 @@ lint_metavariable_still_repeating = variable `{$name}` is still repeating at thi
lint_metavariable_wrong_operator = meta-variable repeats with different Kleene operator
lint_mismatched_lifetime_syntaxes =
lifetime flowing from input to output with different syntax can be confusing
.label_mismatched_lifetime_syntaxes_inputs =
{$n_inputs ->
[one] this lifetime flows
*[other] these lifetimes flow
} to the output
.label_mismatched_lifetime_syntaxes_outputs =
the {$n_outputs ->
[one] lifetime gets
*[other] lifetimes get
} resolved as `{$lifetime_name}`
lint_mismatched_lifetime_syntaxes_suggestion_explicit =
one option is to consistently use `{$lifetime_name}`
lint_mismatched_lifetime_syntaxes_suggestion_implicit =
one option is to consistently remove the lifetime
lint_mismatched_lifetime_syntaxes_suggestion_mixed =
one option is to remove the lifetime for references and use the anonymous lifetime for paths
lint_missing_fragment_specifier = missing fragment specifier
lint_missing_unsafe_on_extern = extern blocks should be unsafe

View file

@ -55,6 +55,7 @@ mod invalid_from_utf8;
mod late;
mod let_underscore;
mod levels;
mod lifetime_syntax;
mod lints;
mod macro_expr_fragment_specifier_2024_migration;
mod map_unit_fn;
@ -96,6 +97,7 @@ use impl_trait_overcaptures::ImplTraitOvercaptures;
use internal::*;
use invalid_from_utf8::*;
use let_underscore::*;
use lifetime_syntax::*;
use macro_expr_fragment_specifier_2024_migration::*;
use map_unit_fn::*;
use multiple_supertrait_upcastable::*;
@ -246,6 +248,7 @@ late_lint_methods!(
StaticMutRefs: StaticMutRefs,
UnqualifiedLocalImports: UnqualifiedLocalImports,
CheckTransmutes: CheckTransmutes,
LifetimeSyntax: LifetimeSyntax,
]
]
);

View file

@ -0,0 +1,503 @@
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{self as hir, LifetimeSource};
use rustc_session::{declare_lint, declare_lint_pass};
use rustc_span::Span;
use tracing::instrument;
use crate::{LateContext, LateLintPass, LintContext, lints};
declare_lint! {
/// The `mismatched_lifetime_syntaxes` lint detects when the same
/// lifetime is referred to by different syntaxes between function
/// arguments and return values.
///
/// The three kinds of syntaxes are:
///
/// 1. Named lifetimes. These are references (`&'a str`) or paths
/// (`Person<'a>`) that use a lifetime with a name, such as
/// `'static` or `'a`.
///
/// 2. Elided lifetimes. These are references with no explicit
/// lifetime (`&str`), references using the anonymous lifetime
/// (`&'_ str`), and paths using the anonymous lifetime
/// (`Person<'_>`).
///
/// 3. Hidden lifetimes. These are paths that do not contain any
/// visual indication that it contains a lifetime (`Person`).
///
/// ### Example
///
/// ```rust,compile_fail
/// #![deny(mismatched_lifetime_syntaxes)]
///
/// pub fn mixing_named_with_elided(v: &'static u8) -> &u8 {
/// v
/// }
///
/// struct Person<'a> {
/// name: &'a str,
/// }
///
/// pub fn mixing_hidden_with_elided(v: Person) -> Person<'_> {
/// v
/// }
///
/// struct Foo;
///
/// impl Foo {
/// // Lifetime elision results in the output lifetime becoming
/// // `'static`, which is not what was intended.
/// pub fn get_mut(&'static self, x: &mut u8) -> &mut u8 {
/// unsafe { &mut *(x as *mut _) }
/// }
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Lifetime elision is useful because it frees you from having to
/// give each lifetime its own name and show the relation of input
/// and output lifetimes for common cases. However, a lifetime
/// that uses inconsistent syntax between related arguments and
/// return values is more confusing.
///
/// In certain `unsafe` code, lifetime elision combined with
/// inconsistent lifetime syntax may result in unsound code.
pub MISMATCHED_LIFETIME_SYNTAXES,
Allow,
"detects when a lifetime uses different syntax between arguments and return values"
}
declare_lint_pass!(LifetimeSyntax => [MISMATCHED_LIFETIME_SYNTAXES]);
impl<'tcx> LateLintPass<'tcx> for LifetimeSyntax {
#[instrument(skip_all)]
fn check_fn(
&mut self,
cx: &LateContext<'tcx>,
_: hir::intravisit::FnKind<'tcx>,
fd: &'tcx hir::FnDecl<'tcx>,
_: &'tcx hir::Body<'tcx>,
_: rustc_span::Span,
_: rustc_span::def_id::LocalDefId,
) {
let mut input_map = Default::default();
let mut output_map = Default::default();
for input in fd.inputs {
LifetimeInfoCollector::collect(input, &mut input_map);
}
if let hir::FnRetTy::Return(output) = fd.output {
LifetimeInfoCollector::collect(output, &mut output_map);
}
report_mismatches(cx, &input_map, &output_map);
}
}
#[instrument(skip_all)]
fn report_mismatches<'tcx>(
cx: &LateContext<'tcx>,
inputs: &LifetimeInfoMap<'tcx>,
outputs: &LifetimeInfoMap<'tcx>,
) {
for (resolved_lifetime, output_info) in outputs {
if let Some(input_info) = inputs.get(resolved_lifetime) {
if !lifetimes_use_matched_syntax(input_info, output_info) {
emit_mismatch_diagnostic(cx, input_info, output_info);
}
}
}
}
fn lifetimes_use_matched_syntax(input_info: &[Info<'_>], output_info: &[Info<'_>]) -> bool {
// Categorize lifetimes into source/syntax buckets.
let mut n_hidden = 0;
let mut n_elided = 0;
let mut n_named = 0;
for info in input_info.iter().chain(output_info) {
use LifetimeSource::*;
use hir::LifetimeSyntax::*;
let syntax_source = (info.lifetime.syntax, info.lifetime.source);
match syntax_source {
// Ignore any other kind of lifetime.
(_, Other) => continue,
// E.g. `&T`.
(Implicit, Reference | OutlivesBound | PreciseCapturing) |
// E.g. `&'_ T`.
(ExplicitAnonymous, Reference | OutlivesBound | PreciseCapturing) |
// E.g. `ContainsLifetime<'_>`.
(ExplicitAnonymous, Path { .. }) => n_elided += 1,
// E.g. `ContainsLifetime`.
(Implicit, Path { .. }) => n_hidden += 1,
// E.g. `&'a T`.
(ExplicitBound, Reference | OutlivesBound | PreciseCapturing) |
// E.g. `ContainsLifetime<'a>`.
(ExplicitBound, Path { .. }) => n_named += 1,
};
}
let syntax_counts = (n_hidden, n_elided, n_named);
tracing::debug!(?syntax_counts);
matches!(syntax_counts, (_, 0, 0) | (0, _, 0) | (0, 0, _))
}
fn emit_mismatch_diagnostic<'tcx>(
cx: &LateContext<'tcx>,
input_info: &[Info<'_>],
output_info: &[Info<'_>],
) {
// There can only ever be zero or one bound lifetime
// for a given lifetime resolution.
let mut bound_lifetime = None;
// We offer the following kinds of suggestions (when appropriate
// such that the suggestion wouldn't violate the lint):
//
// 1. Every lifetime becomes named, when there is already a
// user-provided name.
//
// 2. A "mixed" signature, where references become implicit
// and paths become explicitly anonymous.
//
// 3. Every lifetime becomes implicit.
//
// 4. Every lifetime becomes explicitly anonymous.
//
// Number 2 is arguably the most common pattern and the one we
// should push strongest. Number 3 is likely the next most common,
// followed by number 1. Coming in at a distant last would be
// number 4.
//
// Beyond these, there are variants of acceptable signatures that
// we won't suggest because they are very low-value. For example,
// we will never suggest `fn(&T1, &'_ T2) -> &T3` even though that
// would pass the lint.
//
// The following collections are the lifetime instances that we
// suggest changing to a given alternate style.
// 1. Convert all to named.
let mut suggest_change_to_explicit_bound = Vec::new();
// 2. Convert to mixed. We track each kind of change separately.
let mut suggest_change_to_mixed_implicit = Vec::new();
let mut suggest_change_to_mixed_explicit_anonymous = Vec::new();
// 3. Convert all to implicit.
let mut suggest_change_to_implicit = Vec::new();
// 4. Convert all to explicit anonymous.
let mut suggest_change_to_explicit_anonymous = Vec::new();
// Some styles prevent using implicit syntax at all.
let mut allow_suggesting_implicit = true;
// It only makes sense to suggest mixed if we have both sources.
let mut saw_a_reference = false;
let mut saw_a_path = false;
for info in input_info.iter().chain(output_info) {
use LifetimeSource::*;
use hir::LifetimeSyntax::*;
let syntax_source = (info.lifetime.syntax, info.lifetime.source);
if let (_, Other) = syntax_source {
// Ignore any other kind of lifetime.
continue;
}
if let (ExplicitBound, _) = syntax_source {
bound_lifetime = Some(info);
}
match syntax_source {
// E.g. `&T`.
(Implicit, Reference) => {
suggest_change_to_explicit_anonymous.push(info);
suggest_change_to_explicit_bound.push(info);
}
// E.g. `&'_ T`.
(ExplicitAnonymous, Reference) => {
suggest_change_to_implicit.push(info);
suggest_change_to_mixed_implicit.push(info);
suggest_change_to_explicit_bound.push(info);
}
// E.g. `ContainsLifetime`.
(Implicit, Path { .. }) => {
suggest_change_to_mixed_explicit_anonymous.push(info);
suggest_change_to_explicit_anonymous.push(info);
suggest_change_to_explicit_bound.push(info);
}
// E.g. `ContainsLifetime<'_>`.
(ExplicitAnonymous, Path { .. }) => {
suggest_change_to_explicit_bound.push(info);
}
// E.g. `&'a T`.
(ExplicitBound, Reference) => {
suggest_change_to_implicit.push(info);
suggest_change_to_mixed_implicit.push(info);
suggest_change_to_explicit_anonymous.push(info);
}
// E.g. `ContainsLifetime<'a>`.
(ExplicitBound, Path { .. }) => {
suggest_change_to_mixed_explicit_anonymous.push(info);
suggest_change_to_explicit_anonymous.push(info);
}
(Implicit, OutlivesBound | PreciseCapturing) => {
panic!("This syntax / source combination is not possible");
}
// E.g. `+ '_`, `+ use<'_>`.
(ExplicitAnonymous, OutlivesBound | PreciseCapturing) => {
suggest_change_to_explicit_bound.push(info);
}
// E.g. `+ 'a`, `+ use<'a>`.
(ExplicitBound, OutlivesBound | PreciseCapturing) => {
suggest_change_to_mixed_explicit_anonymous.push(info);
suggest_change_to_explicit_anonymous.push(info);
}
(_, Other) => {
panic!("This syntax / source combination has already been skipped");
}
}
if matches!(syntax_source, (_, Path { .. } | OutlivesBound | PreciseCapturing)) {
allow_suggesting_implicit = false;
}
match syntax_source {
(_, Reference) => saw_a_reference = true,
(_, Path { .. }) => saw_a_path = true,
_ => {}
}
}
let make_implicit_suggestions =
|infos: &[&Info<'_>]| infos.iter().map(|i| i.removing_span()).collect::<Vec<_>>();
let inputs = input_info.iter().map(|info| info.reporting_span()).collect();
let outputs = output_info.iter().map(|info| info.reporting_span()).collect();
let explicit_bound_suggestion = bound_lifetime.map(|info| {
build_mismatch_suggestion(info.lifetime_name(), &suggest_change_to_explicit_bound)
});
let is_bound_static = bound_lifetime.is_some_and(|info| info.is_static());
tracing::debug!(?bound_lifetime, ?explicit_bound_suggestion, ?is_bound_static);
let should_suggest_mixed =
// Do we have a mixed case?
(saw_a_reference && saw_a_path) &&
// Is there anything to change?
(!suggest_change_to_mixed_implicit.is_empty() ||
!suggest_change_to_mixed_explicit_anonymous.is_empty()) &&
// If we have `'static`, we don't want to remove it.
!is_bound_static;
let mixed_suggestion = should_suggest_mixed.then(|| {
let implicit_suggestions = make_implicit_suggestions(&suggest_change_to_mixed_implicit);
let explicit_anonymous_suggestions = suggest_change_to_mixed_explicit_anonymous
.iter()
.map(|info| info.suggestion("'_"))
.collect();
lints::MismatchedLifetimeSyntaxesSuggestion::Mixed {
implicit_suggestions,
explicit_anonymous_suggestions,
tool_only: false,
}
});
tracing::debug!(
?suggest_change_to_mixed_implicit,
?suggest_change_to_mixed_explicit_anonymous,
?mixed_suggestion,
);
let should_suggest_implicit =
// Is there anything to change?
!suggest_change_to_implicit.is_empty() &&
// We never want to hide the lifetime in a path (or similar).
allow_suggesting_implicit &&
// If we have `'static`, we don't want to remove it.
!is_bound_static;
let implicit_suggestion = should_suggest_implicit.then(|| {
let suggestions = make_implicit_suggestions(&suggest_change_to_implicit);
lints::MismatchedLifetimeSyntaxesSuggestion::Implicit { suggestions, tool_only: false }
});
tracing::debug!(
?should_suggest_implicit,
?suggest_change_to_implicit,
allow_suggesting_implicit,
?implicit_suggestion,
);
let should_suggest_explicit_anonymous =
// Is there anything to change?
!suggest_change_to_explicit_anonymous.is_empty() &&
// If we have `'static`, we don't want to remove it.
!is_bound_static;
let explicit_anonymous_suggestion = should_suggest_explicit_anonymous
.then(|| build_mismatch_suggestion("'_", &suggest_change_to_explicit_anonymous));
tracing::debug!(
?should_suggest_explicit_anonymous,
?suggest_change_to_explicit_anonymous,
?explicit_anonymous_suggestion,
);
let lifetime_name = bound_lifetime.map(|info| info.lifetime_name()).unwrap_or("'_").to_owned();
// We can produce a number of suggestions which may overwhelm
// the user. Instead, we order the suggestions based on Rust
// idioms. The "best" choice is shown to the user and the
// remaining choices are shown to tools only.
let mut suggestions = Vec::new();
suggestions.extend(explicit_bound_suggestion);
suggestions.extend(mixed_suggestion);
suggestions.extend(implicit_suggestion);
suggestions.extend(explicit_anonymous_suggestion);
cx.emit_span_lint(
MISMATCHED_LIFETIME_SYNTAXES,
Vec::clone(&inputs),
lints::MismatchedLifetimeSyntaxes { lifetime_name, inputs, outputs, suggestions },
);
}
fn build_mismatch_suggestion(
lifetime_name: &str,
infos: &[&Info<'_>],
) -> lints::MismatchedLifetimeSyntaxesSuggestion {
let lifetime_name = lifetime_name.to_owned();
let suggestions = infos.iter().map(|info| info.suggestion(&lifetime_name)).collect();
lints::MismatchedLifetimeSyntaxesSuggestion::Explicit {
lifetime_name,
suggestions,
tool_only: false,
}
}
#[derive(Debug)]
struct Info<'tcx> {
type_span: Span,
referenced_type_span: Option<Span>,
lifetime: &'tcx hir::Lifetime,
}
impl<'tcx> Info<'tcx> {
fn lifetime_name(&self) -> &str {
self.lifetime.ident.as_str()
}
fn is_static(&self) -> bool {
self.lifetime.is_static()
}
/// When reporting a lifetime that is implicit, we expand the span
/// to include the type. Otherwise we end up pointing at nothing,
/// which is a bit confusing.
fn reporting_span(&self) -> Span {
if self.lifetime.is_implicit() { self.type_span } else { self.lifetime.ident.span }
}
/// When removing an explicit lifetime from a reference,
/// we want to remove the whitespace after the lifetime.
///
/// ```rust
/// fn x(a: &'_ u8) {}
/// ```
///
/// Should become:
///
/// ```rust
/// fn x(a: &u8) {}
/// ```
// FIXME: Ideally, we'd also remove the lifetime declaration.
fn removing_span(&self) -> Span {
let mut span = self.suggestion("'dummy").0;
if let Some(referenced_type_span) = self.referenced_type_span {
span = span.until(referenced_type_span);
}
span
}
fn suggestion(&self, lifetime_name: &str) -> (Span, String) {
self.lifetime.suggestion(lifetime_name)
}
}
type LifetimeInfoMap<'tcx> = FxIndexMap<&'tcx hir::LifetimeKind, Vec<Info<'tcx>>>;
struct LifetimeInfoCollector<'a, 'tcx> {
type_span: Span,
referenced_type_span: Option<Span>,
map: &'a mut LifetimeInfoMap<'tcx>,
}
impl<'a, 'tcx> LifetimeInfoCollector<'a, 'tcx> {
fn collect(ty: &'tcx hir::Ty<'tcx>, map: &'a mut LifetimeInfoMap<'tcx>) {
let mut this = Self { type_span: ty.span, referenced_type_span: None, map };
intravisit::walk_unambig_ty(&mut this, ty);
}
}
impl<'a, 'tcx> Visitor<'tcx> for LifetimeInfoCollector<'a, 'tcx> {
#[instrument(skip(self))]
fn visit_lifetime(&mut self, lifetime: &'tcx hir::Lifetime) {
let type_span = self.type_span;
let referenced_type_span = self.referenced_type_span;
let info = Info { type_span, referenced_type_span, lifetime };
self.map.entry(&lifetime.kind).or_default().push(info);
}
#[instrument(skip(self))]
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) -> Self::Result {
let old_type_span = self.type_span;
let old_referenced_type_span = self.referenced_type_span;
self.type_span = ty.span;
if let hir::TyKind::Ref(_, ty) = ty.kind {
self.referenced_type_span = Some(ty.ty.span);
}
intravisit::walk_ty(self, ty);
self.type_span = old_type_span;
self.referenced_type_span = old_referenced_type_span;
}
}

View file

@ -3241,3 +3241,128 @@ pub(crate) struct ReservedMultihash {
#[suggestion(code = " ", applicability = "machine-applicable")]
pub suggestion: Span,
}
#[derive(Debug)]
pub(crate) struct MismatchedLifetimeSyntaxes {
pub lifetime_name: String,
pub inputs: Vec<Span>,
pub outputs: Vec<Span>,
pub suggestions: Vec<MismatchedLifetimeSyntaxesSuggestion>,
}
impl<'a, G: EmissionGuarantee> LintDiagnostic<'a, G> for MismatchedLifetimeSyntaxes {
fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>) {
diag.primary_message(fluent::lint_mismatched_lifetime_syntaxes);
diag.arg("lifetime_name", self.lifetime_name);
diag.arg("n_inputs", self.inputs.len());
for input in self.inputs {
let a = diag.eagerly_translate(fluent::lint_label_mismatched_lifetime_syntaxes_inputs);
diag.span_label(input, a);
}
diag.arg("n_outputs", self.outputs.len());
for output in self.outputs {
let a = diag.eagerly_translate(fluent::lint_label_mismatched_lifetime_syntaxes_outputs);
diag.span_label(output, a);
}
let mut suggestions = self.suggestions.into_iter();
if let Some(s) = suggestions.next() {
diag.subdiagnostic(s);
for mut s in suggestions {
s.make_tool_only();
diag.subdiagnostic(s);
}
}
}
}
#[derive(Debug)]
pub(crate) enum MismatchedLifetimeSyntaxesSuggestion {
Implicit {
suggestions: Vec<Span>,
tool_only: bool,
},
Mixed {
implicit_suggestions: Vec<Span>,
explicit_anonymous_suggestions: Vec<(Span, String)>,
tool_only: bool,
},
Explicit {
lifetime_name: String,
suggestions: Vec<(Span, String)>,
tool_only: bool,
},
}
impl MismatchedLifetimeSyntaxesSuggestion {
fn make_tool_only(&mut self) {
use MismatchedLifetimeSyntaxesSuggestion::*;
let tool_only = match self {
Implicit { tool_only, .. } | Mixed { tool_only, .. } | Explicit { tool_only, .. } => {
tool_only
}
};
*tool_only = true;
}
}
impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
use MismatchedLifetimeSyntaxesSuggestion::*;
let style = |tool_only| {
if tool_only { SuggestionStyle::CompletelyHidden } else { SuggestionStyle::ShowAlways }
};
match self {
Implicit { suggestions, tool_only } => {
let suggestions = suggestions.into_iter().map(|s| (s, String::new())).collect();
diag.multipart_suggestion_with_style(
fluent::lint_mismatched_lifetime_syntaxes_suggestion_implicit,
suggestions,
Applicability::MachineApplicable,
style(tool_only),
);
}
Mixed { implicit_suggestions, explicit_anonymous_suggestions, tool_only } => {
let implicit_suggestions =
implicit_suggestions.into_iter().map(|s| (s, String::new()));
let suggestions =
implicit_suggestions.chain(explicit_anonymous_suggestions).collect();
diag.multipart_suggestion_with_style(
fluent::lint_mismatched_lifetime_syntaxes_suggestion_mixed,
suggestions,
Applicability::MachineApplicable,
style(tool_only),
);
}
Explicit { lifetime_name, suggestions, tool_only } => {
diag.arg("lifetime_name", lifetime_name);
let msg = diag.eagerly_translate(
fluent::lint_mismatched_lifetime_syntaxes_suggestion_explicit,
);
diag.multipart_suggestion_with_style(
msg,
suggestions,
Applicability::MachineApplicable,
style(tool_only),
);
}
}
}
}

View file

@ -19,11 +19,11 @@ fn fat_ptr_via_local(a: &[u8]) -> &[u8] {
x
}
fn fat_ptr_from_struct(s: FatPtrContainer) -> &[u8] {
fn fat_ptr_from_struct(s: FatPtrContainer<'_>) -> &[u8] {
s.ptr
}
fn fat_ptr_to_struct(a: &[u8]) -> FatPtrContainer {
fn fat_ptr_to_struct(a: &[u8]) -> FatPtrContainer<'_> {
FatPtrContainer { ptr: a }
}

View file

@ -0,0 +1,318 @@
#![deny(mismatched_lifetime_syntaxes)]
// `elided_named_lifetimes` overlaps with `lifetime_style_mismatch`, ignore it for now
#![allow(elided_named_lifetimes)]
#[derive(Copy, Clone)]
struct ContainsLifetime<'a>(&'a u8);
struct S(u8);
fn explicit_bound_ref_to_implicit_ref<'a>(v: &'a u8) -> &u8 {
//~^ ERROR lifetime flowing from input to output with different syntax
v
}
fn explicit_bound_ref_to_explicit_anonymous_ref<'a>(v: &'a u8) -> &'_ u8 {
//~^ ERROR lifetime flowing from input to output with different syntax
v
}
// ---
fn implicit_path_to_explicit_anonymous_path(v: ContainsLifetime) -> ContainsLifetime<'_> {
//~^ ERROR lifetime flowing from input to output with different syntax
v
}
fn explicit_anonymous_path_to_implicit_path(v: ContainsLifetime<'_>) -> ContainsLifetime {
//~^ ERROR lifetime flowing from input to output with different syntax
v
}
fn explicit_bound_path_to_implicit_path<'a>(v: ContainsLifetime<'a>) -> ContainsLifetime {
//~^ ERROR lifetime flowing from input to output with different syntax
v
}
fn explicit_bound_path_to_explicit_anonymous_path<'a>(
v: ContainsLifetime<'a>,
//~^ ERROR lifetime flowing from input to output with different syntax
) -> ContainsLifetime<'_> {
v
}
// ---
fn implicit_ref_to_implicit_path(v: &u8) -> ContainsLifetime {
//~^ ERROR lifetime flowing from input to output with different syntax
ContainsLifetime(v)
}
fn explicit_anonymous_ref_to_implicit_path(v: &'_ u8) -> ContainsLifetime {
//~^ ERROR lifetime flowing from input to output with different syntax
ContainsLifetime(v)
}
fn explicit_bound_ref_to_implicit_path<'a>(v: &'a u8) -> ContainsLifetime {
//~^ ERROR lifetime flowing from input to output with different syntax
ContainsLifetime(v)
}
fn explicit_bound_ref_to_explicit_anonymous_path<'a>(v: &'a u8) -> ContainsLifetime<'_> {
//~^ ERROR lifetime flowing from input to output with different syntax
ContainsLifetime(v)
}
// ---
fn implicit_path_to_implicit_ref(v: ContainsLifetime) -> &u8 {
//~^ ERROR lifetime flowing from input to output with different syntax
v.0
}
fn implicit_path_to_explicit_anonymous_ref(v: ContainsLifetime) -> &'_ u8 {
//~^ ERROR lifetime flowing from input to output with different syntax
v.0
}
fn explicit_bound_path_to_implicit_ref<'a>(v: ContainsLifetime<'a>) -> &u8 {
//~^ ERROR lifetime flowing from input to output with different syntax
v.0
}
fn explicit_bound_path_to_explicit_anonymous_ref<'a>(v: ContainsLifetime<'a>) -> &'_ u8 {
//~^ ERROR lifetime flowing from input to output with different syntax
v.0
}
impl S {
fn method_explicit_bound_ref_to_implicit_ref<'a>(&'a self) -> &u8 {
//~^ ERROR lifetime flowing from input to output with different syntax
&self.0
}
fn method_explicit_bound_ref_to_explicit_anonymous_ref<'a>(&'a self) -> &'_ u8 {
//~^ ERROR lifetime flowing from input to output with different syntax
&self.0
}
// ---
fn method_explicit_anonymous_ref_to_implicit_path(&'_ self) -> ContainsLifetime {
//~^ ERROR lifetime flowing from input to output with different syntax
ContainsLifetime(&self.0)
}
fn method_explicit_bound_ref_to_implicit_path<'a>(&'a self) -> ContainsLifetime {
//~^ ERROR lifetime flowing from input to output with different syntax
ContainsLifetime(&self.0)
}
fn method_explicit_bound_ref_to_explicit_anonymous_path<'a>(&'a self) -> ContainsLifetime<'_> {
//~^ ERROR lifetime flowing from input to output with different syntax
ContainsLifetime(&self.0)
}
}
// If a function uses the `'static` lifetime, we should not suggest
// replacing it with an explicitly anonymous or implicit
// lifetime. Only suggest using `'static` everywhere.
mod static_suggestions {
#[derive(Copy, Clone)]
struct ContainsLifetime<'a>(&'a u8);
struct S(u8);
fn static_ref_to_implicit_ref(v: &'static u8) -> &u8 {
//~^ ERROR lifetime flowing from input to output with different syntax
v
}
fn static_ref_to_explicit_anonymous_ref(v: &'static u8) -> &'_ u8 {
//~^ ERROR lifetime flowing from input to output with different syntax
v
}
fn static_ref_to_implicit_path(v: &'static u8) -> ContainsLifetime {
//~^ ERROR lifetime flowing from input to output with different syntax
ContainsLifetime(v)
}
fn static_ref_to_explicit_anonymous_path(v: &'static u8) -> ContainsLifetime<'_> {
//~^ ERROR lifetime flowing from input to output with different syntax
ContainsLifetime(v)
}
impl S {
fn static_ref_to_implicit_ref(&'static self) -> &u8 {
//~^ ERROR lifetime flowing from input to output with different syntax
&self.0
}
fn static_ref_to_explicit_anonymous_ref(&'static self) -> &'_ u8 {
//~^ ERROR lifetime flowing from input to output with different syntax
&self.0
}
fn static_ref_to_implicit_path(&'static self) -> ContainsLifetime {
//~^ ERROR lifetime flowing from input to output with different syntax
ContainsLifetime(&self.0)
}
fn static_ref_to_explicit_anonymous_path(&'static self) -> ContainsLifetime<'_> {
//~^ ERROR lifetime flowing from input to output with different syntax
ContainsLifetime(&self.0)
}
}
}
/// `impl Trait` uses lifetimes in some additional ways.
mod impl_trait {
#[derive(Copy, Clone)]
struct ContainsLifetime<'a>(&'a u8);
fn explicit_bound_ref_to_impl_trait_bound<'a>(v: &'a u8) -> impl FnOnce() + '_ {
//~^ ERROR lifetime flowing from input to output with different syntax
move || _ = v
}
fn explicit_bound_ref_to_impl_trait_precise_capture<'a>(v: &'a u8) -> impl FnOnce() + use<'_> {
//~^ ERROR lifetime flowing from input to output with different syntax
move || _ = v
}
fn explicit_bound_path_to_impl_trait_bound<'a>(v: ContainsLifetime<'a>) -> impl FnOnce() + '_ {
//~^ ERROR lifetime flowing from input to output with different syntax
move || _ = v
}
fn explicit_bound_path_to_impl_trait_precise_capture<'a>(
v: ContainsLifetime<'a>,
//~^ ERROR lifetime flowing from input to output with different syntax
) -> impl FnOnce() + use<'_> {
move || _ = v
}
}
/// `dyn Trait` uses lifetimes in some additional ways.
mod dyn_trait {
use std::iter;
#[derive(Copy, Clone)]
struct ContainsLifetime<'a>(&'a u8);
fn explicit_bound_ref_to_dyn_trait_bound<'a>(v: &'a u8) -> Box<dyn Iterator<Item = &u8> + '_> {
//~^ ERROR lifetime flowing from input to output with different syntax
Box::new(iter::once(v))
}
fn explicit_bound_path_to_dyn_trait_bound<'a>(
v: ContainsLifetime<'a>,
//~^ ERROR lifetime flowing from input to output with different syntax
) -> Box<dyn Iterator<Item = ContainsLifetime> + '_> {
Box::new(iter::once(v))
}
}
/// These tests serve to exercise edge cases of the lint formatting
mod diagnostic_output {
fn multiple_outputs<'a>(v: &'a u8) -> (&u8, &u8) {
//~^ ERROR lifetime flowing from input to output with different syntax
(v, v)
}
}
/// These usages are expected to **not** trigger the lint
mod acceptable_uses {
#[derive(Copy, Clone)]
struct ContainsLifetime<'a>(&'a u8);
struct S(u8);
fn implicit_ref_to_implicit_ref(v: &u8) -> &u8 {
v
}
fn explicit_anonymous_ref_to_explicit_anonymous_ref(v: &'_ u8) -> &'_ u8 {
v
}
fn explicit_bound_ref_to_explicit_bound_ref<'a>(v: &'a u8) -> &'a u8 {
v
}
fn implicit_path_to_implicit_path(v: ContainsLifetime) -> ContainsLifetime {
v
}
fn explicit_anonymous_path_to_explicit_anonymous_path(
v: ContainsLifetime<'_>,
) -> ContainsLifetime<'_> {
v
}
fn explicit_bound_path_to_explicit_bound_path<'a>(
v: ContainsLifetime<'a>,
) -> ContainsLifetime<'a> {
v
}
fn explicit_anonymous_ref_to_explicit_anonymous_path(v: &'_ u8) -> ContainsLifetime<'_> {
ContainsLifetime(v)
}
fn explicit_bound_ref_to_explicit_bound_path<'a>(v: &'a u8) -> ContainsLifetime<'a> {
ContainsLifetime(v)
}
fn explicit_anonymous_path_to_explicit_anonymous_ref(v: ContainsLifetime<'_>) -> &'_ u8 {
v.0
}
fn explicit_bound_path_to_explicit_bound_ref<'a>(v: ContainsLifetime<'a>) -> &'a u8 {
v.0
}
// These may be surprising, but ampersands count as enough of a
// visual indicator that a reference exists that we treat
// references with implicit lifetimes the same as if they were
// explicitly anonymous.
fn implicit_ref_to_explicit_anonymous_ref(v: &u8) -> &'_ u8 {
v
}
fn explicit_anonymous_ref_to_implicit_ref(v: &'_ u8) -> &u8 {
v
}
fn implicit_ref_to_explicit_anonymous_path(v: &u8) -> ContainsLifetime<'_> {
ContainsLifetime(v)
}
fn explicit_anonymous_path_to_implicit_ref(v: ContainsLifetime<'_>) -> &u8 {
v.0
}
impl S {
fn method_implicit_ref_to_explicit_anonymous_ref(&self) -> &'_ u8 {
&self.0
}
fn method_explicit_anonymous_ref_to_implicit_ref(&'_ self) -> &u8 {
&self.0
}
fn method_implicit_ref_to_explicit_anonymous_path(&self) -> ContainsLifetime<'_> {
ContainsLifetime(&self.0)
}
}
// `dyn Trait` has an "embedded" lifetime that we should **not**
// lint about.
fn dyn_trait_does_not_have_a_lifetime_generic(v: &u8) -> &dyn core::fmt::Debug {
v
}
}
fn main() {}

View file

@ -0,0 +1,473 @@
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:11:47
|
LL | fn explicit_bound_ref_to_implicit_ref<'a>(v: &'a u8) -> &u8 {
| ^^ --- the lifetime gets resolved as `'a`
| |
| this lifetime flows to the output
|
note: the lint level is defined here
--> $DIR/mismatched-lifetime-syntaxes.rs:1:9
|
LL | #![deny(mismatched_lifetime_syntaxes)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: one option is to consistently use `'a`
|
LL | fn explicit_bound_ref_to_implicit_ref<'a>(v: &'a u8) -> &'a u8 {
| ++
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:16:57
|
LL | fn explicit_bound_ref_to_explicit_anonymous_ref<'a>(v: &'a u8) -> &'_ u8 {
| ^^ -- the lifetime gets resolved as `'a`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'a`
|
LL - fn explicit_bound_ref_to_explicit_anonymous_ref<'a>(v: &'a u8) -> &'_ u8 {
LL + fn explicit_bound_ref_to_explicit_anonymous_ref<'a>(v: &'a u8) -> &'a u8 {
|
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:23:48
|
LL | fn implicit_path_to_explicit_anonymous_path(v: ContainsLifetime) -> ContainsLifetime<'_> {
| ^^^^^^^^^^^^^^^^ -- the lifetime gets resolved as `'_`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'_`
|
LL | fn implicit_path_to_explicit_anonymous_path(v: ContainsLifetime<'_>) -> ContainsLifetime<'_> {
| ++++
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:28:65
|
LL | fn explicit_anonymous_path_to_implicit_path(v: ContainsLifetime<'_>) -> ContainsLifetime {
| ^^ ---------------- the lifetime gets resolved as `'_`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'_`
|
LL | fn explicit_anonymous_path_to_implicit_path(v: ContainsLifetime<'_>) -> ContainsLifetime<'_> {
| ++++
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:33:65
|
LL | fn explicit_bound_path_to_implicit_path<'a>(v: ContainsLifetime<'a>) -> ContainsLifetime {
| ^^ ---------------- the lifetime gets resolved as `'a`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'a`
|
LL | fn explicit_bound_path_to_implicit_path<'a>(v: ContainsLifetime<'a>) -> ContainsLifetime<'a> {
| ++++
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:39:25
|
LL | v: ContainsLifetime<'a>,
| ^^ this lifetime flows to the output
LL |
LL | ) -> ContainsLifetime<'_> {
| -- the lifetime gets resolved as `'a`
|
help: one option is to consistently use `'a`
|
LL - ) -> ContainsLifetime<'_> {
LL + ) -> ContainsLifetime<'a> {
|
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:47:37
|
LL | fn implicit_ref_to_implicit_path(v: &u8) -> ContainsLifetime {
| ^^^ ---------------- the lifetime gets resolved as `'_`
| |
| this lifetime flows to the output
|
help: one option is to remove the lifetime for references and use the anonymous lifetime for paths
|
LL | fn implicit_ref_to_implicit_path(v: &u8) -> ContainsLifetime<'_> {
| ++++
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:52:48
|
LL | fn explicit_anonymous_ref_to_implicit_path(v: &'_ u8) -> ContainsLifetime {
| ^^ ---------------- the lifetime gets resolved as `'_`
| |
| this lifetime flows to the output
|
help: one option is to remove the lifetime for references and use the anonymous lifetime for paths
|
LL - fn explicit_anonymous_ref_to_implicit_path(v: &'_ u8) -> ContainsLifetime {
LL + fn explicit_anonymous_ref_to_implicit_path(v: &u8) -> ContainsLifetime<'_> {
|
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:57:48
|
LL | fn explicit_bound_ref_to_implicit_path<'a>(v: &'a u8) -> ContainsLifetime {
| ^^ ---------------- the lifetime gets resolved as `'a`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'a`
|
LL | fn explicit_bound_ref_to_implicit_path<'a>(v: &'a u8) -> ContainsLifetime<'a> {
| ++++
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:62:58
|
LL | fn explicit_bound_ref_to_explicit_anonymous_path<'a>(v: &'a u8) -> ContainsLifetime<'_> {
| ^^ -- the lifetime gets resolved as `'a`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'a`
|
LL - fn explicit_bound_ref_to_explicit_anonymous_path<'a>(v: &'a u8) -> ContainsLifetime<'_> {
LL + fn explicit_bound_ref_to_explicit_anonymous_path<'a>(v: &'a u8) -> ContainsLifetime<'a> {
|
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:69:37
|
LL | fn implicit_path_to_implicit_ref(v: ContainsLifetime) -> &u8 {
| ^^^^^^^^^^^^^^^^ --- the lifetime gets resolved as `'_`
| |
| this lifetime flows to the output
|
help: one option is to remove the lifetime for references and use the anonymous lifetime for paths
|
LL | fn implicit_path_to_implicit_ref(v: ContainsLifetime<'_>) -> &u8 {
| ++++
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:74:47
|
LL | fn implicit_path_to_explicit_anonymous_ref(v: ContainsLifetime) -> &'_ u8 {
| ^^^^^^^^^^^^^^^^ -- the lifetime gets resolved as `'_`
| |
| this lifetime flows to the output
|
help: one option is to remove the lifetime for references and use the anonymous lifetime for paths
|
LL - fn implicit_path_to_explicit_anonymous_ref(v: ContainsLifetime) -> &'_ u8 {
LL + fn implicit_path_to_explicit_anonymous_ref(v: ContainsLifetime<'_>) -> &u8 {
|
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:79:64
|
LL | fn explicit_bound_path_to_implicit_ref<'a>(v: ContainsLifetime<'a>) -> &u8 {
| ^^ --- the lifetime gets resolved as `'a`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'a`
|
LL | fn explicit_bound_path_to_implicit_ref<'a>(v: ContainsLifetime<'a>) -> &'a u8 {
| ++
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:84:74
|
LL | fn explicit_bound_path_to_explicit_anonymous_ref<'a>(v: ContainsLifetime<'a>) -> &'_ u8 {
| ^^ -- the lifetime gets resolved as `'a`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'a`
|
LL - fn explicit_bound_path_to_explicit_anonymous_ref<'a>(v: ContainsLifetime<'a>) -> &'_ u8 {
LL + fn explicit_bound_path_to_explicit_anonymous_ref<'a>(v: ContainsLifetime<'a>) -> &'a u8 {
|
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:90:55
|
LL | fn method_explicit_bound_ref_to_implicit_ref<'a>(&'a self) -> &u8 {
| ^^ --- the lifetime gets resolved as `'a`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'a`
|
LL | fn method_explicit_bound_ref_to_implicit_ref<'a>(&'a self) -> &'a u8 {
| ++
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:95:65
|
LL | fn method_explicit_bound_ref_to_explicit_anonymous_ref<'a>(&'a self) -> &'_ u8 {
| ^^ -- the lifetime gets resolved as `'a`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'a`
|
LL - fn method_explicit_bound_ref_to_explicit_anonymous_ref<'a>(&'a self) -> &'_ u8 {
LL + fn method_explicit_bound_ref_to_explicit_anonymous_ref<'a>(&'a self) -> &'a u8 {
|
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:102:56
|
LL | fn method_explicit_anonymous_ref_to_implicit_path(&'_ self) -> ContainsLifetime {
| ^^ ---------------- the lifetime gets resolved as `'_`
| |
| this lifetime flows to the output
|
help: one option is to remove the lifetime for references and use the anonymous lifetime for paths
|
LL - fn method_explicit_anonymous_ref_to_implicit_path(&'_ self) -> ContainsLifetime {
LL + fn method_explicit_anonymous_ref_to_implicit_path(&self) -> ContainsLifetime<'_> {
|
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:107:56
|
LL | fn method_explicit_bound_ref_to_implicit_path<'a>(&'a self) -> ContainsLifetime {
| ^^ ---------------- the lifetime gets resolved as `'a`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'a`
|
LL | fn method_explicit_bound_ref_to_implicit_path<'a>(&'a self) -> ContainsLifetime<'a> {
| ++++
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:112:66
|
LL | fn method_explicit_bound_ref_to_explicit_anonymous_path<'a>(&'a self) -> ContainsLifetime<'_> {
| ^^ -- the lifetime gets resolved as `'a`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'a`
|
LL - fn method_explicit_bound_ref_to_explicit_anonymous_path<'a>(&'a self) -> ContainsLifetime<'_> {
LL + fn method_explicit_bound_ref_to_explicit_anonymous_path<'a>(&'a self) -> ContainsLifetime<'a> {
|
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:127:39
|
LL | fn static_ref_to_implicit_ref(v: &'static u8) -> &u8 {
| ^^^^^^^ --- the lifetime gets resolved as `'static`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'static`
|
LL | fn static_ref_to_implicit_ref(v: &'static u8) -> &'static u8 {
| +++++++
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:132:49
|
LL | fn static_ref_to_explicit_anonymous_ref(v: &'static u8) -> &'_ u8 {
| ^^^^^^^ -- the lifetime gets resolved as `'static`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'static`
|
LL - fn static_ref_to_explicit_anonymous_ref(v: &'static u8) -> &'_ u8 {
LL + fn static_ref_to_explicit_anonymous_ref(v: &'static u8) -> &'static u8 {
|
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:137:40
|
LL | fn static_ref_to_implicit_path(v: &'static u8) -> ContainsLifetime {
| ^^^^^^^ ---------------- the lifetime gets resolved as `'static`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'static`
|
LL | fn static_ref_to_implicit_path(v: &'static u8) -> ContainsLifetime<'static> {
| +++++++++
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:142:50
|
LL | fn static_ref_to_explicit_anonymous_path(v: &'static u8) -> ContainsLifetime<'_> {
| ^^^^^^^ -- the lifetime gets resolved as `'static`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'static`
|
LL - fn static_ref_to_explicit_anonymous_path(v: &'static u8) -> ContainsLifetime<'_> {
LL + fn static_ref_to_explicit_anonymous_path(v: &'static u8) -> ContainsLifetime<'static> {
|
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:148:40
|
LL | fn static_ref_to_implicit_ref(&'static self) -> &u8 {
| ^^^^^^^ --- the lifetime gets resolved as `'static`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'static`
|
LL | fn static_ref_to_implicit_ref(&'static self) -> &'static u8 {
| +++++++
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:153:50
|
LL | fn static_ref_to_explicit_anonymous_ref(&'static self) -> &'_ u8 {
| ^^^^^^^ -- the lifetime gets resolved as `'static`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'static`
|
LL - fn static_ref_to_explicit_anonymous_ref(&'static self) -> &'_ u8 {
LL + fn static_ref_to_explicit_anonymous_ref(&'static self) -> &'static u8 {
|
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:158:41
|
LL | fn static_ref_to_implicit_path(&'static self) -> ContainsLifetime {
| ^^^^^^^ ---------------- the lifetime gets resolved as `'static`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'static`
|
LL | fn static_ref_to_implicit_path(&'static self) -> ContainsLifetime<'static> {
| +++++++++
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:163:51
|
LL | fn static_ref_to_explicit_anonymous_path(&'static self) -> ContainsLifetime<'_> {
| ^^^^^^^ -- the lifetime gets resolved as `'static`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'static`
|
LL - fn static_ref_to_explicit_anonymous_path(&'static self) -> ContainsLifetime<'_> {
LL + fn static_ref_to_explicit_anonymous_path(&'static self) -> ContainsLifetime<'static> {
|
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:175:55
|
LL | fn explicit_bound_ref_to_impl_trait_bound<'a>(v: &'a u8) -> impl FnOnce() + '_ {
| ^^ -- the lifetime gets resolved as `'a`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'a`
|
LL - fn explicit_bound_ref_to_impl_trait_bound<'a>(v: &'a u8) -> impl FnOnce() + '_ {
LL + fn explicit_bound_ref_to_impl_trait_bound<'a>(v: &'a u8) -> impl FnOnce() + 'a {
|
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:180:65
|
LL | fn explicit_bound_ref_to_impl_trait_precise_capture<'a>(v: &'a u8) -> impl FnOnce() + use<'_> {
| ^^ -- the lifetime gets resolved as `'a`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'a`
|
LL - fn explicit_bound_ref_to_impl_trait_precise_capture<'a>(v: &'a u8) -> impl FnOnce() + use<'_> {
LL + fn explicit_bound_ref_to_impl_trait_precise_capture<'a>(v: &'a u8) -> impl FnOnce() + use<'a> {
|
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:185:72
|
LL | fn explicit_bound_path_to_impl_trait_bound<'a>(v: ContainsLifetime<'a>) -> impl FnOnce() + '_ {
| ^^ -- the lifetime gets resolved as `'a`
| |
| this lifetime flows to the output
|
help: one option is to consistently use `'a`
|
LL - fn explicit_bound_path_to_impl_trait_bound<'a>(v: ContainsLifetime<'a>) -> impl FnOnce() + '_ {
LL + fn explicit_bound_path_to_impl_trait_bound<'a>(v: ContainsLifetime<'a>) -> impl FnOnce() + 'a {
|
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:191:29
|
LL | v: ContainsLifetime<'a>,
| ^^ this lifetime flows to the output
LL |
LL | ) -> impl FnOnce() + use<'_> {
| -- the lifetime gets resolved as `'a`
|
help: one option is to consistently use `'a`
|
LL - ) -> impl FnOnce() + use<'_> {
LL + ) -> impl FnOnce() + use<'a> {
|
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:205:54
|
LL | fn explicit_bound_ref_to_dyn_trait_bound<'a>(v: &'a u8) -> Box<dyn Iterator<Item = &u8> + '_> {
| ^^ --- -- the lifetimes get resolved as `'a`
| | |
| | the lifetimes get resolved as `'a`
| this lifetime flows to the output
|
help: one option is to consistently use `'a`
|
LL | fn explicit_bound_ref_to_dyn_trait_bound<'a>(v: &'a u8) -> Box<dyn Iterator<Item = &'a u8> + '_> {
| ++
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:211:29
|
LL | v: ContainsLifetime<'a>,
| ^^ this lifetime flows to the output
LL |
LL | ) -> Box<dyn Iterator<Item = ContainsLifetime> + '_> {
| ---------------- -- the lifetimes get resolved as `'a`
| |
| the lifetimes get resolved as `'a`
|
help: one option is to consistently use `'a`
|
LL | ) -> Box<dyn Iterator<Item = ContainsLifetime<'a>> + '_> {
| ++++
error: lifetime flowing from input to output with different syntax can be confusing
--> $DIR/mismatched-lifetime-syntaxes.rs:220:33
|
LL | fn multiple_outputs<'a>(v: &'a u8) -> (&u8, &u8) {
| ^^ --- --- the lifetimes get resolved as `'a`
| | |
| | the lifetimes get resolved as `'a`
| this lifetime flows to the output
|
help: one option is to consistently use `'a`
|
LL | fn multiple_outputs<'a>(v: &'a u8) -> (&'a u8, &'a u8) {
| ++ ++
error: aborting due to 34 previous errors