rubydex 0.1.0.beta13 → 0.1.0.beta14
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 +23 -0
- data/ext/rubydex/extconf.rb +6 -2
- data/lib/rubydex/version.rb +1 -1
- data/rbi/rubydex.rbi +3 -0
- data/rust/rubydex/src/diagnostic.rs +1 -0
- data/rust/rubydex/src/indexing/ruby_indexer.rs +2 -1
- data/rust/rubydex/src/model/graph.rs +41 -2
- data/rust/rubydex/src/query.rs +1 -1
- data/rust/rubydex/src/resolution.rs +90 -2
- data/rust/rubydex/src/resolution_tests.rs +4627 -4197
- data/rust/rubydex-sys/src/declaration_api.rs +28 -0
- data/rust/rubydex-sys/src/name_api.rs +9 -7
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 55c47ced758d8aff486a445a0c15a1ffe6525b5d2ee59fea8d38be40bc791207
|
|
4
|
+
data.tar.gz: 9e73f9244f197ed1b062f475ddc077b25f1dd0a5d8c29de430c7b256b20c380e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 50444ddd1ca5576961316b2c8db705a872eaf658366587d11f9cc2860b2f97f517257ffbcc51bf9fbfa0036e436b5fe338479431c90e285ad155048616f77897
|
|
7
|
+
data.tar.gz: 856b302c492ccc0ba74a4ea1dc5c2fc993ba43a47a0f635ee005af764ae39c1be61a50fcc79f8b151c1b20e77a7e0b719115fe3427fed880ed0f0cbdd47fed45
|
data/ext/rubydex/declaration.c
CHANGED
|
@@ -407,6 +407,28 @@ static VALUE rdxr_variable_declaration_references(VALUE self) {
|
|
|
407
407
|
return rb_ary_new();
|
|
408
408
|
}
|
|
409
409
|
|
|
410
|
+
// ConstantAlias#target -> Declaration?
|
|
411
|
+
// Returns the first resolved target declaration for this constant alias, or nil if none of its definitions resolved to
|
|
412
|
+
// a target
|
|
413
|
+
static VALUE rdxr_constant_alias_target(VALUE self) {
|
|
414
|
+
HandleData *data;
|
|
415
|
+
TypedData_Get_Struct(self, HandleData, &handle_type, data);
|
|
416
|
+
|
|
417
|
+
void *graph;
|
|
418
|
+
TypedData_Get_Struct(data->graph_obj, void *, &graph_type, graph);
|
|
419
|
+
|
|
420
|
+
const CDeclaration *decl = rdx_constant_alias_target(graph, data->id);
|
|
421
|
+
if (decl == NULL) {
|
|
422
|
+
return Qnil;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
VALUE decl_class = rdxi_declaration_class_for_kind(decl->kind);
|
|
426
|
+
VALUE argv[] = {data->graph_obj, ULL2NUM(decl->id)};
|
|
427
|
+
free_c_declaration(decl);
|
|
428
|
+
|
|
429
|
+
return rb_class_new_instance(2, argv, decl_class);
|
|
430
|
+
}
|
|
431
|
+
|
|
410
432
|
void rdxi_initialize_declaration(VALUE mRubydex) {
|
|
411
433
|
cDeclaration = rb_define_class_under(mRubydex, "Declaration", rb_cObject);
|
|
412
434
|
cNamespace = rb_define_class_under(mRubydex, "Namespace", cDeclaration);
|
|
@@ -440,6 +462,7 @@ void rdxi_initialize_declaration(VALUE mRubydex) {
|
|
|
440
462
|
// Constant and ConstantAlias have constant references
|
|
441
463
|
rb_define_method(cConstant, "references", rdxr_constant_declaration_references, 0);
|
|
442
464
|
rb_define_method(cConstantAlias, "references", rdxr_constant_declaration_references, 0);
|
|
465
|
+
rb_define_method(cConstantAlias, "target", rdxr_constant_alias_target, 0);
|
|
443
466
|
|
|
444
467
|
// Method has method references
|
|
445
468
|
rb_define_method(cMethod, "references", rdxr_method_declaration_references, 0);
|
data/ext/rubydex/extconf.rb
CHANGED
|
@@ -86,11 +86,15 @@ copy_dylib_commands = if Gem.win_platform?
|
|
|
86
86
|
""
|
|
87
87
|
elsif RUBY_PLATFORM.include?("darwin")
|
|
88
88
|
src_dylib = target_dir.join("librubydex_sys.dylib")
|
|
89
|
-
|
|
89
|
+
dst_dylib = lib_dir.join("librubydex_sys.dylib")
|
|
90
|
+
# Unlink before copy so the new dylib gets a fresh inode. Overwriting in place while another process
|
|
91
|
+
# (e.g. the Ruby LSP) has the old dylib mmap'd triggers a macOS code-signing SIGKILL on its next page fault.
|
|
92
|
+
"\t$(Q)$(RM) #{dst_dylib}\n\t$(COPY) #{src_dylib} #{lib_dir}"
|
|
90
93
|
else
|
|
91
94
|
# Linux
|
|
92
95
|
src_dylib = target_dir.join("librubydex_sys.so")
|
|
93
|
-
|
|
96
|
+
dst_dylib = lib_dir.join("librubydex_sys.so")
|
|
97
|
+
"\t$(Q)$(RM) #{dst_dylib}\n\t$(COPY) #{src_dylib} #{lib_dir}"
|
|
94
98
|
end
|
|
95
99
|
|
|
96
100
|
rust_srcs = Dir.glob("#{root_dir}/**/*.rs").reject { |path| path.include?("rust/target") }
|
data/lib/rubydex/version.rb
CHANGED
data/rbi/rubydex.rbi
CHANGED
|
@@ -76,6 +76,9 @@ end
|
|
|
76
76
|
class Rubydex::ConstantAlias < Rubydex::Declaration
|
|
77
77
|
sig { returns(T::Enumerable[Rubydex::ConstantReference]) }
|
|
78
78
|
def references; end
|
|
79
|
+
|
|
80
|
+
sig { returns(T.nilable(Rubydex::Declaration)) }
|
|
81
|
+
def target; end
|
|
79
82
|
end
|
|
80
83
|
|
|
81
84
|
class Rubydex::ClassVariable < Rubydex::Declaration
|
|
@@ -1507,9 +1507,10 @@ impl Visit<'_> for RubyIndexer<'_> {
|
|
|
1507
1507
|
};
|
|
1508
1508
|
|
|
1509
1509
|
let string_id = self.local_graph.intern_string(singleton_class_name);
|
|
1510
|
+
let nesting = self.current_lexical_scope_name_id();
|
|
1510
1511
|
let name_id = self
|
|
1511
1512
|
.local_graph
|
|
1512
|
-
.add_name(Name::new(string_id, ParentScope::Attached(attached_target),
|
|
1513
|
+
.add_name(Name::new(string_id, ParentScope::Attached(attached_target), nesting));
|
|
1513
1514
|
|
|
1514
1515
|
let definition = Definition::SingletonClass(Box::new(SingletonClassDefinition::new(
|
|
1515
1516
|
name_id,
|
|
@@ -14,6 +14,7 @@ use crate::model::ids::{ConstantReferenceId, DeclarationId, DefinitionId, Method
|
|
|
14
14
|
use crate::model::name::{Name, NameRef, ParentScope, ResolvedName};
|
|
15
15
|
use crate::model::references::{ConstantReference, MethodRef};
|
|
16
16
|
use crate::model::string_ref::StringRef;
|
|
17
|
+
use crate::model::visibility::Visibility;
|
|
17
18
|
use crate::stats;
|
|
18
19
|
|
|
19
20
|
/// An entity whose validity depends on a particular `NameId`.
|
|
@@ -601,6 +602,44 @@ impl Graph {
|
|
|
601
602
|
&self.name_dependents
|
|
602
603
|
}
|
|
603
604
|
|
|
605
|
+
/// Returns the visibility for a method declaration.
|
|
606
|
+
///
|
|
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
|
|
611
|
+
#[must_use]
|
|
612
|
+
pub fn visibility(&self, declaration_id: &DeclarationId) -> Option<Visibility> {
|
|
613
|
+
let declaration = self.declarations.get(declaration_id)?;
|
|
614
|
+
let definitions = declaration.definitions();
|
|
615
|
+
|
|
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(_) => {}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
None
|
|
641
|
+
}
|
|
642
|
+
|
|
604
643
|
/// Drains the accumulated work items, returning them for use by the resolver.
|
|
605
644
|
pub fn take_pending_work(&mut self) -> Vec<Unit> {
|
|
606
645
|
std::mem::take(&mut self.pending_work)
|
|
@@ -2234,14 +2273,14 @@ mod tests {
|
|
|
2234
2273
|
assert_eq!(13, context.graph().constant_references.len());
|
|
2235
2274
|
assert_eq!(2, context.graph().method_references.len());
|
|
2236
2275
|
assert_eq!(2, context.graph().documents.len());
|
|
2237
|
-
assert_eq!(
|
|
2276
|
+
assert_eq!(20, context.graph().names.len());
|
|
2238
2277
|
assert_eq!(47, context.graph().strings.len());
|
|
2239
2278
|
context.index_uri("file:///foo.rb", source);
|
|
2240
2279
|
assert_eq!(49, context.graph().definitions.len());
|
|
2241
2280
|
assert_eq!(13, context.graph().constant_references.len());
|
|
2242
2281
|
assert_eq!(2, context.graph().method_references.len());
|
|
2243
2282
|
assert_eq!(2, context.graph().documents.len());
|
|
2244
|
-
assert_eq!(
|
|
2283
|
+
assert_eq!(20, context.graph().names.len());
|
|
2245
2284
|
assert_eq!(47, context.graph().strings.len());
|
|
2246
2285
|
}
|
|
2247
2286
|
|
data/rust/rubydex/src/query.rs
CHANGED
|
@@ -897,7 +897,7 @@ mod tests {
|
|
|
897
897
|
context.resolve();
|
|
898
898
|
|
|
899
899
|
let foo_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
900
|
-
let name_id = Name::new(StringId::from("<Foo>"), ParentScope::Attached(foo_id),
|
|
900
|
+
let name_id = Name::new(StringId::from("<Foo>"), ParentScope::Attached(foo_id), Some(foo_id)).id();
|
|
901
901
|
assert_declaration_completion_eq!(
|
|
902
902
|
context,
|
|
903
903
|
CompletionReceiver::Expression(name_id),
|
|
@@ -3,6 +3,7 @@ use std::{
|
|
|
3
3
|
hash::BuildHasher,
|
|
4
4
|
};
|
|
5
5
|
|
|
6
|
+
use crate::diagnostic::{Diagnostic, Rule};
|
|
6
7
|
use crate::model::{
|
|
7
8
|
built_in::{BASIC_OBJECT_ID, CLASS_ID, KERNEL_ID, MODULE_ID, OBJECT_ID},
|
|
8
9
|
declaration::{
|
|
@@ -272,6 +273,8 @@ impl<'a> Resolver<'a> {
|
|
|
272
273
|
/// Handle other definitions that don't require resolution, but need to have their declarations and membership created
|
|
273
274
|
#[allow(clippy::too_many_lines)]
|
|
274
275
|
fn handle_remaining_definitions(&mut self, other_ids: Vec<DefinitionId>) {
|
|
276
|
+
let mut method_visibility_ids = Vec::new();
|
|
277
|
+
|
|
275
278
|
for id in other_ids {
|
|
276
279
|
match self.graph.definitions().get(&id).unwrap() {
|
|
277
280
|
Definition::Method(method_definition) => {
|
|
@@ -538,8 +541,8 @@ impl<'a> Resolver<'a> {
|
|
|
538
541
|
Definition::ConstantVisibility(_constant_visibility) => {
|
|
539
542
|
// TODO
|
|
540
543
|
}
|
|
541
|
-
Definition::MethodVisibility(
|
|
542
|
-
|
|
544
|
+
Definition::MethodVisibility(_) => {
|
|
545
|
+
method_visibility_ids.push(id);
|
|
543
546
|
}
|
|
544
547
|
Definition::Class(_)
|
|
545
548
|
| Definition::SingletonClass(_)
|
|
@@ -550,6 +553,91 @@ impl<'a> Resolver<'a> {
|
|
|
550
553
|
}
|
|
551
554
|
}
|
|
552
555
|
}
|
|
556
|
+
|
|
557
|
+
self.resolve_method_visibilities(method_visibility_ids);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/// Resolves retroactive method visibility changes (`private :foo`, `protected :foo`, `public :foo`).
|
|
561
|
+
///
|
|
562
|
+
/// Runs as a second pass after all methods/attrs are declared, so `private :bar` works
|
|
563
|
+
/// regardless of whether `def bar` appeared before or after it in source.
|
|
564
|
+
fn resolve_method_visibilities(&mut self, visibility_ids: Vec<DefinitionId>) {
|
|
565
|
+
let mut pending_work = Vec::new();
|
|
566
|
+
|
|
567
|
+
for id in visibility_ids {
|
|
568
|
+
let Definition::MethodVisibility(method_visibility) = self.graph.definitions().get(&id).unwrap() else {
|
|
569
|
+
unreachable!()
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
let str_id = *method_visibility.str_id();
|
|
573
|
+
let uri_id = *method_visibility.uri_id();
|
|
574
|
+
let offset = method_visibility.offset().clone();
|
|
575
|
+
let lexical_nesting_id = *method_visibility.lexical_nesting_id();
|
|
576
|
+
|
|
577
|
+
let Some(owner_id) = self.resolve_lexical_owner(lexical_nesting_id) else {
|
|
578
|
+
continue;
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
let Some(Declaration::Namespace(namespace)) = self.graph.declarations().get(&owner_id) else {
|
|
582
|
+
continue;
|
|
583
|
+
};
|
|
584
|
+
|
|
585
|
+
let mut visibility_applied = false;
|
|
586
|
+
let mut has_partial = false;
|
|
587
|
+
|
|
588
|
+
for ancestor in namespace.ancestors() {
|
|
589
|
+
match ancestor {
|
|
590
|
+
Ancestor::Complete(ancestor_id) => {
|
|
591
|
+
let has_member = self
|
|
592
|
+
.graph
|
|
593
|
+
.declarations()
|
|
594
|
+
.get(ancestor_id)
|
|
595
|
+
.and_then(|decl| decl.as_namespace())
|
|
596
|
+
.and_then(|ns| ns.member(&str_id))
|
|
597
|
+
.is_some();
|
|
598
|
+
|
|
599
|
+
if has_member {
|
|
600
|
+
// Direct member: `create_declaration`'s fully qualified name dedup attaches
|
|
601
|
+
// this visibility definition to the existing method declaration.
|
|
602
|
+
// Inherited: a new child-owned declaration is created.
|
|
603
|
+
self.create_declaration(str_id, id, owner_id, |name| {
|
|
604
|
+
Declaration::Method(Box::new(MethodDeclaration::new(name, owner_id)))
|
|
605
|
+
});
|
|
606
|
+
visibility_applied = true;
|
|
607
|
+
break;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
Ancestor::Partial(_) => has_partial = true,
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
if visibility_applied {
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if has_partial {
|
|
619
|
+
// Method might exist on an unresolved ancestor — requeue for retry.
|
|
620
|
+
pending_work.push(Unit::Definition(id));
|
|
621
|
+
} else {
|
|
622
|
+
// Ancestors are fully resolved — method definitively doesn't exist.
|
|
623
|
+
let method_name = self.graph.strings().get(&str_id).unwrap().as_str().to_string();
|
|
624
|
+
let owner_name = self.graph.declarations().get(&owner_id).unwrap().name().to_string();
|
|
625
|
+
let diagnostic = Diagnostic::new(
|
|
626
|
+
Rule::UndefinedMethodVisibilityTarget,
|
|
627
|
+
uri_id,
|
|
628
|
+
offset,
|
|
629
|
+
format!("undefined method `{method_name}` for visibility change in `{owner_name}`"),
|
|
630
|
+
);
|
|
631
|
+
self.graph
|
|
632
|
+
.declarations_mut()
|
|
633
|
+
.get_mut(&owner_id)
|
|
634
|
+
.unwrap()
|
|
635
|
+
.add_diagnostic(diagnostic);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// Must extend work here so incremental resolution can resolve previously unresolved visibility operations
|
|
640
|
+
self.graph.extend_work(pending_work);
|
|
553
641
|
}
|
|
554
642
|
|
|
555
643
|
fn create_declaration<F>(
|