From dfe32b6a432523d29550dc302b297028ca01e8f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 11 Nov 2023 04:32:32 +0000 Subject: [PATCH 1/2] On Fn arg mismatch for a fn path, suggest a closure When encountering a fn call that has a path to another fn being passed in, where an `Fn` impl is expected, and the arguments differ, suggest wrapping the argument with a closure with the appropriate arguments. --- .../src/traits/error_reporting/suggestions.rs | 178 ++++++++++++++++-- .../bugs/issue-88382.stderr | 4 + .../intrinsics/const-eval-select-bad.stderr | 4 + tests/ui/mismatched_types/E0631.stderr | 8 + .../closure-ref-114180.stderr | 4 + .../ui/mismatched_types/fn-variance-1.stderr | 8 + ...uggest-option-asderef-inference-var.stderr | 4 + .../suggest-option-asderef-unfixable.stderr | 8 + .../suggest-option-asderef.fixed | 39 ---- .../suggest-option-asderef.rs | 6 +- .../suggest-option-asderef.stderr | 22 ++- .../unboxed-closures-vtable-mismatch.stderr | 4 + .../enum-variant-arg-mismatch.stderr | 4 + .../late-bound-in-borrow-closure-sugg.stderr | 4 + .../typeck/mismatched-map-under-self.stderr | 4 + 15 files changed, 247 insertions(+), 54 deletions(-) delete mode 100644 tests/ui/mismatched_types/suggest-option-asderef.fixed diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index e4cb99727fc30..2c4a0782e60be 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -222,6 +222,15 @@ pub trait TypeErrCtxtExt<'tcx> { param_env: ty::ParamEnv<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; + fn note_conflicting_fn_args( + &self, + err: &mut Diagnostic, + cause: &ObligationCauseCode<'tcx>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ); + fn note_conflicting_closure_bounds( &self, cause: &ObligationCauseCode<'tcx>, @@ -2005,6 +2014,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let signature_kind = format!("{argument_kind} signature"); err.note_expected_found(&signature_kind, expected_str, &signature_kind, found_str); + self.note_conflicting_fn_args(&mut err, cause, expected, found, param_env); self.note_conflicting_closure_bounds(cause, &mut err); if let Some(found_node) = found_node { @@ -2014,6 +2024,152 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err } + fn note_conflicting_fn_args( + &self, + err: &mut Diagnostic, + cause: &ObligationCauseCode<'tcx>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) { + let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } = cause else { + return; + }; + let ty::FnPtr(expected) = expected.kind() else { + return; + }; + let ty::FnPtr(found) = found.kind() else { + return; + }; + let Some(Node::Expr(arg)) = self.tcx.hir().find(*arg_hir_id) else { + return; + }; + let hir::ExprKind::Path(path) = arg.kind else { + return; + }; + let expected_inputs = self.tcx.erase_late_bound_regions(*expected).inputs(); + let found_inputs = self.tcx.erase_late_bound_regions(*found).inputs(); + let both_tys = expected_inputs.iter().cloned().zip(found_inputs.iter().cloned()); + + let arg_expr = |infcx: &InferCtxt<'tcx>, name, expected: Ty<'tcx>, found: Ty<'tcx>| { + let (expected_ty, expected_refs) = get_deref_type_and_refs(expected); + let (found_ty, found_refs) = get_deref_type_and_refs(found); + + if infcx.can_eq(param_env, found_ty, expected_ty) { + if found_refs.len() == expected_refs.len() + && found_refs.iter().zip(expected_refs.iter()).all(|(e, f)| e == f) + { + name + } else if found_refs.len() > expected_refs.len() { + if found_refs[..found_refs.len() - expected_refs.len()] + .iter() + .zip(expected_refs.iter()) + .any(|(e, f)| e != f) + { + // The refs have different mutability. + format!( + "{}*{name}", + found_refs[..found_refs.len() - expected_refs.len()] + .iter() + .map(|mutbl| format!("&{}", mutbl.prefix_str())) + .collect::>() + .join(""), + ) + } else { + format!( + "{}{name}", + found_refs[..found_refs.len() - expected_refs.len()] + .iter() + .map(|mutbl| format!("&{}", mutbl.prefix_str())) + .collect::>() + .join(""), + ) + } + } else if expected_refs.len() > found_refs.len() { + format!( + "{}{name}", + (0..(expected_refs.len() - found_refs.len())) + .map(|_| "*") + .collect::>() + .join(""), + ) + } else { + format!( + "{}{name}", + found_refs + .iter() + .map(|mutbl| format!("&{}", mutbl.prefix_str())) + .chain(found_refs.iter().map(|_| "*".to_string())) + .collect::>() + .join(""), + ) + } + } else { + format!("/* {found} */") + } + }; + let (closure_names, call_names): (Vec<_>, Vec<_>) = + if both_tys.clone().all(|(expected, found)| { + let (expected_ty, _) = get_deref_type_and_refs(expected); + let (found_ty, _) = get_deref_type_and_refs(found); + self.can_eq(param_env, found_ty, expected_ty) + }) && !expected_inputs.is_empty() + && expected_inputs.len() == found_inputs.len() + && let hir::QPath::Resolved(_, path) = path + && let hir::def::Res::Def(_, fn_def_id) = path.res + && let Some(node) = self.tcx.hir().get_if_local(fn_def_id) + && let Some(body_id) = node.body_id() + { + let closure = self + .tcx + .hir() + .body_param_names(body_id) + .map(|name| format!("{name}")) + .collect(); + let args = self + .tcx + .hir() + .body_param_names(body_id) + .zip(both_tys) + .map(|(name, (expected, found))| { + arg_expr(self.infcx, format!("{name}"), expected, found) + }) + .collect(); + (closure, args) + } else { + let closure_args = expected_inputs + .iter() + .enumerate() + .map(|(i, _)| format!("arg{i}")) + .collect::>(); + let call_args = both_tys + .enumerate() + .map(|(i, (expected, found))| { + arg_expr(self.infcx, format!("arg{i}"), expected, found) + }) + .collect::>(); + (closure_args, call_args) + }; + let closure_names: Vec<_> = closure_names + .into_iter() + .zip(expected_inputs.iter()) + .map(|(name, ty)| { + format!( + "{name}{}", + if ty.has_infer_types() { String::new() } else { format!(": {ty}") } + ) + }) + .collect(); + err.multipart_suggestion( + format!("consider wrapping the function in a closure"), + vec![ + (arg.span.shrink_to_lo(), format!("|{}| ", closure_names.join(", "))), + (arg.span.shrink_to_hi(), format!("({})", call_names.join(", "))), + ], + Applicability::MaybeIncorrect, + ); + } + // Add a note if there are two `Fn`-family bounds that have conflicting argument // requirements, which will always cause a closure to have a type error. fn note_conflicting_closure_bounds( @@ -4349,17 +4505,6 @@ fn hint_missing_borrow<'tcx>( let args = fn_decl.inputs.iter(); - fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec) { - let mut refs = vec![]; - - while let ty::Ref(_, new_ty, mutbl) = ty.kind() { - ty = *new_ty; - refs.push(*mutbl); - } - - (ty, refs) - } - let mut to_borrow = Vec::new(); let mut remove_borrow = Vec::new(); @@ -4652,3 +4797,14 @@ pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>( Some(sugg) } + +fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec) { + let mut refs = vec![]; + + while let ty::Ref(_, new_ty, mutbl) = ty.kind() { + ty = *new_ty; + refs.push(*mutbl); + } + + (ty, refs) +} diff --git a/tests/ui/generic-associated-types/bugs/issue-88382.stderr b/tests/ui/generic-associated-types/bugs/issue-88382.stderr index 624fe2799e060..9b061528e3beb 100644 --- a/tests/ui/generic-associated-types/bugs/issue-88382.stderr +++ b/tests/ui/generic-associated-types/bugs/issue-88382.stderr @@ -16,6 +16,10 @@ note: required by a bound in `do_something` | LL | fn do_something(i: I, mut f: impl for<'a> Fn(&mut I::Iterator<'a>)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `do_something` +help: consider wrapping the function in a closure + | +LL | do_something(SomeImplementation(), |arg0: &mut std::iter::Empty| test(/* &mut <_ as Iterable>::Iterator<'_> */)); + | ++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/intrinsics/const-eval-select-bad.stderr b/tests/ui/intrinsics/const-eval-select-bad.stderr index e6ff9d5a0df7d..bf91dc72cc14d 100644 --- a/tests/ui/intrinsics/const-eval-select-bad.stderr +++ b/tests/ui/intrinsics/const-eval-select-bad.stderr @@ -86,6 +86,10 @@ LL | const_eval_select((true,), foo, baz); found function signature `fn(i32) -> _` note: required by a bound in `const_eval_select` --> $SRC_DIR/core/src/intrinsics.rs:LL:COL +help: consider wrapping the function in a closure + | +LL | const_eval_select((true,), |arg0: bool| foo(/* i32 */), baz); + | ++++++++++++ +++++++++++ error: this argument must be a `const fn` --> $DIR/const-eval-select-bad.rs:42:29 diff --git a/tests/ui/mismatched_types/E0631.stderr b/tests/ui/mismatched_types/E0631.stderr index 410ea4b0b3439..9ba8f5035c5f6 100644 --- a/tests/ui/mismatched_types/E0631.stderr +++ b/tests/ui/mismatched_types/E0631.stderr @@ -48,6 +48,10 @@ note: required by a bound in `foo` | LL | fn foo(_: F) {} | ^^^^^^^^^ required by this bound in `foo` +help: consider wrapping the function in a closure + | +LL | foo(|arg0: usize| f(/* u64 */)); + | +++++++++++++ +++++++++++ error[E0631]: type mismatch in function arguments --> $DIR/E0631.rs:10:9 @@ -67,6 +71,10 @@ note: required by a bound in `bar` | LL | fn bar>(_: F) {} | ^^^^^^^^^^^^ required by this bound in `bar` +help: consider wrapping the function in a closure + | +LL | bar(|arg0: usize| f(/* u64 */)); + | +++++++++++++ +++++++++++ error: aborting due to 4 previous errors diff --git a/tests/ui/mismatched_types/closure-ref-114180.stderr b/tests/ui/mismatched_types/closure-ref-114180.stderr index 798c4e00aa7b0..27649822e694f 100644 --- a/tests/ui/mismatched_types/closure-ref-114180.stderr +++ b/tests/ui/mismatched_types/closure-ref-114180.stderr @@ -12,6 +12,10 @@ LL | v.sort_by(compare); found closure signature `fn((_,), (_,)) -> _` note: required by a bound in `slice::::sort_by` --> $SRC_DIR/alloc/src/slice.rs:LL:COL +help: consider wrapping the function in a closure + | +LL | v.sort_by(|arg0, arg1| compare(*arg0, *arg1)); + | ++++++++++++ ++++++++++++++ help: consider adjusting the signature so it borrows its arguments | LL | let compare = |&(a,), &(e,)| todo!(); diff --git a/tests/ui/mismatched_types/fn-variance-1.stderr b/tests/ui/mismatched_types/fn-variance-1.stderr index 5794e606eeb93..fdb2e6f0097a8 100644 --- a/tests/ui/mismatched_types/fn-variance-1.stderr +++ b/tests/ui/mismatched_types/fn-variance-1.stderr @@ -16,6 +16,10 @@ note: required by a bound in `apply` | LL | fn apply(t: T, f: F) where F: FnOnce(T) { | ^^^^^^^^^ required by this bound in `apply` +help: consider wrapping the function in a closure + | +LL | apply(&3, |x| takes_mut(&mut *x)); + | +++ +++++++++ error[E0631]: type mismatch in function arguments --> $DIR/fn-variance-1.rs:15:19 @@ -35,6 +39,10 @@ note: required by a bound in `apply` | LL | fn apply(t: T, f: F) where F: FnOnce(T) { | ^^^^^^^^^ required by this bound in `apply` +help: consider wrapping the function in a closure + | +LL | apply(&mut 3, |x| takes_imm(&*x)); + | +++ +++++ error: aborting due to 2 previous errors diff --git a/tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr b/tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr index 3db9803d58f70..0ed57466e9c58 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr +++ b/tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr @@ -13,6 +13,10 @@ LL | let _has_inference_vars: Option = Some(0).map(deref_int); found function signature `for<'a> fn(&'a i32) -> _` note: required by a bound in `Option::::map` --> $SRC_DIR/core/src/option.rs:LL:COL +help: consider wrapping the function in a closure + | +LL | let _has_inference_vars: Option = Some(0).map(|a| deref_int(&a)); + | +++ ++++ help: consider adjusting the signature so it does not borrow its argument | LL - fn deref_int(a: &i32) -> i32 { diff --git a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr index 3175a258906fe..1ac057a5f382f 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr +++ b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr @@ -13,6 +13,10 @@ LL | let _ = produces_string().and_then(takes_str_but_too_many_refs); found function signature `for<'a, 'b> fn(&'a &'b str) -> _` note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL +help: consider wrapping the function in a closure + | +LL | let _ = produces_string().and_then(|arg0: String| takes_str_but_too_many_refs(/* &&str */)); + | ++++++++++++++ +++++++++++++ error[E0277]: expected a `FnOnce(String)` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` --> $DIR/suggest-option-asderef-unfixable.rs:26:40 @@ -68,6 +72,10 @@ LL | let _ = Some(TypeWithoutDeref).and_then(takes_str_but_too_many_refs); found function signature `for<'a, 'b> fn(&'a &'b str) -> _` note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL +help: consider wrapping the function in a closure + | +LL | let _ = Some(TypeWithoutDeref).and_then(|arg0: TypeWithoutDeref| takes_str_but_too_many_refs(/* &&str */)); + | ++++++++++++++++++++++++ +++++++++++++ error: aborting due to 5 previous errors diff --git a/tests/ui/mismatched_types/suggest-option-asderef.fixed b/tests/ui/mismatched_types/suggest-option-asderef.fixed deleted file mode 100644 index fc488b790b3d8..0000000000000 --- a/tests/ui/mismatched_types/suggest-option-asderef.fixed +++ /dev/null @@ -1,39 +0,0 @@ -// run-rustfix - -fn produces_string() -> Option { - Some("my cool string".to_owned()) -} - -fn takes_str(_: &str) -> Option<()> { - Some(()) -} - -fn takes_str_mut(_: &mut str) -> Option<()> { - Some(()) -} - -fn generic(_: T) -> Option<()> { - Some(()) -} - -fn generic_ref(_: T) -> Option<()> { - //~^ HELP consider adjusting the signature so it does not borrow its argument - Some(()) -} - -fn main() { - let _: Option<()> = produces_string().as_deref().and_then(takes_str); - //~^ ERROR type mismatch in function arguments - //~| HELP call `Option::as_deref()` first - let _: Option> = produces_string().as_deref().map(takes_str); - //~^ ERROR type mismatch in function arguments - //~| HELP call `Option::as_deref()` first - let _: Option> = produces_string().as_deref_mut().map(takes_str_mut); - //~^ ERROR type mismatch in function arguments - //~| HELP call `Option::as_deref_mut()` first - let _ = produces_string().and_then(generic); - - let _ = produces_string().as_deref().and_then(generic_ref); - //~^ ERROR type mismatch in function arguments - //~| HELP call `Option::as_deref()` first -} diff --git a/tests/ui/mismatched_types/suggest-option-asderef.rs b/tests/ui/mismatched_types/suggest-option-asderef.rs index 28f46808a3923..5f5617e1741c4 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef.rs +++ b/tests/ui/mismatched_types/suggest-option-asderef.rs @@ -1,4 +1,4 @@ -// run-rustfix +// this isn't auto-fixable now because we produce two similar suggestions fn produces_string() -> Option { Some("my cool string".to_owned()) @@ -25,15 +25,19 @@ fn main() { let _: Option<()> = produces_string().and_then(takes_str); //~^ ERROR type mismatch in function arguments //~| HELP call `Option::as_deref()` first + //~| HELP consider wrapping the function in a closure let _: Option> = produces_string().map(takes_str); //~^ ERROR type mismatch in function arguments //~| HELP call `Option::as_deref()` first + //~| HELP consider wrapping the function in a closure let _: Option> = produces_string().map(takes_str_mut); //~^ ERROR type mismatch in function arguments //~| HELP call `Option::as_deref_mut()` first + //~| HELP consider wrapping the function in a closure let _ = produces_string().and_then(generic); let _ = produces_string().and_then(generic_ref); //~^ ERROR type mismatch in function arguments //~| HELP call `Option::as_deref()` first + //~| HELP consider wrapping the function in a closure } diff --git a/tests/ui/mismatched_types/suggest-option-asderef.stderr b/tests/ui/mismatched_types/suggest-option-asderef.stderr index bfea086735065..6c4a143e5af2d 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef.stderr +++ b/tests/ui/mismatched_types/suggest-option-asderef.stderr @@ -13,13 +13,17 @@ LL | let _: Option<()> = produces_string().and_then(takes_str); found function signature `for<'a> fn(&'a str) -> _` note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL +help: consider wrapping the function in a closure + | +LL | let _: Option<()> = produces_string().and_then(|arg0: String| takes_str(/* &str */)); + | ++++++++++++++ ++++++++++++ help: call `Option::as_deref()` first | LL | let _: Option<()> = produces_string().as_deref().and_then(takes_str); | +++++++++++ error[E0631]: type mismatch in function arguments - --> $DIR/suggest-option-asderef.rs:28:55 + --> $DIR/suggest-option-asderef.rs:29:55 | LL | fn takes_str(_: &str) -> Option<()> { | ----------------------------------- found signature defined here @@ -33,13 +37,17 @@ LL | let _: Option> = produces_string().map(takes_str); found function signature `for<'a> fn(&'a str) -> _` note: required by a bound in `Option::::map` --> $SRC_DIR/core/src/option.rs:LL:COL +help: consider wrapping the function in a closure + | +LL | let _: Option> = produces_string().map(|arg0: String| takes_str(/* &str */)); + | ++++++++++++++ ++++++++++++ help: call `Option::as_deref()` first | LL | let _: Option> = produces_string().as_deref().map(takes_str); | +++++++++++ error[E0631]: type mismatch in function arguments - --> $DIR/suggest-option-asderef.rs:31:55 + --> $DIR/suggest-option-asderef.rs:33:55 | LL | fn takes_str_mut(_: &mut str) -> Option<()> { | ------------------------------------------- found signature defined here @@ -53,13 +61,17 @@ LL | let _: Option> = produces_string().map(takes_str_mut); found function signature `for<'a> fn(&'a mut str) -> _` note: required by a bound in `Option::::map` --> $SRC_DIR/core/src/option.rs:LL:COL +help: consider wrapping the function in a closure + | +LL | let _: Option> = produces_string().map(|arg0: String| takes_str_mut(/* &mut str */)); + | ++++++++++++++ ++++++++++++++++ help: call `Option::as_deref_mut()` first | LL | let _: Option> = produces_string().as_deref_mut().map(takes_str_mut); | +++++++++++++++ error[E0631]: type mismatch in function arguments - --> $DIR/suggest-option-asderef.rs:36:40 + --> $DIR/suggest-option-asderef.rs:39:40 | LL | fn generic_ref(_: &T) -> Option<()> { | -------------------------------------- found signature defined here @@ -73,6 +85,10 @@ LL | let _ = produces_string().and_then(generic_ref); found function signature `for<'a> fn(&'a _) -> _` note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL +help: consider wrapping the function in a closure + | +LL | let _ = produces_string().and_then(|: String| generic_ref(&)); + | ++++++++++ +++ help: consider adjusting the signature so it does not borrow its argument | LL - fn generic_ref(_: &T) -> Option<()> { diff --git a/tests/ui/mismatched_types/unboxed-closures-vtable-mismatch.stderr b/tests/ui/mismatched_types/unboxed-closures-vtable-mismatch.stderr index a900a49c7105d..d92d5dbd16fad 100644 --- a/tests/ui/mismatched_types/unboxed-closures-vtable-mismatch.stderr +++ b/tests/ui/mismatched_types/unboxed-closures-vtable-mismatch.stderr @@ -16,6 +16,10 @@ note: required by a bound in `call_it` | LL | fn call_it isize>(y: isize, mut f: F) -> isize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it` +help: consider wrapping the function in a closure + | +LL | let z = call_it(3, |arg0: isize, arg1: isize| f(/* usize */, arg1)); + | ++++++++++++++++++++++++++ +++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/enum-variant-arg-mismatch.stderr b/tests/ui/suggestions/enum-variant-arg-mismatch.stderr index 6d72dabf70ecc..16f03d16d8c02 100644 --- a/tests/ui/suggestions/enum-variant-arg-mismatch.stderr +++ b/tests/ui/suggestions/enum-variant-arg-mismatch.stderr @@ -16,6 +16,10 @@ note: required by a bound in `map` | LL | fn map<'a, F: Fn(String) -> Sexpr<'a>>(f: F) {} | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `map` +help: consider wrapping the function in a closure + | +LL | map(|arg0: String| Sexpr::Ident(/* &str */)); + | ++++++++++++++ ++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/late-bound-in-borrow-closure-sugg.stderr b/tests/ui/suggestions/late-bound-in-borrow-closure-sugg.stderr index d78de6a460c9c..ee924522564a8 100644 --- a/tests/ui/suggestions/late-bound-in-borrow-closure-sugg.stderr +++ b/tests/ui/suggestions/late-bound-in-borrow-closure-sugg.stderr @@ -16,6 +16,10 @@ note: required by a bound in `Trader::<'a>::set_closure` | LL | pub fn set_closure(&mut self, function: impl Fn(&mut Trader) + 'a) { | ^^^^^^^^^^^^^^^ required by this bound in `Trader::<'a>::set_closure` +help: consider wrapping the function in a closure + | +LL | trader.set_closure(|arg0: &mut Trader<'_>| closure(*arg0)); + | +++++++++++++++++++++++ +++++++ help: consider adjusting the signature so it borrows its argument | LL | let closure = |trader : &mut Trader| { diff --git a/tests/ui/typeck/mismatched-map-under-self.stderr b/tests/ui/typeck/mismatched-map-under-self.stderr index 51491407c4938..41391720a28c5 100644 --- a/tests/ui/typeck/mismatched-map-under-self.stderr +++ b/tests/ui/typeck/mismatched-map-under-self.stderr @@ -30,6 +30,10 @@ LL | self.map(Insertable::values).unwrap_or_default() found function signature `for<'a> fn(&'a _) -> _` note: required by a bound in `Option::::map` --> $SRC_DIR/core/src/option.rs:LL:COL +help: consider wrapping the function in a closure + | +LL | self.map(|arg0: T| Insertable::values(&arg0)).unwrap_or_default() + | +++++++++ +++++++ error: aborting due to 2 previous errors From 25c75bc156a0491906c8f2bfa39d09eacb536a26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 29 Nov 2023 19:55:24 +0000 Subject: [PATCH 2/2] review comments and rebase fixes --- .../src/traits/error_reporting/suggestions.rs | 129 +++++++++--------- .../suggest-option-asderef.stderr | 4 +- 2 files changed, 66 insertions(+), 67 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 2c4a0782e60be..d5e1efb966326 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -17,7 +17,7 @@ use rustc_errors::{ ErrorGuaranteed, MultiSpan, Style, SuggestionStyle, }; use rustc_hir as hir; -use rustc_hir::def::DefKind; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::is_range_literal; @@ -36,7 +36,7 @@ use rustc_middle::ty::{ TypeSuperFoldable, TypeVisitableExt, TypeckResults, }; use rustc_span::def_id::LocalDefId; -use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span, DUMMY_SP}; use rustc_target::spec::abi; use std::borrow::Cow; @@ -1043,7 +1043,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else { return; }; - let hir::def::Res::Local(hir_id) = path.res else { + let Res::Local(hir_id) = path.res else { return; }; let Some(hir::Node::Pat(pat)) = self.tcx.hir().find(hir_id) else { @@ -1627,7 +1627,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind - && let hir::def::Res::Local(hir_id) = path.res + && let Res::Local(hir_id) = path.res && let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(hir_id) && let Some(hir::Node::Local(local)) = self.tcx.hir().find_parent(binding.hir_id) && let None = local.ty @@ -2047,9 +2047,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let hir::ExprKind::Path(path) = arg.kind else { return; }; - let expected_inputs = self.tcx.erase_late_bound_regions(*expected).inputs(); - let found_inputs = self.tcx.erase_late_bound_regions(*found).inputs(); - let both_tys = expected_inputs.iter().cloned().zip(found_inputs.iter().cloned()); + let expected_inputs = self.tcx.instantiate_bound_regions_with_erased(*expected).inputs(); + let found_inputs = self.tcx.instantiate_bound_regions_with_erased(*found).inputs(); + let both_tys = expected_inputs.iter().copied().zip(found_inputs.iter().copied()); let arg_expr = |infcx: &InferCtxt<'tcx>, name, expected: Ty<'tcx>, found: Ty<'tcx>| { let (expected_ty, expected_refs) = get_deref_type_and_refs(expected); @@ -2057,29 +2057,24 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if infcx.can_eq(param_env, found_ty, expected_ty) { if found_refs.len() == expected_refs.len() - && found_refs.iter().zip(expected_refs.iter()).all(|(e, f)| e == f) + && found_refs.iter().eq(expected_refs.iter()) { name } else if found_refs.len() > expected_refs.len() { - if found_refs[..found_refs.len() - expected_refs.len()] - .iter() - .zip(expected_refs.iter()) - .any(|(e, f)| e != f) - { - // The refs have different mutability. + let refs = &found_refs[..found_refs.len() - expected_refs.len()]; + if found_refs[..expected_refs.len()].iter().eq(expected_refs.iter()) { format!( - "{}*{name}", - found_refs[..found_refs.len() - expected_refs.len()] - .iter() + "{}{name}", + refs.iter() .map(|mutbl| format!("&{}", mutbl.prefix_str())) .collect::>() .join(""), ) } else { + // The refs have different mutability. format!( - "{}{name}", - found_refs[..found_refs.len() - expected_refs.len()] - .iter() + "{}*{name}", + refs.iter() .map(|mutbl| format!("&{}", mutbl.prefix_str())) .collect::>() .join(""), @@ -2108,48 +2103,52 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { format!("/* {found} */") } }; - let (closure_names, call_names): (Vec<_>, Vec<_>) = - if both_tys.clone().all(|(expected, found)| { - let (expected_ty, _) = get_deref_type_and_refs(expected); - let (found_ty, _) = get_deref_type_and_refs(found); - self.can_eq(param_env, found_ty, expected_ty) - }) && !expected_inputs.is_empty() - && expected_inputs.len() == found_inputs.len() - && let hir::QPath::Resolved(_, path) = path - && let hir::def::Res::Def(_, fn_def_id) = path.res - && let Some(node) = self.tcx.hir().get_if_local(fn_def_id) - && let Some(body_id) = node.body_id() - { - let closure = self - .tcx - .hir() - .body_param_names(body_id) - .map(|name| format!("{name}")) - .collect(); - let args = self - .tcx - .hir() - .body_param_names(body_id) - .zip(both_tys) - .map(|(name, (expected, found))| { - arg_expr(self.infcx, format!("{name}"), expected, found) - }) - .collect(); - (closure, args) - } else { - let closure_args = expected_inputs - .iter() - .enumerate() - .map(|(i, _)| format!("arg{i}")) - .collect::>(); - let call_args = both_tys - .enumerate() - .map(|(i, (expected, found))| { - arg_expr(self.infcx, format!("arg{i}"), expected, found) - }) - .collect::>(); - (closure_args, call_args) - }; + let args_have_same_underlying_type = both_tys.clone().all(|(expected, found)| { + let (expected_ty, _) = get_deref_type_and_refs(expected); + let (found_ty, _) = get_deref_type_and_refs(found); + self.can_eq(param_env, found_ty, expected_ty) + }); + let (closure_names, call_names): (Vec<_>, Vec<_>) = if args_have_same_underlying_type + && !expected_inputs.is_empty() + && expected_inputs.len() == found_inputs.len() + && let Some(typeck) = &self.typeck_results + && let Res::Def(_, fn_def_id) = typeck.qpath_res(&path, *arg_hir_id) + { + let closure: Vec<_> = self + .tcx + .fn_arg_names(fn_def_id) + .iter() + .enumerate() + .map(|(i, ident)| { + if ident.name.is_empty() || ident.name == kw::SelfLower { + format!("arg{i}") + } else { + format!("{ident}") + } + }) + .collect(); + let args = closure + .iter() + .zip(both_tys) + .map(|(name, (expected, found))| { + arg_expr(self.infcx, name.to_owned(), expected, found) + }) + .collect(); + (closure, args) + } else { + let closure_args = expected_inputs + .iter() + .enumerate() + .map(|(i, _)| format!("arg{i}")) + .collect::>(); + let call_args = both_tys + .enumerate() + .map(|(i, (expected, found))| { + arg_expr(self.infcx, format!("arg{i}"), expected, found) + }) + .collect::>(); + (closure_args, call_args) + }; let closure_names: Vec<_> = closure_names .into_iter() .zip(expected_inputs.iter()) @@ -3790,7 +3789,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind - && let hir::Path { res: hir::def::Res::Local(hir_id), .. } = path + && let hir::Path { res: Res::Local(hir_id), .. } = path && let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(*hir_id) && let parent_hir_id = self.tcx.hir().parent_id(binding.hir_id) && let Some(hir::Node::Local(local)) = self.tcx.hir().find(parent_hir_id) @@ -4050,7 +4049,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind - && let hir::Path { res: hir::def::Res::Local(hir_id), .. } = path + && let hir::Path { res: Res::Local(hir_id), .. } = path && let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(*hir_id) && let Some(parent) = self.tcx.hir().find_parent(binding.hir_id) { @@ -4664,7 +4663,7 @@ impl<'a, 'hir> hir::intravisit::Visitor<'hir> for ReplaceImplTraitVisitor<'a> { fn visit_ty(&mut self, t: &'hir hir::Ty<'hir>) { if let hir::TyKind::Path(hir::QPath::Resolved( None, - hir::Path { res: hir::def::Res::Def(_, segment_did), .. }, + hir::Path { res: Res::Def(_, segment_did), .. }, )) = t.kind { if self.param_did == *segment_did { diff --git a/tests/ui/mismatched_types/suggest-option-asderef.stderr b/tests/ui/mismatched_types/suggest-option-asderef.stderr index 6c4a143e5af2d..1702a7f1decf6 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef.stderr +++ b/tests/ui/mismatched_types/suggest-option-asderef.stderr @@ -87,8 +87,8 @@ note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL help: consider wrapping the function in a closure | -LL | let _ = produces_string().and_then(|: String| generic_ref(&)); - | ++++++++++ +++ +LL | let _ = produces_string().and_then(|arg0: String| generic_ref(&arg0)); + | ++++++++++++++ +++++++ help: consider adjusting the signature so it does not borrow its argument | LL - fn generic_ref(_: &T) -> Option<()> {