Introduce the mismatched_lifetime_syntaxes lint
This commit is contained in:
parent
d2bf16ad6d
commit
9a50cb4a0c
8 changed files with 1447 additions and 3 deletions
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
]
|
||||
]
|
||||
);
|
||||
|
|
|
|||
503
compiler/rustc_lint/src/lifetime_syntax.rs
Normal file
503
compiler/rustc_lint/src/lifetime_syntax.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
|
||||
|
|
|
|||
318
tests/ui/lifetimes/mismatched-lifetime-syntaxes.rs
Normal file
318
tests/ui/lifetimes/mismatched-lifetime-syntaxes.rs
Normal 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() {}
|
||||
473
tests/ui/lifetimes/mismatched-lifetime-syntaxes.stderr
Normal file
473
tests/ui/lifetimes/mismatched-lifetime-syntaxes.stderr
Normal 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
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue