solargraph 0.58.1 → 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 (162) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +3 -0
  3. data/.github/workflows/linting.yml +4 -5
  4. data/.github/workflows/plugins.yml +40 -36
  5. data/.github/workflows/rspec.yml +45 -13
  6. data/.github/workflows/typecheck.yml +2 -2
  7. data/.rubocop_todo.yml +27 -49
  8. data/README.md +3 -3
  9. data/Rakefile +1 -0
  10. data/lib/solargraph/api_map/cache.rb +110 -110
  11. data/lib/solargraph/api_map/constants.rb +289 -279
  12. data/lib/solargraph/api_map/index.rb +204 -193
  13. data/lib/solargraph/api_map/source_to_yard.rb +109 -97
  14. data/lib/solargraph/api_map/store.rb +387 -384
  15. data/lib/solargraph/api_map.rb +1000 -945
  16. data/lib/solargraph/complex_type/conformance.rb +176 -0
  17. data/lib/solargraph/complex_type/type_methods.rb +242 -228
  18. data/lib/solargraph/complex_type/unique_type.rb +632 -482
  19. data/lib/solargraph/complex_type.rb +549 -444
  20. data/lib/solargraph/convention/data_definition/data_definition_node.rb +93 -91
  21. data/lib/solargraph/convention/data_definition.rb +108 -105
  22. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +62 -61
  23. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +103 -102
  24. data/lib/solargraph/convention/struct_definition.rb +168 -164
  25. data/lib/solargraph/diagnostics/require_not_found.rb +54 -53
  26. data/lib/solargraph/diagnostics/rubocop.rb +119 -118
  27. data/lib/solargraph/diagnostics/rubocop_helpers.rb +70 -68
  28. data/lib/solargraph/diagnostics/type_check.rb +56 -55
  29. data/lib/solargraph/doc_map.rb +200 -439
  30. data/lib/solargraph/equality.rb +34 -34
  31. data/lib/solargraph/gem_pins.rb +97 -98
  32. data/lib/solargraph/language_server/host/dispatch.rb +131 -130
  33. data/lib/solargraph/language_server/host/message_worker.rb +113 -112
  34. data/lib/solargraph/language_server/host/sources.rb +100 -99
  35. data/lib/solargraph/language_server/host.rb +883 -878
  36. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +109 -114
  37. data/lib/solargraph/language_server/message/extended/document.rb +24 -23
  38. data/lib/solargraph/language_server/message/text_document/completion.rb +58 -56
  39. data/lib/solargraph/language_server/message/text_document/definition.rb +42 -40
  40. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +28 -26
  41. data/lib/solargraph/language_server/message/text_document/formatting.rb +150 -148
  42. data/lib/solargraph/language_server/message/text_document/hover.rb +60 -58
  43. data/lib/solargraph/language_server/message/text_document/signature_help.rb +25 -24
  44. data/lib/solargraph/language_server/message/text_document/type_definition.rb +27 -25
  45. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +25 -23
  46. data/lib/solargraph/library.rb +729 -683
  47. data/lib/solargraph/location.rb +87 -82
  48. data/lib/solargraph/logging.rb +57 -37
  49. data/lib/solargraph/parser/comment_ripper.rb +76 -69
  50. data/lib/solargraph/parser/flow_sensitive_typing.rb +483 -255
  51. data/lib/solargraph/parser/node_processor/base.rb +122 -92
  52. data/lib/solargraph/parser/node_processor.rb +63 -62
  53. data/lib/solargraph/parser/parser_gem/class_methods.rb +167 -149
  54. data/lib/solargraph/parser/parser_gem/node_chainer.rb +191 -166
  55. data/lib/solargraph/parser/parser_gem/node_methods.rb +506 -486
  56. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +22 -22
  57. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +61 -59
  58. data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +24 -15
  59. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +46 -46
  60. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +60 -53
  61. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +53 -23
  62. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +41 -40
  63. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +30 -29
  64. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +61 -59
  65. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +98 -98
  66. data/lib/solargraph/parser/parser_gem/node_processors/or_node.rb +22 -0
  67. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +17 -17
  68. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +39 -38
  69. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +53 -52
  70. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +296 -291
  71. data/lib/solargraph/parser/parser_gem/node_processors/when_node.rb +23 -0
  72. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +33 -29
  73. data/lib/solargraph/parser/parser_gem/node_processors.rb +74 -70
  74. data/lib/solargraph/parser/region.rb +75 -69
  75. data/lib/solargraph/parser/snippet.rb +17 -17
  76. data/lib/solargraph/pin/base.rb +761 -729
  77. data/lib/solargraph/pin/base_variable.rb +418 -126
  78. data/lib/solargraph/pin/block.rb +126 -104
  79. data/lib/solargraph/pin/breakable.rb +13 -9
  80. data/lib/solargraph/pin/callable.rb +278 -231
  81. data/lib/solargraph/pin/closure.rb +68 -72
  82. data/lib/solargraph/pin/common.rb +94 -79
  83. data/lib/solargraph/pin/compound_statement.rb +55 -0
  84. data/lib/solargraph/pin/conversions.rb +124 -123
  85. data/lib/solargraph/pin/delegated_method.rb +131 -120
  86. data/lib/solargraph/pin/documenting.rb +115 -114
  87. data/lib/solargraph/pin/instance_variable.rb +38 -34
  88. data/lib/solargraph/pin/keyword.rb +16 -20
  89. data/lib/solargraph/pin/local_variable.rb +31 -75
  90. data/lib/solargraph/pin/method.rb +720 -672
  91. data/lib/solargraph/pin/method_alias.rb +42 -34
  92. data/lib/solargraph/pin/namespace.rb +121 -115
  93. data/lib/solargraph/pin/parameter.rb +338 -275
  94. data/lib/solargraph/pin/proxy_type.rb +40 -39
  95. data/lib/solargraph/pin/reference/override.rb +47 -47
  96. data/lib/solargraph/pin/reference/superclass.rb +17 -15
  97. data/lib/solargraph/pin/reference.rb +41 -39
  98. data/lib/solargraph/pin/search.rb +62 -61
  99. data/lib/solargraph/pin/signature.rb +69 -61
  100. data/lib/solargraph/pin/symbol.rb +53 -53
  101. data/lib/solargraph/pin/until.rb +18 -18
  102. data/lib/solargraph/pin/while.rb +18 -18
  103. data/lib/solargraph/pin.rb +46 -44
  104. data/lib/solargraph/pin_cache.rb +665 -245
  105. data/lib/solargraph/position.rb +118 -119
  106. data/lib/solargraph/range.rb +112 -112
  107. data/lib/solargraph/rbs_map/conversions.rb +846 -823
  108. data/lib/solargraph/rbs_map/core_map.rb +65 -58
  109. data/lib/solargraph/rbs_map/stdlib_map.rb +72 -43
  110. data/lib/solargraph/rbs_map.rb +217 -163
  111. data/lib/solargraph/shell.rb +397 -352
  112. data/lib/solargraph/source/chain/call.rb +372 -337
  113. data/lib/solargraph/source/chain/constant.rb +28 -26
  114. data/lib/solargraph/source/chain/hash.rb +35 -34
  115. data/lib/solargraph/source/chain/if.rb +29 -28
  116. data/lib/solargraph/source/chain/instance_variable.rb +34 -13
  117. data/lib/solargraph/source/chain/literal.rb +53 -48
  118. data/lib/solargraph/source/chain/or.rb +31 -23
  119. data/lib/solargraph/source/chain.rb +294 -291
  120. data/lib/solargraph/source/change.rb +89 -82
  121. data/lib/solargraph/source/cursor.rb +172 -166
  122. data/lib/solargraph/source/source_chainer.rb +204 -194
  123. data/lib/solargraph/source/updater.rb +59 -55
  124. data/lib/solargraph/source.rb +524 -498
  125. data/lib/solargraph/source_map/clip.rb +237 -226
  126. data/lib/solargraph/source_map/data.rb +37 -34
  127. data/lib/solargraph/source_map/mapper.rb +282 -259
  128. data/lib/solargraph/source_map.rb +220 -212
  129. data/lib/solargraph/type_checker/problem.rb +34 -32
  130. data/lib/solargraph/type_checker/rules.rb +157 -84
  131. data/lib/solargraph/type_checker.rb +895 -814
  132. data/lib/solargraph/version.rb +1 -1
  133. data/lib/solargraph/workspace/config.rb +257 -255
  134. data/lib/solargraph/workspace/gemspecs.rb +367 -0
  135. data/lib/solargraph/workspace/require_paths.rb +98 -97
  136. data/lib/solargraph/workspace.rb +362 -220
  137. data/lib/solargraph/yard_map/helpers.rb +45 -44
  138. data/lib/solargraph/yard_map/mapper/to_method.rb +134 -130
  139. data/lib/solargraph/yard_map/mapper/to_namespace.rb +32 -31
  140. data/lib/solargraph/yard_map/mapper.rb +84 -79
  141. data/lib/solargraph/yardoc.rb +97 -87
  142. data/lib/solargraph.rb +126 -105
  143. data/rbs/fills/rubygems/0/dependency.rbs +193 -0
  144. data/rbs/fills/tuple/tuple.rbs +28 -0
  145. data/rbs/shims/ast/0/node.rbs +5 -0
  146. data/rbs/shims/diff-lcs/1.5/diff-lcs.rbs +11 -0
  147. data/rbs_collection.yaml +1 -1
  148. data/solargraph.gemspec +2 -1
  149. metadata +22 -17
  150. data/lib/solargraph/type_checker/checks.rb +0 -124
  151. data/lib/solargraph/type_checker/param_def.rb +0 -37
  152. data/lib/solargraph/yard_map/to_method.rb +0 -89
  153. data/sig/shims/ast/0/node.rbs +0 -5
  154. /data/{sig → rbs}/shims/ast/2.4/.rbs_meta.yaml +0 -0
  155. /data/{sig → rbs}/shims/ast/2.4/ast.rbs +0 -0
  156. /data/{sig → rbs}/shims/parser/3.2.0.1/builders/default.rbs +0 -0
  157. /data/{sig → rbs}/shims/parser/3.2.0.1/manifest.yaml +0 -0
  158. /data/{sig → rbs}/shims/parser/3.2.0.1/parser.rbs +0 -0
  159. /data/{sig → rbs}/shims/parser/3.2.0.1/polyfill.rbs +0 -0
  160. /data/{sig → rbs}/shims/thor/1.2.0.1/.rbs_meta.yaml +0 -0
  161. /data/{sig → rbs}/shims/thor/1.2.0.1/manifest.yaml +0 -0
  162. /data/{sig → rbs}/shims/thor/1.2.0.1/thor.rbs +0 -0
@@ -1,337 +1,372 @@
1
- # frozen_string_literal: true
2
-
3
- module Solargraph
4
- class Source
5
- class Chain
6
- #
7
- # Handles both method calls and local variable references by
8
- # first looking for a variable with the name 'word', then
9
- # proceeding to method signature resolution if not found.
10
- #
11
- class Call < Chain::Link
12
- include Solargraph::Parser::NodeMethods
13
-
14
- # @return [String]
15
- attr_reader :word
16
-
17
- # @return [Location]
18
- attr_reader :location
19
-
20
- # @return [::Array<Chain>]
21
- attr_reader :arguments
22
-
23
- # @return [Chain, nil]
24
- attr_reader :block
25
-
26
- # @param word [String]
27
- # @param location [Location, nil]
28
- # @param arguments [::Array<Chain>]
29
- # @param block [Chain, nil]
30
- def initialize word, location = nil, arguments = [], block = nil
31
- @word = word
32
- @location = location
33
- @arguments = arguments
34
- @block = block
35
- fix_block_pass
36
- end
37
-
38
- # @sg-ignore Fix "Not enough arguments to Module#protected"
39
- protected def equality_fields
40
- super + [arguments, block]
41
- end
42
-
43
- def with_block?
44
- !!@block
45
- end
46
-
47
- # @param api_map [ApiMap]
48
- # @param name_pin [Pin::Closure] name_pin.binder should give us the type of the object on which 'word' will be invoked
49
- # @param locals [::Array<Pin::LocalVariable>]
50
- def resolve api_map, name_pin, locals
51
- return super_pins(api_map, name_pin) if word == 'super'
52
- return yield_pins(api_map, name_pin) if word == 'yield'
53
- found = if head?
54
- api_map.visible_pins(locals, word, name_pin, location)
55
- else
56
- []
57
- end
58
- return inferred_pins(found, api_map, name_pin, locals) unless found.empty?
59
- pins = name_pin.binder.each_unique_type.flat_map do |context|
60
- ns_tag = context.namespace == '' ? '' : context.namespace_type.tag
61
- stack = api_map.get_method_stack(ns_tag, word, scope: context.scope)
62
- [stack.first].compact
63
- end
64
- return [] if pins.empty?
65
- inferred_pins(pins, api_map, name_pin, locals)
66
- end
67
-
68
- private
69
-
70
- # @param pins [::Enumerable<Pin::Method>]
71
- # @param api_map [ApiMap]
72
- # @param name_pin [Pin::Base]
73
- # @param locals [::Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>]
74
- # @return [::Array<Pin::Base>]
75
- def inferred_pins pins, api_map, name_pin, locals
76
- result = pins.map do |p|
77
- next p unless p.is_a?(Pin::Method)
78
- overloads = p.signatures
79
- # next p if overloads.empty?
80
- type = ComplexType::UNDEFINED
81
- # start with overloads that require blocks; if we are
82
- # passing a block, we want to find a signature that will
83
- # use it. If we didn't pass a block, the logic below will
84
- # reject it regardless
85
-
86
- with_block, without_block = overloads.partition(&:block?)
87
- sorted_overloads = with_block + without_block
88
- # @type [Pin::Signature, nil]
89
- new_signature_pin = nil
90
- sorted_overloads.each do |ol|
91
- next unless ol.arity_matches?(arguments, with_block?)
92
- match = true
93
-
94
- atypes = []
95
- arguments.each_with_index do |arg, idx|
96
- param = ol.parameters[idx]
97
- if param.nil?
98
- match = ol.parameters.any?(&:restarg?)
99
- break
100
- end
101
- arg_name_pin = Pin::ProxyType.anonymous(name_pin.context,
102
- gates: name_pin.gates,
103
- source: :chain)
104
- atype = atypes[idx] ||= arg.infer(api_map, arg_name_pin, locals)
105
- unless param.compatible_arg?(atype, api_map) || param.restarg?
106
- match = false
107
- break
108
- end
109
- end
110
- if match
111
- if ol.block && with_block?
112
- block_atypes = ol.block.parameters.map(&:return_type)
113
- if block.links.map(&:class) == [BlockSymbol]
114
- # like the bar in foo(&:bar)
115
- blocktype = block_symbol_call_type(api_map, name_pin.context, block_atypes, locals)
116
- else
117
- blocktype = block_call_type(api_map, name_pin, locals)
118
- end
119
- end
120
- # @type new_signature_pin [Pin::Signature]
121
- new_signature_pin = ol.resolve_generics_from_context_until_complete(ol.generics, atypes, nil, nil, blocktype)
122
- new_return_type = new_signature_pin.return_type
123
- if head?
124
- # If we're at the head of the chain, we called a
125
- # method somewhere that marked itself as returning
126
- # self. Given we didn't invoke this on an object,
127
- # this must be a method in this same class - so we
128
- # use our own self type
129
- self_type = name_pin.context
130
- else
131
- # if we're past the head in the chain, whatever the
132
- # type of the lhs side is what 'self' will be in its
133
- # declaration - we can't just use the type of the
134
- # method pin, as this might be a subclass of the
135
- # place where the method is defined
136
- self_type = name_pin.binder
137
- end
138
- # This same logic applies to the YARD work done by
139
- # 'with_params()'.
140
- #
141
- # qualify(), however, happens in the namespace where
142
- # the docs were written - from the method pin.
143
- type = with_params(new_return_type.self_to_type(self_type), self_type).qualify(api_map, *p.gates) if new_return_type.defined?
144
- type ||= ComplexType::UNDEFINED
145
- end
146
- break if type.defined?
147
- end
148
- p = p.with_single_signature(new_signature_pin) unless new_signature_pin.nil?
149
- next p.proxy(type) if type.defined?
150
- if !p.macros.empty?
151
- result = process_macro(p, api_map, name_pin.context, locals)
152
- next result unless result.return_type.undefined?
153
- elsif !p.directives.empty?
154
- result = process_directive(p, api_map, name_pin.context, locals)
155
- next result unless result.return_type.undefined?
156
- end
157
- p
158
- end
159
- logger.debug { "Call#inferred_pins(name_pin.binder=#{name_pin.binder}, word=#{word}, pins=#{pins.map(&:desc)}, name_pin=#{name_pin}) - result=#{result}" }
160
- out = result.map do |pin|
161
- if pin.path == 'Class#new' && name_pin.binder.tag != 'Class'
162
- reduced_context = name_pin.binder.reduce_class_type
163
- pin.proxy(reduced_context)
164
- else
165
- next pin if pin.return_type.undefined?
166
- selfy = pin.return_type.self_to_type(name_pin.binder)
167
- selfy == pin.return_type ? pin : pin.proxy(selfy)
168
- end
169
- end
170
- end
171
-
172
- # @param pin [Pin::Base]
173
- # @param api_map [ApiMap]
174
- # @param context [ComplexType]
175
- # @param locals [::Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>]
176
- # @return [Pin::Base]
177
- def process_macro pin, api_map, context, locals
178
- pin.macros.each do |macro|
179
- # @todo 'Wrong argument type for
180
- # Solargraph::Source::Chain::Call#inner_process_macro:
181
- # macro expected YARD::Tags::MacroDirective, received
182
- # generic<Elem>' is because we lose 'rooted' information
183
- # in the 'Chain::Array' class internally, leaving
184
- # ::Array#each shadowed when it shouldn't be.
185
- result = inner_process_macro(pin, macro, api_map, context, locals)
186
- return result unless result.return_type.undefined?
187
- end
188
- Pin::ProxyType.anonymous(ComplexType::UNDEFINED, source: :chain)
189
- end
190
-
191
- # @param pin [Pin::Method]
192
- # @param api_map [ApiMap]
193
- # @param context [ComplexType]
194
- # @param locals [::Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>]
195
- # @return [Pin::ProxyType]
196
- def process_directive pin, api_map, context, locals
197
- pin.directives.each do |dir|
198
- macro = api_map.named_macro(dir.tag.name)
199
- next if macro.nil?
200
- result = inner_process_macro(pin, macro, api_map, context, locals)
201
- return result unless result.return_type.undefined?
202
- end
203
- Pin::ProxyType.anonymous ComplexType::UNDEFINED, source: :chain
204
- end
205
-
206
- # @param pin [Pin::Base]
207
- # @param macro [YARD::Tags::MacroDirective]
208
- # @param api_map [ApiMap]
209
- # @param context [ComplexType]
210
- # @param locals [::Array<Pin::LocalVariable, Pin::Parameter>]
211
- # @return [Pin::ProxyType]
212
- def inner_process_macro pin, macro, api_map, context, locals
213
- vals = arguments.map{ |c| Pin::ProxyType.anonymous(c.infer(api_map, pin, locals), source: :chain) }
214
- txt = macro.tag.text.clone
215
- if txt.empty? && macro.tag.name
216
- named = api_map.named_macro(macro.tag.name)
217
- txt = named.tag.text.clone if named
218
- end
219
- i = 1
220
- vals.each do |v|
221
- txt.gsub!(/\$#{i}/, v.context.namespace)
222
- i += 1
223
- end
224
- docstring = Solargraph::Source.parse_docstring(txt).to_docstring
225
- tag = docstring.tag(:return)
226
- unless tag.nil? || tag.types.nil?
227
- return Pin::ProxyType.anonymous(ComplexType.try_parse(*tag.types), source: :chain)
228
- end
229
- Pin::ProxyType.anonymous(ComplexType::UNDEFINED, source: :chain)
230
- end
231
-
232
- # @param docstring [YARD::Docstring]
233
- # @param context [ComplexType]
234
- # @return [ComplexType, nil]
235
- def extra_return_type docstring, context
236
- if docstring.has_tag?('return_single_parameter') #&& context.subtypes.one?
237
- return context.subtypes.first || ComplexType::UNDEFINED
238
- elsif docstring.has_tag?('return_value_parameter') && context.value_types.one?
239
- return context.value_types.first
240
- end
241
- nil
242
- end
243
-
244
- # @param name_pin [Pin::Base]
245
- # @return [Pin::Method, nil]
246
- def find_method_pin(name_pin)
247
- method_pin = name_pin
248
- until method_pin.is_a?(Pin::Method)
249
- method_pin = method_pin.closure
250
- return if method_pin.nil?
251
- end
252
- method_pin
253
- end
254
-
255
- # @param api_map [ApiMap]
256
- # @param name_pin [Pin::Base]
257
- # @return [::Array<Pin::Base>]
258
- def super_pins api_map, name_pin
259
- method_pin = find_method_pin(name_pin)
260
- return [] if method_pin.nil?
261
- pins = api_map.get_method_stack(method_pin.namespace, method_pin.name, scope: method_pin.context.scope)
262
- pins.reject{|p| p.path == name_pin.path}
263
- end
264
-
265
- # @param api_map [ApiMap]
266
- # @param name_pin [Pin::Base]
267
- # @return [::Array<Pin::Base>]
268
- def yield_pins api_map, name_pin
269
- method_pin = find_method_pin(name_pin)
270
- return [] unless method_pin
271
-
272
- # @param signature_pin [Pin::Signature]
273
- method_pin.signatures.map(&:block).compact.map do |signature_pin|
274
- return_type = signature_pin.return_type.qualify(api_map, *name_pin.gates)
275
- signature_pin.proxy(return_type)
276
- end
277
- end
278
-
279
- # @param type [ComplexType]
280
- # @param context [ComplexType]
281
- # @return [ComplexType]
282
- def with_params type, context
283
- return type unless type.to_s.include?('$')
284
- ComplexType.try_parse(type.to_s.gsub('$', context.value_types.map(&:rooted_tag).join(', ')).gsub('<>', ''))
285
- end
286
-
287
- # @return [void]
288
- def fix_block_pass
289
- argument = @arguments.last&.links&.first
290
- @block = @arguments.pop if argument.is_a?(BlockSymbol) || argument.is_a?(BlockVariable)
291
- end
292
-
293
- # @param api_map [ApiMap]
294
- # @param context [ComplexType]
295
- # @param block_parameter_types [::Array<ComplexType>]
296
- # @param locals [::Array<Pin::LocalVariable>]
297
- # @return [ComplexType, nil]
298
- def block_symbol_call_type(api_map, context, block_parameter_types, locals)
299
- # Ruby's shorthand for sending the passed in method name
300
- # to the first yield parameter with no arguments
301
- block_symbol_name = block.links.first.word
302
- block_symbol_call_path = "#{block_parameter_types.first}##{block_symbol_name}"
303
- callee = api_map.get_path_pins(block_symbol_call_path).first
304
- return_type = callee&.return_type
305
- # @todo: Figure out why we get unresolved generics at
306
- # this point and need to assume method return types
307
- # based on the generic type
308
- return_type ||= api_map.get_path_pins("#{context.subtypes.first}##{block.links.first.word}").first&.return_type
309
- return_type || ComplexType::UNDEFINED
310
- end
311
-
312
- # @param api_map [ApiMap]
313
- # @return [Pin::Block, nil]
314
- def find_block_pin(api_map)
315
- node_location = Solargraph::Location.from_node(block.node)
316
- return if node_location.nil?
317
- block_pins = api_map.get_block_pins
318
- block_pins.find { |pin| pin.location.contain?(node_location) }
319
- end
320
-
321
- # @param api_map [ApiMap]
322
- # @param name_pin [Pin::Base]
323
- # @param block_parameter_types [::Array<ComplexType>]
324
- # @param locals [::Array<Pin::LocalVariable>]
325
- # @return [ComplexType, nil]
326
- def block_call_type(api_map, name_pin, locals)
327
- return nil unless with_block?
328
-
329
- block_context_pin = name_pin
330
- block_pin = find_block_pin(api_map)
331
- block_context_pin = block_pin.closure if block_pin
332
- block.infer(api_map, block_context_pin, locals)
333
- end
334
- end
335
- end
336
- end
337
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class Source
5
+ class Chain
6
+ #
7
+ # Handles both method calls and local variable references by
8
+ # first looking for a variable with the name 'word', then
9
+ # proceeding to method signature resolution if not found.
10
+ #
11
+ class Call < Chain::Link
12
+ include Solargraph::Parser::NodeMethods
13
+
14
+ # @return [String]
15
+ attr_reader :word
16
+
17
+ # @return [Location]
18
+ attr_reader :location
19
+
20
+ # @return [::Array<Chain>]
21
+ attr_reader :arguments
22
+
23
+ # @return [Chain, nil]
24
+ attr_reader :block
25
+
26
+ # @param word [String]
27
+ # @param location [Location, nil]
28
+ # @param arguments [::Array<Chain>]
29
+ # @param block [Chain, nil]
30
+ def initialize word, location = nil, arguments = [], block = nil
31
+ @word = word
32
+ @location = location
33
+ @arguments = arguments
34
+ @block = block
35
+ fix_block_pass
36
+ end
37
+
38
+ # @sg-ignore Fix "Not enough arguments to Module#protected"
39
+ protected def equality_fields
40
+ # @sg-ignore literal arrays in this module turn into ::Solargraph::Source::Chain::Array
41
+ super + [arguments, block]
42
+ end
43
+
44
+ def with_block?
45
+ !!@block
46
+ end
47
+
48
+ # @param api_map [ApiMap]
49
+ # @param name_pin [Pin::Closure] name_pin.binder should give us the type of the object on which 'word' will be invoked
50
+ # @param locals [::Array<Pin::LocalVariable>]
51
+ def resolve api_map, name_pin, locals
52
+ return super_pins(api_map, name_pin) if word == 'super'
53
+ return yield_pins(api_map, name_pin) if word == 'yield'
54
+ found = api_map.var_at_location(locals, word, name_pin, location) if head?
55
+
56
+ return inferred_pins([found], api_map, name_pin, locals) unless found.nil?
57
+ binder = name_pin.binder
58
+ # this is a q_call - i.e., foo&.bar - assume result of call
59
+ # will be nil or result as if binder were not nil -
60
+ # chain.rb#maybe_nil will add the nil type later, we just
61
+ # need to worry about the not-nil case
62
+
63
+ # @sg-ignore Need to handle duck-typed method calls on union types
64
+ binder = binder.without_nil if nullable?
65
+ # @sg-ignore Need to handle duck-typed method calls on union types
66
+ pin_groups = binder.each_unique_type.map do |context|
67
+ ns_tag = context.namespace == '' ? '' : context.namespace_type.tag
68
+ stack = api_map.get_method_stack(ns_tag, word, scope: context.scope)
69
+ [stack.first].compact
70
+ end
71
+ # @sg-ignore literal arrays in this module turn into ::Solargraph::Source::Chain::Array
72
+ if !api_map.loose_unions && pin_groups.any? { |pins| pins.empty? }
73
+ pin_groups = []
74
+ end
75
+ pins = pin_groups.flatten.uniq(&:path)
76
+ return [] if pins.empty?
77
+ inferred_pins(pins, api_map, name_pin, locals)
78
+ end
79
+
80
+ private
81
+
82
+ # @param pins [::Enumerable<Pin::Base>]
83
+ # @param api_map [ApiMap]
84
+ # @param name_pin [Pin::Base]
85
+ # @param locals [::Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>]
86
+ # @return [::Array<Pin::Base>]
87
+ def inferred_pins pins, api_map, name_pin, locals
88
+ result = pins.map do |p|
89
+ next p unless p.is_a?(Pin::Method)
90
+ overloads = p.signatures
91
+ # next p if overloads.empty?
92
+ type = ComplexType::UNDEFINED
93
+ # start with overloads that require blocks; if we are
94
+ # passing a block, we want to find a signature that will
95
+ # use it. If we didn't pass a block, the logic below will
96
+ # reject it regardless
97
+
98
+ with_block, without_block = overloads.partition(&:block?)
99
+ # @sg-ignore flow sensitive typing should handle is_a? and next
100
+ # @type Array<Pin::Signature>
101
+ sorted_overloads = with_block + without_block
102
+ # @type [Pin::Signature, nil]
103
+ new_signature_pin = nil
104
+ # @sg-ignore flow sensitive typing should handle is_a? and next
105
+ # @param ol [Pin::Signature]
106
+ sorted_overloads.each do |ol|
107
+ next unless ol.arity_matches?(arguments, with_block?)
108
+ match = true
109
+
110
+ atypes = []
111
+ arguments.each_with_index do |arg, idx|
112
+ param = ol.parameters[idx]
113
+ if param.nil?
114
+ match = ol.parameters.any?(&:restarg?)
115
+ break
116
+ end
117
+ arg_name_pin = Pin::ProxyType.anonymous(name_pin.context,
118
+ closure: name_pin.closure,
119
+ gates: name_pin.gates,
120
+ source: :chain)
121
+ atype = atypes[idx] ||= arg.infer(api_map, arg_name_pin, locals)
122
+ unless param.compatible_arg?(atype, api_map) || param.restarg?
123
+ match = false
124
+ break
125
+ end
126
+ end
127
+ if match
128
+ if ol.block && with_block?
129
+ block_atypes = ol.block.parameters.map(&:return_type)
130
+ # @todo Need to add nil check here
131
+ if block.links.map(&:class) == [BlockSymbol]
132
+ # like the bar in foo(&:bar)
133
+ blocktype = block_symbol_call_type(api_map, name_pin.context, block_atypes, locals)
134
+ else
135
+ blocktype = block_call_type(api_map, name_pin, locals)
136
+ end
137
+ end
138
+ # @type new_signature_pin [Pin::Signature]
139
+ new_signature_pin = ol.resolve_generics_from_context_until_complete(ol.generics, atypes, nil, nil, blocktype)
140
+ new_return_type = new_signature_pin.return_type
141
+ if head?
142
+ # If we're at the head of the chain, we called a
143
+ # method somewhere that marked itself as returning
144
+ # self. Given we didn't invoke this on an object,
145
+ # this must be a method in this same class - so we
146
+ # use our own self type
147
+ self_type = name_pin.context
148
+ else
149
+ # if we're past the head in the chain, whatever the
150
+ # type of the lhs side is what 'self' will be in its
151
+ # declaration - we can't just use the type of the
152
+ # method pin, as this might be a subclass of the
153
+ # place where the method is defined
154
+ self_type = name_pin.binder
155
+ end
156
+ # This same logic applies to the YARD work done by
157
+ # 'with_params()'.
158
+ #
159
+ # qualify(), however, happens in the namespace where
160
+ # the docs were written - from the method pin.
161
+ # @todo Need to add nil check here
162
+ type = with_params(new_return_type.self_to_type(self_type), self_type).qualify(api_map, *p.gates) if new_return_type.defined?
163
+ type ||= ComplexType::UNDEFINED
164
+ end
165
+ break if type.defined?
166
+ end
167
+ p = p.with_single_signature(new_signature_pin) unless new_signature_pin.nil?
168
+ next p.proxy(type) if type.defined?
169
+ if !p.macros.empty?
170
+ result = process_macro(p, api_map, name_pin.context, locals)
171
+ # @sg-ignore flow sensitive typing should be able to handle redefinition
172
+ next result unless result.return_type.undefined?
173
+ elsif !p.directives.empty?
174
+ result = process_directive(p, api_map, name_pin.context, locals)
175
+ # @sg-ignore flow sensitive typing should be able to handle redefinition
176
+ next result unless result.return_type.undefined?
177
+ end
178
+ p
179
+ end
180
+ logger.debug { "Call#inferred_pins(name_pin.binder=#{name_pin.binder}, word=#{word}, pins=#{pins.map(&:desc)}, name_pin=#{name_pin}) - result=#{result}" }
181
+ out = result.map do |pin|
182
+ if pin.path == 'Class#new' && name_pin.binder.tag != 'Class'
183
+ reduced_context = name_pin.binder.reduce_class_type
184
+ pin.proxy(reduced_context)
185
+ else
186
+ # @sg-ignore Need to add nil check here
187
+ next pin if pin.return_type.undefined?
188
+ # @sg-ignore Need to add nil check here
189
+ selfy = pin.return_type.self_to_type(name_pin.binder)
190
+ # @sg-ignore Need to add nil check here
191
+ selfy == pin.return_type ? pin : pin.proxy(selfy)
192
+ end
193
+ end
194
+ end
195
+
196
+ # @param pin [Pin::Base]
197
+ # @param api_map [ApiMap]
198
+ # @param context [ComplexType, ComplexType::UniqueType]
199
+ # @param locals [::Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>]
200
+ # @return [Pin::Base]
201
+ def process_macro pin, api_map, context, locals
202
+ pin.macros.each do |macro|
203
+ # @todo 'Wrong argument type for
204
+ # Solargraph::Source::Chain::Call#inner_process_macro:
205
+ # macro expected YARD::Tags::MacroDirective, received
206
+ # generic<Elem>' is because we lose 'rooted' information
207
+ # in the 'Chain::Array' class internally, leaving
208
+ # ::Array#each shadowed when it shouldn't be.
209
+ result = inner_process_macro(pin, macro, api_map, context, locals)
210
+ return result unless result.return_type.undefined?
211
+ end
212
+ Pin::ProxyType.anonymous(ComplexType::UNDEFINED, source: :chain)
213
+ end
214
+
215
+ # @param pin [Pin::Method]
216
+ # @param api_map [ApiMap]
217
+ # @param context [ComplexType, ComplexType::UniqueType]
218
+ # @param locals [::Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>]
219
+ # @return [Pin::ProxyType]
220
+ def process_directive pin, api_map, context, locals
221
+ pin.directives.each do |dir|
222
+ macro = api_map.named_macro(dir.tag.name)
223
+ next if macro.nil?
224
+ result = inner_process_macro(pin, macro, api_map, context, locals)
225
+ return result unless result.return_type.undefined?
226
+ end
227
+ Pin::ProxyType.anonymous ComplexType::UNDEFINED, source: :chain
228
+ end
229
+
230
+ # @param pin [Pin::Base]
231
+ # @param macro [YARD::Tags::MacroDirective]
232
+ # @param api_map [ApiMap]
233
+ # @param context [ComplexType, ComplexType::UniqueType]
234
+ # @param locals [::Array<Pin::LocalVariable, Pin::Parameter>]
235
+ # @return [Pin::ProxyType]
236
+ def inner_process_macro pin, macro, api_map, context, locals
237
+ vals = arguments.map{ |c| Pin::ProxyType.anonymous(c.infer(api_map, pin, locals), source: :chain) }
238
+ txt = macro.tag.text.clone
239
+ # @sg-ignore Need to add nil check here
240
+ if txt.empty? && macro.tag.name
241
+ named = api_map.named_macro(macro.tag.name)
242
+ txt = named.tag.text.clone if named
243
+ end
244
+ i = 1
245
+ vals.each do |v|
246
+ # @sg-ignore Need to add nil check here
247
+ txt.gsub!(/\$#{i}/, v.context.namespace)
248
+ i += 1
249
+ end
250
+ # @sg-ignore Need to add nil check here
251
+ docstring = Solargraph::Source.parse_docstring(txt).to_docstring
252
+ tag = docstring.tag(:return)
253
+ unless tag.nil? || tag.types.nil?
254
+ return Pin::ProxyType.anonymous(ComplexType.try_parse(*tag.types), source: :chain)
255
+ end
256
+ Pin::ProxyType.anonymous(ComplexType::UNDEFINED, source: :chain)
257
+ end
258
+
259
+ # @param docstring [YARD::Docstring]
260
+ # @param context [ComplexType]
261
+ # @return [ComplexType, nil]
262
+ def extra_return_type docstring, context
263
+ if docstring.has_tag?('return_single_parameter') #&& context.subtypes.one?
264
+ return context.subtypes.first || ComplexType::UNDEFINED
265
+ elsif docstring.has_tag?('return_value_parameter') && context.value_types.one?
266
+ return context.value_types.first
267
+ end
268
+ nil
269
+ end
270
+
271
+ # @param name_pin [Pin::Base]
272
+ # @return [Pin::Method, nil]
273
+ def find_method_pin(name_pin)
274
+ method_pin = name_pin
275
+ until method_pin.is_a?(Pin::Method)
276
+ # @sg-ignore Need to support this in flow sensitive typing
277
+ method_pin = method_pin.closure
278
+ return if method_pin.nil?
279
+ end
280
+ method_pin
281
+ end
282
+
283
+ # @param api_map [ApiMap]
284
+ # @param name_pin [Pin::Base]
285
+ # @return [::Array<Pin::Base>]
286
+ def super_pins api_map, name_pin
287
+ method_pin = find_method_pin(name_pin)
288
+ return [] if method_pin.nil?
289
+ pins = api_map.get_method_stack(method_pin.namespace, method_pin.name, scope: method_pin.context.scope)
290
+ pins.reject{|p| p.path == name_pin.path}
291
+ end
292
+
293
+ # @param api_map [ApiMap]
294
+ # @param name_pin [Pin::Base]
295
+ # @return [::Array<Pin::Base>]
296
+ def yield_pins api_map, name_pin
297
+ method_pin = find_method_pin(name_pin)
298
+ return [] unless method_pin
299
+
300
+ # @param signature_pin [Pin::Signature]
301
+ method_pin.signatures.map(&:block).compact.map do |signature_pin|
302
+ # @sg-ignore Need to add nil check here
303
+ return_type = signature_pin.return_type.qualify(api_map, *name_pin.gates)
304
+ signature_pin.proxy(return_type)
305
+ end
306
+ end
307
+
308
+ # @param type [ComplexType]
309
+ # @param context [ComplexType, ComplexType::UniqueType]
310
+ # @return [ComplexType]
311
+ def with_params type, context
312
+ return type unless type.to_s.include?('$')
313
+ ComplexType.try_parse(type.to_s.gsub('$', context.value_types.map(&:rooted_tag).join(', ')).gsub('<>', ''))
314
+ end
315
+
316
+ # @return [void]
317
+ def fix_block_pass
318
+ argument = @arguments.last&.links&.first
319
+ @block = @arguments.pop if argument.is_a?(BlockSymbol) || argument.is_a?(BlockVariable)
320
+ end
321
+
322
+ # @param api_map [ApiMap]
323
+ # @param context [ComplexType, ComplexType::UniqueType]
324
+ # @param block_parameter_types [::Array<ComplexType>]
325
+ # @param locals [::Array<Pin::LocalVariable>]
326
+ # @return [ComplexType, nil]
327
+ def block_symbol_call_type(api_map, context, block_parameter_types, locals)
328
+ # Ruby's shorthand for sending the passed in method name
329
+ # to the first yield parameter with no arguments
330
+ # @sg-ignore Need to add nil check here
331
+ block_symbol_name = block.links.first.word
332
+ block_symbol_call_path = "#{block_parameter_types.first}##{block_symbol_name}"
333
+ callee = api_map.get_path_pins(block_symbol_call_path).first
334
+ return_type = callee&.return_type
335
+ # @todo: Figure out why we get unresolved generics at
336
+ # this point and need to assume method return types
337
+ # based on the generic type
338
+ # @sg-ignore Need to add nil check here
339
+ return_type ||= api_map.get_path_pins("#{context.subtypes.first}##{block.links.first.word}").first&.return_type
340
+ return_type || ComplexType::UNDEFINED
341
+ end
342
+
343
+ # @param api_map [ApiMap]
344
+ # @return [Pin::Block, nil]
345
+ def find_block_pin(api_map)
346
+ # @sg-ignore Need to add nil check here
347
+ node_location = Solargraph::Location.from_node(block.node)
348
+ return if node_location.nil?
349
+ block_pins = api_map.get_block_pins
350
+ # @sg-ignore Need to add nil check here
351
+ block_pins.find { |pin| pin.location.contain?(node_location) }
352
+ end
353
+
354
+ # @param api_map [ApiMap]
355
+ # @param name_pin [Pin::Base]
356
+ # @param block_parameter_types [::Array<ComplexType>]
357
+ # @param locals [::Array<Pin::LocalVariable>]
358
+ # @return [ComplexType, nil]
359
+ def block_call_type(api_map, name_pin, locals)
360
+ return nil unless with_block?
361
+
362
+ block_pin = find_block_pin(api_map)
363
+ # We use the block pin as the closure, as the parameters
364
+ # here will only be defined inside the block itself and we
365
+ # need to be able to see them
366
+ # @sg-ignore Need to add nil check here
367
+ block.infer(api_map, block_pin, locals)
368
+ end
369
+ end
370
+ end
371
+ end
372
+ end