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 }
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -1199,20 +1199,15 @@ 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()
|
|
||||||
.inputs_and_output
|
|
||||||
.iter()
|
|
||||||
.all(|t| t.is_suggestable(tcx, false))
|
|
||||||
{
|
{
|
||||||
diag.span_suggestion(
|
diag.span_suggestion(
|
||||||
ty.span,
|
ty.span,
|
||||||
|
@ -1220,7 +1215,6 @@ fn infer_return_ty_for_fn_sig<'tcx>(
|
||||||
fn_sig,
|
fn_sig,
|
||||||
Applicability::MachineApplicable,
|
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);
|
||||||
|
|
|
@ -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::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();
|
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(
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
Add a link
Reference in a new issue