rubydex 0.2.0 → 0.2.2

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.
@@ -339,6 +339,54 @@ impl Declaration {
339
339
  }
340
340
  }
341
341
 
342
+ #[must_use]
343
+ pub fn as_constant(&self) -> Option<&ConstantDeclaration> {
344
+ match self {
345
+ Declaration::Constant(constant) => Some(constant),
346
+ _ => None,
347
+ }
348
+ }
349
+
350
+ #[must_use]
351
+ pub fn as_constant_alias(&self) -> Option<&ConstantAliasDeclaration> {
352
+ match self {
353
+ Declaration::ConstantAlias(alias) => Some(alias),
354
+ _ => None,
355
+ }
356
+ }
357
+
358
+ #[must_use]
359
+ pub fn as_method(&self) -> Option<&MethodDeclaration> {
360
+ match self {
361
+ Declaration::Method(method) => Some(method),
362
+ _ => None,
363
+ }
364
+ }
365
+
366
+ #[must_use]
367
+ pub fn as_global_variable(&self) -> Option<&GlobalVariableDeclaration> {
368
+ match self {
369
+ Declaration::GlobalVariable(global) => Some(global),
370
+ _ => None,
371
+ }
372
+ }
373
+
374
+ #[must_use]
375
+ pub fn as_class_variable(&self) -> Option<&ClassVariableDeclaration> {
376
+ match self {
377
+ Declaration::ClassVariable(cvar) => Some(cvar),
378
+ _ => None,
379
+ }
380
+ }
381
+
382
+ #[must_use]
383
+ pub fn as_instance_variable(&self) -> Option<&InstanceVariableDeclaration> {
384
+ match self {
385
+ Declaration::InstanceVariable(ivar) => Some(ivar),
386
+ _ => None,
387
+ }
388
+ }
389
+
342
390
  #[must_use]
343
391
  pub fn definitions(&self) -> &[DefinitionId] {
344
392
  all_declarations!(self, it => &it.definition_ids)
@@ -40,6 +40,7 @@ bitflags! {
40
40
  pub struct DefinitionFlags: u8 {
41
41
  const DEPRECATED = 0b0001;
42
42
  const PROMOTABLE = 0b0010;
43
+ const SINGLETON_METHOD_VISIBILITY = 0b0100;
43
44
  }
44
45
  }
45
46
 
@@ -53,6 +54,11 @@ impl DefinitionFlags {
53
54
  pub fn is_promotable(&self) -> bool {
54
55
  self.contains(Self::PROMOTABLE)
55
56
  }
57
+
58
+ #[must_use]
59
+ pub fn is_singleton_method_visibility(&self) -> bool {
60
+ self.contains(Self::SINGLETON_METHOD_VISIBILITY)
61
+ }
56
62
  }
57
63
 
58
64
  #[derive(Debug)]
@@ -155,8 +161,8 @@ impl Definition {
155
161
  Definition::Module(d) => Some(d.name_id()),
156
162
  Definition::Constant(d) => Some(d.name_id()),
157
163
  Definition::ConstantAlias(d) => Some(d.name_id()),
158
- Definition::ConstantVisibility(d) => Some(d.name_id()),
159
- Definition::MethodVisibility(_)
164
+ Definition::ConstantVisibility(_)
165
+ | Definition::MethodVisibility(_)
160
166
  | Definition::GlobalVariable(_)
161
167
  | Definition::InstanceVariable(_)
162
168
  | Definition::ClassVariable(_)
@@ -711,7 +717,8 @@ impl ConstantAliasDefinition {
711
717
 
712
718
  #[derive(Debug)]
713
719
  pub struct ConstantVisibilityDefinition {
714
- name_id: NameId,
720
+ receiver: Option<NameId>,
721
+ target: StringId,
715
722
  visibility: Visibility,
716
723
  uri_id: UriId,
717
724
  offset: Offset,
@@ -722,8 +729,10 @@ pub struct ConstantVisibilityDefinition {
722
729
 
723
730
  impl ConstantVisibilityDefinition {
724
731
  #[must_use]
732
+ #[allow(clippy::too_many_arguments)]
725
733
  pub const fn new(
726
- name_id: NameId,
734
+ receiver: Option<NameId>,
735
+ target: StringId,
727
736
  visibility: Visibility,
728
737
  uri_id: UriId,
729
738
  offset: Offset,
@@ -732,7 +741,8 @@ impl ConstantVisibilityDefinition {
732
741
  lexical_nesting_id: Option<DefinitionId>,
733
742
  ) -> Self {
734
743
  Self {
735
- name_id,
744
+ receiver,
745
+ target,
736
746
  visibility,
737
747
  uri_id,
738
748
  offset,
@@ -744,12 +754,17 @@ impl ConstantVisibilityDefinition {
744
754
 
745
755
  #[must_use]
746
756
  pub fn id(&self) -> DefinitionId {
747
- DefinitionId::from(&format!("{}{}{}", *self.uri_id, self.offset.start(), *self.name_id))
757
+ DefinitionId::from(&format!("{}{}{}", *self.uri_id, self.offset.start(), *self.target))
748
758
  }
749
759
 
750
760
  #[must_use]
751
- pub fn name_id(&self) -> &NameId {
752
- &self.name_id
761
+ pub fn receiver(&self) -> &Option<NameId> {
762
+ &self.receiver
763
+ }
764
+
765
+ #[must_use]
766
+ pub fn target(&self) -> &StringId {
767
+ &self.target
753
768
  }
754
769
 
755
770
  #[must_use]
@@ -782,7 +797,7 @@ impl ConstantVisibilityDefinition {
782
797
  &self.flags
783
798
  }
784
799
  }
785
- assert_mem_size!(ConstantVisibilityDefinition, 56);
800
+ assert_mem_size!(ConstantVisibilityDefinition, 64);
786
801
 
787
802
  #[derive(Debug)]
788
803
  pub struct MethodVisibilityDefinition {
@@ -1024,6 +1039,23 @@ pub enum Parameter {
1024
1039
  }
1025
1040
  assert_mem_size!(Parameter, 24);
1026
1041
 
1042
+ impl Parameter {
1043
+ #[must_use]
1044
+ pub fn inner(&self) -> &ParameterStruct {
1045
+ match self {
1046
+ Parameter::RequiredPositional(s)
1047
+ | Parameter::OptionalPositional(s)
1048
+ | Parameter::RestPositional(s)
1049
+ | Parameter::Post(s)
1050
+ | Parameter::RequiredKeyword(s)
1051
+ | Parameter::OptionalKeyword(s)
1052
+ | Parameter::RestKeyword(s)
1053
+ | Parameter::Forward(s)
1054
+ | Parameter::Block(s) => s,
1055
+ }
1056
+ }
1057
+ }
1058
+
1027
1059
  #[derive(Debug, Clone)]
1028
1060
  pub struct ParameterStruct {
1029
1061
  offset: Offset,
@@ -6,7 +6,7 @@ use crate::diagnostic::Diagnostic;
6
6
  use crate::indexing::local_graph::LocalGraph;
7
7
  use crate::model::built_in::{OBJECT_ID, add_built_in_data};
8
8
  use crate::model::declaration::{Ancestor, Declaration, Namespace};
9
- use crate::model::definitions::{Definition, Receiver};
9
+ use crate::model::definitions::{Definition, MethodVisibilityDefinition, Receiver};
10
10
  use crate::model::document::Document;
11
11
  use crate::model::encoding::Encoding;
12
12
  use crate::model::identity_maps::{IdentityHashMap, IdentityHashSet};
@@ -260,10 +260,7 @@ impl Graph {
260
260
  let name = self.names.get(it.name_id()).unwrap();
261
261
  name.str()
262
262
  }
263
- Definition::ConstantVisibility(it) => {
264
- let name = self.names.get(it.name_id()).unwrap();
265
- name.str()
266
- }
263
+ Definition::ConstantVisibility(it) => it.target(),
267
264
  Definition::MethodVisibility(it) => it.str_id(),
268
265
  Definition::GlobalVariable(it) => it.str_id(),
269
266
  Definition::InstanceVariable(it) => it.str_id(),
@@ -291,6 +288,16 @@ impl Graph {
291
288
  &self.documents
292
289
  }
293
290
 
291
+ /// Attaches a diagnostic to the document with the given `uri_id`. The diagnostic clears
292
+ /// automatically when the document is deleted or re-indexed.
293
+ ///
294
+ /// # Panics
295
+ ///
296
+ /// Panics if no document is registered for `uri_id`.
297
+ pub fn add_document_diagnostic(&mut self, uri_id: UriId, diagnostic: Diagnostic) {
298
+ self.documents.get_mut(&uri_id).unwrap().add_diagnostic(diagnostic);
299
+ }
300
+
294
301
  /// # Panics
295
302
  ///
296
303
  /// Panics if the definition is not found
@@ -320,13 +327,21 @@ impl Graph {
320
327
  Definition::ConstantAlias(it) => {
321
328
  return self.name_id_to_declaration_id(*it.name_id());
322
329
  }
323
- Definition::ConstantVisibility(it) => {
324
- return self.name_id_to_declaration_id(*it.name_id());
325
- }
326
- Definition::MethodVisibility(it) => (
327
- self.find_enclosing_namespace_name_id(it.lexical_nesting_id().as_ref()),
328
- it.str_id(),
330
+ Definition::ConstantVisibility(it) => (
331
+ it.receiver()
332
+ .as_ref()
333
+ .or_else(|| self.find_enclosing_namespace_name_id(it.lexical_nesting_id().as_ref())),
334
+ it.target(),
329
335
  ),
336
+ Definition::MethodVisibility(it) => {
337
+ if it.flags().is_singleton_method_visibility() {
338
+ return self.find_singleton_method_visibility_declaration(it);
339
+ }
340
+ (
341
+ self.find_enclosing_namespace_name_id(it.lexical_nesting_id().as_ref()),
342
+ it.str_id(),
343
+ )
344
+ }
330
345
  Definition::GlobalVariable(it) => (
331
346
  self.find_enclosing_namespace_name_id(it.lexical_nesting_id().as_ref()),
332
347
  it.str_id(),
@@ -365,13 +380,15 @@ impl Graph {
365
380
  )
366
381
  }
367
382
  Definition::MethodAlias(it) => {
368
- if let Some(Receiver::SelfReceiver(def_id)) = it.receiver() {
369
- return self.find_self_receiver_declaration(*def_id, *it.new_name_str_id());
370
- }
371
- (
372
- self.find_enclosing_namespace_name_id(it.lexical_nesting_id().as_ref()),
373
- it.new_name_str_id(),
374
- )
383
+ let nesting_name_id = match it.receiver() {
384
+ Some(Receiver::SelfReceiver(def_id)) => {
385
+ return self.find_self_receiver_declaration(*def_id, *it.new_name_str_id());
386
+ }
387
+ Some(Receiver::ConstantReceiver(name_id)) => Some(name_id),
388
+ None => self.find_enclosing_namespace_name_id(it.lexical_nesting_id().as_ref()),
389
+ };
390
+
391
+ (nesting_name_id, it.new_name_str_id())
375
392
  }
376
393
  };
377
394
 
@@ -403,6 +420,27 @@ impl Graph {
403
420
  None
404
421
  }
405
422
 
423
+ /// Looks up the declaration for a singleton method visibility through the singleton class.
424
+ fn find_singleton_method_visibility_declaration(
425
+ &self,
426
+ definition: &MethodVisibilityDefinition,
427
+ ) -> Option<&DeclarationId> {
428
+ let nesting_name_id = self.find_enclosing_namespace_name_id(definition.lexical_nesting_id().as_ref());
429
+ let nesting_declaration_id = match nesting_name_id {
430
+ Some(name_id) => self.name_id_to_declaration_id(*name_id),
431
+ None => Some(&*OBJECT_ID),
432
+ }?;
433
+ let singleton_id = self
434
+ .declarations
435
+ .get(nesting_declaration_id)?
436
+ .as_namespace()?
437
+ .singleton_class()?;
438
+ self.declarations
439
+ .get(singleton_id)?
440
+ .as_namespace()?
441
+ .member(definition.str_id())
442
+ }
443
+
406
444
  /// Looks up the declaration for a `SelfReceiver` method/alias through the singleton class.
407
445
  fn find_self_receiver_declaration(&self, def_id: DefinitionId, member_str_id: StringId) -> Option<&DeclarationId> {
408
446
  let owner_decl_id = self.definition_id_to_declaration_id(def_id)?;
@@ -602,42 +640,50 @@ impl Graph {
602
640
  &self.name_dependents
603
641
  }
604
642
 
605
- /// Returns the visibility for a method declaration.
643
+ /// Returns the visibility for a declaration.
606
644
  ///
607
- /// Scans the declaration's backing definitions from the end:
608
- /// - First `MethodVisibilityDefinition` wins
609
- /// - Otherwise, falls back to the latest method-like definition's indexed visibility
610
- /// - Returns `None` if the declaration has no definitions with visibility
645
+ /// For methods, the latest definition wins. For constants, the latest
646
+ /// `private_constant`/`public_constant` wins, otherwise `Public`.
611
647
  #[must_use]
612
648
  pub fn visibility(&self, declaration_id: &DeclarationId) -> Option<Visibility> {
613
649
  let declaration = self.declarations.get(declaration_id)?;
614
650
  let definitions = declaration.definitions();
615
651
 
616
- // Scan from the end: last visibility directive wins
617
- for def_id in definitions.iter().rev() {
618
- if let Some(definition) = self.definitions.get(def_id) {
619
- match definition {
620
- Definition::MethodVisibility(vis) => return Some(*vis.visibility()),
621
- Definition::Method(method) => return Some(*method.visibility()),
622
- Definition::AttrAccessor(attr) => return Some(*attr.visibility()),
623
- Definition::AttrReader(attr) => return Some(*attr.visibility()),
624
- Definition::AttrWriter(attr) => return Some(*attr.visibility()),
625
- Definition::Class(_)
626
- | Definition::SingletonClass(_)
627
- | Definition::Module(_)
628
- | Definition::Constant(_)
629
- | Definition::ConstantAlias(_)
630
- | Definition::ConstantVisibility(_)
631
- | Definition::GlobalVariable(_)
632
- | Definition::InstanceVariable(_)
633
- | Definition::ClassVariable(_)
634
- | Definition::MethodAlias(_)
635
- | Definition::GlobalVariableAlias(_) => {}
652
+ match declaration {
653
+ Declaration::Namespace(Namespace::Class(_) | Namespace::Module(_))
654
+ | Declaration::Constant(_)
655
+ | Declaration::ConstantAlias(_) => {
656
+ for def_id in definitions.iter().rev() {
657
+ if let Some(Definition::ConstantVisibility(vis)) = self.definitions.get(def_id) {
658
+ return Some(*vis.visibility());
659
+ }
636
660
  }
661
+ Some(Visibility::Public)
662
+ }
663
+ Declaration::Method(_) => {
664
+ for def_id in definitions.iter().rev() {
665
+ let Some(definition) = self.definitions.get(def_id) else {
666
+ continue;
667
+ };
668
+ let visibility = match definition {
669
+ Definition::MethodVisibility(vis) => Some(*vis.visibility()),
670
+ Definition::Method(method) => Some(*method.visibility()),
671
+ Definition::AttrAccessor(attr) => Some(*attr.visibility()),
672
+ Definition::AttrReader(attr) => Some(*attr.visibility()),
673
+ Definition::AttrWriter(attr) => Some(*attr.visibility()),
674
+ _ => None,
675
+ };
676
+ if visibility.is_some() {
677
+ return visibility;
678
+ }
679
+ }
680
+ None
637
681
  }
682
+ Declaration::Namespace(Namespace::SingletonClass(_) | Namespace::Todo(_))
683
+ | Declaration::GlobalVariable(_)
684
+ | Declaration::InstanceVariable(_)
685
+ | Declaration::ClassVariable(_) => None,
638
686
  }
639
-
640
- None
641
687
  }
642
688
 
643
689
  /// Drains the accumulated work items, returning them for use by the resolver.
@@ -645,7 +691,7 @@ impl Graph {
645
691
  std::mem::take(&mut self.pending_work)
646
692
  }
647
693
 
648
- fn push_work(&mut self, unit: Unit) {
694
+ pub(crate) fn push_work(&mut self, unit: Unit) {
649
695
  self.pending_work.push(unit);
650
696
  }
651
697
 
@@ -2398,6 +2444,34 @@ mod incremental_resolution_tests {
2398
2444
  }
2399
2445
  }
2400
2446
 
2447
+ /// Compares incremental resolution against a fresh index at the declaration-ID level.
2448
+ ///
2449
+ /// This is a broad consistency check: it catches both stale declarations left
2450
+ /// behind by incremental invalidation and declarations that incremental
2451
+ /// resolution failed to recreate.
2452
+ fn assert_declaration_ids_match(incremental: &GraphTest, fresh: &GraphTest) {
2453
+ let extras: Vec<_> = incremental
2454
+ .graph()
2455
+ .declarations()
2456
+ .iter()
2457
+ .filter(|(id, _)| !fresh.graph().declarations().contains_key(id))
2458
+ .map(|(_, d)| format!("{} ({})", d.name(), d.kind()))
2459
+ .collect();
2460
+
2461
+ let missing: Vec<_> = fresh
2462
+ .graph()
2463
+ .declarations()
2464
+ .iter()
2465
+ .filter(|(id, _)| !incremental.graph().declarations().contains_key(id))
2466
+ .map(|(_, d)| format!("{} ({})", d.name(), d.kind()))
2467
+ .collect();
2468
+
2469
+ assert!(
2470
+ extras.is_empty() && missing.is_empty(),
2471
+ "Declaration mismatch:\n Extra: {extras:?}\n Missing: {missing:?}"
2472
+ );
2473
+ }
2474
+
2401
2475
  #[test]
2402
2476
  fn new_namespace_shadowing_include_target_invalidates_references() {
2403
2477
  let mut context = GraphTest::new();
@@ -3772,6 +3846,162 @@ mod incremental_resolution_tests {
3772
3846
  assert_declaration_exists!(context, "Parent::Target::<Target>");
3773
3847
  }
3774
3848
 
3849
+ #[test]
3850
+ fn singleton_definition_survives_receiver_delete_readd() {
3851
+ let mut incremental = GraphTest::new();
3852
+ incremental.index_uri("file:///foo.rb", "class Foo; end");
3853
+ incremental.index_uri("file:///singleton.rb", "class << Foo; def bar; end; end");
3854
+ incremental.resolve();
3855
+ assert_declaration_exists!(incremental, "Foo::<Foo>");
3856
+ assert_declaration_exists!(incremental, "Foo::<Foo>#bar()");
3857
+
3858
+ incremental.delete_uri("file:///foo.rb");
3859
+ incremental.resolve();
3860
+ assert_declaration_does_not_exist!(incremental, "Foo");
3861
+ assert_declaration_does_not_exist!(incremental, "Foo::<Foo>");
3862
+ assert_declaration_does_not_exist!(incremental, "Foo::<Foo>#bar()");
3863
+ assert_declaration_does_not_exist!(incremental, "Object#bar()");
3864
+
3865
+ incremental.index_uri("file:///foo.rb", "class Foo; end");
3866
+ incremental.resolve();
3867
+
3868
+ let mut fresh = GraphTest::new();
3869
+ fresh.index_uri("file:///foo.rb", "class Foo; end");
3870
+ fresh.index_uri("file:///singleton.rb", "class << Foo; def bar; end; end");
3871
+ fresh.resolve();
3872
+
3873
+ assert_declaration_ids_match(&incremental, &fresh);
3874
+ assert_declaration_exists!(incremental, "Foo::<Foo>#bar()");
3875
+ }
3876
+
3877
+ #[test]
3878
+ fn explicit_singleton_method_survives_receiver_delete_readd() {
3879
+ let mut incremental = GraphTest::new();
3880
+ incremental.index_uri("file:///foo.rb", "class Foo; end");
3881
+ incremental.index_uri("file:///singleton.rb", "def Foo.bar; end");
3882
+ incremental.resolve();
3883
+ assert_declaration_exists!(incremental, "Foo::<Foo>#bar()");
3884
+
3885
+ incremental.delete_uri("file:///foo.rb");
3886
+ incremental.resolve();
3887
+ assert_declaration_does_not_exist!(incremental, "Foo::<Foo>#bar()");
3888
+
3889
+ incremental.index_uri("file:///foo.rb", "class Foo; end");
3890
+ incremental.resolve();
3891
+
3892
+ let mut fresh = GraphTest::new();
3893
+ fresh.index_uri("file:///foo.rb", "class Foo; end");
3894
+ fresh.index_uri("file:///singleton.rb", "def Foo.bar; end");
3895
+ fresh.resolve();
3896
+
3897
+ assert_declaration_ids_match(&incremental, &fresh);
3898
+ assert_declaration_exists!(incremental, "Foo::<Foo>#bar()");
3899
+ }
3900
+
3901
+ #[test]
3902
+ fn explicit_singleton_method_ivar_survives_receiver_delete_readd() {
3903
+ let mut incremental = GraphTest::new();
3904
+ incremental.index_uri("file:///foo.rb", "class Foo; end");
3905
+ incremental.index_uri("file:///singleton.rb", "def Foo.bar; @x = 1; end");
3906
+ incremental.resolve();
3907
+ assert_declaration_exists!(incremental, "Foo::<Foo>#bar()");
3908
+ assert_declaration_exists!(incremental, "Foo::<Foo>#@x");
3909
+
3910
+ incremental.delete_uri("file:///foo.rb");
3911
+ incremental.resolve();
3912
+ assert_declaration_does_not_exist!(incremental, "Foo::<Foo>#bar()");
3913
+ assert_declaration_does_not_exist!(incremental, "Foo::<Foo>#@x");
3914
+
3915
+ incremental.index_uri("file:///foo.rb", "class Foo; end");
3916
+ incremental.resolve();
3917
+
3918
+ let mut fresh = GraphTest::new();
3919
+ fresh.index_uri("file:///foo.rb", "class Foo; end");
3920
+ fresh.index_uri("file:///singleton.rb", "def Foo.bar; @x = 1; end");
3921
+ fresh.resolve();
3922
+
3923
+ assert_declaration_ids_match(&incremental, &fresh);
3924
+ assert_declaration_exists!(incremental, "Foo::<Foo>#bar()");
3925
+ assert_declaration_exists!(incremental, "Foo::<Foo>#@x");
3926
+ }
3927
+
3928
+ #[test]
3929
+ fn constant_receiver_method_alias_survives_receiver_delete_readd() {
3930
+ let mut incremental = GraphTest::new();
3931
+ incremental.index_uri("file:///foo.rb", "class Foo; end");
3932
+ incremental.index_uri("file:///alias.rb", "Foo.alias_method :new_name, :old_name");
3933
+ incremental.resolve();
3934
+ assert_declaration_exists!(incremental, "Foo#new_name()");
3935
+
3936
+ incremental.delete_uri("file:///foo.rb");
3937
+ incremental.resolve();
3938
+ assert_declaration_does_not_exist!(incremental, "Foo#new_name()");
3939
+
3940
+ incremental.index_uri("file:///foo.rb", "class Foo; end");
3941
+ incremental.resolve();
3942
+
3943
+ let mut fresh = GraphTest::new();
3944
+ fresh.index_uri("file:///foo.rb", "class Foo; end");
3945
+ fresh.index_uri("file:///alias.rb", "Foo.alias_method :new_name, :old_name");
3946
+ fresh.resolve();
3947
+
3948
+ assert_declaration_ids_match(&incremental, &fresh);
3949
+ assert_declaration_exists!(incremental, "Foo#new_name()");
3950
+ }
3951
+
3952
+ #[test]
3953
+ fn singleton_body_method_alias_survives_receiver_delete_readd() {
3954
+ let mut incremental = GraphTest::new();
3955
+ incremental.index_uri("file:///foo.rb", "class Foo; end");
3956
+ incremental.index_uri("file:///singleton.rb", "class << Foo; def old; end; alias new old; end");
3957
+ incremental.resolve();
3958
+ assert_declaration_exists!(incremental, "Foo::<Foo>#old()");
3959
+ assert_declaration_exists!(incremental, "Foo::<Foo>#new()");
3960
+
3961
+ incremental.delete_uri("file:///foo.rb");
3962
+ incremental.resolve();
3963
+ assert_declaration_does_not_exist!(incremental, "Foo::<Foo>#old()");
3964
+ assert_declaration_does_not_exist!(incremental, "Foo::<Foo>#new()");
3965
+ assert_declaration_does_not_exist!(incremental, "Object#old()");
3966
+ assert_declaration_does_not_exist!(incremental, "Object#new()");
3967
+
3968
+ incremental.index_uri("file:///foo.rb", "class Foo; end");
3969
+ incremental.resolve();
3970
+
3971
+ let mut fresh = GraphTest::new();
3972
+ fresh.index_uri("file:///foo.rb", "class Foo; end");
3973
+ fresh.index_uri("file:///singleton.rb", "class << Foo; def old; end; alias new old; end");
3974
+ fresh.resolve();
3975
+
3976
+ assert_declaration_ids_match(&incremental, &fresh);
3977
+ assert_declaration_exists!(incremental, "Foo::<Foo>#new()");
3978
+ }
3979
+
3980
+ #[test]
3981
+ fn singleton_body_ivar_survives_receiver_delete_readd() {
3982
+ let mut incremental = GraphTest::new();
3983
+ incremental.index_uri("file:///foo.rb", "class Foo; end");
3984
+ incremental.index_uri("file:///singleton.rb", "class << Foo; @bar = 1; end");
3985
+ incremental.resolve();
3986
+ assert_declaration_exists!(incremental, "Foo::<Foo>::<<Foo>>#@bar");
3987
+
3988
+ incremental.delete_uri("file:///foo.rb");
3989
+ incremental.resolve();
3990
+ assert_declaration_does_not_exist!(incremental, "Foo::<Foo>::<<Foo>>#@bar");
3991
+ assert_declaration_does_not_exist!(incremental, "Object::<Object>#@bar");
3992
+
3993
+ incremental.index_uri("file:///foo.rb", "class Foo; end");
3994
+ incremental.resolve();
3995
+
3996
+ let mut fresh = GraphTest::new();
3997
+ fresh.index_uri("file:///foo.rb", "class Foo; end");
3998
+ fresh.index_uri("file:///singleton.rb", "class << Foo; @bar = 1; end");
3999
+ fresh.resolve();
4000
+
4001
+ assert_declaration_ids_match(&incremental, &fresh);
4002
+ assert_declaration_exists!(incremental, "Foo::<Foo>::<<Foo>>#@bar");
4003
+ }
4004
+
3775
4005
  #[test]
3776
4006
  fn no_duplicate_definition_on_identical_file_delete_readd() {
3777
4007
  let source = "class Foo; def self.run; end; def run; end; end";