Fix precise capturing suggestion for hidden type when APITs are involved
This commit is contained in:
parent
3de0a7c716
commit
1d40d4c4f4
@ -225,6 +225,8 @@ infer_outlives_content = lifetime of reference outlives lifetime of borrowed con
|
||||
infer_precise_capturing_existing = add `{$new_lifetime}` to the `use<...>` bound to explicitly capture it
|
||||
infer_precise_capturing_new = add a `use<...>` bound to explicitly capture `{$new_lifetime}`
|
||||
|
||||
infer_precise_capturing_new_but_apit = add a `use<...>` bound to explicitly capture `{$new_lifetime}` after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate
|
||||
|
||||
infer_prlf_defined_with_sub = the lifetime `{$sub_symbol}` defined here...
|
||||
infer_prlf_defined_without_sub = the lifetime defined here...
|
||||
infer_prlf_known_limitation = this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
|
||||
@ -387,6 +389,9 @@ infer_type_annotations_needed = {$source_kind ->
|
||||
.label = type must be known at this point
|
||||
|
||||
infer_types_declared_different = these two types are declared with different lifetimes...
|
||||
|
||||
infer_warn_removing_apit_params = you could use a `use<...>` bound to explicitly capture `{$new_lifetime}`, but argument-position `impl Trait`s are not nameable
|
||||
|
||||
infer_where_copy_predicates = copy the `where` clause predicates from the trait
|
||||
|
||||
infer_where_remove = remove the `where` clause
|
||||
|
@ -1269,9 +1269,13 @@ fn suggest_precise_capturing<'tcx>(
|
||||
captured_lifetime: ty::Region<'tcx>,
|
||||
diag: &mut Diag<'_>,
|
||||
) {
|
||||
let hir::OpaqueTy { bounds, .. } =
|
||||
let hir::OpaqueTy { bounds, origin, .. } =
|
||||
tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty();
|
||||
|
||||
let hir::OpaqueTyOrigin::FnReturn(fn_def_id) = *origin else {
|
||||
return;
|
||||
};
|
||||
|
||||
let new_lifetime = Symbol::intern(&captured_lifetime.to_string());
|
||||
|
||||
if let Some((args, span)) = bounds.iter().find_map(|bound| match bound {
|
||||
@ -1306,6 +1310,7 @@ fn suggest_precise_capturing<'tcx>(
|
||||
|
||||
let variances = tcx.variances_of(opaque_def_id);
|
||||
let mut generics = tcx.generics_of(opaque_def_id);
|
||||
let mut synthetics = vec![];
|
||||
loop {
|
||||
for param in &generics.own_params {
|
||||
if variances[param.index as usize] == ty::Bivariant {
|
||||
@ -1317,9 +1322,7 @@ fn suggest_precise_capturing<'tcx>(
|
||||
captured_lifetimes.insert(param.name);
|
||||
}
|
||||
ty::GenericParamDefKind::Type { synthetic: true, .. } => {
|
||||
// FIXME: We can't provide a good suggestion for
|
||||
// `use<...>` if we have an APIT. Bail for now.
|
||||
return;
|
||||
synthetics.push((tcx.def_span(param.def_id), param.name));
|
||||
}
|
||||
ty::GenericParamDefKind::Type { .. }
|
||||
| ty::GenericParamDefKind::Const { .. } => {
|
||||
@ -1340,6 +1343,7 @@ fn suggest_precise_capturing<'tcx>(
|
||||
return;
|
||||
}
|
||||
|
||||
if synthetics.is_empty() {
|
||||
let concatenated_bounds = captured_lifetimes
|
||||
.into_iter()
|
||||
.chain(captured_non_lifetimes)
|
||||
@ -1352,5 +1356,73 @@ fn suggest_precise_capturing<'tcx>(
|
||||
new_lifetime,
|
||||
concatenated_bounds,
|
||||
});
|
||||
} else {
|
||||
let mut next_fresh_param = || {
|
||||
["T", "U", "V", "W", "X", "Y", "A", "B", "C"]
|
||||
.into_iter()
|
||||
.map(Symbol::intern)
|
||||
.chain((0..).map(|i| Symbol::intern(&format!("T{i}"))))
|
||||
.find(|s| captured_non_lifetimes.insert(*s))
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let mut new_params = String::new();
|
||||
let mut suggs = vec![];
|
||||
let mut apit_spans = vec![];
|
||||
|
||||
for (i, (span, name)) in synthetics.into_iter().enumerate() {
|
||||
apit_spans.push(span);
|
||||
|
||||
let fresh_param = next_fresh_param();
|
||||
|
||||
// Suggest renaming.
|
||||
suggs.push((span, fresh_param.to_string()));
|
||||
|
||||
// Super jank. Turn `impl Trait` into `T: Trait`.
|
||||
//
|
||||
// This currently involves stripping the `impl` from the name of
|
||||
// the parameter, since APITs are always named after how they are
|
||||
// rendered in the AST. This sucks! But to recreate the bound list
|
||||
// from the APIT itself would be miserable, so we're stuck with
|
||||
// this for now!
|
||||
if i > 0 {
|
||||
new_params += ", ";
|
||||
}
|
||||
let name_as_bounds = name.as_str().trim_start_matches("impl").trim_start();
|
||||
new_params += fresh_param.as_str();
|
||||
new_params += ": ";
|
||||
new_params += name_as_bounds;
|
||||
}
|
||||
|
||||
let Some(generics) = tcx.hir().get_generics(fn_def_id) else {
|
||||
// This shouldn't happen, but don't ICE.
|
||||
return;
|
||||
};
|
||||
|
||||
// Add generics or concatenate to the end of the list.
|
||||
suggs.push(if let Some(params_span) = generics.span_for_param_suggestion() {
|
||||
(params_span, format!(", {new_params}"))
|
||||
} else {
|
||||
(generics.span, format!("<{new_params}>"))
|
||||
});
|
||||
|
||||
let concatenated_bounds = captured_lifetimes
|
||||
.into_iter()
|
||||
.chain(captured_non_lifetimes)
|
||||
.map(|sym| sym.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
suggs.push((
|
||||
tcx.def_span(opaque_def_id).shrink_to_hi(),
|
||||
format!(" + use<{concatenated_bounds}>"),
|
||||
));
|
||||
|
||||
diag.subdiagnostic(errors::AddPreciseCapturingAndParams {
|
||||
suggs,
|
||||
new_lifetime,
|
||||
apit_spans,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1609,3 +1609,25 @@ pub enum AddPreciseCapturing {
|
||||
post: &'static str,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct AddPreciseCapturingAndParams {
|
||||
pub suggs: Vec<(Span, String)>,
|
||||
pub new_lifetime: Symbol,
|
||||
pub apit_spans: Vec<Span>,
|
||||
}
|
||||
|
||||
impl Subdiagnostic for AddPreciseCapturingAndParams {
|
||||
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
|
||||
self,
|
||||
diag: &mut Diag<'_, G>,
|
||||
_f: &F,
|
||||
) {
|
||||
diag.arg("new_lifetime", self.new_lifetime);
|
||||
diag.multipart_suggestion_verbose(
|
||||
fluent::infer_precise_capturing_new_but_apit,
|
||||
self.suggs,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
diag.span_note(self.apit_spans, fluent::infer_warn_removing_apit_params);
|
||||
}
|
||||
}
|
||||
|
@ -27,4 +27,16 @@ fn missing<'a, 'captured, 'not_captured, Captured>(x: &'a ()) -> impl Captures<'
|
||||
//~^ ERROR hidden type for
|
||||
}
|
||||
|
||||
fn no_params_yet(_: impl Sized, y: &()) -> impl Sized {
|
||||
//~^ HELP add a `use<...>` bound
|
||||
y
|
||||
//~^ ERROR hidden type for
|
||||
}
|
||||
|
||||
fn yes_params_yet<'a, T>(_: impl Sized, y: &'a ()) -> impl Sized {
|
||||
//~^ HELP add a `use<...>` bound
|
||||
y
|
||||
//~^ ERROR hidden type for
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -62,6 +62,48 @@ help: add a `use<...>` bound to explicitly capture `'a`
|
||||
LL | fn missing<'a, 'captured, 'not_captured, Captured>(x: &'a ()) -> impl Captures<'captured> + use<'captured, 'a, Captured> {
|
||||
| ++++++++++++++++++++++++++++++
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds
|
||||
--> $DIR/hidden-type-suggestion.rs:32:5
|
||||
|
|
||||
LL | fn no_params_yet(_: impl Sized, y: &()) -> impl Sized {
|
||||
| --- ---------- opaque type defined here
|
||||
| |
|
||||
| hidden type `&()` captures the anonymous lifetime defined here
|
||||
LL |
|
||||
LL | y
|
||||
| ^
|
||||
|
|
||||
note: you could use a `use<...>` bound to explicitly capture `'_`, but argument-position `impl Trait`s are not nameable
|
||||
--> $DIR/hidden-type-suggestion.rs:30:21
|
||||
|
|
||||
LL | fn no_params_yet(_: impl Sized, y: &()) -> impl Sized {
|
||||
| ^^^^^^^^^^
|
||||
help: add a `use<...>` bound to explicitly capture `'_` after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate
|
||||
|
|
||||
LL | fn no_params_yet<T: Sized>(_: T, y: &()) -> impl Sized + use<'_, T> {
|
||||
| ++++++++++ ~ ++++++++++++
|
||||
|
||||
error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds
|
||||
--> $DIR/hidden-type-suggestion.rs:38:5
|
||||
|
|
||||
LL | fn yes_params_yet<'a, T>(_: impl Sized, y: &'a ()) -> impl Sized {
|
||||
| -- ---------- opaque type defined here
|
||||
| |
|
||||
| hidden type `&'a ()` captures the lifetime `'a` as defined here
|
||||
LL |
|
||||
LL | y
|
||||
| ^
|
||||
|
|
||||
note: you could use a `use<...>` bound to explicitly capture `'a`, but argument-position `impl Trait`s are not nameable
|
||||
--> $DIR/hidden-type-suggestion.rs:36:29
|
||||
|
|
||||
LL | fn yes_params_yet<'a, T>(_: impl Sized, y: &'a ()) -> impl Sized {
|
||||
| ^^^^^^^^^^
|
||||
help: add a `use<...>` bound to explicitly capture `'a` after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate
|
||||
|
|
||||
LL | fn yes_params_yet<'a, T, U: Sized>(_: U, y: &'a ()) -> impl Sized + use<'a, T, U> {
|
||||
| ++++++++++ ~ +++++++++++++++
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0700`.
|
||||
|
Loading…
Reference in New Issue
Block a user