From 54c11a688f38a979e789fa50592088654ed56680 Mon Sep 17 00:00:00 2001 From: Alex Chi Date: Fri, 17 Mar 2023 22:22:49 -0400 Subject: [PATCH] better suggestion based on hir Signed-off-by: Alex Chi --- .../src/infer/error_reporting/mod.rs | 2 +- .../src/infer/error_reporting/suggest.rs | 88 ++++++------ tests/ui/lifetimes/issue-105675.rs | 16 +++ tests/ui/lifetimes/issue-105675.stderr | 131 ++++++++++++++++++ tests/ui/lifetimes/issue-79187.stderr | 2 +- .../mismatched_types/closure-mismatch.stderr | 6 +- 6 files changed, 200 insertions(+), 45 deletions(-) create mode 100644 tests/ui/lifetimes/issue-105675.rs create mode 100644 tests/ui/lifetimes/issue-105675.stderr diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 72184277222..992b07db154 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1927,7 +1927,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { { let span = self.tcx.def_span(def_id); 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 diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index 3755d80874c..ec0aa77cef3 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -542,54 +542,62 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { pub(super) fn suggest_for_all_lifetime_closure( &self, span: Span, + hir: hir::Node<'_>, exp_found: &ty::error::ExpectedFound>, 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. // 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 found = exp_found.found.map_bound(|x| x.substs.get(1).cloned()).transpose(); - + let Some(expected) = exp_found.expected.skip_binder().substs.get(1) else { return; }; + 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. - if let (Some(expected), Some(found)) = (expected, found) { - let expected = expected.skip_binder().unpack(); - let found = found.skip_binder().unpack(); - if let (GenericArgKind::Type(expected), GenericArgKind::Type(found)) = (expected, found) - && let (ty::Tuple(expected), ty::Tuple(found)) = (expected.kind(), found.kind()) - && expected.len() == found.len() { - let mut suggestion = "|".to_string(); - let mut is_first = true; - let mut has_suggestion = false; + if let GenericArgKind::Type(expected) = expected && + let GenericArgKind::Type(found) = found && + let ty::Tuple(expected) = expected.kind() && + let ty::Tuple(found)= found.kind() && + expected.len() == found.len() { + let mut suggestion = "|".to_string(); + let mut is_first = true; + let mut has_suggestion = false; - for (expected, found) in expected.iter().zip(found.iter()) { - if is_first { - is_first = true; - } else { - 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, - ); - } + for ((expected, found), arg_hir) in expected.iter().zip(found.iter()).zip(fn_decl.inputs.iter()) { + if is_first { + is_first = false; + } else { + suggestion += ", "; } + + 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, + ); + } } } } diff --git a/tests/ui/lifetimes/issue-105675.rs b/tests/ui/lifetimes/issue-105675.rs new file mode 100644 index 00000000000..a01fbef4b3b --- /dev/null +++ b/tests/ui/lifetimes/issue-105675.rs @@ -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 +} diff --git a/tests/ui/lifetimes/issue-105675.stderr b/tests/ui/lifetimes/issue-105675.stderr new file mode 100644 index 00000000000..3d8172ad14a --- /dev/null +++ b/tests/ui/lifetimes/issue-105675.stderr @@ -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`. diff --git a/tests/ui/lifetimes/issue-79187.stderr b/tests/ui/lifetimes/issue-79187.stderr index 16eaf654cf3..209f2b7b739 100644 --- a/tests/ui/lifetimes/issue-79187.stderr +++ b/tests/ui/lifetimes/issue-79187.stderr @@ -16,7 +16,7 @@ note: the lifetime requirement is introduced here | 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 = |_: &_| (); | ~~~~~~~ diff --git a/tests/ui/mismatched_types/closure-mismatch.stderr b/tests/ui/mismatched_types/closure-mismatch.stderr index ab0e137418a..c5b8270ba84 100644 --- a/tests/ui/mismatched_types/closure-mismatch.stderr +++ b/tests/ui/mismatched_types/closure-mismatch.stderr @@ -25,7 +25,7 @@ note: the lifetime requirement is introduced here | LL | fn baz(_: T) {} | ^^^ -help: consider changing the type of the closure parameters +help: consider specifying the type of the closure parameters | LL | baz(|_: &_| ()); | ~~~~~~~ @@ -57,9 +57,9 @@ note: the lifetime requirement is introduced here | LL | fn baz(_: 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