rubydex 0.2.0 → 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.
@@ -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";