better suggestion based on hir
Signed-off-by: Alex Chi <iskyzh@gmail.com>
This commit is contained in:
parent
6e17349b12
commit
54c11a688f
@ -1927,7 +1927,7 @@ enum Similar<'tcx> {
|
|||||||
{
|
{
|
||||||
let span = self.tcx.def_span(def_id);
|
let span = self.tcx.def_span(def_id);
|
||||||
diag.span_note(span, "this closure does not fulfill the lifetime requirements");
|
diag.span_note(span, "this closure does not fulfill the lifetime requirements");
|
||||||
self.suggest_for_all_lifetime_closure(span, &exp_found, diag);
|
self.suggest_for_all_lifetime_closure(span, self.tcx.hir().get_by_def_id(def_id), &exp_found, diag);
|
||||||
}
|
}
|
||||||
|
|
||||||
// It reads better to have the error origin as the final
|
// It reads better to have the error origin as the final
|
||||||
|
@ -542,54 +542,62 @@ fn visit_body(&mut self, body: &'v hir::Body<'v>) {
|
|||||||
pub(super) fn suggest_for_all_lifetime_closure(
|
pub(super) fn suggest_for_all_lifetime_closure(
|
||||||
&self,
|
&self,
|
||||||
span: Span,
|
span: Span,
|
||||||
|
hir: hir::Node<'_>,
|
||||||
exp_found: &ty::error::ExpectedFound<ty::PolyTraitRef<'tcx>>,
|
exp_found: &ty::error::ExpectedFound<ty::PolyTraitRef<'tcx>>,
|
||||||
diag: &mut Diagnostic,
|
diag: &mut Diagnostic,
|
||||||
) {
|
) {
|
||||||
|
// 0. Extract fn_decl from hir
|
||||||
|
let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(hir::Closure { fn_decl, .. }), .. }) = hir else { return; };
|
||||||
|
|
||||||
// 1. Get the substs of the closure.
|
// 1. Get the substs of the closure.
|
||||||
// 2. Assume exp_found is FnOnce / FnMut / Fn, we can extract function parameters from [1].
|
// 2. Assume exp_found is FnOnce / FnMut / Fn, we can extract function parameters from [1].
|
||||||
let expected = exp_found.expected.map_bound(|x| x.substs.get(1).cloned()).transpose();
|
let Some(expected) = exp_found.expected.skip_binder().substs.get(1) else { return; };
|
||||||
let found = exp_found.found.map_bound(|x| x.substs.get(1).cloned()).transpose();
|
let Some(found) = exp_found.found.skip_binder().substs.get(1) else { return; };
|
||||||
|
let expected = expected.unpack();
|
||||||
|
let found = found.unpack();
|
||||||
// 3. Extract the tuple type from Fn trait and suggest the change.
|
// 3. Extract the tuple type from Fn trait and suggest the change.
|
||||||
if let (Some(expected), Some(found)) = (expected, found) {
|
if let GenericArgKind::Type(expected) = expected &&
|
||||||
let expected = expected.skip_binder().unpack();
|
let GenericArgKind::Type(found) = found &&
|
||||||
let found = found.skip_binder().unpack();
|
let ty::Tuple(expected) = expected.kind() &&
|
||||||
if let (GenericArgKind::Type(expected), GenericArgKind::Type(found)) = (expected, found)
|
let ty::Tuple(found)= found.kind() &&
|
||||||
&& let (ty::Tuple(expected), ty::Tuple(found)) = (expected.kind(), found.kind())
|
expected.len() == found.len() {
|
||||||
&& expected.len() == found.len() {
|
let mut suggestion = "|".to_string();
|
||||||
let mut suggestion = "|".to_string();
|
let mut is_first = true;
|
||||||
let mut is_first = true;
|
let mut has_suggestion = false;
|
||||||
let mut has_suggestion = false;
|
|
||||||
|
|
||||||
for (expected, found) in expected.iter().zip(found.iter()) {
|
for ((expected, found), arg_hir) in expected.iter().zip(found.iter()).zip(fn_decl.inputs.iter()) {
|
||||||
if is_first {
|
if is_first {
|
||||||
is_first = true;
|
is_first = false;
|
||||||
} else {
|
} else {
|
||||||
suggestion += ", ";
|
suggestion += ", ";
|
||||||
}
|
|
||||||
|
|
||||||
if let (ty::Ref(expected_region, _, _), ty::Ref(found_region, _, _)) = (expected.kind(), found.kind())
|
|
||||||
&& expected_region.is_late_bound() && !found_region.is_late_bound() {
|
|
||||||
// If the expected region is late bound, and the found region is not, we can suggest adding `: &_`.
|
|
||||||
// FIXME: use the actual type + variable name provided by user instead of `_`.
|
|
||||||
suggestion += "_: &_";
|
|
||||||
has_suggestion = true;
|
|
||||||
} else {
|
|
||||||
// Otherwise, keep it as-is.
|
|
||||||
suggestion += "_";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
suggestion += "|";
|
|
||||||
|
|
||||||
if has_suggestion {
|
|
||||||
diag.span_suggestion_verbose(
|
|
||||||
span,
|
|
||||||
"consider changing the type of the closure parameters",
|
|
||||||
suggestion,
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let ty::Ref(expected_region, _, _) = expected.kind() &&
|
||||||
|
let ty::Ref(found_region, _, _) = found.kind() &&
|
||||||
|
expected_region.is_late_bound() &&
|
||||||
|
!found_region.is_late_bound() &&
|
||||||
|
let hir::TyKind::Infer = arg_hir.kind {
|
||||||
|
// If the expected region is late bound, the found region is not, and users are asking compiler
|
||||||
|
// to infer the type, we can suggest adding `: &_`.
|
||||||
|
let Ok(arg) = self.tcx.sess.source_map().span_to_snippet(arg_hir.span) else { return; };
|
||||||
|
suggestion += &format!("{}: &_", arg);
|
||||||
|
has_suggestion = true;
|
||||||
|
} else {
|
||||||
|
let Ok(arg) = self.tcx.sess.source_map().span_to_snippet(arg_hir.span) else { return; };
|
||||||
|
// Otherwise, keep it as-is.
|
||||||
|
suggestion += &arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
suggestion += "|";
|
||||||
|
|
||||||
|
if has_suggestion {
|
||||||
|
diag.span_suggestion_verbose(
|
||||||
|
span,
|
||||||
|
"consider specifying the type of the closure parameters",
|
||||||
|
suggestion,
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
16
tests/ui/lifetimes/issue-105675.rs
Normal file
16
tests/ui/lifetimes/issue-105675.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
fn thing(x: impl FnOnce(&u32, &u32)) {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let f = |_, _| ();
|
||||||
|
thing(f);
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
//~^^ ERROR mismatched types
|
||||||
|
//~^^^ ERROR implementation of `FnOnce` is not general enough
|
||||||
|
//~^^^^ ERROR implementation of `FnOnce` is not general enough
|
||||||
|
let f = |x, y| ();
|
||||||
|
thing(f);
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
//~^^ ERROR mismatched types
|
||||||
|
//~^^^ ERROR implementation of `FnOnce` is not general enough
|
||||||
|
//~^^^^ ERROR implementation of `FnOnce` is not general enough
|
||||||
|
}
|
131
tests/ui/lifetimes/issue-105675.stderr
Normal file
131
tests/ui/lifetimes/issue-105675.stderr
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/issue-105675.rs:5:5
|
||||||
|
|
|
||||||
|
LL | thing(f);
|
||||||
|
| ^^^^^^^^ one type is more general than the other
|
||||||
|
|
|
||||||
|
= note: expected trait `for<'a, 'b> FnOnce<(&'a u32, &'b u32)>`
|
||||||
|
found trait `FnOnce<(&u32, &u32)>`
|
||||||
|
note: this closure does not fulfill the lifetime requirements
|
||||||
|
--> $DIR/issue-105675.rs:4:13
|
||||||
|
|
|
||||||
|
LL | let f = |_, _| ();
|
||||||
|
| ^^^^^^
|
||||||
|
note: the lifetime requirement is introduced here
|
||||||
|
--> $DIR/issue-105675.rs:1:18
|
||||||
|
|
|
||||||
|
LL | fn thing(x: impl FnOnce(&u32, &u32)) {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^
|
||||||
|
help: consider specifying the type of the closure parameters
|
||||||
|
|
|
||||||
|
LL | let f = |_: &_, _: &_| ();
|
||||||
|
| ~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/issue-105675.rs:5:5
|
||||||
|
|
|
||||||
|
LL | thing(f);
|
||||||
|
| ^^^^^^^^ one type is more general than the other
|
||||||
|
|
|
||||||
|
= note: expected trait `for<'a, 'b> FnOnce<(&'a u32, &'b u32)>`
|
||||||
|
found trait `FnOnce<(&u32, &u32)>`
|
||||||
|
note: this closure does not fulfill the lifetime requirements
|
||||||
|
--> $DIR/issue-105675.rs:4:13
|
||||||
|
|
|
||||||
|
LL | let f = |_, _| ();
|
||||||
|
| ^^^^^^
|
||||||
|
note: the lifetime requirement is introduced here
|
||||||
|
--> $DIR/issue-105675.rs:1:18
|
||||||
|
|
|
||||||
|
LL | fn thing(x: impl FnOnce(&u32, &u32)) {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^
|
||||||
|
help: consider specifying the type of the closure parameters
|
||||||
|
|
|
||||||
|
LL | let f = |_: &_, _: &_| ();
|
||||||
|
| ~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
error: implementation of `FnOnce` is not general enough
|
||||||
|
--> $DIR/issue-105675.rs:5:5
|
||||||
|
|
|
||||||
|
LL | thing(f);
|
||||||
|
| ^^^^^^^^ implementation of `FnOnce` is not general enough
|
||||||
|
|
|
||||||
|
= note: closure with signature `fn(&'2 u32, &u32)` must implement `FnOnce<(&'1 u32, &u32)>`, for any lifetime `'1`...
|
||||||
|
= note: ...but it actually implements `FnOnce<(&'2 u32, &u32)>`, for some specific lifetime `'2`
|
||||||
|
|
||||||
|
error: implementation of `FnOnce` is not general enough
|
||||||
|
--> $DIR/issue-105675.rs:5:5
|
||||||
|
|
|
||||||
|
LL | thing(f);
|
||||||
|
| ^^^^^^^^ implementation of `FnOnce` is not general enough
|
||||||
|
|
|
||||||
|
= note: closure with signature `fn(&u32, &'2 u32)` must implement `FnOnce<(&u32, &'1 u32)>`, for any lifetime `'1`...
|
||||||
|
= note: ...but it actually implements `FnOnce<(&u32, &'2 u32)>`, for some specific lifetime `'2`
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/issue-105675.rs:11:5
|
||||||
|
|
|
||||||
|
LL | thing(f);
|
||||||
|
| ^^^^^^^^ one type is more general than the other
|
||||||
|
|
|
||||||
|
= note: expected trait `for<'a, 'b> FnOnce<(&'a u32, &'b u32)>`
|
||||||
|
found trait `FnOnce<(&u32, &u32)>`
|
||||||
|
note: this closure does not fulfill the lifetime requirements
|
||||||
|
--> $DIR/issue-105675.rs:10:13
|
||||||
|
|
|
||||||
|
LL | let f = |x, y| ();
|
||||||
|
| ^^^^^^
|
||||||
|
note: the lifetime requirement is introduced here
|
||||||
|
--> $DIR/issue-105675.rs:1:18
|
||||||
|
|
|
||||||
|
LL | fn thing(x: impl FnOnce(&u32, &u32)) {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^
|
||||||
|
help: consider specifying the type of the closure parameters
|
||||||
|
|
|
||||||
|
LL | let f = |x: &_, y: &_| ();
|
||||||
|
| ~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/issue-105675.rs:11:5
|
||||||
|
|
|
||||||
|
LL | thing(f);
|
||||||
|
| ^^^^^^^^ one type is more general than the other
|
||||||
|
|
|
||||||
|
= note: expected trait `for<'a, 'b> FnOnce<(&'a u32, &'b u32)>`
|
||||||
|
found trait `FnOnce<(&u32, &u32)>`
|
||||||
|
note: this closure does not fulfill the lifetime requirements
|
||||||
|
--> $DIR/issue-105675.rs:10:13
|
||||||
|
|
|
||||||
|
LL | let f = |x, y| ();
|
||||||
|
| ^^^^^^
|
||||||
|
note: the lifetime requirement is introduced here
|
||||||
|
--> $DIR/issue-105675.rs:1:18
|
||||||
|
|
|
||||||
|
LL | fn thing(x: impl FnOnce(&u32, &u32)) {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^
|
||||||
|
help: consider specifying the type of the closure parameters
|
||||||
|
|
|
||||||
|
LL | let f = |x: &_, y: &_| ();
|
||||||
|
| ~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
error: implementation of `FnOnce` is not general enough
|
||||||
|
--> $DIR/issue-105675.rs:11:5
|
||||||
|
|
|
||||||
|
LL | thing(f);
|
||||||
|
| ^^^^^^^^ implementation of `FnOnce` is not general enough
|
||||||
|
|
|
||||||
|
= note: closure with signature `fn(&'2 u32, &u32)` must implement `FnOnce<(&'1 u32, &u32)>`, for any lifetime `'1`...
|
||||||
|
= note: ...but it actually implements `FnOnce<(&'2 u32, &u32)>`, for some specific lifetime `'2`
|
||||||
|
|
||||||
|
error: implementation of `FnOnce` is not general enough
|
||||||
|
--> $DIR/issue-105675.rs:11:5
|
||||||
|
|
|
||||||
|
LL | thing(f);
|
||||||
|
| ^^^^^^^^ implementation of `FnOnce` is not general enough
|
||||||
|
|
|
||||||
|
= note: closure with signature `fn(&u32, &'2 u32)` must implement `FnOnce<(&u32, &'1 u32)>`, for any lifetime `'1`...
|
||||||
|
= note: ...but it actually implements `FnOnce<(&u32, &'2 u32)>`, for some specific lifetime `'2`
|
||||||
|
|
||||||
|
error: aborting due to 8 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
@ -16,7 +16,7 @@ note: the lifetime requirement is introduced here
|
|||||||
|
|
|
|
||||||
LL | fn thing(x: impl FnOnce(&u32)) {}
|
LL | fn thing(x: impl FnOnce(&u32)) {}
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
help: consider changing the type of the closure parameters
|
help: consider specifying the type of the closure parameters
|
||||||
|
|
|
|
||||||
LL | let f = |_: &_| ();
|
LL | let f = |_: &_| ();
|
||||||
| ~~~~~~~
|
| ~~~~~~~
|
||||||
|
@ -25,7 +25,7 @@ note: the lifetime requirement is introduced here
|
|||||||
|
|
|
|
||||||
LL | fn baz<T: Foo>(_: T) {}
|
LL | fn baz<T: Foo>(_: T) {}
|
||||||
| ^^^
|
| ^^^
|
||||||
help: consider changing the type of the closure parameters
|
help: consider specifying the type of the closure parameters
|
||||||
|
|
|
|
||||||
LL | baz(|_: &_| ());
|
LL | baz(|_: &_| ());
|
||||||
| ~~~~~~~
|
| ~~~~~~~
|
||||||
@ -57,9 +57,9 @@ note: the lifetime requirement is introduced here
|
|||||||
|
|
|
|
||||||
LL | fn baz<T: Foo>(_: T) {}
|
LL | fn baz<T: Foo>(_: T) {}
|
||||||
| ^^^
|
| ^^^
|
||||||
help: consider changing the type of the closure parameters
|
help: consider specifying the type of the closure parameters
|
||||||
|
|
|
|
||||||
LL | baz(|_: &_| ());
|
LL | baz(|x: &_| ());
|
||||||
| ~~~~~~~
|
| ~~~~~~~
|
||||||
|
|
||||||
error: aborting due to 4 previous errors
|
error: aborting due to 4 previous errors
|
||||||
|
Loading…
Reference in New Issue
Block a user