728 lines
26 KiB
Rust
728 lines
26 KiB
Rust
//! Diagnostics related methods for `Ty`.
|
|
|
|
use std::fmt::Write;
|
|
use std::ops::ControlFlow;
|
|
|
|
use rustc_data_structures::fx::FxHashMap;
|
|
use rustc_errors::{Applicability, Diag, DiagArgValue, IntoDiagArg, into_diag_arg_using_display};
|
|
use rustc_hir::def::DefKind;
|
|
use rustc_hir::def_id::DefId;
|
|
use rustc_hir::{self as hir, LangItem, PredicateOrigin, WherePredicateKind};
|
|
use rustc_span::{BytePos, Span};
|
|
use rustc_type_ir::TyKind::*;
|
|
|
|
use crate::ty::{
|
|
self, AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Opaque,
|
|
PolyTraitPredicate, Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable,
|
|
TypeSuperVisitable, TypeVisitable, TypeVisitor,
|
|
};
|
|
|
|
into_diag_arg_using_display! {
|
|
Ty<'_>,
|
|
ty::Region<'_>,
|
|
}
|
|
|
|
impl<'tcx> Ty<'tcx> {
|
|
/// Similar to `Ty::is_primitive`, but also considers inferred numeric values to be primitive.
|
|
pub fn is_primitive_ty(self) -> bool {
|
|
matches!(
|
|
self.kind(),
|
|
Bool | Char
|
|
| Str
|
|
| Int(_)
|
|
| Uint(_)
|
|
| Float(_)
|
|
| Infer(
|
|
InferTy::IntVar(_)
|
|
| InferTy::FloatVar(_)
|
|
| InferTy::FreshIntTy(_)
|
|
| InferTy::FreshFloatTy(_)
|
|
)
|
|
)
|
|
}
|
|
|
|
/// Whether the type is succinctly representable as a type instead of just referred to with a
|
|
/// description in error messages. This is used in the main error message.
|
|
pub fn is_simple_ty(self) -> bool {
|
|
match self.kind() {
|
|
Bool
|
|
| Char
|
|
| Str
|
|
| Int(_)
|
|
| Uint(_)
|
|
| Float(_)
|
|
| Infer(
|
|
InferTy::IntVar(_)
|
|
| InferTy::FloatVar(_)
|
|
| InferTy::FreshIntTy(_)
|
|
| InferTy::FreshFloatTy(_),
|
|
) => true,
|
|
Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(),
|
|
Tuple(tys) if tys.is_empty() => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
/// Whether the type is succinctly representable as a type instead of just referred to with a
|
|
/// description in error messages. This is used in the primary span label. Beyond what
|
|
/// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to
|
|
/// ADTs with no type arguments.
|
|
pub fn is_simple_text(self, tcx: TyCtxt<'tcx>) -> bool {
|
|
match self.kind() {
|
|
Adt(_, args) => args.non_erasable_generics().next().is_none(),
|
|
Ref(_, ty, _) => ty.is_simple_text(tcx),
|
|
_ => self.is_simple_ty(),
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
/// meaningful rendered suggestions when pretty-printed. We leave some
|
|
/// nonsense, such as region vars, since those render as `'_` and are
|
|
/// usually okay to reinterpret as elided lifetimes.
|
|
///
|
|
/// 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,
|
|
placeholder: Option<Ty<'tcx>>,
|
|
) -> Option<Self>;
|
|
}
|
|
|
|
impl<'tcx, T> IsSuggestable<'tcx> for T
|
|
where
|
|
T: TypeVisitable<TyCtxt<'tcx>> + TypeFoldable<TyCtxt<'tcx>>,
|
|
{
|
|
#[tracing::instrument(level = "debug", skip(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,
|
|
placeholder: Option<Ty<'tcx>>,
|
|
) -> Option<T> {
|
|
self.try_fold_with(&mut MakeSuggestableFolder { tcx, infer_suggestable, placeholder }).ok()
|
|
}
|
|
}
|
|
|
|
pub fn suggest_arbitrary_trait_bound<'tcx>(
|
|
tcx: TyCtxt<'tcx>,
|
|
generics: &hir::Generics<'_>,
|
|
err: &mut Diag<'_>,
|
|
trait_pred: PolyTraitPredicate<'tcx>,
|
|
associated_ty: Option<(&'static str, Ty<'tcx>)>,
|
|
) -> bool {
|
|
if !trait_pred.is_suggestable(tcx, false) {
|
|
return false;
|
|
}
|
|
|
|
let param_name = trait_pred.skip_binder().self_ty().to_string();
|
|
let mut constraint = trait_pred.to_string();
|
|
|
|
if let Some((name, term)) = associated_ty {
|
|
// FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err.
|
|
// That should be extracted into a helper function.
|
|
if constraint.ends_with('>') {
|
|
constraint = format!("{}, {} = {}>", &constraint[..constraint.len() - 1], name, term);
|
|
} else {
|
|
constraint.push_str(&format!("<{name} = {term}>"));
|
|
}
|
|
}
|
|
|
|
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.where_clause_span.is_empty() { "introducing a" } else { "extending the" },
|
|
),
|
|
format!("{} {constraint}", generics.add_where_or_trailing_comma()),
|
|
Applicability::MaybeIncorrect,
|
|
);
|
|
true
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum SuggestChangingConstraintsMessage<'a> {
|
|
RestrictBoundFurther,
|
|
RestrictType { ty: &'a str },
|
|
RestrictTypeFurther { ty: &'a str },
|
|
RemoveMaybeUnsized,
|
|
ReplaceMaybeUnsizedWithSized,
|
|
}
|
|
|
|
fn suggest_changing_unsized_bound(
|
|
generics: &hir::Generics<'_>,
|
|
suggestions: &mut Vec<(Span, String, String, SuggestChangingConstraintsMessage<'_>)>,
|
|
param: &hir::GenericParam<'_>,
|
|
def_id: Option<DefId>,
|
|
) {
|
|
// See if there's a `?Sized` bound that can be removed to suggest that.
|
|
// First look at the `where` clause because we can have `where T: ?Sized`,
|
|
// then look at params.
|
|
for (where_pos, predicate) in generics.predicates.iter().enumerate() {
|
|
let WherePredicateKind::BoundPredicate(predicate) = predicate.kind else {
|
|
continue;
|
|
};
|
|
if !predicate.is_param_bound(param.def_id.to_def_id()) {
|
|
continue;
|
|
};
|
|
|
|
let unsized_bounds = predicate
|
|
.bounds
|
|
.iter()
|
|
.enumerate()
|
|
.filter(|(_, bound)| {
|
|
if let hir::GenericBound::Trait(poly) = bound
|
|
&& let hir::BoundPolarity::Maybe(_) = poly.modifiers.polarity
|
|
&& poly.trait_ref.trait_def_id() == def_id
|
|
{
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
if unsized_bounds.is_empty() {
|
|
continue;
|
|
}
|
|
|
|
let mut push_suggestion =
|
|
|sp, msg| suggestions.push((sp, "Sized".to_string(), String::new(), msg));
|
|
|
|
if predicate.bounds.len() == unsized_bounds.len() {
|
|
// All the bounds are unsized bounds, e.g.
|
|
// `T: ?Sized + ?Sized` or `_: impl ?Sized + ?Sized`,
|
|
// so in this case:
|
|
// - if it's an impl trait predicate suggest changing the
|
|
// the first bound to sized and removing the rest
|
|
// - Otherwise simply suggest removing the entire predicate
|
|
if predicate.origin == PredicateOrigin::ImplTrait {
|
|
let first_bound = unsized_bounds[0].1;
|
|
let first_bound_span = first_bound.span();
|
|
if first_bound_span.can_be_used_for_suggestions() {
|
|
let question_span =
|
|
first_bound_span.with_hi(first_bound_span.lo() + BytePos(1));
|
|
push_suggestion(
|
|
question_span,
|
|
SuggestChangingConstraintsMessage::ReplaceMaybeUnsizedWithSized,
|
|
);
|
|
|
|
for (pos, _) in unsized_bounds.iter().skip(1) {
|
|
let sp = generics.span_for_bound_removal(where_pos, *pos);
|
|
push_suggestion(sp, SuggestChangingConstraintsMessage::RemoveMaybeUnsized);
|
|
}
|
|
}
|
|
} else {
|
|
let sp = generics.span_for_predicate_removal(where_pos);
|
|
push_suggestion(sp, SuggestChangingConstraintsMessage::RemoveMaybeUnsized);
|
|
}
|
|
} else {
|
|
// Some of the bounds are other than unsized.
|
|
// So push separate removal suggestion for each unsized bound
|
|
for (pos, _) in unsized_bounds {
|
|
let sp = generics.span_for_bound_removal(where_pos, pos);
|
|
push_suggestion(sp, SuggestChangingConstraintsMessage::RemoveMaybeUnsized);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Suggest restricting a type param with a new bound.
|
|
///
|
|
/// If `span_to_replace` is provided, then that span will be replaced with the
|
|
/// `constraint`. If one wasn't provided, then the full bound will be suggested.
|
|
pub fn suggest_constraining_type_param(
|
|
tcx: TyCtxt<'_>,
|
|
generics: &hir::Generics<'_>,
|
|
err: &mut Diag<'_>,
|
|
param_name: &str,
|
|
constraint: &str,
|
|
def_id: Option<DefId>,
|
|
span_to_replace: Option<Span>,
|
|
) -> bool {
|
|
suggest_constraining_type_params(
|
|
tcx,
|
|
generics,
|
|
err,
|
|
[(param_name, constraint, def_id)].into_iter(),
|
|
span_to_replace,
|
|
)
|
|
}
|
|
|
|
/// Suggest restricting a type param with a new bound.
|
|
pub fn suggest_constraining_type_params<'a>(
|
|
tcx: TyCtxt<'_>,
|
|
generics: &hir::Generics<'_>,
|
|
err: &mut Diag<'_>,
|
|
param_names_and_constraints: impl Iterator<Item = (&'a str, &'a str, Option<DefId>)>,
|
|
span_to_replace: Option<Span>,
|
|
) -> bool {
|
|
let mut grouped = FxHashMap::default();
|
|
let mut unstable_suggestion = false;
|
|
param_names_and_constraints.for_each(|(param_name, constraint, def_id)| {
|
|
let stable = match def_id {
|
|
Some(def_id) => match tcx.lookup_stability(def_id) {
|
|
Some(s) => s.level.is_stable(),
|
|
None => true,
|
|
},
|
|
None => true,
|
|
};
|
|
if stable || tcx.sess.is_nightly_build() {
|
|
grouped.entry(param_name).or_insert(Vec::new()).push((constraint, def_id));
|
|
if !stable {
|
|
unstable_suggestion = true;
|
|
}
|
|
}
|
|
});
|
|
|
|
let mut applicability = Applicability::MachineApplicable;
|
|
let mut suggestions = Vec::new();
|
|
|
|
for (param_name, mut constraints) in grouped {
|
|
let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
|
|
let Some(param) = param else { return false };
|
|
|
|
{
|
|
let mut sized_constraints = constraints.extract_if(|(_, def_id)| {
|
|
def_id.is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Sized))
|
|
});
|
|
if let Some((_, def_id)) = sized_constraints.next() {
|
|
applicability = Applicability::MaybeIncorrect;
|
|
|
|
err.span_label(param.span, "this type parameter needs to be `Sized`");
|
|
suggest_changing_unsized_bound(generics, &mut suggestions, param, def_id);
|
|
}
|
|
}
|
|
|
|
// in the scenario like impl has stricter requirements than trait,
|
|
// we should not suggest restrict bound on the impl, here we double check
|
|
// the whether the param already has the constraint by checking `def_id`
|
|
let bound_trait_defs: Vec<DefId> = generics
|
|
.bounds_for_param(param.def_id)
|
|
.flat_map(|bound| {
|
|
bound.bounds.iter().flat_map(|b| b.trait_ref().and_then(|t| t.trait_def_id()))
|
|
})
|
|
.collect();
|
|
|
|
constraints
|
|
.retain(|(_, def_id)| def_id.map_or(true, |def| !bound_trait_defs.contains(&def)));
|
|
|
|
if constraints.is_empty() {
|
|
continue;
|
|
}
|
|
|
|
let mut constraint = constraints.iter().map(|&(c, _)| c).collect::<Vec<_>>();
|
|
constraint.sort();
|
|
constraint.dedup();
|
|
let constraint = constraint.join(" + ");
|
|
let mut suggest_restrict = |span, bound_list_non_empty, open_paren_sp| {
|
|
let suggestion = if span_to_replace.is_some() {
|
|
constraint.clone()
|
|
} else if constraint.starts_with('<') {
|
|
constraint.clone()
|
|
} else if bound_list_non_empty {
|
|
format!(" + {constraint}")
|
|
} else {
|
|
format!(" {constraint}")
|
|
};
|
|
|
|
use SuggestChangingConstraintsMessage::RestrictBoundFurther;
|
|
|
|
if let Some(open_paren_sp) = open_paren_sp {
|
|
suggestions.push((
|
|
open_paren_sp,
|
|
constraint.clone(),
|
|
"(".to_string(),
|
|
RestrictBoundFurther,
|
|
));
|
|
suggestions.push((
|
|
span,
|
|
constraint.clone(),
|
|
format!("){suggestion}"),
|
|
RestrictBoundFurther,
|
|
));
|
|
} else {
|
|
suggestions.push((span, constraint.clone(), suggestion, RestrictBoundFurther));
|
|
}
|
|
};
|
|
|
|
if let Some(span) = span_to_replace {
|
|
suggest_restrict(span, true, None);
|
|
continue;
|
|
}
|
|
|
|
// When the type parameter has been provided bounds
|
|
//
|
|
// Message:
|
|
// fn foo<T>(t: T) where T: Foo { ... }
|
|
// ^^^^^^
|
|
// |
|
|
// help: consider further restricting this bound with `+ Bar`
|
|
//
|
|
// Suggestion:
|
|
// fn foo<T>(t: T) where T: Foo { ... }
|
|
// ^
|
|
// |
|
|
// replace with: ` + Bar`
|
|
//
|
|
// Or, if user has provided some bounds, suggest restricting them:
|
|
//
|
|
// fn foo<T: Foo>(t: T) { ... }
|
|
// ---
|
|
// |
|
|
// help: consider further restricting this bound with `+ Bar`
|
|
//
|
|
// Suggestion for tools in this case is:
|
|
//
|
|
// fn foo<T: Foo>(t: T) { ... }
|
|
// --
|
|
// |
|
|
// replace with: `T: Bar +`
|
|
|
|
if let Some((span, open_paren_sp)) = generics.bounds_span_for_suggestions(param.def_id) {
|
|
suggest_restrict(span, true, open_paren_sp);
|
|
continue;
|
|
}
|
|
|
|
if generics.has_where_clause_predicates {
|
|
// This part is a bit tricky, because using the `where` clause user can
|
|
// provide zero, one or many bounds for the same type parameter, so we
|
|
// have following cases to consider:
|
|
//
|
|
// When the type parameter has been provided zero bounds
|
|
//
|
|
// Message:
|
|
// fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
|
|
// - help: consider restricting this type parameter with `where X: Bar`
|
|
//
|
|
// Suggestion:
|
|
// fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
|
|
// - insert: `, X: Bar`
|
|
suggestions.push((
|
|
generics.tail_span_for_predicate_suggestion(),
|
|
constraint.clone(),
|
|
constraints.iter().fold(String::new(), |mut string, &(constraint, _)| {
|
|
write!(string, ", {param_name}: {constraint}").unwrap();
|
|
string
|
|
}),
|
|
SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
|
|
));
|
|
continue;
|
|
}
|
|
|
|
// Additionally, there may be no `where` clause but the generic parameter has a default:
|
|
//
|
|
// Message:
|
|
// trait Foo<T=()> {... }
|
|
// - help: consider further restricting this type parameter with `where T: Zar`
|
|
//
|
|
// Suggestion:
|
|
// trait Foo<T=()> {... }
|
|
// - insert: `where T: Zar`
|
|
if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) {
|
|
// If we are here and the where clause span is of non-zero length
|
|
// it means we're dealing with an empty where clause like this:
|
|
// fn foo<X>(x: X) where { ... }
|
|
// In that case we don't want to add another "where" (Fixes #120838)
|
|
let where_prefix = if generics.where_clause_span.is_empty() { " where" } else { "" };
|
|
|
|
// Suggest a bound, but there is no existing `where` clause *and* the type param has a
|
|
// default (`<T=Foo>`), so we suggest adding `where T: Bar`.
|
|
suggestions.push((
|
|
generics.tail_span_for_predicate_suggestion(),
|
|
constraint.clone(),
|
|
format!("{where_prefix} {param_name}: {constraint}"),
|
|
SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
|
|
));
|
|
continue;
|
|
}
|
|
|
|
// If user has provided a colon, don't suggest adding another:
|
|
//
|
|
// fn foo<T:>(t: T) { ... }
|
|
// - insert: consider restricting this type parameter with `T: Foo`
|
|
if let Some(colon_span) = param.colon_span {
|
|
suggestions.push((
|
|
colon_span.shrink_to_hi(),
|
|
constraint.clone(),
|
|
format!(" {constraint}"),
|
|
SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
|
|
));
|
|
continue;
|
|
}
|
|
|
|
// If user hasn't provided any bounds, suggest adding a new one:
|
|
//
|
|
// fn foo<T>(t: T) { ... }
|
|
// - help: consider restricting this type parameter with `T: Foo`
|
|
suggestions.push((
|
|
param.span.shrink_to_hi(),
|
|
constraint.clone(),
|
|
format!(": {constraint}"),
|
|
SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
|
|
));
|
|
}
|
|
|
|
// FIXME: remove the suggestions that are from derive, as the span is not correct
|
|
suggestions = suggestions
|
|
.into_iter()
|
|
.filter(|(span, _, _, _)| !span.in_derive_expansion())
|
|
.collect::<Vec<_>>();
|
|
|
|
if suggestions.len() == 1 {
|
|
let (span, constraint, suggestion, msg) = suggestions.pop().unwrap();
|
|
let post = format!(
|
|
" with {}trait{} `{constraint}`",
|
|
if unstable_suggestion { "unstable " } else { "" },
|
|
if constraint.contains('+') { "s" } else { "" },
|
|
);
|
|
let msg = match msg {
|
|
SuggestChangingConstraintsMessage::RestrictBoundFurther => {
|
|
format!("consider further restricting this bound{post}")
|
|
}
|
|
SuggestChangingConstraintsMessage::RestrictType { ty } => {
|
|
format!("consider restricting type parameter `{ty}`{post}")
|
|
}
|
|
SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } => {
|
|
format!("consider further restricting type parameter `{ty}`{post}")
|
|
}
|
|
SuggestChangingConstraintsMessage::RemoveMaybeUnsized => {
|
|
format!("consider removing the `?Sized` bound to make the type parameter `Sized`")
|
|
}
|
|
SuggestChangingConstraintsMessage::ReplaceMaybeUnsizedWithSized => {
|
|
format!("consider replacing `?Sized` with `Sized`")
|
|
}
|
|
};
|
|
|
|
err.span_suggestion_verbose(span, msg, suggestion, applicability);
|
|
} else if suggestions.len() > 1 {
|
|
let post = if unstable_suggestion { " (some of them are unstable traits)" } else { "" };
|
|
err.multipart_suggestion_verbose(
|
|
format!("consider restricting type parameters{post}"),
|
|
suggestions.into_iter().map(|(span, _, suggestion, _)| (span, suggestion)).collect(),
|
|
applicability,
|
|
);
|
|
}
|
|
|
|
true
|
|
}
|
|
|
|
/// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
|
|
pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>);
|
|
|
|
impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> {
|
|
fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
|
|
match ty.kind {
|
|
hir::TyKind::TraitObject(
|
|
_,
|
|
hir::Lifetime {
|
|
res:
|
|
hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static,
|
|
..
|
|
},
|
|
_,
|
|
)
|
|
| hir::TyKind::OpaqueDef(..) => self.0.push(ty),
|
|
_ => {}
|
|
}
|
|
hir::intravisit::walk_ty(self, ty);
|
|
}
|
|
}
|
|
|
|
/// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
|
|
pub struct StaticLifetimeVisitor<'tcx>(pub Vec<Span>, pub crate::hir::map::Map<'tcx>);
|
|
|
|
impl<'v> hir::intravisit::Visitor<'v> for StaticLifetimeVisitor<'v> {
|
|
fn visit_lifetime(&mut self, lt: &'v hir::Lifetime) {
|
|
if let hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static = lt.res
|
|
{
|
|
self.0.push(lt.ident.span);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct IsSuggestableVisitor<'tcx> {
|
|
tcx: TyCtxt<'tcx>,
|
|
infer_suggestable: bool,
|
|
}
|
|
|
|
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IsSuggestableVisitor<'tcx> {
|
|
type Result = ControlFlow<()>;
|
|
|
|
fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
|
|
match *t.kind() {
|
|
Infer(InferTy::TyVar(_)) if self.infer_suggestable => {}
|
|
|
|
FnDef(..)
|
|
| Closure(..)
|
|
| Infer(..)
|
|
| Coroutine(..)
|
|
| CoroutineWitness(..)
|
|
| Bound(_, _)
|
|
| Placeholder(_)
|
|
| Error(_) => {
|
|
return ControlFlow::Break(());
|
|
}
|
|
|
|
Alias(Opaque, AliasTy { def_id, .. }) => {
|
|
let parent = self.tcx.parent(def_id);
|
|
let parent_ty = self.tcx.type_of(parent).instantiate_identity();
|
|
if let DefKind::TyAlias | DefKind::AssocTy = self.tcx.def_kind(parent)
|
|
&& let Alias(Opaque, AliasTy { def_id: parent_opaque_def_id, .. }) =
|
|
*parent_ty.kind()
|
|
&& parent_opaque_def_id == def_id
|
|
{
|
|
// Okay
|
|
} else {
|
|
return ControlFlow::Break(());
|
|
}
|
|
}
|
|
|
|
Alias(Projection, AliasTy { def_id, .. }) => {
|
|
if self.tcx.def_kind(def_id) != DefKind::AssocTy {
|
|
return ControlFlow::Break(());
|
|
}
|
|
}
|
|
|
|
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 ControlFlow::Break(());
|
|
}
|
|
}
|
|
|
|
_ => {}
|
|
}
|
|
|
|
t.super_visit_with(self)
|
|
}
|
|
|
|
fn visit_const(&mut self, c: Const<'tcx>) -> Self::Result {
|
|
match c.kind() {
|
|
ConstKind::Infer(InferConst::Var(_)) if self.infer_suggestable => {}
|
|
|
|
ConstKind::Infer(..)
|
|
| ConstKind::Bound(..)
|
|
| ConstKind::Placeholder(..)
|
|
| ConstKind::Error(..) => {
|
|
return ControlFlow::Break(());
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
c.super_visit_with(self)
|
|
}
|
|
}
|
|
|
|
pub struct MakeSuggestableFolder<'tcx> {
|
|
tcx: TyCtxt<'tcx>,
|
|
infer_suggestable: bool,
|
|
placeholder: Option<Ty<'tcx>>,
|
|
}
|
|
|
|
impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for MakeSuggestableFolder<'tcx> {
|
|
type Error = ();
|
|
|
|
fn cx(&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, args) if self.placeholder.is_none() => {
|
|
Ty::new_fn_ptr(self.tcx, self.tcx.fn_sig(def_id).instantiate(self.tcx, args))
|
|
}
|
|
|
|
Closure(..)
|
|
| FnDef(..)
|
|
| Infer(..)
|
|
| Coroutine(..)
|
|
| CoroutineWitness(..)
|
|
| Bound(_, _)
|
|
| Placeholder(_)
|
|
| Error(_) => {
|
|
if let Some(placeholder) = self.placeholder {
|
|
// We replace these with infer (which is passed in from an infcx).
|
|
placeholder
|
|
} else {
|
|
return Err(());
|
|
}
|
|
}
|
|
|
|
Alias(Opaque, AliasTy { def_id, .. }) => {
|
|
let parent = self.tcx.parent(def_id);
|
|
let parent_ty = self.tcx.type_of(parent).instantiate_identity();
|
|
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, .. }) =
|
|
*parent_ty.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)
|
|
}
|
|
}
|