1
Fork 0

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 } if r.is_erased() { tcx.lifetimes.re_static } else { r }
}); });
let span = ast_ty.span; let span = ast_ty.span;
tcx.sess.emit_err(TypeofReservedKeywordUsed { let (ty, opt_sugg) = if let Some(ty) = ty.make_suggestable(tcx, false) {
span, (ty, Some((span, Applicability::MachineApplicable)))
ty, } else {
opt_sugg: Some((span, Applicability::MachineApplicable)) (ty, None)
.filter(|_| ty.is_suggestable(tcx, false)), };
}); tcx.sess.emit_err(TypeofReservedKeywordUsed { span, ty, opt_sugg });
ty ty
} }

View file

@ -1199,28 +1199,22 @@ fn infer_return_ty_for_fn_sig<'tcx>(
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.output(); 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( diag.span_suggestion(
ty.span, ty.span,
"replace with the correct return type", "replace with the correct return type",
ret_ty, ret_ty,
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} else if matches!(ret_ty.kind(), ty::FnDef(..)) { } else if matches!(ret_ty.kind(), ty::FnDef(..))
let fn_sig = ret_ty.fn_sig(tcx); && let Some(fn_sig) = ret_ty.fn_sig(tcx).make_suggestable(tcx, false)
if fn_sig {
.skip_binder() diag.span_suggestion(
.inputs_and_output ty.span,
.iter() "replace with the correct return type",
.all(|t| t.is_suggestable(tcx, false)) fn_sig,
{ Applicability::MachineApplicable,
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) { } else if let Some(sugg) = suggest_impl_trait(tcx, ret_ty, ty.span, hir_id, def_id) {
diag.span_suggestion( diag.span_suggestion(
ty.span, ty.span,
@ -1280,9 +1274,7 @@ fn suggest_impl_trait<'tcx>(
let trait_name = tcx.item_name(trait_def_id); let trait_name = tcx.item_name(trait_def_id);
let args_tuple = substs.type_at(1); let args_tuple = substs.type_at(1);
let ty::Tuple(types) = *args_tuple.kind() else { return None; }; let ty::Tuple(types) = *args_tuple.kind() else { return None; };
if !types.is_suggestable(tcx, false) { let types = types.make_suggestable(tcx, false)?;
return 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!(
@ -1337,7 +1329,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)
&& 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) && let Some(sugg) = formatter(tcx, infcx.resolve_vars_if_possible(substs), trait_def_id, assoc_item_def_id, item_ty)
{ {
return Some(sugg); 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::print::with_forced_trimmed_paths;
use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::util::IntTypeExt; 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::symbol::Ident;
use rustc_span::{Span, DUMMY_SP}; use rustc_span::{Span, DUMMY_SP};
@ -845,37 +847,23 @@ fn infer_placeholder_type<'a>(
) -> Ty<'a> { ) -> Ty<'a> {
// Attempts to make the type nameable by turning FnDefs into FnPtrs. // Attempts to make the type nameable by turning FnDefs into FnPtrs.
struct MakeNameable<'tcx> { struct MakeNameable<'tcx> {
success: bool,
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
} }
impl<'tcx> MakeNameable<'tcx> {
fn new(tcx: TyCtxt<'tcx>) -> Self {
MakeNameable { success: true, tcx }
}
}
impl<'tcx> TypeFolder<'tcx> for MakeNameable<'tcx> { impl<'tcx> TypeFolder<'tcx> for MakeNameable<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx self.tcx
} }
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
if !self.success { let ty = match *ty.kind() {
return ty;
}
match ty.kind() {
ty::FnDef(def_id, substs) => { 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,
ty::Closure(..) | ty::Generator(..) => { };
self.success = false;
ty ty.super_fold_with(self)
}
_ => ty.super_fold_with(self),
}
} }
} }
@ -898,15 +886,11 @@ fn infer_placeholder_type<'a>(
suggestions.clear(); suggestions.clear();
} }
// Suggesting unnameable types won't help. if let Some(ty) = ty.make_suggestable(tcx, false) {
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 {
err.span_suggestion( err.span_suggestion(
span, span,
&format!("provide a type for the {item}", item = kind), &format!("provide a type for the {item}", item = kind),
format!("{colon} {sugg_ty}"), format!("{colon} {ty}"),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} else { } else {
@ -923,15 +907,12 @@ 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() {
let mut mk_nameable = MakeNameable::new(tcx); if let Some(ty) = ty.make_suggestable(tcx, false) {
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 {
diag.span_suggestion( diag.span_suggestion(
span, span,
"replace with the correct type", "replace with the correct type",
sugg_ty, ty,
Applicability::MaybeIncorrect, Applicability::MachineApplicable,
); );
} else { } else {
with_forced_trimmed_paths!(diag.span_note( with_forced_trimmed_paths!(diag.span_note(

View file

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

View file

@ -3,8 +3,9 @@
use std::ops::ControlFlow; use std::ops::ControlFlow;
use crate::ty::{ use crate::ty::{
visit::TypeVisitable, AliasTy, Const, ConstKind, DefIdTree, InferConst, InferTy, Opaque, visit::TypeVisitable, AliasTy, Const, ConstKind, DefIdTree, FallibleTypeFolder, InferConst,
PolyTraitPredicate, Projection, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, InferTy, Opaque, PolyTraitPredicate, Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable,
TypeSuperVisitable, TypeVisitor,
}; };
use rustc_data_structures::fx::FxHashMap; 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. /// Whether this makes sense to suggest in a diagnostic.
/// ///
/// We filter out certain types and constants since they don't provide /// 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 /// Only if `infer_suggestable` is true, we consider type and const
/// 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>;
} }
impl<'tcx, T> IsSuggestable<'tcx> for T impl<'tcx, T> IsSuggestable<'tcx> for T
where where
T: TypeVisitable<'tcx>, T: TypeVisitable<'tcx> + TypeFoldable<'tcx>,
{ {
fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool { 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> {
self.try_fold_with(&mut MakeSuggestableFolder { tcx, infer_suggestable }).ok()
}
} }
pub fn suggest_arbitrary_trait_bound<'tcx>( pub fn suggest_arbitrary_trait_bound<'tcx>(
@ -509,3 +516,83 @@ impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> {
c.super_visit_with(self) 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 /// the infallible methods of this trait to ensure that the two APIs
/// are coherent. /// are coherent.
pub trait TypeFolder<'tcx>: FallibleTypeFolder<'tcx, Error = !> { 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> fn fold_binder<T>(&mut self, t: Binder<'tcx, T>) -> Binder<'tcx, T>
where 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`.