Skip to content

Commit 1c585c2

Browse files
committed
Clean up synthetic declarations during incremental resolution
1 parent 0d94164 commit 1c585c2

3 files changed

Lines changed: 405 additions & 16 deletions

File tree

rust/rubydex/src/model/declaration.rs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,14 @@ macro_rules! namespace_declaration {
162162
self.singleton_class_id = Some(declaration_id);
163163
}
164164

165+
/// Clears `singleton_class_id` only if it matches `expected`.
166+
/// Prevents accidentally clearing a pointer to a re-created singleton.
167+
pub fn clear_singleton_class_id(&mut self, expected: &DeclarationId) {
168+
if self.singleton_class_id.as_ref() == Some(expected) {
169+
self.singleton_class_id = None;
170+
}
171+
}
172+
165173
pub fn singleton_class_id(&self) -> Option<&DeclarationId> {
166174
self.singleton_class_id.as_ref()
167175
}
@@ -346,15 +354,24 @@ impl Declaration {
346354
}
347355

348356
/// Returns true if this declaration has no backing definitions and is eligible
349-
/// for removal. Synthetic declarations (singleton classes, bootstrap Object/Module/Class)
350-
/// are never definition-backed — they should only be removed when their owner is removed.
357+
/// for removal. Bootstrap declarations (Object, Module, Class) are never
358+
/// removable. Singleton classes are only removable when they have no
359+
/// definitions AND no members — populated singletons serve as namespace
360+
/// parents for class-level methods.
351361
#[must_use]
352362
pub fn has_no_backing_definitions(&self, decl_id: &DeclarationId) -> bool {
353-
let is_synthetic = matches!(self, Declaration::Namespace(Namespace::SingletonClass(_)))
354-
|| *decl_id == *super::graph::OBJECT_ID
363+
if *decl_id == *super::graph::OBJECT_ID
355364
|| *decl_id == *super::graph::MODULE_ID
356-
|| *decl_id == *super::graph::CLASS_ID;
357-
!is_synthetic && self.has_no_definitions()
365+
|| *decl_id == *super::graph::CLASS_ID
366+
{
367+
return false;
368+
}
369+
370+
if matches!(self, Declaration::Namespace(Namespace::SingletonClass(_))) {
371+
return self.has_no_definitions() && self.as_namespace().is_some_and(|ns| ns.members().is_empty());
372+
}
373+
374+
self.has_no_definitions()
358375
}
359376

360377
pub fn add_definition(&mut self, definition_id: DefinitionId) {
@@ -590,6 +607,10 @@ impl Namespace {
590607
all_namespaces!(self, it => it.set_singleton_class_id(declaration_id));
591608
}
592609

610+
pub fn clear_singleton_class_id(&mut self, expected: &DeclarationId) {
611+
all_namespaces!(self, it => it.clear_singleton_class_id(expected));
612+
}
613+
593614
#[must_use]
594615
pub fn owner_id(&self) -> &DeclarationId {
595616
all_namespaces!(self, it => &it.owner_id)

0 commit comments

Comments
 (0)