Make is_suggestable work on all TypeFoldable

This commit is contained in:
Michael Goulet 2022-06-05 18:45:45 -07:00
parent 9c47afe9fa
commit 55805ab473
8 changed files with 74 additions and 51 deletions

View File

@ -2509,11 +2509,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
labeled_user_string
);
let pred = format!("{}: {}", bound_kind, sub);
let suggestion = format!(
"{} {}",
generics.add_where_or_trailing_comma(),
pred,
);
let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred,);
err.span_suggestion(
generics.tail_span_for_predicate_suggestion(),
"consider adding a where clause",

View File

@ -3,8 +3,8 @@
use std::ops::ControlFlow;
use crate::ty::{
fold::TypeFoldable, Const, ConstKind, DefIdTree, ExistentialPredicate, InferTy, Ty, TyCtxt,
TypeVisitor,
fold::TypeFoldable, Const, ConstKind, DefIdTree, ExistentialPredicate, InferTy,
PolyTraitPredicate, Ty, TyCtxt, TypeSuperFoldable, TypeVisitor,
};
use rustc_data_structures::fx::FxHashMap;
@ -73,31 +73,53 @@ impl<'tcx> Ty<'tcx> {
_ => self.is_simple_ty(),
}
}
}
/// Whether the type can be safely suggested during error recovery.
pub fn is_suggestable(self, tcx: TyCtxt<'tcx>) -> bool {
self.visit_with(&mut IsSuggestableVisitor { tcx }).is_continue()
pub trait IsSuggestable<'tcx> {
fn is_suggestable(self, tcx: TyCtxt<'tcx>) -> bool;
fn is_suggestable_modulo_impl_trait(self, tcx: TyCtxt<'tcx>, bound_str: &str) -> bool;
}
impl<'tcx, T> IsSuggestable<'tcx> for T
where
T: TypeFoldable<'tcx>,
{
fn is_suggestable(self, tcx: TyCtxt<'tcx>) -> bool {
self.visit_with(&mut IsSuggestableVisitor { tcx, bound_str: None }).is_continue()
}
fn is_suggestable_modulo_impl_trait(self, tcx: TyCtxt<'tcx>, bound_str: &str) -> bool {
self.visit_with(&mut IsSuggestableVisitor { tcx, bound_str: Some(bound_str) }).is_continue()
}
}
pub fn suggest_arbitrary_trait_bound(
pub fn suggest_arbitrary_trait_bound<'tcx>(
tcx: TyCtxt<'tcx>,
generics: &hir::Generics<'_>,
err: &mut Diagnostic,
param_name: &str,
constraint: &str,
trait_pred: PolyTraitPredicate<'tcx>,
) -> bool {
let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
match (param, param_name) {
(Some(_), "Self") => return false,
_ => {}
if !trait_pred.is_suggestable(tcx) {
return false;
}
let param_name = trait_pred.skip_binder().self_ty().to_string();
let constraint = trait_pred.print_modifiers_and_trait_path().to_string();
let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
// Skip, there is a param named Self
if param.is_some() && param_name == "Self" {
return false;
}
// Suggest a where clause bound for a non-type parameter.
err.span_suggestion_verbose(
generics.tail_span_for_predicate_suggestion(),
&format!(
"consider {} `where` clause, but there might be an alternative better way to express \
this requirement",
if generics.has_where_clause_token { "extending the" } else { "introducing a" },
if generics.has_where_clause_token { "extending the" } else { "introducing a" },
),
format!("{} {}: {}", generics.add_where_or_trailing_comma(), param_name, constraint),
Applicability::MaybeIncorrect,
@ -395,11 +417,12 @@ impl<'v> hir::intravisit::Visitor<'v> for StaticLifetimeVisitor<'v> {
}
}
pub struct IsSuggestableVisitor<'tcx> {
pub struct IsSuggestableVisitor<'tcx, 's> {
tcx: TyCtxt<'tcx>,
bound_str: Option<&'s str>,
}
impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> {
impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx, '_> {
type BreakTy = ();
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
@ -438,6 +461,16 @@ impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> {
}
}
Param(param) => {
if let Some(found_bound_str) =
param.name.as_str().strip_prefix("impl ").map(|s| s.trim_start())
{
if self.bound_str.map_or(true, |bound_str| bound_str != found_bound_str) {
return ControlFlow::Break(());
}
}
}
_ => {}
}

View File

@ -21,11 +21,9 @@ use rustc_hir::lang_items::LangItem;
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node};
use rustc_middle::hir::map;
use rustc_middle::ty::{
self,
subst::{GenericArgKind, SubstsRef},
suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree,
GeneratorDiagnosticData, GeneratorInteriorTypeCause, Infer, InferTy, ToPredicate, Ty, TyCtxt,
TypeFoldable,
self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree,
GeneratorDiagnosticData, GeneratorInteriorTypeCause, Infer, InferTy, IsSuggestable,
ToPredicate, Ty, TyCtxt, TypeFoldable,
};
use rustc_middle::ty::{TypeAndMut, TypeckResults};
use rustc_session::Limit;
@ -358,11 +356,14 @@ fn suggest_restriction<'tcx>(
ty::Param(param) => {
// `fn foo(t: impl Trait)`
// ^^^^^ get this string
param.name.as_str().strip_prefix("impl").map(|s| (s.trim_start().to_string(), sig))
param.name.as_str().strip_prefix("impl ").map(|s| (s.trim_start().to_string(), sig))
}
_ => None,
})
{
if !trait_pred.is_suggestable_modulo_impl_trait(tcx, &bound_str) {
return;
}
// We know we have an `impl Trait` that doesn't satisfy a required projection.
// Find all of the occurrences of `impl Trait` for `Trait` in the function arguments'
@ -417,6 +418,9 @@ fn suggest_restriction<'tcx>(
Applicability::MaybeIncorrect,
);
} else {
if !trait_pred.is_suggestable(tcx) {
return;
}
// Trivial case: `T` needs an extra bound: `T: Bound`.
let (sp, suggestion) = match (
generics
@ -463,16 +467,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
_ => (false, None),
};
let generic_args_have_impl_trait = |args: SubstsRef<'tcx>| -> bool {
args.iter().any(|arg| match arg.unpack() {
GenericArgKind::Type(ty) => match ty.kind() {
ty::Param(param) => param.name.as_str().starts_with("impl"),
_ => false,
},
_ => false,
})
};
// FIXME: Add check for trait bound that is already present, particularly `?Sized` so we
// don't suggest `T: Sized + ?Sized`.
let mut hir_id = body_id;
@ -574,6 +568,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
| hir::Node::ImplItem(hir::ImplItem { generics, .. })
if param_ty =>
{
if !trait_pred.skip_binder().trait_ref.substs[1..]
.iter()
.all(|g| g.is_suggestable(self.tcx))
{
return;
}
// Missing generic type parameter bound.
let param_name = self_ty.to_string();
let constraint = with_no_trimmed_paths!(
@ -603,13 +603,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
| hir::ItemKind::TraitAlias(generics, _)
| hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }),
..
}) if !param_ty
&& !generic_args_have_impl_trait(trait_pred.skip_binder().trait_ref.substs) =>
{
}) if !param_ty => {
// Missing generic type parameter bound.
let param_name = self_ty.to_string();
let constraint = trait_pred.print_modifiers_and_trait_path().to_string();
if suggest_arbitrary_trait_bound(generics, &mut err, &param_name, &constraint) {
if suggest_arbitrary_trait_bound(self.tcx, generics, &mut err, trait_pred) {
return;
}
}

View File

@ -13,7 +13,7 @@ use rustc_hir::def_id::DefId;
use rustc_hir::GenericArg;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::ty::{
self, subst, subst::SubstsRef, GenericParamDef, GenericParamDefKind, Ty, TyCtxt,
self, subst, subst::SubstsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, TyCtxt,
};
use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS;
use rustc_span::{symbol::kw, Span};

View File

@ -27,7 +27,9 @@ use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin};
use rustc_middle::middle::stability::AllowUnstable;
use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, Subst, SubstsRef};
use rustc_middle::ty::GenericParamDefKind;
use rustc_middle::ty::{self, Const, DefIdTree, EarlyBinder, Ty, TyCtxt, TypeFoldable};
use rustc_middle::ty::{
self, Const, DefIdTree, EarlyBinder, IsSuggestable, Ty, TyCtxt, TypeFoldable,
};
use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECTS};
use rustc_span::edition::Edition;
use rustc_span::lev_distance::find_best_match_for_name;

View File

@ -15,7 +15,7 @@ use rustc_infer::infer::{self, TyCtxtInferExt};
use rustc_infer::traits;
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, Binder, ToPredicate, Ty};
use rustc_middle::ty::{self, Binder, IsSuggestable, ToPredicate, Ty};
use rustc_span::symbol::{kw, sym};
use rustc_span::Span;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;

View File

@ -817,11 +817,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
trait bound{s}",
s = pluralize!(obligations.len())
),
format!(
"{} {}",
add_where_or_comma,
obligations.join(", ")
),
format!("{} {}", add_where_or_comma, obligations.join(", ")),
Applicability::MaybeIncorrect,
);
}

View File

@ -39,7 +39,7 @@ use rustc_middle::ty::query::Providers;
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::util::Discr;
use rustc_middle::ty::util::IntTypeExt;
use rustc_middle::ty::{self, AdtKind, Const, DefIdTree, Ty, TyCtxt};
use rustc_middle::ty::{self, AdtKind, Const, DefIdTree, IsSuggestable, Ty, TyCtxt};
use rustc_middle::ty::{ReprOptions, ToPredicate};
use rustc_session::lint;
use rustc_session::parse::feature_err;