1
Fork 0

Re-use logic for adding a suggestion when a lifetime bound is missing on an impl trait

This commit is contained in:
Oli Scherer 2021-10-05 13:32:03 +00:00
parent be399635a2
commit 888ba509ea
6 changed files with 150 additions and 91 deletions

View file

@ -2235,8 +2235,7 @@ pub enum TyKind<'hir> {
/// ///
/// Type parameters may be stored in each `PathSegment`. /// Type parameters may be stored in each `PathSegment`.
Path(QPath<'hir>), Path(QPath<'hir>),
/// An opaque type definition itself. This is currently only used for the /// An opaque type definition itself. This is only used for `impl Trait`.
/// `opaque type Foo: Trait` item that `impl Trait` in desugars to.
/// ///
/// The generic argument list contains the lifetimes (and in the future /// The generic argument list contains the lifetimes (and in the future
/// possibly parameters) that are actually bound on the `impl Trait`. /// possibly parameters) that are actually bound on the `impl Trait`.

View file

@ -267,6 +267,18 @@ pub fn unexpected_hidden_region_diagnostic(
hidden_region, hidden_region,
"", "",
); );
if let Some(reg_info) = tcx.is_suitable_region(hidden_region) {
let fn_returns = tcx.return_type_impl_or_dyn_traits(reg_info.def_id);
nice_region_error::suggest_new_region_bound(
tcx,
&mut err,
fn_returns,
hidden_region.to_string(),
None,
format!("captures {}", hidden_region),
None,
)
}
} }
_ => { _ => {
// Ugh. This is a painful case: the hidden region is not one // Ugh. This is a painful case: the hidden region is not one

View file

@ -14,6 +14,8 @@ mod static_impl_trait;
mod trait_impl_difference; mod trait_impl_difference;
mod util; mod util;
pub use static_impl_trait::suggest_new_region_bound;
impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
pub fn try_report_nice_region_error(&self, error: &RegionResolutionError<'tcx>) -> bool { pub fn try_report_nice_region_error(&self, error: &RegionResolutionError<'tcx>) -> bool {
NiceRegionError::new(self, error.clone()).try_report().is_some() NiceRegionError::new(self, error.clone()).try_report().is_some()

View file

@ -217,17 +217,42 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
)); ));
} }
debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns);
// FIXME: account for the need of parens in `&(dyn Trait + '_)`
let consider = "consider changing the";
let declare = "to declare that the";
let arg = match param.param.pat.simple_ident() { let arg = match param.param.pat.simple_ident() {
Some(simple_ident) => format!("argument `{}`", simple_ident), Some(simple_ident) => format!("argument `{}`", simple_ident),
None => "the argument".to_string(), None => "the argument".to_string(),
}; };
let explicit = format!("you can add an explicit `{}` lifetime bound", lifetime_name);
let explicit_static = format!("explicit `'static` bound to the lifetime of {}", arg);
let captures = format!("captures data from {}", arg); let captures = format!("captures data from {}", arg);
suggest_new_region_bound(
tcx,
&mut err,
fn_returns,
lifetime_name,
Some(arg),
captures,
Some((param.param_ty_span, param.param_ty.to_string())),
);
err.emit();
Some(ErrorReported)
}
}
pub fn suggest_new_region_bound(
tcx: TyCtxt<'tcx>,
err: &mut DiagnosticBuilder<'_>,
fn_returns: Vec<&rustc_hir::Ty<'_>>,
lifetime_name: String,
arg: Option<String>,
captures: String,
param: Option<(Span, String)>,
) {
debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns);
// FIXME: account for the need of parens in `&(dyn Trait + '_)`
let consider = "consider changing the";
let declare = "to declare that the";
let explicit = format!("you can add an explicit `{}` lifetime bound", lifetime_name);
let explicit_static =
arg.map(|arg| format!("explicit `'static` bound to the lifetime of {}", arg));
let add_static_bound = "alternatively, add an explicit `'static` bound to this reference"; let add_static_bound = "alternatively, add an explicit `'static` bound to this reference";
let plus_lt = format!(" + {}", lifetime_name); let plus_lt = format!(" + {}", lifetime_name);
for fn_return in fn_returns { for fn_return in fn_returns {
@ -241,8 +266,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
let opaque = if let ItemKind::OpaqueTy(opaque) = &item.kind { let opaque = if let ItemKind::OpaqueTy(opaque) = &item.kind {
opaque opaque
} else { } else {
err.emit(); return;
return Some(ErrorReported);
}; };
if let Some(span) = opaque if let Some(span) = opaque
@ -258,18 +282,22 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
}) })
.next() .next()
{ {
if let Some(explicit_static) = &explicit_static {
err.span_suggestion_verbose( err.span_suggestion_verbose(
span, span,
&format!("{} `impl Trait`'s {}", consider, explicit_static), &format!("{} `impl Trait`'s {}", consider, explicit_static),
lifetime_name.clone(), lifetime_name.clone(),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
}
if let Some((param_span, param_ty)) = param.clone() {
err.span_suggestion_verbose( err.span_suggestion_verbose(
param.param_ty_span, param_span,
add_static_bound, add_static_bound,
param.param_ty.to_string(), param_ty,
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
}
} else if opaque } else if opaque
.bounds .bounds
.iter() .iter()
@ -317,28 +345,31 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
// would happen if there are nested impl/dyn traits and only // would happen if there are nested impl/dyn traits and only
// one of them has the bound we'd suggest already there, like // one of them has the bound we'd suggest already there, like
// in `impl Foo<X = dyn Bar> + '_`. // in `impl Foo<X = dyn Bar> + '_`.
if let Some(explicit_static) = &explicit_static {
err.span_suggestion_verbose( err.span_suggestion_verbose(
lt.span, lt.span,
&format!("{} trait object's {}", consider, explicit_static), &format!("{} trait object's {}", consider, explicit_static),
lifetime_name.clone(), lifetime_name.clone(),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
}
if let Some((param_span, param_ty)) = param.clone() {
err.span_suggestion_verbose( err.span_suggestion_verbose(
param.param_ty_span, param_span,
add_static_bound, add_static_bound,
param.param_ty.to_string(), param_ty,
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
} }
}
_ => {} _ => {}
}, },
_ => {} _ => {}
} }
} }
err.emit();
Some(ErrorReported)
} }
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
fn get_impl_ident_and_self_ty_from_trait( fn get_impl_ident_and_self_ty_from_trait(
&self, &self,
def_id: DefId, def_id: DefId,

View file

@ -5,6 +5,11 @@ LL | fn hide_ref<'a, 'b, T: 'static>(x: &'a mut &'b T) -> impl Swap + 'a {
| -- ^^^^^^^^^^^^^^ | -- ^^^^^^^^^^^^^^
| | | |
| hidden type `&'a mut &'b T` captures the lifetime `'b` as defined here | hidden type `&'a mut &'b T` captures the lifetime `'b` as defined here
|
help: to declare that the `impl Trait` captures 'b, you can add an explicit `'b` lifetime bound
|
LL | fn hide_ref<'a, 'b, T: 'static>(x: &'a mut &'b T) -> impl Swap + 'a + 'b {
| ++++
error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
--> $DIR/hidden-lifetimes.rs:45:70 --> $DIR/hidden-lifetimes.rs:45:70
@ -13,6 +18,11 @@ LL | fn hide_rc_refcell<'a, 'b: 'a, T: 'static>(x: Rc<RefCell<&'b T>>) -> impl S
| -- ^^^^^^^^^^^^^^ | -- ^^^^^^^^^^^^^^
| | | |
| hidden type `Rc<RefCell<&'b T>>` captures the lifetime `'b` as defined here | hidden type `Rc<RefCell<&'b T>>` captures the lifetime `'b` as defined here
|
help: to declare that the `impl Trait` captures 'b, you can add an explicit `'b` lifetime bound
|
LL | fn hide_rc_refcell<'a, 'b: 'a, T: 'static>(x: Rc<RefCell<&'b T>>) -> impl Swap + 'a + 'b {
| ++++
error: aborting due to 2 previous errors error: aborting due to 2 previous errors

View file

@ -6,6 +6,11 @@ LL | fn foo(x: Cell<&'x u32>) -> impl Trait<'y>
LL | LL |
LL | where 'x: 'y LL | where 'x: 'y
| -- hidden type `Cell<&'x u32>` captures the lifetime `'x` as defined here | -- hidden type `Cell<&'x u32>` captures the lifetime `'x` as defined here
|
help: to declare that the `impl Trait` captures 'x, you can add an explicit `'x` lifetime bound
|
LL | fn foo(x: Cell<&'x u32>) -> impl Trait<'y> + 'x
| ++++
error: aborting due to previous error error: aborting due to previous error