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:
parent
a128516cf9
commit
5fae665924
@ -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
|
||||||
|
@ -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),
|
||||||
|
@ -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",
|
||||||
|
@ -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() },
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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, .. }) => {
|
||||||
|
@ -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()
|
||||||
|
15
tests/ui/suggestions/types/into-inference-needs-type.rs
Normal file
15
tests/ui/suggestions/types/into-inference-needs-type.rs
Normal 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(());
|
||||||
|
}
|
19
tests/ui/suggestions/types/into-inference-needs-type.stderr
Normal file
19
tests/ui/suggestions/types/into-inference-needs-type.stderr
Normal 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`.
|
@ -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.
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user