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 = <FilterMap<Map<std::slice::Iter<'_, &str>, _>, _> as Into<T>>::into(vec
LL |         .iter()
LL |         .map(|s| s.strip_prefix("t"))
LL ~         .filter_map(Option::Some))?;
   |
```

Fix #122569.
This commit is contained in:
Esteban Küber 2024-03-20 22:50:32 +00:00
parent a128516cf9
commit 5fae665924
12 changed files with 118 additions and 53 deletions

View File

@ -565,7 +565,7 @@ fn check_assoc_const_binding_type<'tcx>(
let mut guar = ty.visit_with(&mut collector).break_value(); let mut guar = ty.visit_with(&mut collector).break_value();
let ty_note = ty let ty_note = ty
.make_suggestable(tcx, false) .make_suggestable(tcx, false, None)
.map(|ty| crate::errors::TyOfAssocConstBindingNote { assoc_const, ty }); .map(|ty| crate::errors::TyOfAssocConstBindingNote { assoc_const, ty });
let enclosing_item_owner_id = tcx let enclosing_item_owner_id = tcx

View File

@ -1364,7 +1364,7 @@ fn infer_return_ty_for_fn_sig<'tcx>(
// recursive function definition to leak out into the fn sig. // recursive function definition to leak out into the fn sig.
let mut should_recover = false; 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( diag.span_suggestion(
ty.span, ty.span,
"replace with the correct return type", "replace with the correct return type",
@ -1442,7 +1442,7 @@ fn suggest_impl_trait<'tcx>(
let ty::Tuple(types) = *args_tuple.kind() else { let ty::Tuple(types) = *args_tuple.kind() else {
return None; return None;
}; };
let types = types.make_suggestable(tcx, false)?; let types = types.make_suggestable(tcx, false, None)?;
let maybe_ret = let maybe_ret =
if item_ty.is_unit() { String::new() } else { format!(" -> {item_ty}") }; if item_ty.is_unit() { String::new() } else { format!(" -> {item_ty}") };
Some(format!( Some(format!(
@ -1500,7 +1500,7 @@ fn suggest_impl_trait<'tcx>(
// FIXME(compiler-errors): We may benefit from resolving regions here. // FIXME(compiler-errors): We may benefit from resolving regions here.
if ocx.select_where_possible().is_empty() if ocx.select_where_possible().is_empty()
&& let item_ty = infcx.resolve_vars_if_possible(item_ty) && 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( && let Some(sugg) = formatter(
tcx, tcx,
infcx.resolve_vars_if_possible(args), infcx.resolve_vars_if_possible(args),

View File

@ -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, _| { let ty = tcx.fold_regions(ty, |r, _| {
if r.is_erased() { ty::Region::new_error_misc(tcx) } else { 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))) (ty, Some((span, Applicability::MachineApplicable)))
} else { } else {
(ty, None) (ty, None)
@ -587,7 +587,7 @@ fn infer_placeholder_type<'a>(
suggestions.clear(); suggestions.clear();
} }
if let Some(ty) = ty.make_suggestable(tcx, false) { if let Some(ty) = ty.make_suggestable(tcx, false, None) {
err.span_suggestion( err.span_suggestion(
span, span,
format!("provide a type for the {kind}"), 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); let mut diag = bad_placeholder(tcx, vec![span], kind);
if !ty.references_error() { 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( diag.span_suggestion(
span, span,
"replace with the correct type", "replace with the correct type",

View File

@ -809,7 +809,7 @@ pub(in super::super) fn suggest_missing_return_type(
return true; return true;
} }
&hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => { &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( err.subdiagnostic(
self.dcx(), self.dcx(),
errors::AddReturnTypeSuggestion::Add { span, found: found.to_string() }, errors::AddReturnTypeSuggestion::Add { span, found: found.to_string() },

View File

@ -601,8 +601,8 @@ fn check_overloaded_binop(
if let Some(output_def_id) = output_def_id if let Some(output_def_id) = output_def_id
&& let Some(trait_def_id) = trait_def_id && let Some(trait_def_id) = trait_def_id
&& self.tcx.parent(output_def_id) == trait_def_id && self.tcx.parent(output_def_id) == trait_def_id
&& let Some(output_ty) = && let Some(output_ty) = output_ty
output_ty.make_suggestable(self.tcx, false) .make_suggestable(self.tcx, false, None)
{ {
Some(("Output", output_ty)) Some(("Output", output_ty))
} else { } else {

View File

@ -546,40 +546,55 @@ pub fn emit_inference_failure_err(
} }
} }
InferSourceKind::FullyQualifiedMethodCall { receiver, successor, args, def_id } => { InferSourceKind::FullyQualifiedMethodCall { receiver, successor, args, def_id } => {
let mut printer = fmt_printer(self, Namespace::ValueNS); let placeholder = Some(self.next_ty_var(TypeVariableOrigin {
printer.print_def_path(def_id, args).unwrap(); span: rustc_span::DUMMY_SP,
let def_path = printer.into_buffer(); 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. // 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 // This is the case if the last adjustment is a borrow and the
// first adjustment was not a builtin deref. // first adjustment was not a builtin deref.
let adjustment = match typeck_results.expr_adjustments(receiver) { let adjustment = match typeck_results.expr_adjustments(receiver) {
[ [
Adjustment { kind: Adjust::Deref(None), target: _ }, Adjustment { kind: Adjust::Deref(None), target: _ },
.., ..,
Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), target: _ }, Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), target: _ },
] => "", ] => "",
[ [
.., ..,
Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(_, mut_)), target: _ }, Adjustment {
] => hir::Mutability::from(*mut_).ref_prefix_str(), kind: Adjust::Borrow(AutoBorrow::Ref(_, mut_)),
_ => "", target: _,
}; },
] => hir::Mutability::from(*mut_).ref_prefix_str(),
_ => "",
};
multi_suggestions.push(SourceKindMultiSuggestion::new_fully_qualified( multi_suggestions.push(SourceKindMultiSuggestion::new_fully_qualified(
receiver.span, receiver.span,
def_path, def_path,
adjustment, adjustment,
successor, successor,
)); ));
}
} }
InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => { InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => {
let ty_info = ty_to_string(self, ty, None); let placeholder = Some(self.next_ty_var(TypeVariableOrigin {
multi_suggestions.push(SourceKindMultiSuggestion::new_closure_return( span: rustc_span::DUMMY_SP,
ty_info, kind: TypeVariableOriginKind::MiscVariable,
data, }));
should_wrap_expr, 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 { match error_code {

View File

@ -91,7 +91,12 @@ pub trait IsSuggestable<'tcx>: Sized {
/// inference variables to be suggestable. /// inference variables to be suggestable.
fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool; fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool;
fn make_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> Option<Self>; fn make_suggestable(
self,
tcx: TyCtxt<'tcx>,
infer_suggestable: bool,
placeholder: Option<Ty<'tcx>>,
) -> Option<Self>;
} }
impl<'tcx, T> IsSuggestable<'tcx> for T 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() self.visit_with(&mut IsSuggestableVisitor { tcx, infer_suggestable }).is_continue()
} }
fn make_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> Option<T> { fn make_suggestable(
self.try_fold_with(&mut MakeSuggestableFolder { tcx, infer_suggestable }).ok() self,
tcx: TyCtxt<'tcx>,
infer_suggestable: bool,
placeholder: Option<Ty<'tcx>>,
) -> Option<T> {
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> { pub struct MakeSuggestableFolder<'tcx> {
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
infer_suggestable: bool, infer_suggestable: bool,
placeholder: Option<Ty<'tcx>>,
} }
impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for MakeSuggestableFolder<'tcx> { impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for MakeSuggestableFolder<'tcx> {
@ -572,19 +583,24 @@ fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
let t = match *t.kind() { let t = match *t.kind() {
Infer(InferTy::TyVar(_)) if self.infer_suggestable => t, 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)) 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(..) Closure(..)
| FnDef(..)
| Infer(..) | Infer(..)
| Coroutine(..) | Coroutine(..)
| CoroutineWitness(..) | CoroutineWitness(..)
| Bound(_, _) | Bound(_, _)
| Placeholder(_) | Placeholder(_)
| Error(_) => { | 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, .. }) => { Alias(Opaque, AliasTy { def_id, .. }) => {

View File

@ -70,7 +70,9 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
"try", "try",
if is_plain_default(cx, arg_path) || given_type(cx, expr) { if is_plain_default(cx, arg_path) || given_type(cx, expr) {
"Box::default()".into() "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. // Check if we can copy from the source expression in the replacement.
// We need the call to have no argument (see `explicit_default_type`). // We need the call to have no argument (see `explicit_default_type`).
if inner_call_args.is_empty() if inner_call_args.is_empty()

View File

@ -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(());
}

View File

@ -0,0 +1,19 @@
error[E0283]: type annotations needed
--> $DIR/into-inference-needs-type.rs:12:10
|
LL | .into()?;
| ^^^^
|
= note: cannot satisfy `_: From<FilterMap<Map<std::slice::Iter<'_, &str>, {closure@$DIR/into-inference-needs-type.rs:10:14: 10:17}>, fn(Option<&str>) -> Option<Option<&str>> {Option::<Option<&str>>::Some}>>`
= note: required for `FilterMap<Map<std::slice::Iter<'_, &str>, {closure@$DIR/into-inference-needs-type.rs:10:14: 10:17}>, fn(Option<&str>) -> Option<Option<&str>> {Option::<Option<&str>>::Some}>` to implement `Into<_>`
help: try using a fully qualified path to specify the expected types
|
LL ~ let list = <FilterMap<Map<std::slice::Iter<'_, &str>, _>, _> as Into<T>>::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`.

View File

@ -1,7 +1,5 @@
//@ check-fail //@ check-fail
//@ known-bug: #103705 //@ known-bug: #103705
//@ normalize-stderr-test "\{closure@.*\}" -> "{closure@}"
//@ normalize-stderr-test "\+* ~" -> "+++ ~"
// The output of this currently suggests writing a closure in the qualified path. // The output of this currently suggests writing a closure in the qualified path.

View File

@ -1,11 +1,11 @@
error[E0283]: type annotations needed error[E0283]: type annotations needed
--> $DIR/suggest-fully-qualified-closure.rs:23:7 --> $DIR/suggest-fully-qualified-closure.rs:21:7
| |
LL | q.lol(||()); LL | q.lol(||());
| ^^^ | ^^^
| |
note: multiple `impl`s satisfying `Qqq: MyTrait<_>` found 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<u32> for Qqq{ LL | impl MyTrait<u32> for Qqq{
| ^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^
@ -14,8 +14,8 @@ LL | impl MyTrait<u64> for Qqq{
| ^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^
help: try using a fully qualified path to specify the expected types help: try using a fully qualified path to specify the expected types
| |
LL | <Qqq as MyTrait<T>>::lol::<{closure@}>(&q, ||()); LL | <Qqq as MyTrait<T>>::lol::<_>(&q, ||());
| +++ ~ | +++++++++++++++++++++++++++++++ ~
error: aborting due to 1 previous error error: aborting due to 1 previous error