Make const/fn return params more suggestable

This commit is contained in:
Michael Goulet 2023-01-14 22:55:27 +00:00
parent 658fad6c55
commit 0b5941aa11
10 changed files with 163 additions and 67 deletions

View file

@ -2945,12 +2945,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
if r.is_erased() { tcx.lifetimes.re_static } else { r }
});
let span = ast_ty.span;
tcx.sess.emit_err(TypeofReservedKeywordUsed {
span,
ty,
opt_sugg: Some((span, Applicability::MachineApplicable))
.filter(|_| ty.is_suggestable(tcx, false)),
});
let (ty, opt_sugg) = if let Some(ty) = ty.make_suggestable(tcx, false) {
(ty, Some((span, Applicability::MachineApplicable)))
} else {
(ty, None)
};
tcx.sess.emit_err(TypeofReservedKeywordUsed { span, ty, opt_sugg });
ty
}

View file

@ -1199,28 +1199,22 @@ fn infer_return_ty_for_fn_sig<'tcx>(
visitor.visit_ty(ty);
let mut diag = bad_placeholder(tcx, visitor.0, "return type");
let ret_ty = fn_sig.output();
if ret_ty.is_suggestable(tcx, false) {
if let Some(ret_ty) = ret_ty.make_suggestable(tcx, false) {
diag.span_suggestion(
ty.span,
"replace with the correct return type",
ret_ty,
Applicability::MachineApplicable,
);
} else if matches!(ret_ty.kind(), ty::FnDef(..)) {
let fn_sig = ret_ty.fn_sig(tcx);
if fn_sig
.skip_binder()
.inputs_and_output
.iter()
.all(|t| t.is_suggestable(tcx, false))
{
diag.span_suggestion(
ty.span,
"replace with the correct return type",
fn_sig,
Applicability::MachineApplicable,
);
}
} else if matches!(ret_ty.kind(), ty::FnDef(..))
&& let Some(fn_sig) = ret_ty.fn_sig(tcx).make_suggestable(tcx, false)
{
diag.span_suggestion(
ty.span,
"replace with the correct return type",
fn_sig,
Applicability::MachineApplicable,
);
} else if let Some(sugg) = suggest_impl_trait(tcx, ret_ty, ty.span, hir_id, def_id) {
diag.span_suggestion(
ty.span,
@ -1280,9 +1274,7 @@ fn suggest_impl_trait<'tcx>(
let trait_name = tcx.item_name(trait_def_id);
let args_tuple = substs.type_at(1);
let ty::Tuple(types) = *args_tuple.kind() else { return None; };
if !types.is_suggestable(tcx, false) {
return None;
}
let types = types.make_suggestable(tcx, false)?;
let maybe_ret =
if item_ty.is_unit() { String::new() } else { format!(" -> {item_ty}") };
Some(format!(
@ -1337,7 +1329,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)
&& item_ty.is_suggestable(tcx, false)
&& let Some(item_ty) = item_ty.make_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);

View file

@ -8,7 +8,9 @@ use rustc_middle::hir::nested_filter;
use rustc_middle::ty::print::with_forced_trimmed_paths;
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::util::IntTypeExt;
use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable};
use rustc_middle::ty::{
self, DefIdTree, IsSuggestable, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable,
};
use rustc_span::symbol::Ident;
use rustc_span::{Span, DUMMY_SP};
@ -845,37 +847,23 @@ fn infer_placeholder_type<'a>(
) -> Ty<'a> {
// Attempts to make the type nameable by turning FnDefs into FnPtrs.
struct MakeNameable<'tcx> {
success: bool,
tcx: TyCtxt<'tcx>,
}
impl<'tcx> MakeNameable<'tcx> {
fn new(tcx: TyCtxt<'tcx>) -> Self {
MakeNameable { success: true, tcx }
}
}
impl<'tcx> TypeFolder<'tcx> for MakeNameable<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
if !self.success {
return ty;
}
match ty.kind() {
let ty = match *ty.kind() {
ty::FnDef(def_id, substs) => {
self.tcx.mk_fn_ptr(self.tcx.fn_sig(*def_id).subst(self.tcx, substs))
self.tcx.mk_fn_ptr(self.tcx.fn_sig(def_id).subst(self.tcx, substs))
}
// FIXME: non-capturing closures should also suggest a function pointer
ty::Closure(..) | ty::Generator(..) => {
self.success = false;
ty
}
_ => ty.super_fold_with(self),
}
_ => ty,
};
ty.super_fold_with(self)
}
}
@ -898,15 +886,11 @@ fn infer_placeholder_type<'a>(
suggestions.clear();
}
// Suggesting unnameable types won't help.
let mut mk_nameable = MakeNameable::new(tcx);
let ty = mk_nameable.fold_ty(ty);
let sugg_ty = if mk_nameable.success { Some(ty) } else { None };
if let Some(sugg_ty) = sugg_ty {
if let Some(ty) = ty.make_suggestable(tcx, false) {
err.span_suggestion(
span,
&format!("provide a type for the {item}", item = kind),
format!("{colon} {sugg_ty}"),
format!("{colon} {ty}"),
Applicability::MachineApplicable,
);
} else {
@ -923,15 +907,12 @@ fn infer_placeholder_type<'a>(
let mut diag = bad_placeholder(tcx, vec![span], kind);
if !ty.references_error() {
let mut mk_nameable = MakeNameable::new(tcx);
let ty = mk_nameable.fold_ty(ty);
let sugg_ty = if mk_nameable.success { Some(ty) } else { None };
if let Some(sugg_ty) = sugg_ty {
if let Some(ty) = ty.make_suggestable(tcx, false) {
diag.span_suggestion(
span,
"replace with the correct type",
sugg_ty,
Applicability::MaybeIncorrect,
ty,
Applicability::MachineApplicable,
);
} else {
with_forced_trimmed_paths!(diag.span_note(

View file

@ -687,7 +687,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return true;
}
&hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => {
if found.is_suggestable(self.tcx, false) {
if let Some(found) = found.make_suggestable(self.tcx, false) {
err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() });
return true;
} else if let ty::Closure(_, substs) = found.kind()

View file

@ -490,9 +490,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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
&& output_ty.is_suggestable(self.tcx, false)
&& let Some(output_ty) = output_ty.make_suggestable(self.tcx, false)
{
Some(("Output", *output_ty))
Some(("Output", output_ty))
} else {
None
}

View file

@ -3,8 +3,9 @@
use std::ops::ControlFlow;
use crate::ty::{
visit::TypeVisitable, AliasTy, Const, ConstKind, DefIdTree, InferConst, InferTy, Opaque,
PolyTraitPredicate, Projection, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
visit::TypeVisitable, AliasTy, Const, ConstKind, DefIdTree, FallibleTypeFolder, InferConst,
InferTy, Opaque, PolyTraitPredicate, Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable,
TypeSuperVisitable, TypeVisitor,
};
use rustc_data_structures::fx::FxHashMap;
@ -76,7 +77,7 @@ impl<'tcx> Ty<'tcx> {
}
}
pub trait IsSuggestable<'tcx> {
pub trait IsSuggestable<'tcx>: Sized {
/// Whether this makes sense to suggest in a diagnostic.
///
/// We filter out certain types and constants since they don't provide
@ -87,15 +88,21 @@ pub trait IsSuggestable<'tcx> {
/// Only if `infer_suggestable` is true, we consider type and const
/// 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<Self>;
}
impl<'tcx, T> IsSuggestable<'tcx> for T
where
T: TypeVisitable<'tcx>,
T: TypeVisitable<'tcx> + TypeFoldable<'tcx>,
{
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<T> {
self.try_fold_with(&mut MakeSuggestableFolder { tcx, infer_suggestable }).ok()
}
}
pub fn suggest_arbitrary_trait_bound<'tcx>(
@ -509,3 +516,83 @@ impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> {
c.super_visit_with(self)
}
}
pub struct MakeSuggestableFolder<'tcx> {
tcx: TyCtxt<'tcx>,
infer_suggestable: bool,
}
impl<'tcx> FallibleTypeFolder<'tcx> for MakeSuggestableFolder<'tcx> {
type Error = ();
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
let t = match *t.kind() {
Infer(InferTy::TyVar(_)) if self.infer_suggestable => t,
FnDef(def_id, substs) => {
self.tcx.mk_fn_ptr(self.tcx.fn_sig(def_id).subst(self.tcx, substs))
}
// FIXME(compiler-errors): We could replace these with infer, I guess.
Closure(..)
| Infer(..)
| Generator(..)
| GeneratorWitness(..)
| Bound(_, _)
| Placeholder(_)
| Error(_) => {
return Err(());
}
Alias(Opaque, AliasTy { def_id, .. }) => {
let parent = self.tcx.parent(def_id);
if let hir::def::DefKind::TyAlias | hir::def::DefKind::AssocTy = self.tcx.def_kind(parent)
&& let Alias(Opaque, AliasTy { def_id: parent_opaque_def_id, .. }) = *self.tcx.type_of(parent).kind()
&& parent_opaque_def_id == def_id
{
t
} else {
return Err(());
}
}
Param(param) => {
// FIXME: It would be nice to make this not use string manipulation,
// but it's pretty hard to do this, since `ty::ParamTy` is missing
// sufficient info to determine if it is synthetic, and we don't
// always have a convenient way of getting `ty::Generics` at the call
// sites we invoke `IsSuggestable::is_suggestable`.
if param.name.as_str().starts_with("impl ") {
return Err(());
}
t
}
_ => t,
};
t.try_super_fold_with(self)
}
fn try_fold_const(&mut self, c: Const<'tcx>) -> Result<Const<'tcx>, ()> {
let c = match c.kind() {
ConstKind::Infer(InferConst::Var(_)) if self.infer_suggestable => c,
ConstKind::Infer(..)
| ConstKind::Bound(..)
| ConstKind::Placeholder(..)
| ConstKind::Error(..) => {
return Err(());
}
_ => c,
};
c.try_super_fold_with(self)
}
}

View file

@ -105,7 +105,7 @@ pub trait TypeSuperFoldable<'tcx>: TypeFoldable<'tcx> {
/// the infallible methods of this trait to ensure that the two APIs
/// are coherent.
pub trait TypeFolder<'tcx>: FallibleTypeFolder<'tcx, Error = !> {
fn tcx<'a>(&'a self) -> TyCtxt<'tcx>;
fn tcx(&self) -> TyCtxt<'tcx>;
fn fold_binder<T>(&mut self, t: Binder<'tcx, T>) -> Binder<'tcx, T>
where

View file

@ -0,0 +1,12 @@
// run-rustfix
#![allow(unused)]
struct Wrapper<T>(T);
fn bar() -> Wrapper<fn()> { Wrapper(foo) }
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types
fn foo() {}
fn main() {}

View file

@ -0,0 +1,12 @@
// run-rustfix
#![allow(unused)]
struct Wrapper<T>(T);
fn bar() -> _ { Wrapper(foo) }
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types
fn foo() {}
fn main() {}

View file

@ -0,0 +1,12 @@
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
--> $DIR/suggest-fn-ptr-for-fn-item-in-fn-ret.rs:7:13
|
LL | fn bar() -> _ { Wrapper(foo) }
| ^
| |
| not allowed in type signatures
| help: replace with the correct return type: `Wrapper<fn()>`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0121`.