rubydex 0.1.0.beta14 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 55c47ced758d8aff486a445a0c15a1ffe6525b5d2ee59fea8d38be40bc791207
4
- data.tar.gz: 9e73f9244f197ed1b062f475ddc077b25f1dd0a5d8c29de430c7b256b20c380e
3
+ metadata.gz: 8b492c7be7e3fdd85ffa12dc17e7bdb19a77e469dbfa2a69cdb175a4c0b0cb84
4
+ data.tar.gz: e0b0f1f47e15a93f99e1e634ab76aee01aa6c4d78110a95344817fa4276b769a
5
5
  SHA512:
6
- metadata.gz: 50444ddd1ca5576961316b2c8db705a872eaf658366587d11f9cc2860b2f97f517257ffbcc51bf9fbfa0036e436b5fe338479431c90e285ad155048616f77897
7
- data.tar.gz: 856b302c492ccc0ba74a4ea1dc5c2fc993ba43a47a0f635ee005af764ae39c1be61a50fcc79f8b151c1b20e77a7e0b719115fe3427fed880ed0f0cbdd47fed45
6
+ metadata.gz: 31e6325b46717f297adacc1ceb08d5700f6f189976358677858710150073e1f016d6a739bc88cb936d777c1bb318e48f96d0cba9f3f6287697d33e0d5c679bc4
7
+ data.tar.gz: c9bc693cdb897e65def20958848762997cc3f6cb1d7b6d42e5b58f7620495da4bb20bf181391cc7d42cce9f5bf7d9106060019874f224214c22b9a6a2a15fec1
data/exe/rdx CHANGED
@@ -2,11 +2,12 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
5
+
5
6
  require "optparse"
6
7
 
7
- OptionParser.new do |parser|
8
- parser.banner = "Usage: [path1, path2]"
8
+ options = {}
9
9
 
10
+ OptionParser.new do |parser|
10
11
  parser.on("--version", "Print the gem's version") do
11
12
  require "rubydex/version"
12
13
  puts "v#{Rubydex::VERSION}"
@@ -17,31 +18,34 @@ OptionParser.new do |parser|
17
18
  puts parser
18
19
  exit
19
20
  end
21
+
22
+ parser.on("-i", "--interactive", "Open an interactive session with a populated graph for the current workspace") do
23
+ options[:interactive] = true
24
+ end
20
25
  end.parse!
21
26
 
22
27
  require "rubydex"
23
28
 
24
- workspace_path = ARGV.first
25
-
26
- # If a workspace path is passed, we need to ensure that Bundler is setup in that context so that we find the
27
- # dependencies for that project
28
- if workspace_path
29
- gemfile_path = File.join(workspace_path, "Gemfile")
30
-
31
- if File.exist?(gemfile_path)
32
- ENV["BUNDLE_GEMFILE"] = gemfile_path
33
-
34
- begin
35
- Bundler.setup
36
- rescue Bundler::BundlerError => e
37
- $stderr.puts(<<~MESSAGE)
38
- Bundle setup failed for #{gemfile_path}. Indexing of dependencies may be partial
39
- Error:
40
- #{e.message}
41
- MESSAGE
42
- end
43
- end
29
+ def __with_timer(message, &block)
30
+ print(message)
31
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
32
+ block.call
33
+ duration = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) - start
34
+ puts " finished in #{duration.round(2)}ms"
44
35
  end
45
36
 
46
- graph = Rubydex::Graph.new(workspace_path: workspace_path)
47
- graph.index_workspace
37
+ graph = Rubydex::Graph.new
38
+ __with_timer("Indexing workspace...") { graph.index_workspace }
39
+ __with_timer("Resolving graph...") { graph.resolve }
40
+
41
+ if options[:interactive]
42
+ begin
43
+ require "irb"
44
+ IRB.setup(nil)
45
+ IRB.conf[:IRB_NAME] = "rubydex"
46
+ workspace = IRB::WorkSpace.new(binding)
47
+ IRB::Irb.new(workspace).run(IRB.conf)
48
+ rescue LoadError
49
+ abort("Interactive mode requires `irb` to be in the bundle")
50
+ end
51
+ end
@@ -407,6 +407,38 @@ static VALUE rdxr_variable_declaration_references(VALUE self) {
407
407
  return rb_ary_new();
408
408
  }
409
409
 
410
+ static VALUE rdxi_visibility_to_symbol(CVisibility visibility) {
411
+ switch (visibility) {
412
+ case CVisibility_Public:
413
+ return ID2SYM(rb_intern("public"));
414
+ case CVisibility_Protected:
415
+ return ID2SYM(rb_intern("protected"));
416
+ case CVisibility_Private:
417
+ return ID2SYM(rb_intern("private"));
418
+ default:
419
+ rb_raise(rb_eRuntimeError, "Unknown CVisibility: %d", visibility);
420
+ }
421
+ }
422
+
423
+ // Declaration#visibility -> Symbol
424
+ static VALUE rdxr_declaration_visibility(VALUE self) {
425
+ HandleData *data;
426
+ TypedData_Get_Struct(self, HandleData, &handle_type, data);
427
+
428
+ void *graph;
429
+ TypedData_Get_Struct(data->graph_obj, void *, &graph_type, graph);
430
+
431
+ const CVisibility *visibility = rdx_graph_visibility(graph, data->id);
432
+ if (visibility == NULL) {
433
+ rb_raise(rb_eRuntimeError, "declaration has no visibility");
434
+ }
435
+
436
+ VALUE symbol = rdxi_visibility_to_symbol(*visibility);
437
+ free_c_visibility(visibility);
438
+
439
+ return symbol;
440
+ }
441
+
410
442
  // ConstantAlias#target -> Declaration?
411
443
  // Returns the first resolved target declaration for this constant alias, or nil if none of its definitions resolved to
412
444
  // a target
@@ -459,13 +491,19 @@ void rdxi_initialize_declaration(VALUE mRubydex) {
459
491
  rb_define_method(cNamespace, "descendants", rdxr_declaration_descendants, 0);
460
492
  rb_define_method(cNamespace, "members", rdxr_declaration_members, 0);
461
493
 
494
+ rb_define_method(cClass, "visibility", rdxr_declaration_visibility, 0);
495
+ rb_define_method(cModule, "visibility", rdxr_declaration_visibility, 0);
496
+
462
497
  // Constant and ConstantAlias have constant references
463
498
  rb_define_method(cConstant, "references", rdxr_constant_declaration_references, 0);
499
+ rb_define_method(cConstant, "visibility", rdxr_declaration_visibility, 0);
464
500
  rb_define_method(cConstantAlias, "references", rdxr_constant_declaration_references, 0);
465
501
  rb_define_method(cConstantAlias, "target", rdxr_constant_alias_target, 0);
502
+ rb_define_method(cConstantAlias, "visibility", rdxr_declaration_visibility, 0);
466
503
 
467
504
  // Method has method references
468
505
  rb_define_method(cMethod, "references", rdxr_method_declaration_references, 0);
506
+ rb_define_method(cMethod, "visibility", rdxr_declaration_visibility, 0);
469
507
 
470
508
  // Variable declarations don't yet support references
471
509
  rb_define_method(cGlobalVariable, "references", rdxr_variable_declaration_references, 0);
@@ -141,10 +141,12 @@ end
141
141
 
142
142
  File.write("Makefile", new_makefile)
143
143
 
144
- begin
145
- require "extconf_compile_commands_json"
144
+ if developing_rubydex
145
+ begin
146
+ require "extconf_compile_commands_json"
146
147
 
147
- ExtconfCompileCommandsJson.generate!
148
- ExtconfCompileCommandsJson.symlink!
149
- rescue LoadError # rubocop:disable Lint/SuppressedException
148
+ ExtconfCompileCommandsJson.generate!
149
+ ExtconfCompileCommandsJson.symlink!
150
+ rescue LoadError # rubocop:disable Lint/SuppressedException
151
+ end
150
152
  end
data/ext/rubydex/graph.c CHANGED
@@ -13,6 +13,31 @@ static VALUE mRubydex;
13
13
  static VALUE cKeyword;
14
14
  static VALUE cKeywordParameter;
15
15
 
16
+ // Interned once in `rdxi_initialize_graph` to avoid repeated symbol-table lookups on hot completion paths.
17
+ static ID id_self_receiver;
18
+
19
+ // Extracts the optional `self_receiver:` kwarg from `opts`. Returns NULL when the kwarg is
20
+ // absent or nil; raises ArgumentError when the value is the wrong type or empty.
21
+ static const char *extract_self_receiver(VALUE opts) {
22
+ if (NIL_P(opts)) {
23
+ return NULL;
24
+ }
25
+
26
+ VALUE kwarg_val;
27
+ rb_get_kwargs(opts, &id_self_receiver, 0, 1, &kwarg_val);
28
+
29
+ if (kwarg_val == Qundef || NIL_P(kwarg_val)) {
30
+ return NULL;
31
+ }
32
+
33
+ Check_Type(kwarg_val, T_STRING);
34
+ if (RSTRING_LEN(kwarg_val) == 0) {
35
+ rb_raise(rb_eArgError, "self_receiver cannot be empty");
36
+ }
37
+
38
+ return StringValueCStr(kwarg_val);
39
+ }
40
+
16
41
  // Free function for the custom Graph allocator. We always have to call into Rust to free data allocated by it
17
42
  static void graph_free(void *ptr) {
18
43
  if (ptr) {
@@ -303,6 +328,24 @@ static VALUE rdxr_graph_method_references(VALUE self) {
303
328
  return self;
304
329
  }
305
330
 
331
+ // Graph#document: (String uri) -> Document?
332
+ // Returns the Document for the given URI, or nil if it doesn't exist.
333
+ static VALUE rdxr_graph_document(VALUE self, VALUE uri) {
334
+ Check_Type(uri, T_STRING);
335
+
336
+ void *graph;
337
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
338
+ const uint64_t *uri_id = rdx_graph_get_document(graph, StringValueCStr(uri));
339
+
340
+ if (uri_id == NULL) {
341
+ return Qnil;
342
+ }
343
+
344
+ VALUE argv[] = {self, ULL2NUM(*uri_id)};
345
+ free_u64(uri_id);
346
+ return rb_class_new_instance(2, argv, cDocument);
347
+ }
348
+
306
349
  // Graph#delete_document: (String uri) -> Document?
307
350
  // Deletes a document and all of its definitions from the graph.
308
351
  // Returns the removed Document or nil if it doesn't exist.
@@ -546,12 +589,18 @@ static VALUE completion_result_to_ruby_array(struct CompletionResult result, VAL
546
589
  return ruby_array;
547
590
  }
548
591
 
549
- // Graph#complete_expression: (Array[String] nesting) -> Array[Declaration | Keyword]
592
+ // Graph#complete_expression: (Array[String] nesting, self_receiver: nil) -> Array[Declaration | Keyword]
550
593
  // Returns completion candidates for an expression context.
551
- // The nesting array represents the lexical scope stack
552
- static VALUE rdxr_graph_complete_expression(VALUE self, VALUE nesting) {
594
+ // The nesting array represents the lexical scope stack. The optional self_receiver keyword argument
595
+ // overrides the self-type (e.g., "Foo::<Foo>" for `def Foo.bar`); when nil, self is derived from
596
+ // the innermost nesting element.
597
+ static VALUE rdxr_graph_complete_expression(int argc, VALUE *argv, VALUE self) {
598
+ VALUE nesting, opts;
599
+ rb_scan_args(argc, argv, "1:", &nesting, &opts);
553
600
  rdxi_check_array_of_strings(nesting);
554
601
 
602
+ const char *self_receiver = extract_self_receiver(opts);
603
+
555
604
  void *graph;
556
605
  TypedData_Get_Struct(self, void *, &graph_type, graph);
557
606
 
@@ -559,42 +608,62 @@ static VALUE rdxr_graph_complete_expression(VALUE self, VALUE nesting) {
559
608
  char **converted_nesting = rdxi_str_array_to_char(nesting, nesting_count);
560
609
 
561
610
  struct CompletionResult result =
562
- rdx_graph_complete_expression(graph, (const char *const *)converted_nesting, nesting_count);
611
+ rdx_graph_complete_expression(graph, (const char *const *)converted_nesting, nesting_count, self_receiver);
563
612
 
564
613
  rdxi_free_str_array(converted_nesting, nesting_count);
565
614
  return completion_result_to_ruby_array(result, self);
566
615
  }
567
616
 
568
- // Graph#complete_namespace_access: (String name) -> Array[Declaration]
617
+ // Graph#complete_namespace_access: (String name, self_receiver: nil) -> Array[Declaration]
569
618
  // Returns completion candidates after a namespace access operator (e.g., `Foo::`).
570
- static VALUE rdxr_graph_complete_namespace_access(VALUE self, VALUE name) {
619
+ // The optional self_receiver kwarg is the caller's runtime self type, used to filter
620
+ // visibility-restricted singleton methods (e.g., `private_class_method`).
621
+ static VALUE rdxr_graph_complete_namespace_access(int argc, VALUE *argv, VALUE self) {
622
+ VALUE name, opts;
623
+ rb_scan_args(argc, argv, "1:", &name, &opts);
571
624
  Check_Type(name, T_STRING);
572
625
 
626
+ const char *self_receiver = extract_self_receiver(opts);
627
+
573
628
  void *graph;
574
629
  TypedData_Get_Struct(self, void *, &graph_type, graph);
575
630
 
576
- struct CompletionResult result = rdx_graph_complete_namespace_access(graph, StringValueCStr(name));
631
+ struct CompletionResult result =
632
+ rdx_graph_complete_namespace_access(graph, StringValueCStr(name), self_receiver);
577
633
  return completion_result_to_ruby_array(result, self);
578
634
  }
579
635
 
580
- // Graph#complete_method_call: (String name) -> Array[Declaration]
636
+ // Graph#complete_method_call: (String name, self_receiver: nil) -> Array[Declaration]
581
637
  // Returns completion candidates after a method call operator (e.g., `foo.`).
582
- static VALUE rdxr_graph_complete_method_call(VALUE self, VALUE name) {
638
+ // The optional self_receiver kwarg is the caller's runtime self type, used for MRI-style
639
+ // visibility checks (private/protected).
640
+ static VALUE rdxr_graph_complete_method_call(int argc, VALUE *argv, VALUE self) {
641
+ VALUE name, opts;
642
+ rb_scan_args(argc, argv, "1:", &name, &opts);
583
643
  Check_Type(name, T_STRING);
584
644
 
645
+ const char *self_receiver = extract_self_receiver(opts);
646
+
585
647
  void *graph;
586
648
  TypedData_Get_Struct(self, void *, &graph_type, graph);
587
649
 
588
- struct CompletionResult result = rdx_graph_complete_method_call(graph, StringValueCStr(name));
650
+ struct CompletionResult result =
651
+ rdx_graph_complete_method_call(graph, StringValueCStr(name), self_receiver);
589
652
  return completion_result_to_ruby_array(result, self);
590
653
  }
591
654
 
592
- // Graph#complete_method_argument: (String name, Array[String] nesting) -> Array[Declaration | Keyword | KeywordParameter]
655
+ // Graph#complete_method_argument: (String name, Array[String] nesting, self_receiver: nil) -> Array[Declaration | Keyword | KeywordParameter]
593
656
  // Returns completion candidates inside a method call's argument list (e.g., `foo.bar(|)`).
594
- static VALUE rdxr_graph_complete_method_argument(VALUE self, VALUE name, VALUE nesting) {
657
+ // See complete_expression for semantics of self_receiver.
658
+ static VALUE rdxr_graph_complete_method_argument(int argc, VALUE *argv, VALUE self) {
659
+ VALUE name, nesting, opts;
660
+ rb_scan_args(argc, argv, "2:", &name, &nesting, &opts);
661
+
595
662
  Check_Type(name, T_STRING);
596
663
  rdxi_check_array_of_strings(nesting);
597
664
 
665
+ const char *self_receiver = extract_self_receiver(opts);
666
+
598
667
  void *graph;
599
668
  TypedData_Get_Struct(self, void *, &graph_type, graph);
600
669
 
@@ -602,7 +671,7 @@ static VALUE rdxr_graph_complete_method_argument(VALUE self, VALUE name, VALUE n
602
671
  char **converted_nesting = rdxi_str_array_to_char(nesting, nesting_count);
603
672
 
604
673
  struct CompletionResult result = rdx_graph_complete_method_argument(
605
- graph, StringValueCStr(name), (const char *const *)converted_nesting, nesting_count);
674
+ graph, StringValueCStr(name), (const char *const *)converted_nesting, nesting_count, self_receiver);
606
675
 
607
676
  rdxi_free_str_array(converted_nesting, nesting_count);
608
677
  return completion_result_to_ruby_array(result, self);
@@ -673,9 +742,12 @@ void rdxi_initialize_graph(VALUE moduleRubydex) {
673
742
  cKeyword = rb_define_class_under(mRubydex, "Keyword", rb_cObject);
674
743
  cKeywordParameter = rb_define_class_under(mRubydex, "KeywordParameter", rb_cObject);
675
744
 
745
+ id_self_receiver = rb_intern("self_receiver");
746
+
676
747
  rb_define_alloc_func(cGraph, rdxr_graph_alloc);
677
748
  rb_define_method(cGraph, "index_all", rdxr_graph_index_all, 1);
678
749
  rb_define_method(cGraph, "index_source", rdxr_graph_index_source, 3);
750
+ rb_define_method(cGraph, "document", rdxr_graph_document, 1);
679
751
  rb_define_method(cGraph, "delete_document", rdxr_graph_delete_document, 1);
680
752
  rb_define_method(cGraph, "resolve", rdxr_graph_resolve, 0);
681
753
  rb_define_method(cGraph, "resolve_constant", rdxr_graph_resolve_constant, 2);
@@ -691,10 +763,10 @@ void rdxi_initialize_graph(VALUE moduleRubydex) {
691
763
  rb_define_method(cGraph, "encoding=", rdxr_graph_set_encoding, 1);
692
764
  rb_define_method(cGraph, "resolve_require_path", rdxr_graph_resolve_require_path, 2);
693
765
  rb_define_method(cGraph, "require_paths", rdxr_graph_require_paths, 1);
694
- rb_define_method(cGraph, "complete_expression", rdxr_graph_complete_expression, 1);
695
- rb_define_method(cGraph, "complete_namespace_access", rdxr_graph_complete_namespace_access, 1);
696
- rb_define_method(cGraph, "complete_method_call", rdxr_graph_complete_method_call, 1);
697
- rb_define_method(cGraph, "complete_method_argument", rdxr_graph_complete_method_argument, 2);
766
+ rb_define_method(cGraph, "complete_expression", rdxr_graph_complete_expression, -1);
767
+ rb_define_method(cGraph, "complete_namespace_access", rdxr_graph_complete_namespace_access, -1);
768
+ rb_define_method(cGraph, "complete_method_call", rdxr_graph_complete_method_call, -1);
769
+ rb_define_method(cGraph, "complete_method_argument", rdxr_graph_complete_method_argument, -1);
698
770
  rb_define_method(cGraph, "exclude_paths", rdxr_graph_exclude_paths, 1);
699
771
  rb_define_method(cGraph, "excluded_paths", rdxr_graph_excluded_paths, 0);
700
772
  rb_define_method(cGraph, "keyword", rdxr_graph_keyword, 1);
@@ -75,6 +75,28 @@ static VALUE rdxr_method_reference_location(VALUE self) {
75
75
  return location;
76
76
  }
77
77
 
78
+ // MethodReference#receiver -> Rubydex::Declaration?
79
+ // Returns the resolved declaration for the receiver of the method call. Returns nil when the receiver is not a
80
+ // tracked constant or cannot be resolved.
81
+ static VALUE rdxr_method_reference_receiver(VALUE self) {
82
+ HandleData *data;
83
+ TypedData_Get_Struct(self, HandleData, &handle_type, data);
84
+
85
+ void *graph;
86
+ TypedData_Get_Struct(data->graph_obj, void *, &graph_type, graph);
87
+
88
+ const struct CDeclaration *decl = rdx_method_reference_receiver_declaration(graph, data->id);
89
+ if (decl == NULL) {
90
+ return Qnil;
91
+ }
92
+
93
+ VALUE decl_class = rdxi_declaration_class_for_kind(decl->kind);
94
+ VALUE argv[] = {data->graph_obj, ULL2NUM(decl->id)};
95
+ free_c_declaration(decl);
96
+
97
+ return rb_class_new_instance(2, argv, decl_class);
98
+ }
99
+
78
100
  // ResolvedConstantReference#declaration -> Declaration
79
101
  static VALUE rdxr_resolved_constant_reference_declaration(VALUE self) {
80
102
  HandleData *data;
@@ -120,4 +142,5 @@ void rdxi_initialize_reference(VALUE mRubydex) {
120
142
  rb_define_method(cMethodReference, "initialize", rdxr_handle_initialize, 2);
121
143
  rb_define_method(cMethodReference, "name", rdxr_method_reference_name, 0);
122
144
  rb_define_method(cMethodReference, "location", rdxr_method_reference_location, 0);
145
+ rb_define_method(cMethodReference, "receiver", rdxr_method_reference_receiver, 0);
123
146
  }
@@ -1,6 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rubydex
4
+ module Visibility
5
+ #: () -> bool
6
+ def public? = visibility == :public
7
+
8
+ #: () -> bool
9
+ def private? = visibility == :private
10
+
11
+ #: () -> bool
12
+ def protected? = visibility == :protected
13
+ end
14
+
4
15
  class Declaration
5
16
  # @abstract
6
17
  #: () -> Enumerable[Reference]
@@ -8,4 +19,24 @@ module Rubydex
8
19
  raise NotImplementedError, "Subclasses must implement #references"
9
20
  end
10
21
  end
22
+
23
+ class Class < Namespace
24
+ include Visibility
25
+ end
26
+
27
+ class Module < Namespace
28
+ include Visibility
29
+ end
30
+
31
+ class Constant < Declaration
32
+ include Visibility
33
+ end
34
+
35
+ class ConstantAlias < Declaration
36
+ include Visibility
37
+ end
38
+
39
+ class Method < Declaration
40
+ include Visibility
41
+ end
11
42
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rubydex
4
- VERSION = "0.1.0.beta14"
4
+ VERSION = "0.2.1"
5
5
  end
data/rbi/rubydex.rbi CHANGED
@@ -152,6 +152,7 @@ class Rubydex::GlobalVariableDefinition < Rubydex::Definition; end
152
152
  class Rubydex::InstanceVariableDefinition < Rubydex::Definition; end
153
153
  class Rubydex::MethodAliasDefinition < Rubydex::Definition; end
154
154
  class Rubydex::MethodDefinition < Rubydex::Definition; end
155
+
155
156
  class Rubydex::ModuleDefinition < Rubydex::Definition
156
157
  sig { returns(T::Array[Rubydex::Mixin]) }
157
158
  def mixins; end
@@ -259,6 +260,9 @@ class Rubydex::Graph
259
260
  sig { params(uri: String).returns(T.nilable(Rubydex::Document)) }
260
261
  def delete_document(uri); end
261
262
 
263
+ sig { params(uri: String).returns(T.nilable(Rubydex::Document)) }
264
+ def document(uri); end
265
+
262
266
  sig { returns(T::Array[Rubydex::Diagnostic]) }
263
267
  def diagnostics; end
264
268
 
@@ -314,27 +318,56 @@ class Rubydex::Graph
314
318
  # Returns completion candidates for an expression context. This includes all keywords, constants, methods, instance
315
319
  # variables, class variables and global variables reachable from the current lexical scope and self type.
316
320
  #
317
- # The nesting array represents the lexical scope stack, where the last element is the self type. An empty array
318
- # defaults to `Object` as the self type (top-level context).
319
- sig { params(nesting: T::Array[String]).returns(T::Array[T.any(Rubydex::Declaration, Rubydex::Keyword)]) }
320
- def complete_expression(nesting); end
321
+ # The nesting array represents the lexical scope stack. The optional `self_receiver` keyword argument overrides the
322
+ # self type independently of the lexical scope (e.g., `"Foo::<Foo>"` for `def Foo.bar`). This distinction is important
323
+ # because constants and class variables are always attached to the lexical scope. Meanwhile, methods and instance
324
+ # variables are attached to the type of `self` and those don't always match.
325
+ sig do
326
+ params(
327
+ nesting: T::Array[String],
328
+ self_receiver: T.nilable(String),
329
+ ).returns(T::Array[T.any(Rubydex::Declaration, Rubydex::Keyword)])
330
+ end
331
+ def complete_expression(nesting, self_receiver: nil); end
321
332
 
322
333
  # Returns completion candidates after a namespace access operator (e.g., `Foo::`). This includes all constants and
323
334
  # singleton methods for the namespace and its ancestors.
324
- sig { params(name: String).returns(T::Array[Rubydex::Declaration]) }
325
- def complete_namespace_access(name); end
335
+ #
336
+ # The optional `self_receiver` kwarg is the caller's runtime self type. It's used to filter visibility-restricted
337
+ # singleton methods (e.g., `private_class_method`). Pass `nil` (the default) for top-level/script scope.
338
+ sig do
339
+ params(
340
+ name: String,
341
+ self_receiver: T.nilable(String),
342
+ ).returns(T::Array[Rubydex::Declaration])
343
+ end
344
+ def complete_namespace_access(name, self_receiver: nil); end
326
345
 
327
346
  # Returns completion candidates after a method call operator (e.g., `foo.`). This includes all methods that exist on
328
347
  # the type of the receiver and its ancestors.
329
- sig { params(name: String).returns(T::Array[Rubydex::Method]) }
330
- def complete_method_call(name); end
348
+ #
349
+ # The optional `self_receiver` kwarg is the caller's runtime self type. It's used for visibility checks for `private`
350
+ # and `protected` methods. Pass `nil` (the default) for top-level/script scope.
351
+ sig do
352
+ params(
353
+ name: String,
354
+ self_receiver: T.nilable(String),
355
+ ).returns(T::Array[Rubydex::Method])
356
+ end
357
+ def complete_method_call(name, self_receiver: nil); end
331
358
 
332
359
  # Returns completion candidates inside a method call's argument list (e.g., `foo.bar(|)`). This includes everything
333
360
  # that expression completion provides plus keyword argument names of the method being called.
334
361
  #
335
- # The nesting array represents the lexical scope stack, where the last element is the self type.
336
- sig { params(name: String, nesting: T::Array[String]).returns(T::Array[T.any(Rubydex::Declaration, Rubydex::Keyword, Rubydex::KeywordParameter)]) }
337
- def complete_method_argument(name, nesting); end
362
+ # See `complete_expression` for the semantics of `nesting` and `self_receiver`.
363
+ sig do
364
+ params(
365
+ name: String,
366
+ nesting: T::Array[String],
367
+ self_receiver: T.nilable(String),
368
+ ).returns(T::Array[T.any(Rubydex::Declaration, Rubydex::Keyword, Rubydex::KeywordParameter)])
369
+ end
370
+ def complete_method_argument(name, nesting, self_receiver: nil); end
338
371
 
339
372
  private
340
373
 
@@ -415,6 +448,9 @@ class Rubydex::MethodReference < Rubydex::Reference
415
448
 
416
449
  sig { returns(String) }
417
450
  def name; end
451
+
452
+ sig { returns(T.nilable(Rubydex::Declaration)) }
453
+ def receiver; end
418
454
  end
419
455
 
420
456
  class Rubydex::Reference
@@ -108,4 +108,5 @@ rules! {
108
108
 
109
109
  // Resolution
110
110
  UndefinedMethodVisibilityTarget;
111
+ UndefinedConstantVisibilityTarget;
111
112
  }
@@ -234,6 +234,7 @@ impl<'a> RubyIndexer<'a> {
234
234
  Offset::from_prism_location(&name_loc),
235
235
  str_id,
236
236
  )));
237
+ self.visit(&opt_param.value());
237
238
  }
238
239
 
239
240
  if let Some(rest) = parameters_list.rest() {
@@ -276,6 +277,7 @@ impl<'a> RubyIndexer<'a> {
276
277
  let str_id = self.local_graph.intern_string(self.offset_to_string(&offset));
277
278
 
278
279
  parameters.push(Parameter::OptionalKeyword(ParameterStruct::new(offset, str_id)));
280
+ self.visit(&optional.value());
279
281
  }
280
282
  _ => {}
281
283
  }
@@ -1258,11 +1260,8 @@ impl<'a> RubyIndexer<'a> {
1258
1260
  let str_id = self.local_graph.intern_string(name);
1259
1261
  let offset = Offset::from_prism_location(&location);
1260
1262
  let definition = Definition::ConstantVisibility(Box::new(ConstantVisibilityDefinition::new(
1261
- self.local_graph.add_name(Name::new(
1262
- str_id,
1263
- receiver_name_id.map_or(ParentScope::None, ParentScope::Some),
1264
- self.current_lexical_scope_name_id(),
1265
- )),
1263
+ receiver_name_id,
1264
+ str_id,
1266
1265
  visibility,
1267
1266
  self.uri_id,
1268
1267
  offset,
@@ -3271,31 +3271,38 @@ mod visibility_tests {
3271
3271
  assert_no_local_diagnostics!(&context);
3272
3272
 
3273
3273
  assert_definition_at!(&context, "6:21-6:24", ConstantVisibility, |def| {
3274
- assert_def_name_eq!(&context, def, "BAR");
3274
+ assert_string_eq!(&context, def.target(), "BAR");
3275
+ assert!(def.receiver().is_none());
3275
3276
  assert_eq!(def.visibility(), &Visibility::Private);
3276
3277
  });
3277
3278
  assert_definition_at!(&context, "6:27-6:30", ConstantVisibility, |def| {
3278
- assert_def_name_eq!(&context, def, "BAZ");
3279
+ assert_string_eq!(&context, def.target(), "BAZ");
3280
+ assert!(def.receiver().is_none());
3279
3281
  assert_eq!(def.visibility(), &Visibility::Private);
3280
3282
  });
3281
3283
  assert_definition_at!(&context, "7:20-7:25", ConstantVisibility, |def| {
3282
- assert_def_name_eq!(&context, def, "FOO");
3284
+ assert_string_eq!(&context, def.target(), "FOO");
3285
+ assert!(def.receiver().is_none());
3283
3286
  assert_eq!(def.visibility(), &Visibility::Private);
3284
3287
  });
3285
3288
  assert_definition_at!(&context, "13:26-13:29", ConstantVisibility, |def| {
3286
- assert_def_name_eq!(&context, def, "Foo::BAR");
3289
+ assert_string_eq!(&context, def.target(), "BAR");
3290
+ assert_name_path_eq!(&context, "Foo", def.receiver().unwrap());
3287
3291
  assert_eq!(def.visibility(), &Visibility::Public);
3288
3292
  });
3289
3293
  assert_definition_at!(&context, "14:25-14:30", ConstantVisibility, |def| {
3290
- assert_def_name_eq!(&context, def, "Foo::BAZ");
3294
+ assert_string_eq!(&context, def.target(), "BAZ");
3295
+ assert_name_path_eq!(&context, "Foo", def.receiver().unwrap());
3291
3296
  assert_eq!(def.visibility(), &Visibility::Public);
3292
3297
  });
3293
3298
  assert_definition_at!(&context, "17:26-17:29", ConstantVisibility, |def| {
3294
- assert_def_name_eq!(&context, def, "Qux");
3299
+ assert_string_eq!(&context, def.target(), "Qux");
3300
+ assert!(def.receiver().is_none());
3295
3301
  assert_eq!(def.visibility(), &Visibility::Private);
3296
3302
  });
3297
3303
  assert_definition_at!(&context, "20:22-20:25", ConstantVisibility, |def| {
3298
- assert_def_name_eq!(&context, def, "Foo::BAR");
3304
+ assert_string_eq!(&context, def.target(), "BAR");
3305
+ assert_name_path_eq!(&context, "Foo", def.receiver().unwrap());
3299
3306
  assert_eq!(def.visibility(), &Visibility::Public);
3300
3307
  });
3301
3308
  }
@@ -4060,6 +4067,19 @@ mod constant_reference_tests {
4060
4067
  );
4061
4068
  }
4062
4069
 
4070
+ #[test]
4071
+ fn index_unresolved_constant_references_in_default_values() {
4072
+ let context = index_source({
4073
+ "
4074
+ def foo(a = C1, b = C2::C3); end
4075
+ def bar(a: C4, b: C5::C6); end
4076
+ "
4077
+ });
4078
+
4079
+ assert_no_local_diagnostics!(&context);
4080
+ assert_constant_references_eq!(&context, ["C1", "C2", "C3", "C4", "C5", "C6"]);
4081
+ }
4082
+
4063
4083
  #[test]
4064
4084
  fn index_constant_path_and_write_visits_value() {
4065
4085
  let context = index_source({