Compute lifetimes in scope at diagnostic time.
This commit is contained in:
parent
cb0584f86b
commit
a07290047e
9 changed files with 101 additions and 94 deletions
|
@ -313,6 +313,13 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn visit_assoc_type_binding(&mut self, type_binding: &'hir TypeBinding<'hir>) {
|
||||||
|
self.insert(type_binding.span, type_binding.hir_id, Node::TypeBinding(type_binding));
|
||||||
|
self.with_parent(type_binding.hir_id, |this| {
|
||||||
|
intravisit::walk_assoc_type_binding(this, type_binding)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn visit_trait_item_ref(&mut self, ii: &'hir TraitItemRef) {
|
fn visit_trait_item_ref(&mut self, ii: &'hir TraitItemRef) {
|
||||||
// Do not visit the duplicate information in TraitItemRef. We want to
|
// Do not visit the duplicate information in TraitItemRef. We want to
|
||||||
// map the actual nodes, not the duplicate ones in the *Ref.
|
// map the actual nodes, not the duplicate ones in the *Ref.
|
||||||
|
|
|
@ -3302,6 +3302,7 @@ pub enum Node<'hir> {
|
||||||
Stmt(&'hir Stmt<'hir>),
|
Stmt(&'hir Stmt<'hir>),
|
||||||
PathSegment(&'hir PathSegment<'hir>),
|
PathSegment(&'hir PathSegment<'hir>),
|
||||||
Ty(&'hir Ty<'hir>),
|
Ty(&'hir Ty<'hir>),
|
||||||
|
TypeBinding(&'hir TypeBinding<'hir>),
|
||||||
TraitRef(&'hir TraitRef<'hir>),
|
TraitRef(&'hir TraitRef<'hir>),
|
||||||
Binding(&'hir Pat<'hir>),
|
Binding(&'hir Pat<'hir>),
|
||||||
Pat(&'hir Pat<'hir>),
|
Pat(&'hir Pat<'hir>),
|
||||||
|
@ -3347,6 +3348,7 @@ impl<'hir> Node<'hir> {
|
||||||
| Node::PathSegment(PathSegment { ident, .. }) => Some(*ident),
|
| Node::PathSegment(PathSegment { ident, .. }) => Some(*ident),
|
||||||
Node::Lifetime(lt) => Some(lt.name.ident()),
|
Node::Lifetime(lt) => Some(lt.name.ident()),
|
||||||
Node::GenericParam(p) => Some(p.name.ident()),
|
Node::GenericParam(p) => Some(p.name.ident()),
|
||||||
|
Node::TypeBinding(b) => Some(b.ident),
|
||||||
Node::Param(..)
|
Node::Param(..)
|
||||||
| Node::AnonConst(..)
|
| Node::AnonConst(..)
|
||||||
| Node::Expr(..)
|
| Node::Expr(..)
|
||||||
|
|
|
@ -85,6 +85,7 @@ impl<'a> State<'a> {
|
||||||
Node::Stmt(a) => self.print_stmt(&a),
|
Node::Stmt(a) => self.print_stmt(&a),
|
||||||
Node::PathSegment(a) => self.print_path_segment(&a),
|
Node::PathSegment(a) => self.print_path_segment(&a),
|
||||||
Node::Ty(a) => self.print_type(&a),
|
Node::Ty(a) => self.print_type(&a),
|
||||||
|
Node::TypeBinding(a) => self.print_type_binding(&a),
|
||||||
Node::TraitRef(a) => self.print_trait_ref(&a),
|
Node::TraitRef(a) => self.print_trait_ref(&a),
|
||||||
Node::Binding(a) | Node::Pat(a) => self.print_pat(&a),
|
Node::Binding(a) | Node::Pat(a) => self.print_pat(&a),
|
||||||
Node::Arm(a) => self.print_arm(&a),
|
Node::Arm(a) => self.print_arm(&a),
|
||||||
|
@ -1703,21 +1704,7 @@ impl<'a> State<'a> {
|
||||||
|
|
||||||
for binding in generic_args.bindings.iter() {
|
for binding in generic_args.bindings.iter() {
|
||||||
start_or_comma(self);
|
start_or_comma(self);
|
||||||
self.print_ident(binding.ident);
|
self.print_type_binding(binding);
|
||||||
self.print_generic_args(binding.gen_args, false, false);
|
|
||||||
self.space();
|
|
||||||
match generic_args.bindings[0].kind {
|
|
||||||
hir::TypeBindingKind::Equality { ref term } => {
|
|
||||||
self.word_space("=");
|
|
||||||
match term {
|
|
||||||
Term::Ty(ref ty) => self.print_type(ty),
|
|
||||||
Term::Const(ref c) => self.print_anon_const(c),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hir::TypeBindingKind::Constraint { bounds } => {
|
|
||||||
self.print_bounds(":", bounds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !empty.get() {
|
if !empty.get() {
|
||||||
|
@ -1726,6 +1713,24 @@ impl<'a> State<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn print_type_binding(&mut self, binding: &hir::TypeBinding<'_>) {
|
||||||
|
self.print_ident(binding.ident);
|
||||||
|
self.print_generic_args(binding.gen_args, false, false);
|
||||||
|
self.space();
|
||||||
|
match binding.kind {
|
||||||
|
hir::TypeBindingKind::Equality { ref term } => {
|
||||||
|
self.word_space("=");
|
||||||
|
match term {
|
||||||
|
Term::Ty(ref ty) => self.print_type(ty),
|
||||||
|
Term::Const(ref c) => self.print_anon_const(c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hir::TypeBindingKind::Constraint { bounds } => {
|
||||||
|
self.print_bounds(":", bounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn print_pat(&mut self, pat: &hir::Pat<'_>) {
|
pub fn print_pat(&mut self, pat: &hir::Pat<'_>) {
|
||||||
self.maybe_print_comment(pat.span.lo());
|
self.maybe_print_comment(pat.span.lo());
|
||||||
self.ann.pre(self, AnnNode::Pat(pat));
|
self.ann.pre(self, AnnNode::Pat(pat));
|
||||||
|
|
|
@ -298,6 +298,7 @@ impl<'hir> Map<'hir> {
|
||||||
Node::Stmt(_)
|
Node::Stmt(_)
|
||||||
| Node::PathSegment(_)
|
| Node::PathSegment(_)
|
||||||
| Node::Ty(_)
|
| Node::Ty(_)
|
||||||
|
| Node::TypeBinding(_)
|
||||||
| Node::Infer(_)
|
| Node::Infer(_)
|
||||||
| Node::TraitRef(_)
|
| Node::TraitRef(_)
|
||||||
| Node::Pat(_)
|
| Node::Pat(_)
|
||||||
|
@ -323,7 +324,8 @@ impl<'hir> Map<'hir> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_parent_node(self, hir_id: HirId) -> HirId {
|
pub fn get_parent_node(self, hir_id: HirId) -> HirId {
|
||||||
self.find_parent_node(hir_id).unwrap()
|
self.find_parent_node(hir_id)
|
||||||
|
.unwrap_or_else(|| bug!("No parent for node {:?}", self.node_to_string(hir_id)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves the `Node` corresponding to `id`, returning `None` if cannot be found.
|
/// Retrieves the `Node` corresponding to `id`, returning `None` if cannot be found.
|
||||||
|
@ -973,6 +975,7 @@ impl<'hir> Map<'hir> {
|
||||||
.with_hi(seg.args.map_or_else(|| ident_span.hi(), |args| args.span_ext.hi()))
|
.with_hi(seg.args.map_or_else(|| ident_span.hi(), |args| args.span_ext.hi()))
|
||||||
}
|
}
|
||||||
Node::Ty(ty) => ty.span,
|
Node::Ty(ty) => ty.span,
|
||||||
|
Node::TypeBinding(tb) => tb.span,
|
||||||
Node::TraitRef(tr) => tr.path.span,
|
Node::TraitRef(tr) => tr.path.span,
|
||||||
Node::Binding(pat) => pat.span,
|
Node::Binding(pat) => pat.span,
|
||||||
Node::Pat(pat) => pat.span,
|
Node::Pat(pat) => pat.span,
|
||||||
|
@ -1205,6 +1208,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String {
|
||||||
Some(Node::Stmt(_)) => node_str("stmt"),
|
Some(Node::Stmt(_)) => node_str("stmt"),
|
||||||
Some(Node::PathSegment(_)) => node_str("path segment"),
|
Some(Node::PathSegment(_)) => node_str("path segment"),
|
||||||
Some(Node::Ty(_)) => node_str("type"),
|
Some(Node::Ty(_)) => node_str("type"),
|
||||||
|
Some(Node::TypeBinding(_)) => node_str("type binding"),
|
||||||
Some(Node::TraitRef(_)) => node_str("trait ref"),
|
Some(Node::TraitRef(_)) => node_str("trait ref"),
|
||||||
Some(Node::Binding(_)) => node_str("local"),
|
Some(Node::Binding(_)) => node_str("local"),
|
||||||
Some(Node::Pat(_)) => node_str("pat"),
|
Some(Node::Pat(_)) => node_str("pat"),
|
||||||
|
|
|
@ -23,8 +23,7 @@ pub enum Region {
|
||||||
pub enum LifetimeScopeForPath {
|
pub enum LifetimeScopeForPath {
|
||||||
/// Contains all lifetime names that are in scope and could possibly be used in generics
|
/// Contains all lifetime names that are in scope and could possibly be used in generics
|
||||||
/// arguments of path.
|
/// arguments of path.
|
||||||
NonElided(Vec<LocalDefId>),
|
NonElided,
|
||||||
|
|
||||||
/// Information that allows us to suggest args of the form `<'_>` in case
|
/// Information that allows us to suggest args of the form `<'_>` in case
|
||||||
/// no generic arguments were provided for a path.
|
/// no generic arguments were provided for a path.
|
||||||
Elided,
|
Elided,
|
||||||
|
|
|
@ -517,30 +517,16 @@ fn late_region_as_bound_region<'tcx>(tcx: TyCtxt<'tcx>, region: &Region) -> ty::
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug")]
|
#[tracing::instrument(level = "debug")]
|
||||||
fn get_lifetime_scopes_for_path(mut scope: &Scope<'_>) -> LifetimeScopeForPath {
|
fn get_lifetime_scopes_for_path(mut scope: &Scope<'_>) -> LifetimeScopeForPath {
|
||||||
let mut available_lifetimes = vec![];
|
|
||||||
loop {
|
loop {
|
||||||
match scope {
|
match scope {
|
||||||
Scope::Binder { lifetimes, s, .. } => {
|
Scope::Elision { elide: Elide::Exact(_), .. } => return LifetimeScopeForPath::Elided,
|
||||||
available_lifetimes.extend(lifetimes.keys());
|
Scope::Root => return LifetimeScopeForPath::NonElided,
|
||||||
scope = s;
|
Scope::Binder { s, .. }
|
||||||
}
|
| Scope::Body { s, .. }
|
||||||
Scope::Body { s, .. } => {
|
| Scope::ObjectLifetimeDefault { s, .. }
|
||||||
scope = s;
|
| Scope::Supertrait { s, .. }
|
||||||
}
|
| Scope::TraitRefBoundary { s, .. }
|
||||||
Scope::Elision { elide, s } => {
|
| Scope::Elision { s, .. } => {
|
||||||
if let Elide::Exact(_) = elide {
|
|
||||||
return LifetimeScopeForPath::Elided;
|
|
||||||
} else {
|
|
||||||
scope = s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Scope::ObjectLifetimeDefault { s, .. } => {
|
|
||||||
scope = s;
|
|
||||||
}
|
|
||||||
Scope::Root => {
|
|
||||||
return LifetimeScopeForPath::NonElided(available_lifetimes);
|
|
||||||
}
|
|
||||||
Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } => {
|
|
||||||
scope = s;
|
scope = s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2170,6 +2156,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||||
|
|
||||||
// Foreign functions, `fn(...) -> R` and `Trait(...) -> R` (both types and bounds).
|
// Foreign functions, `fn(...) -> R` and `Trait(...) -> R` (both types and bounds).
|
||||||
Node::ForeignItem(_) | Node::Ty(_) | Node::TraitRef(_) => None,
|
Node::ForeignItem(_) | Node::Ty(_) | Node::TraitRef(_) => None,
|
||||||
|
|
||||||
|
Node::TypeBinding(_) if let Node::TraitRef(_) = self.tcx.hir().get(self.tcx.hir().get_parent_node(parent)) => None,
|
||||||
|
|
||||||
// Everything else (only closures?) doesn't
|
// Everything else (only closures?) doesn't
|
||||||
// actually enjoy elision in return types.
|
// actually enjoy elision in return types.
|
||||||
_ => {
|
_ => {
|
||||||
|
|
|
@ -249,6 +249,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
|
||||||
| hir::Node::Stmt(..)
|
| hir::Node::Stmt(..)
|
||||||
| hir::Node::PathSegment(..)
|
| hir::Node::PathSegment(..)
|
||||||
| hir::Node::Ty(..)
|
| hir::Node::Ty(..)
|
||||||
|
| hir::Node::TypeBinding(..)
|
||||||
| hir::Node::TraitRef(..)
|
| hir::Node::TraitRef(..)
|
||||||
| hir::Node::Binding(..)
|
| hir::Node::Binding(..)
|
||||||
| hir::Node::Pat(..)
|
| hir::Node::Pat(..)
|
||||||
|
|
|
@ -450,21 +450,10 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
|
||||||
.discr_type()
|
.discr_type()
|
||||||
.to_ty(tcx),
|
.to_ty(tcx),
|
||||||
|
|
||||||
Node::TraitRef(trait_ref @ &TraitRef {
|
Node::TypeBinding(binding @ &TypeBinding { hir_id: binding_id, .. })
|
||||||
path, ..
|
if let Node::TraitRef(trait_ref) = tcx.hir().get(
|
||||||
}) if let Some((binding, seg)) =
|
tcx.hir().get_parent_node(binding_id)
|
||||||
path
|
) =>
|
||||||
.segments
|
|
||||||
.iter()
|
|
||||||
.find_map(|seg| {
|
|
||||||
seg.args?.bindings
|
|
||||||
.iter()
|
|
||||||
.find_map(|binding| if binding.opt_const()?.hir_id == hir_id {
|
|
||||||
Some((binding, seg))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
})
|
|
||||||
}) =>
|
|
||||||
{
|
{
|
||||||
let Some(trait_def_id) = trait_ref.trait_def_id() else {
|
let Some(trait_def_id) = trait_ref.trait_def_id() else {
|
||||||
return tcx.ty_error_with_message(DUMMY_SP, "Could not find trait");
|
return tcx.ty_error_with_message(DUMMY_SP, "Could not find trait");
|
||||||
|
|
|
@ -291,7 +291,52 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates lifetime name suggestions from the lifetime parameter names
|
// Creates lifetime name suggestions from the lifetime parameter names
|
||||||
fn get_lifetime_args_suggestions_from_param_names(&self, num_params_to_take: usize) -> String {
|
fn get_lifetime_args_suggestions_from_param_names(
|
||||||
|
&self,
|
||||||
|
path_hir_id: Option<hir::HirId>,
|
||||||
|
num_params_to_take: usize,
|
||||||
|
) -> String {
|
||||||
|
debug!(?path_hir_id);
|
||||||
|
|
||||||
|
if let Some(path_hir_id) = path_hir_id {
|
||||||
|
// We first try to get lifetime name suggestions from scope or elision information.
|
||||||
|
// If none is available we use the parameter definitions
|
||||||
|
if let Some(LifetimeScopeForPath::Elided) = self.tcx.lifetime_scope(path_hir_id) {
|
||||||
|
// Use suggestions of the form `<'_, '_>` in case lifetime can be elided
|
||||||
|
return ["'_"].repeat(num_params_to_take).join(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ret = Vec::new();
|
||||||
|
for (id, node) in self.tcx.hir().parent_iter(path_hir_id) {
|
||||||
|
debug!(?id);
|
||||||
|
let params = if let Some(generics) = node.generics() {
|
||||||
|
generics.params
|
||||||
|
} else if let hir::Node::Ty(ty) = node
|
||||||
|
&& let hir::TyKind::BareFn(bare_fn) = ty.kind
|
||||||
|
{
|
||||||
|
bare_fn.generic_params
|
||||||
|
} else {
|
||||||
|
&[]
|
||||||
|
};
|
||||||
|
ret.extend(params.iter().filter_map(|p| {
|
||||||
|
let hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit }
|
||||||
|
= p.kind
|
||||||
|
else { return None };
|
||||||
|
let hir::ParamName::Plain(name) = p.name else { return None };
|
||||||
|
Some(name.to_string())
|
||||||
|
}));
|
||||||
|
if ret.len() >= num_params_to_take {
|
||||||
|
return ret[..num_params_to_take].join(", ");
|
||||||
|
}
|
||||||
|
// We cannot refer to lifetimes defined in an outer function.
|
||||||
|
if let hir::Node::Item(_) = node {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We could not gather enough lifetime parameters in the scope.
|
||||||
|
// We use the parameter names from the target type's definition instead.
|
||||||
self.gen_params
|
self.gen_params
|
||||||
.params
|
.params
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -501,44 +546,10 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
|
||||||
let num_params_to_take = num_missing_args;
|
let num_params_to_take = num_missing_args;
|
||||||
let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args));
|
let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args));
|
||||||
|
|
||||||
// we first try to get lifetime name suggestions from scope or elision information. If none is
|
let suggested_args = self.get_lifetime_args_suggestions_from_param_names(
|
||||||
// available we use the parameter definitions
|
self.path_segment.hir_id,
|
||||||
let suggested_args = if let Some(hir_id) = self.path_segment.hir_id {
|
num_params_to_take,
|
||||||
if let Some(lifetimes_in_scope) = self.tcx.lifetime_scope(hir_id) {
|
);
|
||||||
match lifetimes_in_scope {
|
|
||||||
LifetimeScopeForPath::NonElided(param_names) => {
|
|
||||||
debug!("NonElided(param_names: {:?})", param_names);
|
|
||||||
|
|
||||||
if param_names.len() >= num_params_to_take {
|
|
||||||
// use lifetime parameters in scope for suggestions
|
|
||||||
param_names
|
|
||||||
.iter()
|
|
||||||
.take(num_params_to_take)
|
|
||||||
.map(|def_id| {
|
|
||||||
self.tcx.item_name(def_id.to_def_id()).to_ident_string()
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(", ")
|
|
||||||
} else {
|
|
||||||
// Not enough lifetime arguments in scope -> create suggestions from
|
|
||||||
// lifetime parameter names in definition. An error for the incorrect
|
|
||||||
// lifetime scope will be output later.
|
|
||||||
self.get_lifetime_args_suggestions_from_param_names(num_params_to_take)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LifetimeScopeForPath::Elided => {
|
|
||||||
debug!("Elided");
|
|
||||||
// use suggestions of the form `<'_, '_>` in case lifetime can be elided
|
|
||||||
["'_"].repeat(num_params_to_take).join(",")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.get_lifetime_args_suggestions_from_param_names(num_params_to_take)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.get_lifetime_args_suggestions_from_param_names(num_params_to_take)
|
|
||||||
};
|
|
||||||
|
|
||||||
debug!("suggested_args: {:?}", &suggested_args);
|
debug!("suggested_args: {:?}", &suggested_args);
|
||||||
|
|
||||||
match self.angle_brackets {
|
match self.angle_brackets {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue