rubydex 0.2.0-aarch64-linux → 0.2.2-aarch64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -287,11 +287,8 @@ impl<'a> Resolver<'a> {
287
287
  unreachable!("SelfReceiver methods should be routed to handle_definition_unit");
288
288
  }
289
289
  Some(Receiver::ConstantReceiver(name_id)) => {
290
- let receiver_decl_id = match self.graph.names().get(name_id).unwrap() {
291
- NameRef::Resolved(resolved) => *resolved.declaration_id(),
292
- NameRef::Unresolved(_) => {
293
- continue;
294
- }
290
+ let Some(receiver_decl_id) = self.resolve_constant_receiver(*name_id, id) else {
291
+ continue;
295
292
  };
296
293
 
297
294
  let Some(singleton_id) = self.get_or_create_singleton_class(receiver_decl_id, true) else {
@@ -301,8 +298,8 @@ impl<'a> Resolver<'a> {
301
298
  singleton_id
302
299
  }
303
300
  None => {
304
- let Some(resolved) = self.resolve_lexical_owner(*method_definition.lexical_nesting_id())
305
- else {
301
+ let lexical = *method_definition.lexical_nesting_id();
302
+ let Some(resolved) = self.resolve_lexical_owner(lexical, id) else {
306
303
  continue;
307
304
  };
308
305
  resolved
@@ -314,29 +311,35 @@ impl<'a> Resolver<'a> {
314
311
  });
315
312
  }
316
313
  Definition::AttrAccessor(attr) => {
317
- let Some(owner_id) = self.resolve_lexical_owner(*attr.lexical_nesting_id()) else {
314
+ let lexical = *attr.lexical_nesting_id();
315
+ let str_id = *attr.str_id();
316
+ let Some(owner_id) = self.resolve_lexical_owner(lexical, id) else {
318
317
  continue;
319
318
  };
320
319
 
321
- self.create_declaration(*attr.str_id(), id, owner_id, |name| {
320
+ self.create_declaration(str_id, id, owner_id, |name| {
322
321
  Declaration::Method(Box::new(MethodDeclaration::new(name, owner_id)))
323
322
  });
324
323
  }
325
324
  Definition::AttrReader(attr) => {
326
- let Some(owner_id) = self.resolve_lexical_owner(*attr.lexical_nesting_id()) else {
325
+ let lexical = *attr.lexical_nesting_id();
326
+ let str_id = *attr.str_id();
327
+ let Some(owner_id) = self.resolve_lexical_owner(lexical, id) else {
327
328
  continue;
328
329
  };
329
330
 
330
- self.create_declaration(*attr.str_id(), id, owner_id, |name| {
331
+ self.create_declaration(str_id, id, owner_id, |name| {
331
332
  Declaration::Method(Box::new(MethodDeclaration::new(name, owner_id)))
332
333
  });
333
334
  }
334
335
  Definition::AttrWriter(attr) => {
335
- let Some(owner_id) = self.resolve_lexical_owner(*attr.lexical_nesting_id()) else {
336
+ let lexical = *attr.lexical_nesting_id();
337
+ let str_id = *attr.str_id();
338
+ let Some(owner_id) = self.resolve_lexical_owner(lexical, id) else {
336
339
  continue;
337
340
  };
338
341
 
339
- self.create_declaration(*attr.str_id(), id, owner_id, |name| {
342
+ self.create_declaration(str_id, id, owner_id, |name| {
340
343
  Declaration::Method(Box::new(MethodDeclaration::new(name, owner_id)))
341
344
  });
342
345
  }
@@ -377,11 +380,11 @@ impl<'a> Resolver<'a> {
377
380
  .definition_id_to_declaration_id(*def_id)
378
381
  .expect("SelfReceiver definition should have a declaration"),
379
382
  Receiver::ConstantReceiver(name_id) => {
380
- let Some(NameRef::Resolved(resolved)) = self.graph.names().get(name_id) else {
383
+ let Some(receiver_decl_id) = self.resolve_constant_receiver(*name_id, id)
384
+ else {
381
385
  continue;
382
386
  };
383
-
384
- *resolved.declaration_id()
387
+ receiver_decl_id
385
388
  }
386
389
  };
387
390
 
@@ -407,7 +410,8 @@ impl<'a> Resolver<'a> {
407
410
  }
408
411
 
409
412
  // If the method has no explicit receiver, we resolve the owner based on the lexical nesting
410
- let Some(method_owner_id) = self.resolve_lexical_owner(*method.lexical_nesting_id()) else {
413
+ let Some(method_owner_id) = self.resolve_lexical_owner(*method.lexical_nesting_id(), id)
414
+ else {
411
415
  continue;
412
416
  };
413
417
 
@@ -464,11 +468,14 @@ impl<'a> Resolver<'a> {
464
468
  // If in a singleton class body directly, the owner is the singleton class's singleton class
465
469
  // Like `class << Foo; @bar = 1; end`, where `@bar` is owned by `Foo::<Foo>::<<Foo>>`
466
470
  Definition::SingletonClass(_) => {
467
- let singleton_class_decl_id = self
468
- .graph
469
- .definition_id_to_declaration_id(nesting_id)
470
- .copied()
471
- .unwrap_or(*OBJECT_ID);
471
+ // The singleton's declaration may be missing (e.g. its receiver was
472
+ // just deleted). Re-queue and let the next resolve place `@bar` on
473
+ // the right owner instead of falling back to Object.
474
+ let Some(&singleton_class_decl_id) = self.graph.definition_id_to_declaration_id(nesting_id)
475
+ else {
476
+ self.graph.push_work(Unit::Definition(id));
477
+ continue;
478
+ };
472
479
  let owner_id = self
473
480
  .get_or_create_singleton_class(singleton_class_decl_id, true)
474
481
  .expect("singleton class nesting should always be a namespace");
@@ -515,14 +522,15 @@ impl<'a> Resolver<'a> {
515
522
  };
516
523
  owner_id
517
524
  }
518
- Some(Receiver::ConstantReceiver(name_id)) => match self.graph.names().get(name_id).unwrap() {
519
- NameRef::Resolved(resolved) => *resolved.declaration_id(),
520
- NameRef::Unresolved(_) => {
525
+ Some(Receiver::ConstantReceiver(name_id)) => {
526
+ let Some(resolved) = self.resolve_constant_receiver(*name_id, id) else {
521
527
  continue;
522
- }
523
- },
528
+ };
529
+ resolved
530
+ }
524
531
  None => {
525
- let Some(resolved) = self.resolve_lexical_owner(*alias.lexical_nesting_id()) else {
532
+ let lexical = *alias.lexical_nesting_id();
533
+ let Some(resolved) = self.resolve_lexical_owner(lexical, id) else {
526
534
  continue;
527
535
  };
528
536
  resolved
@@ -538,8 +546,63 @@ impl<'a> Resolver<'a> {
538
546
  Declaration::GlobalVariable(Box::new(GlobalVariableDeclaration::new(name, *OBJECT_ID)))
539
547
  });
540
548
  }
541
- Definition::ConstantVisibility(_constant_visibility) => {
542
- // TODO
549
+ Definition::ConstantVisibility(constant_visibility) => {
550
+ // Both `private_constant` and `public_constant` can only target direct members.
551
+ // Inheritance or surrounding lexical scopes are not taken into account.
552
+ let receiver = *constant_visibility.receiver();
553
+ let target = *constant_visibility.target();
554
+ let uri_id = *constant_visibility.uri_id();
555
+ let offset = constant_visibility.offset().clone();
556
+ let lexical_nesting_id = *constant_visibility.lexical_nesting_id();
557
+ let constant_name = self.graph.strings().get(&target).unwrap().as_str().to_string();
558
+
559
+ let owner_id = if let Some(receiver_name_id) = receiver {
560
+ let NameRef::Resolved(resolved_receiver) = self.graph.names().get(&receiver_name_id).unwrap()
561
+ else {
562
+ continue;
563
+ };
564
+ let Some(namespace_id) = self.resolve_to_namespace(*resolved_receiver.declaration_id()) else {
565
+ continue;
566
+ };
567
+ namespace_id
568
+ } else {
569
+ let Some(decl_id) = self.resolve_lexical_owner(lexical_nesting_id, id) else {
570
+ continue;
571
+ };
572
+ decl_id
573
+ };
574
+
575
+ let Some(Declaration::Namespace(namespace)) = self.graph.declarations().get(&owner_id) else {
576
+ continue;
577
+ };
578
+
579
+ if let Some(member) = namespace
580
+ .member(&target)
581
+ .and_then(|member_id| self.graph.declarations().get(member_id))
582
+ && matches!(
583
+ member,
584
+ Declaration::Constant(_)
585
+ | Declaration::ConstantAlias(_)
586
+ | Declaration::Namespace(Namespace::Class(_) | Namespace::Module(_))
587
+ )
588
+ {
589
+ // `add_declaration` deduplicates by fully qualified name, so this appends
590
+ // the visibility definition to the existing constant declaration.
591
+ self.graph.add_declaration(id, member.name().to_string(), |name| {
592
+ Declaration::Constant(Box::new(ConstantDeclaration::new(name, owner_id)))
593
+ });
594
+ } else {
595
+ let diagnostic = Diagnostic::new(
596
+ Rule::UndefinedConstantVisibilityTarget,
597
+ uri_id,
598
+ offset,
599
+ format!(
600
+ "undefined constant `{constant_name}` for visibility change in `{}`",
601
+ namespace.name()
602
+ ),
603
+ );
604
+ self.graph.add_document_diagnostic(uri_id, diagnostic);
605
+ }
543
606
  }
544
607
  Definition::MethodVisibility(_) => {
545
608
  method_visibility_ids.push(id);
@@ -557,7 +620,8 @@ impl<'a> Resolver<'a> {
557
620
  self.resolve_method_visibilities(method_visibility_ids);
558
621
  }
559
622
 
560
- /// Resolves retroactive method visibility changes (`private :foo`, `protected :foo`, `public :foo`).
623
+ /// Resolves retroactive method visibility changes (`private :foo`, `protected :foo`, `public :foo`,
624
+ /// `private_class_method :foo`, `public_class_method :foo`).
561
625
  ///
562
626
  /// Runs as a second pass after all methods/attrs are declared, so `private :bar` works
563
627
  /// regardless of whether `def bar` appeared before or after it in source.
@@ -573,11 +637,21 @@ impl<'a> Resolver<'a> {
573
637
  let uri_id = *method_visibility.uri_id();
574
638
  let offset = method_visibility.offset().clone();
575
639
  let lexical_nesting_id = *method_visibility.lexical_nesting_id();
640
+ let is_singleton = method_visibility.flags().is_singleton_method_visibility();
576
641
 
577
- let Some(owner_id) = self.resolve_lexical_owner(lexical_nesting_id) else {
642
+ let Some(lexical_owner_id) = self.resolve_lexical_owner(lexical_nesting_id, id) else {
578
643
  continue;
579
644
  };
580
645
 
646
+ let owner_id = if is_singleton {
647
+ let Some(singleton_id) = self.get_or_create_singleton_class(lexical_owner_id, true) else {
648
+ continue;
649
+ };
650
+ singleton_id
651
+ } else {
652
+ lexical_owner_id
653
+ };
654
+
581
655
  let Some(Declaration::Namespace(namespace)) = self.graph.declarations().get(&owner_id) else {
582
656
  continue;
583
657
  };
@@ -626,13 +700,20 @@ impl<'a> Resolver<'a> {
626
700
  Rule::UndefinedMethodVisibilityTarget,
627
701
  uri_id,
628
702
  offset,
629
- format!("undefined method `{method_name}` for visibility change in `{owner_name}`"),
703
+ format!("undefined method `{owner_name}#{method_name}` for visibility change"),
630
704
  );
631
- self.graph
632
- .declarations_mut()
633
- .get_mut(&owner_id)
634
- .unwrap()
635
- .add_diagnostic(diagnostic);
705
+ if is_singleton {
706
+ // Document-scoped: the singleton class may be synthetic (created by this
707
+ // visibility resolution) and won't be cleaned up on file delete, so attaching
708
+ // the diagnostic to the declaration would leave it orphaned.
709
+ self.graph.add_document_diagnostic(uri_id, diagnostic);
710
+ } else {
711
+ self.graph
712
+ .declarations_mut()
713
+ .get_mut(&owner_id)
714
+ .unwrap()
715
+ .add_diagnostic(diagnostic);
716
+ }
636
717
  }
637
718
  }
638
719
 
@@ -640,6 +721,19 @@ impl<'a> Resolver<'a> {
640
721
  self.graph.extend_work(pending_work);
641
722
  }
642
723
 
724
+ /// Resolves a constant receiver for `handle_remaining_definitions`.
725
+ /// If the receiver name is unresolved, preserve the definition for a later
726
+ /// resolve cycle instead of dropping work during an incremental delete/re-add gap.
727
+ fn resolve_constant_receiver(&mut self, name_id: NameId, id: DefinitionId) -> Option<DeclarationId> {
728
+ match self.graph.names().get(&name_id).unwrap() {
729
+ NameRef::Resolved(resolved) => Some(*resolved.declaration_id()),
730
+ NameRef::Unresolved(_) => {
731
+ self.graph.push_work(Unit::Definition(id));
732
+ None
733
+ }
734
+ }
735
+ }
736
+
643
737
  fn create_declaration<F>(
644
738
  &mut self,
645
739
  str_id: StringId,
@@ -689,37 +783,62 @@ impl<'a> Resolver<'a> {
689
783
  }
690
784
 
691
785
  /// Resolves owner from lexical nesting.
692
- fn resolve_lexical_owner(&self, lexical_nesting_id: Option<DefinitionId>) -> Option<DeclarationId> {
693
- let Some(id) = lexical_nesting_id else {
694
- return Some(*OBJECT_ID);
695
- };
786
+ ///
787
+ /// If the owner cannot be resolved yet, re-queues the current definition so
788
+ /// a later resolve cycle can retry instead of permanently dropping it.
789
+ fn resolve_lexical_owner(
790
+ &mut self,
791
+ lexical_nesting_id: Option<DefinitionId>,
792
+ definition_id: DefinitionId,
793
+ ) -> Option<DeclarationId> {
794
+ let mut current_nesting = lexical_nesting_id;
696
795
 
697
- // If no declaration exists yet for this definition, walk up the lexical chain.
698
- // This handles the case where attr_* definitions inside methods are processed
699
- // before the method definition itself.
700
- let Some(declaration_id) = self.graph.definition_id_to_declaration_id(id) else {
701
- let definition = self.graph.definitions().get(&id).unwrap();
702
- return self.resolve_lexical_owner(*definition.lexical_nesting_id());
703
- };
796
+ let resolved = loop {
797
+ let Some(id) = current_nesting else {
798
+ break Some(*OBJECT_ID);
799
+ };
704
800
 
705
- let decl = self.graph.declarations().get(declaration_id).unwrap();
801
+ // If no declaration exists yet for this definition, walk up the lexical chain.
802
+ // This handles the case where attr_* definitions inside methods are processed
803
+ // before the method definition itself. A SingletonClass with no declaration
804
+ // is an exception: returning the surrounding scope would attach its members to
805
+ // the wrong owner (e.g. `Object`) and never recover, so retry later instead.
806
+ let Some(declaration_id) = self.graph.definition_id_to_declaration_id(id) else {
807
+ let definition = self.graph.definitions().get(&id).unwrap();
808
+ if matches!(definition, Definition::SingletonClass(_)) {
809
+ break None;
810
+ }
811
+ current_nesting = *definition.lexical_nesting_id();
812
+ continue;
813
+ };
814
+
815
+ let decl = self.graph.declarations().get(declaration_id).unwrap();
816
+
817
+ // If the associated declaration is a namespace that can own things, we found the right owner. Otherwise, we might
818
+ // have found something nested inside something else (like a method), in which case we have to walk up until we find
819
+ // the appropriate owner.
820
+ if matches!(
821
+ decl,
822
+ Declaration::Namespace(Namespace::Class(_) | Namespace::Module(_) | Namespace::SingletonClass(_))
823
+ ) {
824
+ break Some(*declaration_id);
825
+ }
826
+
827
+ if matches!(decl, Declaration::ConstantAlias(_)) {
828
+ // Follow the alias chain to find the target namespace. If the alias is unresolved,
829
+ // the definition cannot be properly owned yet and should be retried later.
830
+ break self.resolve_to_namespace(*declaration_id);
831
+ }
706
832
 
707
- // If the associated declaration is a namespace that can own things, we found the right owner. Otherwise, we might
708
- // have found something nested inside something else (like a method), in which case we have to recurse until we find
709
- // the appropriate owner
710
- if matches!(
711
- decl,
712
- Declaration::Namespace(Namespace::Class(_) | Namespace::Module(_) | Namespace::SingletonClass(_))
713
- ) {
714
- Some(*declaration_id)
715
- } else if matches!(decl, Declaration::ConstantAlias(_)) {
716
- // Follow the alias chain to find the target namespace. If the alias is unresolved,
717
- // the definition cannot be properly owned and should be skipped by the caller.
718
- self.resolve_to_namespace(*declaration_id)
719
- } else {
720
833
  let definition = self.graph.definitions().get(&id).unwrap();
721
- self.resolve_lexical_owner(*definition.lexical_nesting_id())
834
+ current_nesting = *definition.lexical_nesting_id();
835
+ };
836
+
837
+ if resolved.is_none() {
838
+ self.graph.push_work(Unit::Definition(definition_id));
722
839
  }
840
+
841
+ resolved
723
842
  }
724
843
 
725
844
  /// Gets or creates a singleton class declaration for a given class/module declaration. For class `Foo`, this
@@ -1124,11 +1243,17 @@ impl<'a> Resolver<'a> {
1124
1243
  let name_ref = self.graph.names().get(&name_id).unwrap();
1125
1244
  let str_id = *name_ref.str();
1126
1245
 
1127
- let outcome = match self.name_owner_id(name_id) {
1246
+ let outcome = match self.name_owner_id(name_id, singleton) {
1128
1247
  // name_owner_id returns Unresolved(None) only when the parent scope is genuinely unknown
1129
1248
  // (e.g., `class A::B::C` where `A` doesn't exist). This definition needs an owner, so
1130
1249
  // create Todo placeholders for the missing parent chain. Todos get promoted when real
1131
1250
  // definitions appear later.
1251
+ //
1252
+ // Singleton classes are the exception: `class << UndefinedReceiver` attaches via
1253
+ // `set_singleton_class_id`, not `add_member`, so a TODO receiver would never gain a
1254
+ // member. Emit Retry so the unit is preserved for a later resolve where the receiver
1255
+ // may exist.
1256
+ Outcome::Unresolved(None) if singleton => Outcome::Retry(None),
1132
1257
  Outcome::Unresolved(None) => Outcome::Resolved(self.create_todo_for_parent(name_id), None),
1133
1258
  other => other,
1134
1259
  };
@@ -1200,7 +1325,11 @@ impl<'a> Resolver<'a> {
1200
1325
  // Returns the owner declaration ID for a given name. If the name is simple and has no parent scope, then the owner is
1201
1326
  // either the nesting or Object. If the name has a parent scope, we attempt to resolve the reference and that should be
1202
1327
  // the name's owner. For aliases, resolves through to get the actual namespace.
1203
- fn name_owner_id(&mut self, name_id: NameId) -> Outcome {
1328
+ //
1329
+ // When `preserve_retry` is true, Retry from resolve_constant_internal is NOT folded into
1330
+ // Unresolved(None). This is used by the singleton path so the unit can retry when the
1331
+ // receiver might resolve later rather than being dropped.
1332
+ fn name_owner_id(&mut self, name_id: NameId, preserve_retry: bool) -> Outcome {
1204
1333
  let name_ref = self.graph.names().get(&name_id).unwrap();
1205
1334
 
1206
1335
  if let Some(&parent_scope) = name_ref.parent_scope().as_ref() {
@@ -1210,7 +1339,8 @@ impl<'a> Resolver<'a> {
1210
1339
  Outcome::Resolved(id, linearization) => self.resolve_to_primary_namespace(id, linearization),
1211
1340
  // The parent scope is genuinely unknown — not a circular alias or pending
1212
1341
  // linearization, but a name that doesn't exist anywhere in the graph.
1213
- Outcome::Retry(None) | Outcome::Unresolved(None) => Outcome::Unresolved(None),
1342
+ Outcome::Unresolved(None) => Outcome::Unresolved(None),
1343
+ Outcome::Retry(None) if !preserve_retry => Outcome::Unresolved(None),
1214
1344
  other => other,
1215
1345
  }
1216
1346
  } else if let Some(nesting_id) = name_ref.nesting()
@@ -1251,7 +1381,7 @@ impl<'a> Resolver<'a> {
1251
1381
  // Object so it becomes top-level `A`. This way `module A; end` appearing later
1252
1382
  // promotes it correctly. Using nesting would incorrectly create `SomeModule::A`.
1253
1383
  let parent_owner_id = if parent_has_parent_scope {
1254
- match self.name_owner_id(parent_scope) {
1384
+ match self.name_owner_id(parent_scope, false) {
1255
1385
  Outcome::Resolved(id, _) => id,
1256
1386
  _ => self.create_todo_for_parent(parent_scope),
1257
1387
  }
@@ -1768,7 +1898,7 @@ impl<'a> Resolver<'a> {
1768
1898
  singleton_methods.push(Unit::Definition(id));
1769
1899
  }
1770
1900
  _ => {
1771
- others.push(id);
1901
+ others.push((id, (*definition.uri_id(), definition.offset())));
1772
1902
  }
1773
1903
  }
1774
1904
  }
@@ -1808,14 +1938,15 @@ impl<'a> Resolver<'a> {
1808
1938
  (depths.get(name_a).unwrap(), uri_a, offset_a).cmp(&(depths.get(name_b).unwrap(), uri_b, offset_b))
1809
1939
  });
1810
1940
 
1941
+ others.sort_unstable_by_key(|(_, key)| *key);
1942
+
1811
1943
  // Definitions first, then constant refs, then singleton methods, then ancestors
1812
1944
  self.unit_queue.extend(definitions.into_iter().map(|(id, _)| id));
1813
1945
  self.unit_queue.extend(const_refs.into_iter().map(|(id, _)| id));
1814
1946
  self.unit_queue.extend(singleton_methods);
1815
1947
  self.unit_queue.extend(ancestors.into_iter().map(Unit::Ancestors));
1816
1948
 
1817
- others.shrink_to_fit();
1818
- others
1949
+ others.into_iter().map(|(id, _)| id).collect()
1819
1950
  }
1820
1951
 
1821
1952
  /// Returns the singleton parent ID for an attached object ID. A singleton class' parent depends on what the attached