From 5fae66592494d37aa9b7c3add8c0e6077e0833f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 20 Mar 2024 22:50:32 +0000 Subject: [PATCH] Replace closures with `_` when suggesting fully qualified path for method call ``` error[E0283]: type annotations needed --> $DIR/into-inference-needs-type.rs:12:10 | LL | .into()?; | ^^^^ | = note: cannot satisfy `_: From<...>` = note: required for `FilterMap<...>` to implement `Into<_>` help: try using a fully qualified path to specify the expected types | LL ~ let list = , _>, _> as Into>::into(vec LL | .iter() LL | .map(|s| s.strip_prefix("t")) LL ~ .filter_map(Option::Some))?; | ``` Fix #122569. --- .../rustc_hir_analysis/src/astconv/bounds.rs | 2 +- compiler/rustc_hir_analysis/src/collect.rs | 6 +- .../rustc_hir_analysis/src/collect/type_of.rs | 6 +- .../src/fn_ctxt/suggestions.rs | 2 +- compiler/rustc_hir_typeck/src/op.rs | 4 +- .../infer/error_reporting/need_type_info.rs | 75 +++++++++++-------- compiler/rustc_middle/src/ty/diagnostics.rs | 28 +++++-- .../clippy/clippy_lints/src/box_default.rs | 4 +- .../types/into-inference-needs-type.rs | 15 ++++ .../types/into-inference-needs-type.stderr | 19 +++++ .../traits/suggest-fully-qualified-closure.rs | 2 - .../suggest-fully-qualified-closure.stderr | 8 +- 12 files changed, 118 insertions(+), 53 deletions(-) create mode 100644 tests/ui/suggestions/types/into-inference-needs-type.rs create mode 100644 tests/ui/suggestions/types/into-inference-needs-type.stderr diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs index 3067e2d0b71..7a5cb6e9d6f 100644 --- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs +++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs @@ -565,7 +565,7 @@ fn check_assoc_const_binding_type<'tcx>( let mut guar = ty.visit_with(&mut collector).break_value(); let ty_note = ty - .make_suggestable(tcx, false) + .make_suggestable(tcx, false, None) .map(|ty| crate::errors::TyOfAssocConstBindingNote { assoc_const, ty }); let enclosing_item_owner_id = tcx diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 5cd6862786b..d8bcc3a687e 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -1364,7 +1364,7 @@ fn infer_return_ty_for_fn_sig<'tcx>( // recursive function definition to leak out into the fn sig. let mut should_recover = false; - if let Some(ret_ty) = ret_ty.make_suggestable(tcx, false) { + if let Some(ret_ty) = ret_ty.make_suggestable(tcx, false, None) { diag.span_suggestion( ty.span, "replace with the correct return type", @@ -1442,7 +1442,7 @@ fn suggest_impl_trait<'tcx>( let ty::Tuple(types) = *args_tuple.kind() else { return None; }; - let types = types.make_suggestable(tcx, false)?; + let types = types.make_suggestable(tcx, false, None)?; let maybe_ret = if item_ty.is_unit() { String::new() } else { format!(" -> {item_ty}") }; Some(format!( @@ -1500,7 +1500,7 @@ fn suggest_impl_trait<'tcx>( // FIXME(compiler-errors): We may benefit from resolving regions here. if ocx.select_where_possible().is_empty() && let item_ty = infcx.resolve_vars_if_possible(item_ty) - && let Some(item_ty) = item_ty.make_suggestable(tcx, false) + && let Some(item_ty) = item_ty.make_suggestable(tcx, false, None) && let Some(sugg) = formatter( tcx, infcx.resolve_vars_if_possible(args), diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index fd86f2dd1b1..cec01abae4b 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -47,7 +47,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { let ty = tcx.fold_regions(ty, |r, _| { if r.is_erased() { ty::Region::new_error_misc(tcx) } else { r } }); - let (ty, opt_sugg) = if let Some(ty) = ty.make_suggestable(tcx, false) { + let (ty, opt_sugg) = if let Some(ty) = ty.make_suggestable(tcx, false, None) { (ty, Some((span, Applicability::MachineApplicable))) } else { (ty, None) @@ -587,7 +587,7 @@ fn infer_placeholder_type<'a>( suggestions.clear(); } - if let Some(ty) = ty.make_suggestable(tcx, false) { + if let Some(ty) = ty.make_suggestable(tcx, false, None) { err.span_suggestion( span, format!("provide a type for the {kind}"), @@ -606,7 +606,7 @@ fn infer_placeholder_type<'a>( let mut diag = bad_placeholder(tcx, vec![span], kind); if !ty.references_error() { - if let Some(ty) = ty.make_suggestable(tcx, false) { + if let Some(ty) = ty.make_suggestable(tcx, false, None) { diag.span_suggestion( span, "replace with the correct type", diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 3f6f4cccba7..f4090feee17 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -809,7 +809,7 @@ pub(in super::super) fn suggest_missing_return_type( return true; } &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => { - if let Some(found) = found.make_suggestable(self.tcx, false) { + if let Some(found) = found.make_suggestable(self.tcx, false, None) { err.subdiagnostic( self.dcx(), errors::AddReturnTypeSuggestion::Add { span, found: found.to_string() }, diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 79f574aa7fd..8969030d683 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -601,8 +601,8 @@ fn check_overloaded_binop( if let Some(output_def_id) = output_def_id && let Some(trait_def_id) = trait_def_id && self.tcx.parent(output_def_id) == trait_def_id - && let Some(output_ty) = - output_ty.make_suggestable(self.tcx, false) + && let Some(output_ty) = output_ty + .make_suggestable(self.tcx, false, None) { Some(("Output", output_ty)) } else { diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index 0686994b037..ce9001ccb1c 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -546,40 +546,55 @@ pub fn emit_inference_failure_err( } } InferSourceKind::FullyQualifiedMethodCall { receiver, successor, args, def_id } => { - let mut printer = fmt_printer(self, Namespace::ValueNS); - printer.print_def_path(def_id, args).unwrap(); - let def_path = printer.into_buffer(); + let placeholder = Some(self.next_ty_var(TypeVariableOrigin { + span: rustc_span::DUMMY_SP, + kind: TypeVariableOriginKind::MiscVariable, + })); + if let Some(args) = args.make_suggestable(self.infcx.tcx, true, placeholder) { + let mut printer = fmt_printer(self, Namespace::ValueNS); + printer.print_def_path(def_id, args).unwrap(); + let def_path = printer.into_buffer(); - // We only care about whether we have to add `&` or `&mut ` for now. - // This is the case if the last adjustment is a borrow and the - // first adjustment was not a builtin deref. - let adjustment = match typeck_results.expr_adjustments(receiver) { - [ - Adjustment { kind: Adjust::Deref(None), target: _ }, - .., - Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), target: _ }, - ] => "", - [ - .., - Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(_, mut_)), target: _ }, - ] => hir::Mutability::from(*mut_).ref_prefix_str(), - _ => "", - }; + // We only care about whether we have to add `&` or `&mut ` for now. + // This is the case if the last adjustment is a borrow and the + // first adjustment was not a builtin deref. + let adjustment = match typeck_results.expr_adjustments(receiver) { + [ + Adjustment { kind: Adjust::Deref(None), target: _ }, + .., + Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), target: _ }, + ] => "", + [ + .., + Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(_, mut_)), + target: _, + }, + ] => hir::Mutability::from(*mut_).ref_prefix_str(), + _ => "", + }; - multi_suggestions.push(SourceKindMultiSuggestion::new_fully_qualified( - receiver.span, - def_path, - adjustment, - successor, - )); + multi_suggestions.push(SourceKindMultiSuggestion::new_fully_qualified( + receiver.span, + def_path, + adjustment, + successor, + )); + } } InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => { - let ty_info = ty_to_string(self, ty, None); - multi_suggestions.push(SourceKindMultiSuggestion::new_closure_return( - ty_info, - data, - should_wrap_expr, - )); + let placeholder = Some(self.next_ty_var(TypeVariableOrigin { + span: rustc_span::DUMMY_SP, + kind: TypeVariableOriginKind::MiscVariable, + })); + if let Some(ty) = ty.make_suggestable(self.infcx.tcx, true, placeholder) { + let ty_info = ty_to_string(self, ty, None); + multi_suggestions.push(SourceKindMultiSuggestion::new_closure_return( + ty_info, + data, + should_wrap_expr, + )); + } } } match error_code { diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 05463b8554f..ee18647cdd8 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -91,7 +91,12 @@ pub trait IsSuggestable<'tcx>: Sized { /// inference variables to be suggestable. fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool; - fn make_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> Option; + fn make_suggestable( + self, + tcx: TyCtxt<'tcx>, + infer_suggestable: bool, + placeholder: Option>, + ) -> Option; } impl<'tcx, T> IsSuggestable<'tcx> for T @@ -103,8 +108,13 @@ fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool { self.visit_with(&mut IsSuggestableVisitor { tcx, infer_suggestable }).is_continue() } - fn make_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> Option { - self.try_fold_with(&mut MakeSuggestableFolder { tcx, infer_suggestable }).ok() + fn make_suggestable( + self, + tcx: TyCtxt<'tcx>, + infer_suggestable: bool, + placeholder: Option>, + ) -> Option { + self.try_fold_with(&mut MakeSuggestableFolder { tcx, infer_suggestable, placeholder }).ok() } } @@ -559,6 +569,7 @@ fn visit_const(&mut self, c: Const<'tcx>) -> Self::Result { pub struct MakeSuggestableFolder<'tcx> { tcx: TyCtxt<'tcx>, infer_suggestable: bool, + placeholder: Option>, } impl<'tcx> FallibleTypeFolder> for MakeSuggestableFolder<'tcx> { @@ -572,19 +583,24 @@ fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result, Self::Error> { let t = match *t.kind() { Infer(InferTy::TyVar(_)) if self.infer_suggestable => t, - FnDef(def_id, args) => { + FnDef(def_id, args) if self.placeholder.is_none() => { Ty::new_fn_ptr(self.tcx, self.tcx.fn_sig(def_id).instantiate(self.tcx, args)) } - // FIXME(compiler-errors): We could replace these with infer, I guess. Closure(..) + | FnDef(..) | Infer(..) | Coroutine(..) | CoroutineWitness(..) | Bound(_, _) | Placeholder(_) | Error(_) => { - return Err(()); + if let Some(placeholder) = self.placeholder { + // We replace these with infer (which is passed in from an infcx). + placeholder + } else { + return Err(()); + } } Alias(Opaque, AliasTy { def_id, .. }) => { diff --git a/src/tools/clippy/clippy_lints/src/box_default.rs b/src/tools/clippy/clippy_lints/src/box_default.rs index 779ae03c464..66206d1a059 100644 --- a/src/tools/clippy/clippy_lints/src/box_default.rs +++ b/src/tools/clippy/clippy_lints/src/box_default.rs @@ -70,7 +70,9 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { "try", if is_plain_default(cx, arg_path) || given_type(cx, expr) { "Box::default()".into() - } else if let Some(arg_ty) = cx.typeck_results().expr_ty(arg).make_suggestable(cx.tcx, true) { + } else if let Some(arg_ty) = + cx.typeck_results().expr_ty(arg).make_suggestable(cx.tcx, true, None) + { // Check if we can copy from the source expression in the replacement. // We need the call to have no argument (see `explicit_default_type`). if inner_call_args.is_empty() diff --git a/tests/ui/suggestions/types/into-inference-needs-type.rs b/tests/ui/suggestions/types/into-inference-needs-type.rs new file mode 100644 index 00000000000..4c8b6ec2113 --- /dev/null +++ b/tests/ui/suggestions/types/into-inference-needs-type.rs @@ -0,0 +1,15 @@ +#[derive(Debug)] +enum MyError { + MainError +} + +fn main() -> Result<(), MyError> { + let vec = vec!["one", "two", "three"]; + let list = vec + .iter() + .map(|s| s.strip_prefix("t")) + .filter_map(Option::Some) + .into()?; //~ ERROR type annotations needed + + return Ok(()); +} diff --git a/tests/ui/suggestions/types/into-inference-needs-type.stderr b/tests/ui/suggestions/types/into-inference-needs-type.stderr new file mode 100644 index 00000000000..dd688f90289 --- /dev/null +++ b/tests/ui/suggestions/types/into-inference-needs-type.stderr @@ -0,0 +1,19 @@ +error[E0283]: type annotations needed + --> $DIR/into-inference-needs-type.rs:12:10 + | +LL | .into()?; + | ^^^^ + | + = note: cannot satisfy `_: From, {closure@$DIR/into-inference-needs-type.rs:10:14: 10:17}>, fn(Option<&str>) -> Option> {Option::>::Some}>>` + = note: required for `FilterMap, {closure@$DIR/into-inference-needs-type.rs:10:14: 10:17}>, fn(Option<&str>) -> Option> {Option::>::Some}>` to implement `Into<_>` +help: try using a fully qualified path to specify the expected types + | +LL ~ let list = , _>, _> as Into>::into(vec +LL | .iter() +LL | .map(|s| s.strip_prefix("t")) +LL ~ .filter_map(Option::Some))?; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/suggest-fully-qualified-closure.rs b/tests/ui/traits/suggest-fully-qualified-closure.rs index f401a3012da..3d28a4e6bf5 100644 --- a/tests/ui/traits/suggest-fully-qualified-closure.rs +++ b/tests/ui/traits/suggest-fully-qualified-closure.rs @@ -1,7 +1,5 @@ //@ check-fail //@ known-bug: #103705 -//@ normalize-stderr-test "\{closure@.*\}" -> "{closure@}" -//@ normalize-stderr-test "\+* ~" -> "+++ ~" // The output of this currently suggests writing a closure in the qualified path. diff --git a/tests/ui/traits/suggest-fully-qualified-closure.stderr b/tests/ui/traits/suggest-fully-qualified-closure.stderr index e077bd7ac2a..a2c1115e673 100644 --- a/tests/ui/traits/suggest-fully-qualified-closure.stderr +++ b/tests/ui/traits/suggest-fully-qualified-closure.stderr @@ -1,11 +1,11 @@ error[E0283]: type annotations needed - --> $DIR/suggest-fully-qualified-closure.rs:23:7 + --> $DIR/suggest-fully-qualified-closure.rs:21:7 | LL | q.lol(||()); | ^^^ | note: multiple `impl`s satisfying `Qqq: MyTrait<_>` found - --> $DIR/suggest-fully-qualified-closure.rs:14:1 + --> $DIR/suggest-fully-qualified-closure.rs:12:1 | LL | impl MyTrait for Qqq{ | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -14,8 +14,8 @@ LL | impl MyTrait for Qqq{ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | >::lol::<{closure@}>(&q, ||()); - | +++ ~ +LL | >::lol::<_>(&q, ||()); + | +++++++++++++++++++++++++++++++ ~ error: aborting due to 1 previous error