From 6e17349b12ee5c8c76c742f4be96f6eba60a8a93 Mon Sep 17 00:00:00 2001 From: Alex Chi Date: Sun, 12 Mar 2023 21:10:11 -0400 Subject: [PATCH] suggest lifetime for closure parameter type when mismatch --- .../src/infer/error_reporting/mod.rs | 1 + .../src/infer/error_reporting/suggest.rs | 58 ++++++++++++++++++- tests/ui/lifetimes/issue-79187-2.stderr | 4 ++ tests/ui/lifetimes/issue-79187.stderr | 4 ++ tests/ui/mismatched_types/closure-mismatch.rs | 3 + .../mismatched_types/closure-mismatch.stderr | 38 +++++++++++- 6 files changed, 106 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 9e5f6d107d1..72184277222 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1927,6 +1927,7 @@ enum Similar<'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); } // 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 b5aeca12a1f..3755d80874c 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -8,7 +8,7 @@ StatementAsExpression, }; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TypeVisitableExt}; +use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt}; use rustc_span::{sym, BytePos, Span}; use rustc_target::abi::FieldIdx; @@ -536,6 +536,62 @@ fn visit_body(&mut self, body: &'v hir::Body<'v>) { } None } + + /// For "one type is more general than the other" errors on closures, suggest changing the lifetime + /// of the parameters to accept all lifetimes. + pub(super) fn suggest_for_all_lifetime_closure( + &self, + span: Span, + exp_found: &ty::error::ExpectedFound>, + diag: &mut Diagnostic, + ) { + // 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(); + + // 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; + + 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, + ); + } + } + } + } } impl<'tcx> TypeErrCtxt<'_, 'tcx> { diff --git a/tests/ui/lifetimes/issue-79187-2.stderr b/tests/ui/lifetimes/issue-79187-2.stderr index c5f654b37bf..c14d3e0c61b 100644 --- a/tests/ui/lifetimes/issue-79187-2.stderr +++ b/tests/ui/lifetimes/issue-79187-2.stderr @@ -43,6 +43,10 @@ note: the lifetime requirement is introduced here | LL | fn take_foo(_: impl Foo) {} | ^^^ +help: consider changing the type of the closure parameters + | +LL | take_foo(|_: &_| a); + | ~~~~~~~ error[E0308]: mismatched types --> $DIR/issue-79187-2.rs:11:5 diff --git a/tests/ui/lifetimes/issue-79187.stderr b/tests/ui/lifetimes/issue-79187.stderr index ee6e7b89d5f..16eaf654cf3 100644 --- a/tests/ui/lifetimes/issue-79187.stderr +++ b/tests/ui/lifetimes/issue-79187.stderr @@ -16,6 +16,10 @@ note: the lifetime requirement is introduced here | LL | fn thing(x: impl FnOnce(&u32)) {} | ^^^^^^^^^^^^ +help: consider changing the type of the closure parameters + | +LL | let f = |_: &_| (); + | ~~~~~~~ error: implementation of `FnOnce` is not general enough --> $DIR/issue-79187.rs:5:5 diff --git a/tests/ui/mismatched_types/closure-mismatch.rs b/tests/ui/mismatched_types/closure-mismatch.rs index b0644e79611..4eb33497c39 100644 --- a/tests/ui/mismatched_types/closure-mismatch.rs +++ b/tests/ui/mismatched_types/closure-mismatch.rs @@ -8,4 +8,7 @@ fn main() { baz(|_| ()); //~^ ERROR implementation of `FnOnce` is not general enough //~| ERROR mismatched types + baz(|x| ()); + //~^ ERROR implementation of `FnOnce` is not general enough + //~| ERROR mismatched types } diff --git a/tests/ui/mismatched_types/closure-mismatch.stderr b/tests/ui/mismatched_types/closure-mismatch.stderr index a7ef8fa0892..ab0e137418a 100644 --- a/tests/ui/mismatched_types/closure-mismatch.stderr +++ b/tests/ui/mismatched_types/closure-mismatch.stderr @@ -25,7 +25,43 @@ note: the lifetime requirement is introduced here | LL | fn baz(_: T) {} | ^^^ +help: consider changing the type of the closure parameters + | +LL | baz(|_: &_| ()); + | ~~~~~~~ -error: aborting due to 2 previous errors +error: implementation of `FnOnce` is not general enough + --> $DIR/closure-mismatch.rs:11:5 + | +LL | baz(|x| ()); + | ^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'2 ())` must implement `FnOnce<(&'1 (),)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2` + +error[E0308]: mismatched types + --> $DIR/closure-mismatch.rs:11:5 + | +LL | baz(|x| ()); + | ^^^^^^^^^^^ one type is more general than the other + | + = note: expected trait `for<'a> Fn<(&'a (),)>` + found trait `Fn<(&(),)>` +note: this closure does not fulfill the lifetime requirements + --> $DIR/closure-mismatch.rs:11:9 + | +LL | baz(|x| ()); + | ^^^ +note: the lifetime requirement is introduced here + --> $DIR/closure-mismatch.rs:5:11 + | +LL | fn baz(_: T) {} + | ^^^ +help: consider changing the type of the closure parameters + | +LL | baz(|_: &_| ()); + | ~~~~~~~ + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0308`.