solargraph 0.48.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 (62) 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 +9 -0
  5. data/LICENSE +1 -1
  6. data/SPONSORS.md +2 -4
  7. data/lib/solargraph/api_map/store.rb +13 -1
  8. data/lib/solargraph/api_map.rb +30 -12
  9. data/lib/solargraph/cache.rb +51 -0
  10. data/lib/solargraph/complex_type/type_methods.rb +3 -6
  11. data/lib/solargraph/complex_type/unique_type.rb +57 -0
  12. data/lib/solargraph/complex_type.rb +20 -1
  13. data/lib/solargraph/convention/rakefile.rb +17 -0
  14. data/lib/solargraph/convention.rb +2 -0
  15. data/lib/solargraph/diagnostics/rubocop.rb +15 -2
  16. data/lib/solargraph/diagnostics/rubocop_helpers.rb +3 -1
  17. data/lib/solargraph/language_server/host.rb +22 -18
  18. data/lib/solargraph/language_server/message/extended/download_core.rb +1 -5
  19. data/lib/solargraph/language_server/message/initialize.rb +2 -0
  20. data/lib/solargraph/language_server/message/text_document/formatting.rb +1 -1
  21. data/lib/solargraph/language_server/message/text_document/signature_help.rb +1 -6
  22. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +10 -3
  23. data/lib/solargraph/library.rb +21 -20
  24. data/lib/solargraph/parser/rubyvm/class_methods.rb +6 -1
  25. data/lib/solargraph/parser/rubyvm/node_processors/def_node.rb +20 -8
  26. data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +14 -3
  27. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +4 -2
  28. data/lib/solargraph/parser/rubyvm/node_wrapper.rb +47 -0
  29. data/lib/solargraph/pin/base.rb +5 -2
  30. data/lib/solargraph/pin/conversions.rb +2 -6
  31. data/lib/solargraph/pin/method.rb +84 -10
  32. data/lib/solargraph/pin/namespace.rb +4 -1
  33. data/lib/solargraph/pin/parameter.rb +8 -3
  34. data/lib/solargraph/pin/signature.rb +23 -0
  35. data/lib/solargraph/pin.rb +1 -0
  36. data/lib/solargraph/rbs_map/conversions.rb +394 -0
  37. data/lib/solargraph/rbs_map/core_fills.rb +61 -0
  38. data/lib/solargraph/rbs_map/core_map.rb +38 -0
  39. data/lib/solargraph/rbs_map/core_signs.rb +33 -0
  40. data/lib/solargraph/rbs_map/stdlib_map.rb +36 -0
  41. data/lib/solargraph/rbs_map.rb +73 -0
  42. data/lib/solargraph/shell.rb +38 -30
  43. data/lib/solargraph/source/chain/call.rb +30 -22
  44. data/lib/solargraph/source/chain.rb +21 -6
  45. data/lib/solargraph/source_map/clip.rb +5 -0
  46. data/lib/solargraph/source_map/mapper.rb +2 -0
  47. data/lib/solargraph/type_checker.rb +71 -65
  48. data/lib/solargraph/version.rb +1 -1
  49. data/lib/solargraph/views/environment.erb +2 -2
  50. data/lib/solargraph/workspace.rb +11 -14
  51. data/lib/solargraph/yard_map/mapper/to_method.rb +7 -4
  52. data/lib/solargraph/yard_map.rb +7 -148
  53. data/lib/solargraph.rb +2 -2
  54. data/solargraph.gemspec +8 -6
  55. metadata +43 -36
  56. data/lib/solargraph/compat.rb +0 -37
  57. data/lib/solargraph/yard_map/core_docs.rb +0 -170
  58. data/lib/solargraph/yard_map/core_fills.rb +0 -208
  59. data/lib/solargraph/yard_map/core_gen.rb +0 -76
  60. data/lib/solargraph/yard_map/rdoc_to_yard.rb +0 -143
  61. data/lib/solargraph/yard_map/stdlib_fills.rb +0 -43
  62. data/yardoc/2.2.2.tar.gz +0 -0
@@ -71,43 +71,47 @@ module Solargraph
71
71
  STDOUT.puts "Configuration file initialized."
72
72
  end
73
73
 
74
- desc 'download-core [VERSION]', 'Download core documentation'
75
- def download_core version = nil
76
- ver = version || Solargraph::YardMap::CoreDocs.best_download
77
- if RUBY_VERSION != ver
78
- puts "Documentation for #{RUBY_VERSION} is not available. Reverting to closest match..."
79
- end
80
- puts "Downloading docs for #{ver}..."
81
- Solargraph::YardMap::CoreDocs.download ver
82
- # Clear cached documentation if it exists
83
- FileUtils.rm_rf Dir.glob(File.join(Solargraph::YardMap::CoreDocs.cache_dir, ver, '*.ser'))
84
- puts "Download complete."
85
- rescue ArgumentError => e
86
- STDERR.puts "ERROR: #{e.message}"
87
- STDERR.puts "Run `solargraph available-cores` for a list."
88
- exit 1
74
+ desc 'download-core [VERSION]', 'Download core documentation [deprecated]'
75
+ long_desc %(
76
+ The `download-core` command is deprecated. Current versions of Solargraph
77
+ use RBS for core and stdlib documentation.
78
+ )
79
+ # @deprecated
80
+ def download_core _version = nil
81
+ puts 'The `download-core` command is deprecated.'
82
+ puts 'Current versions of Solargraph use RBS for core and stdlib documentation.'
89
83
  end
90
84
 
91
- desc 'list-cores', 'List the local documentation versions'
85
+ desc 'list-cores', 'List the local documentation versions [deprecated]'
86
+ long_desc %(
87
+ The `list-cores` command is deprecated. Current versions of Solargraph use
88
+ RBS for core and stdlib documentation.
89
+ )
90
+ # @deprecated
92
91
  def list_cores
93
- puts Solargraph::YardMap::CoreDocs.versions.join("\n")
92
+ puts 'The `list-cores` command is deprecated.'
93
+ puts 'Current versions of Solargraph use RBS for core and stdlib documentation.'
94
94
  end
95
95
 
96
- desc 'available-cores', 'List available documentation versions'
96
+ desc 'available-cores', 'List available documentation versions [deprecated]'
97
+ long_desc %(
98
+ The `available-cores` command is deprecated. Current versions of Solargraph
99
+ use RBS for core and stdlib documentation.
100
+ )
101
+ # @deprecated
97
102
  def available_cores
98
- puts Solargraph::YardMap::CoreDocs.available.join("\n")
103
+ puts 'The `available-cores` command is deprecated.'
104
+ puts 'Current versions of Solargraph use RBS for core and stdlib documentation.'
99
105
  end
100
106
 
101
107
  desc 'clear', 'Delete all cached documentation'
102
108
  long_desc %(
103
109
  This command will delete all core and gem documentation from the cache.
104
- You can also delete specific gem caches with the `uncache` command or
105
- update documentation for specific Ruby versions with the `download-core`
106
- command.
107
110
  )
111
+ # @deprecated
108
112
  def clear
109
113
  puts "Deleting the cached documentation"
110
- Solargraph::YardMap::CoreDocs.clear
114
+ Solargraph::Cache.clear
111
115
  end
112
116
  map 'clear-cache' => :clear
113
117
  map 'clear-cores' => :clear
@@ -191,18 +195,22 @@ module Solargraph
191
195
  puts "Scanned #{directory} (#{api_map.pins.length} pins) in #{time.real} seconds."
192
196
  end
193
197
 
194
- desc 'bundle', 'Generate documentation for bundled gems'
198
+ desc 'bundle', 'Generate documentation for bundled gems [deprecated]'
199
+ long_desc %(
200
+ The `bundle` command is deprecated. Solargraph currently uses RBS instead.
201
+ )
195
202
  option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
196
203
  option :rebuild, type: :boolean, aliases: :r, desc: 'Rebuild existing documentation', default: false
197
204
  def bundle
198
- Documentor.new(options[:directory], rebuild: options[:rebuild], out: STDOUT).document
205
+ puts 'The `bundle` command is deprecated. Solargraph currently uses RBS instead.'
199
206
  end
200
207
 
201
- desc 'rdoc GEM [VERSION]', 'Use RDoc to cache documentation'
202
- def rdoc gem, version = '>= 0'
203
- spec = Gem::Specification.find_by_name(gem, version)
204
- puts "Caching #{spec.name} #{spec.version} from RDoc"
205
- Solargraph::YardMap::RdocToYard.run(spec)
208
+ desc 'rdoc GEM [VERSION]', 'Use RDoc to cache documentation [deprecated]'
209
+ long_desc %(
210
+ The `rdoc` command is deprecated. Solargraph currently uses RBS instead.
211
+ )
212
+ def rdoc _gem, _version = '>= 0'
213
+ puts 'The `rdoc` command is deprecated. Solargraph currently uses RBS instead.'
206
214
  end
207
215
 
208
216
  private
@@ -52,36 +52,32 @@ module Solargraph
52
52
  # @return [Array<Pin::Base>]
53
53
  def inferred_pins pins, api_map, context, locals
54
54
  result = pins.map do |p|
55
- overloads = p.docstring.tags(:overload)
55
+ next p unless p.is_a?(Pin::Method)
56
+ overloads = p.signatures
56
57
  # next p if overloads.empty?
57
58
  type = ComplexType::UNDEFINED
58
- # @param [YARD::Tags::OverloadTag]
59
59
  overloads.each do |ol|
60
- next unless arguments_match(arguments, ol.parameters)
61
- next if ol.parameters.last && ol.parameters.last.first.start_with?('&') && ol.parameters.last.last.nil? && !with_block?
60
+ next unless arguments_match(arguments, ol)
61
+ # next if ol.parameters.last && ol.parameters.last.first.start_with?('&') && ol.parameters.last.last.nil? && !with_block?
62
62
  match = true
63
63
  arguments.each_with_index do |arg, idx|
64
- achain = arguments[idx]
65
- next if achain.nil?
66
64
  param = ol.parameters[idx]
67
65
  if param.nil?
68
- match = false unless ol.parameters.last && ol.parameters.last.first.start_with?('*')
66
+ match = false unless ol.parameters.any?(&:restarg?)
69
67
  break
70
68
  end
71
- par = ol.tags(:param).select { |pp| pp.name == param.first }.first
72
- next if par.nil? || par.types.nil? || par.types.empty?
73
- atype = achain.infer(api_map, Pin::ProxyType.anonymous(context), locals)
74
- other = ComplexType.try_parse(*par.types)
69
+ atype = arg.infer(api_map, Pin::ProxyType.anonymous(context), locals)
75
70
  # @todo Weak type comparison
76
- unless atype.tag == other.tag || api_map.super_and_sub?(other.tag, atype.tag)
71
+ # unless atype.tag == param.return_type.tag || api_map.super_and_sub?(param.return_type.tag, atype.tag)
72
+ unless param.return_type.undefined? || atype.name == param.return_type.name || api_map.super_and_sub?(param.return_type.name, atype.name)
77
73
  match = false
78
74
  break
79
75
  end
80
76
  end
81
77
  if match
82
- type = extra_return_type(ol, context)
78
+ type = extra_return_type(p.docstring, context)
83
79
  break if type
84
- type = ComplexType.try_parse(*ol.tag(:return).types).self_to(context.to_s).qualify(api_map, context.namespace) if ol.has_tag?(:return) && ol.tag(:return).types && !ol.tag(:return).types.empty? && (type.nil? || type.undefined?)
80
+ type = with_params(ol.return_type.self_to(context.to_s), context).qualify(api_map, context.namespace) if ol.return_type.defined?
85
81
  type ||= ComplexType::UNDEFINED
86
82
  end
87
83
  break if type.defined?
@@ -110,9 +106,13 @@ module Solargraph
110
106
  p
111
107
  end
112
108
  result.map do |pin|
113
- next pin if pin.return_type.undefined?
114
- selfy = pin.return_type.self_to(context.tag)
115
- selfy == pin.return_type ? pin : pin.proxy(selfy)
109
+ if pin.path == 'Class#new' && context.tag != 'Class'
110
+ pin.proxy(ComplexType.try_parse(context.namespace))
111
+ else
112
+ next pin if pin.return_type.undefined?
113
+ selfy = pin.return_type.self_to(context.tag)
114
+ selfy == pin.return_type ? pin : pin.proxy(selfy)
115
+ end
116
116
  end
117
117
  end
118
118
 
@@ -183,14 +183,15 @@ module Solargraph
183
183
  end
184
184
 
185
185
  # @param arguments [Array<Chain>]
186
- # @param parameters [Array<String>]
186
+ # @param signature [Pin::Signature]
187
187
  # @return [Boolean]
188
- def arguments_match arguments, parameters
188
+ def arguments_match arguments, signature
189
+ parameters = signature.parameters
189
190
  argcount = arguments.length
190
- # argcount -= 1 if !arguments.empty? && arguments.last.links.first.word.start_with?('&')
191
191
  parcount = parameters.length
192
- parcount -= 1 if !parameters.empty? && parameters.last.first.start_with?('&')
193
- return false if argcount < parcount && !(argcount == parcount - 1 && parameters.last.first.start_with?('*'))
192
+ parcount -= 1 if !parameters.empty? && parameters.last.block?
193
+ return false if signature.block? && !with_block?
194
+ return false if argcount < parcount && !(argcount == parcount - 1 && parameters.last.restarg?)
194
195
  true
195
196
  end
196
197
 
@@ -201,6 +202,13 @@ module Solargraph
201
202
  pins = api_map.get_method_stack(name_pin.namespace, name_pin.name, scope: name_pin.scope)
202
203
  pins.reject{|p| p.path == name_pin.path}
203
204
  end
205
+
206
+ # @param type [ComplexType]
207
+ # @param context [ComplexType]
208
+ def with_params type, context
209
+ return type unless type.to_s.include?('$')
210
+ ComplexType.try_parse(type.to_s.gsub('$', context.value_types.map(&:tag).join(', ')).gsub('<>', ''))
211
+ end
204
212
  end
205
213
  end
206
214
  end
@@ -63,11 +63,11 @@ module Solargraph
63
63
  working_pin = name_pin
64
64
  links[0..-2].each do |link|
65
65
  pins = link.resolve(api_map, working_pin, locals)
66
- type = infer_first_defined(pins, working_pin, api_map)
66
+ type = infer_first_defined(pins, working_pin, api_map, locals)
67
67
  return [] if type.undefined?
68
68
  working_pin = Pin::ProxyType.anonymous(type)
69
69
  end
70
- links.last.last_context = working_pin
70
+ links.last.last_context = name_pin
71
71
  links.last.resolve(api_map, working_pin, locals)
72
72
  end
73
73
 
@@ -76,8 +76,12 @@ module Solargraph
76
76
  # @param locals [Array<Pin::Base>]
77
77
  # @return [ComplexType]
78
78
  def infer api_map, name_pin, locals
79
+ from_here = base.infer(api_map, name_pin, locals) unless links.length == 1
80
+ if from_here
81
+ name_pin = name_pin.proxy(from_here)
82
+ end
79
83
  pins = define(api_map, name_pin, locals)
80
- type = infer_first_defined(pins, links.last.last_context, api_map)
84
+ type = infer_first_defined(pins, links.last.last_context, api_map, locals)
81
85
  maybe_nil(type)
82
86
  end
83
87
 
@@ -110,9 +114,10 @@ module Solargraph
110
114
  private
111
115
 
112
116
  # @param pins [Array<Pin::Base>]
117
+ # @param context [Pin::Base]
113
118
  # @param api_map [ApiMap]
114
119
  # @return [ComplexType]
115
- def infer_first_defined pins, context, api_map
120
+ def infer_first_defined pins, context, api_map, locals
116
121
  possibles = []
117
122
  pins.each do |pin|
118
123
  # Avoid infinite recursion
@@ -121,8 +126,18 @@ module Solargraph
121
126
  type = pin.typify(api_map)
122
127
  @@inference_stack.pop
123
128
  if type.defined?
124
- possibles.push type
125
- break if pin.is_a?(Pin::Method)
129
+ if type.parameterized?
130
+ type = type.resolve_parameters(pin.closure, context)
131
+ # idx = pin.closure.parameters.index(type.subtypes.first.name)
132
+ # next if idx.nil?
133
+ # param_type = context.return_type.all_params[idx]
134
+ # next unless param_type
135
+ # type = ComplexType.try_parse(param_type.to_s)
136
+ end
137
+ if type.defined?
138
+ possibles.push type
139
+ break if pin.is_a?(Pin::Method)
140
+ end
126
141
  end
127
142
  end
128
143
  if possibles.empty?
@@ -43,6 +43,11 @@ module Solargraph
43
43
  # @return [ComplexType]
44
44
  def infer
45
45
  result = cursor.chain.infer(api_map, block, locals)
46
+ if result.tag == 'Class'
47
+ # HACK: Exception to return Object from Class#new
48
+ dfn = cursor.chain.define(api_map, block, locals).first
49
+ return ComplexType.try_parse('Object') if dfn && dfn.path == 'Class#new'
50
+ end
46
51
  return result unless result.tag == 'self'
47
52
  ComplexType.try_parse(cursor.chain.base.infer(api_map, block, locals).namespace)
48
53
  end
@@ -24,6 +24,8 @@ module Solargraph
24
24
  @code = source.code
25
25
  @comments = source.comments
26
26
  @pins, @locals = Parser.map(source)
27
+ @pins.each { |p| p.source = :code }
28
+ @locals.each { |l| l.source = :code }
27
29
  process_comment_directives
28
30
  [@pins, @locals]
29
31
  # rescue Exception => e
@@ -120,12 +120,10 @@ module Solargraph
120
120
  # @param pin [Pin::Base]
121
121
  # @return [Boolean]
122
122
  def resolved_constant? pin
123
- api_map.get_constants('', pin.binder.tag)
123
+ return true if pin.typify(api_map).defined?
124
+ api_map.get_constants('', *pin.closure.gates)
124
125
  .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
126
+ .any? { |p| p.infer(api_map).defined? }
129
127
  end
130
128
 
131
129
  def virtual_pin? pin
@@ -139,10 +137,12 @@ module Solargraph
139
137
  params = first_param_hash(stack)
140
138
  result = []
141
139
  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)
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
146
146
  end
147
147
  end
148
148
  end
@@ -184,7 +184,7 @@ module Solargraph
184
184
  elsif declared_externally?(pin)
185
185
  ignored_pins.push pin
186
186
  end
187
- elsif !pin.is_a?(Pin::Parameter)
187
+ elsif !pin.is_a?(Pin::Parameter) && !resolved_constant?(pin)
188
188
  result.push Problem.new(pin.location, "Unresolved type #{pin.return_type} for variable #{pin.name}", pin: pin)
189
189
  end
190
190
  else
@@ -243,7 +243,7 @@ module Solargraph
243
243
  end
244
244
  closest = found.typify(api_map) if found
245
245
  if !found || found.is_a?(Pin::BaseVariable) || (closest.defined? && internal_or_core?(found))
246
- unless ignored_pins.include?(found)
246
+ unless closest.parameterized? || ignored_pins.include?(found)
247
247
  result.push Problem.new(location, "Unresolved call to #{missing.links.last.word}")
248
248
  @marked_ranges.push rng
249
249
  end
@@ -273,34 +273,44 @@ module Solargraph
273
273
  end
274
274
  break unless rules.validate_calls?
275
275
  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
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
290
290
  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}")
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
294
300
  end
295
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
296
305
  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}")
306
+ end
307
+ if errors.empty?
308
+ all_errors.clear
301
309
  break
302
310
  end
311
+ all_errors.concat errors
303
312
  end
313
+ result.concat all_errors
304
314
  end
305
315
  base = base.base
306
316
  end
@@ -310,7 +320,7 @@ module Solargraph
310
320
  def kwarg_problems_for argchain, api_map, block_pin, locals, location, pin, params, first
311
321
  result = []
312
322
  kwargs = convert_hash(argchain.node)
313
- pin.parameters[first..-1].each_with_index do |par, cur|
323
+ pin.signatures.first.parameters[first..-1].each_with_index do |par, cur|
314
324
  idx = first + cur
315
325
  argchain = kwargs[par.name.to_sym]
316
326
  if par.decl == :kwrestarg || (par.decl == :optarg && idx == pin.parameters.length - 1 && par.asgn_code == '{}')
@@ -381,8 +391,10 @@ module Solargraph
381
391
  pin.location && api_map.bundled?(pin.location.filename)
382
392
  end
383
393
 
394
+ # True if the pin is either internal (part of the workspace) or from the core/stdlib
384
395
  def internal_or_core? pin
385
- 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
386
398
  end
387
399
 
388
400
  # @param pin [Pin::Base]
@@ -417,20 +429,20 @@ module Solargraph
417
429
  true
418
430
  end
419
431
 
420
- # @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)
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
427
439
  end
428
440
 
429
- # @param pin [Pin::Method]
430
- def pin_arity_problems_for(pin, arguments, location)
441
+ def parameterized_arity_problems_for(pin, parameters, arguments, location)
431
442
  return [] unless pin.explicit?
432
- return [] if pin.parameters.empty? && arguments.empty?
433
- if pin.parameters.empty?
443
+ return [] if parameters.empty? && arguments.empty?
444
+ return [] if pin.anon_splat?
445
+ if parameters.empty?
434
446
  # Functions tagged param_tuple accepts two arguments (e.g., Hash#[]=)
435
447
  return [] if pin.docstring.tag(:param_tuple) && arguments.length == 2
436
448
  return [] if arguments.length == 1 && arguments.last.links.last.is_a?(Source::Chain::BlockVariable)
@@ -438,21 +450,21 @@ module Solargraph
438
450
  end
439
451
  unchecked = arguments.clone
440
452
  add_params = 0
441
- if unchecked.empty? && pin.parameters.any? { |param| param.decl == :kwarg }
453
+ if unchecked.empty? && parameters.any? { |param| param.decl == :kwarg }
442
454
  return [Problem.new(location, "Missing keyword arguments to #{pin.path}")]
443
455
  end
444
456
  settled_kwargs = 0
445
457
  unless unchecked.empty?
446
458
  if any_splatted_call?(unchecked.map(&:node))
447
- settled_kwargs = pin.parameters.count(&:keyword?)
459
+ settled_kwargs = parameters.count(&:keyword?)
448
460
  else
449
461
  kwargs = convert_hash(unchecked.last.node)
450
- if pin.parameters.any? { |param| [:kwarg, :kwoptarg].include?(param.decl) || param.kwrestarg? }
462
+ if parameters.any? { |param| [:kwarg, :kwoptarg].include?(param.decl) || param.kwrestarg? }
451
463
  if kwargs.empty?
452
464
  add_params += 1
453
465
  else
454
466
  unchecked.pop
455
- pin.parameters.each do |param|
467
+ parameters.each do |param|
456
468
  next unless param.keyword?
457
469
  if kwargs.key?(param.name.to_sym)
458
470
  kwargs.delete param.name.to_sym
@@ -462,7 +474,7 @@ module Solargraph
462
474
  return [Problem.new(location, "Missing keyword argument #{param.name} to #{pin.path}")]
463
475
  end
464
476
  end
465
- kwargs.clear if pin.parameters.any?(&:kwrestarg?)
477
+ kwargs.clear if parameters.any?(&:kwrestarg?)
466
478
  unless kwargs.empty?
467
479
  return [Problem.new(location, "Unrecognized keyword argument #{kwargs.keys.first} to #{pin.path}")]
468
480
  end
@@ -470,18 +482,18 @@ module Solargraph
470
482
  end
471
483
  end
472
484
  end
473
- req = required_param_count(pin)
485
+ req = required_param_count(parameters)
474
486
  if req + add_params < unchecked.length
475
- return [] if pin.parameters.any?(&:rest?)
476
- opt = optional_param_count(pin)
487
+ return [] if parameters.any?(&:rest?)
488
+ opt = optional_param_count(parameters)
477
489
  return [] if unchecked.length <= req + opt
478
490
  if unchecked.length == req + opt + 1 && unchecked.last.links.last.is_a?(Source::Chain::BlockVariable)
479
491
  return []
480
492
  end
481
- 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?
482
494
  return []
483
495
  end
484
- 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
485
497
  return [Problem.new(location, "Too many arguments to #{pin.path}")]
486
498
  elsif unchecked.length < req - settled_kwargs && (arguments.empty? || (!arguments.last.splat? && !arguments.last.links.last.is_a?(Solargraph::Source::Chain::Hash)))
487
499
  # HACK: Kernel#raise signature is incorrect in Ruby 2.7 core docs.
@@ -493,19 +505,13 @@ module Solargraph
493
505
  []
494
506
  end
495
507
 
496
- # @param pin [Pin::Method]
497
- def required_param_count(pin)
498
- 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 }
499
510
  end
500
511
 
501
512
  # @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
513
+ def optional_param_count(parameters)
514
+ parameters.select { |p| p.decl == :optarg }.length
509
515
  end
510
516
 
511
517
  def abstract? pin
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- VERSION = '0.48.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>
@@ -46,24 +46,21 @@ module Solargraph
46
46
  #
47
47
  # @param source [Solargraph::Source]
48
48
  # @return [Boolean] True if the source was added to the workspace
49
- def merge source
50
- unless directory == '*' || source_hash.key?(source.filename)
49
+ def merge *sources
50
+ unless directory == '*' || sources.all? { |source| source_hash.key?(source.filename) }
51
51
  # Reload the config to determine if a new source should be included
52
52
  @config = Solargraph::Workspace::Config.new(directory)
53
- return false unless config.calculated.include?(source.filename)
54
53
  end
55
- source_hash[source.filename] = source
56
- true
57
- end
58
54
 
59
- # Determine whether a file would be merged into the workspace.
60
- #
61
- # @param filename [String]
62
- # @return [Boolean]
63
- def would_merge? filename
64
- return true if directory == '*' || source_hash.include?(filename)
65
- @config = Solargraph::Workspace::Config.new(directory)
66
- 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
67
64
  end
68
65
 
69
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,