Make const/fn return params more suggestable
This commit is contained in:
parent
658fad6c55
commit
0b5941aa11
10 changed files with 163 additions and 67 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {}
|
12
tests/ui/suggestions/suggest-fn-ptr-for-fn-item-in-fn-ret.rs
Normal file
12
tests/ui/suggestions/suggest-fn-ptr-for-fn-item-in-fn-ret.rs
Normal 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() {}
|
|
@ -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`.
|
Loading…
Add table
Reference in a new issue