Skip to content

Commit 0b5941a

Browse files
Make const/fn return params more suggestable
1 parent 658fad6 commit 0b5941a

File tree

10 files changed

+163
-67
lines changed

10 files changed

+163
-67
lines changed

compiler/rustc_hir_analysis/src/astconv/mod.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -2945,12 +2945,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
29452945
if r.is_erased() { tcx.lifetimes.re_static } else { r }
29462946
});
29472947
let span = ast_ty.span;
2948-
tcx.sess.emit_err(TypeofReservedKeywordUsed {
2949-
span,
2950-
ty,
2951-
opt_sugg: Some((span, Applicability::MachineApplicable))
2952-
.filter(|_| ty.is_suggestable(tcx, false)),
2953-
});
2948+
let (ty, opt_sugg) = if let Some(ty) = ty.make_suggestable(tcx, false) {
2949+
(ty, Some((span, Applicability::MachineApplicable)))
2950+
} else {
2951+
(ty, None)
2952+
};
2953+
tcx.sess.emit_err(TypeofReservedKeywordUsed { span, ty, opt_sugg });
29542954

29552955
ty
29562956
}

compiler/rustc_hir_analysis/src/collect.rs

+12-20
Original file line numberDiff line numberDiff line change
@@ -1199,28 +1199,22 @@ fn infer_return_ty_for_fn_sig<'tcx>(
11991199
visitor.visit_ty(ty);
12001200
let mut diag = bad_placeholder(tcx, visitor.0, "return type");
12011201
let ret_ty = fn_sig.output();
1202-
if ret_ty.is_suggestable(tcx, false) {
1202+
if let Some(ret_ty) = ret_ty.make_suggestable(tcx, false) {
12031203
diag.span_suggestion(
12041204
ty.span,
12051205
"replace with the correct return type",
12061206
ret_ty,
12071207
Applicability::MachineApplicable,
12081208
);
1209-
} else if matches!(ret_ty.kind(), ty::FnDef(..)) {
1210-
let fn_sig = ret_ty.fn_sig(tcx);
1211-
if fn_sig
1212-
.skip_binder()
1213-
.inputs_and_output
1214-
.iter()
1215-
.all(|t| t.is_suggestable(tcx, false))
1216-
{
1217-
diag.span_suggestion(
1218-
ty.span,
1219-
"replace with the correct return type",
1220-
fn_sig,
1221-
Applicability::MachineApplicable,
1222-
);
1223-
}
1209+
} else if matches!(ret_ty.kind(), ty::FnDef(..))
1210+
&& let Some(fn_sig) = ret_ty.fn_sig(tcx).make_suggestable(tcx, false)
1211+
{
1212+
diag.span_suggestion(
1213+
ty.span,
1214+
"replace with the correct return type",
1215+
fn_sig,
1216+
Applicability::MachineApplicable,
1217+
);
12241218
} else if let Some(sugg) = suggest_impl_trait(tcx, ret_ty, ty.span, hir_id, def_id) {
12251219
diag.span_suggestion(
12261220
ty.span,
@@ -1280,9 +1274,7 @@ fn suggest_impl_trait<'tcx>(
12801274
let trait_name = tcx.item_name(trait_def_id);
12811275
let args_tuple = substs.type_at(1);
12821276
let ty::Tuple(types) = *args_tuple.kind() else { return None; };
1283-
if !types.is_suggestable(tcx, false) {
1284-
return None;
1285-
}
1277+
let types = types.make_suggestable(tcx, false)?;
12861278
let maybe_ret =
12871279
if item_ty.is_unit() { String::new() } else { format!(" -> {item_ty}") };
12881280
Some(format!(
@@ -1337,7 +1329,7 @@ fn suggest_impl_trait<'tcx>(
13371329
// FIXME(compiler-errors): We may benefit from resolving regions here.
13381330
if ocx.select_where_possible().is_empty()
13391331
&& let item_ty = infcx.resolve_vars_if_possible(item_ty)
1340-
&& item_ty.is_suggestable(tcx, false)
1332+
&& let Some(item_ty) = item_ty.make_suggestable(tcx, false)
13411333
&& let Some(sugg) = formatter(tcx, infcx.resolve_vars_if_possible(substs), trait_def_id, assoc_item_def_id, item_ty)
13421334
{
13431335
return Some(sugg);

compiler/rustc_hir_analysis/src/collect/type_of.rs

+14-33
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ use rustc_middle::hir::nested_filter;
88
use rustc_middle::ty::print::with_forced_trimmed_paths;
99
use rustc_middle::ty::subst::InternalSubsts;
1010
use rustc_middle::ty::util::IntTypeExt;
11-
use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable};
11+
use rustc_middle::ty::{
12+
self, DefIdTree, IsSuggestable, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable,
13+
};
1214
use rustc_span::symbol::Ident;
1315
use rustc_span::{Span, DUMMY_SP};
1416

@@ -845,37 +847,23 @@ fn infer_placeholder_type<'a>(
845847
) -> Ty<'a> {
846848
// Attempts to make the type nameable by turning FnDefs into FnPtrs.
847849
struct MakeNameable<'tcx> {
848-
success: bool,
849850
tcx: TyCtxt<'tcx>,
850851
}
851852

852-
impl<'tcx> MakeNameable<'tcx> {
853-
fn new(tcx: TyCtxt<'tcx>) -> Self {
854-
MakeNameable { success: true, tcx }
855-
}
856-
}
857-
858853
impl<'tcx> TypeFolder<'tcx> for MakeNameable<'tcx> {
859854
fn tcx(&self) -> TyCtxt<'tcx> {
860855
self.tcx
861856
}
862857

863858
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
864-
if !self.success {
865-
return ty;
866-
}
867-
868-
match ty.kind() {
859+
let ty = match *ty.kind() {
869860
ty::FnDef(def_id, substs) => {
870-
self.tcx.mk_fn_ptr(self.tcx.fn_sig(*def_id).subst(self.tcx, substs))
861+
self.tcx.mk_fn_ptr(self.tcx.fn_sig(def_id).subst(self.tcx, substs))
871862
}
872-
// FIXME: non-capturing closures should also suggest a function pointer
873-
ty::Closure(..) | ty::Generator(..) => {
874-
self.success = false;
875-
ty
876-
}
877-
_ => ty.super_fold_with(self),
878-
}
863+
_ => ty,
864+
};
865+
866+
ty.super_fold_with(self)
879867
}
880868
}
881869

@@ -898,15 +886,11 @@ fn infer_placeholder_type<'a>(
898886
suggestions.clear();
899887
}
900888

901-
// Suggesting unnameable types won't help.
902-
let mut mk_nameable = MakeNameable::new(tcx);
903-
let ty = mk_nameable.fold_ty(ty);
904-
let sugg_ty = if mk_nameable.success { Some(ty) } else { None };
905-
if let Some(sugg_ty) = sugg_ty {
889+
if let Some(ty) = ty.make_suggestable(tcx, false) {
906890
err.span_suggestion(
907891
span,
908892
&format!("provide a type for the {item}", item = kind),
909-
format!("{colon} {sugg_ty}"),
893+
format!("{colon} {ty}"),
910894
Applicability::MachineApplicable,
911895
);
912896
} else {
@@ -923,15 +907,12 @@ fn infer_placeholder_type<'a>(
923907
let mut diag = bad_placeholder(tcx, vec![span], kind);
924908

925909
if !ty.references_error() {
926-
let mut mk_nameable = MakeNameable::new(tcx);
927-
let ty = mk_nameable.fold_ty(ty);
928-
let sugg_ty = if mk_nameable.success { Some(ty) } else { None };
929-
if let Some(sugg_ty) = sugg_ty {
910+
if let Some(ty) = ty.make_suggestable(tcx, false) {
930911
diag.span_suggestion(
931912
span,
932913
"replace with the correct type",
933-
sugg_ty,
934-
Applicability::MaybeIncorrect,
914+
ty,
915+
Applicability::MachineApplicable,
935916
);
936917
} else {
937918
with_forced_trimmed_paths!(diag.span_note(

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -687,7 +687,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
687687
return true;
688688
}
689689
&hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => {
690-
if found.is_suggestable(self.tcx, false) {
690+
if let Some(found) = found.make_suggestable(self.tcx, false) {
691691
err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() });
692692
return true;
693693
} else if let ty::Closure(_, substs) = found.kind()

compiler/rustc_hir_typeck/src/op.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -490,9 +490,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
490490
if let Some(output_def_id) = output_def_id
491491
&& let Some(trait_def_id) = trait_def_id
492492
&& self.tcx.parent(output_def_id) == trait_def_id
493-
&& output_ty.is_suggestable(self.tcx, false)
493+
&& let Some(output_ty) = output_ty.make_suggestable(self.tcx, false)
494494
{
495-
Some(("Output", *output_ty))
495+
Some(("Output", output_ty))
496496
} else {
497497
None
498498
}

compiler/rustc_middle/src/ty/diagnostics.rs

+91-4
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
use std::ops::ControlFlow;
44

55
use crate::ty::{
6-
visit::TypeVisitable, AliasTy, Const, ConstKind, DefIdTree, InferConst, InferTy, Opaque,
7-
PolyTraitPredicate, Projection, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
6+
visit::TypeVisitable, AliasTy, Const, ConstKind, DefIdTree, FallibleTypeFolder, InferConst,
7+
InferTy, Opaque, PolyTraitPredicate, Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable,
8+
TypeSuperVisitable, TypeVisitor,
89
};
910

1011
use rustc_data_structures::fx::FxHashMap;
@@ -76,7 +77,7 @@ impl<'tcx> Ty<'tcx> {
7677
}
7778
}
7879

79-
pub trait IsSuggestable<'tcx> {
80+
pub trait IsSuggestable<'tcx>: Sized {
8081
/// Whether this makes sense to suggest in a diagnostic.
8182
///
8283
/// We filter out certain types and constants since they don't provide
@@ -87,15 +88,21 @@ pub trait IsSuggestable<'tcx> {
8788
/// Only if `infer_suggestable` is true, we consider type and const
8889
/// inference variables to be suggestable.
8990
fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool;
91+
92+
fn make_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> Option<Self>;
9093
}
9194

9295
impl<'tcx, T> IsSuggestable<'tcx> for T
9396
where
94-
T: TypeVisitable<'tcx>,
97+
T: TypeVisitable<'tcx> + TypeFoldable<'tcx>,
9598
{
9699
fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool {
97100
self.visit_with(&mut IsSuggestableVisitor { tcx, infer_suggestable }).is_continue()
98101
}
102+
103+
fn make_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> Option<T> {
104+
self.try_fold_with(&mut MakeSuggestableFolder { tcx, infer_suggestable }).ok()
105+
}
99106
}
100107

101108
pub fn suggest_arbitrary_trait_bound<'tcx>(
@@ -509,3 +516,83 @@ impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> {
509516
c.super_visit_with(self)
510517
}
511518
}
519+
520+
pub struct MakeSuggestableFolder<'tcx> {
521+
tcx: TyCtxt<'tcx>,
522+
infer_suggestable: bool,
523+
}
524+
525+
impl<'tcx> FallibleTypeFolder<'tcx> for MakeSuggestableFolder<'tcx> {
526+
type Error = ();
527+
528+
fn tcx(&self) -> TyCtxt<'tcx> {
529+
self.tcx
530+
}
531+
532+
fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
533+
let t = match *t.kind() {
534+
Infer(InferTy::TyVar(_)) if self.infer_suggestable => t,
535+
536+
FnDef(def_id, substs) => {
537+
self.tcx.mk_fn_ptr(self.tcx.fn_sig(def_id).subst(self.tcx, substs))
538+
}
539+
540+
// FIXME(compiler-errors): We could replace these with infer, I guess.
541+
Closure(..)
542+
| Infer(..)
543+
| Generator(..)
544+
| GeneratorWitness(..)
545+
| Bound(_, _)
546+
| Placeholder(_)
547+
| Error(_) => {
548+
return Err(());
549+
}
550+
551+
Alias(Opaque, AliasTy { def_id, .. }) => {
552+
let parent = self.tcx.parent(def_id);
553+
if let hir::def::DefKind::TyAlias | hir::def::DefKind::AssocTy = self.tcx.def_kind(parent)
554+
&& let Alias(Opaque, AliasTy { def_id: parent_opaque_def_id, .. }) = *self.tcx.type_of(parent).kind()
555+
&& parent_opaque_def_id == def_id
556+
{
557+
t
558+
} else {
559+
return Err(());
560+
}
561+
}
562+
563+
Param(param) => {
564+
// FIXME: It would be nice to make this not use string manipulation,
565+
// but it's pretty hard to do this, since `ty::ParamTy` is missing
566+
// sufficient info to determine if it is synthetic, and we don't
567+
// always have a convenient way of getting `ty::Generics` at the call
568+
// sites we invoke `IsSuggestable::is_suggestable`.
569+
if param.name.as_str().starts_with("impl ") {
570+
return Err(());
571+
}
572+
573+
t
574+
}
575+
576+
_ => t,
577+
};
578+
579+
t.try_super_fold_with(self)
580+
}
581+
582+
fn try_fold_const(&mut self, c: Const<'tcx>) -> Result<Const<'tcx>, ()> {
583+
let c = match c.kind() {
584+
ConstKind::Infer(InferConst::Var(_)) if self.infer_suggestable => c,
585+
586+
ConstKind::Infer(..)
587+
| ConstKind::Bound(..)
588+
| ConstKind::Placeholder(..)
589+
| ConstKind::Error(..) => {
590+
return Err(());
591+
}
592+
593+
_ => c,
594+
};
595+
596+
c.try_super_fold_with(self)
597+
}
598+
}

compiler/rustc_middle/src/ty/fold.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ pub trait TypeSuperFoldable<'tcx>: TypeFoldable<'tcx> {
105105
/// the infallible methods of this trait to ensure that the two APIs
106106
/// are coherent.
107107
pub trait TypeFolder<'tcx>: FallibleTypeFolder<'tcx, Error = !> {
108-
fn tcx<'a>(&'a self) -> TyCtxt<'tcx>;
108+
fn tcx(&self) -> TyCtxt<'tcx>;
109109

110110
fn fold_binder<T>(&mut self, t: Binder<'tcx, T>) -> Binder<'tcx, T>
111111
where
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// run-rustfix
2+
3+
#![allow(unused)]
4+
5+
struct Wrapper<T>(T);
6+
7+
fn bar() -> Wrapper<fn()> { Wrapper(foo) }
8+
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types
9+
10+
fn foo() {}
11+
12+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// run-rustfix
2+
3+
#![allow(unused)]
4+
5+
struct Wrapper<T>(T);
6+
7+
fn bar() -> _ { Wrapper(foo) }
8+
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types
9+
10+
fn foo() {}
11+
12+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
2+
--> $DIR/suggest-fn-ptr-for-fn-item-in-fn-ret.rs:7:13
3+
|
4+
LL | fn bar() -> _ { Wrapper(foo) }
5+
| ^
6+
| |
7+
| not allowed in type signatures
8+
| help: replace with the correct return type: `Wrapper<fn()>`
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0121`.

0 commit comments

Comments
 (0)