solargraph 0.58.0 → 0.59.0.dev.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.
Files changed (166) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +3 -0
  3. data/.gitattributes +2 -0
  4. data/.github/workflows/linting.yml +4 -5
  5. data/.github/workflows/plugins.yml +40 -36
  6. data/.github/workflows/rspec.yml +45 -13
  7. data/.github/workflows/typecheck.yml +2 -2
  8. data/.rubocop_todo.yml +27 -49
  9. data/CHANGELOG.md +3 -0
  10. data/README.md +3 -3
  11. data/Rakefile +1 -0
  12. data/bin/solargraph +8 -8
  13. data/lib/solargraph/api_map/cache.rb +110 -110
  14. data/lib/solargraph/api_map/constants.rb +289 -279
  15. data/lib/solargraph/api_map/index.rb +204 -193
  16. data/lib/solargraph/api_map/source_to_yard.rb +109 -97
  17. data/lib/solargraph/api_map/store.rb +387 -384
  18. data/lib/solargraph/api_map.rb +1000 -945
  19. data/lib/solargraph/complex_type/conformance.rb +176 -0
  20. data/lib/solargraph/complex_type/type_methods.rb +242 -228
  21. data/lib/solargraph/complex_type/unique_type.rb +632 -482
  22. data/lib/solargraph/complex_type.rb +549 -444
  23. data/lib/solargraph/convention/data_definition/data_definition_node.rb +93 -91
  24. data/lib/solargraph/convention/data_definition.rb +108 -105
  25. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +62 -61
  26. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +103 -102
  27. data/lib/solargraph/convention/struct_definition.rb +168 -164
  28. data/lib/solargraph/diagnostics/require_not_found.rb +54 -53
  29. data/lib/solargraph/diagnostics/rubocop.rb +119 -118
  30. data/lib/solargraph/diagnostics/rubocop_helpers.rb +70 -68
  31. data/lib/solargraph/diagnostics/type_check.rb +56 -55
  32. data/lib/solargraph/doc_map.rb +200 -439
  33. data/lib/solargraph/equality.rb +34 -34
  34. data/lib/solargraph/gem_pins.rb +97 -98
  35. data/lib/solargraph/language_server/host/dispatch.rb +131 -130
  36. data/lib/solargraph/language_server/host/message_worker.rb +113 -112
  37. data/lib/solargraph/language_server/host/sources.rb +100 -99
  38. data/lib/solargraph/language_server/host.rb +883 -878
  39. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +109 -114
  40. data/lib/solargraph/language_server/message/extended/document.rb +24 -23
  41. data/lib/solargraph/language_server/message/text_document/completion.rb +58 -56
  42. data/lib/solargraph/language_server/message/text_document/definition.rb +42 -40
  43. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +28 -26
  44. data/lib/solargraph/language_server/message/text_document/formatting.rb +150 -148
  45. data/lib/solargraph/language_server/message/text_document/hover.rb +60 -58
  46. data/lib/solargraph/language_server/message/text_document/signature_help.rb +25 -24
  47. data/lib/solargraph/language_server/message/text_document/type_definition.rb +27 -25
  48. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +25 -23
  49. data/lib/solargraph/library.rb +729 -683
  50. data/lib/solargraph/location.rb +87 -82
  51. data/lib/solargraph/logging.rb +57 -37
  52. data/lib/solargraph/parser/comment_ripper.rb +76 -69
  53. data/lib/solargraph/parser/flow_sensitive_typing.rb +483 -255
  54. data/lib/solargraph/parser/node_processor/base.rb +122 -92
  55. data/lib/solargraph/parser/node_processor.rb +63 -62
  56. data/lib/solargraph/parser/parser_gem/class_methods.rb +167 -149
  57. data/lib/solargraph/parser/parser_gem/node_chainer.rb +191 -166
  58. data/lib/solargraph/parser/parser_gem/node_methods.rb +506 -486
  59. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +22 -22
  60. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +61 -59
  61. data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +24 -15
  62. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +46 -46
  63. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +60 -53
  64. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +53 -23
  65. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +41 -40
  66. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +30 -29
  67. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +61 -59
  68. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +98 -98
  69. data/lib/solargraph/parser/parser_gem/node_processors/or_node.rb +22 -0
  70. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +17 -17
  71. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +39 -38
  72. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +53 -52
  73. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +296 -291
  74. data/lib/solargraph/parser/parser_gem/node_processors/when_node.rb +23 -0
  75. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +33 -29
  76. data/lib/solargraph/parser/parser_gem/node_processors.rb +74 -70
  77. data/lib/solargraph/parser/region.rb +75 -69
  78. data/lib/solargraph/parser/snippet.rb +17 -17
  79. data/lib/solargraph/pin/base.rb +761 -729
  80. data/lib/solargraph/pin/base_variable.rb +418 -126
  81. data/lib/solargraph/pin/block.rb +126 -104
  82. data/lib/solargraph/pin/breakable.rb +13 -9
  83. data/lib/solargraph/pin/callable.rb +278 -231
  84. data/lib/solargraph/pin/closure.rb +68 -72
  85. data/lib/solargraph/pin/common.rb +94 -79
  86. data/lib/solargraph/pin/compound_statement.rb +55 -0
  87. data/lib/solargraph/pin/conversions.rb +124 -123
  88. data/lib/solargraph/pin/delegated_method.rb +131 -120
  89. data/lib/solargraph/pin/documenting.rb +115 -114
  90. data/lib/solargraph/pin/instance_variable.rb +38 -34
  91. data/lib/solargraph/pin/keyword.rb +16 -20
  92. data/lib/solargraph/pin/local_variable.rb +31 -75
  93. data/lib/solargraph/pin/method.rb +720 -672
  94. data/lib/solargraph/pin/method_alias.rb +42 -34
  95. data/lib/solargraph/pin/namespace.rb +121 -115
  96. data/lib/solargraph/pin/parameter.rb +338 -275
  97. data/lib/solargraph/pin/proxy_type.rb +40 -39
  98. data/lib/solargraph/pin/reference/override.rb +47 -47
  99. data/lib/solargraph/pin/reference/superclass.rb +17 -15
  100. data/lib/solargraph/pin/reference.rb +41 -39
  101. data/lib/solargraph/pin/search.rb +62 -61
  102. data/lib/solargraph/pin/signature.rb +69 -61
  103. data/lib/solargraph/pin/symbol.rb +53 -53
  104. data/lib/solargraph/pin/until.rb +18 -18
  105. data/lib/solargraph/pin/while.rb +18 -18
  106. data/lib/solargraph/pin.rb +46 -44
  107. data/lib/solargraph/pin_cache.rb +665 -245
  108. data/lib/solargraph/position.rb +118 -119
  109. data/lib/solargraph/range.rb +112 -112
  110. data/lib/solargraph/rbs_map/conversions.rb +846 -823
  111. data/lib/solargraph/rbs_map/core_map.rb +65 -58
  112. data/lib/solargraph/rbs_map/stdlib_map.rb +72 -43
  113. data/lib/solargraph/rbs_map.rb +217 -163
  114. data/lib/solargraph/shell.rb +397 -352
  115. data/lib/solargraph/source/chain/call.rb +372 -337
  116. data/lib/solargraph/source/chain/constant.rb +28 -26
  117. data/lib/solargraph/source/chain/hash.rb +35 -34
  118. data/lib/solargraph/source/chain/if.rb +29 -28
  119. data/lib/solargraph/source/chain/instance_variable.rb +34 -13
  120. data/lib/solargraph/source/chain/literal.rb +53 -48
  121. data/lib/solargraph/source/chain/or.rb +31 -23
  122. data/lib/solargraph/source/chain.rb +294 -291
  123. data/lib/solargraph/source/change.rb +89 -82
  124. data/lib/solargraph/source/cursor.rb +172 -166
  125. data/lib/solargraph/source/encoding_fixes.rb +23 -23
  126. data/lib/solargraph/source/source_chainer.rb +204 -194
  127. data/lib/solargraph/source/updater.rb +59 -55
  128. data/lib/solargraph/source.rb +524 -498
  129. data/lib/solargraph/source_map/clip.rb +237 -226
  130. data/lib/solargraph/source_map/data.rb +37 -34
  131. data/lib/solargraph/source_map/mapper.rb +282 -259
  132. data/lib/solargraph/source_map.rb +220 -212
  133. data/lib/solargraph/type_checker/problem.rb +34 -32
  134. data/lib/solargraph/type_checker/rules.rb +157 -84
  135. data/lib/solargraph/type_checker.rb +895 -814
  136. data/lib/solargraph/version.rb +5 -5
  137. data/lib/solargraph/workspace/config.rb +257 -255
  138. data/lib/solargraph/workspace/gemspecs.rb +367 -0
  139. data/lib/solargraph/workspace/require_paths.rb +98 -97
  140. data/lib/solargraph/workspace.rb +362 -220
  141. data/lib/solargraph/yard_map/helpers.rb +45 -44
  142. data/lib/solargraph/yard_map/mapper/to_method.rb +134 -130
  143. data/lib/solargraph/yard_map/mapper/to_namespace.rb +32 -31
  144. data/lib/solargraph/yard_map/mapper.rb +84 -79
  145. data/lib/solargraph/yardoc.rb +97 -87
  146. data/lib/solargraph.rb +126 -105
  147. data/rbs/fills/rubygems/0/dependency.rbs +193 -0
  148. data/rbs/fills/tuple/tuple.rbs +28 -0
  149. data/rbs/shims/ast/0/node.rbs +5 -0
  150. data/rbs/shims/diff-lcs/1.5/diff-lcs.rbs +11 -0
  151. data/rbs_collection.yaml +1 -1
  152. data/solargraph.gemspec +2 -1
  153. metadata +23 -17
  154. data/lib/solargraph/type_checker/checks.rb +0 -124
  155. data/lib/solargraph/type_checker/param_def.rb +0 -37
  156. data/lib/solargraph/yard_map/to_method.rb +0 -89
  157. data/sig/shims/ast/0/node.rbs +0 -5
  158. /data/{sig → rbs}/shims/ast/2.4/.rbs_meta.yaml +0 -0
  159. /data/{sig → rbs}/shims/ast/2.4/ast.rbs +0 -0
  160. /data/{sig → rbs}/shims/parser/3.2.0.1/builders/default.rbs +0 -0
  161. /data/{sig → rbs}/shims/parser/3.2.0.1/manifest.yaml +0 -0
  162. /data/{sig → rbs}/shims/parser/3.2.0.1/parser.rbs +0 -0
  163. /data/{sig → rbs}/shims/parser/3.2.0.1/polyfill.rbs +0 -0
  164. /data/{sig → rbs}/shims/thor/1.2.0.1/.rbs_meta.yaml +0 -0
  165. /data/{sig → rbs}/shims/thor/1.2.0.1/manifest.yaml +0 -0
  166. /data/{sig → rbs}/shims/thor/1.2.0.1/thor.rbs +0 -0
@@ -1,279 +1,289 @@
1
- # frozen_string_literal: true
2
-
3
- module Solargraph
4
- class ApiMap
5
- # Methods for handling constants.
6
- #
7
- class Constants
8
- # @param store [Store]
9
- def initialize store
10
- @store = store
11
- end
12
-
13
- # Resolve a name to a fully qualified namespace or constant.
14
- #
15
- # `Constants#resolve` finds fully qualified (absolute)
16
- # namespaces based on relative names and the open gates
17
- # (namespaces) provided. Names must be runtime-visible (erased)
18
- # non-literal types, non-duck, non-signature types - e.g.,
19
- # TrueClass, NilClass, Integer and Hash instead of true, nil,
20
- # 96, or Hash{String => Symbol}
21
- #
22
- # Note: You may want to be using #qualify. Notably, #resolve:
23
- # - does not handle anything with type parameters
24
- # - will not gracefully handle nil, self and Boolean
25
- # - will return a constant name instead of following its assignment
26
- #
27
- # @param name [String] Namespace which may relative and not be rooted.
28
- # @param gates [Array<Array<String>, String>] Namespaces to search while resolving the name
29
- #
30
- # @return [String, nil] fully qualified namespace (i.e., is
31
- # absolute, but will not start with ::)
32
- def resolve(name, *gates)
33
- return store.get_path_pins(name[2..]).first&.path if name.start_with?('::')
34
-
35
- flat = gates.flatten
36
- flat.push '' if flat.empty?
37
- if cached_resolve.include? [name, flat]
38
- cached_result = cached_resolve[[name, flat]]
39
- # don't recurse
40
- return nil if cached_result == :in_process
41
- return cached_result
42
- end
43
- resolve_and_cache(name, flat)
44
- end
45
-
46
- # Get a fully qualified namespace from a reference pin.
47
- #
48
- # @param pin [Pin::Reference]
49
- # @return [String, nil]
50
- def dereference pin
51
- qualify_type(pin.type, *pin.reference_gates)&.tag
52
- end
53
-
54
- # Collect a list of all constants defined in the specified gates.
55
- #
56
- # @param gates [Array<Array<String>, String>]
57
- # @return [Array<Solargraph::Pin::Namespace, Solargraph::Pin::Constant>]
58
- def collect(*gates)
59
- flat = gates.flatten
60
- cached_collect[flat] || collect_and_cache(flat)
61
- end
62
-
63
- # Determine a fully qualified namespace for a given tag
64
- # referenced from the specified open gates. This method will
65
- # search in each gate until it finds a match for the name.
66
- #
67
- # @param tag [String, nil] The type to match
68
- # @param gates [Array<String>]
69
- # @return [String, nil] fully qualified tag
70
- def qualify tag, *gates
71
- type = ComplexType.try_parse(tag)
72
- qualify_type(type, *gates)&.tag
73
- end
74
-
75
- # @param type [ComplexType, nil] The type to match
76
- # @param gates [Array<String>]
77
- #
78
- # @return [ComplexType, nil] A new rooted ComplexType
79
- def qualify_type type, *gates
80
- return nil if type.nil?
81
- return type if type.selfy? || type.literal? || type.tag == 'nil' || type.interface? ||
82
- type.tag == 'Boolean'
83
-
84
- gates.push '' unless gates.include?('')
85
- fqns = resolve(type.rooted_namespace, *gates)
86
- return unless fqns
87
- pin = store.get_path_pins(fqns).first
88
- if pin.is_a?(Pin::Constant)
89
- const = Solargraph::Parser::NodeMethods.unpack_name(pin.assignment)
90
- return unless const
91
- fqns = resolve(const, *pin.gates)
92
- end
93
- type.recreate(new_name: fqns, make_rooted: true)
94
- end
95
-
96
- # @return [void]
97
- def clear
98
- [cached_collect, cached_resolve].each(&:clear)
99
- end
100
-
101
- private
102
-
103
- # @return [Store]
104
- attr_reader :store
105
-
106
- # @param name [String]
107
- # @param gates [Array<String>]
108
- # @return [String, nil]
109
- def resolve_and_cache name, gates
110
- cached_resolve[[name, gates]] = :in_process
111
- cached_resolve[[name, gates]] = resolve_uncached(name, gates)
112
- end
113
-
114
- # @param name [String]
115
- # @param gates [Array<String>]
116
- # @return [String, nil]
117
- def resolve_uncached name, gates
118
- resolved = nil
119
- base = gates
120
- parts = name.split('::')
121
- first = nil
122
- parts.each.with_index do |nam, idx|
123
- resolved, remainder = complex_resolve(nam, base, idx != parts.length - 1)
124
- first ||= remainder
125
- if resolved
126
- base = [resolved]
127
- else
128
- return resolve(name, first) unless first.empty?
129
- end
130
- end
131
- resolved
132
- end
133
-
134
- # @todo I'm not sure of a better way to express the return value in YARD.
135
- # It's a tuple where the first element is a nullable string. Something
136
- # like `Array(String|nil, Array<String>)` would be more accurate.
137
- #
138
- # @param name [String]
139
- # @param gates [Array<String>]
140
- # @param internal [Boolean] True if the name is not the last in the namespace
141
- # @return [Array(Object, Array<String>)]
142
- def complex_resolve name, gates, internal
143
- resolved = nil
144
- gates.each.with_index do |gate, idx|
145
- resolved = simple_resolve(name, gate, internal)
146
- return [resolved, gates[(idx + 1)..]] if resolved
147
- store.get_ancestor_references(gate).each do |ref|
148
- return ref.name.sub(/^::/, '') if ref.name.end_with?("::#{name}") && ref.name.start_with?('::')
149
-
150
- mixin = resolve(ref.name, ref.reference_gates)
151
- next unless mixin
152
-
153
- resolved = simple_resolve(name, mixin, internal)
154
- return [resolved, gates[(idx + 1)..]] if resolved
155
- end
156
- end
157
- [nil, []]
158
- end
159
-
160
- # @param name [String]
161
- # @param gate [String]
162
- # @param internal [Boolean] True if the name is not the last in the namespace
163
- # @return [String, nil]
164
- def simple_resolve name, gate, internal
165
- here = "#{gate}::#{name}".sub(/^::/, '').sub(/::$/, '')
166
- pin = store.get_path_pins(here).first
167
- if pin.is_a?(Pin::Constant) && internal
168
- const = Solargraph::Parser::NodeMethods.unpack_name(pin.assignment)
169
- return unless const
170
- resolve(const, pin.gates)
171
- else
172
- pin&.path
173
- end
174
- end
175
-
176
- # @param gates [Array<String>]
177
- # @return [Array<Solargraph::Pin::Namespace, Solargraph::Pin::Constant>]
178
- def collect_and_cache gates
179
- skip = Set.new
180
- cached_collect[gates] = gates.flat_map do |gate|
181
- inner_get_constants(gate, %i[public private], skip)
182
- end
183
- end
184
-
185
- # @return [Hash{Array(String, Array<String>) => String, :in_process, nil}]
186
- def cached_resolve
187
- @cached_resolve ||= {}
188
- end
189
-
190
- # @return [Hash{Array<String> => Array<Solargraph::Pin::Namespace, Solargraph::Pin::Constant>}]
191
- def cached_collect
192
- @cached_collect ||= {}
193
- end
194
-
195
- # Determine fully qualified namespace for a given namespace used
196
- # inside the definition of another tag ("context"). This method
197
- # will start the search in the specified context until it finds a
198
- # match for the namespace.
199
- #
200
- # @param namespace [String, nil] The namespace to
201
- # match
202
- # @param context_namespace [String] The context namespace in which the
203
- # tag was referenced; start from here to resolve the name
204
- # @return [String, nil] fully qualified namespace
205
- def qualify_namespace namespace, context_namespace = ''
206
- if namespace.start_with?('::')
207
- inner_qualify(namespace[2..], '', Set.new)
208
- else
209
- inner_qualify(namespace, context_namespace, Set.new)
210
- end
211
- end
212
-
213
- # @param name [String] Namespace to fully qualify
214
- # @param root [String] The context to search
215
- # @param skip [Set<String>] Contexts already searched
216
- # @return [String, nil] Fully qualified ("rooted") namespace
217
- def inner_qualify name, root, skip
218
- return name if name == ComplexType::GENERIC_TAG_NAME
219
- return nil if name.nil?
220
- return nil if skip.include?(root)
221
- skip.add root
222
- possibles = []
223
- if name == ''
224
- return '' if root == ''
225
-
226
- inner_qualify(root, '', skip)
227
- else
228
- return name if root == '' && store.namespace_exists?(name)
229
- roots = root.to_s.split('::')
230
- while roots.length.positive?
231
- fqns = "#{roots.join('::')}::#{name}"
232
- return fqns if store.namespace_exists?(fqns)
233
- incs = store.get_includes(roots.join('::'))
234
- incs.each do |inc|
235
- foundinc = inner_qualify(name, inc.type.to_s, skip)
236
- possibles.push foundinc unless foundinc.nil?
237
- end
238
- roots.pop
239
- end
240
- if possibles.empty?
241
- incs = store.get_includes('')
242
- incs.each do |inc|
243
- foundinc = inner_qualify(name, inc.type.to_s, skip)
244
- possibles.push foundinc unless foundinc.nil?
245
- end
246
- end
247
- return name if store.namespace_exists?(name)
248
- possibles.last
249
- end
250
- end
251
-
252
- # @param fqns [String]
253
- # @param visibility [Array<Symbol>]
254
- # @param skip [Set<String>]
255
- # @return [Array<Solargraph::Pin::Namespace, Solargraph::Pin::Constant>]
256
- def inner_get_constants fqns, visibility, skip
257
- return [] if fqns.nil? || skip.include?(fqns)
258
- skip.add fqns
259
- result = []
260
-
261
- store.get_prepends(fqns).each do |pre|
262
- pre_fqns = resolve(pre.name, pre.closure.gates - skip.to_a)
263
- result.concat inner_get_constants(pre_fqns, [:public], skip)
264
- end
265
- result.concat(store.get_constants(fqns, visibility).sort { |a, b| a.name <=> b.name })
266
- store.get_includes(fqns).each do |pin|
267
- inc_fqns = resolve(pin.name, pin.closure.gates - skip.to_a)
268
- result.concat inner_get_constants(inc_fqns, [:public], skip)
269
- end
270
- sc_ref = store.get_superclass(fqns)
271
- if sc_ref
272
- fqsc = dereference(sc_ref)
273
- result.concat inner_get_constants(fqsc, [:public], skip) unless %w[Object BasicObject].include?(fqsc)
274
- end
275
- result
276
- end
277
- end
278
- end
279
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class ApiMap
5
+ # Methods for handling constants.
6
+ #
7
+ class Constants
8
+ # @param store [Store]
9
+ def initialize store
10
+ @store = store
11
+ end
12
+
13
+ # Resolve a name to a fully qualified namespace or constant.
14
+ #
15
+ # `Constants#resolve` finds fully qualified (absolute)
16
+ # namespaces based on relative names and the open gates
17
+ # (namespaces) provided. Names must be runtime-visible (erased)
18
+ # non-literal types, non-duck, non-signature types - e.g.,
19
+ # TrueClass, NilClass, Integer and Hash instead of true, nil,
20
+ # 96, or Hash{String => Symbol}
21
+ #
22
+ # Note: You may want to be using #qualify. Notably, #resolve:
23
+ # - does not handle anything with type parameters
24
+ # - will not gracefully handle nil, self and Boolean
25
+ # - will return a constant name instead of following its assignment
26
+ #
27
+ # @param name [String] Namespace which may relative and not be rooted.
28
+ # @param gates [Array<Array<String>, String>] Namespaces to search while resolving the name
29
+ #
30
+ # @sg-ignore flow sensitive typing needs to eliminate literal from union with return if foo == :bar
31
+ # @return [String, nil] fully qualified namespace (i.e., is
32
+ # absolute, but will not start with ::)
33
+ def resolve(name, *gates)
34
+ # @sg-ignore Need to add nil check here
35
+ return store.get_path_pins(name[2..]).first&.path if name.start_with?('::')
36
+
37
+ flat = gates.flatten
38
+ flat.push '' if flat.empty?
39
+ if cached_resolve.include? [name, flat]
40
+ cached_result = cached_resolve[[name, flat]]
41
+ # don't recurse
42
+ return nil if cached_result == :in_process
43
+ return cached_result
44
+ end
45
+ resolve_and_cache(name, flat)
46
+ end
47
+
48
+ # Get a fully qualified namespace from a reference pin.
49
+ #
50
+ # @param pin [Pin::Reference]
51
+ # @return [String, nil]
52
+ def dereference pin
53
+ qualify_type(pin.type, *pin.reference_gates)&.tag
54
+ end
55
+
56
+ # Collect a list of all constants defined in the specified gates.
57
+ #
58
+ # @param gates [Array<Array<String>, String>]
59
+ # @return [Array<Solargraph::Pin::Namespace, Solargraph::Pin::Constant>]
60
+ def collect(*gates)
61
+ flat = gates.flatten
62
+ cached_collect[flat] || collect_and_cache(flat)
63
+ end
64
+
65
+ # Determine a fully qualified namespace for a given tag
66
+ # referenced from the specified open gates. This method will
67
+ # search in each gate until it finds a match for the name.
68
+ #
69
+ # @param tag [String, nil] The type to match
70
+ # @param gates [Array<String>]
71
+ # @return [String, nil] fully qualified tag
72
+ def qualify tag, *gates
73
+ type = ComplexType.try_parse(tag)
74
+ qualify_type(type, *gates)&.tag
75
+ end
76
+
77
+ # @param type [ComplexType, nil] The type to match
78
+ # @param gates [Array<String>]
79
+ #
80
+ # @return [ComplexType, nil] A new rooted ComplexType
81
+ def qualify_type type, *gates
82
+ return nil if type.nil?
83
+ return type if type.selfy? || type.literal? || type.tag == 'nil' || type.interface? ||
84
+ type.tag == 'Boolean'
85
+
86
+ gates.push '' unless gates.include?('')
87
+ fqns = resolve(type.rooted_namespace, *gates)
88
+ return unless fqns
89
+ pin = store.get_path_pins(fqns).first
90
+ if pin.is_a?(Pin::Constant)
91
+ # @sg-ignore Need to add nil check here
92
+ const = Solargraph::Parser::NodeMethods.unpack_name(pin.assignment)
93
+ return unless const
94
+ fqns = resolve(const, *pin.gates)
95
+ end
96
+ type.recreate(new_name: fqns, make_rooted: true)
97
+ end
98
+
99
+ # @return [void]
100
+ def clear
101
+ [cached_collect, cached_resolve].each(&:clear)
102
+ end
103
+
104
+ private
105
+
106
+ # @return [Store]
107
+ attr_reader :store
108
+
109
+ # @param name [String]
110
+ # @param gates [Array<String>]
111
+ # @sg-ignore flow sensitive typing should be able to handle redefinition
112
+ # @return [String, nil]
113
+ def resolve_and_cache name, gates
114
+ cached_resolve[[name, gates]] = :in_process
115
+ cached_resolve[[name, gates]] = resolve_uncached(name, gates)
116
+ end
117
+
118
+ # @param name [String]
119
+ # @param gates [Array<String>]
120
+ # @return [String, nil]
121
+ def resolve_uncached name, gates
122
+ resolved = nil
123
+ base = gates
124
+ parts = name.split('::')
125
+ first = nil
126
+ parts.each.with_index do |nam, idx|
127
+ resolved, remainder = complex_resolve(nam, base, idx != parts.length - 1)
128
+ first ||= remainder
129
+ if resolved
130
+ base = [resolved]
131
+ else
132
+ # @sg-ignore flow sensitive typing needs better handling of ||= on lvars
133
+ return resolve(name, first) unless first.empty?
134
+ end
135
+ end
136
+ resolved
137
+ end
138
+
139
+ # @todo I'm not sure of a better way to express the return value in YARD.
140
+ # It's a tuple where the first element is a nullable string. Something
141
+ # like `Array(String|nil, Array<String>)` would be more accurate.
142
+ #
143
+ # @param name [String]
144
+ # @param gates [Array<String>]
145
+ # @param internal [Boolean] True if the name is not the last in the namespace
146
+ # @return [Array(String, Array<String>), Array(nil, Array<String>), String]
147
+ def complex_resolve name, gates, internal
148
+ resolved = nil
149
+ gates.each.with_index do |gate, idx|
150
+ resolved = simple_resolve(name, gate, internal)
151
+ return [resolved, gates[(idx + 1)..]] if resolved
152
+ store.get_ancestor_references(gate).each do |ref|
153
+ return ref.name.sub(/^::/, '') if ref.name.end_with?("::#{name}") && ref.name.start_with?('::')
154
+
155
+ mixin = resolve(ref.name, ref.reference_gates)
156
+ next unless mixin
157
+
158
+ resolved = simple_resolve(name, mixin, internal)
159
+ return [resolved, gates[(idx + 1)..]] if resolved
160
+ end
161
+ end
162
+ [nil, []]
163
+ end
164
+
165
+ # @param name [String]
166
+ # @param gate [String]
167
+ # @param internal [Boolean] True if the name is not the last in the namespace
168
+ # @return [String, nil]
169
+ def simple_resolve name, gate, internal
170
+ here = "#{gate}::#{name}".sub(/^::/, '').sub(/::$/, '')
171
+ pin = store.get_path_pins(here).first
172
+ if pin.is_a?(Pin::Constant) && internal
173
+ # @sg-ignore Need to add nil check here
174
+ const = Solargraph::Parser::NodeMethods.unpack_name(pin.assignment)
175
+ return unless const
176
+ resolve(const, pin.gates)
177
+ else
178
+ pin&.path
179
+ end
180
+ end
181
+
182
+ # @param gates [Array<String>]
183
+ # @return [Array<Solargraph::Pin::Namespace, Solargraph::Pin::Constant>]
184
+ def collect_and_cache gates
185
+ skip = Set.new
186
+ cached_collect[gates] = gates.flat_map do |gate|
187
+ inner_get_constants(gate, %i[public private], skip)
188
+ end
189
+ end
190
+
191
+ # @return [Hash{Array(String, Array<String>) => String, :in_process, nil}]
192
+ def cached_resolve
193
+ @cached_resolve ||= {}
194
+ end
195
+
196
+ # @return [Hash{Array<String> => Array<Solargraph::Pin::Namespace, Solargraph::Pin::Constant>}]
197
+ def cached_collect
198
+ @cached_collect ||= {}
199
+ end
200
+
201
+ # Determine fully qualified namespace for a given namespace used
202
+ # inside the definition of another tag ("context"). This method
203
+ # will start the search in the specified context until it finds a
204
+ # match for the namespace.
205
+ #
206
+ # @param namespace [String] The namespace to
207
+ # match
208
+ # @param context_namespace [String] The context namespace in which the
209
+ # tag was referenced; start from here to resolve the name
210
+ # @return [String, nil] fully qualified namespace
211
+ def qualify_namespace namespace, context_namespace = ''
212
+ if namespace.start_with?('::')
213
+ # @sg-ignore Need to add nil check here
214
+ inner_qualify(namespace[2..], '', Set.new)
215
+ else
216
+ inner_qualify(namespace, context_namespace, Set.new)
217
+ end
218
+ end
219
+
220
+ # @param name [String] Namespace to fully qualify
221
+ # @param root [String] The context to search
222
+ # @param skip [Set<String>] Contexts already searched
223
+ # @return [String, nil] Fully qualified ("rooted") namespace
224
+ def inner_qualify name, root, skip
225
+ return name if name == ComplexType::GENERIC_TAG_NAME
226
+ return nil if name.nil?
227
+ return nil if skip.include?(root)
228
+ skip.add root
229
+ possibles = []
230
+ if name == ''
231
+ return '' if root == ''
232
+
233
+ inner_qualify(root, '', skip)
234
+ else
235
+ return name if root == '' && store.namespace_exists?(name)
236
+ roots = root.to_s.split('::')
237
+ while roots.length.positive?
238
+ fqns = "#{roots.join('::')}::#{name}"
239
+ return fqns if store.namespace_exists?(fqns)
240
+ incs = store.get_includes(roots.join('::'))
241
+ incs.each do |inc|
242
+ foundinc = inner_qualify(name, inc.type.to_s, skip)
243
+ possibles.push foundinc unless foundinc.nil?
244
+ end
245
+ roots.pop
246
+ end
247
+ if possibles.empty?
248
+ incs = store.get_includes('')
249
+ incs.each do |inc|
250
+ foundinc = inner_qualify(name, inc.type.to_s, skip)
251
+ possibles.push foundinc unless foundinc.nil?
252
+ end
253
+ end
254
+ return name if store.namespace_exists?(name)
255
+ possibles.last
256
+ end
257
+ end
258
+
259
+ # @param fqns [String, nil]
260
+ # @param visibility [Array<Symbol>]
261
+ # @param skip [Set<String>]
262
+ # @return [Array<Solargraph::Pin::Namespace, Solargraph::Pin::Constant>]
263
+ def inner_get_constants fqns, visibility, skip
264
+ return [] if fqns.nil? || skip.include?(fqns)
265
+ skip.add fqns
266
+ result = []
267
+
268
+ store.get_prepends(fqns).each do |pre|
269
+ # @sg-ignore Need to add nil check here
270
+ pre_fqns = resolve(pre.name, pre.closure.gates - skip.to_a)
271
+ result.concat inner_get_constants(pre_fqns, [:public], skip)
272
+ end
273
+ result.concat(store.get_constants(fqns, visibility).sort { |a, b| a.name <=> b.name })
274
+ store.get_includes(fqns).each do |pin|
275
+ # @sg-ignore Need to add nil check here
276
+ inc_fqns = resolve(pin.name, pin.closure.gates - skip.to_a)
277
+ result.concat inner_get_constants(inc_fqns, [:public], skip)
278
+ end
279
+ sc_ref = store.get_superclass(fqns)
280
+ if sc_ref
281
+ fqsc = dereference(sc_ref)
282
+ # @sg-ignore Need to add nil check here
283
+ result.concat inner_get_constants(fqsc, [:public], skip) unless %w[Object BasicObject].include?(fqsc)
284
+ end
285
+ result
286
+ end
287
+ end
288
+ end
289
+ end