improve the suggestion of generic_bound_failure
This commit is contained in:
parent
6d271692b0
commit
5be0b2283a
28 changed files with 696 additions and 384 deletions
|
@ -66,13 +66,12 @@ use rustc_hir::def::DefKind;
|
|||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::Node;
|
||||
use rustc_middle::dep_graph::DepContext;
|
||||
use rustc_middle::ty::print::with_forced_trimmed_paths;
|
||||
use rustc_middle::ty::relate::{self, RelateResult, TypeRelation};
|
||||
use rustc_middle::ty::{
|
||||
self, error::TypeError, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
|
||||
TypeVisitable, TypeVisitableExt,
|
||||
self, error::TypeError, IsSuggestable, List, Region, Ty, TyCtxt, TypeFoldable,
|
||||
TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
|
||||
};
|
||||
use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span};
|
||||
use rustc_target::spec::abi;
|
||||
|
@ -2317,126 +2316,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
bound_kind: GenericKind<'tcx>,
|
||||
sub: Region<'tcx>,
|
||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||
// Attempt to obtain the span of the parameter so we can
|
||||
// suggest adding an explicit lifetime bound to it.
|
||||
let generics = self.tcx.generics_of(generic_param_scope);
|
||||
// type_param_span is (span, has_bounds)
|
||||
let mut is_synthetic = false;
|
||||
let mut ast_generics = None;
|
||||
let type_param_span = match bound_kind {
|
||||
GenericKind::Param(ref param) => {
|
||||
// Account for the case where `param` corresponds to `Self`,
|
||||
// which doesn't have the expected type argument.
|
||||
if !(generics.has_self && param.index == 0) {
|
||||
let type_param = generics.type_param(param, self.tcx);
|
||||
is_synthetic = type_param.kind.is_synthetic();
|
||||
type_param.def_id.as_local().map(|def_id| {
|
||||
// Get the `hir::Param` to verify whether it already has any bounds.
|
||||
// We do this to avoid suggesting code that ends up as `T: 'a'b`,
|
||||
// instead we suggest `T: 'a + 'b` in that case.
|
||||
let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
|
||||
ast_generics = self.tcx.hir().get_generics(hir_id.owner.def_id);
|
||||
let bounds =
|
||||
ast_generics.and_then(|g| g.bounds_span_for_suggestions(def_id));
|
||||
// `sp` only covers `T`, change it so that it covers
|
||||
// `T:` when appropriate
|
||||
if let Some(span) = bounds {
|
||||
(span, true)
|
||||
} else {
|
||||
let sp = self.tcx.def_span(def_id);
|
||||
(sp.shrink_to_hi(), false)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let new_lt = {
|
||||
let mut possible = (b'a'..=b'z').map(|c| format!("'{}", c as char));
|
||||
let lts_names =
|
||||
iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p)))
|
||||
.flat_map(|g| &g.params)
|
||||
.filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))
|
||||
.map(|p| p.name.as_str())
|
||||
.collect::<Vec<_>>();
|
||||
possible
|
||||
.find(|candidate| !lts_names.contains(&&candidate[..]))
|
||||
.unwrap_or("'lt".to_string())
|
||||
};
|
||||
|
||||
let mut add_lt_suggs: Vec<Option<_>> = vec![];
|
||||
if is_synthetic {
|
||||
if let Some(ast_generics) = ast_generics {
|
||||
let named_lifetime_param_exist = ast_generics.params.iter().any(|p| {
|
||||
matches!(
|
||||
p.kind,
|
||||
hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit }
|
||||
)
|
||||
});
|
||||
if named_lifetime_param_exist && let [param, ..] = ast_generics.params
|
||||
{
|
||||
add_lt_suggs.push(Some((
|
||||
self.tcx.def_span(param.def_id).shrink_to_lo(),
|
||||
format!("{new_lt}, "),
|
||||
)));
|
||||
} else {
|
||||
add_lt_suggs
|
||||
.push(Some((ast_generics.span.shrink_to_hi(), format!("<{new_lt}>"))));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let [param, ..] = &generics.params[..] && let Some(def_id) = param.def_id.as_local()
|
||||
{
|
||||
add_lt_suggs
|
||||
.push(Some((self.tcx.def_span(def_id).shrink_to_lo(), format!("{new_lt}, "))));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ast_generics) = ast_generics {
|
||||
for p in ast_generics.params {
|
||||
if p.is_elided_lifetime() {
|
||||
if self
|
||||
.tcx
|
||||
.sess
|
||||
.source_map()
|
||||
.span_to_prev_source(p.span.shrink_to_hi())
|
||||
.ok()
|
||||
.is_some_and(|s| *s.as_bytes().last().unwrap() == b'&')
|
||||
{
|
||||
add_lt_suggs
|
||||
.push(Some(
|
||||
(
|
||||
p.span.shrink_to_hi(),
|
||||
if let Ok(snip) = self.tcx.sess.source_map().span_to_next_source(p.span)
|
||||
&& snip.starts_with(' ')
|
||||
{
|
||||
new_lt.to_string()
|
||||
} else {
|
||||
format!("{new_lt} ")
|
||||
}
|
||||
)
|
||||
));
|
||||
} else {
|
||||
add_lt_suggs.push(Some((p.span.shrink_to_hi(), format!("<{new_lt}>"))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let labeled_user_string = match bound_kind {
|
||||
GenericKind::Param(ref p) => format!("the parameter type `{p}`"),
|
||||
GenericKind::Alias(ref p) => match p.kind(self.tcx) {
|
||||
ty::AliasKind::Projection | ty::AliasKind::Inherent => {
|
||||
format!("the associated type `{p}`")
|
||||
}
|
||||
ty::AliasKind::Weak => format!("the type alias `{p}`"),
|
||||
ty::AliasKind::Opaque => format!("the opaque type `{p}`"),
|
||||
},
|
||||
};
|
||||
|
||||
if let Some(SubregionOrigin::CompareImplItemObligation {
|
||||
span,
|
||||
impl_item_def_id,
|
||||
|
@ -2451,165 +2330,33 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
fn binding_suggestion<'tcx, S: fmt::Display>(
|
||||
err: &mut Diagnostic,
|
||||
type_param_span: Option<(Span, bool)>,
|
||||
bound_kind: GenericKind<'tcx>,
|
||||
sub: S,
|
||||
add_lt_suggs: Vec<Option<(Span, String)>>,
|
||||
) {
|
||||
let msg = "consider adding an explicit lifetime bound";
|
||||
if let Some((sp, has_lifetimes)) = type_param_span {
|
||||
let suggestion =
|
||||
if has_lifetimes { format!(" + {sub}") } else { format!(": {sub}") };
|
||||
let mut suggestions = vec![(sp, suggestion)];
|
||||
for add_lt_sugg in add_lt_suggs.into_iter().flatten() {
|
||||
suggestions.push(add_lt_sugg);
|
||||
let labeled_user_string = match bound_kind {
|
||||
GenericKind::Param(ref p) => format!("the parameter type `{p}`"),
|
||||
GenericKind::Alias(ref p) => match p.kind(self.tcx) {
|
||||
ty::AliasKind::Projection | ty::AliasKind::Inherent => {
|
||||
format!("the associated type `{p}`")
|
||||
}
|
||||
err.multipart_suggestion_verbose(
|
||||
format!("{msg}..."),
|
||||
suggestions,
|
||||
Applicability::MaybeIncorrect, // Issue #41966
|
||||
);
|
||||
} else {
|
||||
let consider = format!("{msg} `{bound_kind}: {sub}`...");
|
||||
err.help(consider);
|
||||
}
|
||||
}
|
||||
|
||||
let new_binding_suggestion =
|
||||
|err: &mut Diagnostic, type_param_span: Option<(Span, bool)>| {
|
||||
let msg = "consider introducing an explicit lifetime bound";
|
||||
if let Some((sp, has_lifetimes)) = type_param_span {
|
||||
let suggestion =
|
||||
if has_lifetimes { format!(" + {new_lt}") } else { format!(": {new_lt}") };
|
||||
let mut sugg =
|
||||
vec![(sp, suggestion), (span.shrink_to_hi(), format!(" + {new_lt}"))];
|
||||
for lt in add_lt_suggs.clone().into_iter().flatten() {
|
||||
sugg.push(lt);
|
||||
sugg.rotate_right(1);
|
||||
}
|
||||
// `MaybeIncorrect` due to issue #41966.
|
||||
err.multipart_suggestion(msg, sugg, Applicability::MaybeIncorrect);
|
||||
}
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
enum SubOrigin<'hir> {
|
||||
GAT(&'hir hir::Generics<'hir>),
|
||||
Impl,
|
||||
Trait,
|
||||
Fn,
|
||||
Unknown,
|
||||
}
|
||||
let sub_origin = 'origin: {
|
||||
match *sub {
|
||||
ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, .. }) => {
|
||||
let node = self.tcx.hir().get_if_local(def_id).unwrap();
|
||||
match node {
|
||||
Node::GenericParam(param) => {
|
||||
for h in self.tcx.hir().parent_iter(param.hir_id) {
|
||||
break 'origin match h.1 {
|
||||
Node::ImplItem(hir::ImplItem {
|
||||
kind: hir::ImplItemKind::Type(..),
|
||||
generics,
|
||||
..
|
||||
})
|
||||
| Node::TraitItem(hir::TraitItem {
|
||||
kind: hir::TraitItemKind::Type(..),
|
||||
generics,
|
||||
..
|
||||
}) => SubOrigin::GAT(generics),
|
||||
Node::ImplItem(hir::ImplItem {
|
||||
kind: hir::ImplItemKind::Fn(..),
|
||||
..
|
||||
})
|
||||
| Node::TraitItem(hir::TraitItem {
|
||||
kind: hir::TraitItemKind::Fn(..),
|
||||
..
|
||||
})
|
||||
| Node::Item(hir::Item {
|
||||
kind: hir::ItemKind::Fn(..), ..
|
||||
}) => SubOrigin::Fn,
|
||||
Node::Item(hir::Item {
|
||||
kind: hir::ItemKind::Trait(..),
|
||||
..
|
||||
}) => SubOrigin::Trait,
|
||||
Node::Item(hir::Item {
|
||||
kind: hir::ItemKind::Impl(..), ..
|
||||
}) => SubOrigin::Impl,
|
||||
_ => continue,
|
||||
};
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
SubOrigin::Unknown
|
||||
ty::AliasKind::Weak => format!("the type alias `{p}`"),
|
||||
ty::AliasKind::Opaque => format!("the opaque type `{p}`"),
|
||||
},
|
||||
};
|
||||
debug!(?sub_origin);
|
||||
|
||||
let mut err = match (*sub, sub_origin) {
|
||||
// In the case of GATs, we have to be careful. If we a type parameter `T` on an impl,
|
||||
// but a lifetime `'a` on an associated type, then we might need to suggest adding
|
||||
// `where T: 'a`. Importantly, this is on the GAT span, not on the `T` declaration.
|
||||
(ty::ReEarlyBound(ty::EarlyBoundRegion { name: _, .. }), SubOrigin::GAT(generics)) => {
|
||||
// Does the required lifetime have a nice name we can print?
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx.sess,
|
||||
span,
|
||||
E0309,
|
||||
"{} may not live long enough",
|
||||
labeled_user_string
|
||||
);
|
||||
let pred = format!("{bound_kind}: {sub}");
|
||||
let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred,);
|
||||
err.span_suggestion(
|
||||
generics.tail_span_for_predicate_suggestion(),
|
||||
"consider adding a where clause",
|
||||
suggestion,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
err
|
||||
}
|
||||
(
|
||||
ty::ReEarlyBound(ty::EarlyBoundRegion { name, .. })
|
||||
| ty::ReFree(ty::FreeRegion { bound_region: ty::BrNamed(_, name), .. }),
|
||||
_,
|
||||
) if name != kw::UnderscoreLifetime => {
|
||||
// Does the required lifetime have a nice name we can print?
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx.sess,
|
||||
span,
|
||||
E0309,
|
||||
"{} may not live long enough",
|
||||
labeled_user_string
|
||||
);
|
||||
// Explicitly use the name instead of `sub`'s `Display` impl. The `Display` impl
|
||||
// for the bound is not suitable for suggestions when `-Zverbose` is set because it
|
||||
// uses `Debug` output, so we handle it specially here so that suggestions are
|
||||
// always correct.
|
||||
binding_suggestion(&mut err, type_param_span, bound_kind, name, vec![]);
|
||||
err
|
||||
}
|
||||
|
||||
(ty::ReStatic, _) => {
|
||||
// Does the required lifetime have a nice name we can print?
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx.sess,
|
||||
span,
|
||||
E0310,
|
||||
"{} may not live long enough",
|
||||
labeled_user_string
|
||||
);
|
||||
binding_suggestion(&mut err, type_param_span, bound_kind, "'static", vec![]);
|
||||
err
|
||||
}
|
||||
|
||||
let mut err = match sub.kind() {
|
||||
ty::ReEarlyBound(_) | ty::ReFree(_) if sub.has_name() => struct_span_err!(
|
||||
self.tcx.sess,
|
||||
span,
|
||||
E0309,
|
||||
"{} may not live long enough",
|
||||
labeled_user_string
|
||||
),
|
||||
ty::ReStatic => struct_span_err!(
|
||||
self.tcx.sess,
|
||||
span,
|
||||
E0310,
|
||||
"{} may not live long enough",
|
||||
labeled_user_string
|
||||
),
|
||||
_ => {
|
||||
// If not, be less specific.
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx.sess,
|
||||
span,
|
||||
|
@ -2625,37 +2372,192 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
"...",
|
||||
None,
|
||||
);
|
||||
if let Some(infer::RelateParamBound(_, t, _)) = origin {
|
||||
let t = self.resolve_vars_if_possible(t);
|
||||
match t.kind() {
|
||||
// We've got:
|
||||
// fn get_later<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
|
||||
// suggest:
|
||||
// fn get_later<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a
|
||||
ty::Closure(..) | ty::Alias(ty::Opaque, ..) => {
|
||||
new_binding_suggestion(&mut err, type_param_span);
|
||||
}
|
||||
_ => {
|
||||
binding_suggestion(
|
||||
&mut err,
|
||||
type_param_span,
|
||||
bound_kind,
|
||||
new_lt,
|
||||
add_lt_suggs,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
err
|
||||
}
|
||||
};
|
||||
|
||||
'suggestion: {
|
||||
let msg = "consider adding an explicit lifetime bound";
|
||||
|
||||
if (bound_kind, sub).has_infer_regions()
|
||||
|| (bound_kind, sub).has_placeholders()
|
||||
|| !bound_kind.is_suggestable(self.tcx, false)
|
||||
{
|
||||
let lt_name = sub.get_name_or_anon().to_string();
|
||||
err.help(format!("{msg} `{bound_kind}: {lt_name}`..."));
|
||||
break 'suggestion;
|
||||
}
|
||||
|
||||
let mut generic_param_scope = generic_param_scope;
|
||||
while self.tcx.def_kind(generic_param_scope) == DefKind::OpaqueTy {
|
||||
generic_param_scope = self.tcx.local_parent(generic_param_scope);
|
||||
}
|
||||
|
||||
// type_param_sugg_span is (span, has_bounds)
|
||||
let (type_scope, type_param_sugg_span) = match bound_kind {
|
||||
GenericKind::Param(ref param) => {
|
||||
let generics = self.tcx.generics_of(generic_param_scope);
|
||||
let def_id = generics.type_param(param, self.tcx).def_id.expect_local();
|
||||
let scope = self.tcx.local_def_id_to_hir_id(def_id).owner.def_id;
|
||||
// Get the `hir::Param` to verify whether it already has any bounds.
|
||||
// We do this to avoid suggesting code that ends up as `T: 'a'b`,
|
||||
// instead we suggest `T: 'a + 'b` in that case.
|
||||
let hir_generics = self.tcx.hir().get_generics(scope).unwrap();
|
||||
let sugg_span = match hir_generics.bounds_span_for_suggestions(def_id) {
|
||||
Some(span) => Some((span, true)),
|
||||
// If `param` corresponds to `Self`, no usable suggestion span.
|
||||
None if generics.has_self && param.index == 0 => None,
|
||||
None => Some((self.tcx.def_span(def_id).shrink_to_hi(), false)),
|
||||
};
|
||||
(scope, sugg_span)
|
||||
}
|
||||
_ => (generic_param_scope, None),
|
||||
};
|
||||
let suggestion_scope = {
|
||||
let lifetime_scope = match sub.kind() {
|
||||
ty::ReStatic => hir::def_id::CRATE_DEF_ID,
|
||||
_ => match self.tcx.is_suitable_region(sub) {
|
||||
Some(info) => info.def_id,
|
||||
None => generic_param_scope,
|
||||
},
|
||||
};
|
||||
match self.tcx.is_descendant_of(type_scope.into(), lifetime_scope.into()) {
|
||||
true => type_scope,
|
||||
false => lifetime_scope,
|
||||
}
|
||||
};
|
||||
|
||||
let mut suggs = vec![];
|
||||
let lt_name = self.suggest_name_region(sub, &mut suggs);
|
||||
|
||||
if let Some((sp, has_lifetimes)) = type_param_sugg_span
|
||||
&& suggestion_scope == type_scope
|
||||
{
|
||||
let suggestion =
|
||||
if has_lifetimes { format!(" + {lt_name}") } else { format!(": {lt_name}") };
|
||||
suggs.push((sp, suggestion))
|
||||
} else {
|
||||
let generics = self.tcx.hir().get_generics(suggestion_scope).unwrap();
|
||||
let pred = format!("{bound_kind}: {lt_name}");
|
||||
let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred,);
|
||||
suggs.push((generics.tail_span_for_predicate_suggestion(), suggestion))
|
||||
}
|
||||
|
||||
err.multipart_suggestion_verbose(
|
||||
format!("{msg}..."),
|
||||
suggs,
|
||||
Applicability::MaybeIncorrect, // Issue #41966
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(origin) = origin {
|
||||
self.note_region_origin(&mut err, &origin);
|
||||
}
|
||||
err
|
||||
}
|
||||
|
||||
pub fn suggest_name_region(
|
||||
&self,
|
||||
lifetime: Region<'tcx>,
|
||||
add_lt_suggs: &mut Vec<(Span, String)>,
|
||||
) -> String {
|
||||
struct LifetimeReplaceVisitor<'tcx, 'a> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
needle: hir::LifetimeName,
|
||||
new_lt: &'a str,
|
||||
add_lt_suggs: &'a mut Vec<(Span, String)>,
|
||||
}
|
||||
|
||||
impl<'hir, 'tcx> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'tcx, '_> {
|
||||
fn visit_lifetime(&mut self, lt: &'hir hir::Lifetime) {
|
||||
if lt.res == self.needle {
|
||||
let (pos, span) = lt.suggestion_position();
|
||||
let new_lt = &self.new_lt;
|
||||
let sugg = match pos {
|
||||
hir::LifetimeSuggestionPosition::Normal => format!("{new_lt}"),
|
||||
hir::LifetimeSuggestionPosition::Ampersand => format!("{new_lt} "),
|
||||
hir::LifetimeSuggestionPosition::ElidedPath => format!("<{new_lt}>"),
|
||||
hir::LifetimeSuggestionPosition::ElidedPathArgument => {
|
||||
format!("{new_lt}, ")
|
||||
}
|
||||
hir::LifetimeSuggestionPosition::ObjectDefault => format!("+ {new_lt}"),
|
||||
};
|
||||
self.add_lt_suggs.push((span, sugg));
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, ty: &'hir hir::Ty<'hir>) {
|
||||
let hir::TyKind::OpaqueDef(item_id, _, _) = ty.kind else {
|
||||
return hir::intravisit::walk_ty(self, ty);
|
||||
};
|
||||
let opaque_ty = self.tcx.hir().item(item_id).expect_opaque_ty();
|
||||
if let Some(&(_, b)) =
|
||||
opaque_ty.lifetime_mapping.iter().find(|&(a, _)| a.res == self.needle)
|
||||
{
|
||||
let prev_needle =
|
||||
std::mem::replace(&mut self.needle, hir::LifetimeName::Param(b));
|
||||
for bound in opaque_ty.bounds {
|
||||
self.visit_param_bound(bound);
|
||||
}
|
||||
self.needle = prev_needle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (lifetime_def_id, lifetime_scope) = match self.tcx.is_suitable_region(lifetime) {
|
||||
Some(info) if !lifetime.has_name() => {
|
||||
(info.boundregion.get_id().unwrap().expect_local(), info.def_id)
|
||||
}
|
||||
_ => return lifetime.get_name_or_anon().to_string(),
|
||||
};
|
||||
|
||||
let new_lt = {
|
||||
let generics = self.tcx.generics_of(lifetime_scope);
|
||||
let mut used_names =
|
||||
iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p)))
|
||||
.flat_map(|g| &g.params)
|
||||
.filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))
|
||||
.map(|p| p.name)
|
||||
.collect::<Vec<_>>();
|
||||
if let Some(hir_id) = self.tcx.opt_local_def_id_to_hir_id(lifetime_scope) {
|
||||
// consider late-bound lifetimes ...
|
||||
used_names.extend(self.tcx.late_bound_vars(hir_id).into_iter().filter_map(|p| {
|
||||
match p {
|
||||
ty::BoundVariableKind::Region(lt) => lt.get_name(),
|
||||
_ => None,
|
||||
}
|
||||
}))
|
||||
}
|
||||
(b'a'..=b'z')
|
||||
.map(|c| format!("'{}", c as char))
|
||||
.find(|candidate| !used_names.iter().any(|e| e.as_str() == candidate))
|
||||
.unwrap_or("'lt".to_string())
|
||||
};
|
||||
|
||||
let mut visitor = LifetimeReplaceVisitor {
|
||||
tcx: self.tcx,
|
||||
needle: hir::LifetimeName::Param(lifetime_def_id),
|
||||
add_lt_suggs,
|
||||
new_lt: &new_lt,
|
||||
};
|
||||
match self.tcx.hir().expect_owner(lifetime_scope) {
|
||||
hir::OwnerNode::Item(i) => visitor.visit_item(i),
|
||||
hir::OwnerNode::ForeignItem(i) => visitor.visit_foreign_item(i),
|
||||
hir::OwnerNode::ImplItem(i) => visitor.visit_impl_item(i),
|
||||
hir::OwnerNode::TraitItem(i) => visitor.visit_trait_item(i),
|
||||
hir::OwnerNode::Crate(_) => bug!("OwnerNode::Crate doesn't not have generics"),
|
||||
}
|
||||
|
||||
let ast_generics = self.tcx.hir().get_generics(lifetime_scope).unwrap();
|
||||
let sugg = ast_generics
|
||||
.span_for_lifetime_suggestion()
|
||||
.map(|span| (span, format!("{new_lt}, ")))
|
||||
.unwrap_or_else(|| (ast_generics.span, format!("<{new_lt}>")));
|
||||
add_lt_suggs.push(sugg);
|
||||
|
||||
new_lt
|
||||
}
|
||||
|
||||
fn report_sub_sup_conflict(
|
||||
&self,
|
||||
var_origin: RegionVariableOrigin,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue