rubydex 0.1.0.beta14 → 0.2.1

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)
@@ -155,8 +155,8 @@ impl Definition {
155
155
  Definition::Module(d) => Some(d.name_id()),
156
156
  Definition::Constant(d) => Some(d.name_id()),
157
157
  Definition::ConstantAlias(d) => Some(d.name_id()),
158
- Definition::ConstantVisibility(d) => Some(d.name_id()),
159
- Definition::MethodVisibility(_)
158
+ Definition::ConstantVisibility(_)
159
+ | Definition::MethodVisibility(_)
160
160
  | Definition::GlobalVariable(_)
161
161
  | Definition::InstanceVariable(_)
162
162
  | Definition::ClassVariable(_)
@@ -711,7 +711,8 @@ impl ConstantAliasDefinition {
711
711
 
712
712
  #[derive(Debug)]
713
713
  pub struct ConstantVisibilityDefinition {
714
- name_id: NameId,
714
+ receiver: Option<NameId>,
715
+ target: StringId,
715
716
  visibility: Visibility,
716
717
  uri_id: UriId,
717
718
  offset: Offset,
@@ -722,8 +723,10 @@ pub struct ConstantVisibilityDefinition {
722
723
 
723
724
  impl ConstantVisibilityDefinition {
724
725
  #[must_use]
726
+ #[allow(clippy::too_many_arguments)]
725
727
  pub const fn new(
726
- name_id: NameId,
728
+ receiver: Option<NameId>,
729
+ target: StringId,
727
730
  visibility: Visibility,
728
731
  uri_id: UriId,
729
732
  offset: Offset,
@@ -732,7 +735,8 @@ impl ConstantVisibilityDefinition {
732
735
  lexical_nesting_id: Option<DefinitionId>,
733
736
  ) -> Self {
734
737
  Self {
735
- name_id,
738
+ receiver,
739
+ target,
736
740
  visibility,
737
741
  uri_id,
738
742
  offset,
@@ -744,12 +748,17 @@ impl ConstantVisibilityDefinition {
744
748
 
745
749
  #[must_use]
746
750
  pub fn id(&self) -> DefinitionId {
747
- DefinitionId::from(&format!("{}{}{}", *self.uri_id, self.offset.start(), *self.name_id))
751
+ DefinitionId::from(&format!("{}{}{}", *self.uri_id, self.offset.start(), *self.target))
748
752
  }
749
753
 
750
754
  #[must_use]
751
- pub fn name_id(&self) -> &NameId {
752
- &self.name_id
755
+ pub fn receiver(&self) -> &Option<NameId> {
756
+ &self.receiver
757
+ }
758
+
759
+ #[must_use]
760
+ pub fn target(&self) -> &StringId {
761
+ &self.target
753
762
  }
754
763
 
755
764
  #[must_use]
@@ -782,7 +791,7 @@ impl ConstantVisibilityDefinition {
782
791
  &self.flags
783
792
  }
784
793
  }
785
- assert_mem_size!(ConstantVisibilityDefinition, 56);
794
+ assert_mem_size!(ConstantVisibilityDefinition, 64);
786
795
 
787
796
  #[derive(Debug)]
788
797
  pub struct MethodVisibilityDefinition {
@@ -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,9 +327,12 @@ 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
- }
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(),
335
+ ),
326
336
  Definition::MethodVisibility(it) => (
327
337
  self.find_enclosing_namespace_name_id(it.lexical_nesting_id().as_ref()),
328
338
  it.str_id(),
@@ -602,42 +612,50 @@ impl Graph {
602
612
  &self.name_dependents
603
613
  }
604
614
 
605
- /// Returns the visibility for a method declaration.
615
+ /// Returns the visibility for a declaration.
606
616
  ///
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
617
+ /// For methods, the latest definition wins. For constants, the latest
618
+ /// `private_constant`/`public_constant` wins, otherwise `Public`.
611
619
  #[must_use]
612
620
  pub fn visibility(&self, declaration_id: &DeclarationId) -> Option<Visibility> {
613
621
  let declaration = self.declarations.get(declaration_id)?;
614
622
  let definitions = declaration.definitions();
615
623
 
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(_) => {}
624
+ match declaration {
625
+ Declaration::Namespace(Namespace::Class(_) | Namespace::Module(_))
626
+ | Declaration::Constant(_)
627
+ | Declaration::ConstantAlias(_) => {
628
+ for def_id in definitions.iter().rev() {
629
+ if let Some(Definition::ConstantVisibility(vis)) = self.definitions.get(def_id) {
630
+ return Some(*vis.visibility());
631
+ }
632
+ }
633
+ Some(Visibility::Public)
634
+ }
635
+ Declaration::Method(_) => {
636
+ for def_id in definitions.iter().rev() {
637
+ let Some(definition) = self.definitions.get(def_id) else {
638
+ continue;
639
+ };
640
+ let visibility = match definition {
641
+ Definition::MethodVisibility(vis) => Some(*vis.visibility()),
642
+ Definition::Method(method) => Some(*method.visibility()),
643
+ Definition::AttrAccessor(attr) => Some(*attr.visibility()),
644
+ Definition::AttrReader(attr) => Some(*attr.visibility()),
645
+ Definition::AttrWriter(attr) => Some(*attr.visibility()),
646
+ _ => None,
647
+ };
648
+ if visibility.is_some() {
649
+ return visibility;
650
+ }
636
651
  }
652
+ None
637
653
  }
654
+ Declaration::Namespace(Namespace::SingletonClass(_) | Namespace::Todo(_))
655
+ | Declaration::GlobalVariable(_)
656
+ | Declaration::InstanceVariable(_)
657
+ | Declaration::ClassVariable(_) => None,
638
658
  }
639
-
640
- None
641
659
  }
642
660
 
643
661
  /// Drains the accumulated work items, returning them for use by the resolver.
@@ -645,7 +663,7 @@ impl Graph {
645
663
  std::mem::take(&mut self.pending_work)
646
664
  }
647
665
 
648
- fn push_work(&mut self, unit: Unit) {
666
+ pub(crate) fn push_work(&mut self, unit: Unit) {
649
667
  self.pending_work.push(unit);
650
668
  }
651
669
 
@@ -2398,6 +2416,34 @@ mod incremental_resolution_tests {
2398
2416
  }
2399
2417
  }
2400
2418
 
2419
+ /// Compares incremental resolution against a fresh index at the declaration-ID level.
2420
+ ///
2421
+ /// This is a broad consistency check: it catches both stale declarations left
2422
+ /// behind by incremental invalidation and declarations that incremental
2423
+ /// resolution failed to recreate.
2424
+ fn assert_declaration_ids_match(incremental: &GraphTest, fresh: &GraphTest) {
2425
+ let extras: Vec<_> = incremental
2426
+ .graph()
2427
+ .declarations()
2428
+ .iter()
2429
+ .filter(|(id, _)| !fresh.graph().declarations().contains_key(id))
2430
+ .map(|(_, d)| format!("{} ({})", d.name(), d.kind()))
2431
+ .collect();
2432
+
2433
+ let missing: Vec<_> = fresh
2434
+ .graph()
2435
+ .declarations()
2436
+ .iter()
2437
+ .filter(|(id, _)| !incremental.graph().declarations().contains_key(id))
2438
+ .map(|(_, d)| format!("{} ({})", d.name(), d.kind()))
2439
+ .collect();
2440
+
2441
+ assert!(
2442
+ extras.is_empty() && missing.is_empty(),
2443
+ "Declaration mismatch:\n Extra: {extras:?}\n Missing: {missing:?}"
2444
+ );
2445
+ }
2446
+
2401
2447
  #[test]
2402
2448
  fn new_namespace_shadowing_include_target_invalidates_references() {
2403
2449
  let mut context = GraphTest::new();
@@ -3772,6 +3818,162 @@ mod incremental_resolution_tests {
3772
3818
  assert_declaration_exists!(context, "Parent::Target::<Target>");
3773
3819
  }
3774
3820
 
3821
+ #[test]
3822
+ fn singleton_definition_survives_receiver_delete_readd() {
3823
+ let mut incremental = GraphTest::new();
3824
+ incremental.index_uri("file:///foo.rb", "class Foo; end");
3825
+ incremental.index_uri("file:///singleton.rb", "class << Foo; def bar; end; end");
3826
+ incremental.resolve();
3827
+ assert_declaration_exists!(incremental, "Foo::<Foo>");
3828
+ assert_declaration_exists!(incremental, "Foo::<Foo>#bar()");
3829
+
3830
+ incremental.delete_uri("file:///foo.rb");
3831
+ incremental.resolve();
3832
+ assert_declaration_does_not_exist!(incremental, "Foo");
3833
+ assert_declaration_does_not_exist!(incremental, "Foo::<Foo>");
3834
+ assert_declaration_does_not_exist!(incremental, "Foo::<Foo>#bar()");
3835
+ assert_declaration_does_not_exist!(incremental, "Object#bar()");
3836
+
3837
+ incremental.index_uri("file:///foo.rb", "class Foo; end");
3838
+ incremental.resolve();
3839
+
3840
+ let mut fresh = GraphTest::new();
3841
+ fresh.index_uri("file:///foo.rb", "class Foo; end");
3842
+ fresh.index_uri("file:///singleton.rb", "class << Foo; def bar; end; end");
3843
+ fresh.resolve();
3844
+
3845
+ assert_declaration_ids_match(&incremental, &fresh);
3846
+ assert_declaration_exists!(incremental, "Foo::<Foo>#bar()");
3847
+ }
3848
+
3849
+ #[test]
3850
+ fn explicit_singleton_method_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", "def Foo.bar; end");
3854
+ incremental.resolve();
3855
+ assert_declaration_exists!(incremental, "Foo::<Foo>#bar()");
3856
+
3857
+ incremental.delete_uri("file:///foo.rb");
3858
+ incremental.resolve();
3859
+ assert_declaration_does_not_exist!(incremental, "Foo::<Foo>#bar()");
3860
+
3861
+ incremental.index_uri("file:///foo.rb", "class Foo; end");
3862
+ incremental.resolve();
3863
+
3864
+ let mut fresh = GraphTest::new();
3865
+ fresh.index_uri("file:///foo.rb", "class Foo; end");
3866
+ fresh.index_uri("file:///singleton.rb", "def Foo.bar; end");
3867
+ fresh.resolve();
3868
+
3869
+ assert_declaration_ids_match(&incremental, &fresh);
3870
+ assert_declaration_exists!(incremental, "Foo::<Foo>#bar()");
3871
+ }
3872
+
3873
+ #[test]
3874
+ fn explicit_singleton_method_ivar_survives_receiver_delete_readd() {
3875
+ let mut incremental = GraphTest::new();
3876
+ incremental.index_uri("file:///foo.rb", "class Foo; end");
3877
+ incremental.index_uri("file:///singleton.rb", "def Foo.bar; @x = 1; end");
3878
+ incremental.resolve();
3879
+ assert_declaration_exists!(incremental, "Foo::<Foo>#bar()");
3880
+ assert_declaration_exists!(incremental, "Foo::<Foo>#@x");
3881
+
3882
+ incremental.delete_uri("file:///foo.rb");
3883
+ incremental.resolve();
3884
+ assert_declaration_does_not_exist!(incremental, "Foo::<Foo>#bar()");
3885
+ assert_declaration_does_not_exist!(incremental, "Foo::<Foo>#@x");
3886
+
3887
+ incremental.index_uri("file:///foo.rb", "class Foo; end");
3888
+ incremental.resolve();
3889
+
3890
+ let mut fresh = GraphTest::new();
3891
+ fresh.index_uri("file:///foo.rb", "class Foo; end");
3892
+ fresh.index_uri("file:///singleton.rb", "def Foo.bar; @x = 1; end");
3893
+ fresh.resolve();
3894
+
3895
+ assert_declaration_ids_match(&incremental, &fresh);
3896
+ assert_declaration_exists!(incremental, "Foo::<Foo>#bar()");
3897
+ assert_declaration_exists!(incremental, "Foo::<Foo>#@x");
3898
+ }
3899
+
3900
+ #[test]
3901
+ fn constant_receiver_method_alias_survives_receiver_delete_readd() {
3902
+ let mut incremental = GraphTest::new();
3903
+ incremental.index_uri("file:///foo.rb", "class Foo; end");
3904
+ incremental.index_uri("file:///alias.rb", "Foo.alias_method :new_name, :old_name");
3905
+ incremental.resolve();
3906
+ assert_declaration_exists!(incremental, "Foo#new_name()");
3907
+
3908
+ incremental.delete_uri("file:///foo.rb");
3909
+ incremental.resolve();
3910
+ assert_declaration_does_not_exist!(incremental, "Foo#new_name()");
3911
+
3912
+ incremental.index_uri("file:///foo.rb", "class Foo; end");
3913
+ incremental.resolve();
3914
+
3915
+ let mut fresh = GraphTest::new();
3916
+ fresh.index_uri("file:///foo.rb", "class Foo; end");
3917
+ fresh.index_uri("file:///alias.rb", "Foo.alias_method :new_name, :old_name");
3918
+ fresh.resolve();
3919
+
3920
+ assert_declaration_ids_match(&incremental, &fresh);
3921
+ assert_declaration_exists!(incremental, "Foo#new_name()");
3922
+ }
3923
+
3924
+ #[test]
3925
+ fn singleton_body_method_alias_survives_receiver_delete_readd() {
3926
+ let mut incremental = GraphTest::new();
3927
+ incremental.index_uri("file:///foo.rb", "class Foo; end");
3928
+ incremental.index_uri("file:///singleton.rb", "class << Foo; def old; end; alias new old; end");
3929
+ incremental.resolve();
3930
+ assert_declaration_exists!(incremental, "Foo::<Foo>#old()");
3931
+ assert_declaration_exists!(incremental, "Foo::<Foo>#new()");
3932
+
3933
+ incremental.delete_uri("file:///foo.rb");
3934
+ incremental.resolve();
3935
+ assert_declaration_does_not_exist!(incremental, "Foo::<Foo>#old()");
3936
+ assert_declaration_does_not_exist!(incremental, "Foo::<Foo>#new()");
3937
+ assert_declaration_does_not_exist!(incremental, "Object#old()");
3938
+ assert_declaration_does_not_exist!(incremental, "Object#new()");
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:///singleton.rb", "class << Foo; def old; end; alias new old; end");
3946
+ fresh.resolve();
3947
+
3948
+ assert_declaration_ids_match(&incremental, &fresh);
3949
+ assert_declaration_exists!(incremental, "Foo::<Foo>#new()");
3950
+ }
3951
+
3952
+ #[test]
3953
+ fn singleton_body_ivar_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; @bar = 1; end");
3957
+ incremental.resolve();
3958
+ assert_declaration_exists!(incremental, "Foo::<Foo>::<<Foo>>#@bar");
3959
+
3960
+ incremental.delete_uri("file:///foo.rb");
3961
+ incremental.resolve();
3962
+ assert_declaration_does_not_exist!(incremental, "Foo::<Foo>::<<Foo>>#@bar");
3963
+ assert_declaration_does_not_exist!(incremental, "Object::<Object>#@bar");
3964
+
3965
+ incremental.index_uri("file:///foo.rb", "class Foo; end");
3966
+ incremental.resolve();
3967
+
3968
+ let mut fresh = GraphTest::new();
3969
+ fresh.index_uri("file:///foo.rb", "class Foo; end");
3970
+ fresh.index_uri("file:///singleton.rb", "class << Foo; @bar = 1; end");
3971
+ fresh.resolve();
3972
+
3973
+ assert_declaration_ids_match(&incremental, &fresh);
3974
+ assert_declaration_exists!(incremental, "Foo::<Foo>::<<Foo>>#@bar");
3975
+ }
3976
+
3775
3977
  #[test]
3776
3978
  fn no_duplicate_definition_on_identical_file_delete_readd() {
3777
3979
  let source = "class Foo; def self.run; end; def run; end; end";