Rollup merge of #123761 - compiler-errors:suggest-more-impl-trait, r=estebank

Use `suggest_impl_trait` in return type suggestion on type error

Discovered while doing other refactoring. Review with whitespace disabled.

r? estebank
This commit is contained in:
León Orell Valerian Liehr 2024-04-11 01:56:26 +02:00 committed by GitHub
commit 2930dce479
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 68 additions and 60 deletions

View File

@ -30,7 +30,7 @@
use rustc_middle::ty::util::{Discr, IntTypeExt}; use rustc_middle::ty::util::{Discr, IntTypeExt};
use rustc_middle::ty::{self, AdtKind, Const, IsSuggestable, ToPredicate, Ty, TyCtxt}; use rustc_middle::ty::{self, AdtKind, Const, IsSuggestable, ToPredicate, Ty, TyCtxt};
use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::Span; use rustc_span::{Span, DUMMY_SP};
use rustc_target::abi::FieldIdx; use rustc_target::abi::FieldIdx;
use rustc_target::spec::abi; use rustc_target::spec::abi;
use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::infer::InferCtxtExt;
@ -1383,7 +1383,9 @@ fn infer_return_ty_for_fn_sig<'tcx>(
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
should_recover = true; should_recover = true;
} else if let Some(sugg) = suggest_impl_trait(tcx, ret_ty, ty.span, def_id) { } else if let Some(sugg) =
suggest_impl_trait(&tcx.infer_ctxt().build(), tcx.param_env(def_id), ret_ty)
{
diag.span_suggestion( diag.span_suggestion(
ty.span, ty.span,
"replace with an appropriate return type", "replace with an appropriate return type",
@ -1426,11 +1428,10 @@ fn infer_return_ty_for_fn_sig<'tcx>(
} }
} }
fn suggest_impl_trait<'tcx>( pub fn suggest_impl_trait<'tcx>(
tcx: TyCtxt<'tcx>, infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
ret_ty: Ty<'tcx>, ret_ty: Ty<'tcx>,
span: Span,
def_id: LocalDefId,
) -> Option<String> { ) -> Option<String> {
let format_as_assoc: fn(_, _, _, _, _) -> _ = let format_as_assoc: fn(_, _, _, _, _) -> _ =
|tcx: TyCtxt<'tcx>, |tcx: TyCtxt<'tcx>,
@ -1464,24 +1465,28 @@ fn suggest_impl_trait<'tcx>(
for (trait_def_id, assoc_item_def_id, formatter) in [ for (trait_def_id, assoc_item_def_id, formatter) in [
( (
tcx.get_diagnostic_item(sym::Iterator), infcx.tcx.get_diagnostic_item(sym::Iterator),
tcx.get_diagnostic_item(sym::IteratorItem), infcx.tcx.get_diagnostic_item(sym::IteratorItem),
format_as_assoc, format_as_assoc,
), ),
( (
tcx.lang_items().future_trait(), infcx.tcx.lang_items().future_trait(),
tcx.get_diagnostic_item(sym::FutureOutput), infcx.tcx.get_diagnostic_item(sym::FutureOutput),
format_as_assoc, format_as_assoc,
), ),
(tcx.lang_items().fn_trait(), tcx.lang_items().fn_once_output(), format_as_parenthesized),
( (
tcx.lang_items().fn_mut_trait(), infcx.tcx.lang_items().fn_trait(),
tcx.lang_items().fn_once_output(), infcx.tcx.lang_items().fn_once_output(),
format_as_parenthesized, format_as_parenthesized,
), ),
( (
tcx.lang_items().fn_once_trait(), infcx.tcx.lang_items().fn_mut_trait(),
tcx.lang_items().fn_once_output(), infcx.tcx.lang_items().fn_once_output(),
format_as_parenthesized,
),
(
infcx.tcx.lang_items().fn_once_trait(),
infcx.tcx.lang_items().fn_once_output(),
format_as_parenthesized, format_as_parenthesized,
), ),
] { ] {
@ -1491,36 +1496,45 @@ fn suggest_impl_trait<'tcx>(
let Some(assoc_item_def_id) = assoc_item_def_id else { let Some(assoc_item_def_id) = assoc_item_def_id else {
continue; continue;
}; };
if tcx.def_kind(assoc_item_def_id) != DefKind::AssocTy { if infcx.tcx.def_kind(assoc_item_def_id) != DefKind::AssocTy {
continue; continue;
} }
let param_env = tcx.param_env(def_id); let sugg = infcx.probe(|_| {
let infcx = tcx.infer_ctxt().build(); let args = ty::GenericArgs::for_item(infcx.tcx, trait_def_id, |param, _| {
let args = ty::GenericArgs::for_item(tcx, trait_def_id, |param, _| { if param.index == 0 { ret_ty.into() } else { infcx.var_for_def(DUMMY_SP, param) }
if param.index == 0 { ret_ty.into() } else { infcx.var_for_def(span, param) } });
if !infcx
.type_implements_trait(trait_def_id, args, param_env)
.must_apply_modulo_regions()
{
return None;
}
let ocx = ObligationCtxt::new(&infcx);
let item_ty = ocx.normalize(
&ObligationCause::dummy(),
param_env,
Ty::new_projection(infcx.tcx, assoc_item_def_id, args),
);
// 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(infcx.tcx, false, None)
&& let Some(sugg) = formatter(
infcx.tcx,
infcx.resolve_vars_if_possible(args),
trait_def_id,
assoc_item_def_id,
item_ty,
)
{
return Some(sugg);
}
None
}); });
if !infcx.type_implements_trait(trait_def_id, args, param_env).must_apply_modulo_regions() {
continue; if sugg.is_some() {
} return sugg;
let ocx = ObligationCtxt::new(&infcx);
let item_ty = ocx.normalize(
&ObligationCause::misc(span, def_id),
param_env,
Ty::new_projection(tcx, assoc_item_def_id, args),
);
// 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, None)
&& let Some(sugg) = formatter(
tcx,
infcx.resolve_vars_if_possible(args),
trait_def_id,
assoc_item_def_id,
item_ty,
)
{
return Some(sugg);
} }
} }
None None

View File

@ -20,6 +20,7 @@
CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, Node, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, Node,
Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
}; };
use rustc_hir_analysis::collect::suggest_impl_trait;
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
use rustc_infer::traits::{self}; use rustc_infer::traits::{self};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
@ -814,17 +815,10 @@ pub(in super::super) fn suggest_missing_return_type(
errors::AddReturnTypeSuggestion::Add { span, found: found.to_string() }, errors::AddReturnTypeSuggestion::Add { span, found: found.to_string() },
); );
return true; return true;
} else if let ty::Closure(_, args) = found.kind() } else if let Some(sugg) = suggest_impl_trait(self, self.param_env, found) {
// FIXME(compiler-errors): Get better at printing binders...
&& let closure = args.as_closure()
&& closure.sig().is_suggestable(self.tcx, false)
{
err.subdiagnostic( err.subdiagnostic(
self.dcx(), self.dcx(),
errors::AddReturnTypeSuggestion::Add { errors::AddReturnTypeSuggestion::Add { span, found: sugg },
span,
found: closure.print_as_impl_trait().to_string(),
},
); );
return true; return true;
} else { } else {

View File

@ -62,7 +62,7 @@ error[E0308]: mismatched types
--> $DIR/async-closure-gate.rs:27:5 --> $DIR/async-closure-gate.rs:27:5
| |
LL | fn foo3() { LL | fn foo3() {
| - help: a return type might be missing here: `-> _` | - help: try adding a return type: `-> impl Future<Output = ()>`
LL | / async { LL | / async {
LL | | LL | |
LL | | let _ = #[track_caller] || { LL | | let _ = #[track_caller] || {
@ -78,7 +78,7 @@ error[E0308]: mismatched types
--> $DIR/async-closure-gate.rs:44:5 --> $DIR/async-closure-gate.rs:44:5
| |
LL | fn foo5() { LL | fn foo5() {
| - help: a return type might be missing here: `-> _` | - help: try adding a return type: `-> impl Future<Output = ()>`
LL | / async { LL | / async {
LL | | LL | |
LL | | let _ = || { LL | | let _ = || {

View File

@ -62,7 +62,7 @@ error[E0308]: mismatched types
--> $DIR/async-closure-gate.rs:27:5 --> $DIR/async-closure-gate.rs:27:5
| |
LL | fn foo3() { LL | fn foo3() {
| - help: a return type might be missing here: `-> _` | - help: try adding a return type: `-> impl Future<Output = ()>`
LL | / async { LL | / async {
LL | | LL | |
LL | | let _ = #[track_caller] || { LL | | let _ = #[track_caller] || {
@ -78,7 +78,7 @@ error[E0308]: mismatched types
--> $DIR/async-closure-gate.rs:44:5 --> $DIR/async-closure-gate.rs:44:5
| |
LL | fn foo5() { LL | fn foo5() {
| - help: a return type might be missing here: `-> _` | - help: try adding a return type: `-> impl Future<Output = ()>`
LL | / async { LL | / async {
LL | | LL | |
LL | | let _ = || { LL | | let _ = || {

View File

@ -2,7 +2,7 @@ error[E0308]: mismatched types
--> $DIR/return-closures.rs:3:5 --> $DIR/return-closures.rs:3:5
| |
LL | fn foo() { LL | fn foo() {
| - help: try adding a return type: `-> impl for<'a> Fn(&'a i32) -> i32` | - help: try adding a return type: `-> impl FnOnce(&i32) -> i32`
LL | LL |
LL | |x: &i32| 1i32 LL | |x: &i32| 1i32
| ^^^^^^^^^^^^^^ expected `()`, found closure | ^^^^^^^^^^^^^^ expected `()`, found closure

View File

@ -1,5 +1,5 @@
#[allow(unused)] #[allow(unused)]
fn foo() { //~ HELP a return type might be missing here fn foo() { //~ HELP try adding a return type
vec!['a'].iter().map(|c| c) vec!['a'].iter().map(|c| c)
//~^ ERROR mismatched types [E0308] //~^ ERROR mismatched types [E0308]
//~| NOTE expected `()`, found `Map<Iter<'_, char>, ...>` //~| NOTE expected `()`, found `Map<Iter<'_, char>, ...>`

View File

@ -10,10 +10,10 @@ help: consider using a semicolon here
| |
LL | vec!['a'].iter().map(|c| c); LL | vec!['a'].iter().map(|c| c);
| + | +
help: a return type might be missing here help: try adding a return type
| |
LL | fn foo() -> _ { LL | fn foo() -> impl Iterator<Item = &char> {
| ++++ | ++++++++++++++++++++++++++++++
error: aborting due to 1 previous error error: aborting due to 1 previous error