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_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 = 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_with_sub = the lifetime `{$sub_symbol}` defined here...
|
||||||
infer_prlf_defined_without_sub = the lifetime 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)
|
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
|
.label = type must be known at this point
|
||||||
|
|
||||||
infer_types_declared_different = these two types are declared with different lifetimes...
|
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_copy_predicates = copy the `where` clause predicates from the trait
|
||||||
|
|
||||||
infer_where_remove = remove the `where` clause
|
infer_where_remove = remove the `where` clause
|
||||||
|
@ -1269,9 +1269,13 @@ fn suggest_precise_capturing<'tcx>(
|
|||||||
captured_lifetime: ty::Region<'tcx>,
|
captured_lifetime: ty::Region<'tcx>,
|
||||||
diag: &mut Diag<'_>,
|
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();
|
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());
|
let new_lifetime = Symbol::intern(&captured_lifetime.to_string());
|
||||||
|
|
||||||
if let Some((args, span)) = bounds.iter().find_map(|bound| match bound {
|
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 variances = tcx.variances_of(opaque_def_id);
|
||||||
let mut generics = tcx.generics_of(opaque_def_id);
|
let mut generics = tcx.generics_of(opaque_def_id);
|
||||||
|
let mut synthetics = vec![];
|
||||||
loop {
|
loop {
|
||||||
for param in &generics.own_params {
|
for param in &generics.own_params {
|
||||||
if variances[param.index as usize] == ty::Bivariant {
|
if variances[param.index as usize] == ty::Bivariant {
|
||||||
@ -1317,9 +1322,7 @@ fn suggest_precise_capturing<'tcx>(
|
|||||||
captured_lifetimes.insert(param.name);
|
captured_lifetimes.insert(param.name);
|
||||||
}
|
}
|
||||||
ty::GenericParamDefKind::Type { synthetic: true, .. } => {
|
ty::GenericParamDefKind::Type { synthetic: true, .. } => {
|
||||||
// FIXME: We can't provide a good suggestion for
|
synthetics.push((tcx.def_span(param.def_id), param.name));
|
||||||
// `use<...>` if we have an APIT. Bail for now.
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
ty::GenericParamDefKind::Type { .. }
|
ty::GenericParamDefKind::Type { .. }
|
||||||
| ty::GenericParamDefKind::Const { .. } => {
|
| ty::GenericParamDefKind::Const { .. } => {
|
||||||
@ -1340,6 +1343,7 @@ fn suggest_precise_capturing<'tcx>(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if synthetics.is_empty() {
|
||||||
let concatenated_bounds = captured_lifetimes
|
let concatenated_bounds = captured_lifetimes
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(captured_non_lifetimes)
|
.chain(captured_non_lifetimes)
|
||||||
@ -1352,5 +1356,73 @@ fn suggest_precise_capturing<'tcx>(
|
|||||||
new_lifetime,
|
new_lifetime,
|
||||||
concatenated_bounds,
|
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,
|
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
|
//~^ 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() {}
|
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> {
|
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`.
|
For more information about this error, try `rustc --explain E0700`.
|
||||||
|
Loading…
Reference in New Issue
Block a user