solargraph 0.47.2 → 0.54.0

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 (191) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/plugins.yml +40 -0
  4. data/.github/workflows/rspec.yml +4 -8
  5. data/.github/workflows/typecheck.yml +34 -0
  6. data/.yardopts +2 -2
  7. data/CHANGELOG.md +166 -3
  8. data/LICENSE +1 -1
  9. data/README.md +19 -16
  10. data/SPONSORS.md +2 -9
  11. data/lib/solargraph/api_map/cache.rb +50 -20
  12. data/lib/solargraph/api_map/source_to_yard.rb +17 -10
  13. data/lib/solargraph/api_map/store.rb +68 -15
  14. data/lib/solargraph/api_map.rb +238 -112
  15. data/lib/solargraph/bench.rb +3 -2
  16. data/lib/solargraph/cache.rb +77 -0
  17. data/lib/solargraph/complex_type/type_methods.rb +116 -35
  18. data/lib/solargraph/complex_type/unique_type.rb +261 -33
  19. data/lib/solargraph/complex_type.rb +149 -30
  20. data/lib/solargraph/convention/rakefile.rb +17 -0
  21. data/lib/solargraph/convention.rb +2 -3
  22. data/lib/solargraph/converters/dd.rb +5 -0
  23. data/lib/solargraph/converters/dl.rb +3 -0
  24. data/lib/solargraph/converters/dt.rb +3 -0
  25. data/lib/solargraph/diagnostics/rubocop.rb +23 -8
  26. data/lib/solargraph/diagnostics/rubocop_helpers.rb +4 -1
  27. data/lib/solargraph/diagnostics/type_check.rb +1 -0
  28. data/lib/solargraph/diagnostics.rb +2 -2
  29. data/lib/solargraph/doc_map.rb +187 -0
  30. data/lib/solargraph/gem_pins.rb +72 -0
  31. data/lib/solargraph/language_server/host/diagnoser.rb +2 -2
  32. data/lib/solargraph/language_server/host/dispatch.rb +22 -5
  33. data/lib/solargraph/language_server/host/message_worker.rb +4 -0
  34. data/lib/solargraph/language_server/host/sources.rb +8 -65
  35. data/lib/solargraph/language_server/host.rb +88 -93
  36. data/lib/solargraph/language_server/message/base.rb +1 -1
  37. data/lib/solargraph/language_server/message/completion_item/resolve.rb +3 -1
  38. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +13 -1
  39. data/lib/solargraph/language_server/message/extended/download_core.rb +1 -5
  40. data/lib/solargraph/language_server/message/initialize.rb +27 -0
  41. data/lib/solargraph/language_server/message/initialized.rb +1 -0
  42. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +4 -1
  43. data/lib/solargraph/language_server/message/text_document/formatting.rb +5 -4
  44. data/lib/solargraph/language_server/message/text_document/hover.rb +2 -0
  45. data/lib/solargraph/language_server/message/text_document/signature_help.rb +1 -6
  46. data/lib/solargraph/language_server/message/text_document/type_definition.rb +24 -0
  47. data/lib/solargraph/language_server/message/text_document.rb +1 -1
  48. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +5 -0
  49. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +10 -3
  50. data/lib/solargraph/language_server/message.rb +1 -0
  51. data/lib/solargraph/language_server/progress.rb +118 -0
  52. data/lib/solargraph/language_server/transport/adapter.rb +16 -1
  53. data/lib/solargraph/language_server/transport/data_reader.rb +2 -0
  54. data/lib/solargraph/language_server.rb +1 -0
  55. data/lib/solargraph/library.rb +231 -104
  56. data/lib/solargraph/location.rb +1 -0
  57. data/lib/solargraph/page.rb +6 -0
  58. data/lib/solargraph/parser/comment_ripper.rb +4 -0
  59. data/lib/solargraph/parser/node_methods.rb +47 -7
  60. data/lib/solargraph/parser/node_processor/base.rb +11 -1
  61. data/lib/solargraph/parser/node_processor.rb +1 -0
  62. data/lib/solargraph/parser/{legacy → parser_gem}/class_methods.rb +31 -9
  63. data/lib/solargraph/parser/{legacy → parser_gem}/flawed_builder.rb +3 -1
  64. data/lib/solargraph/parser/{legacy → parser_gem}/node_chainer.rb +57 -41
  65. data/lib/solargraph/parser/parser_gem/node_methods.rb +495 -0
  66. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/alias_node.rb +1 -1
  67. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +53 -0
  68. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/begin_node.rb +1 -1
  69. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/block_node.rb +3 -2
  70. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/casgn_node.rb +14 -4
  71. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/cvasgn_node.rb +1 -1
  72. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/def_node.rb +7 -20
  73. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/defs_node.rb +2 -2
  74. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/gvasgn_node.rb +1 -1
  75. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/ivasgn_node.rb +2 -2
  76. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/lvasgn_node.rb +2 -2
  77. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +47 -0
  78. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/namespace_node.rb +2 -2
  79. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/orasgn_node.rb +1 -1
  80. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/resbody_node.rb +3 -3
  81. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +42 -0
  82. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/send_node.rb +7 -5
  83. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/sym_node.rb +1 -1
  84. data/lib/solargraph/parser/parser_gem/node_processors.rb +56 -0
  85. data/lib/solargraph/parser/parser_gem.rb +12 -0
  86. data/lib/solargraph/parser/region.rb +1 -1
  87. data/lib/solargraph/parser/snippet.rb +2 -0
  88. data/lib/solargraph/parser.rb +9 -10
  89. data/lib/solargraph/pin/base.rb +69 -11
  90. data/lib/solargraph/pin/base_variable.rb +40 -7
  91. data/lib/solargraph/pin/block.rb +81 -33
  92. data/lib/solargraph/pin/closure.rb +17 -2
  93. data/lib/solargraph/pin/common.rb +7 -3
  94. data/lib/solargraph/pin/conversions.rb +34 -8
  95. data/lib/solargraph/pin/delegated_method.rb +101 -0
  96. data/lib/solargraph/pin/documenting.rb +25 -32
  97. data/lib/solargraph/pin/instance_variable.rb +4 -0
  98. data/lib/solargraph/pin/local_variable.rb +13 -1
  99. data/lib/solargraph/pin/method.rb +273 -17
  100. data/lib/solargraph/pin/namespace.rb +17 -1
  101. data/lib/solargraph/pin/parameter.rb +40 -28
  102. data/lib/solargraph/pin/reference/override.rb +2 -2
  103. data/lib/solargraph/pin/reference.rb +8 -0
  104. data/lib/solargraph/pin/search.rb +4 -4
  105. data/lib/solargraph/pin/signature.rb +143 -0
  106. data/lib/solargraph/pin.rb +2 -1
  107. data/lib/solargraph/range.rb +4 -6
  108. data/lib/solargraph/rbs_map/conversions.rb +607 -0
  109. data/lib/solargraph/rbs_map/core_fills.rb +50 -0
  110. data/lib/solargraph/rbs_map/core_map.rb +28 -0
  111. data/lib/solargraph/rbs_map/stdlib_map.rb +33 -0
  112. data/lib/solargraph/rbs_map.rb +92 -0
  113. data/lib/solargraph/shell.rb +85 -59
  114. data/lib/solargraph/source/chain/array.rb +32 -0
  115. data/lib/solargraph/source/chain/block_symbol.rb +13 -0
  116. data/lib/solargraph/source/chain/call.rb +125 -61
  117. data/lib/solargraph/source/chain/constant.rb +15 -1
  118. data/lib/solargraph/source/chain/if.rb +23 -0
  119. data/lib/solargraph/source/chain/link.rb +8 -2
  120. data/lib/solargraph/source/chain/or.rb +1 -1
  121. data/lib/solargraph/source/chain/z_super.rb +3 -3
  122. data/lib/solargraph/source/chain.rb +64 -14
  123. data/lib/solargraph/source/change.rb +3 -0
  124. data/lib/solargraph/source/cursor.rb +2 -0
  125. data/lib/solargraph/source/source_chainer.rb +8 -5
  126. data/lib/solargraph/source/updater.rb +1 -0
  127. data/lib/solargraph/source.rb +18 -63
  128. data/lib/solargraph/source_map/clip.rb +31 -23
  129. data/lib/solargraph/source_map/mapper.rb +23 -7
  130. data/lib/solargraph/source_map.rb +36 -11
  131. data/lib/solargraph/type_checker/checks.rb +10 -2
  132. data/lib/solargraph/type_checker.rb +229 -100
  133. data/lib/solargraph/version.rb +1 -1
  134. data/lib/solargraph/views/environment.erb +2 -2
  135. data/lib/solargraph/workspace/config.rb +15 -11
  136. data/lib/solargraph/workspace.rb +41 -17
  137. data/lib/solargraph/yard_map/cache.rb +6 -0
  138. data/lib/solargraph/yard_map/helpers.rb +1 -1
  139. data/lib/solargraph/yard_map/mapper/to_method.rb +23 -7
  140. data/lib/solargraph/yard_map/mapper.rb +1 -1
  141. data/lib/solargraph/yard_map/to_method.rb +11 -4
  142. data/lib/solargraph/yard_map.rb +1 -443
  143. data/lib/solargraph/yard_tags.rb +20 -0
  144. data/lib/solargraph/yardoc.rb +52 -0
  145. data/lib/solargraph.rb +8 -6
  146. data/solargraph.gemspec +19 -8
  147. metadata +164 -99
  148. data/.travis.yml +0 -19
  149. data/lib/solargraph/api_map/bundler_methods.rb +0 -22
  150. data/lib/solargraph/compat.rb +0 -37
  151. data/lib/solargraph/convention/rspec.rb +0 -30
  152. data/lib/solargraph/documentor.rb +0 -76
  153. data/lib/solargraph/language_server/host/cataloger.rb +0 -56
  154. data/lib/solargraph/parser/legacy/node_methods.rb +0 -325
  155. data/lib/solargraph/parser/legacy/node_processors/alias_node.rb +0 -23
  156. data/lib/solargraph/parser/legacy/node_processors/args_node.rb +0 -35
  157. data/lib/solargraph/parser/legacy/node_processors/begin_node.rb +0 -15
  158. data/lib/solargraph/parser/legacy/node_processors/cvasgn_node.rb +0 -23
  159. data/lib/solargraph/parser/legacy/node_processors/def_node.rb +0 -63
  160. data/lib/solargraph/parser/legacy/node_processors/sclass_node.rb +0 -21
  161. data/lib/solargraph/parser/legacy/node_processors.rb +0 -54
  162. data/lib/solargraph/parser/legacy.rb +0 -12
  163. data/lib/solargraph/parser/rubyvm/class_methods.rb +0 -144
  164. data/lib/solargraph/parser/rubyvm/node_chainer.rb +0 -160
  165. data/lib/solargraph/parser/rubyvm/node_methods.rb +0 -315
  166. data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +0 -85
  167. data/lib/solargraph/parser/rubyvm/node_processors/block_node.rb +0 -42
  168. data/lib/solargraph/parser/rubyvm/node_processors/casgn_node.rb +0 -22
  169. data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +0 -57
  170. data/lib/solargraph/parser/rubyvm/node_processors/gvasgn_node.rb +0 -23
  171. data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +0 -38
  172. data/lib/solargraph/parser/rubyvm/node_processors/kw_arg_node.rb +0 -39
  173. data/lib/solargraph/parser/rubyvm/node_processors/lit_node.rb +0 -20
  174. data/lib/solargraph/parser/rubyvm/node_processors/lvasgn_node.rb +0 -27
  175. data/lib/solargraph/parser/rubyvm/node_processors/namespace_node.rb +0 -39
  176. data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +0 -26
  177. data/lib/solargraph/parser/rubyvm/node_processors/orasgn_node.rb +0 -15
  178. data/lib/solargraph/parser/rubyvm/node_processors/resbody_node.rb +0 -45
  179. data/lib/solargraph/parser/rubyvm/node_processors/sclass_node.rb +0 -21
  180. data/lib/solargraph/parser/rubyvm/node_processors/scope_node.rb +0 -15
  181. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +0 -277
  182. data/lib/solargraph/parser/rubyvm/node_processors/sym_node.rb +0 -18
  183. data/lib/solargraph/parser/rubyvm/node_processors.rb +0 -63
  184. data/lib/solargraph/parser/rubyvm.rb +0 -40
  185. data/lib/solargraph/yard_map/core_docs.rb +0 -170
  186. data/lib/solargraph/yard_map/core_fills.rb +0 -208
  187. data/lib/solargraph/yard_map/core_gen.rb +0 -76
  188. data/lib/solargraph/yard_map/rdoc_to_yard.rb +0 -140
  189. data/lib/solargraph/yard_map/stdlib_fills.rb +0 -43
  190. data/lib/yard-solargraph.rb +0 -33
  191. data/yardoc/2.2.2.tar.gz +0 -0
@@ -22,7 +22,7 @@ module Solargraph
22
22
  attr_reader :api_map
23
23
 
24
24
  # @param filename [String]
25
- # @param api_map [ApiMap]
25
+ # @param api_map [ApiMap, nil]
26
26
  # @param level [Symbol]
27
27
  def initialize filename, api_map: nil, level: :normal
28
28
  @filename = filename
@@ -51,6 +51,7 @@ module Solargraph
51
51
 
52
52
  class << self
53
53
  # @param filename [String]
54
+ # @param level [Symbol]
54
55
  # @return [self]
55
56
  def load filename, level = :normal
56
57
  source = Solargraph::Source.load(filename)
@@ -61,6 +62,7 @@ module Solargraph
61
62
 
62
63
  # @param code [String]
63
64
  # @param filename [String, nil]
65
+ # @param level [Symbol]
64
66
  # @return [self]
65
67
  def load_string code, filename = nil, level = :normal
66
68
  source = Solargraph::Source.load_string(code, filename)
@@ -91,7 +93,12 @@ module Solargraph
91
93
  declared = pin.typify(api_map).self_to(pin.full_context.namespace)
92
94
  if declared.undefined?
93
95
  if pin.return_type.undefined? && rules.require_type_tags?
94
- result.push Problem.new(pin.location, "Missing @return tag for #{pin.path}", pin: pin)
96
+ if pin.attribute?
97
+ inferred = pin.probe(api_map).self_to(pin.full_context.namespace)
98
+ result.push Problem.new(pin.location, "Missing @return tag for #{pin.path}", pin: pin) unless inferred.defined?
99
+ else
100
+ result.push Problem.new(pin.location, "Missing @return tag for #{pin.path}", pin: pin)
101
+ end
95
102
  elsif pin.return_type.defined? && !resolved_constant?(pin)
96
103
  result.push Problem.new(pin.location, "Unresolved return type #{pin.return_type} for #{pin.path}", pin: pin)
97
104
  elsif rules.must_tag_or_infer? && pin.probe(api_map).undefined?
@@ -120,14 +127,17 @@ module Solargraph
120
127
  # @param pin [Pin::Base]
121
128
  # @return [Boolean]
122
129
  def resolved_constant? pin
123
- api_map.get_constants('', pin.binder.tag)
124
- .select { |p| p.name == pin.return_type.namespace }
125
- .any? do |p|
126
- inferred = p.infer(api_map)
127
- ['Class', 'Module'].include?(inferred.name)
128
- end
130
+ return true if pin.typify(api_map).defined?
131
+ constant_pins = api_map.get_constants('', *pin.closure.gates)
132
+ .select { |p| p.name == pin.return_type.namespace }
133
+ return true if constant_pins.find { |p| p.typify(api_map).defined? }
134
+ # will need to probe when a constant name is assigned to a
135
+ # class/module (alias)
136
+ return true if constant_pins.find { |p| p.probe(api_map).defined? }
137
+ false
129
138
  end
130
139
 
140
+ # @param pin [Pin::Base]
131
141
  def virtual_pin? pin
132
142
  pin.location && source_map.source.comment_at?(pin.location.range.ending)
133
143
  end
@@ -139,14 +149,27 @@ module Solargraph
139
149
  params = first_param_hash(stack)
140
150
  result = []
141
151
  if rules.require_type_tags?
142
- pin.parameters.each do |par|
143
- break if par.decl == :restarg || par.decl == :kwrestarg || par.decl == :blockarg
144
- unless params[par.name]
145
- result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin)
152
+ pin.signatures.each do |sig|
153
+ sig.parameters.each do |par|
154
+ break if par.decl == :restarg || par.decl == :kwrestarg || par.decl == :blockarg
155
+ unless params[par.name]
156
+ if pin.attribute?
157
+ inferred = pin.probe(api_map).self_to(pin.full_context.namespace)
158
+ if inferred.undefined?
159
+ result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin)
160
+ end
161
+ else
162
+ result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin)
163
+ end
164
+ end
146
165
  end
147
166
  end
148
167
  end
168
+ # @todo Should be able to probe type of name and data here
169
+ # @param name [String]
170
+ # @param data [Hash{Symbol => BasicObject}]
149
171
  params.each_pair do |name, data|
172
+ # @type [ComplexType]
150
173
  type = data[:qualified]
151
174
  if type.undefined?
152
175
  result.push Problem.new(pin.location, "Unresolved type #{data[:tagged]} for #{name} param on #{pin.path}", pin: pin)
@@ -155,6 +178,7 @@ module Solargraph
155
178
  result
156
179
  end
157
180
 
181
+ # @return [Array<Pin::Base>]
158
182
  def ignored_pins
159
183
  @ignored_pins ||= []
160
184
  end
@@ -184,7 +208,7 @@ module Solargraph
184
208
  elsif declared_externally?(pin)
185
209
  ignored_pins.push pin
186
210
  end
187
- elsif !pin.is_a?(Pin::Parameter)
211
+ elsif !pin.is_a?(Pin::Parameter) && !resolved_constant?(pin)
188
212
  result.push Problem.new(pin.location, "Unresolved type #{pin.return_type} for variable #{pin.name}", pin: pin)
189
213
  end
190
214
  else
@@ -202,6 +226,7 @@ module Solargraph
202
226
  source_map.pins_by_class(Pin::BaseVariable) + source_map.locals.select { |pin| pin.is_a?(Pin::LocalVariable) }
203
227
  end
204
228
 
229
+ # @return [Array<Problem>]
205
230
  def const_problems
206
231
  return [] unless rules.validate_consts?
207
232
  result = []
@@ -220,6 +245,7 @@ module Solargraph
220
245
  result
221
246
  end
222
247
 
248
+ # @return [Array<Problem>]
223
249
  def call_problems
224
250
  result = []
225
251
  Solargraph::Parser::NodeMethods.call_nodes_from(source_map.source.node).each do |call|
@@ -242,8 +268,9 @@ module Solargraph
242
268
  base = base.base
243
269
  end
244
270
  closest = found.typify(api_map) if found
271
+ # @todo remove the internal_or_core? check at a higher-than-strict level
245
272
  if !found || found.is_a?(Pin::BaseVariable) || (closest.defined? && internal_or_core?(found))
246
- unless ignored_pins.include?(found)
273
+ unless closest.generic? || ignored_pins.include?(found)
247
274
  result.push Problem.new(location, "Unresolved call to #{missing.links.last.word}")
248
275
  @marked_ranges.push rng
249
276
  end
@@ -254,88 +281,178 @@ module Solargraph
254
281
  result
255
282
  end
256
283
 
284
+ # @param chain [Solargraph::Source::Chain]
285
+ # @param api_map [Solargraph::ApiMap]
286
+ # @param block_pin [Solargraph::Pin::Base]
287
+ # @param locals [Array<Solargraph::Pin::Base>]
288
+ # @param location [Solargraph::Location]
289
+ # @return [Array<Problem>]
257
290
  def argument_problems_for chain, api_map, block_pin, locals, location
258
291
  result = []
259
292
  base = chain
260
293
  until base.links.length == 1 && base.undefined?
294
+ last_base_link = base.links.last
295
+ break unless last_base_link.is_a?(Solargraph::Source::Chain::Call)
296
+
297
+ arguments = last_base_link.arguments
298
+
261
299
  pins = base.define(api_map, block_pin, locals)
262
- if pins.first.is_a?(Pin::Method)
300
+
301
+ first_pin = pins.first
302
+ if first_pin.is_a?(Pin::DelegatedMethod) && !first_pin.resolvable?(api_map)
303
+ # Do nothing, as we can't find the actual method implementation
304
+ elsif first_pin.is_a?(Pin::Method)
263
305
  # @type [Pin::Method]
264
- pin = pins.first
306
+ pin = first_pin
265
307
  ap = if base.links.last.is_a?(Solargraph::Source::Chain::ZSuper)
266
308
  arity_problems_for(pin, fake_args_for(block_pin), location)
309
+ elsif pin.path == 'Class#new'
310
+ fqns = if base.links.one?
311
+ block_pin.namespace
312
+ else
313
+ base.base.infer(api_map, block_pin, locals).namespace
314
+ end
315
+ init = api_map.get_method_stack(fqns, 'initialize').first
316
+ init ? arity_problems_for(init, arguments, location) : []
267
317
  else
268
- arity_problems_for(pin, base.links.last.arguments, location)
318
+ arity_problems_for(pin, arguments, location)
269
319
  end
270
320
  unless ap.empty?
271
321
  result.concat ap
272
322
  break
273
323
  end
274
- break unless rules.validate_calls?
324
+ break if !rules.validate_calls? || base.links.first.is_a?(Solargraph::Source::Chain::ZSuper)
325
+
275
326
  params = first_param_hash(pins)
276
- pin.parameters.each_with_index do |par, idx|
277
- argchain = base.links.last.arguments[idx]
278
- if argchain.nil? && par.decl == :arg
279
- result.push Problem.new(location, "Not enough arguments to #{pin.path}")
280
- break
281
- end
282
- if argchain
283
- if par.decl != :arg
284
- result.concat kwarg_problems_for argchain, api_map, block_pin, locals, location, pin, params, idx
285
- break
286
- else
287
- ptype = params.key?(par.name) ? params[par.name][:qualified] : ComplexType::UNDEFINED
288
- if ptype.nil?
289
- # @todo Some level (strong, I guess) should require the param here
327
+
328
+ all_errors = []
329
+ pin.signatures.sort { |sig| sig.parameters.length }.each do |sig|
330
+ errors = []
331
+ sig.parameters.each_with_index do |par, idx|
332
+ # @todo add logic mapping up restarg parameters with
333
+ # arguments (including restarg arguments). Use tuples
334
+ # when possible, and when not, ensure provably
335
+ # incorrect situations are detected.
336
+ break if par.decl == :restarg # bail out pending better arg processing
337
+ argchain = arguments[idx]
338
+ if argchain.nil?
339
+ if par.decl == :arg
340
+ final_arg = arguments.last
341
+ if final_arg && final_arg.node.type == :splat
342
+ argchain = final_arg
343
+ next # don't try to apply the type of the splat - unlikely to be specific enough
344
+ else
345
+ errors.push Problem.new(location, "Not enough arguments to #{pin.path}")
346
+ next
347
+ end
348
+ else
349
+ final_arg = arguments.last
350
+ argchain = final_arg if final_arg && [:kwsplat, :hash].include?(final_arg.node.type)
351
+ end
352
+ end
353
+ if argchain
354
+ if par.decl != :arg
355
+ errors.concat kwarg_problems_for sig, argchain, api_map, block_pin, locals, location, pin, params, idx
356
+ next
290
357
  else
291
- argtype = argchain.infer(api_map, block_pin, locals)
292
- if argtype.defined? && ptype.defined? && !any_types_match?(api_map, ptype, argtype)
293
- result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}")
358
+ if argchain.node.type == :splat && argchain == arguments.last
359
+ final_arg = argchain
360
+ end
361
+ if (final_arg && final_arg.node.type == :splat)
362
+ # The final argument given has been seen and was a
363
+ # splat, which doesn't give us useful types or
364
+ # arities against positional parameters, so let's
365
+ # continue on in case there are any required
366
+ # kwargs we should warn about
367
+ next
368
+ end
369
+
370
+ if argchain.node.type == :splat && par != sig.parameters.last
371
+ # we have been given a splat and there are more
372
+ # arguments to come.
373
+
374
+ # @todo Improve this so that we can skip past the
375
+ # rest of the positional parameters here but still
376
+ # process the kwargs
377
+ break
378
+ end
379
+ ptype = params.key?(par.name) ? params[par.name][:qualified] : ComplexType::UNDEFINED
380
+ ptype = ptype.self_to(par.context.namespace)
381
+ if ptype.nil?
382
+ # @todo Some level (strong, I guess) should require the param here
383
+ else
384
+ argtype = argchain.infer(api_map, block_pin, locals)
385
+ if argtype.defined? && ptype.defined? && !any_types_match?(api_map, ptype, argtype)
386
+ errors.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}")
387
+ next
388
+ end
294
389
  end
295
390
  end
391
+ elsif par.decl == :kwarg
392
+ errors.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}")
393
+ next
296
394
  end
297
- elsif par.rest?
298
- next
299
- elsif par.decl == :kwarg
300
- result.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}")
395
+ end
396
+ if errors.empty?
397
+ all_errors.clear
301
398
  break
302
399
  end
400
+ all_errors.concat errors
303
401
  end
402
+ result.concat all_errors
304
403
  end
305
404
  base = base.base
306
405
  end
307
406
  result
308
407
  end
309
408
 
310
- def kwarg_problems_for argchain, api_map, block_pin, locals, location, pin, params, first
409
+ # @param sig [Pin::Signature]
410
+ # @param argchain [Source::Chain]
411
+ # @param api_map [ApiMap]
412
+ # @param block_pin [Pin::Block]
413
+ # @param locals [Array<Pin::LocalVariable>]
414
+ # @param location [Location]
415
+ # @param pin [Pin::Method]
416
+ # @param params [Hash{String => [nil, Hash]}]
417
+ # @param idx [Integer]
418
+ #
419
+ # @return [Array<Problem>]
420
+ def kwarg_problems_for sig, argchain, api_map, block_pin, locals, location, pin, params, idx
311
421
  result = []
312
422
  kwargs = convert_hash(argchain.node)
313
- pin.parameters[first..-1].each_with_index do |par, cur|
314
- idx = first + cur
315
- argchain = kwargs[par.name.to_sym]
316
- if par.decl == :kwrestarg || (par.decl == :optarg && idx == pin.parameters.length - 1 && par.asgn_code == '{}')
317
- result.concat kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kwargs)
318
- else
319
- if argchain
320
- data = params[par.name]
321
- if data.nil?
322
- # @todo Some level (strong, I guess) should require the param here
323
- else
324
- ptype = data[:qualified]
325
- next if ptype.undefined?
423
+ par = sig.parameters[idx]
424
+ argchain = kwargs[par.name.to_sym]
425
+ if par.decl == :kwrestarg || (par.decl == :optarg && idx == pin.parameters.length - 1 && par.asgn_code == '{}')
426
+ result.concat kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kwargs)
427
+ else
428
+ if argchain
429
+ data = params[par.name]
430
+ if data.nil?
431
+ # @todo Some level (strong, I guess) should require the param here
432
+ else
433
+ ptype = data[:qualified]
434
+ unless ptype.undefined?
326
435
  argtype = argchain.infer(api_map, block_pin, locals)
327
436
  if argtype.defined? && ptype && !any_types_match?(api_map, ptype, argtype)
328
437
  result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}")
329
438
  end
330
439
  end
331
- elsif par.decl == :kwarg
332
- result.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}")
333
440
  end
441
+ elsif par.decl == :kwarg
442
+ result.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}")
334
443
  end
335
444
  end
336
445
  result
337
446
  end
338
447
 
448
+ # @param api_map [ApiMap]
449
+ # @param block_pin [Pin::Block]
450
+ # @param locals [Array<Pin::LocalVariable>]
451
+ # @param location [Location]
452
+ # @param pin [Pin::Method]
453
+ # @param params [Hash{String => [nil, Hash]}]
454
+ # @param kwargs [Hash{Symbol => Source::Chain}]
455
+ # @return [Array<Problem>]
339
456
  def kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kwargs)
340
457
  result = []
341
458
  kwargs.each_pair do |pname, argchain|
@@ -349,8 +466,8 @@ module Solargraph
349
466
  result
350
467
  end
351
468
 
352
- # @param [Pin::Method]
353
- # @return [Hash]
469
+ # @param pin [Pin::Method]
470
+ # @return [Hash{String => Hash{Symbol => BaseObject}}]
354
471
  def param_hash(pin)
355
472
  tags = pin.docstring.tags(:param)
356
473
  return {} if tags.empty?
@@ -365,10 +482,12 @@ module Solargraph
365
482
  result
366
483
  end
367
484
 
368
- # @param [Array<Pin::Method>]
369
- # @return [Hash]
485
+ # @param pins [Array<Pin::Method>]
486
+ # @return [Hash{String => Hash{Symbol => BasicObject}}]
370
487
  def first_param_hash(pins)
371
488
  pins.each do |pin|
489
+ # @todo this assignment from parametric use of Hash should not lose its generic
490
+ # @type [Hash{String => Hash{Symbol => BasicObject}}]
372
491
  result = param_hash(pin)
373
492
  return result unless result.empty?
374
493
  end
@@ -381,8 +500,11 @@ module Solargraph
381
500
  pin.location && api_map.bundled?(pin.location.filename)
382
501
  end
383
502
 
503
+ # True if the pin is either internal (part of the workspace) or from the core/stdlib
504
+ # @param pin [Pin::Base]
384
505
  def internal_or_core? pin
385
- internal?(pin) || api_map.yard_map.core_pins.include?(pin) || api_map.yard_map.stdlib_pins.include?(pin)
506
+ # @todo RBS pins are not necessarily core/stdlib pins
507
+ internal?(pin) || pin.source == :rbs
386
508
  end
387
509
 
388
510
  # @param pin [Pin::Base]
@@ -390,6 +512,7 @@ module Solargraph
390
512
  !internal? pin
391
513
  end
392
514
 
515
+ # @param pin [Pin::Base]
393
516
  def declared_externally? pin
394
517
  return true if pin.assignment.nil?
395
518
  chain = Solargraph::Parser.chain(pin.assignment, filename)
@@ -418,41 +541,44 @@ module Solargraph
418
541
  end
419
542
 
420
543
  # @param pin [Pin::Method]
421
- def arity_problems_for(pin, arguments, location)
422
- ([pin] + pin.overloads).map do |p|
423
- result = pin_arity_problems_for(p, arguments, location)
424
- return [] if result.empty?
425
- result
426
- end.flatten.uniq(&:message)
544
+ # @param arguments [Array<Source::Chain>]
545
+ # @param location [Location]
546
+ # @return [Array<Problem>]
547
+ def arity_problems_for pin, arguments, location
548
+ results = pin.signatures.map do |sig|
549
+ r = parameterized_arity_problems_for(pin, sig.parameters, arguments, location)
550
+ return [] if r.empty?
551
+ r
552
+ end
553
+ results.first
427
554
  end
428
555
 
429
556
  # @param pin [Pin::Method]
430
- def pin_arity_problems_for(pin, arguments, location)
557
+ # @param parameters [Array<Pin::Parameter>]
558
+ # @param arguments [Array<Source::Chain>]
559
+ # @param location [Location]
560
+ # @return [Array<Problem>]
561
+ def parameterized_arity_problems_for(pin, parameters, arguments, location)
431
562
  return [] unless pin.explicit?
432
- return [] if pin.parameters.empty? && arguments.empty?
433
- if pin.parameters.empty?
434
- # Functions tagged param_tuple accepts two arguments (e.g., Hash#[]=)
435
- return [] if pin.docstring.tag(:param_tuple) && arguments.length == 2
436
- return [] if arguments.length == 1 && arguments.last.links.last.is_a?(Source::Chain::BlockVariable)
437
- return [Problem.new(location, "Too many arguments to #{pin.path}")]
438
- end
563
+ return [] if parameters.empty? && arguments.empty?
564
+ return [] if pin.anon_splat?
439
565
  unchecked = arguments.clone
440
566
  add_params = 0
441
- if unchecked.empty? && pin.parameters.any? { |param| param.decl == :kwarg }
567
+ if unchecked.empty? && parameters.any? { |param| param.decl == :kwarg }
442
568
  return [Problem.new(location, "Missing keyword arguments to #{pin.path}")]
443
569
  end
444
570
  settled_kwargs = 0
445
571
  unless unchecked.empty?
446
572
  if any_splatted_call?(unchecked.map(&:node))
447
- settled_kwargs = pin.parameters.count(&:keyword?)
573
+ settled_kwargs = parameters.count(&:keyword?)
448
574
  else
449
575
  kwargs = convert_hash(unchecked.last.node)
450
- if pin.parameters.any? { |param| [:kwarg, :kwoptarg].include?(param.decl) || param.kwrestarg? }
576
+ if parameters.any? { |param| [:kwarg, :kwoptarg].include?(param.decl) || param.kwrestarg? }
451
577
  if kwargs.empty?
452
578
  add_params += 1
453
579
  else
454
580
  unchecked.pop
455
- pin.parameters.each do |param|
581
+ parameters.each do |param|
456
582
  next unless param.keyword?
457
583
  if kwargs.key?(param.name.to_sym)
458
584
  kwargs.delete param.name.to_sym
@@ -462,7 +588,7 @@ module Solargraph
462
588
  return [Problem.new(location, "Missing keyword argument #{param.name} to #{pin.path}")]
463
589
  end
464
590
  end
465
- kwargs.clear if pin.parameters.any?(&:kwrestarg?)
591
+ kwargs.clear if parameters.any?(&:kwrestarg?)
466
592
  unless kwargs.empty?
467
593
  return [Problem.new(location, "Unrecognized keyword argument #{kwargs.keys.first} to #{pin.path}")]
468
594
  end
@@ -470,18 +596,15 @@ module Solargraph
470
596
  end
471
597
  end
472
598
  end
473
- req = required_param_count(pin)
599
+ req = required_param_count(parameters)
474
600
  if req + add_params < unchecked.length
475
- return [] if pin.parameters.any?(&:rest?)
476
- opt = optional_param_count(pin)
601
+ return [] if parameters.any?(&:rest?)
602
+ opt = optional_param_count(parameters)
477
603
  return [] if unchecked.length <= req + opt
478
- if unchecked.length == req + opt + 1 && unchecked.last.links.last.is_a?(Source::Chain::BlockVariable)
604
+ if req + add_params + 1 == unchecked.length && any_splatted_call?(unchecked.map(&:node)) && (parameters.map(&:decl) & [:kwarg, :kwoptarg, :kwrestarg]).any?
479
605
  return []
480
606
  end
481
- if req + add_params + 1 == unchecked.length && any_splatted_call?(unchecked.map(&:node)) && (pin.parameters.map(&:decl) & [:kwarg, :kwoptarg, :kwrestarg]).any?
482
- return []
483
- end
484
- return [] if arguments.length - req == pin.parameters.select { |p| [:optarg, :kwoptarg].include?(p.decl) }.length
607
+ return [] if arguments.length - req == parameters.select { |p| [:optarg, :kwoptarg].include?(p.decl) }.length
485
608
  return [Problem.new(location, "Too many arguments to #{pin.path}")]
486
609
  elsif unchecked.length < req - settled_kwargs && (arguments.empty? || (!arguments.last.splat? && !arguments.last.links.last.is_a?(Solargraph::Source::Chain::Hash)))
487
610
  # HACK: Kernel#raise signature is incorrect in Ruby 2.7 core docs.
@@ -493,26 +616,30 @@ module Solargraph
493
616
  []
494
617
  end
495
618
 
496
- # @param pin [Pin::Method]
497
- def required_param_count(pin)
498
- pin.parameters.sum { |param| %i[arg kwarg].include?(param.decl) ? 1 : 0 }
619
+ # @param parameters [Enumerable<Pin::Parameter>]
620
+ # @todo need to use generic types in method to choose correct
621
+ # signature and generate Integer as return type
622
+ # @sg-ignore
623
+ # @return [Integer]
624
+ def required_param_count(parameters)
625
+ parameters.sum { |param| %i[arg kwarg].include?(param.decl) ? 1 : 0 }
499
626
  end
500
627
 
628
+ # @param parameters [Enumerable<Pin::Parameter>]
501
629
  # @param pin [Pin::Method]
502
- def optional_param_count(pin)
503
- count = 0
504
- pin.parameters.each do |param|
505
- next unless param.decl == :optarg
506
- count += 1
507
- end
508
- count
630
+ # @return [Integer]
631
+ def optional_param_count(parameters)
632
+ parameters.select { |p| p.decl == :optarg }.length
509
633
  end
510
634
 
635
+ # @param pin [Pin::Method]
511
636
  def abstract? pin
512
- pin.docstring.has_tag?(:abstract) ||
513
- (pin.closure && pin.closure.docstring.has_tag?(:abstract))
637
+ pin.docstring.has_tag?('abstract') ||
638
+ (pin.closure && pin.closure.docstring.has_tag?('abstract'))
514
639
  end
515
640
 
641
+ # @param pin [Pin::Base]
642
+ # @return [Array<Source::Chain>]
516
643
  def fake_args_for(pin)
517
644
  args = []
518
645
  with_opts = false
@@ -533,10 +660,12 @@ module Solargraph
533
660
  args
534
661
  end
535
662
 
663
+ # @param problems [Array<Problem>]
664
+ # @return [Array<Problem>]
536
665
  def without_ignored problems
537
666
  problems.reject do |problem|
538
667
  node = source_map.source.node_at(problem.location.range.start.line, problem.location.range.start.column)
539
- source_map.source.comments_for(node)&.include?('@sg-ignore')
668
+ node && source_map.source.comments_for(node)&.include?('@sg-ignore')
540
669
  end
541
670
  end
542
671
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- VERSION = '0.47.2'
4
+ VERSION = '0.54.0'
5
5
  end
@@ -30,10 +30,10 @@
30
30
  Solargraph Version: <%= Solargraph::VERSION %>
31
31
  </li>
32
32
  <li>
33
- Core Documentation Version: <%= Solargraph::YardMap::CoreDocs.best_match %>
33
+ Core Documentation Version: N/A <%# @todo Fix %>
34
34
  </li>
35
35
  <li>
36
- Core Cache Directory: <%= Solargraph::YardMap::CoreDocs.cache_dir %>
36
+ Core Cache Directory: N/A <%# @todo Fix %>
37
37
  </li>
38
38
  <% unless Solargraph::Parser.rubyvm? %>
39
39
  <li>
@@ -14,12 +14,13 @@ module Solargraph
14
14
  # @return [String]
15
15
  attr_reader :directory
16
16
 
17
- # @return [Hash]
17
+ # @todo To make this strongly typed we'll need a record syntax
18
+ # @return [Hash{String => undefined}]
18
19
  attr_reader :raw_data
19
20
 
20
21
  # @param directory [String]
21
22
  def initialize directory = ''
22
- @directory = directory
23
+ @directory = File.absolute_path(directory)
23
24
  @raw_data = config_data
24
25
  included
25
26
  excluded
@@ -41,8 +42,10 @@ module Solargraph
41
42
  @excluded ||= process_exclusions(@raw_data['exclude'])
42
43
  end
43
44
 
45
+ # @param filename [String]
44
46
  def allow? filename
45
- filename.start_with?(directory) &&
47
+ filename = File.absolute_path(filename, directory)
48
+ filename.start_with?(directory) &&
46
49
  !excluded.include?(filename) &&
47
50
  excluded_directories.none? { |d| filename.start_with?(d) }
48
51
  end
@@ -110,7 +113,7 @@ module Solargraph
110
113
 
111
114
  # @return [String]
112
115
  def global_config_path
113
- ENV['SOLARGRAPH_GLOBAL_CONFIG'] ||
116
+ ENV['SOLARGRAPH_GLOBAL_CONFIG'] ||
114
117
  File.join(Dir.home, '.config', 'solargraph', 'config.yml')
115
118
  end
116
119
 
@@ -120,7 +123,7 @@ module Solargraph
120
123
  File.join(@directory, '.solargraph.yml')
121
124
  end
122
125
 
123
- # @return [Hash]
126
+ # @return [Hash{String => Array, Hash, Integer}]
124
127
  def config_data
125
128
  workspace_config = read_config(workspace_config_path)
126
129
  global_config = read_config(global_config_path)
@@ -135,15 +138,15 @@ module Solargraph
135
138
 
136
139
  # Read a .solargraph yaml config
137
140
  #
138
- # @param directory [String]
139
- # @return [Hash, nil]
141
+ # @param config_path [String]
142
+ # @return [Hash{String => Array, Hash, Integer}, nil]
140
143
  def read_config config_path = ''
141
144
  return nil if config_path.empty?
142
145
  return nil unless File.file?(config_path)
143
146
  YAML.safe_load(File.read(config_path))
144
147
  end
145
148
 
146
- # @return [Hash]
149
+ # @return [Hash{String => Array, Hash, Integer}]
147
150
  def default_config
148
151
  {
149
152
  'include' => ['**/*.rb'],
@@ -171,7 +174,7 @@ module Solargraph
171
174
  # @return [Array<String>]
172
175
  def process_globs globs
173
176
  result = globs.flat_map do |glob|
174
- Dir[File.join directory, glob]
177
+ Dir[File.absolute_path(glob, directory)]
175
178
  .map{ |f| f.gsub(/\\/, '/') }
176
179
  .select { |f| File.file?(f) }
177
180
  end
@@ -186,7 +189,7 @@ module Solargraph
186
189
  def process_exclusions globs
187
190
  remainder = globs.select do |glob|
188
191
  if glob_is_directory?(glob)
189
- exdir = File.join(directory, glob_to_directory(glob))
192
+ exdir = File.absolute_path(glob_to_directory(glob), directory)
190
193
  included.delete_if { |file| file.start_with?(exdir) }
191
194
  false
192
195
  else
@@ -221,10 +224,11 @@ module Solargraph
221
224
  glob.gsub(/(\/\*|\/\*\*\/\*\*?)$/, '')
222
225
  end
223
226
 
227
+ # @return [Array<String>]
224
228
  def excluded_directories
225
229
  @raw_data['exclude']
226
230
  .select { |g| glob_is_directory?(g) }
227
- .map { |g| File.join(directory, glob_to_directory(g)) }
231
+ .map { |g| File.absolute_path(glob_to_directory(g), directory) }
228
232
  end
229
233
  end
230
234
  end