mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
Lint single-use-lifetimes on the AST.
This commit is contained in:
parent
db8a9274a9
commit
563916d698
@ -819,6 +819,43 @@ pub trait LintContext: Sized {
|
||||
"see issue #89122 <https://github.com/rust-lang/rust/issues/89122> for more information",
|
||||
);
|
||||
},
|
||||
BuiltinLintDiagnostics::SingleUseLifetime {
|
||||
param_span,
|
||||
use_span: Some((use_span, elide)),
|
||||
deletion_span,
|
||||
} => {
|
||||
debug!(?param_span, ?use_span, ?deletion_span);
|
||||
db.span_label(param_span, "this lifetime...");
|
||||
db.span_label(use_span, "...is used only here");
|
||||
let msg = "elide the single-use lifetime";
|
||||
let (use_span, replace_lt) = if elide {
|
||||
let use_span = sess.source_map().span_extend_while(
|
||||
use_span,
|
||||
char::is_whitespace,
|
||||
).unwrap_or(use_span);
|
||||
(use_span, String::new())
|
||||
} else {
|
||||
(use_span, "'_".to_owned())
|
||||
};
|
||||
db.multipart_suggestion(
|
||||
msg,
|
||||
vec![(deletion_span, String::new()), (use_span, replace_lt)],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
BuiltinLintDiagnostics::SingleUseLifetime {
|
||||
param_span: _,
|
||||
use_span: None,
|
||||
deletion_span,
|
||||
} => {
|
||||
debug!(?deletion_span);
|
||||
db.span_suggestion(
|
||||
deletion_span,
|
||||
"elide the unused lifetime",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
}
|
||||
// Rewrap `db`, and pass control to the user.
|
||||
decorate(LintDiagnosticBuilder::new(db));
|
||||
|
@ -239,6 +239,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
|
||||
|
||||
fn visit_generic_param(&mut self, param: &'a ast::GenericParam) {
|
||||
run_early_pass!(self, check_generic_param, param);
|
||||
self.check_id(param.id);
|
||||
ast_visit::walk_generic_param(self, param);
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ use rustc_middle::lint::{
|
||||
use rustc_middle::ty::query::Providers;
|
||||
use rustc_middle::ty::{RegisteredTools, TyCtxt};
|
||||
use rustc_session::lint::{
|
||||
builtin::{self, FORBIDDEN_LINT_GROUPS, UNFULFILLED_LINT_EXPECTATIONS},
|
||||
builtin::{self, FORBIDDEN_LINT_GROUPS, SINGLE_USE_LIFETIMES, UNFULFILLED_LINT_EXPECTATIONS},
|
||||
Level, Lint, LintExpectationId, LintId,
|
||||
};
|
||||
use rustc_session::parse::{add_feature_diagnostics, feature_err};
|
||||
@ -259,6 +259,14 @@ impl<'s> LintLevelsBuilder<'s> {
|
||||
let sess = self.sess;
|
||||
let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input");
|
||||
for (attr_index, attr) in attrs.iter().enumerate() {
|
||||
if attr.has_name(sym::automatically_derived) {
|
||||
self.current_specs_mut().insert(
|
||||
LintId::of(SINGLE_USE_LIFETIMES),
|
||||
(Level::Allow, LintLevelSource::Default),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
let level = match Level::from_attr(attr) {
|
||||
None => continue,
|
||||
Some(Level::Expect(unstable_id)) if let Some(hir_id) = source_hir_id => {
|
||||
|
@ -423,7 +423,11 @@ pub enum BuiltinLintDiagnostics {
|
||||
DeprecatedMacro(Option<Symbol>, Span),
|
||||
MissingAbi(Span, Abi),
|
||||
UnusedDocComment(Span),
|
||||
UnusedBuiltinAttribute { attr_name: Symbol, macro_name: String, invoc_span: Span },
|
||||
UnusedBuiltinAttribute {
|
||||
attr_name: Symbol,
|
||||
macro_name: String,
|
||||
invoc_span: Span,
|
||||
},
|
||||
PatternsInFnsWithoutBody(Span, Ident),
|
||||
LegacyDeriveHelpers(Span),
|
||||
ProcMacroBackCompat(String),
|
||||
@ -435,6 +439,16 @@ pub enum BuiltinLintDiagnostics {
|
||||
UnicodeTextFlow(Span, String),
|
||||
UnexpectedCfg((Symbol, Span), Option<(Symbol, Span)>),
|
||||
DeprecatedWhereclauseLocation(Span, String),
|
||||
SingleUseLifetime {
|
||||
/// Span of the parameter which declares this lifetime.
|
||||
param_span: Span,
|
||||
/// Span of the code that should be removed when eliding this lifetime.
|
||||
/// This span should include leading or trailing comma.
|
||||
deletion_span: Span,
|
||||
/// Span of the single use, or None if the lifetime is never used.
|
||||
/// If true, the lifetime will be fully elided.
|
||||
use_span: Option<(Span, bool)>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Lints that are buffered up early on in the `Session` before the
|
||||
|
@ -1,3 +1,4 @@
|
||||
// ignore-tidy-filelength
|
||||
//! "Late resolution" is the pass that resolves most of names in a crate beside imports and macros.
|
||||
//! It runs when the crate is fully expanded and its module structure is fully built.
|
||||
//! So it just walks through the crate and resolves all the expressions, types, etc.
|
||||
@ -19,7 +20,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
|
||||
use rustc_errors::DiagnosticId;
|
||||
use rustc_hir::def::Namespace::{self, *};
|
||||
use rustc_hir::def::{self, CtorKind, DefKind, PartialRes, PerNS};
|
||||
use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
|
||||
use rustc_hir::definitions::DefPathData;
|
||||
use rustc_hir::{PrimTy, TraitCandidate};
|
||||
use rustc_index::vec::Idx;
|
||||
@ -197,13 +198,19 @@ impl<'a, R> Rib<'a, R> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum LifetimeUseSet {
|
||||
One { use_span: Span, use_ctxt: visit::LifetimeCtxt },
|
||||
Many,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum LifetimeRibKind {
|
||||
/// This rib acts as a barrier to forbid reference to lifetimes of a parent item.
|
||||
Item,
|
||||
|
||||
/// This rib declares generic parameters.
|
||||
Generics { parent: NodeId, span: Span, kind: LifetimeBinderKind },
|
||||
Generics { binder: NodeId, span: Span, kind: LifetimeBinderKind },
|
||||
|
||||
/// FIXME(const_generics): This patches over an ICE caused by non-'static lifetimes in const
|
||||
/// generics. We are disallowing this until we can decide on how we want to handle non-'static
|
||||
@ -230,7 +237,7 @@ enum LifetimeRibKind {
|
||||
AnonymousReportError,
|
||||
|
||||
/// Pass responsibility to `resolve_lifetime` code for all cases.
|
||||
AnonymousPassThrough(NodeId),
|
||||
AnonymousPassThrough(NodeId, /* in_fn_return */ bool),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
@ -519,6 +526,9 @@ struct LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
/// In most cases this will be `None`, in which case errors will always be reported.
|
||||
/// If it is `true`, then it will be updated when entering a nested function or trait body.
|
||||
in_func_body: bool,
|
||||
|
||||
/// Count the number of places a lifetime is used.
|
||||
lifetime_uses: FxHashMap<LocalDefId, LifetimeUseSet>,
|
||||
}
|
||||
|
||||
/// Walks the whole crate in DFS order, visiting each item, resolving names as it goes.
|
||||
@ -599,17 +609,19 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
||||
&bare_fn.generic_params,
|
||||
NormalRibKind,
|
||||
LifetimeRibKind::Generics {
|
||||
parent: ty.id,
|
||||
binder: ty.id,
|
||||
kind: LifetimeBinderKind::BareFnType,
|
||||
span,
|
||||
},
|
||||
|this| {
|
||||
this.with_lifetime_rib(
|
||||
LifetimeRibKind::AnonymousPassThrough(ty.id),
|
||||
|this| {
|
||||
this.visit_generic_param_vec(&bare_fn.generic_params, false);
|
||||
visit::walk_fn_decl(this, &bare_fn.decl);
|
||||
},
|
||||
this.with_lifetime_rib(
|
||||
LifetimeRibKind::AnonymousPassThrough(ty.id, false),
|
||||
|this| walk_list!(this, visit_param, &bare_fn.decl.inputs),
|
||||
);
|
||||
this.with_lifetime_rib(
|
||||
LifetimeRibKind::AnonymousPassThrough(ty.id, true),
|
||||
|this| this.visit_fn_ret_ty(&bare_fn.decl.output),
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -628,7 +640,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
||||
&tref.bound_generic_params,
|
||||
NormalRibKind,
|
||||
LifetimeRibKind::Generics {
|
||||
parent: tref.trait_ref.ref_id,
|
||||
binder: tref.trait_ref.ref_id,
|
||||
kind: LifetimeBinderKind::PolyTrait,
|
||||
span,
|
||||
},
|
||||
@ -652,7 +664,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
||||
&generics.params,
|
||||
ItemRibKind(HasGenericParams::Yes),
|
||||
LifetimeRibKind::Generics {
|
||||
parent: foreign_item.id,
|
||||
binder: foreign_item.id,
|
||||
kind: LifetimeBinderKind::Item,
|
||||
span: generics.span,
|
||||
},
|
||||
@ -666,7 +678,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
||||
&generics.params,
|
||||
ItemRibKind(HasGenericParams::Yes),
|
||||
LifetimeRibKind::Generics {
|
||||
parent: foreign_item.id,
|
||||
binder: foreign_item.id,
|
||||
kind: LifetimeBinderKind::Function,
|
||||
span: generics.span,
|
||||
},
|
||||
@ -690,13 +702,20 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
||||
// a body, or if there's no body for some other reason.
|
||||
FnKind::Fn(FnCtxt::Foreign, _, sig, _, generics, _)
|
||||
| FnKind::Fn(_, _, sig, _, generics, None) => {
|
||||
self.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(fn_id), |this| {
|
||||
// We don't need to deal with patterns in parameters, because
|
||||
// they are not possible for foreign or bodiless functions.
|
||||
self.with_lifetime_rib(
|
||||
LifetimeRibKind::AnonymousPassThrough(fn_id, false),
|
||||
|this| {
|
||||
this.visit_fn_header(&sig.header);
|
||||
this.visit_generics(generics);
|
||||
visit::walk_fn_decl(this, &sig.decl);
|
||||
});
|
||||
walk_list!(this, visit_param, &sig.decl.inputs);
|
||||
},
|
||||
);
|
||||
self.with_lifetime_rib(
|
||||
LifetimeRibKind::AnonymousPassThrough(fn_id, true),
|
||||
|this| this.visit_fn_ret_ty(&sig.decl.output),
|
||||
);
|
||||
return;
|
||||
}
|
||||
FnKind::Fn(FnCtxt::Free, ..) => FnItemRibKind,
|
||||
@ -759,28 +778,32 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
||||
this.r.extra_lifetime_params_map.insert(async_node_id, extra_lifetime_params);
|
||||
|
||||
this.with_lifetime_rib(
|
||||
LifetimeRibKind::AnonymousPassThrough(async_node_id),
|
||||
LifetimeRibKind::AnonymousPassThrough(async_node_id, true),
|
||||
|this| visit::walk_fn_ret_ty(this, &declaration.output),
|
||||
);
|
||||
} else {
|
||||
this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(fn_id), |this| {
|
||||
// Add each argument to the rib.
|
||||
this.resolve_params(&declaration.inputs);
|
||||
|
||||
visit::walk_fn_ret_ty(this, &declaration.output);
|
||||
});
|
||||
this.with_lifetime_rib(
|
||||
LifetimeRibKind::AnonymousPassThrough(fn_id, false),
|
||||
|this| this.resolve_params(&declaration.inputs),
|
||||
);
|
||||
this.with_lifetime_rib(
|
||||
LifetimeRibKind::AnonymousPassThrough(fn_id, true),
|
||||
|this| visit::walk_fn_ret_ty(this, &declaration.output),
|
||||
);
|
||||
};
|
||||
|
||||
// Ignore errors in function bodies if this is rustdoc
|
||||
// Be sure not to set this until the function signature has been resolved.
|
||||
let previous_state = replace(&mut this.in_func_body, true);
|
||||
// Resolve the function body, potentially inside the body of an async closure
|
||||
this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(fn_id), |this| {
|
||||
match fn_kind {
|
||||
this.with_lifetime_rib(
|
||||
LifetimeRibKind::AnonymousPassThrough(fn_id, false),
|
||||
|this| match fn_kind {
|
||||
FnKind::Fn(.., body) => walk_list!(this, visit_block, body),
|
||||
FnKind::Closure(_, body) => this.visit_expr(body),
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
debug!("(resolving function) leaving function");
|
||||
this.in_func_body = previous_state;
|
||||
@ -788,8 +811,8 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
||||
});
|
||||
self.diagnostic_metadata.current_function = previous_value;
|
||||
}
|
||||
fn visit_lifetime(&mut self, lifetime: &'ast Lifetime, _: visit::LifetimeCtxt) {
|
||||
self.resolve_lifetime(lifetime)
|
||||
fn visit_lifetime(&mut self, lifetime: &'ast Lifetime, use_ctxt: visit::LifetimeCtxt) {
|
||||
self.resolve_lifetime(lifetime, use_ctxt)
|
||||
}
|
||||
|
||||
fn visit_generics(&mut self, generics: &'ast Generics) {
|
||||
@ -864,10 +887,16 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
||||
if let Some(ref args) = path_segment.args {
|
||||
match &**args {
|
||||
GenericArgs::AngleBracketed(..) => visit::walk_generic_args(self, path_span, args),
|
||||
GenericArgs::Parenthesized(..) => self.with_lifetime_rib(
|
||||
LifetimeRibKind::AnonymousPassThrough(path_segment.id),
|
||||
|this| visit::walk_generic_args(this, path_span, args),
|
||||
),
|
||||
GenericArgs::Parenthesized(ref data) => {
|
||||
self.with_lifetime_rib(
|
||||
LifetimeRibKind::AnonymousPassThrough(path_segment.id, false),
|
||||
|this| walk_list!(this, visit_ty, &data.inputs),
|
||||
);
|
||||
self.with_lifetime_rib(
|
||||
LifetimeRibKind::AnonymousPassThrough(path_segment.id, true),
|
||||
|this| visit::walk_fn_ret_ty(this, &data.output),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -890,7 +919,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
||||
&bound_generic_params,
|
||||
NormalRibKind,
|
||||
LifetimeRibKind::Generics {
|
||||
parent: bounded_ty.id,
|
||||
binder: bounded_ty.id,
|
||||
kind: LifetimeBinderKind::WhereBound,
|
||||
span,
|
||||
},
|
||||
@ -971,6 +1000,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
diagnostic_metadata: DiagnosticMetadata::default(),
|
||||
// errors at module scope should always be reported
|
||||
in_func_body: false,
|
||||
lifetime_uses: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1178,7 +1208,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self))]
|
||||
fn resolve_lifetime(&mut self, lifetime: &'ast Lifetime) {
|
||||
fn resolve_lifetime(&mut self, lifetime: &'ast Lifetime, use_ctxt: visit::LifetimeCtxt) {
|
||||
let ident = lifetime.ident;
|
||||
|
||||
if ident.name == kw::StaticLifetime {
|
||||
@ -1196,6 +1226,40 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
let normalized_ident = ident.normalize_to_macros_2_0();
|
||||
if let Some(&(_, region)) = rib.bindings.get(&normalized_ident) {
|
||||
self.record_lifetime_res(lifetime.id, region);
|
||||
|
||||
if let LifetimeRes::Param { param, .. } = region {
|
||||
match self.lifetime_uses.entry(param) {
|
||||
Entry::Vacant(v) => {
|
||||
debug!("First use of {:?} at {:?}", region, ident.span);
|
||||
let use_set = self
|
||||
.lifetime_ribs
|
||||
.iter()
|
||||
.rev()
|
||||
.find_map(|rib| match rib.kind {
|
||||
// Do not suggest eliding a lifetime where an anonymous
|
||||
// lifetime would be illegal.
|
||||
LifetimeRibKind::Item
|
||||
| LifetimeRibKind::AnonymousPassThrough(_, true)
|
||||
| LifetimeRibKind::AnonymousReportError => {
|
||||
Some(LifetimeUseSet::Many)
|
||||
}
|
||||
// An anonymous lifetime is legal here, go ahead.
|
||||
LifetimeRibKind::AnonymousPassThrough(_, false)
|
||||
| LifetimeRibKind::AnonymousCreateParameter(_) => {
|
||||
Some(LifetimeUseSet::One { use_span: ident.span, use_ctxt })
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or(LifetimeUseSet::Many);
|
||||
debug!(?use_ctxt, ?use_set);
|
||||
v.insert(use_set);
|
||||
}
|
||||
Entry::Occupied(mut o) => {
|
||||
debug!("Many uses of {:?} at {:?}", region, ident.span);
|
||||
*o.get_mut() = LifetimeUseSet::Many;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1262,7 +1326,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
self.record_lifetime_res(lifetime.id, LifetimeRes::Error);
|
||||
return;
|
||||
}
|
||||
LifetimeRibKind::AnonymousPassThrough(node_id) => {
|
||||
LifetimeRibKind::AnonymousPassThrough(node_id, _) => {
|
||||
self.record_lifetime_res(
|
||||
lifetime.id,
|
||||
LifetimeRes::Anonymous { binder: node_id, elided },
|
||||
@ -1382,7 +1446,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
// `PathSegment`, for which there is no associated `'_` or `&T` with no explicit
|
||||
// lifetime. Instead, we simply create an implicit lifetime, which will be checked
|
||||
// later, at which point a suitable error will be emitted.
|
||||
LifetimeRibKind::AnonymousPassThrough(binder) => {
|
||||
LifetimeRibKind::AnonymousPassThrough(binder, _) => {
|
||||
res = LifetimeRes::Anonymous { binder, elided: true };
|
||||
break;
|
||||
}
|
||||
@ -1550,7 +1614,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
&generics.params,
|
||||
ItemRibKind(HasGenericParams::Yes),
|
||||
LifetimeRibKind::Generics {
|
||||
parent: item.id,
|
||||
binder: item.id,
|
||||
kind: LifetimeBinderKind::Item,
|
||||
span: generics.span,
|
||||
},
|
||||
@ -1620,7 +1684,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
&generics.params,
|
||||
ItemRibKind(HasGenericParams::Yes),
|
||||
LifetimeRibKind::Generics {
|
||||
parent: item.id,
|
||||
binder: item.id,
|
||||
kind: LifetimeBinderKind::Item,
|
||||
span: generics.span,
|
||||
},
|
||||
@ -1633,7 +1697,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
&generics.params,
|
||||
ItemRibKind(HasGenericParams::Yes),
|
||||
LifetimeRibKind::Generics {
|
||||
parent: item.id,
|
||||
binder: item.id,
|
||||
kind: LifetimeBinderKind::Function,
|
||||
span: generics.span,
|
||||
},
|
||||
@ -1665,7 +1729,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
&generics.params,
|
||||
ItemRibKind(HasGenericParams::Yes),
|
||||
LifetimeRibKind::Generics {
|
||||
parent: item.id,
|
||||
binder: item.id,
|
||||
kind: LifetimeBinderKind::Item,
|
||||
span: generics.span,
|
||||
},
|
||||
@ -1686,7 +1750,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
&generics.params,
|
||||
AssocItemRibKind,
|
||||
LifetimeRibKind::Generics {
|
||||
parent: item.id,
|
||||
binder: item.id,
|
||||
span: generics.span,
|
||||
kind,
|
||||
},
|
||||
@ -1754,7 +1818,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
&generics.params,
|
||||
ItemRibKind(HasGenericParams::Yes),
|
||||
LifetimeRibKind::Generics {
|
||||
parent: item.id,
|
||||
binder: item.id,
|
||||
kind: LifetimeBinderKind::Item,
|
||||
span: generics.span,
|
||||
},
|
||||
@ -1824,6 +1888,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
F: FnOnce(&mut Self),
|
||||
{
|
||||
debug!("with_generic_param_rib");
|
||||
let LifetimeRibKind::Generics { binder, span: generics_span, kind: generics_kind, .. }
|
||||
= lifetime_kind else { panic!() };
|
||||
|
||||
let mut function_type_rib = Rib::new(kind);
|
||||
let mut function_value_rib = Rib::new(kind);
|
||||
let mut function_lifetime_rib = LifetimeRib::new(lifetime_kind);
|
||||
@ -1892,8 +1959,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
GenericParamKind::Type { .. } => (&mut function_type_rib, DefKind::TyParam),
|
||||
GenericParamKind::Const { .. } => (&mut function_value_rib, DefKind::ConstParam),
|
||||
GenericParamKind::Lifetime => {
|
||||
let LifetimeRibKind::Generics { parent, .. } = lifetime_kind else { panic!() };
|
||||
let res = LifetimeRes::Param { param: def_id, binder: parent };
|
||||
let res = LifetimeRes::Param { param: def_id, binder };
|
||||
self.record_lifetime_res(param.id, res);
|
||||
function_lifetime_rib.bindings.insert(ident, (param.id, res));
|
||||
continue;
|
||||
@ -1913,6 +1979,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
self.ribs[TypeNS].pop();
|
||||
self.ribs[ValueNS].pop();
|
||||
self.lifetime_ribs.pop();
|
||||
|
||||
if let LifetimeBinderKind::BareFnType
|
||||
| LifetimeBinderKind::WhereBound
|
||||
| LifetimeBinderKind::Function
|
||||
| LifetimeBinderKind::ImplBlock = generics_kind
|
||||
{
|
||||
self.maybe_report_lifetime_uses(generics_span, params)
|
||||
}
|
||||
}
|
||||
|
||||
fn with_label_rib(&mut self, kind: RibKind<'a>, f: impl FnOnce(&mut Self)) {
|
||||
@ -2039,7 +2113,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
) {
|
||||
debug!("resolve_implementation");
|
||||
// If applicable, create a rib for the type parameters.
|
||||
self.with_generic_param_rib(&generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { span: generics.span, parent: item_id, kind: LifetimeBinderKind::ImplBlock }, |this| {
|
||||
self.with_generic_param_rib(&generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { span: generics.span, binder: item_id, kind: LifetimeBinderKind::ImplBlock }, |this| {
|
||||
// Dummy self type for better errors if `Self` is used in the trait path.
|
||||
this.with_self_rib(Res::SelfTy { trait_: None, alias_to: None }, |this| {
|
||||
this.with_lifetime_rib(LifetimeRibKind::AnonymousCreateParameter(item_id), |this| {
|
||||
@ -2066,7 +2140,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
this.visit_generics(generics);
|
||||
|
||||
// Resolve the items within the impl.
|
||||
this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(item_id),
|
||||
this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(item_id,false),
|
||||
|this| {
|
||||
this.with_current_self_type(self_type, |this| {
|
||||
this.with_self_rib_ns(ValueNS, Res::SelfCtor(item_def_id), |this| {
|
||||
@ -2111,7 +2185,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
this.with_generic_param_rib(
|
||||
&generics.params,
|
||||
AssocItemRibKind,
|
||||
LifetimeRibKind::Generics { parent: item.id, span: generics.span, kind: LifetimeBinderKind::Function },
|
||||
LifetimeRibKind::Generics { binder: item.id, span: generics.span, kind: LifetimeBinderKind::Function },
|
||||
|this| {
|
||||
// If this is a trait impl, ensure the method
|
||||
// exists in trait
|
||||
@ -2140,7 +2214,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
this.with_generic_param_rib(
|
||||
&generics.params,
|
||||
AssocItemRibKind,
|
||||
LifetimeRibKind::Generics { parent: item.id, span: generics.span, kind: LifetimeBinderKind::Item },
|
||||
LifetimeRibKind::Generics { binder: item.id, span: generics.span, kind: LifetimeBinderKind::Item },
|
||||
|this| {
|
||||
// If this is a trait impl, ensure the type
|
||||
// exists in trait
|
||||
|
@ -1,16 +1,17 @@
|
||||
use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion};
|
||||
use crate::late::lifetimes::{ElisionFailureInfo, LifetimeContext};
|
||||
use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind};
|
||||
use crate::late::{LifetimeBinderKind, LifetimeRibKind};
|
||||
use crate::late::{LifetimeBinderKind, LifetimeRibKind, LifetimeUseSet};
|
||||
use crate::path_names_to_string;
|
||||
use crate::{Module, ModuleKind, ModuleOrUniformRoot};
|
||||
use crate::{PathResult, PathSource, Segment};
|
||||
|
||||
use rustc_ast::visit::{FnCtxt, FnKind};
|
||||
use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt};
|
||||
use rustc_ast::{
|
||||
self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
|
||||
NodeId, Path, Ty, TyKind,
|
||||
};
|
||||
use rustc_ast_lowering::ResolverAstLowering;
|
||||
use rustc_ast_pretty::pprust::path_segment_to_string;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::{
|
||||
@ -22,6 +23,7 @@ use rustc_hir::def::Namespace::{self, *};
|
||||
use rustc_hir::def::{self, CtorKind, CtorOf, DefKind};
|
||||
use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE};
|
||||
use rustc_hir::PrimTy;
|
||||
use rustc_session::lint;
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::hygiene::MacroKind;
|
||||
@ -1832,6 +1834,76 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
||||
})
|
||||
}
|
||||
|
||||
crate fn maybe_report_lifetime_uses(
|
||||
&mut self,
|
||||
generics_span: Span,
|
||||
params: &[ast::GenericParam],
|
||||
) {
|
||||
for (param_index, param) in params.iter().enumerate() {
|
||||
let GenericParamKind::Lifetime = param.kind else { continue };
|
||||
|
||||
let def_id = self.r.local_def_id(param.id);
|
||||
|
||||
let use_set = self.lifetime_uses.remove(&def_id);
|
||||
debug!(
|
||||
"Use set for {:?}({:?} at {:?}) is {:?}",
|
||||
def_id, param.ident, param.ident.span, use_set
|
||||
);
|
||||
|
||||
let deletion_span = || {
|
||||
if params.len() == 1 {
|
||||
// if sole lifetime, remove the entire `<>` brackets
|
||||
generics_span
|
||||
} else if param_index == 0 {
|
||||
// if removing within `<>` brackets, we also want to
|
||||
// delete a leading or trailing comma as appropriate
|
||||
param.span().to(params[param_index + 1].span().shrink_to_lo())
|
||||
} else {
|
||||
// if removing within `<>` brackets, we also want to
|
||||
// delete a leading or trailing comma as appropriate
|
||||
params[param_index - 1].span().shrink_to_hi().to(param.span())
|
||||
}
|
||||
};
|
||||
match use_set {
|
||||
Some(LifetimeUseSet::Many) => {}
|
||||
Some(LifetimeUseSet::One { use_span, use_ctxt }) => {
|
||||
debug!(?param.ident, ?param.ident.span, ?use_span);
|
||||
|
||||
let elidable = matches!(use_ctxt, LifetimeCtxt::Rptr);
|
||||
|
||||
let deletion_span = deletion_span();
|
||||
self.r.lint_buffer.buffer_lint_with_diagnostic(
|
||||
lint::builtin::SINGLE_USE_LIFETIMES,
|
||||
param.id,
|
||||
param.ident.span,
|
||||
&format!("lifetime parameter `{}` only used once", param.ident),
|
||||
lint::BuiltinLintDiagnostics::SingleUseLifetime {
|
||||
param_span: param.ident.span,
|
||||
use_span: Some((use_span, elidable)),
|
||||
deletion_span,
|
||||
},
|
||||
);
|
||||
}
|
||||
None => {
|
||||
debug!(?param.ident, ?param.ident.span);
|
||||
|
||||
let deletion_span = deletion_span();
|
||||
self.r.lint_buffer.buffer_lint_with_diagnostic(
|
||||
lint::builtin::UNUSED_LIFETIMES,
|
||||
param.id,
|
||||
param.ident.span,
|
||||
&format!("lifetime parameter `{}` never used", param.ident),
|
||||
lint::BuiltinLintDiagnostics::SingleUseLifetime {
|
||||
param_span: param.ident.span,
|
||||
use_span: None,
|
||||
deletion_span,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crate fn emit_undeclared_lifetime_error(
|
||||
&self,
|
||||
lifetime_ref: &ast::Lifetime,
|
||||
@ -1863,7 +1935,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
||||
|
||||
for rib in self.lifetime_ribs.iter().rev() {
|
||||
match rib.kind {
|
||||
LifetimeRibKind::Generics { parent: _, span, kind } => {
|
||||
LifetimeRibKind::Generics { binder: _, span, kind } => {
|
||||
if !span.can_be_used_for_suggestions() && suggest_note {
|
||||
suggest_note = false; // Avoid displaying the same help multiple times.
|
||||
err.span_label(
|
||||
|
@ -9,20 +9,19 @@
|
||||
use crate::late::diagnostics::{ForLifetimeSpanType, MissingLifetimeSpot};
|
||||
use rustc_ast::walk_list;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
|
||||
use rustc_errors::{struct_span_err, Applicability, Diagnostic};
|
||||
use rustc_errors::struct_span_err;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{DefIdMap, LocalDefId};
|
||||
use rustc_hir::hir_id::ItemLocalId;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{GenericArg, GenericParam, LifetimeName, Node, ParamName, QPath};
|
||||
use rustc_hir::{GenericArg, GenericParam, LifetimeName, Node, ParamName};
|
||||
use rustc_hir::{GenericParamKind, HirIdMap, HirIdSet};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::middle::resolve_lifetime::*;
|
||||
use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, TyCtxt};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_session::lint;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::Span;
|
||||
@ -33,13 +32,6 @@ use std::mem::take;
|
||||
|
||||
use tracing::{debug, span, Level};
|
||||
|
||||
// This counts the no of times a lifetime is used
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum LifetimeUseSet<'tcx> {
|
||||
One(&'tcx hir::Lifetime),
|
||||
Many,
|
||||
}
|
||||
|
||||
trait RegionExt {
|
||||
fn early(hir_map: Map<'_>, index: &mut u32, param: &GenericParam<'_>) -> (ParamName, Region);
|
||||
|
||||
@ -175,8 +167,6 @@ crate struct LifetimeContext<'a, 'tcx> {
|
||||
/// Cache for cross-crate per-definition object lifetime defaults.
|
||||
xcrate_object_lifetime_defaults: DefIdMap<Vec<ObjectLifetimeDefault>>,
|
||||
|
||||
lifetime_uses: &'a mut DefIdMap<LifetimeUseSet<'tcx>>,
|
||||
|
||||
/// When encountering an undefined named lifetime, we will suggest introducing it in these
|
||||
/// places.
|
||||
crate missing_named_lifetime_spots: Vec<MissingLifetimeSpot<'tcx>>,
|
||||
@ -197,11 +187,6 @@ enum Scope<'a> {
|
||||
/// we should use for an early-bound region?
|
||||
next_early_index: u32,
|
||||
|
||||
/// Flag is set to true if, in this binder, `'_` would be
|
||||
/// equivalent to a "single-use region". This is true on
|
||||
/// impls, but not other kinds of items.
|
||||
track_lifetime_uses: bool,
|
||||
|
||||
/// Whether or not this binder would serve as the parent
|
||||
/// binder for opaque types introduced within. For example:
|
||||
///
|
||||
@ -297,7 +282,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
|
||||
Scope::Binder {
|
||||
lifetimes,
|
||||
next_early_index,
|
||||
track_lifetime_uses,
|
||||
opaque_type_parent,
|
||||
scope_type,
|
||||
hir_id,
|
||||
@ -307,7 +291,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
|
||||
.debug_struct("Binder")
|
||||
.field("lifetimes", lifetimes)
|
||||
.field("next_early_index", next_early_index)
|
||||
.field("track_lifetime_uses", track_lifetime_uses)
|
||||
.field("opaque_type_parent", opaque_type_parent)
|
||||
.field("scope_type", scope_type)
|
||||
.field("hir_id", hir_id)
|
||||
@ -453,7 +436,6 @@ fn do_resolve(
|
||||
trait_definition_only,
|
||||
labels_in_fn: vec![],
|
||||
xcrate_object_lifetime_defaults: Default::default(),
|
||||
lifetime_uses: &mut Default::default(),
|
||||
missing_named_lifetime_spots: vec![],
|
||||
};
|
||||
visitor.visit_item(item);
|
||||
@ -697,7 +679,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||
lifetimes: FxIndexMap::default(),
|
||||
next_early_index: self.next_early_index(),
|
||||
s: self.scope,
|
||||
track_lifetime_uses: true,
|
||||
opaque_type_parent: false,
|
||||
scope_type: BinderScopeType::Normal,
|
||||
allow_late_bound: true,
|
||||
@ -796,9 +777,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||
| hir::ItemKind::Impl(hir::Impl { ref generics, .. }) => {
|
||||
self.missing_named_lifetime_spots.push(generics.into());
|
||||
|
||||
// Impls permit `'_` to be used and it is equivalent to "some fresh lifetime name".
|
||||
// This is not true for other kinds of items.
|
||||
let track_lifetime_uses = matches!(item.kind, hir::ItemKind::Impl { .. });
|
||||
// These kinds of items have only early-bound lifetime parameters.
|
||||
let mut index = if sub_items_have_self_param(&item.kind) {
|
||||
1 // Self comes before lifetimes
|
||||
@ -825,7 +803,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||
lifetimes,
|
||||
next_early_index: index + non_lifetime_count,
|
||||
opaque_type_parent: true,
|
||||
track_lifetime_uses,
|
||||
scope_type: BinderScopeType::Normal,
|
||||
s: ROOT_SCOPE,
|
||||
allow_late_bound: false,
|
||||
@ -892,7 +869,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||
lifetimes,
|
||||
s: self.scope,
|
||||
next_early_index,
|
||||
track_lifetime_uses: true,
|
||||
opaque_type_parent: false,
|
||||
scope_type: BinderScopeType::Normal,
|
||||
allow_late_bound: true,
|
||||
@ -1053,11 +1029,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||
match param.kind {
|
||||
GenericParamKind::Lifetime { .. } => {
|
||||
let (name, reg) = Region::early(self.tcx.hir(), &mut index, ¶m);
|
||||
let Region::EarlyBound(_, def_id) = reg else {
|
||||
bug!();
|
||||
};
|
||||
// We cannot predict what lifetimes are unused in opaque type.
|
||||
self.lifetime_uses.insert(def_id, LifetimeUseSet::Many);
|
||||
if let hir::ParamName::Plain(Ident {
|
||||
name: kw::UnderscoreLifetime,
|
||||
..
|
||||
@ -1087,7 +1058,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||
lifetimes,
|
||||
next_early_index,
|
||||
s: this.scope,
|
||||
track_lifetime_uses: true,
|
||||
opaque_type_parent: false,
|
||||
scope_type: BinderScopeType::Normal,
|
||||
allow_late_bound: false,
|
||||
@ -1108,7 +1078,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||
lifetimes,
|
||||
next_early_index,
|
||||
s: self.scope,
|
||||
track_lifetime_uses: true,
|
||||
opaque_type_parent: false,
|
||||
scope_type: BinderScopeType::Normal,
|
||||
allow_late_bound: false,
|
||||
@ -1168,7 +1137,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||
lifetimes,
|
||||
next_early_index: index + non_lifetime_count,
|
||||
s: self.scope,
|
||||
track_lifetime_uses: true,
|
||||
opaque_type_parent: true,
|
||||
scope_type: BinderScopeType::Normal,
|
||||
allow_late_bound: false,
|
||||
@ -1238,7 +1206,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||
lifetimes,
|
||||
next_early_index: index + non_lifetime_count,
|
||||
s: self.scope,
|
||||
track_lifetime_uses: true,
|
||||
opaque_type_parent: true,
|
||||
scope_type: BinderScopeType::Normal,
|
||||
allow_late_bound: true,
|
||||
@ -1383,7 +1350,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||
lifetimes,
|
||||
s: this.scope,
|
||||
next_early_index,
|
||||
track_lifetime_uses: true,
|
||||
opaque_type_parent: false,
|
||||
scope_type: BinderScopeType::Normal,
|
||||
allow_late_bound: true,
|
||||
@ -1457,7 +1423,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||
lifetimes: FxIndexMap::default(),
|
||||
s: self.scope,
|
||||
next_early_index: self.next_early_index(),
|
||||
track_lifetime_uses: true,
|
||||
opaque_type_parent: false,
|
||||
scope_type,
|
||||
allow_late_bound: true,
|
||||
@ -1510,7 +1475,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||
lifetimes,
|
||||
s: self.scope,
|
||||
next_early_index,
|
||||
track_lifetime_uses: true,
|
||||
opaque_type_parent: false,
|
||||
scope_type,
|
||||
allow_late_bound: true,
|
||||
@ -1812,7 +1776,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||
where
|
||||
F: for<'b> FnOnce(ScopeRef<'_>, &mut LifetimeContext<'b, 'tcx>),
|
||||
{
|
||||
let LifetimeContext { tcx, map, lifetime_uses, .. } = self;
|
||||
let LifetimeContext { tcx, map, .. } = self;
|
||||
let labels_in_fn = take(&mut self.labels_in_fn);
|
||||
let xcrate_object_lifetime_defaults = take(&mut self.xcrate_object_lifetime_defaults);
|
||||
let missing_named_lifetime_spots = take(&mut self.missing_named_lifetime_spots);
|
||||
@ -1823,298 +1787,18 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||
trait_definition_only: self.trait_definition_only,
|
||||
labels_in_fn,
|
||||
xcrate_object_lifetime_defaults,
|
||||
lifetime_uses,
|
||||
missing_named_lifetime_spots,
|
||||
};
|
||||
let span = tracing::debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope));
|
||||
{
|
||||
let _enter = span.enter();
|
||||
f(self.scope, &mut this);
|
||||
if !self.trait_definition_only {
|
||||
this.check_uses_for_lifetimes_defined_by_scope();
|
||||
}
|
||||
}
|
||||
self.labels_in_fn = this.labels_in_fn;
|
||||
self.xcrate_object_lifetime_defaults = this.xcrate_object_lifetime_defaults;
|
||||
self.missing_named_lifetime_spots = this.missing_named_lifetime_spots;
|
||||
}
|
||||
|
||||
/// helper method to determine the span to remove when suggesting the
|
||||
/// deletion of a lifetime
|
||||
fn lifetime_deletion_span(&self, name: Ident, generics: &hir::Generics<'_>) -> Option<Span> {
|
||||
generics.params.iter().enumerate().find_map(|(i, param)| {
|
||||
if param.name.ident() == name {
|
||||
if generics.params.len() == 1 {
|
||||
// if sole lifetime, remove the entire `<>` brackets
|
||||
Some(generics.span)
|
||||
} else {
|
||||
// if removing within `<>` brackets, we also want to
|
||||
// delete a leading or trailing comma as appropriate
|
||||
if i >= generics.params.len() - 1 {
|
||||
Some(generics.params[i - 1].span.shrink_to_hi().to(param.span))
|
||||
} else {
|
||||
Some(param.span.to(generics.params[i + 1].span.shrink_to_lo()))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// helper method to issue suggestions from `fn rah<'a>(&'a T)` to `fn rah(&T)`
|
||||
// or from `fn rah<'a>(T<'a>)` to `fn rah(T<'_>)`
|
||||
fn suggest_eliding_single_use_lifetime(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
def_id: DefId,
|
||||
lifetime: &hir::Lifetime,
|
||||
) {
|
||||
let name = lifetime.name.ident();
|
||||
let remove_decl = self
|
||||
.tcx
|
||||
.parent(def_id)
|
||||
.as_local()
|
||||
.and_then(|parent_def_id| self.tcx.hir().get_generics(parent_def_id))
|
||||
.and_then(|generics| self.lifetime_deletion_span(name, generics));
|
||||
|
||||
let mut remove_use = None;
|
||||
let mut elide_use = None;
|
||||
let mut find_arg_use_span = |inputs: &[hir::Ty<'_>]| {
|
||||
for input in inputs {
|
||||
match input.kind {
|
||||
hir::TyKind::Rptr(lt, _) => {
|
||||
if lt.name.ident() == name {
|
||||
// include the trailing whitespace between the lifetime and type names
|
||||
let lt_through_ty_span = lifetime.span.to(input.span.shrink_to_hi());
|
||||
remove_use = Some(
|
||||
self.tcx
|
||||
.sess
|
||||
.source_map()
|
||||
.span_until_non_whitespace(lt_through_ty_span),
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
hir::TyKind::Path(QPath::Resolved(_, path)) => {
|
||||
let last_segment = &path.segments[path.segments.len() - 1];
|
||||
let generics = last_segment.args();
|
||||
for arg in generics.args.iter() {
|
||||
if let GenericArg::Lifetime(lt) = arg {
|
||||
if lt.name.ident() == name {
|
||||
elide_use = Some(lt.span);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
};
|
||||
if let Node::Lifetime(hir_lifetime) = self.tcx.hir().get(lifetime.hir_id) {
|
||||
if let Some(parent) =
|
||||
self.tcx.hir().find_by_def_id(self.tcx.hir().get_parent_item(hir_lifetime.hir_id))
|
||||
{
|
||||
match parent {
|
||||
Node::Item(item) => {
|
||||
if let hir::ItemKind::Fn(sig, _, _) = &item.kind {
|
||||
find_arg_use_span(sig.decl.inputs);
|
||||
}
|
||||
}
|
||||
Node::ImplItem(impl_item) => {
|
||||
if let hir::ImplItemKind::Fn(sig, _) = &impl_item.kind {
|
||||
find_arg_use_span(sig.decl.inputs);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let msg = "elide the single-use lifetime";
|
||||
match (remove_decl, remove_use, elide_use) {
|
||||
(Some(decl_span), Some(use_span), None) => {
|
||||
// if both declaration and use deletion spans start at the same
|
||||
// place ("start at" because the latter includes trailing
|
||||
// whitespace), then this is an in-band lifetime
|
||||
if decl_span.shrink_to_lo() == use_span.shrink_to_lo() {
|
||||
err.span_suggestion(
|
||||
use_span,
|
||||
msg,
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
err.multipart_suggestion(
|
||||
msg,
|
||||
vec![(decl_span, String::new()), (use_span, String::new())],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
(Some(decl_span), None, Some(use_span)) => {
|
||||
err.multipart_suggestion(
|
||||
msg,
|
||||
vec![(decl_span, String::new()), (use_span, "'_".to_owned())],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_uses_for_lifetimes_defined_by_scope(&mut self) {
|
||||
let Scope::Binder { lifetimes: defined_by, .. } = self.scope else {
|
||||
debug!("check_uses_for_lifetimes_defined_by_scope: not in a binder scope");
|
||||
return;
|
||||
};
|
||||
|
||||
let def_ids: Vec<_> = defined_by
|
||||
.values()
|
||||
.flat_map(|region| match region {
|
||||
Region::EarlyBound(_, def_id)
|
||||
| Region::LateBound(_, _, def_id)
|
||||
| Region::Free(_, def_id) => Some(*def_id),
|
||||
|
||||
Region::LateBoundAnon(..) | Region::Static => None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
'lifetimes: for def_id in def_ids {
|
||||
debug!("check_uses_for_lifetimes_defined_by_scope: def_id = {:?}", def_id);
|
||||
|
||||
let lifetimeuseset = self.lifetime_uses.remove(&def_id);
|
||||
|
||||
debug!(
|
||||
"check_uses_for_lifetimes_defined_by_scope: lifetimeuseset = {:?}",
|
||||
lifetimeuseset
|
||||
);
|
||||
|
||||
match lifetimeuseset {
|
||||
Some(LifetimeUseSet::One(lifetime)) => {
|
||||
debug!(?def_id);
|
||||
if let Some((id, span, name)) =
|
||||
match self.tcx.hir().get_by_def_id(def_id.expect_local()) {
|
||||
Node::Lifetime(hir_lifetime) => Some((
|
||||
hir_lifetime.hir_id,
|
||||
hir_lifetime.span,
|
||||
hir_lifetime.name.ident(),
|
||||
)),
|
||||
Node::GenericParam(param) => {
|
||||
Some((param.hir_id, param.span, param.name.ident()))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
{
|
||||
debug!("id = {:?} span = {:?} name = {:?}", id, span, name);
|
||||
if name.name == kw::UnderscoreLifetime {
|
||||
continue;
|
||||
}
|
||||
|
||||
let parent_def_id = self.tcx.parent(def_id);
|
||||
if let Some(def_id) = parent_def_id.as_local() {
|
||||
// lifetimes in `derive` expansions don't count (Issue #53738)
|
||||
if self.tcx.has_attr(def_id.to_def_id(), sym::automatically_derived) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// opaque types generated when desugaring an async function can have a single
|
||||
// use lifetime even if it is explicitly denied (Issue #77175)
|
||||
if let hir::Node::Item(hir::Item {
|
||||
kind: hir::ItemKind::OpaqueTy(ref opaque),
|
||||
..
|
||||
}) = self.tcx.hir().get_by_def_id(def_id)
|
||||
{
|
||||
if !matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn(..)) {
|
||||
continue 'lifetimes;
|
||||
}
|
||||
// We want to do this only if the lifetime identifier is already defined
|
||||
// in the async function that generated this. Otherwise it could be
|
||||
// an opaque type defined by the developer and we still want this
|
||||
// lint to fail compilation
|
||||
for p in opaque.generics.params {
|
||||
if defined_by.contains_key(&p.name) {
|
||||
continue 'lifetimes;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.tcx.struct_span_lint_hir(
|
||||
lint::builtin::SINGLE_USE_LIFETIMES,
|
||||
id,
|
||||
span,
|
||||
|lint| {
|
||||
let mut err = lint.build(&format!(
|
||||
"lifetime parameter `{}` only used once",
|
||||
name
|
||||
));
|
||||
if span == lifetime.span {
|
||||
// spans are the same for in-band lifetime declarations
|
||||
err.span_label(span, "this lifetime is only used here");
|
||||
} else {
|
||||
err.span_label(span, "this lifetime...");
|
||||
err.span_label(lifetime.span, "...is used only here");
|
||||
}
|
||||
self.suggest_eliding_single_use_lifetime(
|
||||
&mut err, def_id, lifetime,
|
||||
);
|
||||
err.emit();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Some(LifetimeUseSet::Many) => {
|
||||
debug!("not one use lifetime");
|
||||
}
|
||||
None => {
|
||||
if let Some((id, span, name)) =
|
||||
match self.tcx.hir().get_by_def_id(def_id.expect_local()) {
|
||||
Node::Lifetime(hir_lifetime) => Some((
|
||||
hir_lifetime.hir_id,
|
||||
hir_lifetime.span,
|
||||
hir_lifetime.name.ident(),
|
||||
)),
|
||||
Node::GenericParam(param) => {
|
||||
Some((param.hir_id, param.span, param.name.ident()))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
{
|
||||
debug!("id ={:?} span = {:?} name = {:?}", id, span, name);
|
||||
self.tcx.struct_span_lint_hir(
|
||||
lint::builtin::UNUSED_LIFETIMES,
|
||||
id,
|
||||
span,
|
||||
|lint| {
|
||||
let mut err = lint
|
||||
.build(&format!("lifetime parameter `{}` never used", name));
|
||||
let parent_def_id = self.tcx.parent(def_id);
|
||||
if let Some(generics) =
|
||||
self.tcx.hir().get_generics(parent_def_id.expect_local())
|
||||
{
|
||||
let unused_lt_span =
|
||||
self.lifetime_deletion_span(name, generics);
|
||||
if let Some(span) = unused_lt_span {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"elide the unused lifetime",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
err.emit();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Visits self by adding a scope and handling recursive walk over the contents with `walk`.
|
||||
///
|
||||
/// Handles visiting fns and methods. These are a bit complicated because we must distinguish
|
||||
@ -2204,7 +1888,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||
next_early_index,
|
||||
s: self.scope,
|
||||
opaque_type_parent: true,
|
||||
track_lifetime_uses: false,
|
||||
scope_type: BinderScopeType::Normal,
|
||||
allow_late_bound: true,
|
||||
};
|
||||
@ -3201,41 +2884,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if, in the current scope, replacing `'_` would be
|
||||
/// equivalent to a single-use lifetime.
|
||||
fn track_lifetime_uses(&self) -> bool {
|
||||
let mut scope = self.scope;
|
||||
loop {
|
||||
match *scope {
|
||||
Scope::Root => break false,
|
||||
|
||||
// Inside of items, it depends on the kind of item.
|
||||
Scope::Binder { track_lifetime_uses, .. } => break track_lifetime_uses,
|
||||
|
||||
// Inside a body, `'_` will use an inference variable,
|
||||
// should be fine.
|
||||
Scope::Body { .. } => break true,
|
||||
|
||||
// A lifetime only used in a fn argument could as well
|
||||
// be replaced with `'_`, as that would generate a
|
||||
// fresh name, too.
|
||||
Scope::Elision { elide: Elide::FreshLateAnon(..), .. } => break true,
|
||||
|
||||
// In the return type or other such place, `'_` is not
|
||||
// going to make a fresh name, so we cannot
|
||||
// necessarily replace a single-use lifetime with
|
||||
// `'_`.
|
||||
Scope::Elision {
|
||||
elide: Elide::Exact(_) | Elide::Error(_) | Elide::Forbid, ..
|
||||
} => break false,
|
||||
|
||||
Scope::ObjectLifetimeDefault { s, .. }
|
||||
| Scope::Supertrait { s, .. }
|
||||
| Scope::TraitRefBoundary { s, .. } => scope = s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self))]
|
||||
fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: Region) {
|
||||
debug!(
|
||||
@ -3243,27 +2891,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||
span = ?self.tcx.sess.source_map().span_to_diagnostic_string(lifetime_ref.span)
|
||||
);
|
||||
self.map.defs.insert(lifetime_ref.hir_id, def);
|
||||
|
||||
match def {
|
||||
Region::LateBoundAnon(..) | Region::Static => {
|
||||
// These are anonymous lifetimes or lifetimes that are not declared.
|
||||
}
|
||||
|
||||
Region::Free(_, def_id)
|
||||
| Region::LateBound(_, _, def_id)
|
||||
| Region::EarlyBound(_, def_id) => {
|
||||
// A lifetime declared by the user.
|
||||
let track_lifetime_uses = self.track_lifetime_uses();
|
||||
debug!(?track_lifetime_uses);
|
||||
if track_lifetime_uses && !self.lifetime_uses.contains_key(&def_id) {
|
||||
debug!("first use of {:?}", def_id);
|
||||
self.lifetime_uses.insert(def_id, LifetimeUseSet::One(lifetime_ref));
|
||||
} else {
|
||||
debug!("many uses of {:?}", def_id);
|
||||
self.lifetime_uses.insert(def_id, LifetimeUseSet::Many);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sometimes we resolve a lifetime, but later find that it is an
|
||||
|
@ -1,19 +1,15 @@
|
||||
// Check "unused_lifetimes" lint on both async and sync functions
|
||||
// Both cases should be diagnosed the same way.
|
||||
|
||||
// edition:2018
|
||||
|
||||
#![deny(unused_lifetimes)]
|
||||
|
||||
async fn async_wrong_without_args<'a>() {} //~ ERROR lifetime parameter `'a` never used
|
||||
|
||||
// Async part with unused lifetimes
|
||||
//
|
||||
// Even wrong cases don't cause errors because async functions are desugared with all lifetimes
|
||||
// involved in the signature. So, we cannot predict what lifetimes are unused in async function.
|
||||
async fn async_wrong_without_args<'a>() {}
|
||||
async fn async_wrong_1_lifetime<'a>(_: &i32) {} //~ ERROR lifetime parameter `'a` never used
|
||||
|
||||
async fn async_wrong_1_lifetime<'a>(_: &i32) {}
|
||||
|
||||
async fn async_wrong_2_lifetimes<'a, 'b>(_: &'a i32, _: &i32) {}
|
||||
async fn async_wrong_2_lifetimes<'a, 'b>(_: &'a i32, _: &i32) {} //~ ERROR lifetime parameter `'b` never used
|
||||
|
||||
async fn async_right_1_lifetime<'a>(_: &'a i32) {}
|
||||
|
||||
@ -24,10 +20,6 @@ where
|
||||
I: Iterator<Item = &'a i32>
|
||||
{}
|
||||
|
||||
|
||||
// Sync part with unused lifetimes
|
||||
//
|
||||
// These functions are compiled as supposed
|
||||
fn wrong_without_args<'a>() {} //~ ERROR lifetime parameter `'a` never used
|
||||
|
||||
fn wrong_1_lifetime<'a>(_: &i32) {} //~ ERROR lifetime parameter `'a` never used
|
||||
|
@ -1,28 +1,48 @@
|
||||
error: lifetime parameter `'a` never used
|
||||
--> $DIR/unused-lifetime.rs:31:23
|
||||
--> $DIR/unused-lifetime.rs:8:35
|
||||
|
|
||||
LL | fn wrong_without_args<'a>() {}
|
||||
LL | async fn async_wrong_without_args<'a>() {}
|
||||
| -^^- help: elide the unused lifetime
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/unused-lifetime.rs:5:9
|
||||
--> $DIR/unused-lifetime.rs:6:9
|
||||
|
|
||||
LL | #![deny(unused_lifetimes)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: lifetime parameter `'a` never used
|
||||
--> $DIR/unused-lifetime.rs:33:21
|
||||
--> $DIR/unused-lifetime.rs:10:33
|
||||
|
|
||||
LL | async fn async_wrong_1_lifetime<'a>(_: &i32) {}
|
||||
| -^^- help: elide the unused lifetime
|
||||
|
||||
error: lifetime parameter `'b` never used
|
||||
--> $DIR/unused-lifetime.rs:12:38
|
||||
|
|
||||
LL | async fn async_wrong_2_lifetimes<'a, 'b>(_: &'a i32, _: &i32) {}
|
||||
| --^^
|
||||
| |
|
||||
| help: elide the unused lifetime
|
||||
|
||||
error: lifetime parameter `'a` never used
|
||||
--> $DIR/unused-lifetime.rs:23:23
|
||||
|
|
||||
LL | fn wrong_without_args<'a>() {}
|
||||
| -^^- help: elide the unused lifetime
|
||||
|
||||
error: lifetime parameter `'a` never used
|
||||
--> $DIR/unused-lifetime.rs:25:21
|
||||
|
|
||||
LL | fn wrong_1_lifetime<'a>(_: &i32) {}
|
||||
| -^^- help: elide the unused lifetime
|
||||
|
||||
error: lifetime parameter `'b` never used
|
||||
--> $DIR/unused-lifetime.rs:35:26
|
||||
--> $DIR/unused-lifetime.rs:27:26
|
||||
|
|
||||
LL | fn wrong_2_lifetimes<'a, 'b>(_: &'a i32, _: &i32) {}
|
||||
| --^^
|
||||
| |
|
||||
| help: elide the unused lifetime
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
|
@ -11,6 +11,11 @@ note: the lint level is defined here
|
||||
|
|
||||
LL | #![deny(single_use_lifetimes)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
help: elide the single-use lifetime
|
||||
|
|
||||
LL - a: for<'a> fn(&'a u32),
|
||||
LL + a: fn(&u32),
|
||||
|
|
||||
|
||||
error[E0581]: return type references lifetime `'a`, which is not constrained by the fn input types
|
||||
--> $DIR/fn-types.rs:12:22
|
||||
|
@ -19,4 +19,15 @@ fn left<'x, 'y>(foo: Double<'x, 'y>) -> &'x u32 { foo.f } //~ ERROR `'y` only us
|
||||
fn right<'x, 'y>(foo: Double<'x, 'y>) -> &'y u32 { foo.f } //~ ERROR `'x` only used once
|
||||
//~^ HELP elide the single-use lifetime
|
||||
|
||||
pub trait Tfv<'a> {}
|
||||
|
||||
// Do NOT lint in an HRTB.
|
||||
pub fn g<T: for<'a> Tfv<'a>>() {}
|
||||
|
||||
// Do NOT lint for trait bounds.
|
||||
pub fn h<'a, S>(_: S)
|
||||
where
|
||||
S: Tfv<'a>,
|
||||
{}
|
||||
|
||||
fn main() {}
|
||||
|
@ -14,4 +14,10 @@ fn b<'a>() -> &'a u32 {
|
||||
&22
|
||||
}
|
||||
|
||||
pub trait Tfv<'a> {}
|
||||
impl Tfv<'_> for () {}
|
||||
|
||||
// Do NOT lint if used in return type.
|
||||
pub fn i<'a>() -> impl Tfv<'a> {}
|
||||
|
||||
fn main() {}
|
||||
|
@ -11,6 +11,11 @@ note: the lint level is defined here
|
||||
|
|
||||
LL | #![deny(single_use_lifetimes)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
help: elide the single-use lifetime
|
||||
|
|
||||
LL - impl<'f> Foo<'f> {
|
||||
LL + impl Foo<'_> {
|
||||
|
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -9,6 +9,7 @@ struct Foo<'f> {
|
||||
}
|
||||
|
||||
impl<'f> Foo<'f> { //~ ERROR `'f` only used once
|
||||
//~^ HELP elide the single-use lifetime
|
||||
fn inherent_a<'a>(&self, data: &'a u32) { //~ ERROR `'a` only used once
|
||||
//~^ HELP elide the single-use lifetime
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
error: lifetime parameter `'a` only used once
|
||||
--> $DIR/one-use-in-inherent-method-argument.rs:12:19
|
||||
error: lifetime parameter `'f` only used once
|
||||
--> $DIR/one-use-in-inherent-method-argument.rs:11:6
|
||||
|
|
||||
LL | fn inherent_a<'a>(&self, data: &'a u32) {
|
||||
LL | impl<'f> Foo<'f> {
|
||||
| ^^ -- ...is used only here
|
||||
| |
|
||||
| this lifetime...
|
||||
@ -13,17 +13,23 @@ LL | #![deny(single_use_lifetimes)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
help: elide the single-use lifetime
|
||||
|
|
||||
LL - impl<'f> Foo<'f> {
|
||||
LL + impl Foo<'_> {
|
||||
|
|
||||
|
||||
error: lifetime parameter `'a` only used once
|
||||
--> $DIR/one-use-in-inherent-method-argument.rs:13:19
|
||||
|
|
||||
LL | fn inherent_a<'a>(&self, data: &'a u32) {
|
||||
| ^^ -- ...is used only here
|
||||
| |
|
||||
| this lifetime...
|
||||
|
|
||||
help: elide the single-use lifetime
|
||||
|
|
||||
LL - fn inherent_a<'a>(&self, data: &'a u32) {
|
||||
LL + fn inherent_a(&self, data: &u32) {
|
||||
|
|
||||
|
||||
error: lifetime parameter `'f` only used once
|
||||
--> $DIR/one-use-in-inherent-method-argument.rs:11:6
|
||||
|
|
||||
LL | impl<'f> Foo<'f> {
|
||||
| ^^ -- ...is used only here
|
||||
| |
|
||||
| this lifetime...
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
@ -11,6 +11,11 @@ note: the lint level is defined here
|
||||
|
|
||||
LL | #![deny(single_use_lifetimes)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
help: elide the single-use lifetime
|
||||
|
|
||||
LL - impl<'f> Foo<'f> {
|
||||
LL + impl Foo<'_> {
|
||||
|
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -4,7 +4,8 @@
|
||||
//
|
||||
// check-pass
|
||||
|
||||
#![deny(single_use_lifetimes)]
|
||||
// Use forbid to verify that `automatically_derived` is handled correctly.
|
||||
#![forbid(single_use_lifetimes)]
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
|
@ -18,4 +18,9 @@ impl<'f> Iterator for Foo<'f> {
|
||||
}
|
||||
}
|
||||
|
||||
trait Bar<'a> {
|
||||
// But we should not warn here.
|
||||
fn bar(x: Foo<'a>);
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -11,6 +11,11 @@ note: the lint level is defined here
|
||||
|
|
||||
LL | #![deny(single_use_lifetimes)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
help: elide the single-use lifetime
|
||||
|
|
||||
LL - impl<'f> Foo<'f> {
|
||||
LL + impl Foo<'_> {
|
||||
|
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user