solargraph 0.44.3 → 0.49.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/rspec.yml +41 -0
  4. data/CHANGELOG.md +57 -0
  5. data/LICENSE +1 -1
  6. data/README.md +18 -2
  7. data/SPONSORS.md +2 -2
  8. data/lib/solargraph/api_map/store.rb +13 -1
  9. data/lib/solargraph/api_map.rb +57 -34
  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/rspec.rb +13 -4
  16. data/lib/solargraph/convention.rb +2 -0
  17. data/lib/solargraph/diagnostics/require_not_found.rb +16 -0
  18. data/lib/solargraph/diagnostics/rubocop.rb +17 -3
  19. data/lib/solargraph/diagnostics/rubocop_helpers.rb +3 -1
  20. data/lib/solargraph/language_server/host.rb +22 -18
  21. data/lib/solargraph/language_server/message/extended/download_core.rb +1 -5
  22. data/lib/solargraph/language_server/message/initialize.rb +2 -0
  23. data/lib/solargraph/language_server/message/text_document/formatting.rb +1 -1
  24. data/lib/solargraph/language_server/message/text_document/hover.rb +16 -4
  25. data/lib/solargraph/language_server/message/text_document/signature_help.rb +1 -6
  26. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +10 -3
  27. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +1 -1
  28. data/lib/solargraph/language_server/uri_helpers.rb +1 -1
  29. data/lib/solargraph/library.rb +21 -20
  30. data/lib/solargraph/parser/legacy/class_methods.rb +0 -5
  31. data/lib/solargraph/parser/legacy/node_processors/casgn_node.rb +12 -2
  32. data/lib/solargraph/parser/legacy/node_processors/sclass_node.rb +24 -3
  33. data/lib/solargraph/parser/rubyvm/class_methods.rb +8 -4
  34. data/lib/solargraph/parser/rubyvm/node_processors/casgn_node.rb +13 -2
  35. data/lib/solargraph/parser/rubyvm/node_processors/def_node.rb +20 -9
  36. data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +14 -3
  37. data/lib/solargraph/parser/rubyvm/node_processors/kw_arg_node.rb +2 -2
  38. data/lib/solargraph/parser/rubyvm/node_processors/sclass_node.rb +14 -3
  39. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +4 -2
  40. data/lib/solargraph/parser/rubyvm/node_processors.rb +1 -0
  41. data/lib/solargraph/parser/rubyvm/node_wrapper.rb +47 -0
  42. data/lib/solargraph/pin/base.rb +5 -2
  43. data/lib/solargraph/pin/block.rb +2 -1
  44. data/lib/solargraph/pin/conversions.rb +2 -6
  45. data/lib/solargraph/pin/method.rb +100 -10
  46. data/lib/solargraph/pin/namespace.rb +4 -1
  47. data/lib/solargraph/pin/parameter.rb +10 -7
  48. data/lib/solargraph/pin/search.rb +56 -0
  49. data/lib/solargraph/pin/signature.rb +23 -0
  50. data/lib/solargraph/pin.rb +2 -0
  51. data/lib/solargraph/range.rb +1 -1
  52. data/lib/solargraph/rbs_map/conversions.rb +394 -0
  53. data/lib/solargraph/rbs_map/core_fills.rb +61 -0
  54. data/lib/solargraph/rbs_map/core_map.rb +38 -0
  55. data/lib/solargraph/rbs_map/core_signs.rb +33 -0
  56. data/lib/solargraph/rbs_map/stdlib_map.rb +36 -0
  57. data/lib/solargraph/rbs_map.rb +73 -0
  58. data/lib/solargraph/shell.rb +38 -30
  59. data/lib/solargraph/source/chain/call.rb +34 -23
  60. data/lib/solargraph/source/chain.rb +21 -6
  61. data/lib/solargraph/source.rb +1 -1
  62. data/lib/solargraph/source_map/clip.rb +5 -0
  63. data/lib/solargraph/source_map/mapper.rb +31 -2
  64. data/lib/solargraph/source_map.rb +1 -10
  65. data/lib/solargraph/type_checker/checks.rb +13 -0
  66. data/lib/solargraph/type_checker.rb +90 -61
  67. data/lib/solargraph/version.rb +1 -1
  68. data/lib/solargraph/views/environment.erb +2 -2
  69. data/lib/solargraph/workspace.rb +12 -14
  70. data/lib/solargraph/yard_map/mapper/to_method.rb +7 -4
  71. data/lib/solargraph/yard_map.rb +54 -196
  72. data/lib/solargraph.rb +4 -4
  73. data/solargraph.gemspec +8 -6
  74. metadata +46 -37
  75. data/lib/solargraph/compat.rb +0 -37
  76. data/lib/solargraph/yard_map/core_docs.rb +0 -170
  77. data/lib/solargraph/yard_map/core_fills.rb +0 -203
  78. data/lib/solargraph/yard_map/core_gen.rb +0 -76
  79. data/lib/solargraph/yard_map/rdoc_to_yard.rb +0 -140
  80. data/lib/solargraph/yard_map/stdlib_fills.rb +0 -43
  81. 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,11 +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)
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
439
+ end
440
+
441
+ def parameterized_arity_problems_for(pin, parameters, arguments, location)
415
442
  return [] unless pin.explicit?
416
- return [] if pin.parameters.empty? && arguments.empty?
417
- if pin.parameters.empty?
443
+ return [] if parameters.empty? && arguments.empty?
444
+ return [] if pin.anon_splat?
445
+ if parameters.empty?
418
446
  # Functions tagged param_tuple accepts two arguments (e.g., Hash#[]=)
419
447
  return [] if pin.docstring.tag(:param_tuple) && arguments.length == 2
420
448
  return [] if arguments.length == 1 && arguments.last.links.last.is_a?(Source::Chain::BlockVariable)
@@ -422,21 +450,21 @@ module Solargraph
422
450
  end
423
451
  unchecked = arguments.clone
424
452
  add_params = 0
425
- if unchecked.empty? && pin.parameters.any? { |param| param.decl == :kwarg }
453
+ if unchecked.empty? && parameters.any? { |param| param.decl == :kwarg }
426
454
  return [Problem.new(location, "Missing keyword arguments to #{pin.path}")]
427
455
  end
428
456
  settled_kwargs = 0
429
457
  unless unchecked.empty?
430
458
  if any_splatted_call?(unchecked.map(&:node))
431
- settled_kwargs = pin.parameters.count(&:keyword?)
459
+ settled_kwargs = parameters.count(&:keyword?)
432
460
  else
433
461
  kwargs = convert_hash(unchecked.last.node)
434
- if pin.parameters.any? { |param| [:kwarg, :kwoptarg].include?(param.decl) || param.kwrestarg? }
462
+ if parameters.any? { |param| [:kwarg, :kwoptarg].include?(param.decl) || param.kwrestarg? }
435
463
  if kwargs.empty?
436
464
  add_params += 1
437
465
  else
438
466
  unchecked.pop
439
- pin.parameters.each do |param|
467
+ parameters.each do |param|
440
468
  next unless param.keyword?
441
469
  if kwargs.key?(param.name.to_sym)
442
470
  kwargs.delete param.name.to_sym
@@ -446,7 +474,7 @@ module Solargraph
446
474
  return [Problem.new(location, "Missing keyword argument #{param.name} to #{pin.path}")]
447
475
  end
448
476
  end
449
- kwargs.clear if pin.parameters.any?(&:kwrestarg?)
477
+ kwargs.clear if parameters.any?(&:kwrestarg?)
450
478
  unless kwargs.empty?
451
479
  return [Problem.new(location, "Unrecognized keyword argument #{kwargs.keys.first} to #{pin.path}")]
452
480
  end
@@ -454,18 +482,18 @@ module Solargraph
454
482
  end
455
483
  end
456
484
  end
457
- req = required_param_count(pin)
485
+ req = required_param_count(parameters)
458
486
  if req + add_params < unchecked.length
459
- return [] if pin.parameters.any?(&:rest?)
460
- opt = optional_param_count(pin)
487
+ return [] if parameters.any?(&:rest?)
488
+ opt = optional_param_count(parameters)
461
489
  return [] if unchecked.length <= req + opt
462
490
  if unchecked.length == req + opt + 1 && unchecked.last.links.last.is_a?(Source::Chain::BlockVariable)
463
491
  return []
464
492
  end
465
- 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?
466
494
  return []
467
495
  end
468
- 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
469
497
  return [Problem.new(location, "Too many arguments to #{pin.path}")]
470
498
  elsif unchecked.length < req - settled_kwargs && (arguments.empty? || (!arguments.last.splat? && !arguments.last.links.last.is_a?(Solargraph::Source::Chain::Hash)))
471
499
  # HACK: Kernel#raise signature is incorrect in Ruby 2.7 core docs.
@@ -477,19 +505,13 @@ module Solargraph
477
505
  []
478
506
  end
479
507
 
480
- # @param pin [Pin::Method]
481
- def required_param_count(pin)
482
- 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 }
483
510
  end
484
511
 
485
512
  # @param pin [Pin::Method]
486
- def optional_param_count(pin)
487
- count = 0
488
- pin.parameters.each do |param|
489
- next unless param.decl == :optarg
490
- count += 1
491
- end
492
- count
513
+ def optional_param_count(parameters)
514
+ parameters.select { |p| p.decl == :optarg }.length
493
515
  end
494
516
 
495
517
  def abstract? pin
@@ -516,5 +538,12 @@ module Solargraph
516
538
  args.push Solargraph::Parser.chain_string('&') if with_block
517
539
  args
518
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
519
548
  end
520
549
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- VERSION = '0.44.3'
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,