ruby-lsp 0.23.1 → 0.23.5
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/VERSION +1 -1
- data/exe/ruby-lsp-launcher +18 -9
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +89 -58
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +21 -10
- data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +36 -0
- data/lib/ruby_indexer/ruby_indexer.rb +1 -0
- data/lib/ruby_indexer/test/index_test.rb +2 -1
- data/lib/ruby_indexer/test/instance_variables_test.rb +20 -0
- data/lib/ruby_indexer/test/method_test.rb +86 -8
- data/lib/ruby_lsp/base_server.rb +11 -21
- data/lib/ruby_lsp/document.rb +62 -9
- data/lib/ruby_lsp/erb_document.rb +5 -3
- data/lib/ruby_lsp/global_state.rb +9 -0
- data/lib/ruby_lsp/listeners/definition.rb +7 -2
- data/lib/ruby_lsp/rbs_document.rb +2 -2
- data/lib/ruby_lsp/requests/code_action_resolve.rb +2 -6
- data/lib/ruby_lsp/requests/completion.rb +2 -1
- data/lib/ruby_lsp/requests/definition.rb +1 -1
- data/lib/ruby_lsp/requests/document_highlight.rb +1 -1
- data/lib/ruby_lsp/requests/hover.rb +1 -1
- data/lib/ruby_lsp/requests/prepare_rename.rb +1 -1
- data/lib/ruby_lsp/requests/references.rb +1 -1
- data/lib/ruby_lsp/requests/rename.rb +3 -3
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +1 -4
- data/lib/ruby_lsp/requests/signature_help.rb +1 -1
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +3 -3
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +13 -13
- data/lib/ruby_lsp/requests/workspace_symbol.rb +2 -2
- data/lib/ruby_lsp/ruby_document.rb +75 -6
- data/lib/ruby_lsp/server.rb +69 -49
- data/lib/ruby_lsp/store.rb +7 -7
- data/lib/ruby_lsp/type_inferrer.rb +39 -17
- data/lib/ruby_lsp/utils.rb +43 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 924e57270229eb4d0b709b01023d16f414591f8ea40c3b8a51ae6fb6cd09cc4f
|
4
|
+
data.tar.gz: 72f4f544baf412f8580fe28287ba5324c7f646a9436797ebea59f34f3712d62e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 650cf172b8b1408f5498cb2010c2f94b3ef0a4bc93212ef63e24c2177ef5996455625a1bb64fe51f75322a42625e9ebcf156f95f5412c44839fe4957df4c957f
|
7
|
+
data.tar.gz: ce9b866443836810fe5884c2f977d4fd91fe020023ff547a9d60e525715ce388429aa7a901b0acc84876d68cada8f30dac2836494b7403fd8c2e845bea86ce79
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.23.
|
1
|
+
0.23.5
|
data/exe/ruby-lsp-launcher
CHANGED
@@ -7,6 +7,7 @@
|
|
7
7
|
# !!!!!!!
|
8
8
|
|
9
9
|
setup_error = nil
|
10
|
+
install_error = nil
|
10
11
|
|
11
12
|
# Read the initialize request before even starting the server. We need to do this to figure out the workspace URI.
|
12
13
|
# Editors are not required to spawn the language server process on the same directory as the workspace URI, so we need
|
@@ -61,27 +62,35 @@ begin
|
|
61
62
|
|
62
63
|
require "bundler"
|
63
64
|
Bundler.ui.level = :silent
|
65
|
+
|
66
|
+
# This Marshal load can only happen after requiring Bundler because it will load a custom error class from Bundler
|
67
|
+
# itself. If we try to load before requiring, the class will not be defined and loading will fail
|
68
|
+
error_path = File.join(".ruby-lsp", "install_error")
|
69
|
+
install_error = if File.exist?(error_path)
|
70
|
+
Marshal.load(File.read(error_path))
|
71
|
+
end
|
72
|
+
|
64
73
|
Bundler.setup
|
65
74
|
$stderr.puts("Composed Bundle set up successfully")
|
66
75
|
end
|
67
76
|
rescue StandardError => e
|
68
77
|
# If installing gems failed for any reason, we don't want to exit the process prematurely. We can still provide most
|
69
78
|
# features in a degraded mode. We simply save the error so that we can report to the user that certain gems might be
|
70
|
-
# missing, but we respect the LSP life cycle
|
71
|
-
|
72
|
-
|
79
|
+
# missing, but we respect the LSP life cycle.
|
80
|
+
#
|
81
|
+
# If an install error occurred and one of the gems is not installed, Bundler.setup is guaranteed to fail with
|
82
|
+
# `Bundler::GemNotFound`. We don't set `setup_error` here because that would essentially report the same problem twice
|
83
|
+
# to telemetry, which is not useful
|
84
|
+
unless install_error && e.is_a?(Bundler::GemNotFound)
|
85
|
+
setup_error = e
|
86
|
+
$stderr.puts("Failed to set up composed Bundle\n#{e.full_message}")
|
87
|
+
end
|
73
88
|
|
74
89
|
# If Bundler.setup fails, we need to restore the original $LOAD_PATH so that we can still require the Ruby LSP server
|
75
90
|
# in degraded mode
|
76
91
|
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
|
77
92
|
end
|
78
93
|
|
79
|
-
error_path = File.join(".ruby-lsp", "install_error")
|
80
|
-
|
81
|
-
install_error = if File.exist?(error_path)
|
82
|
-
Marshal.load(File.read(error_path))
|
83
|
-
end
|
84
|
-
|
85
94
|
# Now that the bundle is set up, we can begin actually launching the server. Note that `Bundler.setup` will have already
|
86
95
|
# configured the load path using the version of the Ruby LSP present in the composed bundle. Do not push any Ruby LSP
|
87
96
|
# paths into the load path manually or we may end up requiring the wrong version of the gem
|
@@ -24,7 +24,7 @@ module RubyIndexer
|
|
24
24
|
@index = index
|
25
25
|
@uri = uri
|
26
26
|
@enhancements = T.let(Enhancement.all(self), T::Array[Enhancement])
|
27
|
-
@visibility_stack = T.let([
|
27
|
+
@visibility_stack = T.let([VisibilityScope.public_scope], T::Array[VisibilityScope])
|
28
28
|
@comments_by_line = T.let(
|
29
29
|
parse_result.comments.to_h do |c|
|
30
30
|
[c.location.start_line, c]
|
@@ -64,7 +64,6 @@ module RubyIndexer
|
|
64
64
|
:on_constant_path_or_write_node_enter,
|
65
65
|
:on_constant_path_operator_write_node_enter,
|
66
66
|
:on_constant_path_and_write_node_enter,
|
67
|
-
:on_constant_or_write_node_enter,
|
68
67
|
:on_constant_write_node_enter,
|
69
68
|
:on_constant_or_write_node_enter,
|
70
69
|
:on_constant_and_write_node_enter,
|
@@ -138,7 +137,7 @@ module RubyIndexer
|
|
138
137
|
|
139
138
|
sig { params(node: Prism::SingletonClassNode).void }
|
140
139
|
def on_singleton_class_node_enter(node)
|
141
|
-
@visibility_stack.push(
|
140
|
+
@visibility_stack.push(VisibilityScope.public_scope)
|
142
141
|
|
143
142
|
current_owner = @owner_stack.last
|
144
143
|
|
@@ -280,15 +279,14 @@ module RubyIndexer
|
|
280
279
|
when :include, :prepend, :extend
|
281
280
|
handle_module_operation(node, message)
|
282
281
|
when :public
|
283
|
-
@visibility_stack.push(
|
282
|
+
@visibility_stack.push(VisibilityScope.public_scope)
|
284
283
|
when :protected
|
285
|
-
@visibility_stack.push(Entry::Visibility::PROTECTED)
|
284
|
+
@visibility_stack.push(VisibilityScope.new(visibility: Entry::Visibility::PROTECTED))
|
286
285
|
when :private
|
287
|
-
@visibility_stack.push(Entry::Visibility::PRIVATE)
|
286
|
+
@visibility_stack.push(VisibilityScope.new(visibility: Entry::Visibility::PRIVATE))
|
288
287
|
when :module_function
|
289
288
|
handle_module_function(node)
|
290
289
|
when :private_class_method
|
291
|
-
@visibility_stack.push(Entry::Visibility::PRIVATE)
|
292
290
|
handle_private_class_method(node)
|
293
291
|
end
|
294
292
|
|
@@ -324,42 +322,61 @@ module RubyIndexer
|
|
324
322
|
|
325
323
|
sig { params(node: Prism::DefNode).void }
|
326
324
|
def on_def_node_enter(node)
|
325
|
+
owner = @owner_stack.last
|
326
|
+
return unless owner
|
327
|
+
|
327
328
|
@inside_def = true
|
328
329
|
method_name = node.name.to_s
|
329
330
|
comments = collect_comments(node)
|
331
|
+
scope = current_visibility_scope
|
330
332
|
|
331
333
|
case node.receiver
|
332
334
|
when nil
|
335
|
+
location = Location.from_prism_location(node.location, @code_units_cache)
|
336
|
+
name_location = Location.from_prism_location(node.name_loc, @code_units_cache)
|
337
|
+
signatures = [Entry::Signature.new(list_params(node.parameters))]
|
338
|
+
|
333
339
|
@index.add(Entry::Method.new(
|
334
340
|
method_name,
|
335
341
|
@uri,
|
336
|
-
|
337
|
-
|
342
|
+
location,
|
343
|
+
name_location,
|
338
344
|
comments,
|
339
|
-
|
340
|
-
|
341
|
-
|
345
|
+
signatures,
|
346
|
+
scope.visibility,
|
347
|
+
owner,
|
342
348
|
))
|
343
|
-
when Prism::SelfNode
|
344
|
-
owner = @owner_stack.last
|
345
349
|
|
346
|
-
if
|
350
|
+
if scope.module_func
|
347
351
|
singleton = @index.existing_or_new_singleton_class(owner.name)
|
348
352
|
|
349
353
|
@index.add(Entry::Method.new(
|
350
354
|
method_name,
|
351
355
|
@uri,
|
352
|
-
|
353
|
-
|
356
|
+
location,
|
357
|
+
name_location,
|
354
358
|
comments,
|
355
|
-
|
356
|
-
|
359
|
+
signatures,
|
360
|
+
Entry::Visibility::PUBLIC,
|
357
361
|
singleton,
|
358
362
|
))
|
359
|
-
|
360
|
-
@owner_stack << singleton
|
361
|
-
@stack << "<Class:#{@stack.last}>"
|
362
363
|
end
|
364
|
+
when Prism::SelfNode
|
365
|
+
singleton = @index.existing_or_new_singleton_class(owner.name)
|
366
|
+
|
367
|
+
@index.add(Entry::Method.new(
|
368
|
+
method_name,
|
369
|
+
@uri,
|
370
|
+
Location.from_prism_location(node.location, @code_units_cache),
|
371
|
+
Location.from_prism_location(node.name_loc, @code_units_cache),
|
372
|
+
comments,
|
373
|
+
[Entry::Signature.new(list_params(node.parameters))],
|
374
|
+
scope.visibility,
|
375
|
+
singleton,
|
376
|
+
))
|
377
|
+
|
378
|
+
@owner_stack << singleton
|
379
|
+
@stack << "<Class:#{@stack.last}>"
|
363
380
|
end
|
364
381
|
end
|
365
382
|
|
@@ -834,6 +851,8 @@ module RubyIndexer
|
|
834
851
|
return unless receiver.nil? || receiver.is_a?(Prism::SelfNode)
|
835
852
|
|
836
853
|
comments = collect_comments(node)
|
854
|
+
scope = current_visibility_scope
|
855
|
+
|
837
856
|
arguments.each do |argument|
|
838
857
|
name, loc = case argument
|
839
858
|
when Prism::SymbolNode
|
@@ -850,7 +869,7 @@ module RubyIndexer
|
|
850
869
|
@uri,
|
851
870
|
Location.from_prism_location(loc, @code_units_cache),
|
852
871
|
comments,
|
853
|
-
|
872
|
+
scope.visibility,
|
854
873
|
@owner_stack.last,
|
855
874
|
))
|
856
875
|
end
|
@@ -862,7 +881,7 @@ module RubyIndexer
|
|
862
881
|
@uri,
|
863
882
|
Location.from_prism_location(loc, @code_units_cache),
|
864
883
|
comments,
|
865
|
-
|
884
|
+
scope.visibility,
|
866
885
|
@owner_stack.last,
|
867
886
|
))
|
868
887
|
end
|
@@ -904,11 +923,20 @@ module RubyIndexer
|
|
904
923
|
|
905
924
|
sig { params(node: Prism::CallNode).void }
|
906
925
|
def handle_module_function(node)
|
926
|
+
# Invoking `module_function` in a class raises
|
927
|
+
owner = @owner_stack.last
|
928
|
+
return unless owner.is_a?(Entry::Module)
|
929
|
+
|
907
930
|
arguments_node = node.arguments
|
908
|
-
return unless arguments_node
|
909
931
|
|
910
|
-
|
911
|
-
|
932
|
+
# If `module_function` is invoked without arguments, all methods defined after it become singleton methods and the
|
933
|
+
# visibility for instance methods changes to private
|
934
|
+
unless arguments_node
|
935
|
+
@visibility_stack.push(VisibilityScope.module_function_scope)
|
936
|
+
return
|
937
|
+
end
|
938
|
+
|
939
|
+
owner_name = owner.name
|
912
940
|
|
913
941
|
arguments_node.arguments.each do |argument|
|
914
942
|
method_name = case argument
|
@@ -946,45 +974,48 @@ module RubyIndexer
|
|
946
974
|
|
947
975
|
sig { params(node: Prism::CallNode).void }
|
948
976
|
def handle_private_class_method(node)
|
949
|
-
node.arguments&.arguments
|
950
|
-
|
951
|
-
when Prism::StringNode, Prism::SymbolNode
|
952
|
-
[argument]
|
953
|
-
when Prism::ArrayNode
|
954
|
-
argument.elements
|
955
|
-
else
|
956
|
-
[]
|
957
|
-
end
|
977
|
+
arguments = node.arguments&.arguments
|
978
|
+
return unless arguments
|
958
979
|
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
980
|
+
# If we're passing a method definition directly to `private_class_method`, push a new private scope. That will be
|
981
|
+
# applied when the indexer finds the method definition and then popped on `call_node_leave`
|
982
|
+
if arguments.first.is_a?(Prism::DefNode)
|
983
|
+
@visibility_stack.push(VisibilityScope.new(visibility: Entry::Visibility::PRIVATE))
|
984
|
+
return
|
985
|
+
end
|
963
986
|
|
964
|
-
|
965
|
-
|
966
|
-
when Prism::StringNode
|
967
|
-
string_or_symbol_node.content
|
968
|
-
when Prism::SymbolNode
|
969
|
-
string_or_symbol_node.value
|
970
|
-
end
|
971
|
-
next unless method_name
|
987
|
+
owner_name = @owner_stack.last&.name
|
988
|
+
return unless owner_name
|
972
989
|
|
973
|
-
|
974
|
-
|
990
|
+
# private_class_method accepts strings, symbols or arrays of strings and symbols as arguments. Here we build a
|
991
|
+
# single list of all of the method names that have to be made private
|
992
|
+
arrays, others = T.cast(
|
993
|
+
arguments.partition { |argument| argument.is_a?(Prism::ArrayNode) },
|
994
|
+
[T::Array[Prism::ArrayNode], T::Array[Prism::Node]],
|
995
|
+
)
|
996
|
+
arrays.each { |array| others.concat(array.elements) }
|
975
997
|
|
976
|
-
|
977
|
-
|
998
|
+
names = others.filter_map do |argument|
|
999
|
+
case argument
|
1000
|
+
when Prism::StringNode
|
1001
|
+
argument.unescaped
|
1002
|
+
when Prism::SymbolNode
|
1003
|
+
argument.value
|
1004
|
+
end
|
1005
|
+
end
|
978
1006
|
|
979
|
-
|
980
|
-
|
981
|
-
|
1007
|
+
names.each do |name|
|
1008
|
+
entries = @index.resolve_method(name, @index.existing_or_new_singleton_class(owner_name).name)
|
1009
|
+
next unless entries
|
1010
|
+
|
1011
|
+
entries.each do |entry|
|
1012
|
+
entry.visibility = Entry::Visibility::PRIVATE
|
982
1013
|
end
|
983
1014
|
end
|
984
1015
|
end
|
985
1016
|
|
986
|
-
sig { returns(
|
987
|
-
def
|
1017
|
+
sig { returns(VisibilityScope) }
|
1018
|
+
def current_visibility_scope
|
988
1019
|
T.must(@visibility_stack.last)
|
989
1020
|
end
|
990
1021
|
|
@@ -1091,7 +1122,7 @@ module RubyIndexer
|
|
1091
1122
|
|
1092
1123
|
sig { params(short_name: String, entry: Entry::Namespace).void }
|
1093
1124
|
def advance_namespace_stack(short_name, entry)
|
1094
|
-
@visibility_stack.push(
|
1125
|
+
@visibility_stack.push(VisibilityScope.public_scope)
|
1095
1126
|
@owner_stack << entry
|
1096
1127
|
@index.add(entry)
|
1097
1128
|
@stack << short_name
|
@@ -48,6 +48,8 @@ module RubyIndexer
|
|
48
48
|
)
|
49
49
|
|
50
50
|
@configuration = T.let(RubyIndexer::Configuration.new, Configuration)
|
51
|
+
|
52
|
+
@initial_indexing_completed = T.let(false, T::Boolean)
|
51
53
|
end
|
52
54
|
|
53
55
|
# Register an included `hook` that will be executed when `module_name` is included into any namespace
|
@@ -56,8 +58,8 @@ module RubyIndexer
|
|
56
58
|
(@included_hooks[module_name] ||= []) << hook
|
57
59
|
end
|
58
60
|
|
59
|
-
sig { params(uri: URI::Generic).void }
|
60
|
-
def delete(uri)
|
61
|
+
sig { params(uri: URI::Generic, skip_require_paths_tree: T::Boolean).void }
|
62
|
+
def delete(uri, skip_require_paths_tree: false)
|
61
63
|
key = uri.to_s
|
62
64
|
# For each constant discovered in `path`, delete the associated entry from the index. If there are no entries
|
63
65
|
# left, delete the constant from the index.
|
@@ -80,6 +82,7 @@ module RubyIndexer
|
|
80
82
|
end
|
81
83
|
|
82
84
|
@uris_to_entries.delete(key)
|
85
|
+
return if skip_require_paths_tree
|
83
86
|
|
84
87
|
require_path = uri.require_path
|
85
88
|
@require_paths_tree.delete(require_path) if require_path
|
@@ -357,12 +360,13 @@ module RubyIndexer
|
|
357
360
|
# When troubleshooting an indexing issue, e.g. through irb, it's not obvious that `index_all` will augment the
|
358
361
|
# existing index values, meaning it may contain 'stale' entries. This check ensures that the user is aware of this
|
359
362
|
# behavior and can take appropriate action.
|
360
|
-
|
361
|
-
if @entries.any?
|
363
|
+
if @initial_indexing_completed
|
362
364
|
raise IndexNotEmptyError,
|
363
365
|
"The index is not empty. To prevent invalid entries, `index_all` can only be called once."
|
364
366
|
end
|
365
367
|
|
368
|
+
@initial_indexing_completed = true
|
369
|
+
|
366
370
|
RBSIndexer.new(self).index_ruby_core
|
367
371
|
# Calculate how many paths are worth 1% of progress
|
368
372
|
progress_step = (uris.length / 100.0).ceil
|
@@ -618,17 +622,24 @@ module RubyIndexer
|
|
618
622
|
end
|
619
623
|
|
620
624
|
# Synchronizes a change made to the given URI. This method will ensure that new declarations are indexed, removed
|
621
|
-
# declarations removed and that the ancestor linearization cache is cleared if necessary
|
622
|
-
|
623
|
-
|
625
|
+
# declarations removed and that the ancestor linearization cache is cleared if necessary. If a block is passed, the
|
626
|
+
# consumer of this API has to handle deleting and inserting/updating entries in the index instead of passing the
|
627
|
+
# document's source (used to handle unsaved changes to files)
|
628
|
+
sig do
|
629
|
+
params(uri: URI::Generic, source: T.nilable(String), block: T.nilable(T.proc.params(index: Index).void)).void
|
630
|
+
end
|
631
|
+
def handle_change(uri, source = nil, &block)
|
624
632
|
key = uri.to_s
|
625
633
|
original_entries = @uris_to_entries[key]
|
626
634
|
|
627
|
-
|
628
|
-
|
635
|
+
if block
|
636
|
+
block.call(self)
|
637
|
+
else
|
638
|
+
delete(uri)
|
639
|
+
index_single(uri, T.must(source))
|
640
|
+
end
|
629
641
|
|
630
642
|
updated_entries = @uris_to_entries[key]
|
631
|
-
|
632
643
|
return unless original_entries && updated_entries
|
633
644
|
|
634
645
|
# A change in one ancestor may impact several different others, which could be including that ancestor through
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyIndexer
|
5
|
+
# Represents the visibility scope in a Ruby namespace. This keeps track of whether methods are in a public, private or
|
6
|
+
# protected section, and whether they are module functions.
|
7
|
+
class VisibilityScope
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
class << self
|
11
|
+
extend T::Sig
|
12
|
+
|
13
|
+
sig { returns(T.attached_class) }
|
14
|
+
def module_function_scope
|
15
|
+
new(module_func: true, visibility: Entry::Visibility::PRIVATE)
|
16
|
+
end
|
17
|
+
|
18
|
+
sig { returns(T.attached_class) }
|
19
|
+
def public_scope
|
20
|
+
new
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
sig { returns(Entry::Visibility) }
|
25
|
+
attr_reader :visibility
|
26
|
+
|
27
|
+
sig { returns(T::Boolean) }
|
28
|
+
attr_reader :module_func
|
29
|
+
|
30
|
+
sig { params(visibility: Entry::Visibility, module_func: T::Boolean).void }
|
31
|
+
def initialize(visibility: Entry::Visibility::PUBLIC, module_func: false)
|
32
|
+
@visibility = visibility
|
33
|
+
@module_func = module_func
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -5,6 +5,7 @@ require "yaml"
|
|
5
5
|
require "did_you_mean"
|
6
6
|
|
7
7
|
require "ruby_indexer/lib/ruby_indexer/uri"
|
8
|
+
require "ruby_indexer/lib/ruby_indexer/visibility_scope"
|
8
9
|
require "ruby_indexer/lib/ruby_indexer/declaration_listener"
|
9
10
|
require "ruby_indexer/lib/ruby_indexer/reference_finder"
|
10
11
|
require "ruby_indexer/lib/ruby_indexer/enhancement"
|
@@ -2061,7 +2061,8 @@ module RubyIndexer
|
|
2061
2061
|
end
|
2062
2062
|
|
2063
2063
|
def test_prevents_multiple_calls_to_index_all
|
2064
|
-
|
2064
|
+
@index.index_all
|
2065
|
+
|
2065
2066
|
assert_raises(Index::IndexNotEmptyError) do
|
2066
2067
|
@index.index_all
|
2067
2068
|
end
|
@@ -216,5 +216,25 @@ module RubyIndexer
|
|
216
216
|
assert_instance_of(Entry::Class, owner)
|
217
217
|
assert_equal("Foo", owner.name)
|
218
218
|
end
|
219
|
+
|
220
|
+
def test_module_function_does_not_impact_instance_variables
|
221
|
+
# One possible way of implementing `module_function` would be to push a fake singleton class to the stack, so that
|
222
|
+
# methods are inserted into it. However, that would be incorrect because it would then bind instance variables to
|
223
|
+
# the wrong type. This test is here to prevent that from happening.
|
224
|
+
index(<<~RUBY)
|
225
|
+
module Foo
|
226
|
+
module_function
|
227
|
+
|
228
|
+
def something; end
|
229
|
+
|
230
|
+
@a = 123
|
231
|
+
end
|
232
|
+
RUBY
|
233
|
+
|
234
|
+
entry = T.must(@index["@a"]&.first)
|
235
|
+
owner = T.must(entry.owner)
|
236
|
+
assert_instance_of(Entry::SingletonClass, owner)
|
237
|
+
assert_equal("Foo::<Class:Foo>", owner.name)
|
238
|
+
end
|
219
239
|
end
|
220
240
|
end
|
@@ -88,19 +88,21 @@ module RubyIndexer
|
|
88
88
|
|
89
89
|
def test_visibility_tracking
|
90
90
|
index(<<~RUBY)
|
91
|
-
|
92
|
-
|
91
|
+
class Foo
|
92
|
+
private def foo
|
93
|
+
end
|
93
94
|
|
94
|
-
|
95
|
+
def bar; end
|
95
96
|
|
96
|
-
|
97
|
+
protected
|
97
98
|
|
98
|
-
|
99
|
+
def baz; end
|
100
|
+
end
|
99
101
|
RUBY
|
100
102
|
|
101
|
-
assert_entry("foo", Entry::Method, "/fake/path/foo.rb:
|
102
|
-
assert_entry("bar", Entry::Method, "/fake/path/foo.rb:
|
103
|
-
assert_entry("baz", Entry::Method, "/fake/path/foo.rb:
|
103
|
+
assert_entry("foo", Entry::Method, "/fake/path/foo.rb:1-10:2-5", visibility: Entry::Visibility::PRIVATE)
|
104
|
+
assert_entry("bar", Entry::Method, "/fake/path/foo.rb:4-2:4-14", visibility: Entry::Visibility::PUBLIC)
|
105
|
+
assert_entry("baz", Entry::Method, "/fake/path/foo.rb:8-2:8-14", visibility: Entry::Visibility::PROTECTED)
|
104
106
|
end
|
105
107
|
|
106
108
|
def test_visibility_tracking_with_nested_class_or_modules
|
@@ -846,6 +848,82 @@ module RubyIndexer
|
|
846
848
|
assert_signature_matches(entry, "baz(1)")
|
847
849
|
end
|
848
850
|
|
851
|
+
def test_module_function_with_no_arguments
|
852
|
+
index(<<~RUBY)
|
853
|
+
module Foo
|
854
|
+
def bar; end
|
855
|
+
|
856
|
+
module_function
|
857
|
+
|
858
|
+
def baz; end
|
859
|
+
attr_reader :attribute
|
860
|
+
|
861
|
+
public
|
862
|
+
|
863
|
+
def qux; end
|
864
|
+
end
|
865
|
+
RUBY
|
866
|
+
|
867
|
+
entry = T.must(@index["bar"].first)
|
868
|
+
assert_predicate(entry, :public?)
|
869
|
+
assert_equal("Foo", T.must(entry.owner).name)
|
870
|
+
|
871
|
+
instance_baz, singleton_baz = T.must(@index["baz"])
|
872
|
+
assert_predicate(instance_baz, :private?)
|
873
|
+
assert_equal("Foo", T.must(instance_baz.owner).name)
|
874
|
+
|
875
|
+
assert_predicate(singleton_baz, :public?)
|
876
|
+
assert_equal("Foo::<Class:Foo>", T.must(singleton_baz.owner).name)
|
877
|
+
|
878
|
+
# After invoking `public`, the state of `module_function` is reset
|
879
|
+
instance_qux, singleton_qux = T.must(@index["qux"])
|
880
|
+
assert_nil(singleton_qux)
|
881
|
+
assert_predicate(instance_qux, :public?)
|
882
|
+
assert_equal("Foo", T.must(instance_baz.owner).name)
|
883
|
+
|
884
|
+
# Attributes are not turned into class methods, they do become private
|
885
|
+
instance_attribute, singleton_attribute = @index["attribute"]
|
886
|
+
assert_nil(singleton_attribute)
|
887
|
+
assert_equal("Foo", T.must(instance_attribute.owner).name)
|
888
|
+
assert_predicate(instance_attribute, :private?)
|
889
|
+
end
|
890
|
+
|
891
|
+
def test_module_function_does_nothing_in_classes
|
892
|
+
# Invoking `module_function` in a class raises an error. We simply ignore it
|
893
|
+
index(<<~RUBY)
|
894
|
+
class Foo
|
895
|
+
def bar; end
|
896
|
+
|
897
|
+
module_function
|
898
|
+
|
899
|
+
def baz; end
|
900
|
+
end
|
901
|
+
RUBY
|
902
|
+
|
903
|
+
entry = T.must(@index["bar"].first)
|
904
|
+
assert_predicate(entry, :public?)
|
905
|
+
assert_equal("Foo", T.must(entry.owner).name)
|
906
|
+
|
907
|
+
entry = T.must(@index["baz"].first)
|
908
|
+
assert_predicate(entry, :public?)
|
909
|
+
assert_equal("Foo", T.must(entry.owner).name)
|
910
|
+
end
|
911
|
+
|
912
|
+
def test_making_several_class_methods_private
|
913
|
+
index(<<~RUBY)
|
914
|
+
class Foo
|
915
|
+
def self.bar; end
|
916
|
+
def self.baz; end
|
917
|
+
def self.qux; end
|
918
|
+
|
919
|
+
private_class_method :bar, :baz, :qux
|
920
|
+
|
921
|
+
def initialize
|
922
|
+
end
|
923
|
+
end
|
924
|
+
RUBY
|
925
|
+
end
|
926
|
+
|
849
927
|
private
|
850
928
|
|
851
929
|
sig { params(entry: Entry::Method, call_string: String).void }
|