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.
- checksums.yaml +4 -4
- data/ext/rubydex/declaration.c +38 -0
- data/ext/rubydex/graph.c +70 -17
- data/lib/rubydex/declaration.rb +31 -0
- data/lib/rubydex/version.rb +1 -1
- data/rbi/rubydex.rbi +41 -11
- data/rust/rubydex/src/diagnostic.rs +1 -0
- data/rust/rubydex/src/indexing/ruby_indexer.rs +2 -5
- data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +14 -7
- data/rust/rubydex/src/model/declaration.rs +48 -0
- data/rust/rubydex/src/model/definitions.rs +18 -9
- data/rust/rubydex/src/model/graph.rs +237 -35
- data/rust/rubydex/src/query.rs +1475 -159
- data/rust/rubydex/src/resolution.rs +173 -61
- data/rust/rubydex/src/resolution_tests.rs +178 -0
- data/rust/rubydex-sys/src/declaration_api.rs +19 -0
- data/rust/rubydex-sys/src/graph_api.rs +89 -5
- metadata +2 -2
|
@@ -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
|
-
|
|
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
|
|
615
|
+
/// Returns the visibility for a declaration.
|
|
606
616
|
///
|
|
607
|
-
///
|
|
608
|
-
///
|
|
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
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
Definition::
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
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";
|