Skip to content

Commit 54c11a6

Browse files
committed
better suggestion based on hir
Signed-off-by: Alex Chi <[email protected]>
1 parent 6e17349 commit 54c11a6

File tree

6 files changed

+200
-45
lines changed

6 files changed

+200
-45
lines changed

Diff for: compiler/rustc_infer/src/infer/error_reporting/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1927,7 +1927,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
19271927
{
19281928
let span = self.tcx.def_span(def_id);
19291929
diag.span_note(span, "this closure does not fulfill the lifetime requirements");
1930-
self.suggest_for_all_lifetime_closure(span, &exp_found, diag);
1930+
self.suggest_for_all_lifetime_closure(span, self.tcx.hir().get_by_def_id(def_id), &exp_found, diag);
19311931
}
19321932

19331933
// It reads better to have the error origin as the final

Diff for: compiler/rustc_infer/src/infer/error_reporting/suggest.rs

+48-40
Original file line numberDiff line numberDiff line change
@@ -542,54 +542,62 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
542542
pub(super) fn suggest_for_all_lifetime_closure(
543543
&self,
544544
span: Span,
545+
hir: hir::Node<'_>,
545546
exp_found: &ty::error::ExpectedFound<ty::PolyTraitRef<'tcx>>,
546547
diag: &mut Diagnostic,
547548
) {
549+
// 0. Extract fn_decl from hir
550+
let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(hir::Closure { fn_decl, .. }), .. }) = hir else { return; };
551+
548552
// 1. Get the substs of the closure.
549553
// 2. Assume exp_found is FnOnce / FnMut / Fn, we can extract function parameters from [1].
550-
let expected = exp_found.expected.map_bound(|x| x.substs.get(1).cloned()).transpose();
551-
let found = exp_found.found.map_bound(|x| x.substs.get(1).cloned()).transpose();
552-
554+
let Some(expected) = exp_found.expected.skip_binder().substs.get(1) else { return; };
555+
let Some(found) = exp_found.found.skip_binder().substs.get(1) else { return; };
556+
let expected = expected.unpack();
557+
let found = found.unpack();
553558
// 3. Extract the tuple type from Fn trait and suggest the change.
554-
if let (Some(expected), Some(found)) = (expected, found) {
555-
let expected = expected.skip_binder().unpack();
556-
let found = found.skip_binder().unpack();
557-
if let (GenericArgKind::Type(expected), GenericArgKind::Type(found)) = (expected, found)
558-
&& let (ty::Tuple(expected), ty::Tuple(found)) = (expected.kind(), found.kind())
559-
&& expected.len() == found.len() {
560-
let mut suggestion = "|".to_string();
561-
let mut is_first = true;
562-
let mut has_suggestion = false;
563-
564-
for (expected, found) in expected.iter().zip(found.iter()) {
565-
if is_first {
566-
is_first = true;
567-
} else {
568-
suggestion += ", ";
569-
}
559+
if let GenericArgKind::Type(expected) = expected &&
560+
let GenericArgKind::Type(found) = found &&
561+
let ty::Tuple(expected) = expected.kind() &&
562+
let ty::Tuple(found)= found.kind() &&
563+
expected.len() == found.len() {
564+
let mut suggestion = "|".to_string();
565+
let mut is_first = true;
566+
let mut has_suggestion = false;
567+
568+
for ((expected, found), arg_hir) in expected.iter().zip(found.iter()).zip(fn_decl.inputs.iter()) {
569+
if is_first {
570+
is_first = false;
571+
} else {
572+
suggestion += ", ";
573+
}
570574

571-
if let (ty::Ref(expected_region, _, _), ty::Ref(found_region, _, _)) = (expected.kind(), found.kind())
572-
&& expected_region.is_late_bound() && !found_region.is_late_bound() {
573-
// If the expected region is late bound, and the found region is not, we can suggest adding `: &_`.
574-
// FIXME: use the actual type + variable name provided by user instead of `_`.
575-
suggestion += "_: &_";
576-
has_suggestion = true;
577-
} else {
578-
// Otherwise, keep it as-is.
579-
suggestion += "_";
580-
}
581-
}
582-
suggestion += "|";
583-
584-
if has_suggestion {
585-
diag.span_suggestion_verbose(
586-
span,
587-
"consider changing the type of the closure parameters",
588-
suggestion,
589-
Applicability::MaybeIncorrect,
590-
);
591-
}
575+
if let ty::Ref(expected_region, _, _) = expected.kind() &&
576+
let ty::Ref(found_region, _, _) = found.kind() &&
577+
expected_region.is_late_bound() &&
578+
!found_region.is_late_bound() &&
579+
let hir::TyKind::Infer = arg_hir.kind {
580+
// If the expected region is late bound, the found region is not, and users are asking compiler
581+
// to infer the type, we can suggest adding `: &_`.
582+
let Ok(arg) = self.tcx.sess.source_map().span_to_snippet(arg_hir.span) else { return; };
583+
suggestion += &format!("{}: &_", arg);
584+
has_suggestion = true;
585+
} else {
586+
let Ok(arg) = self.tcx.sess.source_map().span_to_snippet(arg_hir.span) else { return; };
587+
// Otherwise, keep it as-is.
588+
suggestion += &arg;
592589
}
590+
}
591+
suggestion += "|";
592+
593+
if has_suggestion {
594+
diag.span_suggestion_verbose(
595+
span,
596+
"consider specifying the type of the closure parameters",
597+
suggestion,
598+
Applicability::MaybeIncorrect,
599+
);
600+
}
593601
}
594602
}
595603
}

Diff for: tests/ui/lifetimes/issue-105675.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
fn thing(x: impl FnOnce(&u32, &u32)) {}
2+
3+
fn main() {
4+
let f = |_, _| ();
5+
thing(f);
6+
//~^ ERROR mismatched types
7+
//~^^ ERROR mismatched types
8+
//~^^^ ERROR implementation of `FnOnce` is not general enough
9+
//~^^^^ ERROR implementation of `FnOnce` is not general enough
10+
let f = |x, y| ();
11+
thing(f);
12+
//~^ ERROR mismatched types
13+
//~^^ ERROR mismatched types
14+
//~^^^ ERROR implementation of `FnOnce` is not general enough
15+
//~^^^^ ERROR implementation of `FnOnce` is not general enough
16+
}

Diff for: tests/ui/lifetimes/issue-105675.stderr

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/issue-105675.rs:5:5
3+
|
4+
LL | thing(f);
5+
| ^^^^^^^^ one type is more general than the other
6+
|
7+
= note: expected trait `for<'a, 'b> FnOnce<(&'a u32, &'b u32)>`
8+
found trait `FnOnce<(&u32, &u32)>`
9+
note: this closure does not fulfill the lifetime requirements
10+
--> $DIR/issue-105675.rs:4:13
11+
|
12+
LL | let f = |_, _| ();
13+
| ^^^^^^
14+
note: the lifetime requirement is introduced here
15+
--> $DIR/issue-105675.rs:1:18
16+
|
17+
LL | fn thing(x: impl FnOnce(&u32, &u32)) {}
18+
| ^^^^^^^^^^^^^^^^^^
19+
help: consider specifying the type of the closure parameters
20+
|
21+
LL | let f = |_: &_, _: &_| ();
22+
| ~~~~~~~~~~~~~~
23+
24+
error[E0308]: mismatched types
25+
--> $DIR/issue-105675.rs:5:5
26+
|
27+
LL | thing(f);
28+
| ^^^^^^^^ one type is more general than the other
29+
|
30+
= note: expected trait `for<'a, 'b> FnOnce<(&'a u32, &'b u32)>`
31+
found trait `FnOnce<(&u32, &u32)>`
32+
note: this closure does not fulfill the lifetime requirements
33+
--> $DIR/issue-105675.rs:4:13
34+
|
35+
LL | let f = |_, _| ();
36+
| ^^^^^^
37+
note: the lifetime requirement is introduced here
38+
--> $DIR/issue-105675.rs:1:18
39+
|
40+
LL | fn thing(x: impl FnOnce(&u32, &u32)) {}
41+
| ^^^^^^^^^^^^^^^^^^
42+
help: consider specifying the type of the closure parameters
43+
|
44+
LL | let f = |_: &_, _: &_| ();
45+
| ~~~~~~~~~~~~~~
46+
47+
error: implementation of `FnOnce` is not general enough
48+
--> $DIR/issue-105675.rs:5:5
49+
|
50+
LL | thing(f);
51+
| ^^^^^^^^ implementation of `FnOnce` is not general enough
52+
|
53+
= note: closure with signature `fn(&'2 u32, &u32)` must implement `FnOnce<(&'1 u32, &u32)>`, for any lifetime `'1`...
54+
= note: ...but it actually implements `FnOnce<(&'2 u32, &u32)>`, for some specific lifetime `'2`
55+
56+
error: implementation of `FnOnce` is not general enough
57+
--> $DIR/issue-105675.rs:5:5
58+
|
59+
LL | thing(f);
60+
| ^^^^^^^^ implementation of `FnOnce` is not general enough
61+
|
62+
= note: closure with signature `fn(&u32, &'2 u32)` must implement `FnOnce<(&u32, &'1 u32)>`, for any lifetime `'1`...
63+
= note: ...but it actually implements `FnOnce<(&u32, &'2 u32)>`, for some specific lifetime `'2`
64+
65+
error[E0308]: mismatched types
66+
--> $DIR/issue-105675.rs:11:5
67+
|
68+
LL | thing(f);
69+
| ^^^^^^^^ one type is more general than the other
70+
|
71+
= note: expected trait `for<'a, 'b> FnOnce<(&'a u32, &'b u32)>`
72+
found trait `FnOnce<(&u32, &u32)>`
73+
note: this closure does not fulfill the lifetime requirements
74+
--> $DIR/issue-105675.rs:10:13
75+
|
76+
LL | let f = |x, y| ();
77+
| ^^^^^^
78+
note: the lifetime requirement is introduced here
79+
--> $DIR/issue-105675.rs:1:18
80+
|
81+
LL | fn thing(x: impl FnOnce(&u32, &u32)) {}
82+
| ^^^^^^^^^^^^^^^^^^
83+
help: consider specifying the type of the closure parameters
84+
|
85+
LL | let f = |x: &_, y: &_| ();
86+
| ~~~~~~~~~~~~~~
87+
88+
error[E0308]: mismatched types
89+
--> $DIR/issue-105675.rs:11:5
90+
|
91+
LL | thing(f);
92+
| ^^^^^^^^ one type is more general than the other
93+
|
94+
= note: expected trait `for<'a, 'b> FnOnce<(&'a u32, &'b u32)>`
95+
found trait `FnOnce<(&u32, &u32)>`
96+
note: this closure does not fulfill the lifetime requirements
97+
--> $DIR/issue-105675.rs:10:13
98+
|
99+
LL | let f = |x, y| ();
100+
| ^^^^^^
101+
note: the lifetime requirement is introduced here
102+
--> $DIR/issue-105675.rs:1:18
103+
|
104+
LL | fn thing(x: impl FnOnce(&u32, &u32)) {}
105+
| ^^^^^^^^^^^^^^^^^^
106+
help: consider specifying the type of the closure parameters
107+
|
108+
LL | let f = |x: &_, y: &_| ();
109+
| ~~~~~~~~~~~~~~
110+
111+
error: implementation of `FnOnce` is not general enough
112+
--> $DIR/issue-105675.rs:11:5
113+
|
114+
LL | thing(f);
115+
| ^^^^^^^^ implementation of `FnOnce` is not general enough
116+
|
117+
= note: closure with signature `fn(&'2 u32, &u32)` must implement `FnOnce<(&'1 u32, &u32)>`, for any lifetime `'1`...
118+
= note: ...but it actually implements `FnOnce<(&'2 u32, &u32)>`, for some specific lifetime `'2`
119+
120+
error: implementation of `FnOnce` is not general enough
121+
--> $DIR/issue-105675.rs:11:5
122+
|
123+
LL | thing(f);
124+
| ^^^^^^^^ implementation of `FnOnce` is not general enough
125+
|
126+
= note: closure with signature `fn(&u32, &'2 u32)` must implement `FnOnce<(&u32, &'1 u32)>`, for any lifetime `'1`...
127+
= note: ...but it actually implements `FnOnce<(&u32, &'2 u32)>`, for some specific lifetime `'2`
128+
129+
error: aborting due to 8 previous errors
130+
131+
For more information about this error, try `rustc --explain E0308`.

Diff for: tests/ui/lifetimes/issue-79187.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ note: the lifetime requirement is introduced here
1616
|
1717
LL | fn thing(x: impl FnOnce(&u32)) {}
1818
| ^^^^^^^^^^^^
19-
help: consider changing the type of the closure parameters
19+
help: consider specifying the type of the closure parameters
2020
|
2121
LL | let f = |_: &_| ();
2222
| ~~~~~~~

Diff for: tests/ui/mismatched_types/closure-mismatch.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ note: the lifetime requirement is introduced here
2525
|
2626
LL | fn baz<T: Foo>(_: T) {}
2727
| ^^^
28-
help: consider changing the type of the closure parameters
28+
help: consider specifying the type of the closure parameters
2929
|
3030
LL | baz(|_: &_| ());
3131
| ~~~~~~~
@@ -57,9 +57,9 @@ note: the lifetime requirement is introduced here
5757
|
5858
LL | fn baz<T: Foo>(_: T) {}
5959
| ^^^
60-
help: consider changing the type of the closure parameters
60+
help: consider specifying the type of the closure parameters
6161
|
62-
LL | baz(|_: &_| ());
62+
LL | baz(|x: &_| ());
6363
| ~~~~~~~
6464

6565
error: aborting due to 4 previous errors

0 commit comments

Comments
 (0)