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

Suggest `impl Fn*` and `impl Future` in `-> _` return suggestions

Follow-up to #106172, only the last commit is relevant. Can rebase once that PR is landed for easier review.

Suggests `impl Future` and `impl Fn{,Mut,Once}` in `-> _` return suggestions.

r? `@estebank`
This commit is contained in:
Matthias Krüger 2023-01-04 07:28:54 +01:00 committed by GitHub
commit 70468af591
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 220 additions and 55 deletions

View file

@ -17,6 +17,7 @@
use crate::astconv::AstConv; use crate::astconv::AstConv;
use crate::check::intrinsic::intrinsic_operation_unsafety; use crate::check::intrinsic::intrinsic_operation_unsafety;
use crate::errors; use crate::errors;
use hir::def::DefKind;
use rustc_data_structures::captures::Captures; use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, StashKey}; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, StashKey};
@ -24,8 +25,8 @@
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{GenericParamKind, Node}; use rustc_hir::{GenericParamKind, Node};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::ObligationCause;
use rustc_middle::hir::nested_filter; use rustc_middle::hir::nested_filter;
use rustc_middle::ty::query::Providers; use rustc_middle::ty::query::Providers;
use rustc_middle::ty::util::{Discr, IntTypeExt}; use rustc_middle::ty::util::{Discr, IntTypeExt};
@ -1195,12 +1196,11 @@ fn infer_return_ty_for_fn_sig<'tcx>(
ty::ReErased => tcx.lifetimes.re_static, ty::ReErased => tcx.lifetimes.re_static,
_ => r, _ => r,
}); });
let fn_sig = ty::Binder::dummy(fn_sig);
let mut visitor = HirPlaceholderCollector::default(); let mut visitor = HirPlaceholderCollector::default();
visitor.visit_ty(ty); visitor.visit_ty(ty);
let mut diag = bad_placeholder(tcx, visitor.0, "return type"); let mut diag = bad_placeholder(tcx, visitor.0, "return type");
let ret_ty = fn_sig.skip_binder().output(); let ret_ty = fn_sig.output();
if ret_ty.is_suggestable(tcx, false) { if ret_ty.is_suggestable(tcx, false) {
diag.span_suggestion( diag.span_suggestion(
ty.span, ty.span,
@ -1223,26 +1223,26 @@ fn infer_return_ty_for_fn_sig<'tcx>(
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }
} else if let Some(sugg) = suggest_impl_trait(tcx, ret_ty, ty.span, hir_id, def_id) {
diag.span_suggestion(
ty.span,
"replace with an appropriate return type",
sugg,
Applicability::MachineApplicable,
);
} else if ret_ty.is_closure() { } else if ret_ty.is_closure() {
// We're dealing with a closure, so we should suggest using `impl Fn` or trait bounds
// to prevent the user from getting a papercut while trying to use the unique closure
// syntax (e.g. `[closure@src/lib.rs:2:5: 2:9]`).
diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound"); diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound");
}
// Also note how `Fn` traits work just in case!
if ret_ty.is_closure() {
diag.note( diag.note(
"for more information on `Fn` traits and closure types, see \ "for more information on `Fn` traits and closure types, see \
https://doc.rust-lang.org/book/ch13-01-closures.html", https://doc.rust-lang.org/book/ch13-01-closures.html",
); );
} else if let Some(i_ty) = suggest_impl_iterator(tcx, ret_ty, ty.span, hir_id, def_id) {
diag.span_suggestion(
ty.span,
"replace with an appropriate return type",
format!("impl Iterator<Item = {}>", i_ty),
Applicability::MachineApplicable,
);
} }
diag.emit(); diag.emit();
fn_sig ty::Binder::dummy(fn_sig)
} }
None => <dyn AstConv<'_>>::ty_of_fn( None => <dyn AstConv<'_>>::ty_of_fn(
icx, icx,
@ -1256,47 +1256,94 @@ fn infer_return_ty_for_fn_sig<'tcx>(
} }
} }
fn suggest_impl_iterator<'tcx>( fn suggest_impl_trait<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
ret_ty: Ty<'tcx>, ret_ty: Ty<'tcx>,
span: Span, span: Span,
hir_id: hir::HirId, hir_id: hir::HirId,
def_id: LocalDefId, def_id: LocalDefId,
) -> Option<Ty<'tcx>> { ) -> Option<String> {
let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) else { return None; }; let format_as_assoc: fn(_, _, _, _, _) -> _ =
let Some(iterator_item) = tcx.get_diagnostic_item(sym::IteratorItem) else { return None; }; |tcx: TyCtxt<'tcx>,
if !tcx _: ty::SubstsRef<'tcx>,
.infer_ctxt() trait_def_id: DefId,
.build() assoc_item_def_id: DefId,
.type_implements_trait(iter_trait, [ret_ty], tcx.param_env(def_id)) item_ty: Ty<'tcx>| {
.must_apply_modulo_regions() let trait_name = tcx.item_name(trait_def_id);
{ let assoc_name = tcx.item_name(assoc_item_def_id);
return None; Some(format!("impl {trait_name}<{assoc_name} = {item_ty}>"))
} };
let infcx = tcx.infer_ctxt().build(); let format_as_parenthesized: fn(_, _, _, _, _) -> _ =
let ocx = ObligationCtxt::new_in_snapshot(&infcx); |tcx: TyCtxt<'tcx>,
// Find the type of `Iterator::Item`. substs: ty::SubstsRef<'tcx>,
let origin = TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }; trait_def_id: DefId,
let ty_var = infcx.next_ty_var(origin); _: DefId,
let projection = ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::Projection( item_ty: Ty<'tcx>| {
ty::ProjectionPredicate { let trait_name = tcx.item_name(trait_def_id);
projection_ty: tcx.mk_alias_ty(iterator_item, tcx.mk_substs([ret_ty.into()].iter())), let args_tuple = substs.type_at(1);
term: ty_var.into(), let ty::Tuple(types) = *args_tuple.kind() else { return None; };
}, if !types.is_suggestable(tcx, false) {
))); return None;
// Add `<ret_ty as Iterator>::Item = _` obligation. }
ocx.register_obligation(crate::traits::Obligation::misc( let maybe_ret =
tcx, if item_ty.is_unit() { String::new() } else { format!(" -> {item_ty}") };
span, Some(format!(
hir_id, "impl {trait_name}({}){maybe_ret}",
tcx.param_env(def_id), types.iter().map(|ty| ty.to_string()).collect::<Vec<_>>().join(", ")
projection, ))
)); };
if ocx.select_where_possible().is_empty()
&& let item_ty = infcx.resolve_vars_if_possible(ty_var) for (trait_def_id, assoc_item_def_id, formatter) in [
&& item_ty.is_suggestable(tcx, false) (
{ tcx.get_diagnostic_item(sym::Iterator),
return Some(item_ty); tcx.get_diagnostic_item(sym::IteratorItem),
format_as_assoc,
),
(
tcx.lang_items().future_trait(),
tcx.get_diagnostic_item(sym::FutureOutput),
format_as_assoc,
),
(tcx.lang_items().fn_trait(), tcx.lang_items().fn_once_output(), format_as_parenthesized),
(
tcx.lang_items().fn_mut_trait(),
tcx.lang_items().fn_once_output(),
format_as_parenthesized,
),
(
tcx.lang_items().fn_once_trait(),
tcx.lang_items().fn_once_output(),
format_as_parenthesized,
),
] {
let Some(trait_def_id) = trait_def_id else { continue; };
let Some(assoc_item_def_id) = assoc_item_def_id else { continue; };
if tcx.def_kind(assoc_item_def_id) != DefKind::AssocTy {
continue;
}
let param_env = tcx.param_env(def_id);
let infcx = tcx.infer_ctxt().build();
let substs = ty::InternalSubsts::for_item(tcx, trait_def_id, |param, _| {
if param.index == 0 { ret_ty.into() } else { infcx.var_for_def(span, param) }
});
if !infcx.type_implements_trait(trait_def_id, substs, param_env).must_apply_modulo_regions()
{
continue;
}
let ocx = ObligationCtxt::new_in_snapshot(&infcx);
let item_ty = ocx.normalize(
&ObligationCause::misc(span, hir_id),
param_env,
tcx.mk_projection(assoc_item_def_id, substs),
);
// 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)
&& item_ty.is_suggestable(tcx, false)
&& let Some(sugg) = formatter(tcx, infcx.resolve_vars_if_possible(substs), trait_def_id, assoc_item_def_id, item_ty)
{
return Some(sugg);
}
} }
None None
} }

View file

@ -193,6 +193,7 @@
FromIterator, FromIterator,
FromResidual, FromResidual,
Future, Future,
FutureOutput,
FxHashMap, FxHashMap,
FxHashSet, FxHashSet,
GlobalAlloc, GlobalAlloc,

View file

@ -37,6 +37,7 @@
pub trait Future { pub trait Future {
/// The type of value produced on completion. /// The type of value produced on completion.
#[stable(feature = "futures_api", since = "1.36.0")] #[stable(feature = "futures_api", since = "1.36.0")]
#[rustc_diagnostic_item = "FutureOutput"]
type Output; type Output;
/// Attempt to resolve the future to a final value, registering /// Attempt to resolve the future to a final value, registering

View file

@ -18,9 +18,9 @@ fn returns_fn_ptr() -> _ {
fn returns_closure() -> _ { fn returns_closure() -> _ {
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types [E0121] //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types [E0121]
//~| NOTE not allowed in type signatures //~| NOTE not allowed in type signatures
//~| HELP consider using an `Fn`, `FnMut`, or `FnOnce` trait bound //~| HELP replace with an appropriate return type
//~| NOTE for more information on `Fn` traits and closure types, see //~| SUGGESTION impl Fn() -> i32
// https://doc.rust-lang.org/book/ch13-01-closures.html //~| NOTE for more information on `Fn` traits and closure types
|| 0 || 0
} }

View file

@ -11,9 +11,11 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures
--> $DIR/issue-80179.rs:18:25 --> $DIR/issue-80179.rs:18:25
| |
LL | fn returns_closure() -> _ { LL | fn returns_closure() -> _ {
| ^ not allowed in type signatures | ^
| |
| not allowed in type signatures
| help: replace with an appropriate return type: `impl Fn() -> i32`
| |
= help: consider using an `Fn`, `FnMut`, or `FnOnce` trait bound
= note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html = note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html
error: aborting due to 2 previous errors error: aborting due to 2 previous errors

View file

@ -0,0 +1,34 @@
fn fn_once() -> _ {
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types [E0121]
//~| NOTE not allowed in type signatures
//~| HELP replace with an appropriate return type
//~| SUGGESTION impl FnOnce()
//~| NOTE for more information on `Fn` traits and closure types
let x = String::new();
|| {
drop(x);
}
}
fn fn_mut() -> _ {
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types [E0121]
//~| NOTE not allowed in type signatures
//~| HELP replace with an appropriate return type
//~| SUGGESTION impl FnMut(char)
//~| NOTE for more information on `Fn` traits and closure types
let x = String::new();
|c| {
x.push(c);
}
}
fn fun() -> _ {
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types [E0121]
//~| NOTE not allowed in type signatures
//~| HELP replace with an appropriate return type
//~| SUGGESTION impl Fn() -> i32
//~| NOTE for more information on `Fn` traits and closure types
|| 1i32
}
fn main() {}

View file

@ -0,0 +1,36 @@
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
--> $DIR/suggest-return-closure.rs:1:17
|
LL | fn fn_once() -> _ {
| ^
| |
| not allowed in type signatures
| help: replace with an appropriate return type: `impl FnOnce()`
|
= note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
--> $DIR/suggest-return-closure.rs:13:16
|
LL | fn fn_mut() -> _ {
| ^
| |
| not allowed in type signatures
| help: replace with an appropriate return type: `impl FnMut(char)`
|
= note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
--> $DIR/suggest-return-closure.rs:25:13
|
LL | fn fun() -> _ {
| ^
| |
| not allowed in type signatures
| help: replace with an appropriate return type: `impl Fn() -> i32`
|
= note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0121`.

View file

@ -0,0 +1,23 @@
// edition: 2021
async fn a() -> i32 {
0
}
fn foo() -> _ {
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types [E0121]
//~| NOTE not allowed in type signatures
//~| HELP replace with an appropriate return type
//~| SUGGESTION impl Future<Output = i32>
a()
}
fn bar() -> _ {
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types [E0121]
//~| NOTE not allowed in type signatures
//~| HELP replace with an appropriate return type
//~| SUGGESTION impl Future<Output = i32>
async { a().await }
}
fn main() {}

View file

@ -0,0 +1,21 @@
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
--> $DIR/suggest-return-future.rs:7:13
|
LL | fn foo() -> _ {
| ^
| |
| not allowed in type signatures
| help: replace with an appropriate return type: `impl Future<Output = i32>`
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
--> $DIR/suggest-return-future.rs:15:13
|
LL | fn bar() -> _ {
| ^
| |
| not allowed in type signatures
| help: replace with an appropriate return type: `impl Future<Output = i32>`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0121`.