Skip to content

Commit 32955c3

Browse files
committed
Add method to get generic parameters in a type
1 parent 0df70d3 commit 32955c3

File tree

2 files changed

+76
-1
lines changed

2 files changed

+76
-1
lines changed

crates/hir-ty/src/lib.rs

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,13 @@ use std::sync::Arc;
3939
use chalk_ir::{
4040
fold::{Shift, TypeFoldable},
4141
interner::HasInterner,
42-
NoSolution,
42+
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
43+
NoSolution, TyData,
4344
};
4445
use hir_def::{expr::ExprId, type_ref::Rawness, TypeOrConstParamId};
4546
use hir_expand::name;
4647
use itertools::Either;
48+
use rustc_hash::FxHashSet;
4749
use traits::FnTrait;
4850
use utils::Generics;
4951

@@ -562,3 +564,68 @@ pub fn callable_sig_from_fnonce(
562564

563565
Some(CallableSig::from_params_and_return(params, ret_ty, false, Safety::Safe))
564566
}
567+
568+
struct PlaceholderCollector<'db> {
569+
db: &'db dyn HirDatabase,
570+
placeholders: FxHashSet<TypeOrConstParamId>,
571+
}
572+
573+
impl PlaceholderCollector<'_> {
574+
fn collect(&mut self, idx: PlaceholderIndex) {
575+
let id = from_placeholder_idx(self.db, idx);
576+
self.placeholders.insert(id);
577+
}
578+
}
579+
580+
impl TypeVisitor<Interner> for PlaceholderCollector<'_> {
581+
type BreakTy = ();
582+
583+
fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy> {
584+
self
585+
}
586+
587+
fn interner(&self) -> Interner {
588+
Interner
589+
}
590+
591+
fn visit_ty(
592+
&mut self,
593+
ty: &Ty,
594+
outer_binder: DebruijnIndex,
595+
) -> std::ops::ControlFlow<Self::BreakTy> {
596+
let has_placeholder_bits = TypeFlags::HAS_TY_PLACEHOLDER | TypeFlags::HAS_CT_PLACEHOLDER;
597+
let TyData { kind, flags } = ty.data(Interner);
598+
599+
if let TyKind::Placeholder(idx) = kind {
600+
self.collect(*idx);
601+
} else if flags.intersects(has_placeholder_bits) {
602+
return ty.super_visit_with(self, outer_binder);
603+
} else {
604+
// Fast path: don't visit inner types (e.g. generic arguments) when `flags` indicate
605+
// that there are no placeholders.
606+
}
607+
608+
std::ops::ControlFlow::Continue(())
609+
}
610+
611+
fn visit_const(
612+
&mut self,
613+
constant: &chalk_ir::Const<Interner>,
614+
_outer_binder: DebruijnIndex,
615+
) -> std::ops::ControlFlow<Self::BreakTy> {
616+
if let chalk_ir::ConstValue::Placeholder(idx) = constant.data(Interner).value {
617+
self.collect(idx);
618+
}
619+
std::ops::ControlFlow::Continue(())
620+
}
621+
}
622+
623+
/// Returns unique placeholders for types and consts contained in `value`.
624+
pub fn collect_placeholders<T>(value: &T, db: &dyn HirDatabase) -> Vec<TypeOrConstParamId>
625+
where
626+
T: ?Sized + TypeVisitable<Interner>,
627+
{
628+
let mut collector = PlaceholderCollector { db, placeholders: FxHashSet::default() };
629+
value.visit_with(&mut collector, DebruijnIndex::INNERMOST);
630+
collector.placeholders.into_iter().collect()
631+
}

crates/hir/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3599,6 +3599,14 @@ impl Type {
35993599
_ => None,
36003600
}
36013601
}
3602+
3603+
/// Returns unique `GenericParam`s contained in this type.
3604+
pub fn generic_params(&self, db: &dyn HirDatabase) -> FxHashSet<GenericParam> {
3605+
hir_ty::collect_placeholders(&self.ty, db)
3606+
.into_iter()
3607+
.map(|id| TypeOrConstParam { id }.split(db).either_into())
3608+
.collect()
3609+
}
36023610
}
36033611

36043612
#[derive(Debug)]

0 commit comments

Comments
 (0)