Unnamed repository; edit this file 'description' to name the repository.
Add method to get generic parameters in a type
Ryo Yoshida 2023-01-31
parent 0df70d3 · commit 32955c3
-rw-r--r--crates/hir-ty/src/lib.rs69
-rw-r--r--crates/hir/src/lib.rs8
2 files changed, 76 insertions, 1 deletions
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index a1182445ed..cbeb61067d 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -39,11 +39,13 @@ use std::sync::Arc;
use chalk_ir::{
fold::{Shift, TypeFoldable},
interner::HasInterner,
- NoSolution,
+ visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
+ NoSolution, TyData,
};
use hir_def::{expr::ExprId, type_ref::Rawness, TypeOrConstParamId};
use hir_expand::name;
use itertools::Either;
+use rustc_hash::FxHashSet;
use traits::FnTrait;
use utils::Generics;
@@ -562,3 +564,68 @@ pub fn callable_sig_from_fnonce(
Some(CallableSig::from_params_and_return(params, ret_ty, false, Safety::Safe))
}
+
+struct PlaceholderCollector<'db> {
+ db: &'db dyn HirDatabase,
+ placeholders: FxHashSet<TypeOrConstParamId>,
+}
+
+impl PlaceholderCollector<'_> {
+ fn collect(&mut self, idx: PlaceholderIndex) {
+ let id = from_placeholder_idx(self.db, idx);
+ self.placeholders.insert(id);
+ }
+}
+
+impl TypeVisitor<Interner> for PlaceholderCollector<'_> {
+ type BreakTy = ();
+
+ fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy> {
+ self
+ }
+
+ fn interner(&self) -> Interner {
+ Interner
+ }
+
+ fn visit_ty(
+ &mut self,
+ ty: &Ty,
+ outer_binder: DebruijnIndex,
+ ) -> std::ops::ControlFlow<Self::BreakTy> {
+ let has_placeholder_bits = TypeFlags::HAS_TY_PLACEHOLDER | TypeFlags::HAS_CT_PLACEHOLDER;
+ let TyData { kind, flags } = ty.data(Interner);
+
+ if let TyKind::Placeholder(idx) = kind {
+ self.collect(*idx);
+ } else if flags.intersects(has_placeholder_bits) {
+ return ty.super_visit_with(self, outer_binder);
+ } else {
+ // Fast path: don't visit inner types (e.g. generic arguments) when `flags` indicate
+ // that there are no placeholders.
+ }
+
+ std::ops::ControlFlow::Continue(())
+ }
+
+ fn visit_const(
+ &mut self,
+ constant: &chalk_ir::Const<Interner>,
+ _outer_binder: DebruijnIndex,
+ ) -> std::ops::ControlFlow<Self::BreakTy> {
+ if let chalk_ir::ConstValue::Placeholder(idx) = constant.data(Interner).value {
+ self.collect(idx);
+ }
+ std::ops::ControlFlow::Continue(())
+ }
+}
+
+/// Returns unique placeholders for types and consts contained in `value`.
+pub fn collect_placeholders<T>(value: &T, db: &dyn HirDatabase) -> Vec<TypeOrConstParamId>
+where
+ T: ?Sized + TypeVisitable<Interner>,
+{
+ let mut collector = PlaceholderCollector { db, placeholders: FxHashSet::default() };
+ value.visit_with(&mut collector, DebruijnIndex::INNERMOST);
+ collector.placeholders.into_iter().collect()
+}
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index fc6a398ed3..24faa127e4 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -3599,6 +3599,14 @@ impl Type {
_ => None,
}
}
+
+ /// Returns unique `GenericParam`s contained in this type.
+ pub fn generic_params(&self, db: &dyn HirDatabase) -> FxHashSet<GenericParam> {
+ hir_ty::collect_placeholders(&self.ty, db)
+ .into_iter()
+ .map(|id| TypeOrConstParam { id }.split(db).either_into())
+ .collect()
+ }
}
#[derive(Debug)]