diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index bec9f0ff0772c..3d5f189e233bb 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -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 } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index cc7235a61c0b8..80426c239ac8b 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -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); diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index e7b0846e10352..c5522c94874dd 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -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( diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 3f433a0928c55..11d47053ade79 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -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() diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 67769fe4478a2..ba72aefe39c16 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -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 } diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 4b4518f61e8d3..cd9b927014077 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -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; } 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 { + 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, 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, ()> { + 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) + } +} diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 1445bc1ed32e6..8a0019bc0127c 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -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(&mut self, t: Binder<'tcx, T>) -> Binder<'tcx, T> where diff --git a/tests/ui/suggestions/suggest-fn-ptr-for-fn-item-in-fn-ret.fixed b/tests/ui/suggestions/suggest-fn-ptr-for-fn-item-in-fn-ret.fixed new file mode 100644 index 0000000000000..abb9ef9177432 --- /dev/null +++ b/tests/ui/suggestions/suggest-fn-ptr-for-fn-item-in-fn-ret.fixed @@ -0,0 +1,12 @@ +// run-rustfix + +#![allow(unused)] + +struct Wrapper(T); + +fn bar() -> Wrapper { Wrapper(foo) } +//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types + +fn foo() {} + +fn main() {} diff --git a/tests/ui/suggestions/suggest-fn-ptr-for-fn-item-in-fn-ret.rs b/tests/ui/suggestions/suggest-fn-ptr-for-fn-item-in-fn-ret.rs new file mode 100644 index 0000000000000..d2a79c3869418 --- /dev/null +++ b/tests/ui/suggestions/suggest-fn-ptr-for-fn-item-in-fn-ret.rs @@ -0,0 +1,12 @@ +// run-rustfix + +#![allow(unused)] + +struct Wrapper(T); + +fn bar() -> _ { Wrapper(foo) } +//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types + +fn foo() {} + +fn main() {} diff --git a/tests/ui/suggestions/suggest-fn-ptr-for-fn-item-in-fn-ret.stderr b/tests/ui/suggestions/suggest-fn-ptr-for-fn-item-in-fn-ret.stderr new file mode 100644 index 0000000000000..347a038525b60 --- /dev/null +++ b/tests/ui/suggestions/suggest-fn-ptr-for-fn-item-in-fn-ret.stderr @@ -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` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0121`.