solargraph 0.39.16 → 0.40.3
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.
- checksums.yaml +4 -4
- data/.travis.yml +4 -8
- data/CHANGELOG.md +998 -0
- data/SPONSORS.md +1 -0
- data/lib/solargraph.rb +2 -4
- data/lib/solargraph/api_map.rb +61 -63
- data/lib/solargraph/api_map/cache.rb +2 -2
- data/lib/solargraph/api_map/store.rb +3 -7
- data/lib/solargraph/{bundle.rb → bench.rb} +6 -2
- data/lib/solargraph/compat.rb +14 -0
- data/lib/solargraph/convention.rb +13 -4
- data/lib/solargraph/convention/base.rb +16 -8
- data/lib/solargraph/convention/gemfile.rb +2 -5
- data/lib/solargraph/convention/gemspec.rb +3 -6
- data/lib/solargraph/convention/rspec.rb +3 -6
- data/lib/solargraph/diagnostics/rubocop_helpers.rb +1 -20
- data/lib/solargraph/environ.rb +11 -6
- data/lib/solargraph/language_server/host.rb +5 -0
- data/lib/solargraph/language_server/message/extended/check_gem_version.rb +6 -1
- data/lib/solargraph/language_server/message/text_document/definition.rb +1 -1
- data/lib/solargraph/language_server/message/text_document/formatting.rb +47 -20
- data/lib/solargraph/library.rb +6 -8
- data/lib/solargraph/parser/legacy/node_methods.rb +9 -0
- data/lib/solargraph/parser/legacy/node_processors/ivasgn_node.rb +1 -1
- data/lib/solargraph/parser/legacy/node_processors/send_node.rb +34 -22
- data/lib/solargraph/parser/node_processor/base.rb +3 -0
- data/lib/solargraph/parser/rubyvm/node_methods.rb +18 -1
- data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +1 -1
- data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +1 -1
- data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +38 -28
- data/lib/solargraph/pin.rb +0 -3
- data/lib/solargraph/pin/common.rb +1 -1
- data/lib/solargraph/pin/conversions.rb +1 -1
- data/lib/solargraph/pin/documenting.rb +3 -9
- data/lib/solargraph/pin/method.rb +141 -7
- data/lib/solargraph/pin/method_alias.rb +1 -1
- data/lib/solargraph/position.rb +2 -14
- data/lib/solargraph/source.rb +10 -6
- data/lib/solargraph/source/chain.rb +3 -3
- data/lib/solargraph/source_map.rb +4 -1
- data/lib/solargraph/source_map/clip.rb +3 -2
- data/lib/solargraph/source_map/mapper.rb +10 -6
- data/lib/solargraph/type_checker.rb +50 -44
- data/lib/solargraph/type_checker/checks.rb +5 -1
- data/lib/solargraph/type_checker/param_def.rb +1 -1
- data/lib/solargraph/type_checker/rules.rb +5 -1
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/workspace/config.rb +15 -0
- data/lib/solargraph/yard_map.rb +38 -47
- data/lib/solargraph/yard_map/core_fills.rb +185 -0
- data/lib/solargraph/yard_map/helpers.rb +16 -0
- data/lib/solargraph/yard_map/mapper.rb +11 -5
- data/lib/solargraph/{pin/yard_pin/constant.rb → yard_map/mapper/to_constant.rb} +6 -6
- data/lib/solargraph/yard_map/mapper/to_method.rb +78 -0
- data/lib/solargraph/{pin/yard_pin/namespace.rb → yard_map/mapper/to_namespace.rb} +6 -6
- data/lib/solargraph/yard_map/rdoc_to_yard.rb +1 -1
- data/lib/solargraph/yard_map/stdlib_fills.rb +43 -0
- data/lib/solargraph/{pin/yard_pin/method.rb → yard_map/to_method.rb} +29 -30
- data/solargraph.gemspec +5 -5
- metadata +22 -36
- data/lib/solargraph/core_fills.rb +0 -164
- data/lib/solargraph/pin/attribute.rb +0 -49
- data/lib/solargraph/pin/base_method.rb +0 -149
- data/lib/solargraph/pin/yard_pin.rb +0 -12
- data/lib/solargraph/pin/yard_pin/yard_mixin.rb +0 -20
- data/lib/solargraph/stdlib_fills.rb +0 -40
- data/travis-bundler.rb +0 -11
data/lib/solargraph/position.rb
CHANGED
@@ -40,20 +40,8 @@ module Solargraph
|
|
40
40
|
# @param position [Position]
|
41
41
|
# @return [Integer]
|
42
42
|
def self.to_offset text, position
|
43
|
-
|
44
|
-
|
45
|
-
line = position.line
|
46
|
-
column = position.character
|
47
|
-
text.lines.each do |l|
|
48
|
-
line_length = l.length
|
49
|
-
if feed == line
|
50
|
-
result += column
|
51
|
-
break
|
52
|
-
end
|
53
|
-
result += line_length
|
54
|
-
feed += 1
|
55
|
-
end
|
56
|
-
result
|
43
|
+
return 0 if text.empty?
|
44
|
+
text.lines[0...position.line].sum(&:length) + position.character
|
57
45
|
end
|
58
46
|
|
59
47
|
# Get a numeric offset for the specified text and a position identified
|
data/lib/solargraph/source.rb
CHANGED
@@ -284,7 +284,7 @@ module Solargraph
|
|
284
284
|
|
285
285
|
FOLDING_NODE_TYPES = if Parser.rubyvm?
|
286
286
|
%i[
|
287
|
-
CLASS SCLASS MODULE DEFN DEFS IF WHILE UNLESS ITER STR
|
287
|
+
CLASS SCLASS MODULE DEFN DEFS IF WHILE UNLESS ITER STR HASH ARRAY LIST
|
288
288
|
].freeze
|
289
289
|
else
|
290
290
|
%i[
|
@@ -345,18 +345,22 @@ module Solargraph
|
|
345
345
|
|
346
346
|
# @param top [Parser::AST::Node]
|
347
347
|
# @param result [Array<Range>]
|
348
|
+
# @param parent [Symbol]
|
348
349
|
# @return [void]
|
349
|
-
def inner_folding_ranges top, result = []
|
350
|
+
def inner_folding_ranges top, result = [], parent = nil
|
350
351
|
# return unless top.is_a?(::Parser::AST::Node)
|
351
352
|
return unless Parser.is_ast_node?(top)
|
352
353
|
if FOLDING_NODE_TYPES.include?(top.type)
|
353
|
-
|
354
|
-
|
355
|
-
|
354
|
+
# @todo Smelly exception for hash's first-level array in RubyVM
|
355
|
+
unless [:ARRAY, :LIST].include?(top.type) && parent == :HASH
|
356
|
+
range = Range.from_node(top)
|
357
|
+
if result.empty? || range.start.line > result.last.start.line
|
358
|
+
result.push range unless range.ending.line - range.start.line < 2
|
359
|
+
end
|
356
360
|
end
|
357
361
|
end
|
358
362
|
top.children.each do |child|
|
359
|
-
inner_folding_ranges(child, result)
|
363
|
+
inner_folding_ranges(child, result, top.type)
|
360
364
|
end
|
361
365
|
end
|
362
366
|
|
@@ -119,12 +119,12 @@ module Solargraph
|
|
119
119
|
@@inference_stack.pop
|
120
120
|
if type.defined?
|
121
121
|
possibles.push type
|
122
|
-
break if pin.is_a?(Pin::
|
122
|
+
break if pin.is_a?(Pin::Method)
|
123
123
|
end
|
124
124
|
end
|
125
125
|
if possibles.empty?
|
126
126
|
# Limit method inference recursion
|
127
|
-
return ComplexType::UNDEFINED if @@inference_depth >= 10 && pins.first.is_a?(Pin::
|
127
|
+
return ComplexType::UNDEFINED if @@inference_depth >= 10 && pins.first.is_a?(Pin::Method)
|
128
128
|
@@inference_depth += 1
|
129
129
|
pins.each do |pin|
|
130
130
|
# Avoid infinite recursion
|
@@ -134,7 +134,7 @@ module Solargraph
|
|
134
134
|
@@inference_stack.pop
|
135
135
|
if type.defined?
|
136
136
|
possibles.push type
|
137
|
-
break if pin.is_a?(Pin::
|
137
|
+
break if pin.is_a?(Pin::Method)
|
138
138
|
end
|
139
139
|
end
|
140
140
|
@@inference_depth -= 1
|
@@ -30,7 +30,7 @@ module Solargraph
|
|
30
30
|
@source = source.dup
|
31
31
|
@pins = pins
|
32
32
|
@locals = locals
|
33
|
-
environ.merge Convention.
|
33
|
+
environ.merge Convention.for_local(self) unless filename.nil?
|
34
34
|
@pin_class_hash = pins.to_set.classify(&:class).transform_values(&:to_a)
|
35
35
|
@pin_select_cache = {}
|
36
36
|
end
|
@@ -168,6 +168,9 @@ module Solargraph
|
|
168
168
|
position = Position.new(line, character)
|
169
169
|
found = nil
|
170
170
|
pins.each do |pin|
|
171
|
+
# @todo Attribute pins should not be treated like closures, but
|
172
|
+
# there's probably a better way to handle it
|
173
|
+
next if pin.is_a?(Pin::Method) && pin.attribute?
|
171
174
|
found = pin if (klasses.empty? || klasses.any? { |kls| pin.is_a?(kls) } ) && pin.location.range.contain?(position)
|
172
175
|
break if pin.location.range.start.line > line
|
173
176
|
end
|
@@ -149,7 +149,7 @@ module Solargraph
|
|
149
149
|
frag_start = cursor.start_of_word.to_s.downcase
|
150
150
|
filtered = result.uniq(&:name).select { |s|
|
151
151
|
s.name.downcase.start_with?(frag_start) &&
|
152
|
-
(!s.is_a?(Pin::
|
152
|
+
(!s.is_a?(Pin::Method) || s.name.match(/^[a-z0-9_]+(\!|\?|=)?$/i))
|
153
153
|
}
|
154
154
|
Completion.new(filtered, cursor.range)
|
155
155
|
end
|
@@ -212,7 +212,8 @@ module Solargraph
|
|
212
212
|
result.concat api_map.get_constants(context_pin.context.namespace, *gates)
|
213
213
|
result.concat api_map.get_methods(block.binder.namespace, scope: block.binder.scope, visibility: [:public, :private, :protected])
|
214
214
|
result.concat api_map.get_methods('Kernel')
|
215
|
-
result.concat ApiMap.keywords
|
215
|
+
# result.concat ApiMap.keywords
|
216
|
+
result.concat api_map.keyword_pins
|
216
217
|
result.concat yielded_self_pins
|
217
218
|
end
|
218
219
|
end
|
@@ -126,27 +126,31 @@ module Solargraph
|
|
126
126
|
namespace = closure_at(source_position)
|
127
127
|
t = (directive.tag.types.nil? || directive.tag.types.empty?) ? nil : directive.tag.types.flatten.join('')
|
128
128
|
if t.nil? || t.include?('r')
|
129
|
-
pins.push Solargraph::Pin::
|
129
|
+
pins.push Solargraph::Pin::Method.new(
|
130
130
|
location: location,
|
131
131
|
closure: namespace,
|
132
132
|
name: directive.tag.name,
|
133
133
|
comments: docstring.all.to_s,
|
134
|
-
access: :reader,
|
135
134
|
scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
|
136
135
|
visibility: :public,
|
137
|
-
explicit: false
|
136
|
+
explicit: false,
|
137
|
+
attribute: true
|
138
138
|
)
|
139
139
|
end
|
140
140
|
if t.nil? || t.include?('w')
|
141
|
-
pins.push Solargraph::Pin::
|
141
|
+
pins.push Solargraph::Pin::Method.new(
|
142
142
|
location: location,
|
143
143
|
closure: namespace,
|
144
144
|
name: "#{directive.tag.name}=",
|
145
145
|
comments: docstring.all.to_s,
|
146
|
-
access: :writer,
|
147
146
|
scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
|
148
|
-
visibility: :public
|
147
|
+
visibility: :public,
|
148
|
+
attribute: true
|
149
149
|
)
|
150
|
+
pins.last.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last)
|
151
|
+
if pins.last.return_type.defined?
|
152
|
+
pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.to_s.split(', '), 'value')
|
153
|
+
end
|
150
154
|
end
|
151
155
|
when 'parse'
|
152
156
|
begin
|
@@ -73,15 +73,15 @@ module Solargraph
|
|
73
73
|
# @return [Array<Problem>]
|
74
74
|
def method_tag_problems
|
75
75
|
result = []
|
76
|
-
# @param pin [Pin::
|
77
|
-
source_map.pins_by_class(Pin::
|
76
|
+
# @param pin [Pin::Method]
|
77
|
+
source_map.pins_by_class(Pin::Method).each do |pin|
|
78
78
|
result.concat method_return_type_problems_for(pin)
|
79
79
|
result.concat method_param_type_problems_for(pin)
|
80
80
|
end
|
81
81
|
result
|
82
82
|
end
|
83
83
|
|
84
|
-
# @param pin [Pin::
|
84
|
+
# @param pin [Pin::Method]
|
85
85
|
# @return [Array<Problem>]
|
86
86
|
def method_return_type_problems_for pin
|
87
87
|
result = []
|
@@ -115,7 +115,7 @@ module Solargraph
|
|
115
115
|
pin.location && source_map.source.comment_at?(pin.location.range.ending)
|
116
116
|
end
|
117
117
|
|
118
|
-
# @param pin [Pin::
|
118
|
+
# @param pin [Pin::Method]
|
119
119
|
# @return [Array<Problem>]
|
120
120
|
def method_param_type_problems_for pin
|
121
121
|
stack = api_map.get_method_stack(pin.namespace, pin.name, scope: pin.scope)
|
@@ -129,10 +129,10 @@ module Solargraph
|
|
129
129
|
end
|
130
130
|
end
|
131
131
|
end
|
132
|
-
params.each_pair do |name,
|
133
|
-
type =
|
132
|
+
params.each_pair do |name, data|
|
133
|
+
type = data[:qualified]
|
134
134
|
if type.undefined?
|
135
|
-
result.push Problem.new(pin.location, "Unresolved type #{
|
135
|
+
result.push Problem.new(pin.location, "Unresolved type #{data[:tagged]} for #{name} param on #{pin.path}", pin: pin)
|
136
136
|
end
|
137
137
|
end
|
138
138
|
result
|
@@ -147,14 +147,12 @@ module Solargraph
|
|
147
147
|
result = []
|
148
148
|
all_variables.each do |pin|
|
149
149
|
if pin.return_type.defined?
|
150
|
-
# @todo Somwhere in here we still need to determine if the variable is defined by an external call
|
151
150
|
declared = pin.typify(api_map)
|
152
151
|
if declared.defined?
|
153
152
|
if rules.validate_tags?
|
154
153
|
inferred = pin.probe(api_map)
|
155
154
|
if inferred.undefined?
|
156
155
|
next if rules.ignore_all_undefined?
|
157
|
-
# next unless internal?(pin) # @todo This might be redundant for variables
|
158
156
|
if declared_externally?(pin)
|
159
157
|
ignored_pins.push pin
|
160
158
|
else
|
@@ -172,7 +170,6 @@ module Solargraph
|
|
172
170
|
result.push Problem.new(pin.location, "Unresolved type #{pin.return_type} for variable #{pin.name}", pin: pin)
|
173
171
|
end
|
174
172
|
else
|
175
|
-
# @todo Check if the variable is defined by an external call
|
176
173
|
inferred = pin.probe(api_map)
|
177
174
|
if inferred.undefined? && declared_externally?(pin)
|
178
175
|
ignored_pins.push pin
|
@@ -227,7 +224,7 @@ module Solargraph
|
|
227
224
|
base = base.base
|
228
225
|
end
|
229
226
|
closest = found.typify(api_map) if found
|
230
|
-
if !found || closest.defined?
|
227
|
+
if !found || (closest.defined? && internal_or_core?(found))
|
231
228
|
unless ignored_pins.include?(found)
|
232
229
|
result.push Problem.new(location, "Unresolved call to #{missing.links.last.word}")
|
233
230
|
@marked_ranges.push rng
|
@@ -244,8 +241,8 @@ module Solargraph
|
|
244
241
|
base = chain
|
245
242
|
until base.links.length == 1 && base.undefined?
|
246
243
|
pins = base.define(api_map, block_pin, locals)
|
247
|
-
if pins.first.is_a?(Pin::
|
248
|
-
# @type [Pin::
|
244
|
+
if pins.first.is_a?(Pin::Method)
|
245
|
+
# @type [Pin::Method]
|
249
246
|
pin = pins.first
|
250
247
|
ap = if base.links.last.is_a?(Solargraph::Source::Chain::ZSuper)
|
251
248
|
arity_problems_for(pin, fake_args_for(block_pin), location)
|
@@ -269,12 +266,12 @@ module Solargraph
|
|
269
266
|
result.concat kwarg_problems_for argchain, api_map, block_pin, locals, location, pin, params, idx
|
270
267
|
break
|
271
268
|
else
|
272
|
-
ptype = params[par.name]
|
269
|
+
ptype = params.key?(par.name) ? params[par.name][:qualified] : ComplexType::UNDEFINED
|
273
270
|
if ptype.nil?
|
274
271
|
# @todo Some level (strong, I guess) should require the param here
|
275
272
|
else
|
276
273
|
argtype = argchain.infer(api_map, block_pin, locals)
|
277
|
-
if argtype.defined? && ptype && !any_types_match?(api_map, ptype, argtype)
|
274
|
+
if argtype.defined? && ptype.defined? && !any_types_match?(api_map, ptype, argtype)
|
278
275
|
result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}")
|
279
276
|
end
|
280
277
|
end
|
@@ -302,20 +299,19 @@ module Solargraph
|
|
302
299
|
result.concat kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kwargs)
|
303
300
|
else
|
304
301
|
if argchain
|
305
|
-
|
306
|
-
if
|
302
|
+
data = params[par.name]
|
303
|
+
if data.nil?
|
307
304
|
# @todo Some level (strong, I guess) should require the param here
|
308
305
|
else
|
306
|
+
ptype = data[:qualified]
|
307
|
+
next if ptype.undefined?
|
309
308
|
argtype = argchain.infer(api_map, block_pin, locals)
|
310
309
|
if argtype.defined? && ptype && !any_types_match?(api_map, ptype, argtype)
|
311
310
|
result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}")
|
312
311
|
end
|
313
312
|
end
|
314
|
-
|
315
|
-
|
316
|
-
# @todo Problem: missing required keyword argument
|
317
|
-
result.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}")
|
318
|
-
end
|
313
|
+
elsif par.decl == :kwarg
|
314
|
+
result.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}")
|
319
315
|
end
|
320
316
|
end
|
321
317
|
end
|
@@ -325,31 +321,34 @@ module Solargraph
|
|
325
321
|
def kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kwargs)
|
326
322
|
result = []
|
327
323
|
kwargs.each_pair do |pname, argchain|
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
if argtype.defined? && ptype && !any_types_match?(api_map, ptype, argtype)
|
334
|
-
result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{pname} expected #{ptype}, received #{argtype}")
|
335
|
-
end
|
324
|
+
next unless params.key?(pname.to_s)
|
325
|
+
ptype = params[pname.to_s][:qualified]
|
326
|
+
argtype = argchain.infer(api_map, block_pin, locals)
|
327
|
+
if argtype.defined? && ptype && !any_types_match?(api_map, ptype, argtype)
|
328
|
+
result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{pname} expected #{ptype}, received #{argtype}")
|
336
329
|
end
|
337
330
|
end
|
338
331
|
result
|
339
332
|
end
|
340
333
|
|
334
|
+
# @param [Pin::Method]
|
335
|
+
# @return [Hash]
|
341
336
|
def param_hash(pin)
|
342
337
|
tags = pin.docstring.tags(:param)
|
343
338
|
return {} if tags.empty?
|
344
339
|
result = {}
|
345
340
|
tags.each do |tag|
|
346
341
|
next if tag.types.nil? || tag.types.empty?
|
347
|
-
result[tag.name.to_s] =
|
342
|
+
result[tag.name.to_s] = {
|
343
|
+
tagged: tag.types.join(', '),
|
344
|
+
qualified: Solargraph::ComplexType.try_parse(*tag.types).qualify(api_map, pin.full_context.namespace)
|
345
|
+
}
|
348
346
|
end
|
349
347
|
result
|
350
348
|
end
|
351
349
|
|
352
350
|
# @param [Array<Pin::Method>]
|
351
|
+
# @return [Hash]
|
353
352
|
def first_param_hash(pins)
|
354
353
|
pins.each do |pin|
|
355
354
|
result = param_hash(pin)
|
@@ -360,9 +359,14 @@ module Solargraph
|
|
360
359
|
|
361
360
|
# @param pin [Pin::Base]
|
362
361
|
def internal? pin
|
362
|
+
return false if pin.nil?
|
363
363
|
pin.location && api_map.bundled?(pin.location.filename)
|
364
364
|
end
|
365
365
|
|
366
|
+
def internal_or_core? pin
|
367
|
+
internal?(pin) || api_map.yard_map.core_pins.include?(pin) || api_map.yard_map.stdlib_pins.include?(pin)
|
368
|
+
end
|
369
|
+
|
366
370
|
# @param pin [Pin::Base]
|
367
371
|
def external? pin
|
368
372
|
!internal? pin
|
@@ -395,7 +399,7 @@ module Solargraph
|
|
395
399
|
true
|
396
400
|
end
|
397
401
|
|
398
|
-
# @param pin [Pin::
|
402
|
+
# @param pin [Pin::Method]
|
399
403
|
def arity_problems_for(pin, arguments, location)
|
400
404
|
return [] unless pin.explicit?
|
401
405
|
return [] if pin.parameters.empty? && arguments.empty?
|
@@ -410,9 +414,12 @@ module Solargraph
|
|
410
414
|
if unchecked.empty? && pin.parameters.any? { |param| param.decl == :kwarg }
|
411
415
|
return [Problem.new(location, "Missing keyword arguments to #{pin.path}")]
|
412
416
|
end
|
417
|
+
settled_kwargs = 0
|
413
418
|
unless unchecked.empty?
|
414
|
-
|
415
|
-
|
419
|
+
if Parser.is_ast_node?(unchecked.last.node) && splatted_call?(unchecked.last.node)
|
420
|
+
settled_kwargs = pin.parameters.count(&:keyword?)
|
421
|
+
else
|
422
|
+
kwargs = convert_hash(unchecked.last.node)
|
416
423
|
if pin.parameters.any? { |param| [:kwarg, :kwoptarg].include?(param.decl) || param.kwrestarg? }
|
417
424
|
if kwargs.empty?
|
418
425
|
add_params += 1
|
@@ -422,6 +429,7 @@ module Solargraph
|
|
422
429
|
next unless param.keyword?
|
423
430
|
if kwargs.key?(param.name.to_sym)
|
424
431
|
kwargs.delete param.name.to_sym
|
432
|
+
settled_kwargs += 1
|
425
433
|
elsif param.decl == :kwarg
|
426
434
|
return [Problem.new(location, "Missing keyword argument #{param.name} to #{pin.path}")]
|
427
435
|
end
|
@@ -432,7 +440,7 @@ module Solargraph
|
|
432
440
|
end
|
433
441
|
end
|
434
442
|
end
|
435
|
-
|
443
|
+
end
|
436
444
|
end
|
437
445
|
req = required_param_count(pin)
|
438
446
|
if req + add_params < unchecked.length
|
@@ -442,24 +450,22 @@ module Solargraph
|
|
442
450
|
if unchecked.length == req + opt + 1 && unchecked.last.links.last.is_a?(Source::Chain::BlockVariable)
|
443
451
|
return []
|
444
452
|
end
|
453
|
+
if req + add_params + 1 == unchecked.length && splatted_call?(unchecked.last.node) && (pin.parameters.map(&:decl) & [:kwarg, :kwoptarg, :kwrestarg]).any?
|
454
|
+
return []
|
455
|
+
end
|
445
456
|
return [Problem.new(location, "Too many arguments to #{pin.path}")]
|
446
|
-
elsif unchecked.length < req && (arguments.empty? || !arguments.last.splat?)
|
457
|
+
elsif unchecked.length < req - settled_kwargs && (arguments.empty? || !arguments.last.splat?)
|
447
458
|
return [Problem.new(location, "Not enough arguments to #{pin.path}")]
|
448
459
|
end
|
449
460
|
[]
|
450
461
|
end
|
451
462
|
|
452
|
-
# @param pin [Pin::
|
463
|
+
# @param pin [Pin::Method]
|
453
464
|
def required_param_count(pin)
|
454
|
-
|
455
|
-
pin.parameters.each do |param|
|
456
|
-
break unless param.decl == :arg
|
457
|
-
count += 1
|
458
|
-
end
|
459
|
-
count
|
465
|
+
pin.parameters.sum { |param| %i[arg kwarg].include?(param.decl) ? 1 : 0 }
|
460
466
|
end
|
461
467
|
|
462
|
-
# @param pin [Pin::
|
468
|
+
# @param pin [Pin::Method]
|
463
469
|
def optional_param_count(pin)
|
464
470
|
count = 0
|
465
471
|
pin.parameters.each do |param|
|
@@ -1,5 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Solargraph
|
2
4
|
class TypeChecker
|
5
|
+
# Helper methods for performing type checks
|
6
|
+
#
|
3
7
|
module Checks
|
4
8
|
module_function
|
5
9
|
|
@@ -62,7 +66,7 @@ module Solargraph
|
|
62
66
|
# @param inferred [ComplexType]
|
63
67
|
# @return [Boolean]
|
64
68
|
def duck_types_match? api_map, expected, inferred
|
65
|
-
raise ArgumentError,
|
69
|
+
raise ArgumentError, 'Expected type must be duck type' unless expected.duck_type?
|
66
70
|
expected.each do |exp|
|
67
71
|
next unless exp.duck_type?
|
68
72
|
quack = exp.to_s[1..-1]
|