rubydex 0.1.0.beta7 → 0.1.0.beta8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4cee63a5c5fbdcff32ea743870b83e52958838df77567d9f6e9835a9291ba87c
4
- data.tar.gz: 7fc1e83c8121b8f0bea2c794b191c9f1fd2f1eb8c136a1fd6fbe6a288e85a5e8
3
+ metadata.gz: 9c07115229a5715e4501adce61eefbb4bfe26694f7945736829230d77e97c65a
4
+ data.tar.gz: 5e845ff2cc4617790785c6cca4f5f67a0cfb02d02d88721062b39fff75e715d1
5
5
  SHA512:
6
- metadata.gz: f3f4fbb0ed860644b68dc8c770f07b922f27a05f13789a51b00b8bd74d00091a556c43a2772a01c8a47cc60776875056bbac6d6fa1e0574a0b18cd0609b947e1
7
- data.tar.gz: df158939d505be5ebec800dfeb486f5da52de1194f0203f119cee3239195ec0c8b2fd42781a881f0cd8f9b133df97757d32697b429f7bf358d6659295c9e219b
6
+ metadata.gz: 0e5103b95c2123cae03d2f40a1e39ef642aee65b47f99416b034a0f40365bf8535a8829794df393274155bf5e548c44a412457e00fdf8701922e08a9926e3b27
7
+ data.tar.gz: 478a4a347f1c95210e69e00c2e609abd6ed1c090bae1a5bf30266ade51efc68eaaa294a3f5371d6890497b5d9a782dc0d42c1009c7440d28a1030753a6a03647
@@ -2,6 +2,7 @@
2
2
  #include "definition.h"
3
3
  #include "graph.h"
4
4
  #include "handle.h"
5
+ #include "reference.h"
5
6
  #include "rustbindings.h"
6
7
  #include "utils.h"
7
8
 
@@ -10,6 +11,7 @@ VALUE cNamespace;
10
11
  VALUE cClass;
11
12
  VALUE cModule;
12
13
  VALUE cSingletonClass;
14
+ VALUE cTodo;
13
15
  VALUE cConstant;
14
16
  VALUE cConstantAlias;
15
17
  VALUE cMethod;
@@ -26,6 +28,8 @@ VALUE rdxi_declaration_class_for_kind(CDeclarationKind kind) {
26
28
  return cModule;
27
29
  case CDeclarationKind_SingletonClass:
28
30
  return cSingletonClass;
31
+ case CDeclarationKind_Todo:
32
+ return cTodo;
29
33
  case CDeclarationKind_Constant:
30
34
  return cConstant;
31
35
  case CDeclarationKind_ConstantAlias:
@@ -291,12 +295,49 @@ static VALUE rdxr_declaration_descendants(VALUE self) {
291
295
  return self;
292
296
  }
293
297
 
298
+ // Size function for the Declaration#references enumerator
299
+ static VALUE declaration_references_size(VALUE self, VALUE _args, VALUE _eobj) {
300
+ HandleData *data;
301
+ TypedData_Get_Struct(self, HandleData, &handle_type, data);
302
+
303
+ void *graph;
304
+ TypedData_Get_Struct(data->graph_obj, void *, &graph_type, graph);
305
+
306
+ struct ReferencesIter *iter = rdx_declaration_references_iter_new(graph, data->id);
307
+ size_t len = rdx_references_iter_len(iter);
308
+ rdx_references_iter_free(iter);
309
+
310
+ return SIZET2NUM(len);
311
+ }
312
+
313
+ // Returns an enumerator for all references to this declaration
314
+ //
315
+ // Declaration#references: () -> Enumerator[Reference]
316
+ static VALUE rdxr_declaration_references(VALUE self) {
317
+ if (!rb_block_given_p()) {
318
+ return rb_enumeratorize_with_size(self, rb_str_new2("references"), 0, NULL, declaration_references_size);
319
+ }
320
+
321
+ HandleData *data;
322
+ TypedData_Get_Struct(self, HandleData, &handle_type, data);
323
+
324
+ void *graph;
325
+ TypedData_Get_Struct(data->graph_obj, void *, &graph_type, graph);
326
+
327
+ void *iter = rdx_declaration_references_iter_new(graph, data->id);
328
+ VALUE args = rb_ary_new_from_args(2, data->graph_obj, ULL2NUM((uintptr_t)iter));
329
+ rb_ensure(rdxi_references_yield, args, rdxi_references_ensure, args);
330
+
331
+ return self;
332
+ }
333
+
294
334
  void rdxi_initialize_declaration(VALUE mRubydex) {
295
335
  cDeclaration = rb_define_class_under(mRubydex, "Declaration", rb_cObject);
296
336
  cNamespace = rb_define_class_under(mRubydex, "Namespace", cDeclaration);
297
337
  cClass = rb_define_class_under(mRubydex, "Class", cNamespace);
298
338
  cModule = rb_define_class_under(mRubydex, "Module", cNamespace);
299
339
  cSingletonClass = rb_define_class_under(mRubydex, "SingletonClass", cNamespace);
340
+ cTodo = rb_define_class_under(mRubydex, "Todo", cNamespace);
300
341
  cConstant = rb_define_class_under(mRubydex, "Constant", cDeclaration);
301
342
  cConstantAlias = rb_define_class_under(mRubydex, "ConstantAlias", cDeclaration);
302
343
  cMethod = rb_define_class_under(mRubydex, "Method", cDeclaration);
@@ -309,6 +350,7 @@ void rdxi_initialize_declaration(VALUE mRubydex) {
309
350
  rb_define_method(cDeclaration, "name", rdxr_declaration_name, 0);
310
351
  rb_define_method(cDeclaration, "unqualified_name", rdxr_declaration_unqualified_name, 0);
311
352
  rb_define_method(cDeclaration, "definitions", rdxr_declaration_definitions, 0);
353
+ rb_define_method(cDeclaration, "references", rdxr_declaration_references, 0);
312
354
  rb_define_method(cDeclaration, "owner", rdxr_declaration_owner, 0);
313
355
 
314
356
  // Namespace only methods
@@ -9,6 +9,7 @@ extern VALUE cNamespace;
9
9
  extern VALUE cClass;
10
10
  extern VALUE cModule;
11
11
  extern VALUE cSingletonClass;
12
+ extern VALUE cTodo;
12
13
  extern VALUE cConstant;
13
14
  extern VALUE cConstantAlias;
14
15
  extern VALUE cMethod;
data/ext/rubydex/graph.c CHANGED
@@ -228,31 +228,6 @@ static VALUE rdxr_graph_aref(VALUE self, VALUE key) {
228
228
  return rb_class_new_instance(2, argv, decl_class);
229
229
  }
230
230
 
231
- // Body function for rb_ensure for the reference enumerators
232
- static VALUE graph_references_yield(VALUE args) {
233
- VALUE self = rb_ary_entry(args, 0);
234
- void *iter = (void *)(uintptr_t)NUM2ULL(rb_ary_entry(args, 1));
235
-
236
- uint64_t id = 0;
237
- ReferenceKind kind;
238
- while (rdx_references_iter_next(iter, &id, &kind)) {
239
- VALUE ref_class = rdxi_reference_class_for_kind(kind);
240
- VALUE argv[] = {self, ULL2NUM(id)};
241
- VALUE obj = rb_class_new_instance(2, argv, ref_class);
242
- rb_yield(obj);
243
- }
244
-
245
- return Qnil;
246
- }
247
-
248
- // Ensure function for rb_ensure for the reference enumerators to always free the iterator
249
- static VALUE graph_references_ensure(VALUE args) {
250
- void *iter = (void *)(uintptr_t)NUM2ULL(rb_ary_entry(args, 1));
251
- rdx_references_iter_free(iter);
252
-
253
- return Qnil;
254
- }
255
-
256
231
  // Size function for the constant_references enumerator
257
232
  static VALUE graph_constant_references_size(VALUE self, VALUE _args, VALUE _eobj) {
258
233
  void *graph;
@@ -278,7 +253,7 @@ static VALUE rdxr_graph_constant_references(VALUE self) {
278
253
 
279
254
  void *iter = rdx_graph_constant_references_iter_new(graph);
280
255
  VALUE args = rb_ary_new_from_args(2, self, ULL2NUM((uintptr_t)iter));
281
- rb_ensure(graph_references_yield, args, graph_references_ensure, args);
256
+ rb_ensure(rdxi_references_yield, args, rdxi_references_ensure, args);
282
257
 
283
258
  return self;
284
259
  }
@@ -308,7 +283,7 @@ static VALUE rdxr_graph_method_references(VALUE self) {
308
283
 
309
284
  void *iter = rdx_graph_method_references_iter_new(graph);
310
285
  VALUE args = rb_ary_new_from_args(2, self, ULL2NUM((uintptr_t)iter));
311
- rb_ensure(graph_references_yield, args, graph_references_ensure, args);
286
+ rb_ensure(rdxi_references_yield, args, rdxi_references_ensure, args);
312
287
 
313
288
  return self;
314
289
  }
data/ext/rubydex/utils.c CHANGED
@@ -1,5 +1,6 @@
1
1
  #include "utils.h"
2
2
  #include "declaration.h"
3
+ #include "reference.h"
3
4
  #include "rustbindings.h"
4
5
 
5
6
  // Convert a Ruby array of strings into a double char pointer so that we can pass that to Rust.
@@ -50,3 +51,26 @@ VALUE rdxi_declarations_ensure(VALUE args) {
50
51
  rdx_graph_declarations_iter_free(iter);
51
52
  return Qnil;
52
53
  }
54
+
55
+ // Yield body for iterating over references
56
+ VALUE rdxi_references_yield(VALUE args) {
57
+ VALUE graph_obj = rb_ary_entry(args, 0);
58
+ void *iter = (void *)(uintptr_t)NUM2ULL(rb_ary_entry(args, 1));
59
+
60
+ CReference cref;
61
+ while (rdx_references_iter_next(iter, &cref)) {
62
+ VALUE ref_class = rdxi_reference_class_for_kind(cref.kind);
63
+ VALUE argv[] = {graph_obj, ULL2NUM(cref.id)};
64
+ VALUE obj = rb_class_new_instance(2, argv, ref_class);
65
+ rb_yield(obj);
66
+ }
67
+
68
+ return Qnil;
69
+ }
70
+
71
+ // Ensure function for iterating over references to always free the iterator
72
+ VALUE rdxi_references_ensure(VALUE args) {
73
+ void *iter = (void *)(uintptr_t)NUM2ULL(rb_ary_entry(args, 1));
74
+ rdx_references_iter_free(iter);
75
+ return Qnil;
76
+ }
data/ext/rubydex/utils.h CHANGED
@@ -16,4 +16,10 @@ VALUE rdxi_declarations_yield(VALUE args);
16
16
  // Ensure function for iterating over declarations to always free the iterator
17
17
  VALUE rdxi_declarations_ensure(VALUE args);
18
18
 
19
+ // Yield body for iterating over references
20
+ VALUE rdxi_references_yield(VALUE args);
21
+
22
+ // Ensure function for iterating over references to always free the iterator
23
+ VALUE rdxi_references_ensure(VALUE args);
24
+
19
25
  #endif // RUBYDEX_UTILS_H
Binary file
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rubydex
4
- VERSION = "0.1.0.beta7"
4
+ VERSION = "0.1.0.beta8"
5
5
  end
data/rbi/rubydex.rbi CHANGED
@@ -1,273 +1,304 @@
1
1
  # frozen_string_literal: true
2
2
  # typed: strict
3
3
 
4
- module Rubydex
5
- class Comment
6
- sig { params(string: String, location: Location).void }
7
- def initialize(string:, location:); end
4
+ module Rubydex; end
8
5
 
9
- sig { returns(Location) }
10
- def location; end
6
+ class Rubydex::Comment
7
+ sig { params(string: String, location: Rubydex::Location).void }
8
+ def initialize(string:, location:); end
11
9
 
12
- sig { returns(String) }
13
- def string; end
14
- end
10
+ sig { returns(Rubydex::Location) }
11
+ def location; end
15
12
 
16
- class ConstantReference < Reference
17
- sig { returns(Location) }
18
- def location; end
13
+ sig { returns(String) }
14
+ def string; end
15
+ end
19
16
 
20
- sig { returns(String) }
21
- def name; end
22
- end
17
+ class Rubydex::ConstantReference < Rubydex::Reference
18
+ sig { returns(Rubydex::Location) }
19
+ def location; end
20
+
21
+ sig { returns(String) }
22
+ def name; end
23
+ end
23
24
 
24
- class Declaration
25
- sig { returns(T::Enumerable[Definition]) }
26
- def definitions; end
25
+ class Rubydex::Declaration
26
+ sig { returns(T::Enumerable[Rubydex::Definition]) }
27
+ def definitions; end
27
28
 
28
- sig { returns(String) }
29
- def name; end
29
+ sig { returns(String) }
30
+ def name; end
30
31
 
31
- sig { returns(Declaration) }
32
- def owner; end
32
+ sig { returns(Rubydex::Declaration) }
33
+ def owner; end
33
34
 
34
- sig { returns(String) }
35
- def unqualified_name; end
35
+ sig { returns(T::Enumerable[Rubydex::Reference]) }
36
+ def references; end
36
37
 
37
- class << self
38
- private
38
+ sig { returns(String) }
39
+ def unqualified_name; end
39
40
 
40
- def new(*args); end
41
- end
41
+ class << self
42
+ private
43
+
44
+ def new(*args); end
42
45
  end
46
+ end
43
47
 
44
- class GlobalVariable < Declaration; end
45
- class InstanceVariable < Declaration; end
46
- class Constant < Declaration; end
47
- class ConstantAlias < Declaration; end
48
- class ClassVariable < Declaration; end
49
- class Method < Declaration; end
48
+ class Rubydex::GlobalVariable < Rubydex::Declaration; end
49
+ class Rubydex::InstanceVariable < Rubydex::Declaration; end
50
+ class Rubydex::Constant < Rubydex::Declaration; end
51
+ class Rubydex::ConstantAlias < Rubydex::Declaration; end
52
+ class Rubydex::ClassVariable < Rubydex::Declaration; end
53
+ class Rubydex::Method < Rubydex::Declaration; end
50
54
 
51
- class Namespace < Declaration
52
- sig { returns(T::Enumerable[Namespace]) }
53
- def ancestors; end
55
+ class Rubydex::Namespace < Rubydex::Declaration
56
+ sig { returns(T::Enumerable[Rubydex::Namespace]) }
57
+ def ancestors; end
54
58
 
55
- sig { returns(T::Enumerable[Namespace]) }
56
- def descendants; end
59
+ sig { returns(T::Enumerable[Rubydex::Namespace]) }
60
+ def descendants; end
57
61
 
58
- sig { params(name: String).returns(T.nilable(Declaration)) }
59
- def member(name); end
62
+ sig { params(name: String).returns(T.nilable(Rubydex::Declaration)) }
63
+ def member(name); end
60
64
 
61
- sig { params(name: String, only_inherited: T::Boolean).returns(T.nilable(Declaration)) }
62
- def find_member(name, only_inherited: false); end
65
+ sig { params(name: String, only_inherited: T::Boolean).returns(T.nilable(Rubydex::Declaration)) }
66
+ def find_member(name, only_inherited: false); end
63
67
 
64
- sig { returns(T.nilable(SingletonClass)) }
65
- def singleton_class; end
66
- end
68
+ sig { returns(T.nilable(Rubydex::SingletonClass)) }
69
+ def singleton_class; end
70
+ end
67
71
 
68
- class Class < Namespace; end
69
- class Module < Namespace; end
70
- class SingletonClass < Namespace; end
72
+ class Rubydex::Class < Rubydex::Namespace; end
73
+ class Rubydex::Module < Rubydex::Namespace; end
74
+ class Rubydex::SingletonClass < Rubydex::Namespace; end
71
75
 
72
- class Definition
73
- sig { returns(T::Array[Comment]) }
74
- def comments; end
76
+ class Rubydex::Definition
77
+ sig { returns(T::Array[Rubydex::Comment]) }
78
+ def comments; end
75
79
 
76
- sig { returns(T::Boolean) }
77
- def deprecated?; end
80
+ sig { returns(T::Boolean) }
81
+ def deprecated?; end
78
82
 
79
- sig { returns(Location) }
80
- def location; end
83
+ sig { returns(Rubydex::Location) }
84
+ def location; end
81
85
 
82
- sig { returns(String) }
83
- def name; end
86
+ sig { returns(String) }
87
+ def name; end
84
88
 
85
- sig { returns(T.nilable(Location)) }
86
- def name_location; end
89
+ sig { returns(T.nilable(Rubydex::Location)) }
90
+ def name_location; end
87
91
 
88
- class << self
89
- private
92
+ class << self
93
+ private
90
94
 
91
- def new(*args); end
92
- end
95
+ def new(*args); end
93
96
  end
97
+ end
94
98
 
95
- class AttrAccessorDefinition < Definition; end
96
- class AttrReaderDefinition < Definition; end
97
- class AttrWriterDefinition < Definition; end
98
- class ClassDefinition < Definition; end
99
- class ClassVariableDefinition < Definition; end
100
- class ConstantAliasDefinition < Definition; end
101
- class ConstantDefinition < Definition; end
102
- class GlobalVariableAliasDefinition < Definition; end
103
- class GlobalVariableDefinition < Definition; end
104
- class InstanceVariableDefinition < Definition; end
105
- class MethodAliasDefinition < Definition; end
106
- class MethodDefinition < Definition; end
107
- class ModuleDefinition < Definition; end
108
- class SingletonClassDefinition < Definition; end
109
-
110
- class Diagnostic
111
- sig { params(rule: Symbol, message: String, location: Location).void }
112
- def initialize(rule:, message:, location:); end
113
-
114
- sig { returns(Location) }
115
- def location; end
116
-
117
- sig { returns(String) }
118
- def message; end
119
-
120
- sig { returns(Symbol) }
121
- def rule; end
122
- end
99
+ class Rubydex::AttrAccessorDefinition < Rubydex::Definition; end
100
+ class Rubydex::AttrReaderDefinition < Rubydex::Definition; end
101
+ class Rubydex::AttrWriterDefinition < Rubydex::Definition; end
102
+ class Rubydex::ClassDefinition < Rubydex::Definition; end
103
+ class Rubydex::ClassVariableDefinition < Rubydex::Definition; end
104
+ class Rubydex::ConstantAliasDefinition < Rubydex::Definition; end
105
+ class Rubydex::ConstantDefinition < Rubydex::Definition; end
106
+ class Rubydex::GlobalVariableAliasDefinition < Rubydex::Definition; end
107
+ class Rubydex::GlobalVariableDefinition < Rubydex::Definition; end
108
+ class Rubydex::InstanceVariableDefinition < Rubydex::Definition; end
109
+ class Rubydex::MethodAliasDefinition < Rubydex::Definition; end
110
+ class Rubydex::MethodDefinition < Rubydex::Definition; end
111
+ class Rubydex::ModuleDefinition < Rubydex::Definition; end
112
+ class Rubydex::SingletonClassDefinition < Rubydex::Definition; end
113
+
114
+ class Rubydex::Diagnostic
115
+ sig { params(rule: Symbol, message: String, location: Rubydex::Location).void }
116
+ def initialize(rule:, message:, location:); end
117
+
118
+ sig { returns(Rubydex::Location) }
119
+ def location; end
120
+
121
+ sig { returns(String) }
122
+ def message; end
123
+
124
+ sig { returns(Symbol) }
125
+ def rule; end
126
+ end
123
127
 
124
- class Document
125
- sig { returns(T::Enumerable[Definition]) }
126
- def definitions; end
128
+ class Rubydex::Document
129
+ sig { returns(T::Enumerable[Rubydex::Definition]) }
130
+ def definitions; end
127
131
 
128
- sig { returns(String) }
129
- def uri; end
132
+ sig { returns(String) }
133
+ def uri; end
130
134
 
131
- class << self
132
- private
135
+ class << self
136
+ private
133
137
 
134
- def new(*args); end
135
- end
138
+ def new(*args); end
136
139
  end
140
+ end
137
141
 
138
- class Error < StandardError; end
142
+ class Rubydex::Error < StandardError; end
139
143
 
140
- # The global graph representing all declarations and their relationships for the workspace
141
- #
142
- # Note: this class is partially defined in C to integrate with the Rust backend
143
- class Graph
144
- IGNORED_DIRECTORIES = T.let(T.unsafe(nil), T::Array[String])
144
+ class Rubydex::Failure
145
+ sig { params(message: String).void }
146
+ def initialize(message); end
145
147
 
146
- sig { params(workspace_path: T.nilable(String)).void }
147
- def initialize(workspace_path: nil); end
148
+ sig { returns(String) }
149
+ def message; end
150
+ end
148
151
 
149
- sig { params(fully_qualified_name: String).returns(T.nilable(Declaration)) }
150
- def [](fully_qualified_name); end
152
+ class Rubydex::IntegrityFailure < Rubydex::Failure; end
151
153
 
152
- sig { returns(T::Enumerable[ConstantReference]) }
153
- def constant_references; end
154
+ class Rubydex::Graph
155
+ IGNORED_DIRECTORIES = T.let(T.unsafe(nil), T::Array[String])
154
156
 
155
- sig { returns(T::Enumerable[Declaration]) }
156
- def declarations; end
157
+ sig { params(workspace_path: T.nilable(String)).void }
158
+ def initialize(workspace_path: nil); end
157
159
 
158
- sig { params(uri: String).returns(T.nilable(Document)) }
159
- def delete_document(uri); end
160
+ sig { params(fully_qualified_name: String).returns(T.nilable(Rubydex::Declaration)) }
161
+ def [](fully_qualified_name); end
160
162
 
161
- sig { returns(T::Array[Diagnostic]) }
162
- def diagnostics; end
163
+ sig { returns(T::Enumerable[Rubydex::ConstantReference]) }
164
+ def constant_references; end
163
165
 
164
- sig { returns(T::Enumerable[Document]) }
165
- def documents; end
166
+ sig { returns(T::Enumerable[Rubydex::Declaration]) }
167
+ def declarations; end
166
168
 
167
- sig { params(file_paths: T::Array[String]).returns(T::Array[String]) }
168
- def index_all(file_paths); end
169
+ sig { params(uri: String).returns(T.nilable(Rubydex::Document)) }
170
+ def delete_document(uri); end
169
171
 
170
- sig { params(uri: String, source: String, language_id: String).void }
171
- def index_source(uri, source, language_id); end
172
+ sig { returns(T::Array[Rubydex::Diagnostic]) }
173
+ def diagnostics; end
172
174
 
173
- # Index all files and dependencies of the workspace that exists in `@workspace_path`
174
- sig { returns(T::Array[String]) }
175
- def index_workspace; end
175
+ sig { returns(T::Enumerable[Rubydex::Document]) }
176
+ def documents; end
176
177
 
177
- sig { returns(T::Enumerable[MethodReference]) }
178
- def method_references; end
178
+ sig { params(file_paths: T::Array[String]).returns(T::Array[String]) }
179
+ def index_all(file_paths); end
179
180
 
180
- sig { params(load_paths: T::Array[String]).returns(T::Array[String]) }
181
- def require_paths(load_paths); end
181
+ sig { params(uri: String, source: String, language_id: String).void }
182
+ def index_source(uri, source, language_id); end
182
183
 
183
- sig { returns(T.self_type) }
184
- def resolve; end
184
+ # Index all files and dependencies of the workspace that exists in `@workspace_path`
185
+ sig { returns(T::Array[String]) }
186
+ def index_workspace; end
185
187
 
186
- sig { params(name: String, nesting: T::Array[String]).returns(T.nilable(Declaration)) }
187
- def resolve_constant(name, nesting); end
188
+ sig { returns(T::Enumerable[Rubydex::MethodReference]) }
189
+ def method_references; end
188
190
 
189
- sig { params(require_path: String, load_paths: T::Array[String]).returns(T.nilable(Document)) }
190
- def resolve_require_path(require_path, load_paths); end
191
+ sig { params(load_paths: T::Array[String]).returns(T::Array[String]) }
192
+ def require_paths(load_paths); end
191
193
 
192
- sig { params(query: String).returns(T::Enumerable[Declaration]) }
193
- def search(query); end
194
+ sig { returns(T.self_type) }
195
+ def resolve; end
194
196
 
195
- sig { params(encoding: String).void }
196
- def encoding=(encoding); end
197
+ sig { params(name: String, nesting: T::Array[String]).returns(T.nilable(Rubydex::Declaration)) }
198
+ def resolve_constant(name, nesting); end
197
199
 
198
- sig { returns(String) }
199
- def workspace_path; end
200
+ sig { params(require_path: String, load_paths: T::Array[String]).returns(T.nilable(Rubydex::Document)) }
201
+ def resolve_require_path(require_path, load_paths); end
200
202
 
201
- sig { params(workspace_path: String).returns(String) }
202
- def workspace_path=(workspace_path); end
203
+ sig { params(query: String).returns(T::Enumerable[Rubydex::Declaration]) }
204
+ def search(query); end
203
205
 
204
- sig { returns(T::Array[String]) }
205
- def workspace_paths; end
206
+ sig { params(encoding: String).void }
207
+ def encoding=(encoding); end
206
208
 
207
- sig { returns(T::Array[Failure]) }
208
- def check_integrity; end
209
+ sig { returns(String) }
210
+ def workspace_path; end
209
211
 
210
- private
212
+ sig { params(workspace_path: String).returns(String) }
213
+ def workspace_path=(workspace_path); end
211
214
 
212
- # Gathers the paths we have to index for all workspace dependencies
213
- sig { params(paths: T::Array[String]).void }
214
- def add_workspace_dependency_paths(paths); end
215
+ sig { returns(T::Array[String]) }
216
+ def workspace_paths; end
217
+
218
+ sig { returns(T::Array[Rubydex::Failure]) }
219
+ def check_integrity; end
220
+
221
+ private
222
+
223
+ sig { params(paths: T::Array[String]).void }
224
+ def add_core_rbs_definition_paths(paths); end
225
+
226
+ sig { params(paths: T::Array[String]).void }
227
+ def add_workspace_dependency_paths(paths); end
228
+ end
229
+
230
+ class Rubydex::DisplayLocation < Rubydex::Location
231
+ sig { returns([String, Integer, Integer, Integer, Integer]) }
232
+ def comparable_values; end
233
+
234
+ sig { returns(Rubydex::DisplayLocation) }
235
+ def to_display; end
236
+
237
+ sig { returns(String) }
238
+ def to_s; end
239
+ end
240
+
241
+ class Rubydex::Location
242
+ include ::Comparable
243
+
244
+ sig do
245
+ params(
246
+ uri: String,
247
+ start_line: Integer,
248
+ end_line: Integer,
249
+ start_column: Integer,
250
+ end_column: Integer,
251
+ ).void
215
252
  end
253
+ def initialize(uri:, start_line:, end_line:, start_column:, end_column:); end
216
254
 
217
- class Location
218
- include ::Comparable
255
+ sig { params(other: T.untyped).returns(T.nilable(Integer)) }
256
+ def <=>(other); end
219
257
 
220
- sig do
221
- params(
222
- uri: String,
223
- start_line: Integer,
224
- end_line: Integer,
225
- start_column: Integer,
226
- end_column: Integer,
227
- ).void
228
- end
229
- def initialize(uri:, start_line:, end_line:, start_column:, end_column:); end
258
+ sig { returns([String, Integer, Integer, Integer, Integer]) }
259
+ def comparable_values; end
230
260
 
231
- sig { params(other: T.untyped).returns(T.nilable(Integer)) }
232
- def <=>(other); end
261
+ sig { returns(Integer) }
262
+ def end_column; end
233
263
 
234
- sig { returns(Integer) }
235
- def end_column; end
264
+ sig { returns(Integer) }
265
+ def end_line; end
236
266
 
237
- sig { returns(Integer) }
238
- def end_line; end
267
+ sig { returns(String) }
268
+ def to_file_path; end
239
269
 
240
- sig { returns(String) }
241
- def to_file_path; end
270
+ sig { returns(Integer) }
271
+ def start_column; end
242
272
 
243
- sig { returns(Integer) }
244
- def start_column; end
273
+ sig { returns(Integer) }
274
+ def start_line; end
245
275
 
246
- sig { returns(Integer) }
247
- def start_line; end
276
+ sig { returns(Rubydex::DisplayLocation) }
277
+ def to_display; end
248
278
 
249
- sig { returns(String) }
250
- def to_s; end
279
+ sig { returns(String) }
280
+ def to_s; end
251
281
 
252
- sig { returns(String) }
253
- def uri; end
254
- end
282
+ sig { returns(String) }
283
+ def uri; end
255
284
 
256
- class MethodReference < Reference
257
- sig { returns(Location) }
258
- def location; end
285
+ class NotFileUriError < StandardError; end
286
+ end
259
287
 
260
- sig { returns(String) }
261
- def name; end
262
- end
288
+ class Rubydex::MethodReference < Rubydex::Reference
289
+ sig { returns(Rubydex::Location) }
290
+ def location; end
263
291
 
264
- class Reference
265
- class << self
266
- private
292
+ sig { returns(String) }
293
+ def name; end
294
+ end
267
295
 
268
- def new(*args); end
269
- end
270
- end
296
+ class Rubydex::Reference
297
+ class << self
298
+ private
271
299
 
272
- VERSION = T.let(T.unsafe(nil), String)
300
+ def new(*args); end
301
+ end
273
302
  end
303
+
304
+ Rubydex::VERSION = T.let(T.unsafe(nil), String)
@@ -60,6 +60,7 @@ macro_rules! all_declarations {
60
60
  Declaration::Namespace(Namespace::Class($var)) => $expr,
61
61
  Declaration::Namespace(Namespace::Module($var)) => $expr,
62
62
  Declaration::Namespace(Namespace::SingletonClass($var)) => $expr,
63
+ Declaration::Namespace(Namespace::Todo($var)) => $expr,
63
64
  Declaration::Constant($var) => $expr,
64
65
  Declaration::ConstantAlias($var) => $expr,
65
66
  Declaration::Method($var) => $expr,
@@ -76,6 +77,7 @@ macro_rules! all_namespaces {
76
77
  Namespace::Class($var) => $expr,
77
78
  Namespace::Module($var) => $expr,
78
79
  Namespace::SingletonClass($var) => $expr,
80
+ Namespace::Todo($var) => $expr,
79
81
  }
80
82
  };
81
83
  }
@@ -378,6 +380,7 @@ pub enum Namespace {
378
380
  Class(Box<ClassDeclaration>),
379
381
  SingletonClass(Box<SingletonClassDeclaration>),
380
382
  Module(Box<ModuleDeclaration>),
383
+ Todo(Box<TodoDeclaration>),
381
384
  }
382
385
  assert_mem_size!(Namespace, 16);
383
386
 
@@ -388,6 +391,7 @@ impl Namespace {
388
391
  Namespace::Class(_) => "Class",
389
392
  Namespace::SingletonClass(_) => "SingletonClass",
390
393
  Namespace::Module(_) => "Module",
394
+ Namespace::Todo(_) => "<TODO>",
391
395
  }
392
396
  }
393
397
 
@@ -504,7 +508,8 @@ namespace_declaration!(Module, ModuleDeclaration);
504
508
  assert_mem_size!(ModuleDeclaration, 224);
505
509
  namespace_declaration!(SingletonClass, SingletonClassDeclaration);
506
510
  assert_mem_size!(SingletonClassDeclaration, 224);
507
-
511
+ namespace_declaration!(Todo, TodoDeclaration);
512
+ assert_mem_size!(TodoDeclaration, 224);
508
513
  simple_declaration!(ConstantDeclaration);
509
514
  assert_mem_size!(ConstantDeclaration, 112);
510
515
  simple_declaration!(MethodDeclaration);
@@ -84,14 +84,20 @@ impl Graph {
84
84
  {
85
85
  let declaration_id = DeclarationId::from(&fully_qualified_name);
86
86
 
87
- let should_promote = self.declarations.get(&declaration_id).is_some_and(|existing| {
88
- matches!(existing, Declaration::Constant(_))
89
- && matches!(
90
- self.definitions.get(&definition_id),
91
- Some(Definition::Class(_) | Definition::Module(_) | Definition::SingletonClass(_))
92
- )
93
- && self.all_definitions_promotable(existing)
94
- });
87
+ let is_namespace_definition = matches!(
88
+ self.definitions.get(&definition_id),
89
+ Some(Definition::Class(_) | Definition::Module(_) | Definition::SingletonClass(_))
90
+ );
91
+
92
+ let should_promote = is_namespace_definition
93
+ && self
94
+ .declarations
95
+ .get(&declaration_id)
96
+ .is_some_and(|existing| match existing {
97
+ Declaration::Constant(_) => self.all_definitions_promotable(existing),
98
+ Declaration::Namespace(Namespace::Todo(_)) => true,
99
+ _ => false,
100
+ });
95
101
 
96
102
  match self.declarations.entry(declaration_id) {
97
103
  Entry::Occupied(mut occupied_entry) => {
@@ -633,6 +639,10 @@ impl Graph {
633
639
  Declaration::Namespace(Namespace::SingletonClass(it)) => {
634
640
  it.add_member(member_str_id, member_declaration_id);
635
641
  }
642
+ Declaration::Namespace(Namespace::Todo(it)) => it.add_member(member_str_id, member_declaration_id),
643
+ Declaration::Constant(_) => {
644
+ // TODO: temporary hack to avoid crashing on `Struct.new`, `Class.new` and `Module.new`
645
+ }
636
646
  _ => panic!("Tried to add member to a declaration that isn't a namespace"),
637
647
  }
638
648
  }
@@ -7,7 +7,7 @@ use crate::model::{
7
7
  declaration::{
8
8
  Ancestor, Ancestors, ClassDeclaration, ClassVariableDeclaration, ConstantAliasDeclaration, ConstantDeclaration,
9
9
  Declaration, GlobalVariableDeclaration, InstanceVariableDeclaration, MethodDeclaration, ModuleDeclaration,
10
- Namespace, SingletonClassDeclaration,
10
+ Namespace, SingletonClassDeclaration, TodoDeclaration,
11
11
  },
12
12
  definitions::{Definition, Mixin, Receiver},
13
13
  graph::{CLASS_ID, Graph, MODULE_ID, OBJECT_ID},
@@ -1057,12 +1057,60 @@ impl<'a> Resolver<'a> {
1057
1057
  fn name_owner_id(&mut self, name_id: NameId) -> Outcome {
1058
1058
  let name_ref = self.graph.names().get(&name_id).unwrap();
1059
1059
 
1060
- if let Some(parent_scope) = name_ref.parent_scope().as_ref() {
1060
+ if let Some(&parent_scope) = name_ref.parent_scope().as_ref() {
1061
1061
  // If we have `A::B`, the owner of `B` is whatever `A` resolves to.
1062
1062
  // If `A` is an alias, resolve through to get the actual namespace.
1063
- match self.resolve_constant_internal(*parent_scope) {
1063
+ // On `Retry`, we don't create a Todo: the parent may still resolve through inheritance once ancestors are
1064
+ // linearized. We only create Todos for `Unresolved` outcomes where the parent is genuinely unknown.
1065
+ match self.resolve_constant_internal(parent_scope) {
1064
1066
  Outcome::Resolved(id, linearization) => self.resolve_to_primary_namespace(id, linearization),
1065
- other => other,
1067
+ // Retry or Unresolved(Some(_)) means we might find it later through ancestor linearization
1068
+ Outcome::Retry(id) => Outcome::Retry(id),
1069
+ Outcome::Unresolved(Some(id)) => Outcome::Unresolved(Some(id)),
1070
+ // Only create a Todo when genuinely unresolvable (no pending linearizations)
1071
+ Outcome::Unresolved(None) => {
1072
+ let parent_name = self.graph.names().get(&parent_scope).unwrap();
1073
+ let parent_str_id = *parent_name.str();
1074
+ let parent_has_explicit_prefix = parent_name.parent_scope().as_ref().is_some();
1075
+ // NLL: borrow of parent_name ends here
1076
+
1077
+ // For bare names (no explicit `::` prefix), always use OBJECT_ID as the owner.
1078
+ // Using nesting here would create "Nesting::Bar" instead of "Bar" for a bare `Bar`
1079
+ // reference, which is incorrect: if `Bar` can't be found anywhere, the placeholder
1080
+ // should live at the top level so it can be promoted when `module Bar` appears later.
1081
+ let parent_owner_id = if parent_has_explicit_prefix {
1082
+ match self.name_owner_id(parent_scope) {
1083
+ Outcome::Resolved(id, _) => id,
1084
+ _ => *OBJECT_ID,
1085
+ }
1086
+ } else {
1087
+ *OBJECT_ID
1088
+ };
1089
+
1090
+ let fully_qualified_name = if parent_owner_id == *OBJECT_ID {
1091
+ self.graph.strings().get(&parent_str_id).unwrap().to_string()
1092
+ } else {
1093
+ format!(
1094
+ "{}::{}",
1095
+ self.graph.declarations().get(&parent_owner_id).unwrap().name(),
1096
+ self.graph.strings().get(&parent_str_id).unwrap().as_str()
1097
+ )
1098
+ };
1099
+
1100
+ let declaration_id = DeclarationId::from(&fully_qualified_name);
1101
+
1102
+ if let std::collections::hash_map::Entry::Vacant(e) =
1103
+ self.graph.declarations_mut().entry(declaration_id)
1104
+ {
1105
+ e.insert(Declaration::Namespace(Namespace::Todo(Box::new(TodoDeclaration::new(
1106
+ fully_qualified_name,
1107
+ parent_owner_id,
1108
+ )))));
1109
+ self.graph.add_member(&parent_owner_id, declaration_id, parent_str_id);
1110
+ }
1111
+
1112
+ Outcome::Resolved(declaration_id, None)
1113
+ }
1066
1114
  }
1067
1115
  } else if let Some(nesting_id) = name_ref.nesting()
1068
1116
  && !name_ref.parent_scope().is_top_level()
@@ -1916,9 +1964,12 @@ mod tests {
1916
1964
 
1917
1965
  assert_no_diagnostics!(&context);
1918
1966
 
1919
- assert_declaration_does_not_exist!(context, "Foo");
1920
- assert_declaration_does_not_exist!(context, "Foo::Bar");
1921
- assert_declaration_does_not_exist!(context, "Foo::Bar::Baz");
1967
+ assert_declaration_kind_eq!(context, "Foo", "<TODO>");
1968
+
1969
+ assert_members_eq!(context, "Object", vec!["Foo"]);
1970
+ assert_members_eq!(context, "Foo", vec!["Bar"]);
1971
+ assert_members_eq!(context, "Foo::Bar", vec!["Baz"]);
1972
+ assert_no_members!(context, "Foo::Bar::Baz");
1922
1973
  }
1923
1974
 
1924
1975
  #[test]
@@ -5194,6 +5245,9 @@ mod tests {
5194
5245
  def initialize
5195
5246
  @instance_ivar = 456
5196
5247
  end
5248
+
5249
+ def bar; end
5250
+ alias new_bar bar
5197
5251
  end
5198
5252
  "
5199
5253
  });
@@ -5206,6 +5260,7 @@ mod tests {
5206
5260
  assert_declaration_exists!(context, "Foo#some_attr()");
5207
5261
  assert_declaration_exists!(context, "Foo::<Foo>#class_method()");
5208
5262
  assert_declaration_exists!(context, "Foo#initialize()");
5263
+ assert_declaration_exists!(context, "Foo#new_bar()");
5209
5264
  }
5210
5265
 
5211
5266
  #[test]
@@ -5261,4 +5316,151 @@ mod tests {
5261
5316
  assert_declaration_does_not_exist!(context, "Foo::<Foo>");
5262
5317
  assert_declaration_does_not_exist!(context, "Foo::<Foo>#bar()");
5263
5318
  }
5319
+
5320
+ #[test]
5321
+ fn resolve_missing_declaration_to_todo() {
5322
+ let mut context = GraphTest::new();
5323
+ context.index_uri("file:///foo.rb", {
5324
+ r"
5325
+ class Foo::Bar
5326
+ include Foo::Baz
5327
+
5328
+ def bar; end
5329
+ end
5330
+
5331
+ module Foo::Baz
5332
+ def baz; end
5333
+ end
5334
+ "
5335
+ });
5336
+ context.resolve();
5337
+
5338
+ assert_no_diagnostics!(&context);
5339
+
5340
+ assert_declaration_kind_eq!(context, "Foo", "<TODO>");
5341
+
5342
+ assert_members_eq!(context, "Object", vec!["Foo"]);
5343
+ assert_members_eq!(context, "Foo", vec!["Bar", "Baz"]);
5344
+ assert_members_eq!(context, "Foo::Bar", vec!["bar()"]);
5345
+ assert_members_eq!(context, "Foo::Baz", vec!["baz()"]);
5346
+ }
5347
+
5348
+ #[test]
5349
+ fn todo_declaration_promoted_to_real_namespace() {
5350
+ let mut context = GraphTest::new();
5351
+ context.index_uri("file:///foo.rb", {
5352
+ r"
5353
+ class Foo::Bar
5354
+ def bar; end
5355
+ end
5356
+
5357
+ class Foo
5358
+ def foo; end
5359
+ end
5360
+ "
5361
+ });
5362
+ context.resolve();
5363
+
5364
+ assert_no_diagnostics!(&context);
5365
+
5366
+ // Foo was initially created as a Todo (from class Foo::Bar), then promoted to Class
5367
+ assert_declaration_kind_eq!(context, "Foo", "Class");
5368
+
5369
+ assert_members_eq!(context, "Object", vec!["Foo"]);
5370
+ assert_members_eq!(context, "Foo", vec!["Bar", "foo()"]);
5371
+ assert_members_eq!(context, "Foo::Bar", vec!["bar()"]);
5372
+ }
5373
+
5374
+ #[test]
5375
+ fn todo_declaration_promoted_to_real_namespace_incrementally() {
5376
+ let mut context = GraphTest::new();
5377
+ context.index_uri("file:///bar.rb", {
5378
+ r"
5379
+ class Foo::Bar
5380
+ def bar; end
5381
+ end
5382
+ "
5383
+ });
5384
+ context.resolve();
5385
+
5386
+ assert_no_diagnostics!(&context);
5387
+ assert_declaration_kind_eq!(context, "Foo", "<TODO>");
5388
+
5389
+ context.index_uri("file:///foo.rb", {
5390
+ r"
5391
+ class Foo
5392
+ def foo; end
5393
+ end
5394
+ "
5395
+ });
5396
+ context.resolve();
5397
+
5398
+ assert_no_diagnostics!(&context);
5399
+
5400
+ // Foo was promoted from Todo to Class after the second resolution
5401
+ assert_declaration_kind_eq!(context, "Foo", "Class");
5402
+
5403
+ assert_members_eq!(context, "Object", vec!["Foo"]);
5404
+ assert_members_eq!(context, "Foo", vec!["Bar", "foo()"]);
5405
+ assert_members_eq!(context, "Foo::Bar", vec!["bar()"]);
5406
+ }
5407
+
5408
+ #[test]
5409
+ fn qualified_name_inside_nesting_resolves_to_top_level() {
5410
+ let mut context = GraphTest::new();
5411
+ context.index_uri("file:///foo.rb", {
5412
+ r"
5413
+ module Foo
5414
+ class Bar::Baz
5415
+ def qux; end
5416
+ end
5417
+ end
5418
+
5419
+ module Bar
5420
+ end
5421
+ "
5422
+ });
5423
+ context.resolve();
5424
+
5425
+ assert_no_diagnostics!(&context);
5426
+ assert_declaration_kind_eq!(context, "Bar", "Module");
5427
+ assert_members_eq!(context, "Bar", vec!["Baz"]);
5428
+ assert_declaration_exists!(context, "Bar::Baz");
5429
+ assert_members_eq!(context, "Bar::Baz", vec!["qux()"]);
5430
+ assert_declaration_does_not_exist!(context, "Foo::Bar");
5431
+ }
5432
+
5433
+ #[test]
5434
+ fn qualified_name_inside_nesting_resolves_when_discovered_incrementally() {
5435
+ let mut context = GraphTest::new();
5436
+ context.index_uri("file:///baz.rb", {
5437
+ r"
5438
+ module Foo
5439
+ class Bar::Baz
5440
+ def qux; end
5441
+ end
5442
+ end
5443
+ "
5444
+ });
5445
+ context.resolve();
5446
+
5447
+ // Bar is unknown — a Todo is created at the top level, not "Foo::Bar"
5448
+ assert_declaration_kind_eq!(context, "Bar", "<TODO>");
5449
+ assert_declaration_does_not_exist!(context, "Foo::Bar");
5450
+
5451
+ context.index_uri("file:///bar.rb", {
5452
+ r"
5453
+ module Bar
5454
+ end
5455
+ "
5456
+ });
5457
+ context.resolve();
5458
+
5459
+ // After discovering top-level Bar, the Todo should be promoted and Baz re-homed.
5460
+ assert_no_diagnostics!(&context);
5461
+ assert_declaration_kind_eq!(context, "Bar", "Module");
5462
+ assert_members_eq!(context, "Bar", vec!["Baz"]);
5463
+ assert_members_eq!(context, "Bar::Baz", vec!["qux()"]);
5464
+ assert_declaration_does_not_exist!(context, "Foo::Bar");
5465
+ }
5264
5466
  }
@@ -7,6 +7,7 @@ use std::ptr;
7
7
 
8
8
  use crate::definition_api::{DefinitionsIter, rdx_definitions_iter_new_from_ids};
9
9
  use crate::graph_api::{GraphPointer, with_graph};
10
+ use crate::reference_api::{CReference, ReferenceKind, ReferencesIter};
10
11
  use crate::utils;
11
12
  use rubydex::model::ids::{DeclarationId, StringId};
12
13
 
@@ -22,6 +23,7 @@ pub enum CDeclarationKind {
22
23
  GlobalVariable = 6,
23
24
  InstanceVariable = 7,
24
25
  ClassVariable = 8,
26
+ Todo = 9,
25
27
  }
26
28
 
27
29
  #[repr(C)]
@@ -51,6 +53,7 @@ impl CDeclaration {
51
53
  Declaration::Namespace(Namespace::Class(_)) => CDeclarationKind::Class,
52
54
  Declaration::Namespace(Namespace::Module(_)) => CDeclarationKind::Module,
53
55
  Declaration::Namespace(Namespace::SingletonClass(_)) => CDeclarationKind::SingletonClass,
56
+ Declaration::Namespace(Namespace::Todo(_)) => CDeclarationKind::Todo,
54
57
  Declaration::Constant(_) => CDeclarationKind::Constant,
55
58
  Declaration::ConstantAlias(_) => CDeclarationKind::ConstantAlias,
56
59
  Declaration::Method(_) => CDeclarationKind::Method,
@@ -194,13 +197,15 @@ pub unsafe extern "C" fn rdx_declaration_find_member(
194
197
  .iter()
195
198
  .find_map(|ancestor| match ancestor {
196
199
  Ancestor::Complete(ancestor_id) => {
197
- if *ancestor_id == id {
198
- found_main_namespace = true;
199
- return None;
200
- }
201
-
202
- if only_inherited && !found_main_namespace {
203
- return None;
200
+ // When only_inherited, skip self and prepended modules
201
+ if only_inherited {
202
+ let is_self = *ancestor_id == id;
203
+ if is_self {
204
+ found_main_namespace = true;
205
+ }
206
+ if is_self || !found_main_namespace {
207
+ return None;
208
+ }
204
209
  }
205
210
 
206
211
  let ancestor_decl = graph.declarations().get(ancestor_id).unwrap().as_namespace().unwrap();
@@ -397,3 +402,40 @@ pub unsafe extern "C" fn rdx_declaration_descendants(pointer: GraphPointer, decl
397
402
 
398
403
  Box::into_raw(Box::new(DeclarationsIter::new(declarations.into_boxed_slice())))
399
404
  }
405
+
406
+ /// Creates a new iterator over references for a given declaration by snapshotting the current set of IDs.
407
+ ///
408
+ /// # Safety
409
+ ///
410
+ /// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
411
+ /// - The returned pointer must be freed with `rdx_references_iter_free`.
412
+ #[unsafe(no_mangle)]
413
+ pub unsafe extern "C" fn rdx_declaration_references_iter_new(
414
+ pointer: GraphPointer,
415
+ decl_id: u64,
416
+ ) -> *mut ReferencesIter {
417
+ with_graph(pointer, |graph| {
418
+ let decl_id = DeclarationId::new(decl_id);
419
+
420
+ let Some(decl) = graph.declarations().get(&decl_id) else {
421
+ return ReferencesIter::new(Vec::new().into_boxed_slice());
422
+ };
423
+
424
+ let kind = match decl {
425
+ Declaration::Namespace(_) | Declaration::Constant(_) | Declaration::ConstantAlias(_) => {
426
+ ReferenceKind::Constant
427
+ }
428
+ Declaration::Method(_) => ReferenceKind::Method,
429
+ Declaration::GlobalVariable(_) | Declaration::InstanceVariable(_) | Declaration::ClassVariable(_) => {
430
+ return ReferencesIter::new(Vec::new().into_boxed_slice());
431
+ }
432
+ };
433
+
434
+ let entries: Vec<_> = decl
435
+ .references()
436
+ .iter()
437
+ .map(|ref_id| CReference::new(**ref_id, kind))
438
+ .collect();
439
+ ReferencesIter::new(entries.into_boxed_slice())
440
+ })
441
+ }
@@ -2,7 +2,7 @@
2
2
 
3
3
  use crate::declaration_api::CDeclaration;
4
4
  use crate::declaration_api::DeclarationsIter;
5
- use crate::reference_api::{ReferenceKind, ReferencesIter};
5
+ use crate::reference_api::{CReference, ReferenceKind, ReferencesIter};
6
6
  use crate::{name_api, utils};
7
7
  use libc::{c_char, c_void};
8
8
  use rubydex::indexing::LanguageId;
@@ -452,7 +452,7 @@ pub unsafe extern "C" fn rdx_graph_constant_references_iter_new(pointer: GraphPo
452
452
  let refs: Vec<_> = graph
453
453
  .constant_references()
454
454
  .keys()
455
- .map(|id| (**id, ReferenceKind::Constant))
455
+ .map(|id| CReference::new(**id, ReferenceKind::Constant))
456
456
  .collect();
457
457
 
458
458
  ReferencesIter::new(refs.into_boxed_slice())
@@ -469,7 +469,7 @@ pub unsafe extern "C" fn rdx_graph_method_references_iter_new(pointer: GraphPoin
469
469
  let refs: Vec<_> = graph
470
470
  .method_references()
471
471
  .keys()
472
- .map(|id| (**id, ReferenceKind::Method))
472
+ .map(|id| CReference::new(**id, ReferenceKind::Method))
473
473
  .collect();
474
474
 
475
475
  ReferencesIter::new(refs.into_boxed_slice())
@@ -15,16 +15,29 @@ pub enum ReferenceKind {
15
15
  Method = 1,
16
16
  }
17
17
 
18
- /// Shared iterator over reference (id, kind) pairs
18
+ #[repr(C)]
19
+ #[derive(Debug, Clone, Copy)]
20
+ pub struct CReference {
21
+ id: u64,
22
+ kind: ReferenceKind,
23
+ }
24
+
25
+ impl CReference {
26
+ #[must_use]
27
+ pub fn new(id: u64, kind: ReferenceKind) -> Self {
28
+ Self { id, kind }
29
+ }
30
+ }
31
+
19
32
  #[derive(Debug)]
20
33
  pub struct ReferencesIter {
21
- pub entries: Box<[(u64, ReferenceKind)]>,
34
+ pub entries: Box<[CReference]>,
22
35
  pub index: usize,
23
36
  }
24
37
 
25
38
  impl ReferencesIter {
26
39
  #[must_use]
27
- pub fn new(entries: Box<[(u64, ReferenceKind)]>) -> *mut ReferencesIter {
40
+ pub fn new(entries: Box<[CReference]>) -> *mut ReferencesIter {
28
41
  Box::into_raw(Box::new(ReferencesIter { entries, index: 0 }))
29
42
  }
30
43
  }
@@ -41,23 +54,15 @@ pub unsafe extern "C" fn rdx_references_iter_len(iter: *const ReferencesIter) ->
41
54
  unsafe { (&*iter).entries.len() }
42
55
  }
43
56
 
44
- /// Advances the iterator and writes the next entry into the provided out params.
57
+ /// Advances the iterator and writes the next entry into `out_ref`.
45
58
  /// Returns `true` if an entry was written, or `false` if the iterator is exhausted or inputs are invalid.
46
59
  ///
47
60
  /// # Safety
48
61
  /// - `iter` must be a valid pointer previously returned by `ReferencesIter::new`.
49
- /// - `out_id` and `out_kind` must be valid, writable pointers.
50
- ///
51
- /// # Panics
52
- /// - If the iterator is exhausted or inputs are invalid.
53
- /// - If the name, URI, start, or end pointers are invalid.
62
+ /// - `out_ref` must be a valid, writable pointer.
54
63
  #[unsafe(no_mangle)]
55
- pub unsafe extern "C" fn rdx_references_iter_next(
56
- iter: *mut ReferencesIter,
57
- out_id: *mut u64,
58
- out_kind: *mut ReferenceKind,
59
- ) -> bool {
60
- if iter.is_null() || out_id.is_null() || out_kind.is_null() {
64
+ pub unsafe extern "C" fn rdx_references_iter_next(iter: *mut ReferencesIter, out_ref: *mut CReference) -> bool {
65
+ if iter.is_null() || out_ref.is_null() {
61
66
  return false;
62
67
  }
63
68
 
@@ -66,11 +71,10 @@ pub unsafe extern "C" fn rdx_references_iter_next(
66
71
  return false;
67
72
  }
68
73
 
69
- let (id, kind) = it.entries[it.index];
74
+ let entry = it.entries[it.index];
70
75
  it.index += 1;
71
76
  unsafe {
72
- *out_id = id;
73
- *out_kind = kind;
77
+ *out_ref = entry;
74
78
  }
75
79
  true
76
80
  }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubydex
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.beta7
4
+ version: 0.1.0.beta8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-03-04 00:00:00.000000000 Z
11
+ date: 2026-03-05 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A high performance static analysis suite for Ruby, built in Rust with
14
14
  Ruby APIs
@@ -48,7 +48,7 @@ files:
48
48
  - lib/rubydex/diagnostic.rb
49
49
  - lib/rubydex/failures.rb
50
50
  - lib/rubydex/graph.rb
51
- - lib/rubydex/librubydex_sys.dylib
51
+ - lib/rubydex/librubydex_sys.so
52
52
  - lib/rubydex/location.rb
53
53
  - lib/rubydex/version.rb
54
54
  - rbi/rubydex.rbi
@@ -125,7 +125,7 @@ metadata:
125
125
  homepage_uri: https://github.com/Shopify/rubydex
126
126
  source_code_uri: https://github.com/Shopify/rubydex
127
127
  changelog_uri: https://github.com/Shopify/rubydex/releases
128
- post_install_message:
128
+ post_install_message:
129
129
  rdoc_options: []
130
130
  require_paths:
131
131
  - lib
@@ -141,7 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
141
141
  version: 3.3.11
142
142
  requirements: []
143
143
  rubygems_version: 3.4.19
144
- signing_key:
144
+ signing_key:
145
145
  specification_version: 4
146
146
  summary: A high performance static analysis suite for Ruby
147
147
  test_files: []
Binary file