rubydex 0.1.0.beta3-x86_64-linux → 0.1.0.beta6-x86_64-linux
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/README.md +59 -2
- data/ext/rubydex/graph.c +92 -16
- data/lib/rubydex/3.2/rubydex.so +0 -0
- data/lib/rubydex/3.3/rubydex.so +0 -0
- data/lib/rubydex/3.4/rubydex.so +0 -0
- data/lib/rubydex/4.0/rubydex.so +0 -0
- data/lib/rubydex/graph.rb +44 -16
- data/lib/rubydex/librubydex_sys.so +0 -0
- data/lib/rubydex/version.rb +1 -1
- data/rust/rubydex/src/indexing/rbs_indexer.rs +239 -0
- data/rust/rubydex/src/indexing/ruby_indexer.rs +109 -249
- data/rust/rubydex/src/indexing.rs +87 -10
- data/rust/rubydex/src/listing.rs +24 -1
- data/rust/rubydex/src/main.rs +24 -0
- data/rust/rubydex/src/model/declaration.rs +5 -0
- data/rust/rubydex/src/model/definitions.rs +10 -1
- data/rust/rubydex/src/model/graph.rs +174 -70
- data/rust/rubydex/src/model/name.rs +5 -0
- data/rust/rubydex/src/offset.rs +17 -2
- data/rust/rubydex/src/query.rs +853 -38
- data/rust/rubydex/src/resolution.rs +86 -0
- data/rust/rubydex/src/stats/orphan_report.rs +262 -0
- data/rust/rubydex/src/stats.rs +2 -0
- data/rust/rubydex/src/test_utils/graph_test.rs +9 -11
- data/rust/rubydex/src/test_utils/local_graph_test.rs +167 -0
- data/rust/rubydex-sys/src/graph_api.rs +122 -22
- data/rust/rubydex-sys/src/utils.rs +20 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ba2df1c014d93db599af99939667aa410786bae376f66caba253fa0cdc6cddb5
|
|
4
|
+
data.tar.gz: 2a6b68cf1658cb5f87225cff5a336565541325cd3356a34e96abd77f527789ae
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5e407f9ade85f4febeadc490b3f306229f3197d323b7d404b0a9f7032b13b93eb78e4794037490ee2fa909a440302f16708437350c8ec946c4e64c5430ab3ec6
|
|
7
|
+
data.tar.gz: 595952a7249abe53c78b35196f8a03f5defe24fb997f9f67cb6fca8f22769d5e718d4795c3487d273f3e179eb5d1368a93294fd4fb6a0fde2b95e6ccc82eeb75
|
data/README.md
CHANGED
|
@@ -11,13 +11,70 @@ of using the Ruby API:
|
|
|
11
11
|
```ruby
|
|
12
12
|
# Create a new graph representing the current workspace
|
|
13
13
|
graph = Rubydex::Graph.new
|
|
14
|
+
# Configuring graph LSP encoding
|
|
15
|
+
graph.encoding = "utf16"
|
|
14
16
|
# Index the entire workspace with all dependencies
|
|
15
17
|
graph.index_workspace
|
|
18
|
+
# Or index specific file paths
|
|
19
|
+
graph.index_all(["path/to/file.rb"])
|
|
16
20
|
# Transform the initially collected information into its semantic understanding by running resolution
|
|
17
21
|
graph.resolve
|
|
22
|
+
# Get all diagnostics acquired during the analysis
|
|
23
|
+
graph.diagnostics
|
|
18
24
|
|
|
19
|
-
#
|
|
20
|
-
graph
|
|
25
|
+
# Iterating over graph nodes
|
|
26
|
+
graph.declarations
|
|
27
|
+
graph.documents
|
|
28
|
+
graph.constant_references
|
|
29
|
+
graph.method_references
|
|
30
|
+
|
|
31
|
+
# Analyzing require paths
|
|
32
|
+
graph.resolve_require_path("rails/engine", load_paths) # => document pointed by `rails/engine`
|
|
33
|
+
graph.require_paths(load_paths) # => array of all indexed require paths
|
|
34
|
+
|
|
35
|
+
# Querying
|
|
36
|
+
graph["Foo"] # Get declaration by fully qualified name
|
|
37
|
+
graph.search("Foo#b") # Name search
|
|
38
|
+
graph.resolve_constant("Bar", ["Foo", "Baz::Qux"]) # Resolve constant reference based on nesting
|
|
39
|
+
|
|
40
|
+
# Declarations
|
|
41
|
+
declaration = graph["Foo"]
|
|
42
|
+
|
|
43
|
+
# All declarations include
|
|
44
|
+
declaration.name
|
|
45
|
+
declaration.unqualified_name
|
|
46
|
+
declaration.definitions
|
|
47
|
+
declaration.owner
|
|
48
|
+
|
|
49
|
+
# Namespace declarations include
|
|
50
|
+
declaration.member("bar()")
|
|
51
|
+
declaration.member("@ivar")
|
|
52
|
+
declaration.singleton_class
|
|
53
|
+
declaration.ancestors
|
|
54
|
+
declaration.descendants
|
|
55
|
+
|
|
56
|
+
# Documents
|
|
57
|
+
document = graph.documents.first
|
|
58
|
+
document.uri
|
|
59
|
+
document.definitions # => list of definitions discovered in this document
|
|
60
|
+
|
|
61
|
+
# Definitions
|
|
62
|
+
definition = declaration.definitions.first
|
|
63
|
+
definition.location
|
|
64
|
+
definition.comments
|
|
65
|
+
definition.name
|
|
66
|
+
definition.deprecated?
|
|
67
|
+
definition.name_location
|
|
68
|
+
|
|
69
|
+
# Locations
|
|
70
|
+
location = definition.location
|
|
71
|
+
location.path
|
|
72
|
+
|
|
73
|
+
# Diagnostics
|
|
74
|
+
diagnostic = graph.diagnostics.first
|
|
75
|
+
diagnostic.rule
|
|
76
|
+
diagnostic.message
|
|
77
|
+
diagnostic.location
|
|
21
78
|
```
|
|
22
79
|
|
|
23
80
|
## Contributing
|
data/ext/rubydex/graph.c
CHANGED
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
#include "utils.h"
|
|
10
10
|
|
|
11
11
|
static VALUE cGraph;
|
|
12
|
-
static VALUE eIndexingError;
|
|
13
12
|
|
|
14
13
|
// Free function for the custom Graph allocator. We always have to call into Rust to free data allocated by it
|
|
15
14
|
static void graph_free(void *ptr) {
|
|
@@ -27,8 +26,8 @@ static VALUE rdxr_graph_alloc(VALUE klass) {
|
|
|
27
26
|
return TypedData_Wrap_Struct(klass, &graph_type, graph);
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
// Graph#index_all: (Array[String] file_paths) ->
|
|
31
|
-
//
|
|
29
|
+
// Graph#index_all: (Array[String] file_paths) -> Array[String]
|
|
30
|
+
// Returns an array of IO error messages encountered during indexing
|
|
32
31
|
static VALUE rdxr_graph_index_all(VALUE self, VALUE file_paths) {
|
|
33
32
|
rdxi_check_array_of_strings(file_paths);
|
|
34
33
|
|
|
@@ -36,10 +35,12 @@ static VALUE rdxr_graph_index_all(VALUE self, VALUE file_paths) {
|
|
|
36
35
|
size_t length = RARRAY_LEN(file_paths);
|
|
37
36
|
char **converted_file_paths = rdxi_str_array_to_char(file_paths, length);
|
|
38
37
|
|
|
39
|
-
// Get the
|
|
38
|
+
// Get the underlying graph pointer and then invoke the Rust index all implementation
|
|
40
39
|
void *graph;
|
|
41
40
|
TypedData_Get_Struct(self, void *, &graph_type, graph);
|
|
42
|
-
|
|
41
|
+
|
|
42
|
+
size_t error_count = 0;
|
|
43
|
+
const char *const *errors = rdx_index_all(graph, (const char **)converted_file_paths, length, &error_count);
|
|
43
44
|
|
|
44
45
|
// Free the converted file paths and allow the GC to collect them
|
|
45
46
|
for (size_t i = 0; i < length; i++) {
|
|
@@ -47,12 +48,36 @@ static VALUE rdxr_graph_index_all(VALUE self, VALUE file_paths) {
|
|
|
47
48
|
}
|
|
48
49
|
free(converted_file_paths);
|
|
49
50
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
if (errors == NULL) {
|
|
52
|
+
return rb_ary_new();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
VALUE array = rb_ary_new_capa((long)error_count);
|
|
56
|
+
for (size_t i = 0; i < error_count; i++) {
|
|
57
|
+
rb_ary_push(array, rb_utf8_str_new_cstr(errors[i]));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
free_c_string_array(errors, error_count);
|
|
61
|
+
return array;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Indexes a single source string in memory, dispatching to the appropriate indexer based on language_id
|
|
65
|
+
//
|
|
66
|
+
// Graph#index_source: (String uri, String source, String language_id) -> void
|
|
67
|
+
static VALUE rdxr_graph_index_source(VALUE self, VALUE uri, VALUE source, VALUE language_id) {
|
|
68
|
+
Check_Type(uri, T_STRING);
|
|
69
|
+
Check_Type(source, T_STRING);
|
|
70
|
+
Check_Type(language_id, T_STRING);
|
|
71
|
+
|
|
72
|
+
void *graph;
|
|
73
|
+
TypedData_Get_Struct(self, void *, &graph_type, graph);
|
|
74
|
+
|
|
75
|
+
const char *uri_str = StringValueCStr(uri);
|
|
76
|
+
const char *source_str = StringValueCStr(source);
|
|
77
|
+
const char *language_id_str = StringValueCStr(language_id);
|
|
78
|
+
|
|
79
|
+
if (!rdx_index_source(graph, uri_str, source_str, language_id_str)) {
|
|
80
|
+
rb_raise(rb_eArgError, "unsupported language_id `%s`", language_id_str);
|
|
56
81
|
}
|
|
57
82
|
|
|
58
83
|
return Qnil;
|
|
@@ -272,6 +297,25 @@ static VALUE rdxr_graph_method_references(VALUE self) {
|
|
|
272
297
|
return self;
|
|
273
298
|
}
|
|
274
299
|
|
|
300
|
+
// Graph#delete_document: (String uri) -> Document?
|
|
301
|
+
// Deletes a document and all of its definitions from the graph.
|
|
302
|
+
// Returns the removed Document or nil if it doesn't exist.
|
|
303
|
+
static VALUE rdxr_graph_delete_document(VALUE self, VALUE uri) {
|
|
304
|
+
Check_Type(uri, T_STRING);
|
|
305
|
+
|
|
306
|
+
void *graph;
|
|
307
|
+
TypedData_Get_Struct(self, void *, &graph_type, graph);
|
|
308
|
+
const uint64_t *uri_id = rdx_graph_delete_document(graph, StringValueCStr(uri));
|
|
309
|
+
|
|
310
|
+
if (uri_id == NULL) {
|
|
311
|
+
return Qnil;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
VALUE argv[] = {self, ULL2NUM(*uri_id)};
|
|
315
|
+
free_u64(uri_id);
|
|
316
|
+
return rb_class_new_instance(2, argv, cDocument);
|
|
317
|
+
}
|
|
318
|
+
|
|
275
319
|
// Graph#resolve: () -> self
|
|
276
320
|
// Runs the resolver to compute declarations and ownership
|
|
277
321
|
static VALUE rdxr_graph_resolve(VALUE self) {
|
|
@@ -281,7 +325,7 @@ static VALUE rdxr_graph_resolve(VALUE self) {
|
|
|
281
325
|
return self;
|
|
282
326
|
}
|
|
283
327
|
|
|
284
|
-
// Graph#
|
|
328
|
+
// Graph#encoding=: (String) -> void
|
|
285
329
|
// Sets the encoding used for transforming byte offsets into LSP code unit line/column positions
|
|
286
330
|
static VALUE rdxr_graph_set_encoding(VALUE self, VALUE encoding) {
|
|
287
331
|
Check_Type(encoding, T_STRING);
|
|
@@ -358,6 +402,38 @@ static VALUE rdxr_graph_resolve_require_path(VALUE self, VALUE require_path, VAL
|
|
|
358
402
|
return rb_class_new_instance(2, argv, cDocument);
|
|
359
403
|
}
|
|
360
404
|
|
|
405
|
+
// Graph#require_paths: (Array[String] load_path) -> Array[String]
|
|
406
|
+
// Returns all require paths for completion.
|
|
407
|
+
static VALUE rdxr_graph_require_paths(VALUE self, VALUE load_path) {
|
|
408
|
+
rdxi_check_array_of_strings(load_path);
|
|
409
|
+
|
|
410
|
+
void *graph;
|
|
411
|
+
TypedData_Get_Struct(self, void *, &graph_type, graph);
|
|
412
|
+
|
|
413
|
+
size_t paths_len = RARRAY_LEN(load_path);
|
|
414
|
+
char **converted_paths = rdxi_str_array_to_char(load_path, paths_len);
|
|
415
|
+
|
|
416
|
+
size_t out_count = 0;
|
|
417
|
+
const char *const *results = rdx_require_paths(graph, (const char **)converted_paths, paths_len, &out_count);
|
|
418
|
+
|
|
419
|
+
for (size_t i = 0; i < paths_len; i++) {
|
|
420
|
+
free(converted_paths[i]);
|
|
421
|
+
}
|
|
422
|
+
free(converted_paths);
|
|
423
|
+
|
|
424
|
+
if (results == NULL) {
|
|
425
|
+
return rb_ary_new();
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
VALUE array = rb_ary_new_capa((long)out_count);
|
|
429
|
+
for (size_t i = 0; i < out_count; i++) {
|
|
430
|
+
rb_ary_push(array, rb_utf8_str_new_cstr(results[i]));
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
free_c_string_array(results, out_count);
|
|
434
|
+
return array;
|
|
435
|
+
}
|
|
436
|
+
|
|
361
437
|
// Graph#diagnostics -> Array[Rubydex::Diagnostic]
|
|
362
438
|
static VALUE rdxr_graph_diagnostics(VALUE self) {
|
|
363
439
|
void *graph;
|
|
@@ -392,12 +468,11 @@ static VALUE rdxr_graph_diagnostics(VALUE self) {
|
|
|
392
468
|
}
|
|
393
469
|
|
|
394
470
|
void rdxi_initialize_graph(VALUE mRubydex) {
|
|
395
|
-
VALUE eRubydexError = rb_const_get(mRubydex, rb_intern("Error"));
|
|
396
|
-
eIndexingError = rb_define_class_under(mRubydex, "IndexingError", eRubydexError);
|
|
397
|
-
|
|
398
471
|
cGraph = rb_define_class_under(mRubydex, "Graph", rb_cObject);
|
|
399
472
|
rb_define_alloc_func(cGraph, rdxr_graph_alloc);
|
|
400
473
|
rb_define_method(cGraph, "index_all", rdxr_graph_index_all, 1);
|
|
474
|
+
rb_define_method(cGraph, "index_source", rdxr_graph_index_source, 3);
|
|
475
|
+
rb_define_method(cGraph, "delete_document", rdxr_graph_delete_document, 1);
|
|
401
476
|
rb_define_method(cGraph, "resolve", rdxr_graph_resolve, 0);
|
|
402
477
|
rb_define_method(cGraph, "resolve_constant", rdxr_graph_resolve_constant, 2);
|
|
403
478
|
rb_define_method(cGraph, "declarations", rdxr_graph_declarations, 0);
|
|
@@ -407,6 +482,7 @@ void rdxi_initialize_graph(VALUE mRubydex) {
|
|
|
407
482
|
rb_define_method(cGraph, "diagnostics", rdxr_graph_diagnostics, 0);
|
|
408
483
|
rb_define_method(cGraph, "[]", rdxr_graph_aref, 1);
|
|
409
484
|
rb_define_method(cGraph, "search", rdxr_graph_search, 1);
|
|
410
|
-
rb_define_method(cGraph, "
|
|
485
|
+
rb_define_method(cGraph, "encoding=", rdxr_graph_set_encoding, 1);
|
|
411
486
|
rb_define_method(cGraph, "resolve_require_path", rdxr_graph_resolve_require_path, 2);
|
|
487
|
+
rb_define_method(cGraph, "require_paths", rdxr_graph_require_paths, 1);
|
|
412
488
|
}
|
data/lib/rubydex/3.2/rubydex.so
CHANGED
|
Binary file
|
data/lib/rubydex/3.3/rubydex.so
CHANGED
|
Binary file
|
data/lib/rubydex/3.4/rubydex.so
CHANGED
|
Binary file
|
data/lib/rubydex/4.0/rubydex.so
CHANGED
|
Binary file
|
data/lib/rubydex/graph.rb
CHANGED
|
@@ -5,40 +5,68 @@ module Rubydex
|
|
|
5
5
|
#
|
|
6
6
|
# Note: this class is partially defined in C to integrate with the Rust backend
|
|
7
7
|
class Graph
|
|
8
|
+
IGNORED_DIRECTORIES = [
|
|
9
|
+
".bundle",
|
|
10
|
+
".git",
|
|
11
|
+
".github",
|
|
12
|
+
"node_modules",
|
|
13
|
+
"tmp",
|
|
14
|
+
].freeze
|
|
15
|
+
|
|
8
16
|
#: (?workspace_path: String) -> void
|
|
9
17
|
def initialize(workspace_path: Dir.pwd)
|
|
10
18
|
@workspace_path = workspace_path
|
|
11
19
|
end
|
|
12
20
|
|
|
13
21
|
# Index all files and dependencies of the workspace that exists in `@workspace_path`
|
|
14
|
-
#: -> String
|
|
22
|
+
#: -> Array[String]
|
|
15
23
|
def index_workspace
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
index_all(workspace_paths)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Returns all workspace paths that should be indexed, excluding directories that we don't need to descend into such
|
|
28
|
+
# as `.git`, `node_modules`. Also includes any top level Ruby files
|
|
29
|
+
#
|
|
30
|
+
#: -> Array[String]
|
|
31
|
+
def workspace_paths
|
|
32
|
+
paths = []
|
|
33
|
+
|
|
34
|
+
Dir.each_child(@workspace_path) do |entry|
|
|
35
|
+
full_path = File.join(@workspace_path, entry)
|
|
36
|
+
|
|
37
|
+
if File.directory?(full_path)
|
|
38
|
+
paths << full_path unless IGNORED_DIRECTORIES.include?(entry)
|
|
39
|
+
elsif File.extname(entry) == ".rb"
|
|
40
|
+
paths << full_path
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
add_workspace_dependency_paths(paths)
|
|
45
|
+
paths.uniq!
|
|
46
|
+
paths
|
|
23
47
|
end
|
|
24
48
|
|
|
25
49
|
private
|
|
26
50
|
|
|
27
51
|
# Gathers the paths we have to index for all workspace dependencies
|
|
28
|
-
|
|
52
|
+
#: (Array[String]) -> void
|
|
53
|
+
def add_workspace_dependency_paths(paths)
|
|
29
54
|
specs = Bundler.locked_gems&.specs
|
|
30
|
-
return
|
|
55
|
+
return unless specs
|
|
31
56
|
|
|
32
|
-
|
|
57
|
+
specs.each do |lazy_spec|
|
|
33
58
|
spec = Gem::Specification.find_by_name(lazy_spec.name)
|
|
34
|
-
spec.require_paths.
|
|
59
|
+
spec.require_paths.each do |path|
|
|
60
|
+
# For native extensions, RubyGems inserts an absolute require path pointing to
|
|
61
|
+
# `gems/some-gem-1.0.0/extensions`. Those paths don't actually include any Ruby files inside, so we can skip
|
|
62
|
+
# descending them
|
|
63
|
+
next if File.absolute_path?(path)
|
|
64
|
+
|
|
65
|
+
paths << File.join(spec.full_gem_path, path)
|
|
66
|
+
end
|
|
35
67
|
rescue Gem::MissingSpecError
|
|
36
68
|
nil
|
|
37
69
|
end
|
|
38
|
-
|
|
39
|
-
paths.flatten!
|
|
40
|
-
paths.uniq!
|
|
41
|
-
paths
|
|
42
70
|
end
|
|
43
71
|
end
|
|
44
72
|
end
|
|
Binary file
|
data/lib/rubydex/version.rb
CHANGED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
//! Visit the RBS AST and create type definitions.
|
|
2
|
+
|
|
3
|
+
use ruby_rbs::node::{self, ModuleNode, Node, TypeNameNode, Visit};
|
|
4
|
+
|
|
5
|
+
use crate::diagnostic::Rule;
|
|
6
|
+
use crate::indexing::local_graph::LocalGraph;
|
|
7
|
+
use crate::model::comment::Comment;
|
|
8
|
+
use crate::model::definitions::{Definition, DefinitionFlags, ModuleDefinition};
|
|
9
|
+
use crate::model::document::Document;
|
|
10
|
+
use crate::model::ids::{DefinitionId, NameId, UriId};
|
|
11
|
+
use crate::model::name::{Name, ParentScope};
|
|
12
|
+
use crate::offset::Offset;
|
|
13
|
+
|
|
14
|
+
pub struct RBSIndexer<'a> {
|
|
15
|
+
uri_id: UriId,
|
|
16
|
+
local_graph: LocalGraph,
|
|
17
|
+
source: &'a str,
|
|
18
|
+
nesting_stack: Vec<DefinitionId>,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
impl<'a> RBSIndexer<'a> {
|
|
22
|
+
#[must_use]
|
|
23
|
+
pub fn new(uri: String, source: &'a str) -> Self {
|
|
24
|
+
let uri_id = UriId::from(&uri);
|
|
25
|
+
let local_graph = LocalGraph::new(uri_id, Document::new(uri, source));
|
|
26
|
+
|
|
27
|
+
Self {
|
|
28
|
+
uri_id,
|
|
29
|
+
local_graph,
|
|
30
|
+
source,
|
|
31
|
+
nesting_stack: Vec::new(),
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
#[must_use]
|
|
36
|
+
pub fn local_graph(self) -> LocalGraph {
|
|
37
|
+
self.local_graph
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
pub fn index(&mut self) {
|
|
41
|
+
let Ok(signature) = node::parse(self.source.as_bytes()) else {
|
|
42
|
+
self.local_graph.add_diagnostic(
|
|
43
|
+
Rule::ParseError,
|
|
44
|
+
Offset::new(0, 0),
|
|
45
|
+
"Failed to parse RBS document".to_string(),
|
|
46
|
+
);
|
|
47
|
+
return;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
self.visit(&signature.as_node());
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
fn bytes_to_string(name: &[u8]) -> String {
|
|
54
|
+
String::from_utf8_lossy(name).into_owned()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/// Converts an RBS `TypeNameNode` into a rubydex `NameId`.
|
|
58
|
+
///
|
|
59
|
+
/// Walks the namespace path (e.g. `Foo::Bar` in `Foo::Bar::Baz`) to build
|
|
60
|
+
/// a `ParentScope` chain, then creates the final `Name` for the leaf segment.
|
|
61
|
+
fn index_type_name(&mut self, type_name: &TypeNameNode, nesting_name_id: Option<NameId>) -> NameId {
|
|
62
|
+
let namespace = type_name.namespace();
|
|
63
|
+
|
|
64
|
+
let mut parent_scope = if namespace.absolute() {
|
|
65
|
+
ParentScope::TopLevel
|
|
66
|
+
} else {
|
|
67
|
+
ParentScope::None
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
for path_node in namespace.path().iter() {
|
|
71
|
+
let Node::Symbol(symbol) = path_node else {
|
|
72
|
+
continue;
|
|
73
|
+
};
|
|
74
|
+
parent_scope = ParentScope::Some(self.intern_name(symbol.name(), parent_scope, nesting_name_id));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
self.intern_name(type_name.name().name(), parent_scope, nesting_name_id)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
fn intern_name(&mut self, name_bytes: &[u8], parent_scope: ParentScope, nesting_name_id: Option<NameId>) -> NameId {
|
|
81
|
+
let string_id = self.local_graph.intern_string(Self::bytes_to_string(name_bytes));
|
|
82
|
+
self.local_graph
|
|
83
|
+
.add_name(Name::new(string_id, parent_scope, nesting_name_id))
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
fn parent_lexical_scope_id(&self) -> Option<DefinitionId> {
|
|
87
|
+
self.nesting_stack.last().copied()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
fn add_member_to_current_lexical_scope(&mut self, owner_id: DefinitionId, member_id: DefinitionId) {
|
|
91
|
+
let owner = self
|
|
92
|
+
.local_graph
|
|
93
|
+
.get_definition_mut(owner_id)
|
|
94
|
+
.expect("owner definition should exist");
|
|
95
|
+
|
|
96
|
+
match owner {
|
|
97
|
+
Definition::Module(module) => module.add_member(member_id),
|
|
98
|
+
Definition::Class(class) => class.add_member(member_id),
|
|
99
|
+
_ => unreachable!("RBS nesting stack only contains modules/classes"),
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
impl Visit for RBSIndexer<'_> {
|
|
105
|
+
fn visit_module_node(&mut self, module_node: &ModuleNode) {
|
|
106
|
+
let lexical_nesting_id = self.parent_lexical_scope_id();
|
|
107
|
+
let nesting_name_id = lexical_nesting_id.map(|id| {
|
|
108
|
+
let owner = self
|
|
109
|
+
.local_graph
|
|
110
|
+
.definitions()
|
|
111
|
+
.get(&id)
|
|
112
|
+
.expect("owner definition should exist");
|
|
113
|
+
*owner.name_id().expect("nesting definition should have a name")
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
let type_name = module_node.name();
|
|
117
|
+
let name_id = self.index_type_name(&type_name, nesting_name_id);
|
|
118
|
+
let offset = Offset::from_rbs_location(&module_node.location());
|
|
119
|
+
let name_offset = Offset::from_rbs_location(&type_name.name().location());
|
|
120
|
+
|
|
121
|
+
let comments: Vec<_> = module_node
|
|
122
|
+
.comment()
|
|
123
|
+
.into_iter()
|
|
124
|
+
.map(|comment| {
|
|
125
|
+
let text = Self::bytes_to_string(comment.string().as_bytes());
|
|
126
|
+
Comment::new(Offset::from_rbs_location(&comment.location()), text)
|
|
127
|
+
})
|
|
128
|
+
.collect();
|
|
129
|
+
|
|
130
|
+
let definition = Definition::Module(Box::new(ModuleDefinition::new(
|
|
131
|
+
name_id,
|
|
132
|
+
self.uri_id,
|
|
133
|
+
offset,
|
|
134
|
+
name_offset,
|
|
135
|
+
comments,
|
|
136
|
+
DefinitionFlags::empty(),
|
|
137
|
+
lexical_nesting_id,
|
|
138
|
+
)));
|
|
139
|
+
|
|
140
|
+
let definition_id = self.local_graph.add_definition(definition);
|
|
141
|
+
if let Some(id) = lexical_nesting_id {
|
|
142
|
+
self.add_member_to_current_lexical_scope(id, definition_id);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
self.nesting_stack.push(definition_id);
|
|
146
|
+
|
|
147
|
+
for member in module_node.members().iter() {
|
|
148
|
+
self.visit(&member);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
self.nesting_stack.pop();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
#[cfg(test)]
|
|
156
|
+
mod tests {
|
|
157
|
+
use crate::test_utils::LocalGraphTest;
|
|
158
|
+
use crate::{
|
|
159
|
+
assert_def_name_eq, assert_def_name_offset_eq, assert_definition_at, assert_local_diagnostics_eq,
|
|
160
|
+
assert_no_local_diagnostics,
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
fn index_source(source: &str) -> LocalGraphTest {
|
|
164
|
+
LocalGraphTest::new_rbs("file:///foo.rbs", source)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
#[test]
|
|
168
|
+
fn index_source_with_errors() {
|
|
169
|
+
let context = index_source("module");
|
|
170
|
+
|
|
171
|
+
assert_local_diagnostics_eq!(&context, ["parse-error: Failed to parse RBS document (1:1-1:1)"]);
|
|
172
|
+
|
|
173
|
+
assert!(context.graph().definitions().is_empty());
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
#[test]
|
|
177
|
+
fn index_module_node() {
|
|
178
|
+
let context = index_source({
|
|
179
|
+
"
|
|
180
|
+
module Foo
|
|
181
|
+
module Bar
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
"
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
assert_no_local_diagnostics!(&context);
|
|
188
|
+
assert_eq!(context.graph().definitions().len(), 2);
|
|
189
|
+
|
|
190
|
+
assert_definition_at!(&context, "1:1-4:4", Module, |def| {
|
|
191
|
+
assert_def_name_eq!(&context, def, "Foo");
|
|
192
|
+
assert_def_name_offset_eq!(&context, def, "1:8-1:11");
|
|
193
|
+
assert_eq!(1, def.members().len());
|
|
194
|
+
assert!(def.lexical_nesting_id().is_none());
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
assert_definition_at!(&context, "2:3-3:6", Module, |def| {
|
|
198
|
+
assert_def_name_eq!(&context, def, "Bar");
|
|
199
|
+
assert_def_name_offset_eq!(&context, def, "2:10-2:13");
|
|
200
|
+
|
|
201
|
+
assert_definition_at!(&context, "1:1-4:4", Module, |parent_nesting| {
|
|
202
|
+
assert_eq!(parent_nesting.id(), def.lexical_nesting_id().unwrap());
|
|
203
|
+
assert_eq!(parent_nesting.members()[0], def.id());
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
#[test]
|
|
209
|
+
fn index_module_node_with_qualified_name() {
|
|
210
|
+
let context = index_source({
|
|
211
|
+
"
|
|
212
|
+
module Foo
|
|
213
|
+
module Bar::Baz
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
"
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
assert_no_local_diagnostics!(&context);
|
|
220
|
+
assert_eq!(context.graph().definitions().len(), 2);
|
|
221
|
+
|
|
222
|
+
assert_definition_at!(&context, "1:1-4:4", Module, |def| {
|
|
223
|
+
assert_def_name_eq!(&context, def, "Foo");
|
|
224
|
+
assert_def_name_offset_eq!(&context, def, "1:8-1:11");
|
|
225
|
+
assert_eq!(1, def.members().len());
|
|
226
|
+
assert!(def.lexical_nesting_id().is_none());
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
assert_definition_at!(&context, "2:3-3:6", Module, |def| {
|
|
230
|
+
assert_def_name_eq!(&context, def, "Bar::Baz");
|
|
231
|
+
assert_def_name_offset_eq!(&context, def, "2:15-2:18");
|
|
232
|
+
|
|
233
|
+
assert_definition_at!(&context, "1:1-4:4", Module, |parent_nesting| {
|
|
234
|
+
assert_eq!(parent_nesting.id(), def.lexical_nesting_id().unwrap());
|
|
235
|
+
assert_eq!(parent_nesting.members()[0], def.id());
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|