solargraph 0.45.0 → 0.49.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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/rspec.yml +1 -1
  4. data/CHANGELOG.md +41 -0
  5. data/LICENSE +1 -1
  6. data/README.md +8 -0
  7. data/SPONSORS.md +2 -4
  8. data/lib/solargraph/api_map/store.rb +13 -1
  9. data/lib/solargraph/api_map.rb +55 -32
  10. data/lib/solargraph/cache.rb +51 -0
  11. data/lib/solargraph/complex_type/type_methods.rb +10 -6
  12. data/lib/solargraph/complex_type/unique_type.rb +57 -0
  13. data/lib/solargraph/complex_type.rb +35 -2
  14. data/lib/solargraph/convention/rakefile.rb +17 -0
  15. data/lib/solargraph/convention.rb +2 -0
  16. data/lib/solargraph/diagnostics/require_not_found.rb +16 -0
  17. data/lib/solargraph/diagnostics/rubocop.rb +17 -3
  18. data/lib/solargraph/diagnostics/rubocop_helpers.rb +3 -1
  19. data/lib/solargraph/language_server/host.rb +22 -18
  20. data/lib/solargraph/language_server/message/extended/download_core.rb +1 -5
  21. data/lib/solargraph/language_server/message/initialize.rb +2 -0
  22. data/lib/solargraph/language_server/message/text_document/formatting.rb +1 -1
  23. data/lib/solargraph/language_server/message/text_document/hover.rb +16 -4
  24. data/lib/solargraph/language_server/message/text_document/signature_help.rb +1 -6
  25. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +10 -3
  26. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +1 -1
  27. data/lib/solargraph/library.rb +21 -20
  28. data/lib/solargraph/parser/legacy/node_processors/casgn_node.rb +12 -2
  29. data/lib/solargraph/parser/legacy/node_processors/sclass_node.rb +24 -3
  30. data/lib/solargraph/parser/rubyvm/class_methods.rb +7 -2
  31. data/lib/solargraph/parser/rubyvm/node_processors/casgn_node.rb +13 -2
  32. data/lib/solargraph/parser/rubyvm/node_processors/def_node.rb +20 -9
  33. data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +14 -3
  34. data/lib/solargraph/parser/rubyvm/node_processors/kw_arg_node.rb +2 -2
  35. data/lib/solargraph/parser/rubyvm/node_processors/sclass_node.rb +14 -3
  36. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +4 -2
  37. data/lib/solargraph/parser/rubyvm/node_wrapper.rb +47 -0
  38. data/lib/solargraph/pin/base.rb +5 -2
  39. data/lib/solargraph/pin/block.rb +2 -1
  40. data/lib/solargraph/pin/conversions.rb +2 -6
  41. data/lib/solargraph/pin/method.rb +100 -10
  42. data/lib/solargraph/pin/namespace.rb +4 -1
  43. data/lib/solargraph/pin/parameter.rb +10 -7
  44. data/lib/solargraph/pin/search.rb +56 -0
  45. data/lib/solargraph/pin/signature.rb +23 -0
  46. data/lib/solargraph/pin.rb +2 -0
  47. data/lib/solargraph/rbs_map/conversions.rb +394 -0
  48. data/lib/solargraph/rbs_map/core_fills.rb +61 -0
  49. data/lib/solargraph/rbs_map/core_map.rb +38 -0
  50. data/lib/solargraph/rbs_map/core_signs.rb +33 -0
  51. data/lib/solargraph/rbs_map/stdlib_map.rb +36 -0
  52. data/lib/solargraph/rbs_map.rb +73 -0
  53. data/lib/solargraph/shell.rb +38 -30
  54. data/lib/solargraph/source/chain/call.rb +34 -23
  55. data/lib/solargraph/source/chain.rb +21 -6
  56. data/lib/solargraph/source.rb +1 -1
  57. data/lib/solargraph/source_map/clip.rb +5 -0
  58. data/lib/solargraph/source_map/mapper.rb +31 -2
  59. data/lib/solargraph/source_map.rb +1 -10
  60. data/lib/solargraph/type_checker/checks.rb +13 -0
  61. data/lib/solargraph/type_checker.rb +88 -68
  62. data/lib/solargraph/version.rb +1 -1
  63. data/lib/solargraph/views/environment.erb +2 -2
  64. data/lib/solargraph/workspace.rb +12 -14
  65. data/lib/solargraph/yard_map/mapper/to_method.rb +7 -4
  66. data/lib/solargraph/yard_map.rb +51 -195
  67. data/lib/solargraph.rb +2 -2
  68. data/solargraph.gemspec +8 -6
  69. metadata +44 -36
  70. data/lib/solargraph/compat.rb +0 -37
  71. data/lib/solargraph/yard_map/core_docs.rb +0 -170
  72. data/lib/solargraph/yard_map/core_fills.rb +0 -208
  73. data/lib/solargraph/yard_map/core_gen.rb +0 -76
  74. data/lib/solargraph/yard_map/rdoc_to_yard.rb +0 -140
  75. data/lib/solargraph/yard_map/stdlib_fills.rb +0 -43
  76. data/yardoc/2.2.2.tar.gz +0 -0
@@ -40,10 +40,12 @@ module Solargraph
40
40
  # @return [Array<Problem>]
41
41
  def problems
42
42
  @problems ||= begin
43
- method_tag_problems
44
- .concat variable_type_tag_problems
45
- .concat const_problems
46
- .concat call_problems
43
+ without_ignored(
44
+ method_tag_problems
45
+ .concat variable_type_tag_problems
46
+ .concat const_problems
47
+ .concat call_problems
48
+ )
47
49
  end
48
50
  end
49
51
 
@@ -103,7 +105,7 @@ module Solargraph
103
105
  result.push Problem.new(pin.location, "#{pin.path} return type could not be inferred", pin: pin)
104
106
  end
105
107
  else
106
- unless (rules.rank > 1 ? types_match?(api_map, declared, inferred) : any_types_match?(api_map, declared, inferred))
108
+ unless (rules.rank > 1 ? all_types_match?(api_map, inferred, declared) : any_types_match?(api_map, declared, inferred))
107
109
  result.push Problem.new(pin.location, "Declared return type #{declared} does not match inferred type #{inferred} for #{pin.path}", pin: pin)
108
110
  end
109
111
  end
@@ -118,7 +120,10 @@ module Solargraph
118
120
  # @param pin [Pin::Base]
119
121
  # @return [Boolean]
120
122
  def resolved_constant? pin
121
- api_map.get_constants('', pin.binder.tag).any? { |pin| pin.name == pin.return_type.namespace && ['Class', 'Module'].include?(pin.return_type.name) }
123
+ return true if pin.typify(api_map).defined?
124
+ api_map.get_constants('', *pin.closure.gates)
125
+ .select { |p| p.name == pin.return_type.namespace }
126
+ .any? { |p| p.infer(api_map).defined? }
122
127
  end
123
128
 
124
129
  def virtual_pin? pin
@@ -132,10 +137,12 @@ module Solargraph
132
137
  params = first_param_hash(stack)
133
138
  result = []
134
139
  if rules.require_type_tags?
135
- pin.parameters.each do |par|
136
- break if par.decl == :restarg || par.decl == :kwrestarg || par.decl == :blockarg
137
- unless params[par.name]
138
- result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin)
140
+ pin.signatures.each do |sig|
141
+ sig.parameters.each do |par|
142
+ break if par.decl == :restarg || par.decl == :kwrestarg || par.decl == :blockarg
143
+ unless params[par.name]
144
+ result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin)
145
+ end
139
146
  end
140
147
  end
141
148
  end
@@ -170,14 +177,14 @@ module Solargraph
170
177
  result.push Problem.new(pin.location, "Variable type could not be inferred for #{pin.name}", pin: pin)
171
178
  end
172
179
  else
173
- unless (rules.rank > 1 ? types_match?(api_map, declared, inferred) : any_types_match?(api_map, declared, inferred))
180
+ unless any_types_match?(api_map, declared, inferred)
174
181
  result.push Problem.new(pin.location, "Declared type #{declared} does not match inferred type #{inferred} for variable #{pin.name}", pin: pin)
175
182
  end
176
183
  end
177
184
  elsif declared_externally?(pin)
178
185
  ignored_pins.push pin
179
186
  end
180
- elsif !pin.is_a?(Pin::Parameter)
187
+ elsif !pin.is_a?(Pin::Parameter) && !resolved_constant?(pin)
181
188
  result.push Problem.new(pin.location, "Unresolved type #{pin.return_type} for variable #{pin.name}", pin: pin)
182
189
  end
183
190
  else
@@ -235,8 +242,8 @@ module Solargraph
235
242
  base = base.base
236
243
  end
237
244
  closest = found.typify(api_map) if found
238
- if !found || (closest.defined? && internal_or_core?(found))
239
- unless ignored_pins.include?(found)
245
+ if !found || found.is_a?(Pin::BaseVariable) || (closest.defined? && internal_or_core?(found))
246
+ unless closest.parameterized? || ignored_pins.include?(found)
240
247
  result.push Problem.new(location, "Unresolved call to #{missing.links.last.word}")
241
248
  @marked_ranges.push rng
242
249
  end
@@ -266,34 +273,44 @@ module Solargraph
266
273
  end
267
274
  break unless rules.validate_calls?
268
275
  params = first_param_hash(pins)
269
- pin.parameters.each_with_index do |par, idx|
270
- argchain = base.links.last.arguments[idx]
271
- if argchain.nil? && par.decl == :arg
272
- result.push Problem.new(location, "Not enough arguments to #{pin.path}")
273
- break
274
- end
275
- if argchain
276
- if par.decl != :arg
277
- result.concat kwarg_problems_for argchain, api_map, block_pin, locals, location, pin, params, idx
278
- break
279
- else
280
- ptype = params.key?(par.name) ? params[par.name][:qualified] : ComplexType::UNDEFINED
281
- if ptype.nil?
282
- # @todo Some level (strong, I guess) should require the param here
276
+
277
+ all_errors = []
278
+ pin.signatures.sort { |sig| sig.parameters.length }.each do |sig|
279
+ errors = []
280
+ sig.parameters.each_with_index do |par, idx|
281
+ argchain = base.links.last.arguments[idx]
282
+ if argchain.nil? && par.decl == :arg
283
+ errors.push Problem.new(location, "Not enough arguments to #{pin.path}")
284
+ next
285
+ end
286
+ if argchain
287
+ if par.decl != :arg
288
+ errors.concat kwarg_problems_for argchain, api_map, block_pin, locals, location, pin, params, idx
289
+ next
283
290
  else
284
- argtype = argchain.infer(api_map, block_pin, locals)
285
- if argtype.defined? && ptype.defined? && !any_types_match?(api_map, ptype, argtype)
286
- result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}")
291
+ ptype = params.key?(par.name) ? params[par.name][:qualified] : ComplexType::UNDEFINED
292
+ if ptype.nil?
293
+ # @todo Some level (strong, I guess) should require the param here
294
+ else
295
+ argtype = argchain.infer(api_map, block_pin, locals)
296
+ if argtype.defined? && ptype.defined? && !any_types_match?(api_map, ptype, argtype)
297
+ errors.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}")
298
+ next
299
+ end
287
300
  end
288
301
  end
302
+ elsif par.decl == :kwarg
303
+ errors.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}")
304
+ next
289
305
  end
290
- elsif par.rest?
291
- next
292
- elsif par.decl == :kwarg
293
- result.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}")
306
+ end
307
+ if errors.empty?
308
+ all_errors.clear
294
309
  break
295
310
  end
311
+ all_errors.concat errors
296
312
  end
313
+ result.concat all_errors
297
314
  end
298
315
  base = base.base
299
316
  end
@@ -303,7 +320,7 @@ module Solargraph
303
320
  def kwarg_problems_for argchain, api_map, block_pin, locals, location, pin, params, first
304
321
  result = []
305
322
  kwargs = convert_hash(argchain.node)
306
- pin.parameters[first..-1].each_with_index do |par, cur|
323
+ pin.signatures.first.parameters[first..-1].each_with_index do |par, cur|
307
324
  idx = first + cur
308
325
  argchain = kwargs[par.name.to_sym]
309
326
  if par.decl == :kwrestarg || (par.decl == :optarg && idx == pin.parameters.length - 1 && par.asgn_code == '{}')
@@ -374,8 +391,10 @@ module Solargraph
374
391
  pin.location && api_map.bundled?(pin.location.filename)
375
392
  end
376
393
 
394
+ # True if the pin is either internal (part of the workspace) or from the core/stdlib
377
395
  def internal_or_core? pin
378
- internal?(pin) || api_map.yard_map.core_pins.include?(pin) || api_map.yard_map.stdlib_pins.include?(pin)
396
+ # @todo RBS pins are not necessarily core/stdlib pins
397
+ internal?(pin) || pin.source == :rbs
379
398
  end
380
399
 
381
400
  # @param pin [Pin::Base]
@@ -410,20 +429,20 @@ module Solargraph
410
429
  true
411
430
  end
412
431
 
413
- # @param pin [Pin::Method]
414
- def arity_problems_for(pin, arguments, location)
415
- ([pin] + pin.overloads).map do |p|
416
- result = pin_arity_problems_for(p, arguments, location)
417
- return [] if result.empty?
418
- result
419
- end.flatten.uniq(&:message)
432
+ def arity_problems_for pin, arguments, location
433
+ results = pin.signatures.map do |sig|
434
+ r = parameterized_arity_problems_for(pin, sig.parameters, arguments, location)
435
+ return [] if r.empty?
436
+ r
437
+ end
438
+ results.first
420
439
  end
421
440
 
422
- # @param pin [Pin::Method]
423
- def pin_arity_problems_for(pin, arguments, location)
441
+ def parameterized_arity_problems_for(pin, parameters, arguments, location)
424
442
  return [] unless pin.explicit?
425
- return [] if pin.parameters.empty? && arguments.empty?
426
- if pin.parameters.empty?
443
+ return [] if parameters.empty? && arguments.empty?
444
+ return [] if pin.anon_splat?
445
+ if parameters.empty?
427
446
  # Functions tagged param_tuple accepts two arguments (e.g., Hash#[]=)
428
447
  return [] if pin.docstring.tag(:param_tuple) && arguments.length == 2
429
448
  return [] if arguments.length == 1 && arguments.last.links.last.is_a?(Source::Chain::BlockVariable)
@@ -431,21 +450,21 @@ module Solargraph
431
450
  end
432
451
  unchecked = arguments.clone
433
452
  add_params = 0
434
- if unchecked.empty? && pin.parameters.any? { |param| param.decl == :kwarg }
453
+ if unchecked.empty? && parameters.any? { |param| param.decl == :kwarg }
435
454
  return [Problem.new(location, "Missing keyword arguments to #{pin.path}")]
436
455
  end
437
456
  settled_kwargs = 0
438
457
  unless unchecked.empty?
439
458
  if any_splatted_call?(unchecked.map(&:node))
440
- settled_kwargs = pin.parameters.count(&:keyword?)
459
+ settled_kwargs = parameters.count(&:keyword?)
441
460
  else
442
461
  kwargs = convert_hash(unchecked.last.node)
443
- if pin.parameters.any? { |param| [:kwarg, :kwoptarg].include?(param.decl) || param.kwrestarg? }
462
+ if parameters.any? { |param| [:kwarg, :kwoptarg].include?(param.decl) || param.kwrestarg? }
444
463
  if kwargs.empty?
445
464
  add_params += 1
446
465
  else
447
466
  unchecked.pop
448
- pin.parameters.each do |param|
467
+ parameters.each do |param|
449
468
  next unless param.keyword?
450
469
  if kwargs.key?(param.name.to_sym)
451
470
  kwargs.delete param.name.to_sym
@@ -455,7 +474,7 @@ module Solargraph
455
474
  return [Problem.new(location, "Missing keyword argument #{param.name} to #{pin.path}")]
456
475
  end
457
476
  end
458
- kwargs.clear if pin.parameters.any?(&:kwrestarg?)
477
+ kwargs.clear if parameters.any?(&:kwrestarg?)
459
478
  unless kwargs.empty?
460
479
  return [Problem.new(location, "Unrecognized keyword argument #{kwargs.keys.first} to #{pin.path}")]
461
480
  end
@@ -463,18 +482,18 @@ module Solargraph
463
482
  end
464
483
  end
465
484
  end
466
- req = required_param_count(pin)
485
+ req = required_param_count(parameters)
467
486
  if req + add_params < unchecked.length
468
- return [] if pin.parameters.any?(&:rest?)
469
- opt = optional_param_count(pin)
487
+ return [] if parameters.any?(&:rest?)
488
+ opt = optional_param_count(parameters)
470
489
  return [] if unchecked.length <= req + opt
471
490
  if unchecked.length == req + opt + 1 && unchecked.last.links.last.is_a?(Source::Chain::BlockVariable)
472
491
  return []
473
492
  end
474
- if req + add_params + 1 == unchecked.length && any_splatted_call?(unchecked.map(&:node)) && (pin.parameters.map(&:decl) & [:kwarg, :kwoptarg, :kwrestarg]).any?
493
+ if req + add_params + 1 == unchecked.length && any_splatted_call?(unchecked.map(&:node)) && (parameters.map(&:decl) & [:kwarg, :kwoptarg, :kwrestarg]).any?
475
494
  return []
476
495
  end
477
- return [] if arguments.length - req == pin.parameters.select { |p| [:optarg, :kwoptarg].include?(p.decl) }.length
496
+ return [] if arguments.length - req == parameters.select { |p| [:optarg, :kwoptarg].include?(p.decl) }.length
478
497
  return [Problem.new(location, "Too many arguments to #{pin.path}")]
479
498
  elsif unchecked.length < req - settled_kwargs && (arguments.empty? || (!arguments.last.splat? && !arguments.last.links.last.is_a?(Solargraph::Source::Chain::Hash)))
480
499
  # HACK: Kernel#raise signature is incorrect in Ruby 2.7 core docs.
@@ -486,19 +505,13 @@ module Solargraph
486
505
  []
487
506
  end
488
507
 
489
- # @param pin [Pin::Method]
490
- def required_param_count(pin)
491
- pin.parameters.sum { |param| %i[arg kwarg].include?(param.decl) ? 1 : 0 }
508
+ def required_param_count(parameters)
509
+ parameters.sum { |param| %i[arg kwarg].include?(param.decl) ? 1 : 0 }
492
510
  end
493
511
 
494
512
  # @param pin [Pin::Method]
495
- def optional_param_count(pin)
496
- count = 0
497
- pin.parameters.each do |param|
498
- next unless param.decl == :optarg
499
- count += 1
500
- end
501
- count
513
+ def optional_param_count(parameters)
514
+ parameters.select { |p| p.decl == :optarg }.length
502
515
  end
503
516
 
504
517
  def abstract? pin
@@ -525,5 +538,12 @@ module Solargraph
525
538
  args.push Solargraph::Parser.chain_string('&') if with_block
526
539
  args
527
540
  end
541
+
542
+ def without_ignored problems
543
+ problems.reject do |problem|
544
+ node = source_map.source.node_at(problem.location.range.start.line, problem.location.range.start.column)
545
+ source_map.source.comments_for(node)&.include?('@sg-ignore')
546
+ end
547
+ end
528
548
  end
529
549
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- VERSION = '0.45.0'
4
+ VERSION = '0.49.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>
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'open3'
4
4
  require 'rubygems'
5
+ require 'json'
5
6
 
6
7
  module Solargraph
7
8
  # A workspace consists of the files in a project's directory and the
@@ -45,24 +46,21 @@ module Solargraph
45
46
  #
46
47
  # @param source [Solargraph::Source]
47
48
  # @return [Boolean] True if the source was added to the workspace
48
- def merge source
49
- unless directory == '*' || source_hash.key?(source.filename)
49
+ def merge *sources
50
+ unless directory == '*' || sources.all? { |source| source_hash.key?(source.filename) }
50
51
  # Reload the config to determine if a new source should be included
51
52
  @config = Solargraph::Workspace::Config.new(directory)
52
- return false unless config.calculated.include?(source.filename)
53
53
  end
54
- source_hash[source.filename] = source
55
- true
56
- end
57
54
 
58
- # Determine whether a file would be merged into the workspace.
59
- #
60
- # @param filename [String]
61
- # @return [Boolean]
62
- def would_merge? filename
63
- return true if directory == '*' || source_hash.include?(filename)
64
- @config = Solargraph::Workspace::Config.new(directory)
65
- config.calculated.include?(filename)
55
+ includes_any = false
56
+ sources.each do |source|
57
+ if directory == "*" || config.calculated.include?(source.filename)
58
+ source_hash[source.filename] = source
59
+ includes_any = true
60
+ end
61
+ end
62
+
63
+ includes_any
66
64
  end
67
65
 
68
66
  # Remove a source from the workspace. The source will not be removed if
@@ -13,16 +13,19 @@ module Solargraph
13
13
  )
14
14
  location = object_location(code_object, spec)
15
15
  comments = code_object.docstring ? code_object.docstring.all.to_s : ''
16
- Pin::Method.new(
16
+ pin = Pin::Method.new(
17
17
  location: location,
18
18
  closure: closure,
19
19
  name: name || code_object.name.to_s,
20
20
  comments: comments,
21
21
  scope: scope || code_object.scope,
22
22
  visibility: visibility || code_object.visibility,
23
- parameters: get_parameters(code_object, location, comments),
23
+ # @todo Might need to convert overloads to signatures
24
+ parameters: [],
24
25
  explicit: code_object.is_explicit?
25
26
  )
27
+ pin.parameters.concat get_parameters(code_object, location, comments, pin)
28
+ pin
26
29
  end
27
30
 
28
31
  class << self
@@ -30,7 +33,7 @@ module Solargraph
30
33
 
31
34
  # @param code_object [YARD::CodeObjects::Base]
32
35
  # @return [Array<Solargraph::Pin::Parameter>]
33
- def get_parameters code_object, location, comments
36
+ def get_parameters code_object, location, comments, pin
34
37
  return [] unless code_object.is_a?(YARD::CodeObjects::MethodObject)
35
38
  # HACK: Skip `nil` and `self` parameters that are sometimes emitted
36
39
  # for methods defined in C
@@ -38,7 +41,7 @@ module Solargraph
38
41
  code_object.parameters.select { |a| a[0] && a[0] != 'self' }.map do |a|
39
42
  Solargraph::Pin::Parameter.new(
40
43
  location: location,
41
- closure: self,
44
+ closure: pin,
42
45
  comments: comments,
43
46
  name: arg_name(a),
44
47
  presence: nil,