Suggests adding named lifetime when the return contains value borrowed from more than one lifetimes of the function's inputs

This commit is contained in:
yanchen4791 2022-12-16 14:47:24 -08:00
parent 388538fc96
commit 523fe7a121
8 changed files with 184 additions and 40 deletions

View file

@ -303,6 +303,7 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>(
None,
format!("captures `{}`", hidden_region),
None,
Some(reg_info.def_id),
)
}
}

View file

@ -8,13 +8,17 @@ use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan};
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{walk_ty, Visitor};
use rustc_hir::{self as hir, GenericBound, Item, ItemKind, Lifetime, LifetimeName, Node, TyKind};
use rustc_hir::{
self as hir, GenericBound, GenericParamKind, Item, ItemKind, Lifetime, LifetimeName, Node,
TyKind,
};
use rustc_middle::ty::{
self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
};
use rustc_span::symbol::Ident;
use rustc_span::Span;
use rustc_span::def_id::LocalDefId;
use std::ops::ControlFlow;
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
@ -268,6 +272,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
Some(arg),
captures,
Some((param.param_ty_span, param.param_ty.to_string())),
Some(anon_reg_sup.def_id),
);
let reported = err.emit();
@ -283,6 +288,7 @@ pub fn suggest_new_region_bound(
arg: Option<String>,
captures: String,
param: Option<(Span, String)>,
scope_def_id: Option<LocalDefId>,
) {
debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns);
// FIXME: account for the need of parens in `&(dyn Trait + '_)`
@ -340,12 +346,69 @@ pub fn suggest_new_region_bound(
_ => false,
}) {
} else {
err.span_suggestion_verbose(
fn_return.span.shrink_to_hi(),
&format!("{declare} `{ty}` {captures}, {explicit}",),
&plus_lt,
Applicability::MaybeIncorrect,
);
// get a lifetime name of existing named lifetimes if any
let existing_lt_name = if let Some(id) = scope_def_id
&& let Some(generics) = tcx.hir().get_generics(id)
&& let named_lifetimes = generics
.params
.iter()
.filter(|p| matches!(p.kind, GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit }))
.map(|p| { if let hir::ParamName::Plain(name) = p.name {Some(name.to_string())} else {None}})
.filter(|n| ! matches!(n, None))
.collect::<Vec<_>>()
&& named_lifetimes.len() > 0 {
named_lifetimes[0].clone()
} else {
None
};
let name = if let Some(name) = &existing_lt_name {
format!("{}", name)
} else {
format!("'a")
};
// if there are more than one elided lifetimes in inputs, the explicit `'_` lifetime cannot be used.
// introducing a new lifetime `'a` or making use of one from existing named lifetimes if any
if let Some(id) = scope_def_id
&& let Some(generics) = tcx.hir().get_generics(id)
&& let mut spans_suggs = generics
.params
.iter()
.filter(|p| p.is_elided_lifetime())
.map(|p|
if p.span.hi() - p.span.lo() == rustc_span::BytePos(1) { // Ampersand (elided without '_)
(p.span.shrink_to_hi(),format!("{name} "))
} else { // Underscore (elided with '_)
(p.span, format!("{name}"))
}
)
.collect::<Vec<_>>()
&& spans_suggs.len() > 1
{
let use_lt =
if existing_lt_name == None {
spans_suggs.push((generics.span.shrink_to_hi(), format!("<{name}>")));
format!("you can introduce a named lifetime parameter `{name}`")
} else {
// make use the existing named lifetime
format!("you can use the named lifetime parameter `{name}`")
};
spans_suggs
.push((fn_return.span.shrink_to_hi(), format!(" + {name} ")));
err.multipart_suggestion_verbose(
&format!(
"{declare} `{ty}` {captures}, {use_lt}",
),
spans_suggs,
Applicability::MaybeIncorrect,
);
} else {
err.span_suggestion_verbose(
fn_return.span.shrink_to_hi(),
&format!("{declare} `{ty}` {captures}, {explicit}",),
&plus_lt,
Applicability::MaybeIncorrect,
);
}
}
}
TyKind::TraitObject(_, lt, _) => {