blob: 4b97bac26b82e270d9bbffa65bdb492dce13cca4 [file] [log] [blame]
use super::RecordedItemId;
use crate::RustIrDatabase;
use chalk_ir::{
interner::Interner,
visit::TypeVisitor,
visit::{TypeSuperVisitable, TypeVisitable},
AliasTy, DebruijnIndex, TyKind, WhereClause,
};
use std::ops::ControlFlow;
use indexmap::IndexSet;
/// Collects the identifiers needed to resolve all the names for a given
/// set of identifers, excluding identifiers we already have.
///
/// When recording identifiers to print, the `LoggingRustIrDatabase` only
/// records identifiers the solver uses. But the solver assumes well-formedness,
/// and thus skips over many names referenced in the definitions.
///
/// For instance, if we have:
///
/// ```rust,ignore
/// struct S {}
///
/// trait Parent {}
/// trait Child where Self: Parent {}
/// impl Parent for S {}
/// impl Child for S {}
/// ```
///
/// And our goal is `S: Child`, we will only render `S`, `impl Child for S`, and
/// `trait Child`. This will not parse because the `Child` trait's definition
/// references parent. IdCollector solves this by collecting all of the directly
/// related identifiers, allowing those to be rendered as well, ensuring name
/// resolution is successful.
pub fn collect_unrecorded_ids<I: Interner, DB: RustIrDatabase<I>>(
db: &DB,
identifiers: &'_ IndexSet<RecordedItemId<I>>,
) -> IndexSet<RecordedItemId<I>> {
let mut collector = IdCollector {
db,
found_identifiers: IndexSet::new(),
};
for id in identifiers {
match *id {
RecordedItemId::Adt(adt_id) => {
collector
.db
.adt_datum(adt_id)
.visit_with(&mut collector, DebruijnIndex::INNERMOST);
}
RecordedItemId::FnDef(fn_def) => {
collector
.db
.fn_def_datum(fn_def)
.visit_with(&mut collector, DebruijnIndex::INNERMOST);
}
RecordedItemId::Coroutine(_coroutine_id) => unimplemented!(),
RecordedItemId::Trait(trait_id) => {
let trait_datum = collector.db.trait_datum(trait_id);
trait_datum.visit_with(&mut collector, DebruijnIndex::INNERMOST);
for assoc_ty_id in &trait_datum.associated_ty_ids {
let assoc_ty_datum = collector.db.associated_ty_data(*assoc_ty_id);
assoc_ty_datum
.bounds_on_self(collector.db.interner())
.visit_with(&mut collector, DebruijnIndex::INNERMOST);
assoc_ty_datum.visit_with(&mut collector, DebruijnIndex::INNERMOST);
}
}
RecordedItemId::OpaqueTy(opaque_id) => {
collector
.db
.opaque_ty_data(opaque_id)
.visit_with(&mut collector, DebruijnIndex::INNERMOST);
collector
.db
.hidden_opaque_type(opaque_id)
.visit_with(&mut collector, DebruijnIndex::INNERMOST);
}
RecordedItemId::Impl(impl_id) => {
let impl_datum = collector.db.impl_datum(impl_id);
for id in &impl_datum.associated_ty_value_ids {
let assoc_ty_value = collector.db.associated_ty_value(*id);
assoc_ty_value.visit_with(&mut collector, DebruijnIndex::INNERMOST);
}
impl_datum.visit_with(&mut collector, DebruijnIndex::INNERMOST);
}
}
}
collector
.found_identifiers
.difference(identifiers)
.copied()
.collect()
}
struct IdCollector<'i, I: Interner, DB: RustIrDatabase<I>> {
db: &'i DB,
found_identifiers: IndexSet<RecordedItemId<I>>,
}
impl<'i, I: Interner, DB: RustIrDatabase<I>> IdCollector<'i, I, DB> {
fn record(&mut self, id: impl Into<RecordedItemId<I>>) {
self.found_identifiers.insert(id.into());
}
fn visit_alias(&mut self, alias: &AliasTy<I>) {
match alias {
AliasTy::Projection(projection_ty) => {
let assoc_ty_datum = self.db.associated_ty_data(projection_ty.associated_ty_id);
self.record(assoc_ty_datum.trait_id)
}
AliasTy::Opaque(opaque_ty) => self.record(opaque_ty.opaque_ty_id),
}
}
}
impl<'i, I: Interner, DB: RustIrDatabase<I>> TypeVisitor<I> for IdCollector<'i, I, DB> {
type BreakTy = ();
fn as_dyn(&mut self) -> &mut dyn TypeVisitor<I, BreakTy = Self::BreakTy> {
self
}
fn interner(&self) -> I {
self.db.interner()
}
fn visit_ty(
&mut self,
ty: &chalk_ir::Ty<I>,
outer_binder: chalk_ir::DebruijnIndex,
) -> ControlFlow<()> {
match ty.kind(self.db.interner()) {
TyKind::Adt(adt, _) => self.record(*adt),
TyKind::FnDef(fn_def, _) => self.record(*fn_def),
TyKind::OpaqueType(opaque, _) => self.record(*opaque),
TyKind::Alias(alias) => self.visit_alias(alias),
TyKind::BoundVar(..) => (),
TyKind::Dyn(..) => (),
TyKind::Function(..) => (),
TyKind::InferenceVar(..) => (),
TyKind::Placeholder(..) => (),
_ => {}
}
ty.super_visit_with(self, outer_binder)
}
fn visit_where_clause(
&mut self,
where_clause: &WhereClause<I>,
outer_binder: DebruijnIndex,
) -> ControlFlow<()> {
match where_clause {
WhereClause::Implemented(trait_ref) => self.record(trait_ref.trait_id),
WhereClause::AliasEq(alias_eq) => self.visit_alias(&alias_eq.alias),
WhereClause::LifetimeOutlives(_lifetime_outlives) => (),
WhereClause::TypeOutlives(_type_outlives) => (),
}
where_clause.super_visit_with(self.as_dyn(), outer_binder)
}
}