Report elision failures on the AST.
This commit is contained in:
parent
bfd0435fd7
commit
3c5048d2ec
57 changed files with 1641 additions and 1749 deletions
|
@ -21,6 +21,7 @@ use rustc_hir::def::Namespace::{self, *};
|
|||
use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, PartialRes, PerNS};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
|
||||
use rustc_hir::{PrimTy, TraitCandidate};
|
||||
use rustc_middle::middle::resolve_lifetime::Set1;
|
||||
use rustc_middle::ty::DefIdTree;
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_session::lint;
|
||||
|
@ -43,6 +44,10 @@ type IdentMap<T> = FxHashMap<Ident, T>;
|
|||
/// Map from the name in a pattern to its binding mode.
|
||||
type BindingMap = IdentMap<BindingInfo>;
|
||||
|
||||
use diagnostics::{
|
||||
ElisionFnParameter, LifetimeElisionCandidate, MissingLifetime, MissingLifetimeKind,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct BindingInfo {
|
||||
span: Span,
|
||||
|
@ -258,10 +263,13 @@ enum LifetimeRibKind {
|
|||
AnonymousReportError,
|
||||
|
||||
/// Pass responsibility to `resolve_lifetime` code for all cases.
|
||||
AnonymousPassThrough(NodeId, /* in_fn_return */ bool),
|
||||
AnonymousPassThrough(NodeId),
|
||||
|
||||
/// Replace all anonymous lifetimes by provided lifetime.
|
||||
Elided(LifetimeRes),
|
||||
|
||||
/// Signal we cannot find which should be the anonymous lifetime.
|
||||
ElisionFailure,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
@ -525,6 +533,10 @@ struct DiagnosticMetadata<'ast> {
|
|||
|
||||
/// When processing impl trait
|
||||
currently_processing_impl_trait: Option<(TraitRef, Ty)>,
|
||||
|
||||
/// Accumulate the errors due to missed lifetime elision,
|
||||
/// and report them all at once for each function.
|
||||
current_elision_failures: Vec<MissingLifetime>,
|
||||
}
|
||||
|
||||
struct LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
|
@ -543,6 +555,13 @@ struct LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
/// The current set of local scopes for lifetimes.
|
||||
lifetime_ribs: Vec<LifetimeRib>,
|
||||
|
||||
/// We are looking for lifetimes in an elision context.
|
||||
/// The set contains all the resolutions that we encountered so far.
|
||||
/// They will be used to determine the correct lifetime for the fn return type.
|
||||
/// The `LifetimeElisionCandidate` is used for diagnostics, to suggest introducing named
|
||||
/// lifetimes.
|
||||
lifetime_elision_candidates: Option<FxIndexMap<LifetimeRes, LifetimeElisionCandidate>>,
|
||||
|
||||
/// The trait that the current context can refer to.
|
||||
current_trait_ref: Option<(Module<'a>, TraitRef)>,
|
||||
|
||||
|
@ -612,8 +631,10 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
|||
TyKind::Rptr(None, _) => {
|
||||
// Elided lifetime in reference: we resolve as if there was some lifetime `'_` with
|
||||
// NodeId `ty.id`.
|
||||
// This span will be used in case of elision failure.
|
||||
let span = self.r.session.source_map().next_point(ty.span.shrink_to_lo());
|
||||
self.resolve_elided_lifetime(ty.id, span);
|
||||
visit::walk_ty(self, ty);
|
||||
}
|
||||
TyKind::Path(ref qself, ref path) => {
|
||||
self.diagnostic_metadata.current_type_path = Some(ty);
|
||||
|
@ -639,8 +660,8 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
|||
},
|
||||
|this| this.visit_path(&path, ty.id),
|
||||
);
|
||||
self.diagnostic_metadata.current_type_path = prev_ty;
|
||||
return;
|
||||
} else {
|
||||
visit::walk_ty(self, ty)
|
||||
}
|
||||
}
|
||||
TyKind::ImplicitSelf => {
|
||||
|
@ -654,9 +675,16 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
|||
)
|
||||
.map_or(Res::Err, |d| d.res());
|
||||
self.r.record_partial_res(ty.id, PartialRes::new(res));
|
||||
visit::walk_ty(self, ty)
|
||||
}
|
||||
TyKind::ImplTrait(..) => {
|
||||
let candidates = self.lifetime_elision_candidates.take();
|
||||
visit::walk_ty(self, ty);
|
||||
self.lifetime_elision_candidates = candidates;
|
||||
}
|
||||
TyKind::TraitObject(ref bounds, ..) => {
|
||||
self.diagnostic_metadata.current_trait_object = Some(&bounds[..]);
|
||||
visit::walk_ty(self, ty)
|
||||
}
|
||||
TyKind::BareFn(ref bare_fn) => {
|
||||
let span = ty.span.shrink_to_lo().to(bare_fn.decl_span.shrink_to_lo());
|
||||
|
@ -670,25 +698,20 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
|||
},
|
||||
|this| {
|
||||
this.visit_generic_params(&bare_fn.generic_params, false);
|
||||
this.with_lifetime_rib(
|
||||
LifetimeRibKind::AnonymousCreateParameter {
|
||||
binder: ty.id,
|
||||
report_in_path: 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),
|
||||
this.resolve_fn_signature(
|
||||
ty.id,
|
||||
None,
|
||||
false,
|
||||
// We don't need to deal with patterns in parameters, because
|
||||
// they are not possible for foreign or bodiless functions.
|
||||
bare_fn.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)),
|
||||
&bare_fn.decl.output,
|
||||
);
|
||||
},
|
||||
);
|
||||
self.diagnostic_metadata.current_trait_object = prev;
|
||||
return;
|
||||
)
|
||||
}
|
||||
_ => (),
|
||||
_ => visit::walk_ty(self, ty),
|
||||
}
|
||||
visit::walk_ty(self, ty);
|
||||
self.diagnostic_metadata.current_trait_object = prev;
|
||||
self.diagnostic_metadata.current_type_path = prev_ty;
|
||||
}
|
||||
|
@ -762,18 +785,12 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
|||
| FnKind::Fn(_, _, sig, _, generics, None) => {
|
||||
self.visit_fn_header(&sig.header);
|
||||
self.visit_generics(generics);
|
||||
// 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::AnonymousCreateParameter {
|
||||
binder: fn_id,
|
||||
report_in_path: false,
|
||||
},
|
||||
|this| 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),
|
||||
self.resolve_fn_signature(
|
||||
fn_id,
|
||||
None,
|
||||
sig.decl.has_self(),
|
||||
sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)),
|
||||
&sig.decl.output,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -798,19 +815,15 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
|||
let declaration = &sig.decl;
|
||||
let async_node_id = sig.header.asyncness.opt_return_id();
|
||||
|
||||
// Argument-position elided lifetimes must be transformed into fresh
|
||||
// generic parameters. This is especially useful for `async fn`, where
|
||||
// these fresh generic parameters can be applied to the opaque `impl Trait`
|
||||
// return type.
|
||||
this.with_lifetime_rib(
|
||||
LifetimeRibKind::AnonymousCreateParameter {
|
||||
binder: fn_id,
|
||||
// Only emit a hard error for `async fn`, since this kind of
|
||||
// elision has always been allowed in regular `fn`s.
|
||||
report_in_path: async_node_id.is_some(),
|
||||
},
|
||||
// Add each argument to the rib.
|
||||
|this| this.resolve_params(&declaration.inputs),
|
||||
this.resolve_fn_signature(
|
||||
fn_id,
|
||||
async_node_id,
|
||||
declaration.has_self(),
|
||||
declaration
|
||||
.inputs
|
||||
.iter()
|
||||
.map(|Param { pat, ty, .. }| (Some(&**pat), &**ty)),
|
||||
&declaration.output,
|
||||
);
|
||||
|
||||
// Construct the list of in-scope lifetime parameters for async lowering.
|
||||
|
@ -849,23 +862,13 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
|||
.insert(async_node_id, extra_lifetime_params);
|
||||
}
|
||||
|
||||
this.with_lifetime_rib(
|
||||
LifetimeRibKind::AnonymousPassThrough(
|
||||
// For async fn, the return type appears inside a custom
|
||||
// `impl Future` RPIT, so we override the binder's id.
|
||||
async_node_id.unwrap_or(fn_id),
|
||||
true,
|
||||
),
|
||||
|this| visit::walk_fn_ret_ty(this, &declaration.output),
|
||||
);
|
||||
|
||||
if let Some(body) = body {
|
||||
// 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, false),
|
||||
LifetimeRibKind::AnonymousPassThrough(fn_id),
|
||||
|this| this.visit_block(body),
|
||||
);
|
||||
|
||||
|
@ -893,7 +896,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
|||
this.with_lifetime_rib(
|
||||
match binder {
|
||||
ClosureBinder::NotPresent => {
|
||||
LifetimeRibKind::AnonymousPassThrough(fn_id, true)
|
||||
LifetimeRibKind::AnonymousPassThrough(fn_id)
|
||||
}
|
||||
ClosureBinder::For { .. } => LifetimeRibKind::AnonymousReportError,
|
||||
},
|
||||
|
@ -905,7 +908,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
|||
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, false),
|
||||
LifetimeRibKind::AnonymousPassThrough(fn_id),
|
||||
|this| this.visit_expr(body),
|
||||
);
|
||||
|
||||
|
@ -1035,16 +1038,12 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
|||
kind: LifetimeBinderKind::PolyTrait,
|
||||
..
|
||||
} => {
|
||||
self.with_lifetime_rib(
|
||||
LifetimeRibKind::AnonymousCreateParameter {
|
||||
binder,
|
||||
report_in_path: false,
|
||||
},
|
||||
|this| walk_list!(this, visit_ty, &p_args.inputs),
|
||||
);
|
||||
self.with_lifetime_rib(
|
||||
LifetimeRibKind::AnonymousPassThrough(binder, true),
|
||||
|this| visit::walk_fn_ret_ty(this, &p_args.output),
|
||||
self.resolve_fn_signature(
|
||||
binder,
|
||||
None,
|
||||
false,
|
||||
p_args.inputs.iter().map(|ty| (None, &**ty)),
|
||||
&p_args.output,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
@ -1058,6 +1057,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
|||
| LifetimeRibKind::AnonymousCreateParameter { .. }
|
||||
| LifetimeRibKind::AnonymousReportError
|
||||
| LifetimeRibKind::Elided(_)
|
||||
| LifetimeRibKind::ElisionFailure
|
||||
| LifetimeRibKind::AnonConst
|
||||
| LifetimeRibKind::ConstGeneric => {}
|
||||
}
|
||||
|
@ -1162,6 +1162,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
},
|
||||
label_ribs: Vec::new(),
|
||||
lifetime_ribs: Vec::new(),
|
||||
lifetime_elision_candidates: None,
|
||||
current_trait_ref: None,
|
||||
diagnostic_metadata: Box::new(DiagnosticMetadata::default()),
|
||||
// errors at module scope should always be reported
|
||||
|
@ -1368,7 +1369,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
work: impl FnOnce(&mut Self) -> T,
|
||||
) -> T {
|
||||
self.lifetime_ribs.push(LifetimeRib::new(kind));
|
||||
let outer_elision_candidates = self.lifetime_elision_candidates.take();
|
||||
let ret = work(self);
|
||||
self.lifetime_elision_candidates = outer_elision_candidates;
|
||||
self.lifetime_ribs.pop();
|
||||
ret
|
||||
}
|
||||
|
@ -1378,7 +1381,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
let ident = lifetime.ident;
|
||||
|
||||
if ident.name == kw::StaticLifetime {
|
||||
self.record_lifetime_res(lifetime.id, LifetimeRes::Static);
|
||||
self.record_lifetime_res(
|
||||
lifetime.id,
|
||||
LifetimeRes::Static,
|
||||
LifetimeElisionCandidate::Named,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1391,7 +1398,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
let rib = &self.lifetime_ribs[i];
|
||||
let normalized_ident = ident.normalize_to_macros_2_0();
|
||||
if let Some(&(_, res)) = rib.bindings.get(&normalized_ident) {
|
||||
self.record_lifetime_res(lifetime.id, res);
|
||||
self.record_lifetime_res(lifetime.id, res, LifetimeElisionCandidate::Named);
|
||||
|
||||
if let LifetimeRes::Param { param, .. } = res {
|
||||
match self.lifetime_uses.entry(param) {
|
||||
|
@ -1405,12 +1412,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
// Do not suggest eliding a lifetime where an anonymous
|
||||
// lifetime would be illegal.
|
||||
LifetimeRibKind::Item
|
||||
| LifetimeRibKind::AnonymousPassThrough(_, true)
|
||||
| LifetimeRibKind::AnonymousReportError => {
|
||||
Some(LifetimeUseSet::Many)
|
||||
}
|
||||
| LifetimeRibKind::AnonymousReportError
|
||||
| LifetimeRibKind::ElisionFailure => Some(LifetimeUseSet::Many),
|
||||
// An anonymous lifetime is legal here, go ahead.
|
||||
LifetimeRibKind::AnonymousPassThrough(_, false)
|
||||
LifetimeRibKind::AnonymousPassThrough(_)
|
||||
| LifetimeRibKind::AnonymousCreateParameter { .. } => {
|
||||
Some(LifetimeUseSet::One { use_span: ident.span, use_ctxt })
|
||||
}
|
||||
|
@ -1442,12 +1447,20 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
LifetimeRibKind::Item => break,
|
||||
LifetimeRibKind::ConstGeneric => {
|
||||
self.emit_non_static_lt_in_const_generic_error(lifetime);
|
||||
self.r.lifetimes_res_map.insert(lifetime.id, LifetimeRes::Error);
|
||||
self.record_lifetime_res(
|
||||
lifetime.id,
|
||||
LifetimeRes::Error,
|
||||
LifetimeElisionCandidate::Ignore,
|
||||
);
|
||||
return;
|
||||
}
|
||||
LifetimeRibKind::AnonConst => {
|
||||
self.maybe_emit_forbidden_non_static_lifetime_error(lifetime);
|
||||
self.r.lifetimes_res_map.insert(lifetime.id, LifetimeRes::Error);
|
||||
self.record_lifetime_res(
|
||||
lifetime.id,
|
||||
LifetimeRes::Error,
|
||||
LifetimeElisionCandidate::Ignore,
|
||||
);
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
|
@ -1465,19 +1478,31 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
}
|
||||
|
||||
self.emit_undeclared_lifetime_error(lifetime, outer_res);
|
||||
self.record_lifetime_res(lifetime.id, LifetimeRes::Error);
|
||||
self.record_lifetime_res(lifetime.id, LifetimeRes::Error, LifetimeElisionCandidate::Named);
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self))]
|
||||
fn resolve_anonymous_lifetime(&mut self, lifetime: &Lifetime, elided: bool) {
|
||||
debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime);
|
||||
|
||||
let missing_lifetime = MissingLifetime {
|
||||
id: lifetime.id,
|
||||
span: lifetime.ident.span,
|
||||
kind: if elided {
|
||||
MissingLifetimeKind::Ampersand
|
||||
} else {
|
||||
MissingLifetimeKind::Underscore
|
||||
},
|
||||
count: 1,
|
||||
};
|
||||
let elision_candidate = LifetimeElisionCandidate::Missing(missing_lifetime);
|
||||
for i in (0..self.lifetime_ribs.len()).rev() {
|
||||
let rib = &mut self.lifetime_ribs[i];
|
||||
debug!(?rib.kind);
|
||||
match rib.kind {
|
||||
LifetimeRibKind::AnonymousCreateParameter { binder, .. } => {
|
||||
let res = self.create_fresh_lifetime(lifetime.id, lifetime.ident, binder);
|
||||
self.record_lifetime_res(lifetime.id, res);
|
||||
self.record_lifetime_res(lifetime.id, res, elision_candidate);
|
||||
return;
|
||||
}
|
||||
LifetimeRibKind::AnonymousReportError => {
|
||||
|
@ -1499,18 +1524,24 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
.span_label(lifetime.ident.span, note)
|
||||
.emit();
|
||||
|
||||
self.record_lifetime_res(lifetime.id, LifetimeRes::Error);
|
||||
self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate);
|
||||
return;
|
||||
}
|
||||
LifetimeRibKind::AnonymousPassThrough(node_id, _) => {
|
||||
LifetimeRibKind::AnonymousPassThrough(node_id) => {
|
||||
self.record_lifetime_res(
|
||||
lifetime.id,
|
||||
LifetimeRes::Anonymous { binder: node_id, elided },
|
||||
elision_candidate,
|
||||
);
|
||||
return;
|
||||
}
|
||||
LifetimeRibKind::Elided(res) => {
|
||||
self.record_lifetime_res(lifetime.id, res);
|
||||
self.record_lifetime_res(lifetime.id, res, elision_candidate);
|
||||
return;
|
||||
}
|
||||
LifetimeRibKind::ElisionFailure => {
|
||||
self.diagnostic_metadata.current_elision_failures.push(missing_lifetime);
|
||||
self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate);
|
||||
return;
|
||||
}
|
||||
LifetimeRibKind::Item => break,
|
||||
|
@ -1519,23 +1550,20 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
| LifetimeRibKind::AnonConst => {}
|
||||
}
|
||||
}
|
||||
// This resolution is wrong, it passes the work to HIR lifetime resolution.
|
||||
// We cannot use `LifetimeRes::Error` because we do not emit a diagnostic.
|
||||
self.record_lifetime_res(
|
||||
lifetime.id,
|
||||
LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided },
|
||||
);
|
||||
self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate);
|
||||
self.report_missing_lifetime_specifiers(vec![missing_lifetime], None);
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self))]
|
||||
fn resolve_elided_lifetime(&mut self, anchor_id: NodeId, span: Span) {
|
||||
let id = self.r.next_node_id();
|
||||
let lt = Lifetime { id, ident: Ident::new(kw::UnderscoreLifetime, span) };
|
||||
|
||||
self.record_lifetime_res(
|
||||
anchor_id,
|
||||
LifetimeRes::ElidedAnchor { start: id, end: NodeId::from_u32(id.as_u32() + 1) },
|
||||
LifetimeElisionCandidate::Ignore,
|
||||
);
|
||||
|
||||
let lt = Lifetime { id, ident: Ident::new(kw::UnderscoreLifetime, span) };
|
||||
self.resolve_anonymous_lifetime(<, true);
|
||||
}
|
||||
|
||||
|
@ -1620,16 +1648,17 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
self.record_lifetime_res(
|
||||
segment_id,
|
||||
LifetimeRes::ElidedAnchor { start: node_ids.start, end: node_ids.end },
|
||||
LifetimeElisionCandidate::Ignore,
|
||||
);
|
||||
|
||||
if !missing {
|
||||
// Do not create a parameter for patterns and expressions.
|
||||
for rib in self.lifetime_ribs.iter().rev() {
|
||||
match rib.kind {
|
||||
LifetimeRibKind::AnonymousPassThrough(binder, _) => {
|
||||
LifetimeRibKind::AnonymousPassThrough(binder) => {
|
||||
let res = LifetimeRes::Anonymous { binder, elided: true };
|
||||
for id in node_ids {
|
||||
self.record_lifetime_res(id, res);
|
||||
self.record_lifetime_res(id, res, LifetimeElisionCandidate::Named);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1644,12 +1673,13 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
let res =
|
||||
LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided: true };
|
||||
for id in node_ids {
|
||||
self.record_lifetime_res(id, res);
|
||||
self.record_lifetime_res(id, res, LifetimeElisionCandidate::Named);
|
||||
}
|
||||
break;
|
||||
}
|
||||
LifetimeRibKind::AnonymousCreateParameter { .. }
|
||||
| LifetimeRibKind::Elided(_)
|
||||
| LifetimeRibKind::ElisionFailure
|
||||
| LifetimeRibKind::Generics { .. }
|
||||
| LifetimeRibKind::ConstGeneric
|
||||
| LifetimeRibKind::AnonConst => {}
|
||||
|
@ -1658,6 +1688,16 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
continue;
|
||||
}
|
||||
|
||||
let missing_lifetime = MissingLifetime {
|
||||
id: node_ids.start,
|
||||
span: elided_lifetime_span,
|
||||
kind: if segment.has_generic_args {
|
||||
MissingLifetimeKind::Comma
|
||||
} else {
|
||||
MissingLifetimeKind::Brackets
|
||||
},
|
||||
count: expected_lifetimes,
|
||||
};
|
||||
let mut should_lint = true;
|
||||
for rib in self.lifetime_ribs.iter().rev() {
|
||||
match rib.kind {
|
||||
|
@ -1688,29 +1728,60 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
should_lint = false;
|
||||
|
||||
for id in node_ids {
|
||||
self.record_lifetime_res(id, LifetimeRes::Error);
|
||||
self.record_lifetime_res(
|
||||
id,
|
||||
LifetimeRes::Error,
|
||||
LifetimeElisionCandidate::Named,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Do not create a parameter for patterns and expressions.
|
||||
LifetimeRibKind::AnonymousCreateParameter { binder, .. } => {
|
||||
// Group all suggestions into the first record.
|
||||
let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime);
|
||||
for id in node_ids {
|
||||
let res = self.create_fresh_lifetime(id, ident, binder);
|
||||
self.record_lifetime_res(id, res);
|
||||
self.record_lifetime_res(
|
||||
id,
|
||||
res,
|
||||
replace(&mut candidate, LifetimeElisionCandidate::Named),
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// `PassThrough` is the normal case.
|
||||
LifetimeRibKind::AnonymousPassThrough(binder, _) => {
|
||||
LifetimeRibKind::AnonymousPassThrough(binder) => {
|
||||
let res = LifetimeRes::Anonymous { binder, elided: true };
|
||||
let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime);
|
||||
for id in node_ids {
|
||||
self.record_lifetime_res(id, res);
|
||||
self.record_lifetime_res(
|
||||
id,
|
||||
res,
|
||||
replace(&mut candidate, LifetimeElisionCandidate::Ignore),
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
LifetimeRibKind::Elided(res) => {
|
||||
let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime);
|
||||
for id in node_ids {
|
||||
self.record_lifetime_res(id, res);
|
||||
self.record_lifetime_res(
|
||||
id,
|
||||
res,
|
||||
replace(&mut candidate, LifetimeElisionCandidate::Ignore),
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
LifetimeRibKind::ElisionFailure => {
|
||||
self.diagnostic_metadata.current_elision_failures.push(missing_lifetime);
|
||||
for id in node_ids {
|
||||
self.record_lifetime_res(
|
||||
id,
|
||||
LifetimeRes::Error,
|
||||
LifetimeElisionCandidate::Ignore,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1719,13 +1790,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
// we simply resolve to an implicit lifetime, which will be checked later, at
|
||||
// which point a suitable error will be emitted.
|
||||
LifetimeRibKind::AnonymousReportError | LifetimeRibKind::Item => {
|
||||
// FIXME(cjgillot) This resolution is wrong, but this does not matter
|
||||
// since these cases are erroneous anyway. Lifetime resolution should
|
||||
// emit a "missing lifetime specifier" diagnostic.
|
||||
let res = LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided: true };
|
||||
for id in node_ids {
|
||||
self.record_lifetime_res(id, res);
|
||||
self.record_lifetime_res(
|
||||
id,
|
||||
LifetimeRes::Error,
|
||||
LifetimeElisionCandidate::Ignore,
|
||||
);
|
||||
}
|
||||
self.report_missing_lifetime_specifiers(vec![missing_lifetime], None);
|
||||
break;
|
||||
}
|
||||
LifetimeRibKind::Generics { .. }
|
||||
|
@ -1752,13 +1824,223 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self))]
|
||||
fn record_lifetime_res(&mut self, id: NodeId, res: LifetimeRes) {
|
||||
fn record_lifetime_res(
|
||||
&mut self,
|
||||
id: NodeId,
|
||||
res: LifetimeRes,
|
||||
candidate: LifetimeElisionCandidate,
|
||||
) {
|
||||
if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) {
|
||||
panic!(
|
||||
"lifetime {:?} resolved multiple times ({:?} before, {:?} now)",
|
||||
id, prev_res, res
|
||||
)
|
||||
}
|
||||
match res {
|
||||
LifetimeRes::Param { .. }
|
||||
| LifetimeRes::Fresh { .. }
|
||||
| LifetimeRes::Anonymous { .. }
|
||||
| LifetimeRes::Static => {
|
||||
if let Some(ref mut candidates) = self.lifetime_elision_candidates {
|
||||
candidates.insert(res, candidate);
|
||||
}
|
||||
}
|
||||
LifetimeRes::Error | LifetimeRes::ElidedAnchor { .. } => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self))]
|
||||
fn record_lifetime_param(&mut self, id: NodeId, res: LifetimeRes) {
|
||||
if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) {
|
||||
panic!(
|
||||
"lifetime parameter {:?} resolved multiple times ({:?} before, {:?} now)",
|
||||
id, prev_res, res
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform resolution of a function signature, accounting for lifetime elision.
|
||||
#[tracing::instrument(level = "debug", skip(self, inputs))]
|
||||
fn resolve_fn_signature(
|
||||
&mut self,
|
||||
fn_id: NodeId,
|
||||
async_node_id: Option<NodeId>,
|
||||
has_self: bool,
|
||||
inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)> + Clone,
|
||||
output_ty: &'ast FnRetTy,
|
||||
) {
|
||||
// Add each argument to the rib.
|
||||
let parameter_rib = LifetimeRibKind::AnonymousCreateParameter {
|
||||
binder: fn_id,
|
||||
report_in_path: async_node_id.is_some(),
|
||||
};
|
||||
let elision_lifetime =
|
||||
self.with_lifetime_rib(parameter_rib, |this| this.resolve_fn_params(has_self, inputs));
|
||||
debug!(?elision_lifetime);
|
||||
|
||||
let outer_failures = take(&mut self.diagnostic_metadata.current_elision_failures);
|
||||
let output_rib = if let Ok(res) = elision_lifetime.as_ref() {
|
||||
LifetimeRibKind::Elided(*res)
|
||||
} else {
|
||||
LifetimeRibKind::ElisionFailure
|
||||
};
|
||||
self.with_lifetime_rib(output_rib, |this| visit::walk_fn_ret_ty(this, &output_ty));
|
||||
let elision_failures =
|
||||
replace(&mut self.diagnostic_metadata.current_elision_failures, outer_failures);
|
||||
if !elision_failures.is_empty() {
|
||||
let Err(failure_info) = elision_lifetime else { bug!() };
|
||||
self.report_missing_lifetime_specifiers(elision_failures, Some(failure_info));
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve inside function parameters and parameter types.
|
||||
/// Returns the lifetime for elision in fn return type,
|
||||
/// or diagnostic information in case of elision failure.
|
||||
fn resolve_fn_params(
|
||||
&mut self,
|
||||
has_self: bool,
|
||||
inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)>,
|
||||
) -> Result<LifetimeRes, (Vec<MissingLifetime>, Vec<ElisionFnParameter>)> {
|
||||
let outer_candidates =
|
||||
replace(&mut self.lifetime_elision_candidates, Some(Default::default()));
|
||||
|
||||
let mut elision_lifetime = None;
|
||||
let mut lifetime_count = 0;
|
||||
let mut parameter_info = Vec::new();
|
||||
|
||||
let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
|
||||
for (index, (pat, ty)) in inputs.enumerate() {
|
||||
debug!(?pat, ?ty);
|
||||
if let Some(pat) = pat {
|
||||
self.resolve_pattern(pat, PatternSource::FnParam, &mut bindings);
|
||||
}
|
||||
self.visit_ty(ty);
|
||||
|
||||
if let Some(ref candidates) = self.lifetime_elision_candidates {
|
||||
let new_count = candidates.len();
|
||||
let local_count = new_count - lifetime_count;
|
||||
if local_count != 0 {
|
||||
parameter_info.push(ElisionFnParameter {
|
||||
index,
|
||||
ident: if let Some(pat) = pat && let PatKind::Ident(_, ident, _) = pat.kind {
|
||||
Some(ident)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
lifetime_count: local_count,
|
||||
span: ty.span,
|
||||
});
|
||||
}
|
||||
lifetime_count = new_count;
|
||||
}
|
||||
|
||||
// Handle `self` specially.
|
||||
if index == 0 && has_self {
|
||||
let self_lifetime = self.find_lifetime_for_self(ty);
|
||||
if let Set1::One(lifetime) = self_lifetime {
|
||||
elision_lifetime = Some(lifetime);
|
||||
self.lifetime_elision_candidates = None;
|
||||
} else {
|
||||
self.lifetime_elision_candidates = Some(Default::default());
|
||||
lifetime_count = 0;
|
||||
}
|
||||
}
|
||||
debug!("(resolving function / closure) recorded parameter");
|
||||
}
|
||||
|
||||
let all_candidates = replace(&mut self.lifetime_elision_candidates, outer_candidates);
|
||||
debug!(?all_candidates);
|
||||
|
||||
if let Some(res) = elision_lifetime {
|
||||
return Ok(res);
|
||||
}
|
||||
|
||||
// We do not have a `self` candidate, look at the full list.
|
||||
let all_candidates = all_candidates.unwrap();
|
||||
if all_candidates.len() == 1 {
|
||||
Ok(*all_candidates.first().unwrap().0)
|
||||
} else {
|
||||
let all_candidates = all_candidates
|
||||
.into_iter()
|
||||
.filter_map(|(_, candidate)| match candidate {
|
||||
LifetimeElisionCandidate::Ignore | LifetimeElisionCandidate::Named => None,
|
||||
LifetimeElisionCandidate::Missing(missing) => Some(missing),
|
||||
})
|
||||
.collect();
|
||||
Err((all_candidates, parameter_info))
|
||||
}
|
||||
}
|
||||
|
||||
/// List all the lifetimes that appear in the provided type.
|
||||
fn find_lifetime_for_self(&self, ty: &'ast Ty) -> Set1<LifetimeRes> {
|
||||
struct SelfVisitor<'r, 'a> {
|
||||
r: &'r Resolver<'a>,
|
||||
impl_self: Option<Res>,
|
||||
lifetime: Set1<LifetimeRes>,
|
||||
}
|
||||
|
||||
impl SelfVisitor<'_, '_> {
|
||||
// Look for `self: &'a Self` - also desugared from `&'a self`,
|
||||
// and if that matches, use it for elision and return early.
|
||||
fn is_self_ty(&self, ty: &Ty) -> bool {
|
||||
match ty.kind {
|
||||
TyKind::ImplicitSelf => true,
|
||||
TyKind::Path(None, _) => {
|
||||
let path_res = self.r.partial_res_map[&ty.id].base_res();
|
||||
if let Res::SelfTy { .. } = path_res {
|
||||
return true;
|
||||
}
|
||||
Some(path_res) == self.impl_self
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visitor<'a> for SelfVisitor<'_, '_> {
|
||||
fn visit_ty(&mut self, ty: &'a Ty) {
|
||||
trace!("SelfVisitor considering ty={:?}", ty);
|
||||
if let TyKind::Rptr(lt, ref mt) = ty.kind && self.is_self_ty(&mt.ty) {
|
||||
let lt_id = if let Some(lt) = lt {
|
||||
lt.id
|
||||
} else {
|
||||
let res = self.r.lifetimes_res_map[&ty.id];
|
||||
let LifetimeRes::ElidedAnchor { start, .. } = res else { bug!() };
|
||||
start
|
||||
};
|
||||
let lt_res = self.r.lifetimes_res_map[<_id];
|
||||
trace!("SelfVisitor inserting res={:?}", lt_res);
|
||||
self.lifetime.insert(lt_res);
|
||||
}
|
||||
visit::walk_ty(self, ty)
|
||||
}
|
||||
}
|
||||
|
||||
let impl_self = self
|
||||
.diagnostic_metadata
|
||||
.current_self_type
|
||||
.as_ref()
|
||||
.and_then(|ty| {
|
||||
if let TyKind::Path(None, _) = ty.kind {
|
||||
self.r.partial_res_map.get(&ty.id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.map(|res| res.base_res())
|
||||
.filter(|res| {
|
||||
// Permit the types that unambiguously always
|
||||
// result in the same type constructor being used
|
||||
// (it can't differ between `Self` and `self`).
|
||||
matches!(
|
||||
res,
|
||||
Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, _,) | Res::PrimTy(_)
|
||||
)
|
||||
});
|
||||
let mut visitor = SelfVisitor { r: self.r, impl_self, lifetime: Set1::Empty };
|
||||
visitor.visit_ty(ty);
|
||||
trace!("SelfVisitor found={:?}", visitor.lifetime);
|
||||
visitor.lifetime
|
||||
}
|
||||
|
||||
/// Searches the current set of local scopes for labels. Returns the `NodeId` of the resolved
|
||||
|
@ -1983,22 +2265,29 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
|
||||
ItemKind::Static(ref ty, _, ref expr) | ItemKind::Const(_, ref ty, ref expr) => {
|
||||
self.with_item_rib(|this| {
|
||||
this.visit_ty(ty);
|
||||
if let Some(expr) = expr {
|
||||
let constant_item_kind = match item.kind {
|
||||
ItemKind::Const(..) => ConstantItemKind::Const,
|
||||
ItemKind::Static(..) => ConstantItemKind::Static,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
// We already forbid generic params because of the above item rib,
|
||||
// so it doesn't matter whether this is a trivial constant.
|
||||
this.with_constant_rib(
|
||||
IsRepeatExpr::No,
|
||||
HasGenericParams::Yes,
|
||||
Some((item.ident, constant_item_kind)),
|
||||
|this| this.visit_expr(expr),
|
||||
);
|
||||
}
|
||||
this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| {
|
||||
this.visit_ty(ty);
|
||||
});
|
||||
this.with_lifetime_rib(
|
||||
LifetimeRibKind::AnonymousPassThrough(item.id),
|
||||
|this| {
|
||||
if let Some(expr) = expr {
|
||||
let constant_item_kind = match item.kind {
|
||||
ItemKind::Const(..) => ConstantItemKind::Const,
|
||||
ItemKind::Static(..) => ConstantItemKind::Static,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
// We already forbid generic params because of the above item rib,
|
||||
// so it doesn't matter whether this is a trivial constant.
|
||||
this.with_constant_rib(
|
||||
IsRepeatExpr::No,
|
||||
HasGenericParams::Yes,
|
||||
Some((item.ident, constant_item_kind)),
|
||||
|this| this.visit_expr(expr),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -2069,7 +2358,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
{
|
||||
diagnostics::signal_lifetime_shadowing(self.r.session, original, param.ident);
|
||||
// Record lifetime res, so lowering knows there is something fishy.
|
||||
self.record_lifetime_res(param.id, LifetimeRes::Error);
|
||||
self.record_lifetime_param(param.id, LifetimeRes::Error);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -2080,7 +2369,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
self.report_error(param.ident.span, err);
|
||||
if let GenericParamKind::Lifetime = param.kind {
|
||||
// Record lifetime res, so lowering knows there is something fishy.
|
||||
self.record_lifetime_res(param.id, LifetimeRes::Error);
|
||||
self.record_lifetime_param(param.id, LifetimeRes::Error);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -2099,7 +2388,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
.span_label(param.ident.span, "`'_` is a reserved lifetime name")
|
||||
.emit();
|
||||
// Record lifetime res, so lowering knows there is something fishy.
|
||||
self.record_lifetime_res(param.id, LifetimeRes::Error);
|
||||
self.record_lifetime_param(param.id, LifetimeRes::Error);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -2114,7 +2403,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
.span_label(param.ident.span, "'static is a reserved lifetime name")
|
||||
.emit();
|
||||
// Record lifetime res, so lowering knows there is something fishy.
|
||||
self.record_lifetime_res(param.id, LifetimeRes::Error);
|
||||
self.record_lifetime_param(param.id, LifetimeRes::Error);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -2126,7 +2415,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
GenericParamKind::Const { .. } => (&mut function_value_rib, DefKind::ConstParam),
|
||||
GenericParamKind::Lifetime => {
|
||||
let res = LifetimeRes::Param { param: def_id, binder };
|
||||
self.record_lifetime_res(param.id, res);
|
||||
self.record_lifetime_param(param.id, res);
|
||||
function_lifetime_rib.bindings.insert(ident, (param.id, res));
|
||||
continue;
|
||||
}
|
||||
|
@ -2149,7 +2438,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
|
||||
self.ribs[TypeNS].pop();
|
||||
self.ribs[ValueNS].pop();
|
||||
self.lifetime_ribs.pop();
|
||||
let function_lifetime_rib = self.lifetime_ribs.pop().unwrap();
|
||||
|
||||
// Do not account for the parameters we just bound for function lifetime elision.
|
||||
if let Some(ref mut candidates) = self.lifetime_elision_candidates {
|
||||
for (_, res) in function_lifetime_rib.bindings.values() {
|
||||
candidates.remove(res);
|
||||
}
|
||||
}
|
||||
|
||||
if let LifetimeBinderKind::BareFnType
|
||||
| LifetimeBinderKind::WhereBound
|
||||
|
@ -2247,20 +2543,26 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
//
|
||||
// Type parameters can already be used and as associated consts are
|
||||
// not used as part of the type system, this is far less surprising.
|
||||
self.with_constant_rib(
|
||||
IsRepeatExpr::No,
|
||||
HasGenericParams::Yes,
|
||||
None,
|
||||
|this| this.visit_expr(expr),
|
||||
self.with_lifetime_rib(
|
||||
LifetimeRibKind::AnonymousPassThrough(item.id),
|
||||
|this| {
|
||||
this.with_constant_rib(
|
||||
IsRepeatExpr::No,
|
||||
HasGenericParams::Yes,
|
||||
None,
|
||||
|this| this.visit_expr(expr),
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
AssocItemKind::Fn(box Fn { generics, .. }) => {
|
||||
walk_assoc_item(self, generics, LifetimeBinderKind::Function, item);
|
||||
}
|
||||
AssocItemKind::TyAlias(box TyAlias { generics, .. }) => {
|
||||
walk_assoc_item(self, generics, LifetimeBinderKind::Item, item);
|
||||
}
|
||||
AssocItemKind::TyAlias(box TyAlias { generics, .. }) => self
|
||||
.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| {
|
||||
walk_assoc_item(this, generics, LifetimeBinderKind::Item, item)
|
||||
}),
|
||||
AssocItemKind::MacCall(_) => {
|
||||
panic!("unexpanded macro in resolve!")
|
||||
}
|
||||
|
@ -2331,7 +2633,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
LifetimeRibKind::Generics {
|
||||
span: generics.span,
|
||||
binder: item_id,
|
||||
kind: LifetimeBinderKind::ImplBlock
|
||||
kind: LifetimeBinderKind::ImplBlock,
|
||||
},
|
||||
|this| {
|
||||
// Dummy self type for better errors if `Self` is used in the trait path.
|
||||
|
@ -2351,7 +2653,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
|
||||
// Register the trait definitions from here.
|
||||
if let Some(trait_id) = trait_id {
|
||||
this.r.trait_impls.entry(trait_id).or_default().push(item_def_id);
|
||||
this.r
|
||||
.trait_impls
|
||||
.entry(trait_id)
|
||||
.or_default()
|
||||
.push(item_def_id);
|
||||
}
|
||||
|
||||
let item_def_id = item_def_id.to_def_id();
|
||||
|
@ -2370,21 +2676,17 @@ 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,false),
|
||||
|this| {
|
||||
this.with_current_self_type(self_type, |this| {
|
||||
this.with_self_rib_ns(ValueNS, Res::SelfCtor(item_def_id), |this| {
|
||||
debug!("resolve_implementation with_self_rib_ns(ValueNS, ...)");
|
||||
for item in impl_items {
|
||||
this.resolve_impl_item(&**item);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
this.with_current_self_type(self_type, |this| {
|
||||
this.with_self_rib_ns(ValueNS, Res::SelfCtor(item_def_id), |this| {
|
||||
debug!("resolve_implementation with_self_rib_ns(ValueNS, ...)");
|
||||
for item in impl_items {
|
||||
this.resolve_impl_item(&**item);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
)
|
||||
},
|
||||
);
|
||||
});
|
||||
|
@ -2415,9 +2717,17 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
//
|
||||
// Type parameters can already be used and as associated consts are
|
||||
// not used as part of the type system, this is far less surprising.
|
||||
self.with_constant_rib(IsRepeatExpr::No, HasGenericParams::Yes, None, |this| {
|
||||
this.visit_expr(expr)
|
||||
});
|
||||
self.with_lifetime_rib(
|
||||
LifetimeRibKind::AnonymousPassThrough(item.id),
|
||||
|this| {
|
||||
this.with_constant_rib(
|
||||
IsRepeatExpr::No,
|
||||
HasGenericParams::Yes,
|
||||
None,
|
||||
|this| this.visit_expr(expr),
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
AssocItemKind::Fn(box Fn { generics, .. }) => {
|
||||
|
@ -2459,18 +2769,20 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
kind: LifetimeBinderKind::Item,
|
||||
},
|
||||
|this| {
|
||||
// If this is a trait impl, ensure the type
|
||||
// exists in trait
|
||||
this.check_trait_item(
|
||||
item.id,
|
||||
item.ident,
|
||||
&item.kind,
|
||||
TypeNS,
|
||||
item.span,
|
||||
|i, s, c| TypeNotMemberOfTrait(i, s, c),
|
||||
);
|
||||
this.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| {
|
||||
// If this is a trait impl, ensure the type
|
||||
// exists in trait
|
||||
this.check_trait_item(
|
||||
item.id,
|
||||
item.ident,
|
||||
&item.kind,
|
||||
TypeNS,
|
||||
item.span,
|
||||
|i, s, c| TypeNotMemberOfTrait(i, s, c),
|
||||
);
|
||||
|
||||
visit::walk_assoc_item(this, item, AssocCtxt::Impl)
|
||||
visit::walk_assoc_item(this, item, AssocCtxt::Impl)
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion};
|
||||
use crate::late::lifetimes::{ElisionFailureInfo, LifetimeContext};
|
||||
use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind};
|
||||
use crate::late::{LifetimeBinderKind, LifetimeRibKind, LifetimeUseSet};
|
||||
use crate::late::{LifetimeBinderKind, LifetimeRes, LifetimeRibKind, LifetimeUseSet};
|
||||
use crate::path_names_to_string;
|
||||
use crate::{Module, ModuleKind, ModuleOrUniformRoot};
|
||||
use crate::{PathResult, PathSource, Segment};
|
||||
|
@ -9,10 +8,10 @@ use crate::{PathResult, PathSource, Segment};
|
|||
use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt};
|
||||
use rustc_ast::{
|
||||
self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
|
||||
NodeId, Path, Ty, TyKind,
|
||||
NodeId, Path, Ty, TyKind, DUMMY_NODE_ID,
|
||||
};
|
||||
use rustc_ast_pretty::pprust::path_segment_to_string;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::{
|
||||
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
|
||||
MultiSpan,
|
||||
|
@ -20,7 +19,7 @@ use rustc_errors::{
|
|||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Namespace::{self, *};
|
||||
use rustc_hir::def::{self, CtorKind, CtorOf, DefKind};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
|
||||
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;
|
||||
|
@ -29,7 +28,7 @@ use rustc_span::edition::Edition;
|
|||
use rustc_span::hygiene::MacroKind;
|
||||
use rustc_span::lev_distance::find_best_match_for_name;
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::{BytePos, Span, DUMMY_SP};
|
||||
use rustc_span::{BytePos, Span};
|
||||
|
||||
use std::iter;
|
||||
use std::ops::Deref;
|
||||
|
@ -59,45 +58,6 @@ impl AssocSuggestion {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) enum MissingLifetimeSpot<'tcx> {
|
||||
Generics(&'tcx hir::Generics<'tcx>),
|
||||
HigherRanked { span: Span, span_type: ForLifetimeSpanType },
|
||||
Static,
|
||||
}
|
||||
|
||||
pub(crate) enum ForLifetimeSpanType {
|
||||
BoundEmpty,
|
||||
BoundTail,
|
||||
TypeEmpty,
|
||||
TypeTail,
|
||||
ClosureEmpty,
|
||||
ClosureTail,
|
||||
}
|
||||
|
||||
impl ForLifetimeSpanType {
|
||||
pub(crate) fn descr(&self) -> &'static str {
|
||||
match self {
|
||||
Self::BoundEmpty | Self::BoundTail => "bound",
|
||||
Self::TypeEmpty | Self::TypeTail => "type",
|
||||
Self::ClosureEmpty | Self::ClosureTail => "closure",
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn suggestion(&self, sugg: impl std::fmt::Display) -> String {
|
||||
match self {
|
||||
Self::BoundEmpty | Self::TypeEmpty => format!("for<{}> ", sugg),
|
||||
Self::ClosureEmpty => format!("for<{}>", sugg),
|
||||
Self::BoundTail | Self::TypeTail | Self::ClosureTail => format!(", {}", sugg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Into<MissingLifetimeSpot<'tcx>> for &&'tcx hir::Generics<'tcx> {
|
||||
fn into(self) -> MissingLifetimeSpot<'tcx> {
|
||||
MissingLifetimeSpot::Generics(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_self_type(path: &[Segment], namespace: Namespace) -> bool {
|
||||
namespace == TypeNS && path.len() == 1 && path[0].ident.name == kw::SelfUpper
|
||||
}
|
||||
|
@ -122,6 +82,56 @@ fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, Str
|
|||
(variant_path_string, enum_path_string)
|
||||
}
|
||||
|
||||
/// Description of an elided lifetime.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
pub(super) struct MissingLifetime {
|
||||
/// Used to overwrite the resolution with the suggestion, to avoid cascasing errors.
|
||||
pub id: NodeId,
|
||||
/// Where to suggest adding the lifetime.
|
||||
pub span: Span,
|
||||
/// How the lifetime was introduced, to have the correct space and comma.
|
||||
pub kind: MissingLifetimeKind,
|
||||
/// Number of elided lifetimes, used for elision in path.
|
||||
pub count: usize,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
pub(super) enum MissingLifetimeKind {
|
||||
/// An explicit `'_`.
|
||||
Underscore,
|
||||
/// An elided lifetime `&' ty`.
|
||||
Ampersand,
|
||||
/// An elided lifetime in brackets with written brackets.
|
||||
Comma,
|
||||
/// An elided lifetime with elided brackets.
|
||||
Brackets,
|
||||
}
|
||||
|
||||
/// Description of the lifetimes appearing in a function parameter.
|
||||
/// This is used to provide a literal explanation to the elision failure.
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) struct ElisionFnParameter {
|
||||
/// The index of the argument in the original definition.
|
||||
pub index: usize,
|
||||
/// The name of the argument if it's a simple ident.
|
||||
pub ident: Option<Ident>,
|
||||
/// The number of lifetimes in the parameter.
|
||||
pub lifetime_count: usize,
|
||||
/// The span of the parameter.
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
/// Description of lifetimes that appear as candidates for elision.
|
||||
/// This is used to suggest introducing an explicit lifetime.
|
||||
#[derive(Debug)]
|
||||
pub(super) enum LifetimeElisionCandidate {
|
||||
/// This is not a real lifetime.
|
||||
Ignore,
|
||||
/// There is a named lifetime, we won't suggest anything.
|
||||
Named,
|
||||
Missing(MissingLifetime),
|
||||
}
|
||||
|
||||
impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
||||
fn def_span(&self, def_id: DefId) -> Option<Span> {
|
||||
match def_id.krate {
|
||||
|
@ -2003,18 +2013,35 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
|||
err.span_label(lifetime_ref.ident.span, "undeclared lifetime");
|
||||
err
|
||||
};
|
||||
let mut suggest_note = true;
|
||||
self.suggest_introducing_lifetime(
|
||||
&mut err,
|
||||
Some(lifetime_ref.ident.name.as_str()),
|
||||
|err, _, span, message, suggestion| {
|
||||
err.span_suggestion(span, message, suggestion, Applicability::MaybeIncorrect);
|
||||
true
|
||||
},
|
||||
);
|
||||
err.emit();
|
||||
}
|
||||
|
||||
fn suggest_introducing_lifetime(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
|
||||
name: Option<&str>,
|
||||
suggest: impl Fn(&mut DiagnosticBuilder<'_, ErrorGuaranteed>, bool, Span, &str, String) -> bool,
|
||||
) {
|
||||
let mut suggest_note = true;
|
||||
for rib in self.lifetime_ribs.iter().rev() {
|
||||
let mut should_continue = true;
|
||||
match rib.kind {
|
||||
LifetimeRibKind::Generics { binder: _, span, kind } => {
|
||||
if !span.can_be_used_for_suggestions() && suggest_note {
|
||||
if !span.can_be_used_for_suggestions() && suggest_note && let Some(name) = name {
|
||||
suggest_note = false; // Avoid displaying the same help multiple times.
|
||||
err.span_label(
|
||||
span,
|
||||
&format!(
|
||||
"lifetime `{}` is missing in item created through this procedural macro",
|
||||
lifetime_ref.ident,
|
||||
name,
|
||||
),
|
||||
);
|
||||
continue;
|
||||
|
@ -2030,46 +2057,42 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
|||
let sugg = format!(
|
||||
"{}<{}>{}",
|
||||
if higher_ranked { "for" } else { "" },
|
||||
lifetime_ref.ident,
|
||||
name.unwrap_or("'a"),
|
||||
if higher_ranked { " " } else { "" },
|
||||
);
|
||||
(span, sugg)
|
||||
} else {
|
||||
let span =
|
||||
self.r.session.source_map().span_through_char(span, '<').shrink_to_hi();
|
||||
let sugg = format!("{}, ", lifetime_ref.ident);
|
||||
let sugg = format!("{}, ", name.unwrap_or("'a"));
|
||||
(span, sugg)
|
||||
};
|
||||
if higher_ranked {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
&format!(
|
||||
"consider making the {} lifetime-generic with a new `{}` lifetime",
|
||||
kind.descr(),
|
||||
lifetime_ref
|
||||
),
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
let message = format!(
|
||||
"consider making the {} lifetime-generic with a new `{}` lifetime",
|
||||
kind.descr(),
|
||||
name.unwrap_or("'a"),
|
||||
);
|
||||
should_continue = suggest(err, true, span, &message, sugg);
|
||||
err.note_once(
|
||||
"for more information on higher-ranked polymorphism, visit \
|
||||
https://doc.rust-lang.org/nomicon/hrtb.html",
|
||||
);
|
||||
} else if let Some(name) = name {
|
||||
let message = format!("consider introducing lifetime `{}` here", name);
|
||||
should_continue = suggest(err, false, span, &message, sugg);
|
||||
} else {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
&format!("consider introducing lifetime `{}` here", lifetime_ref.ident),
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
let message = format!("consider introducing a named lifetime parameter");
|
||||
should_continue = suggest(err, false, span, &message, sugg);
|
||||
}
|
||||
}
|
||||
LifetimeRibKind::Item => break,
|
||||
_ => {}
|
||||
}
|
||||
if !should_continue {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
err.emit();
|
||||
}
|
||||
|
||||
pub(crate) fn emit_non_static_lt_in_const_generic_error(&self, lifetime_ref: &ast::Lifetime) {
|
||||
|
@ -2105,6 +2128,221 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
|||
.emit();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn report_missing_lifetime_specifiers(
|
||||
&mut self,
|
||||
lifetime_refs: Vec<MissingLifetime>,
|
||||
function_param_lifetimes: Option<(Vec<MissingLifetime>, Vec<ElisionFnParameter>)>,
|
||||
) -> ErrorGuaranteed {
|
||||
let num_lifetimes: usize = lifetime_refs.iter().map(|lt| lt.count).sum();
|
||||
let spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect();
|
||||
|
||||
let mut err = struct_span_err!(
|
||||
self.r.session,
|
||||
spans,
|
||||
E0106,
|
||||
"missing lifetime specifier{}",
|
||||
pluralize!(num_lifetimes)
|
||||
);
|
||||
self.add_missing_lifetime_specifiers_label(
|
||||
&mut err,
|
||||
lifetime_refs,
|
||||
function_param_lifetimes,
|
||||
);
|
||||
err.emit()
|
||||
}
|
||||
|
||||
pub(crate) fn add_missing_lifetime_specifiers_label(
|
||||
&mut self,
|
||||
err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
|
||||
lifetime_refs: Vec<MissingLifetime>,
|
||||
function_param_lifetimes: Option<(Vec<MissingLifetime>, Vec<ElisionFnParameter>)>,
|
||||
) {
|
||||
for < in &lifetime_refs {
|
||||
err.span_label(
|
||||
lt.span,
|
||||
format!(
|
||||
"expected {} lifetime parameter{}",
|
||||
if lt.count == 1 { "named".to_string() } else { lt.count.to_string() },
|
||||
pluralize!(lt.count),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
let mut in_scope_lifetimes: Vec<_> = self
|
||||
.lifetime_ribs
|
||||
.iter()
|
||||
.rev()
|
||||
.take_while(|rib| !matches!(rib.kind, LifetimeRibKind::Item))
|
||||
.flat_map(|rib| rib.bindings.iter())
|
||||
.map(|(&ident, &res)| (ident, res))
|
||||
.filter(|(ident, _)| ident.name != kw::UnderscoreLifetime)
|
||||
.collect();
|
||||
debug!(?in_scope_lifetimes);
|
||||
|
||||
debug!(?function_param_lifetimes);
|
||||
if let Some((param_lifetimes, params)) = &function_param_lifetimes {
|
||||
let elided_len = param_lifetimes.len();
|
||||
let num_params = params.len();
|
||||
|
||||
let mut m = String::new();
|
||||
|
||||
for (i, info) in params.iter().enumerate() {
|
||||
let ElisionFnParameter { ident, index, lifetime_count, span } = *info;
|
||||
debug_assert_ne!(lifetime_count, 0);
|
||||
|
||||
err.span_label(span, "");
|
||||
|
||||
if i != 0 {
|
||||
if i + 1 < num_params {
|
||||
m.push_str(", ");
|
||||
} else if num_params == 2 {
|
||||
m.push_str(" or ");
|
||||
} else {
|
||||
m.push_str(", or ");
|
||||
}
|
||||
}
|
||||
|
||||
let help_name = if let Some(ident) = ident {
|
||||
format!("`{}`", ident)
|
||||
} else {
|
||||
format!("argument {}", index + 1)
|
||||
};
|
||||
|
||||
if lifetime_count == 1 {
|
||||
m.push_str(&help_name[..])
|
||||
} else {
|
||||
m.push_str(&format!("one of {}'s {} lifetimes", help_name, lifetime_count)[..])
|
||||
}
|
||||
}
|
||||
|
||||
if num_params == 0 {
|
||||
err.help(
|
||||
"this function's return type contains a borrowed value, \
|
||||
but there is no value for it to be borrowed from",
|
||||
);
|
||||
if in_scope_lifetimes.is_empty() {
|
||||
in_scope_lifetimes = vec![(
|
||||
Ident::with_dummy_span(kw::StaticLifetime),
|
||||
(DUMMY_NODE_ID, LifetimeRes::Static),
|
||||
)];
|
||||
}
|
||||
} else if elided_len == 0 {
|
||||
err.help(
|
||||
"this function's return type contains a borrowed value with \
|
||||
an elided lifetime, but the lifetime cannot be derived from \
|
||||
the arguments",
|
||||
);
|
||||
if in_scope_lifetimes.is_empty() {
|
||||
in_scope_lifetimes = vec![(
|
||||
Ident::with_dummy_span(kw::StaticLifetime),
|
||||
(DUMMY_NODE_ID, LifetimeRes::Static),
|
||||
)];
|
||||
}
|
||||
} else if num_params == 1 {
|
||||
err.help(&format!(
|
||||
"this function's return type contains a borrowed value, \
|
||||
but the signature does not say which {} it is borrowed from",
|
||||
m
|
||||
));
|
||||
} else {
|
||||
err.help(&format!(
|
||||
"this function's return type contains a borrowed value, \
|
||||
but the signature does not say whether it is borrowed from {}",
|
||||
m
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let existing_name = match &in_scope_lifetimes[..] {
|
||||
[] => Symbol::intern("'a"),
|
||||
[(existing, _)] => existing.name,
|
||||
_ => Symbol::intern("'lifetime"),
|
||||
};
|
||||
|
||||
let mut spans_suggs: Vec<_> = Vec::new();
|
||||
let build_sugg = |lt: MissingLifetime| match lt.kind {
|
||||
MissingLifetimeKind::Underscore => {
|
||||
debug_assert_eq!(lt.count, 1);
|
||||
(lt.span, existing_name.to_string())
|
||||
}
|
||||
MissingLifetimeKind::Ampersand => {
|
||||
debug_assert_eq!(lt.count, 1);
|
||||
(lt.span.shrink_to_hi(), format!("{} ", existing_name))
|
||||
}
|
||||
MissingLifetimeKind::Comma => {
|
||||
let sugg: String = std::iter::repeat([existing_name.as_str(), ", "])
|
||||
.take(lt.count)
|
||||
.flatten()
|
||||
.collect();
|
||||
(lt.span.shrink_to_hi(), sugg)
|
||||
}
|
||||
MissingLifetimeKind::Brackets => {
|
||||
let sugg: String = std::iter::once("<")
|
||||
.chain(
|
||||
std::iter::repeat(existing_name.as_str()).take(lt.count).intersperse(", "),
|
||||
)
|
||||
.chain([">"])
|
||||
.collect();
|
||||
(lt.span.shrink_to_hi(), sugg)
|
||||
}
|
||||
};
|
||||
for < in &lifetime_refs {
|
||||
spans_suggs.push(build_sugg(lt));
|
||||
}
|
||||
debug!(?spans_suggs);
|
||||
match in_scope_lifetimes.len() {
|
||||
0 => {
|
||||
if let Some((param_lifetimes, _)) = function_param_lifetimes {
|
||||
for lt in param_lifetimes {
|
||||
spans_suggs.push(build_sugg(lt))
|
||||
}
|
||||
}
|
||||
self.suggest_introducing_lifetime(
|
||||
err,
|
||||
None,
|
||||
|err, higher_ranked, span, message, intro_sugg| {
|
||||
err.multipart_suggestion_verbose(
|
||||
message,
|
||||
std::iter::once((span, intro_sugg))
|
||||
.chain(spans_suggs.clone())
|
||||
.collect(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
higher_ranked
|
||||
},
|
||||
);
|
||||
}
|
||||
1 => {
|
||||
err.multipart_suggestion_verbose(
|
||||
&format!("consider using the `{}` lifetime", existing_name),
|
||||
spans_suggs,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
|
||||
// Record as using the suggested resolution.
|
||||
let (_, (_, res)) = in_scope_lifetimes[0];
|
||||
for < in &lifetime_refs {
|
||||
self.r.lifetimes_res_map.insert(lt.id, res);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let lifetime_spans: Vec<_> =
|
||||
in_scope_lifetimes.iter().map(|(ident, _)| ident.span).collect();
|
||||
err.span_note(lifetime_spans, "these named lifetimes are available to use");
|
||||
|
||||
if spans_suggs.len() > 0 {
|
||||
// This happens when we have `Foo<T>` where we point at the space before `T`,
|
||||
// but this can be confusing so we give a suggestion with placeholders.
|
||||
err.multipart_suggestion_verbose(
|
||||
"consider using one of the available lifetimes here",
|
||||
spans_suggs,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Report lifetime/lifetime shadowing as an error.
|
||||
|
@ -2134,534 +2372,3 @@ pub fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident) {
|
|||
err.span_label(shadower, format!("label `{}` already in scope", name));
|
||||
err.emit();
|
||||
}
|
||||
|
||||
impl<'tcx> LifetimeContext<'_, 'tcx> {
|
||||
pub(crate) fn report_missing_lifetime_specifiers(
|
||||
&self,
|
||||
spans: Vec<Span>,
|
||||
count: usize,
|
||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||
struct_span_err!(
|
||||
self.tcx.sess,
|
||||
spans,
|
||||
E0106,
|
||||
"missing lifetime specifier{}",
|
||||
pluralize!(count)
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns whether to add `'static` lifetime to the suggested lifetime list.
|
||||
pub(crate) fn report_elision_failure(
|
||||
&self,
|
||||
diag: &mut Diagnostic,
|
||||
params: &[ElisionFailureInfo],
|
||||
) -> bool {
|
||||
let mut m = String::new();
|
||||
let len = params.len();
|
||||
|
||||
let elided_params: Vec<_> =
|
||||
params.iter().cloned().filter(|info| info.lifetime_count > 0).collect();
|
||||
|
||||
let elided_len = elided_params.len();
|
||||
|
||||
for (i, info) in elided_params.into_iter().enumerate() {
|
||||
let ElisionFailureInfo { parent, index, lifetime_count: n, have_bound_regions, span } =
|
||||
info;
|
||||
|
||||
diag.span_label(span, "");
|
||||
let help_name = if let Some(ident) =
|
||||
parent.and_then(|body| self.tcx.hir().body(body).params[index].pat.simple_ident())
|
||||
{
|
||||
format!("`{}`", ident)
|
||||
} else {
|
||||
format!("argument {}", index + 1)
|
||||
};
|
||||
|
||||
m.push_str(
|
||||
&(if n == 1 {
|
||||
help_name
|
||||
} else {
|
||||
format!(
|
||||
"one of {}'s {} {}lifetimes",
|
||||
help_name,
|
||||
n,
|
||||
if have_bound_regions { "free " } else { "" }
|
||||
)
|
||||
})[..],
|
||||
);
|
||||
|
||||
if elided_len == 2 && i == 0 {
|
||||
m.push_str(" or ");
|
||||
} else if i + 2 == elided_len {
|
||||
m.push_str(", or ");
|
||||
} else if i != elided_len - 1 {
|
||||
m.push_str(", ");
|
||||
}
|
||||
}
|
||||
|
||||
if len == 0 {
|
||||
diag.help(
|
||||
"this function's return type contains a borrowed value, \
|
||||
but there is no value for it to be borrowed from",
|
||||
);
|
||||
true
|
||||
} else if elided_len == 0 {
|
||||
diag.help(
|
||||
"this function's return type contains a borrowed value with \
|
||||
an elided lifetime, but the lifetime cannot be derived from \
|
||||
the arguments",
|
||||
);
|
||||
true
|
||||
} else if elided_len == 1 {
|
||||
diag.help(&format!(
|
||||
"this function's return type contains a borrowed value, \
|
||||
but the signature does not say which {} it is borrowed from",
|
||||
m
|
||||
));
|
||||
false
|
||||
} else {
|
||||
diag.help(&format!(
|
||||
"this function's return type contains a borrowed value, \
|
||||
but the signature does not say whether it is borrowed from {}",
|
||||
m
|
||||
));
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_trait_ref_fn_scope(
|
||||
&mut self,
|
||||
trait_ref: &'tcx hir::PolyTraitRef<'tcx>,
|
||||
) -> bool {
|
||||
if let def::Res::Def(_, did) = trait_ref.trait_ref.path.res {
|
||||
if [
|
||||
self.tcx.lang_items().fn_once_trait(),
|
||||
self.tcx.lang_items().fn_trait(),
|
||||
self.tcx.lang_items().fn_mut_trait(),
|
||||
]
|
||||
.contains(&Some(did))
|
||||
{
|
||||
let (span, span_type) = if let Some(bound) =
|
||||
trait_ref.bound_generic_params.iter().rfind(|param| {
|
||||
matches!(
|
||||
param.kind,
|
||||
hir::GenericParamKind::Lifetime {
|
||||
kind: hir::LifetimeParamKind::Explicit
|
||||
}
|
||||
)
|
||||
}) {
|
||||
(bound.span.shrink_to_hi(), ForLifetimeSpanType::BoundTail)
|
||||
} else {
|
||||
(trait_ref.span.shrink_to_lo(), ForLifetimeSpanType::BoundEmpty)
|
||||
};
|
||||
self.missing_named_lifetime_spots
|
||||
.push(MissingLifetimeSpot::HigherRanked { span, span_type });
|
||||
return true;
|
||||
}
|
||||
};
|
||||
false
|
||||
}
|
||||
|
||||
pub(crate) fn add_missing_lifetime_specifiers_label(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
mut spans_with_counts: Vec<(Span, usize)>,
|
||||
in_scope_lifetimes: FxIndexSet<LocalDefId>,
|
||||
params: Option<&[ElisionFailureInfo]>,
|
||||
) {
|
||||
let (mut lifetime_names, lifetime_spans): (FxHashSet<_>, Vec<_>) = in_scope_lifetimes
|
||||
.iter()
|
||||
.filter_map(|def_id| {
|
||||
let name = self.tcx.item_name(def_id.to_def_id());
|
||||
let span = self.tcx.def_ident_span(def_id.to_def_id())?;
|
||||
Some((name, span))
|
||||
})
|
||||
.filter(|&(n, _)| n != kw::UnderscoreLifetime)
|
||||
.unzip();
|
||||
|
||||
if let Some(params) = params {
|
||||
// If there's no lifetime available, suggest `'static`.
|
||||
if self.report_elision_failure(err, params) && lifetime_names.is_empty() {
|
||||
lifetime_names.insert(kw::StaticLifetime);
|
||||
}
|
||||
}
|
||||
let params = params.unwrap_or(&[]);
|
||||
|
||||
let snippets: Vec<Option<String>> = spans_with_counts
|
||||
.iter()
|
||||
.map(|(span, _)| self.tcx.sess.source_map().span_to_snippet(*span).ok())
|
||||
.collect();
|
||||
|
||||
// Empty generics are marked with a span of "<", but since from now on
|
||||
// that information is in the snippets it can be removed from the spans.
|
||||
for ((span, _), snippet) in spans_with_counts.iter_mut().zip(&snippets) {
|
||||
if snippet.as_deref() == Some("<") {
|
||||
*span = span.shrink_to_hi();
|
||||
}
|
||||
}
|
||||
|
||||
for &(span, count) in &spans_with_counts {
|
||||
err.span_label(
|
||||
span,
|
||||
format!(
|
||||
"expected {} lifetime parameter{}",
|
||||
if count == 1 { "named".to_string() } else { count.to_string() },
|
||||
pluralize!(count),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
let suggest_existing =
|
||||
|err: &mut Diagnostic,
|
||||
name: Symbol,
|
||||
formatters: Vec<Option<Box<dyn Fn(Symbol) -> String>>>| {
|
||||
if let Some(MissingLifetimeSpot::HigherRanked { span: for_span, span_type }) =
|
||||
self.missing_named_lifetime_spots.iter().rev().next()
|
||||
{
|
||||
// When we have `struct S<'a>(&'a dyn Fn(&X) -> &X);` we want to not only suggest
|
||||
// using `'a`, but also introduce the concept of HRLTs by suggesting
|
||||
// `struct S<'a>(&'a dyn for<'b> Fn(&X) -> &'b X);`. (#72404)
|
||||
let mut introduce_suggestion = vec![];
|
||||
|
||||
let a_to_z_repeat_n = |n| {
|
||||
(b'a'..=b'z').map(move |c| {
|
||||
let mut s = '\''.to_string();
|
||||
s.extend(std::iter::repeat(char::from(c)).take(n));
|
||||
s
|
||||
})
|
||||
};
|
||||
|
||||
// If all single char lifetime names are present, we wrap around and double the chars.
|
||||
let lt_name = (1..)
|
||||
.flat_map(a_to_z_repeat_n)
|
||||
.map(|lt| Symbol::intern(<))
|
||||
.find(|lt| !lifetime_names.contains(lt))
|
||||
.unwrap();
|
||||
let msg = format!(
|
||||
"consider making the {} lifetime-generic with a new `{}` lifetime",
|
||||
span_type.descr(),
|
||||
lt_name,
|
||||
);
|
||||
err.note(
|
||||
"for more information on higher-ranked polymorphism, visit \
|
||||
https://doc.rust-lang.org/nomicon/hrtb.html",
|
||||
);
|
||||
let for_sugg = span_type.suggestion(<_name);
|
||||
for param in params {
|
||||
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span)
|
||||
{
|
||||
if snippet.starts_with('&') && !snippet.starts_with("&'") {
|
||||
introduce_suggestion
|
||||
.push((param.span, format!("&{} {}", lt_name, &snippet[1..])));
|
||||
} else if let Some(stripped) = snippet.strip_prefix("&'_ ") {
|
||||
introduce_suggestion
|
||||
.push((param.span, format!("&{} {}", lt_name, stripped)));
|
||||
}
|
||||
}
|
||||
}
|
||||
introduce_suggestion.push((*for_span, for_sugg));
|
||||
for ((span, _), formatter) in spans_with_counts.iter().zip(formatters.iter()) {
|
||||
if let Some(formatter) = formatter {
|
||||
introduce_suggestion.push((*span, formatter(lt_name)));
|
||||
}
|
||||
}
|
||||
err.multipart_suggestion_verbose(
|
||||
&msg,
|
||||
introduce_suggestion,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
|
||||
let spans_suggs: Vec<_> = formatters
|
||||
.into_iter()
|
||||
.zip(spans_with_counts.iter())
|
||||
.filter_map(|(formatter, (span, _))| {
|
||||
if let Some(formatter) = formatter {
|
||||
Some((*span, formatter(name)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
if spans_suggs.is_empty() {
|
||||
// If all the spans come from macros, we cannot extract snippets and then
|
||||
// `formatters` only contains None and `spans_suggs` is empty.
|
||||
return;
|
||||
}
|
||||
err.multipart_suggestion_verbose(
|
||||
&format!(
|
||||
"consider using the `{}` lifetime",
|
||||
lifetime_names.iter().next().unwrap()
|
||||
),
|
||||
spans_suggs,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
};
|
||||
let suggest_new = |err: &mut Diagnostic, suggs: Vec<Option<String>>| {
|
||||
for missing in self.missing_named_lifetime_spots.iter().rev() {
|
||||
let mut introduce_suggestion = vec![];
|
||||
let msg;
|
||||
let should_break;
|
||||
introduce_suggestion.push(match missing {
|
||||
MissingLifetimeSpot::Generics(generics) => {
|
||||
if generics.span == DUMMY_SP {
|
||||
// Account for malformed generics in the HIR. This shouldn't happen,
|
||||
// but if we make a mistake elsewhere, mainly by keeping something in
|
||||
// `missing_named_lifetime_spots` that we shouldn't, like associated
|
||||
// `const`s or making a mistake in the AST lowering we would provide
|
||||
// nonsensical suggestions. Guard against that by skipping these.
|
||||
// (#74264)
|
||||
continue;
|
||||
}
|
||||
msg = "consider introducing a named lifetime parameter".to_string();
|
||||
should_break = true;
|
||||
if let Some(param) = generics.params.iter().find(|p| {
|
||||
!matches!(
|
||||
p.kind,
|
||||
hir::GenericParamKind::Type { synthetic: true, .. }
|
||||
| hir::GenericParamKind::Lifetime {
|
||||
kind: hir::LifetimeParamKind::Elided
|
||||
}
|
||||
)
|
||||
}) {
|
||||
(param.span.shrink_to_lo(), "'a, ".to_string())
|
||||
} else {
|
||||
(generics.span, "<'a>".to_string())
|
||||
}
|
||||
}
|
||||
MissingLifetimeSpot::HigherRanked { span, span_type } => {
|
||||
msg = format!(
|
||||
"consider making the {} lifetime-generic with a new `'a` lifetime",
|
||||
span_type.descr(),
|
||||
);
|
||||
should_break = false;
|
||||
err.note(
|
||||
"for more information on higher-ranked polymorphism, visit \
|
||||
https://doc.rust-lang.org/nomicon/hrtb.html",
|
||||
);
|
||||
(*span, span_type.suggestion("'a"))
|
||||
}
|
||||
MissingLifetimeSpot::Static => {
|
||||
let mut spans_suggs = Vec::new();
|
||||
for ((span, count), snippet) in
|
||||
spans_with_counts.iter().copied().zip(snippets.iter())
|
||||
{
|
||||
let (span, sugg) = match snippet.as_deref() {
|
||||
Some("&") => (span.shrink_to_hi(), "'static ".to_owned()),
|
||||
Some("'_") => (span, "'static".to_owned()),
|
||||
Some(snippet) if !snippet.ends_with('>') => {
|
||||
if snippet == "" {
|
||||
(
|
||||
span,
|
||||
std::iter::repeat("'static")
|
||||
.take(count)
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
)
|
||||
} else if snippet == "<" || snippet == "(" {
|
||||
(
|
||||
span.shrink_to_hi(),
|
||||
std::iter::repeat("'static")
|
||||
.take(count)
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
span.shrink_to_hi(),
|
||||
format!(
|
||||
"<{}>",
|
||||
std::iter::repeat("'static")
|
||||
.take(count)
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
spans_suggs.push((span, sugg.to_string()));
|
||||
}
|
||||
err.multipart_suggestion_verbose(
|
||||
"consider using the `'static` lifetime",
|
||||
spans_suggs,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
});
|
||||
|
||||
struct Lifetime(Span, String);
|
||||
impl Lifetime {
|
||||
fn is_unnamed(&self) -> bool {
|
||||
self.1.starts_with('&') && !self.1.starts_with("&'")
|
||||
}
|
||||
fn is_underscore(&self) -> bool {
|
||||
self.1.starts_with("&'_ ")
|
||||
}
|
||||
fn is_named(&self) -> bool {
|
||||
self.1.starts_with("&'")
|
||||
}
|
||||
fn suggestion(&self, sugg: String) -> Option<(Span, String)> {
|
||||
Some(
|
||||
match (
|
||||
self.is_unnamed(),
|
||||
self.is_underscore(),
|
||||
self.is_named(),
|
||||
sugg.starts_with('&'),
|
||||
) {
|
||||
(true, _, _, false) => (self.span_unnamed_borrow(), sugg),
|
||||
(true, _, _, true) => {
|
||||
(self.span_unnamed_borrow(), sugg[1..].to_string())
|
||||
}
|
||||
(_, true, _, false) => {
|
||||
(self.span_underscore_borrow(), sugg.trim().to_string())
|
||||
}
|
||||
(_, true, _, true) => {
|
||||
(self.span_underscore_borrow(), sugg[1..].trim().to_string())
|
||||
}
|
||||
(_, _, true, false) => {
|
||||
(self.span_named_borrow(), sugg.trim().to_string())
|
||||
}
|
||||
(_, _, true, true) => {
|
||||
(self.span_named_borrow(), sugg[1..].trim().to_string())
|
||||
}
|
||||
_ => return None,
|
||||
},
|
||||
)
|
||||
}
|
||||
fn span_unnamed_borrow(&self) -> Span {
|
||||
let lo = self.0.lo() + BytePos(1);
|
||||
self.0.with_lo(lo).with_hi(lo)
|
||||
}
|
||||
fn span_named_borrow(&self) -> Span {
|
||||
let lo = self.0.lo() + BytePos(1);
|
||||
self.0.with_lo(lo)
|
||||
}
|
||||
fn span_underscore_borrow(&self) -> Span {
|
||||
let lo = self.0.lo() + BytePos(1);
|
||||
let hi = lo + BytePos(2);
|
||||
self.0.with_lo(lo).with_hi(hi)
|
||||
}
|
||||
}
|
||||
|
||||
for param in params {
|
||||
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) {
|
||||
if let Some((span, sugg)) =
|
||||
Lifetime(param.span, snippet).suggestion("'a ".to_string())
|
||||
{
|
||||
introduce_suggestion.push((span, sugg));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (span, sugg) in spans_with_counts.iter().copied().zip(suggs.iter()).filter_map(
|
||||
|((span, _), sugg)| match &sugg {
|
||||
Some(sugg) => Some((span, sugg.to_string())),
|
||||
_ => None,
|
||||
},
|
||||
) {
|
||||
let (span, sugg) = self
|
||||
.tcx
|
||||
.sess
|
||||
.source_map()
|
||||
.span_to_snippet(span)
|
||||
.ok()
|
||||
.and_then(|snippet| Lifetime(span, snippet).suggestion(sugg.clone()))
|
||||
.unwrap_or((span, sugg));
|
||||
introduce_suggestion.push((span, sugg.to_string()));
|
||||
}
|
||||
err.multipart_suggestion_verbose(
|
||||
&msg,
|
||||
introduce_suggestion,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
if should_break {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let lifetime_names: Vec<_> = lifetime_names.iter().collect();
|
||||
match &lifetime_names[..] {
|
||||
[name] => {
|
||||
let mut suggs: Vec<Option<Box<dyn Fn(Symbol) -> String>>> = Vec::new();
|
||||
for (snippet, (_, count)) in snippets.iter().zip(spans_with_counts.iter().copied())
|
||||
{
|
||||
suggs.push(match snippet.as_deref() {
|
||||
Some("&") => Some(Box::new(|name| format!("&{} ", name))),
|
||||
Some("'_") => Some(Box::new(|n| n.to_string())),
|
||||
Some("") => Some(Box::new(move |n| format!("{}, ", n).repeat(count))),
|
||||
Some("<") => Some(Box::new(move |n| {
|
||||
std::iter::repeat(n)
|
||||
.take(count)
|
||||
.map(|n| n.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
})),
|
||||
Some(snippet) if !snippet.ends_with('>') => Some(Box::new(move |name| {
|
||||
format!(
|
||||
"{}<{}>",
|
||||
snippet,
|
||||
std::iter::repeat(name.to_string())
|
||||
.take(count)
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
)
|
||||
})),
|
||||
_ => None,
|
||||
});
|
||||
}
|
||||
suggest_existing(err, **name, suggs);
|
||||
}
|
||||
[] => {
|
||||
let mut suggs = Vec::new();
|
||||
for (snippet, (_, count)) in
|
||||
snippets.iter().cloned().zip(spans_with_counts.iter().copied())
|
||||
{
|
||||
suggs.push(match snippet.as_deref() {
|
||||
Some("&") => Some("&'a ".to_string()),
|
||||
Some("'_") => Some("'a".to_string()),
|
||||
Some("") => {
|
||||
Some(std::iter::repeat("'a, ").take(count).collect::<Vec<_>>().join(""))
|
||||
}
|
||||
Some("<") => {
|
||||
Some(std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", "))
|
||||
}
|
||||
Some(snippet) => Some(format!(
|
||||
"{}<{}>",
|
||||
snippet,
|
||||
std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", "),
|
||||
)),
|
||||
None => None,
|
||||
});
|
||||
}
|
||||
suggest_new(err, suggs);
|
||||
}
|
||||
lts if lts.len() > 1 => {
|
||||
err.span_note(lifetime_spans, "these named lifetimes are available to use");
|
||||
|
||||
let mut spans_suggs: Vec<_> = Vec::new();
|
||||
for ((span, _), snippet) in spans_with_counts.iter().copied().zip(snippets.iter()) {
|
||||
match snippet.as_deref() {
|
||||
Some("") => spans_suggs.push((span, "'lifetime, ".to_string())),
|
||||
Some("&") => spans_suggs
|
||||
.push((span.with_lo(span.lo() + BytePos(1)), "'lifetime ".to_string())),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if spans_suggs.len() > 0 {
|
||||
// This happens when we have `Foo<T>` where we point at the space before `T`,
|
||||
// but this can be confusing so we give a suggestion with placeholders.
|
||||
err.multipart_suggestion_verbose(
|
||||
"consider using one of the available lifetimes here",
|
||||
spans_suggs,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
//! used between functions, and they operate in a purely top-down
|
||||
//! way. Therefore, we break lifetime name resolution into a separate pass.
|
||||
|
||||
use crate::late::diagnostics::{ForLifetimeSpanType, MissingLifetimeSpot};
|
||||
use rustc_ast::walk_list;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
|
||||
use rustc_errors::struct_span_err;
|
||||
|
@ -14,13 +13,12 @@ use rustc_hir as hir;
|
|||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{DefIdMap, LocalDefId};
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{GenericArg, GenericParam, LifetimeName, Node};
|
||||
use rustc_hir::{GenericParamKind, HirIdMap, LifetimeParamKind};
|
||||
use rustc_hir::{GenericArg, GenericParam, GenericParamKind, HirIdMap, LifetimeName, Node};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::middle::resolve_lifetime::*;
|
||||
use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::symbol::{kw, sym, Ident};
|
||||
use rustc_span::Span;
|
||||
|
@ -152,10 +150,6 @@ pub(crate) struct LifetimeContext<'a, 'tcx> {
|
|||
|
||||
/// Cache for cross-crate per-definition object lifetime defaults.
|
||||
xcrate_object_lifetime_defaults: DefIdMap<Vec<ObjectLifetimeDefault>>,
|
||||
|
||||
/// When encountering an undefined named lifetime, we will suggest introducing it in these
|
||||
/// places.
|
||||
pub(crate) missing_named_lifetime_spots: Vec<MissingLifetimeSpot<'tcx>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -323,23 +317,12 @@ enum Elide {
|
|||
/// Always use this one lifetime.
|
||||
Exact(Region),
|
||||
/// Less or more than one lifetime were found, error on unspecified.
|
||||
Error(Vec<ElisionFailureInfo>),
|
||||
Error,
|
||||
/// Forbid lifetime elision inside of a larger scope where it would be
|
||||
/// permitted. For example, in let position impl trait.
|
||||
Forbid,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ElisionFailureInfo {
|
||||
/// Where we can find the argument pattern.
|
||||
pub(crate) parent: Option<hir::BodyId>,
|
||||
/// The index of the argument in the original definition.
|
||||
pub(crate) index: usize,
|
||||
pub(crate) lifetime_count: usize,
|
||||
pub(crate) have_bound_regions: bool,
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
type ScopeRef<'a> = &'a Scope<'a>;
|
||||
|
||||
const ROOT_SCOPE: ScopeRef<'static> = &Scope::Root;
|
||||
|
@ -421,7 +404,6 @@ fn do_resolve(
|
|||
scope: ROOT_SCOPE,
|
||||
trait_definition_only,
|
||||
xcrate_object_lifetime_defaults: Default::default(),
|
||||
missing_named_lifetime_spots: vec![],
|
||||
};
|
||||
visitor.visit_item(item);
|
||||
|
||||
|
@ -644,40 +626,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
where_bound_origin: None,
|
||||
};
|
||||
|
||||
if let &hir::ClosureBinder::For { span, .. } = binder {
|
||||
let last_lt = bound_generic_params
|
||||
.iter()
|
||||
.filter(|p| {
|
||||
matches!(
|
||||
p,
|
||||
GenericParam {
|
||||
kind: GenericParamKind::Lifetime {
|
||||
kind: LifetimeParamKind::Explicit
|
||||
},
|
||||
..
|
||||
}
|
||||
)
|
||||
})
|
||||
.last();
|
||||
let (span, span_type) = match last_lt {
|
||||
Some(GenericParam { span: last_sp, .. }) => {
|
||||
(last_sp.shrink_to_hi(), ForLifetimeSpanType::ClosureTail)
|
||||
}
|
||||
None => (span, ForLifetimeSpanType::ClosureEmpty),
|
||||
};
|
||||
self.missing_named_lifetime_spots
|
||||
.push(MissingLifetimeSpot::HigherRanked { span, span_type });
|
||||
}
|
||||
|
||||
self.with(scope, |this| {
|
||||
// a closure has no bounds, so everything
|
||||
// contained within is scoped within its binder.
|
||||
intravisit::walk_expr(this, e)
|
||||
});
|
||||
|
||||
if let hir::ClosureBinder::For { .. } = binder {
|
||||
self.missing_named_lifetime_spots.pop();
|
||||
}
|
||||
} else {
|
||||
intravisit::walk_expr(self, e)
|
||||
}
|
||||
|
@ -694,11 +647,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
}
|
||||
match item.kind {
|
||||
hir::ItemKind::Fn(_, ref generics, _) => {
|
||||
self.missing_named_lifetime_spots.push(generics.into());
|
||||
self.visit_early_late(None, item.hir_id(), generics, |this| {
|
||||
intravisit::walk_item(this, item);
|
||||
});
|
||||
self.missing_named_lifetime_spots.pop();
|
||||
}
|
||||
|
||||
hir::ItemKind::ExternCrate(_)
|
||||
|
@ -761,8 +712,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
| hir::ItemKind::Trait(_, _, ref generics, ..)
|
||||
| hir::ItemKind::TraitAlias(ref generics, ..)
|
||||
| hir::ItemKind::Impl(hir::Impl { ref generics, .. }) => {
|
||||
self.missing_named_lifetime_spots.push(generics.into());
|
||||
|
||||
// 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
|
||||
|
@ -800,7 +749,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
intravisit::walk_item(this, item);
|
||||
});
|
||||
});
|
||||
self.missing_named_lifetime_spots.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -826,20 +774,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
match ty.kind {
|
||||
hir::TyKind::BareFn(ref c) => {
|
||||
let next_early_index = self.next_early_index();
|
||||
let lifetime_span: Option<Span> =
|
||||
c.generic_params.iter().rev().find_map(|param| match param.kind {
|
||||
GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit } => {
|
||||
Some(param.span)
|
||||
}
|
||||
_ => None,
|
||||
});
|
||||
let (span, span_type) = if let Some(span) = lifetime_span {
|
||||
(span.shrink_to_hi(), ForLifetimeSpanType::TypeTail)
|
||||
} else {
|
||||
(ty.span.shrink_to_lo(), ForLifetimeSpanType::TypeEmpty)
|
||||
};
|
||||
self.missing_named_lifetime_spots
|
||||
.push(MissingLifetimeSpot::HigherRanked { span, span_type });
|
||||
let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) = c
|
||||
.generic_params
|
||||
.iter()
|
||||
|
@ -867,7 +801,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
// contained within is scoped within its binder.
|
||||
intravisit::walk_ty(this, ty);
|
||||
});
|
||||
self.missing_named_lifetime_spots.pop();
|
||||
}
|
||||
hir::TyKind::TraitObject(bounds, ref lifetime, _) => {
|
||||
debug!(?bounds, ?lifetime, "TraitObject");
|
||||
|
@ -878,11 +811,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
}
|
||||
});
|
||||
match lifetime.name {
|
||||
LifetimeName::Implicit => {
|
||||
// For types like `dyn Foo`, we should
|
||||
// generate a special form of elided.
|
||||
span_bug!(ty.span, "object-lifetime-default expected, not implicit",);
|
||||
}
|
||||
LifetimeName::ImplicitObjectLifetimeDefault => {
|
||||
// If the user does not write *anything*, we
|
||||
// use the object lifetime defaulting
|
||||
|
@ -890,7 +818,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
// `Box<dyn Debug + 'static>`.
|
||||
self.resolve_object_lifetime_default(lifetime)
|
||||
}
|
||||
LifetimeName::Underscore => {
|
||||
LifetimeName::Implicit | LifetimeName::Underscore => {
|
||||
// If the user writes `'_`, we use the *ordinary* elision
|
||||
// rules. So the `'_` in e.g., `Box<dyn Debug + '_>` will be
|
||||
// resolved the same as the `'_` in `&'_ Foo`.
|
||||
|
@ -1010,10 +938,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
let mut elision = None;
|
||||
let mut lifetimes = FxIndexMap::default();
|
||||
let mut non_lifetime_count = 0;
|
||||
debug!(?generics.params);
|
||||
for param in generics.params {
|
||||
match param.kind {
|
||||
GenericParamKind::Lifetime { .. } => {
|
||||
let (def_id, reg) = Region::early(self.tcx.hir(), &mut index, ¶m);
|
||||
lifetimes.insert(def_id, reg);
|
||||
if let hir::ParamName::Plain(Ident {
|
||||
name: kw::UnderscoreLifetime,
|
||||
..
|
||||
|
@ -1022,8 +952,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
// Pick the elided lifetime "definition" if one exists
|
||||
// and use it to make an elision scope.
|
||||
elision = Some(reg);
|
||||
} else {
|
||||
lifetimes.insert(def_id, reg);
|
||||
}
|
||||
}
|
||||
GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
|
||||
|
@ -1088,7 +1016,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
use self::hir::TraitItemKind::*;
|
||||
match trait_item.kind {
|
||||
Fn(_, _) => {
|
||||
self.missing_named_lifetime_spots.push((&trait_item.generics).into());
|
||||
let tcx = self.tcx;
|
||||
self.visit_early_late(
|
||||
Some(tcx.hir().get_parent_item(trait_item.hir_id())),
|
||||
|
@ -1096,10 +1023,8 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
&trait_item.generics,
|
||||
|this| intravisit::walk_trait_item(this, trait_item),
|
||||
);
|
||||
self.missing_named_lifetime_spots.pop();
|
||||
}
|
||||
Type(bounds, ref ty) => {
|
||||
self.missing_named_lifetime_spots.push((&trait_item.generics).into());
|
||||
let generics = &trait_item.generics;
|
||||
let mut index = self.next_early_index();
|
||||
debug!("visit_ty: index = {}", index);
|
||||
|
@ -1140,14 +1065,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
}
|
||||
})
|
||||
});
|
||||
self.missing_named_lifetime_spots.pop();
|
||||
}
|
||||
Const(_, _) => {
|
||||
// Only methods and types support generics.
|
||||
assert!(trait_item.generics.params.is_empty());
|
||||
self.missing_named_lifetime_spots.push(MissingLifetimeSpot::Static);
|
||||
intravisit::walk_trait_item(self, trait_item);
|
||||
self.missing_named_lifetime_spots.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1156,7 +1078,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
use self::hir::ImplItemKind::*;
|
||||
match impl_item.kind {
|
||||
Fn(..) => {
|
||||
self.missing_named_lifetime_spots.push((&impl_item.generics).into());
|
||||
let tcx = self.tcx;
|
||||
self.visit_early_late(
|
||||
Some(tcx.hir().get_parent_item(impl_item.hir_id())),
|
||||
|
@ -1164,11 +1085,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
&impl_item.generics,
|
||||
|this| intravisit::walk_impl_item(this, impl_item),
|
||||
);
|
||||
self.missing_named_lifetime_spots.pop();
|
||||
}
|
||||
TyAlias(ref ty) => {
|
||||
let generics = &impl_item.generics;
|
||||
self.missing_named_lifetime_spots.push(generics.into());
|
||||
let mut index = self.next_early_index();
|
||||
let mut non_lifetime_count = 0;
|
||||
debug!("visit_ty: index = {}", index);
|
||||
|
@ -1203,14 +1122,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
this.visit_ty(ty);
|
||||
})
|
||||
});
|
||||
self.missing_named_lifetime_spots.pop();
|
||||
}
|
||||
Const(_, _) => {
|
||||
// Only methods and types support generics.
|
||||
assert!(impl_item.generics.params.is_empty());
|
||||
self.missing_named_lifetime_spots.push(MissingLifetimeSpot::Static);
|
||||
intravisit::walk_impl_item(self, impl_item);
|
||||
self.missing_named_lifetime_spots.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1393,8 +1309,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
) {
|
||||
debug!("visit_poly_trait_ref(trait_ref={:?})", trait_ref);
|
||||
|
||||
let should_pop_missing_lt = self.is_trait_ref_fn_scope(trait_ref);
|
||||
|
||||
let next_early_index = self.next_early_index();
|
||||
let (mut binders, scope_type) = self.poly_trait_ref_binder_info();
|
||||
|
||||
|
@ -1435,10 +1349,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
walk_list!(this, visit_generic_param, trait_ref.bound_generic_params);
|
||||
this.visit_trait_ref(&trait_ref.trait_ref);
|
||||
});
|
||||
|
||||
if should_pop_missing_lt {
|
||||
self.missing_named_lifetime_spots.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1584,14 +1494,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
{
|
||||
let LifetimeContext { tcx, map, .. } = self;
|
||||
let xcrate_object_lifetime_defaults = take(&mut self.xcrate_object_lifetime_defaults);
|
||||
let missing_named_lifetime_spots = take(&mut self.missing_named_lifetime_spots);
|
||||
let mut this = LifetimeContext {
|
||||
tcx: *tcx,
|
||||
map,
|
||||
scope: &wrap_scope,
|
||||
trait_definition_only: self.trait_definition_only,
|
||||
xcrate_object_lifetime_defaults,
|
||||
missing_named_lifetime_spots,
|
||||
};
|
||||
let span = tracing::debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope));
|
||||
{
|
||||
|
@ -1599,7 +1507,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
f(&mut this);
|
||||
}
|
||||
self.xcrate_object_lifetime_defaults = this.xcrate_object_lifetime_defaults;
|
||||
self.missing_named_lifetime_spots = this.missing_named_lifetime_spots;
|
||||
}
|
||||
|
||||
/// Visits self by adding a scope and handling recursive walk over the contents with `walk`.
|
||||
|
@ -2202,24 +2109,20 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
let mut assoc_item_kind = None;
|
||||
let mut impl_self = None;
|
||||
let parent = self.tcx.hir().get_parent_node(output.hir_id);
|
||||
let body = match self.tcx.hir().get(parent) {
|
||||
match self.tcx.hir().get(parent) {
|
||||
// `fn` definitions and methods.
|
||||
Node::Item(&hir::Item { kind: hir::ItemKind::Fn(.., body), .. }) => Some(body),
|
||||
Node::Item(&hir::Item { kind: hir::ItemKind::Fn(..), .. }) => {}
|
||||
|
||||
Node::TraitItem(&hir::TraitItem { kind: hir::TraitItemKind::Fn(_, ref m), .. }) => {
|
||||
Node::TraitItem(&hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) => {
|
||||
if let hir::ItemKind::Trait(.., ref trait_items) =
|
||||
self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind
|
||||
{
|
||||
assoc_item_kind =
|
||||
trait_items.iter().find(|ti| ti.id.hir_id() == parent).map(|ti| ti.kind);
|
||||
}
|
||||
match *m {
|
||||
hir::TraitFn::Required(_) => None,
|
||||
hir::TraitFn::Provided(body) => Some(body),
|
||||
}
|
||||
}
|
||||
|
||||
Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body), .. }) => {
|
||||
Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, _), .. }) => {
|
||||
if let hir::ItemKind::Impl(hir::Impl { ref self_ty, ref items, .. }) =
|
||||
self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind
|
||||
{
|
||||
|
@ -2227,13 +2130,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
assoc_item_kind =
|
||||
items.iter().find(|ii| ii.id.hir_id() == parent).map(|ii| ii.kind);
|
||||
}
|
||||
Some(body)
|
||||
}
|
||||
|
||||
// Foreign functions, `fn(...) -> R` and `Trait(...) -> R` (both types and bounds).
|
||||
Node::ForeignItem(_) | Node::Ty(_) | Node::TraitRef(_) => None,
|
||||
Node::ForeignItem(_) | Node::Ty(_) | Node::TraitRef(_) => {},
|
||||
|
||||
Node::TypeBinding(_) if let Node::TraitRef(_) = self.tcx.hir().get(self.tcx.hir().get_parent_node(parent)) => None,
|
||||
Node::TypeBinding(_) if let Node::TraitRef(_) = self.tcx.hir().get(self.tcx.hir().get_parent_node(parent)) => {},
|
||||
|
||||
// Everything else (only closures?) doesn't
|
||||
// actually enjoy elision in return types.
|
||||
|
@ -2320,42 +2222,29 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
// have that lifetime.
|
||||
let mut possible_implied_output_region = None;
|
||||
let mut lifetime_count = 0;
|
||||
let arg_lifetimes = inputs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.skip(has_self as usize)
|
||||
.map(|(i, input)| {
|
||||
let mut gather = GatherLifetimes {
|
||||
map: self.map,
|
||||
outer_index: ty::INNERMOST,
|
||||
have_bound_regions: false,
|
||||
lifetimes: Default::default(),
|
||||
};
|
||||
gather.visit_ty(input);
|
||||
for input in inputs.iter().skip(has_self as usize) {
|
||||
let mut gather = GatherLifetimes {
|
||||
map: self.map,
|
||||
outer_index: ty::INNERMOST,
|
||||
have_bound_regions: false,
|
||||
lifetimes: Default::default(),
|
||||
};
|
||||
gather.visit_ty(input);
|
||||
|
||||
lifetime_count += gather.lifetimes.len();
|
||||
lifetime_count += gather.lifetimes.len();
|
||||
|
||||
if lifetime_count == 1 && gather.lifetimes.len() == 1 {
|
||||
// there's a chance that the unique lifetime of this
|
||||
// iteration will be the appropriate lifetime for output
|
||||
// parameters, so lets store it.
|
||||
possible_implied_output_region = gather.lifetimes.iter().cloned().next();
|
||||
}
|
||||
|
||||
ElisionFailureInfo {
|
||||
parent: body,
|
||||
index: i,
|
||||
lifetime_count: gather.lifetimes.len(),
|
||||
have_bound_regions: gather.have_bound_regions,
|
||||
span: input.span,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
if lifetime_count == 1 && gather.lifetimes.len() == 1 {
|
||||
// there's a chance that the unique lifetime of this
|
||||
// iteration will be the appropriate lifetime for output
|
||||
// parameters, so lets store it.
|
||||
possible_implied_output_region = gather.lifetimes.iter().cloned().next();
|
||||
}
|
||||
}
|
||||
|
||||
let elide = if lifetime_count == 1 {
|
||||
Elide::Exact(possible_implied_output_region.unwrap())
|
||||
} else {
|
||||
Elide::Error(arg_lifetimes)
|
||||
Elide::Error
|
||||
};
|
||||
|
||||
debug!(?elide);
|
||||
|
@ -2487,17 +2376,14 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
|
||||
let mut late_depth = 0;
|
||||
let mut scope = self.scope;
|
||||
let mut in_scope_lifetimes = FxIndexSet::default();
|
||||
let error = loop {
|
||||
loop {
|
||||
match *scope {
|
||||
// Do not assign any resolution, it will be inferred.
|
||||
Scope::Body { .. } => return,
|
||||
|
||||
Scope::Root => break None,
|
||||
Scope::Root => break,
|
||||
|
||||
Scope::Binder { s, ref lifetimes, scope_type, .. } => {
|
||||
// collect named lifetimes for suggestions
|
||||
in_scope_lifetimes.extend(lifetimes.keys().copied());
|
||||
Scope::Binder { s, scope_type, .. } => {
|
||||
match scope_type {
|
||||
BinderScopeType::Normal => late_depth += 1,
|
||||
BinderScopeType::Concatenating => {}
|
||||
|
@ -2526,27 +2412,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
return;
|
||||
}
|
||||
|
||||
Scope::Elision { elide: Elide::Error(ref e), ref s, .. } => {
|
||||
let mut scope = s;
|
||||
loop {
|
||||
match scope {
|
||||
Scope::Binder { ref lifetimes, s, .. } => {
|
||||
// Collect named lifetimes for suggestions.
|
||||
in_scope_lifetimes.extend(lifetimes.keys().copied());
|
||||
scope = s;
|
||||
}
|
||||
Scope::ObjectLifetimeDefault { ref s, .. }
|
||||
| Scope::Elision { ref s, .. }
|
||||
| Scope::TraitRefBoundary { ref s, .. } => {
|
||||
scope = s;
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
break Some(&e[..]);
|
||||
}
|
||||
|
||||
Scope::Elision { elide: Elide::Forbid, .. } => break None,
|
||||
Scope::Elision { elide: Elide::Error, .. }
|
||||
| Scope::Elision { elide: Elide::Forbid, .. } => break,
|
||||
|
||||
Scope::ObjectLifetimeDefault { s, .. }
|
||||
| Scope::Supertrait { s, .. }
|
||||
|
@ -2554,26 +2421,11 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
scope = s;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let mut spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect();
|
||||
spans.sort();
|
||||
let mut spans_dedup = spans.clone();
|
||||
spans_dedup.dedup();
|
||||
let spans_with_counts: Vec<_> = spans_dedup
|
||||
.into_iter()
|
||||
.map(|sp| (sp, spans.iter().filter(|nsp| *nsp == &sp).count()))
|
||||
.collect();
|
||||
|
||||
let mut err = self.report_missing_lifetime_specifiers(spans.clone(), lifetime_refs.len());
|
||||
|
||||
self.add_missing_lifetime_specifiers_label(
|
||||
&mut err,
|
||||
spans_with_counts,
|
||||
in_scope_lifetimes,
|
||||
error,
|
||||
);
|
||||
err.emit();
|
||||
for lt in lifetime_refs {
|
||||
self.tcx.sess.delay_span_bug(lt.span, "Missing lifetime specifier");
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_object_lifetime_default(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#![feature(drain_filter)]
|
||||
#![feature(if_let_guard)]
|
||||
#![cfg_attr(bootstrap, feature(let_chains))]
|
||||
#![feature(iter_intersperse)]
|
||||
#![feature(let_else)]
|
||||
#![feature(never_type)]
|
||||
#![recursion_limit = "256"]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue