solargraph 0.56.1 → 0.57.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.
- checksums.yaml +4 -4
- data/.github/workflows/linting.yml +125 -0
- data/.github/workflows/plugins.yml +148 -6
- data/.github/workflows/rspec.yml +39 -4
- data/.github/workflows/typecheck.yml +5 -2
- data/.gitignore +5 -0
- data/.overcommit.yml +72 -0
- data/.rspec +1 -0
- data/.rubocop.yml +66 -0
- data/.rubocop_todo.yml +2627 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +50 -0
- data/README.md +8 -4
- data/Rakefile +125 -13
- data/lib/solargraph/api_map/cache.rb +3 -2
- data/lib/solargraph/api_map/constants.rb +218 -0
- data/lib/solargraph/api_map/index.rb +20 -26
- data/lib/solargraph/api_map/source_to_yard.rb +10 -4
- data/lib/solargraph/api_map/store.rb +126 -18
- data/lib/solargraph/api_map.rb +212 -234
- data/lib/solargraph/bench.rb +1 -0
- data/lib/solargraph/complex_type/type_methods.rb +1 -0
- data/lib/solargraph/complex_type/unique_type.rb +7 -7
- data/lib/solargraph/complex_type.rb +5 -1
- data/lib/solargraph/convention/active_support_concern.rb +111 -0
- data/lib/solargraph/convention/base.rb +17 -0
- data/lib/solargraph/convention/data_definition/data_assignment_node.rb +61 -0
- data/lib/solargraph/convention/data_definition/data_definition_node.rb +91 -0
- data/lib/solargraph/convention/data_definition.rb +105 -0
- data/lib/solargraph/convention/gemspec.rb +3 -2
- data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +2 -1
- data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +4 -2
- data/lib/solargraph/convention/struct_definition.rb +87 -24
- data/lib/solargraph/convention.rb +32 -2
- data/lib/solargraph/diagnostics/rubocop.rb +6 -1
- data/lib/solargraph/diagnostics/rubocop_helpers.rb +1 -1
- data/lib/solargraph/doc_map.rb +40 -12
- data/lib/solargraph/environ.rb +9 -2
- data/lib/solargraph/gem_pins.rb +17 -11
- data/lib/solargraph/language_server/host/dispatch.rb +2 -0
- data/lib/solargraph/language_server/host/message_worker.rb +3 -0
- data/lib/solargraph/language_server/host.rb +2 -1
- data/lib/solargraph/language_server/message/base.rb +2 -1
- data/lib/solargraph/language_server/message/extended/check_gem_version.rb +1 -1
- data/lib/solargraph/language_server/message/text_document/definition.rb +2 -0
- data/lib/solargraph/language_server/message/text_document/formatting.rb +16 -2
- data/lib/solargraph/language_server/message/text_document/type_definition.rb +1 -0
- data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +2 -0
- data/lib/solargraph/language_server/progress.rb +8 -0
- data/lib/solargraph/language_server/request.rb +1 -0
- data/lib/solargraph/library.rb +17 -24
- data/lib/solargraph/location.rb +2 -0
- data/lib/solargraph/logging.rb +11 -2
- data/lib/solargraph/page.rb +4 -0
- data/lib/solargraph/parser/comment_ripper.rb +8 -1
- data/lib/solargraph/parser/flow_sensitive_typing.rb +32 -4
- data/lib/solargraph/parser/node_methods.rb +2 -2
- data/lib/solargraph/parser/node_processor/base.rb +10 -5
- data/lib/solargraph/parser/node_processor.rb +24 -8
- data/lib/solargraph/parser/parser_gem/class_methods.rb +1 -1
- data/lib/solargraph/parser/parser_gem/flawed_builder.rb +1 -0
- data/lib/solargraph/parser/parser_gem/node_chainer.rb +3 -1
- data/lib/solargraph/parser/parser_gem/node_methods.rb +4 -2
- data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +3 -2
- data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +1 -21
- data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +2 -0
- data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +7 -1
- data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +0 -22
- data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +1 -0
- data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -0
- data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +1 -0
- data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +35 -14
- data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +1 -0
- data/lib/solargraph/parser/parser_gem/node_processors.rb +4 -0
- data/lib/solargraph/parser/region.rb +3 -0
- data/lib/solargraph/parser/snippet.rb +2 -0
- data/lib/solargraph/pin/base.rb +65 -8
- data/lib/solargraph/pin/base_variable.rb +1 -2
- data/lib/solargraph/pin/callable.rb +9 -0
- data/lib/solargraph/pin/closure.rb +2 -0
- data/lib/solargraph/pin/common.rb +6 -2
- data/lib/solargraph/pin/constant.rb +2 -0
- data/lib/solargraph/pin/delegated_method.rb +1 -0
- data/lib/solargraph/pin/local_variable.rb +4 -1
- data/lib/solargraph/pin/method.rb +12 -7
- data/lib/solargraph/pin/method_alias.rb +3 -0
- data/lib/solargraph/pin/parameter.rb +18 -8
- data/lib/solargraph/pin/proxy_type.rb +1 -0
- data/lib/solargraph/pin/reference/override.rb +15 -1
- data/lib/solargraph/pin/reference/superclass.rb +5 -0
- data/lib/solargraph/pin/reference.rb +26 -0
- data/lib/solargraph/pin/search.rb +3 -1
- data/lib/solargraph/pin/signature.rb +2 -0
- data/lib/solargraph/pin/symbol.rb +5 -0
- data/lib/solargraph/pin_cache.rb +64 -4
- data/lib/solargraph/position.rb +2 -0
- data/lib/solargraph/range.rb +1 -0
- data/lib/solargraph/rbs_map/conversions.rb +47 -18
- data/lib/solargraph/rbs_map/core_map.rb +3 -0
- data/lib/solargraph/rbs_map.rb +15 -2
- data/lib/solargraph/shell.rb +3 -0
- data/lib/solargraph/source/chain/link.rb +10 -1
- data/lib/solargraph/source/chain.rb +9 -2
- data/lib/solargraph/source/change.rb +2 -2
- data/lib/solargraph/source/cursor.rb +2 -3
- data/lib/solargraph/source/source_chainer.rb +1 -1
- data/lib/solargraph/source.rb +5 -2
- data/lib/solargraph/source_map/clip.rb +1 -1
- data/lib/solargraph/source_map/data.rb +4 -0
- data/lib/solargraph/source_map/mapper.rb +4 -2
- data/lib/solargraph/source_map.rb +21 -14
- data/lib/solargraph/type_checker/param_def.rb +2 -0
- data/lib/solargraph/type_checker/rules.rb +8 -0
- data/lib/solargraph/type_checker.rb +173 -120
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/workspace/config.rb +1 -3
- data/lib/solargraph/workspace/require_paths.rb +98 -0
- data/lib/solargraph/workspace.rb +16 -48
- data/lib/solargraph/yard_map/helpers.rb +29 -1
- data/lib/solargraph/yard_map/mapper/to_constant.rb +5 -5
- data/lib/solargraph/yard_map/mapper/to_method.rb +3 -8
- data/lib/solargraph/yard_map/mapper/to_namespace.rb +7 -7
- data/lib/solargraph/yardoc.rb +16 -3
- data/lib/solargraph.rb +15 -0
- data/rbs/fills/tuple.rbs +2 -3
- data/sig/shims/parser/3.2.0.1/builders/default.rbs +195 -0
- data/sig/shims/thor/1.2.0.1/.rbs_meta.yaml +9 -0
- data/sig/shims/thor/1.2.0.1/manifest.yaml +7 -0
- data/sig/shims/thor/1.2.0.1/thor.rbs +17 -0
- data/solargraph.gemspec +14 -4
- metadata +126 -9
- data/lib/.rubocop.yml +0 -22
@@ -21,6 +21,11 @@ module Solargraph
|
|
21
21
|
data.pins
|
22
22
|
end
|
23
23
|
|
24
|
+
# @return [Array<Pin::Base>]
|
25
|
+
def all_pins
|
26
|
+
pins + convention_pins
|
27
|
+
end
|
28
|
+
|
24
29
|
# @return [Array<Pin::LocalVariable>]
|
25
30
|
def locals
|
26
31
|
data.locals
|
@@ -30,13 +35,18 @@ module Solargraph
|
|
30
35
|
def initialize source
|
31
36
|
@source = source
|
32
37
|
|
33
|
-
|
34
|
-
|
38
|
+
conventions_environ.merge Convention.for_local(self) unless filename.nil?
|
39
|
+
# FIXME: unmemoizing the document_symbols in case it was called and memoized from any of conventions above
|
40
|
+
# this is to ensure that the convention_pins from all conventions are used in the document_symbols.
|
41
|
+
# solargraph-rails is known to use this method to get the document symbols. It should probably be removed.
|
42
|
+
@document_symbols = nil
|
43
|
+
self.convention_pins = conventions_environ.pins
|
35
44
|
@pin_select_cache = {}
|
36
45
|
end
|
37
46
|
|
38
|
-
# @
|
39
|
-
# @
|
47
|
+
# @generic T
|
48
|
+
# @param klass [Class<generic<T>>]
|
49
|
+
# @return [Array<generic<T>>]
|
40
50
|
def pins_by_class klass
|
41
51
|
@pin_select_cache[klass] ||= pin_class_hash.select { |key, _| key <= klass }.values.flatten
|
42
52
|
end
|
@@ -67,8 +77,8 @@ module Solargraph
|
|
67
77
|
end
|
68
78
|
|
69
79
|
# @return [Environ]
|
70
|
-
def
|
71
|
-
@
|
80
|
+
def conventions_environ
|
81
|
+
@conventions_environ ||= Environ.new
|
72
82
|
end
|
73
83
|
|
74
84
|
# all pins except Solargraph::Pin::Reference::Reference
|
@@ -158,10 +168,15 @@ module Solargraph
|
|
158
168
|
|
159
169
|
private
|
160
170
|
|
171
|
+
# @return [Hash{Class => Array<Pin::Base>}]
|
172
|
+
# @return [Array<Pin::Base>]
|
173
|
+
attr_writer :convention_pins
|
174
|
+
|
161
175
|
def pin_class_hash
|
162
176
|
@pin_class_hash ||= pins.to_set.classify(&:class).transform_values(&:to_a)
|
163
177
|
end
|
164
178
|
|
179
|
+
# @return [Data]
|
165
180
|
def data
|
166
181
|
@data ||= Data.new(source)
|
167
182
|
end
|
@@ -171,14 +186,6 @@ module Solargraph
|
|
171
186
|
@convention_pins || []
|
172
187
|
end
|
173
188
|
|
174
|
-
# @param pins [Array<Pin::Base>]
|
175
|
-
# @return [Array<Pin::Base>]
|
176
|
-
def convention_pins=(pins)
|
177
|
-
# unmemoizing the document_symbols in case it was called from any of conventions
|
178
|
-
@document_symbols = nil
|
179
|
-
@convention_pins = pins
|
180
|
-
end
|
181
|
-
|
182
189
|
# @param line [Integer]
|
183
190
|
# @param character [Integer]
|
184
191
|
# @param klasses [Array<Class>]
|
@@ -57,6 +57,14 @@ module Solargraph
|
|
57
57
|
def require_all_return_types_match_inferred?
|
58
58
|
rank >= LEVELS[:alpha]
|
59
59
|
end
|
60
|
+
|
61
|
+
# We keep this at strong because if you added an @ sg-ignore to
|
62
|
+
# address a strong-level issue, then ran at a lower level, you'd
|
63
|
+
# get a false positive - we don't run stronger level checks than
|
64
|
+
# requested for performance reasons
|
65
|
+
def validate_sg_ignores?
|
66
|
+
rank >= LEVELS[:strong]
|
67
|
+
end
|
60
68
|
end
|
61
69
|
end
|
62
70
|
end
|
@@ -38,15 +38,20 @@ module Solargraph
|
|
38
38
|
@source_map ||= api_map.source_map(filename)
|
39
39
|
end
|
40
40
|
|
41
|
+
# @return [Source]
|
42
|
+
def source
|
43
|
+
@source_map.source
|
44
|
+
end
|
45
|
+
|
41
46
|
# @return [Array<Problem>]
|
42
47
|
def problems
|
43
48
|
@problems ||= begin
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
49
|
+
all = method_tag_problems
|
50
|
+
.concat(variable_type_tag_problems)
|
51
|
+
.concat(const_problems)
|
52
|
+
.concat(call_problems)
|
53
|
+
unignored = without_ignored(all)
|
54
|
+
unignored.concat(unneeded_sgignore_problems)
|
50
55
|
end
|
51
56
|
end
|
52
57
|
|
@@ -140,7 +145,7 @@ module Solargraph
|
|
140
145
|
|
141
146
|
# @param pin [Pin::Base]
|
142
147
|
def virtual_pin? pin
|
143
|
-
pin.location &&
|
148
|
+
pin.location && source.comment_at?(pin.location.range.ending)
|
144
149
|
end
|
145
150
|
|
146
151
|
# @param pin [Pin::Method]
|
@@ -231,7 +236,7 @@ module Solargraph
|
|
231
236
|
def const_problems
|
232
237
|
return [] unless rules.validate_consts?
|
233
238
|
result = []
|
234
|
-
Solargraph::Parser::NodeMethods.const_nodes_from(
|
239
|
+
Solargraph::Parser::NodeMethods.const_nodes_from(source.node).each do |const|
|
235
240
|
rng = Solargraph::Range.from_node(const)
|
236
241
|
chain = Solargraph::Parser.chain(const, filename)
|
237
242
|
block_pin = source_map.locate_block_pin(rng.start.line, rng.start.column)
|
@@ -249,7 +254,7 @@ module Solargraph
|
|
249
254
|
# @return [Array<Problem>]
|
250
255
|
def call_problems
|
251
256
|
result = []
|
252
|
-
Solargraph::Parser::NodeMethods.call_nodes_from(
|
257
|
+
Solargraph::Parser::NodeMethods.call_nodes_from(source.node).each do |call|
|
253
258
|
rng = Solargraph::Range.from_node(call)
|
254
259
|
next if @marked_ranges.any? { |d| d.contain?(rng.start) }
|
255
260
|
chain = Solargraph::Parser.chain(call, filename)
|
@@ -272,7 +277,11 @@ module Solargraph
|
|
272
277
|
# @todo remove the internal_or_core? check at a higher-than-strict level
|
273
278
|
if !found || found.is_a?(Pin::BaseVariable) || (closest.defined? && internal_or_core?(found))
|
274
279
|
unless closest.generic? || ignored_pins.include?(found)
|
275
|
-
|
280
|
+
if closest.defined?
|
281
|
+
result.push Problem.new(location, "Unresolved call to #{missing.links.last.word} on #{closest}")
|
282
|
+
else
|
283
|
+
result.push Problem.new(location, "Unresolved call to #{missing.links.last.word}")
|
284
|
+
end
|
276
285
|
@marked_ranges.push rng
|
277
286
|
end
|
278
287
|
end
|
@@ -284,127 +293,136 @@ module Solargraph
|
|
284
293
|
|
285
294
|
# @param chain [Solargraph::Source::Chain]
|
286
295
|
# @param api_map [Solargraph::ApiMap]
|
287
|
-
# @param
|
296
|
+
# @param closure_pin [Solargraph::Pin::Closure]
|
288
297
|
# @param locals [Array<Solargraph::Pin::Base>]
|
289
298
|
# @param location [Solargraph::Location]
|
290
299
|
# @return [Array<Problem>]
|
291
|
-
def argument_problems_for chain, api_map,
|
300
|
+
def argument_problems_for chain, api_map, closure_pin, locals, location
|
292
301
|
result = []
|
293
302
|
base = chain
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
303
|
+
# @type last_base_link [Solargraph::Source::Chain::Call]
|
304
|
+
last_base_link = base.links.last
|
305
|
+
return [] unless last_base_link.is_a?(Solargraph::Source::Chain::Call)
|
306
|
+
|
307
|
+
arguments = last_base_link.arguments
|
308
|
+
|
309
|
+
pins = base.define(api_map, closure_pin, locals)
|
310
|
+
|
311
|
+
first_pin = pins.first
|
312
|
+
unresolvable = first_pin.is_a?(Pin::DelegatedMethod) && !first_pin.resolvable?(api_map)
|
313
|
+
if !unresolvable && first_pin.is_a?(Pin::Method)
|
314
|
+
# @type [Pin::Method]
|
315
|
+
pin = first_pin
|
316
|
+
ap = if base.links.last.is_a?(Solargraph::Source::Chain::ZSuper)
|
317
|
+
arity_problems_for(pin, fake_args_for(closure_pin), location)
|
318
|
+
elsif pin.path == 'Class#new'
|
319
|
+
fqns = if base.links.one?
|
320
|
+
closure_pin.namespace
|
321
|
+
else
|
322
|
+
base.base.infer(api_map, closure_pin, locals).namespace
|
323
|
+
end
|
324
|
+
init = api_map.get_method_stack(fqns, 'initialize').first
|
325
|
+
|
326
|
+
init ? arity_problems_for(init, arguments, location) : []
|
327
|
+
else
|
328
|
+
arity_problems_for(pin, arguments, location)
|
329
|
+
end
|
330
|
+
return ap unless ap.empty?
|
331
|
+
return [] if !rules.validate_calls? || base.links.first.is_a?(Solargraph::Source::Chain::ZSuper)
|
332
|
+
|
333
|
+
params = first_param_hash(pins)
|
334
|
+
|
335
|
+
all_errors = []
|
336
|
+
pin.signatures.sort { |sig| sig.parameters.length }.each do |sig|
|
337
|
+
signature_errors = signature_argument_problems_for location, locals, closure_pin, params, arguments, sig, pin
|
338
|
+
if signature_errors.empty?
|
339
|
+
# we found a signature that works - meaning errors from
|
340
|
+
# other signatures don't matter.
|
341
|
+
return []
|
342
|
+
end
|
343
|
+
all_errors.concat signature_errors
|
344
|
+
end
|
345
|
+
result.concat all_errors
|
346
|
+
end
|
347
|
+
result
|
348
|
+
end
|
349
|
+
|
350
|
+
# @param location [Location]
|
351
|
+
# @param locals [Array<Pin::LocalVariable>]
|
352
|
+
# @param closure_pin [Pin::Closure]
|
353
|
+
# @param params [Hash{String => Hash{Symbol => String, Solargraph::ComplexType}}]
|
354
|
+
# @param arguments [Array<Source::Chain>]
|
355
|
+
# @param sig [Pin::Signature]
|
356
|
+
# @param pin [Pin::Method]
|
357
|
+
# @param pins [Array<Pin::Method>]
|
358
|
+
#
|
359
|
+
# @return [Array<Problem>]
|
360
|
+
def signature_argument_problems_for location, locals, closure_pin, params, arguments, sig, pin
|
361
|
+
errors = []
|
362
|
+
# @todo add logic mapping up restarg parameters with
|
363
|
+
# arguments (including restarg arguments). Use tuples
|
364
|
+
# when possible, and when not, ensure provably
|
365
|
+
# incorrect situations are detected.
|
366
|
+
sig.parameters.each_with_index do |par, idx|
|
367
|
+
return errors if par.decl == :restarg # bail out and assume the rest is valid pending better arg processing
|
368
|
+
argchain = arguments[idx]
|
369
|
+
if argchain.nil?
|
370
|
+
if par.decl == :arg
|
371
|
+
final_arg = arguments.last
|
372
|
+
if final_arg && final_arg.node.type == :splat
|
373
|
+
argchain = final_arg
|
374
|
+
return errors
|
313
375
|
else
|
314
|
-
|
376
|
+
errors.push Problem.new(location, "Not enough arguments to #{pin.path}")
|
315
377
|
end
|
316
|
-
init = api_map.get_method_stack(fqns, 'initialize').first
|
317
|
-
init ? arity_problems_for(init, arguments, location) : []
|
318
378
|
else
|
319
|
-
|
320
|
-
|
321
|
-
unless ap.empty?
|
322
|
-
result.concat ap
|
323
|
-
break
|
379
|
+
final_arg = arguments.last
|
380
|
+
argchain = final_arg if final_arg && [:kwsplat, :hash].include?(final_arg.node.type)
|
324
381
|
end
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
#
|
336
|
-
#
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
final_arg = arguments.last
|
342
|
-
if final_arg && final_arg.node.type == :splat
|
343
|
-
argchain = final_arg
|
344
|
-
next # don't try to apply the type of the splat - unlikely to be specific enough
|
345
|
-
else
|
346
|
-
errors.push Problem.new(location, "Not enough arguments to #{pin.path}")
|
347
|
-
next
|
348
|
-
end
|
349
|
-
else
|
350
|
-
final_arg = arguments.last
|
351
|
-
argchain = final_arg if final_arg && [:kwsplat, :hash].include?(final_arg.node.type)
|
352
|
-
end
|
353
|
-
end
|
354
|
-
if argchain
|
355
|
-
if par.decl != :arg
|
356
|
-
errors.concat kwarg_problems_for sig, argchain, api_map, block_pin, locals, location, pin, params, idx
|
357
|
-
next
|
358
|
-
else
|
359
|
-
if argchain.node.type == :splat && argchain == arguments.last
|
360
|
-
final_arg = argchain
|
361
|
-
end
|
362
|
-
if (final_arg && final_arg.node.type == :splat)
|
363
|
-
# The final argument given has been seen and was a
|
364
|
-
# splat, which doesn't give us useful types or
|
365
|
-
# arities against positional parameters, so let's
|
366
|
-
# continue on in case there are any required
|
367
|
-
# kwargs we should warn about
|
368
|
-
next
|
369
|
-
end
|
370
|
-
|
371
|
-
if argchain.node.type == :splat && par != sig.parameters.last
|
372
|
-
# we have been given a splat and there are more
|
373
|
-
# arguments to come.
|
374
|
-
|
375
|
-
# @todo Improve this so that we can skip past the
|
376
|
-
# rest of the positional parameters here but still
|
377
|
-
# process the kwargs
|
378
|
-
break
|
379
|
-
end
|
380
|
-
ptype = params.key?(par.name) ? params[par.name][:qualified] : ComplexType::UNDEFINED
|
381
|
-
ptype = ptype.self_to_type(par.context)
|
382
|
-
if ptype.nil?
|
383
|
-
# @todo Some level (strong, I guess) should require the param here
|
384
|
-
else
|
385
|
-
argtype = argchain.infer(api_map, block_pin, locals)
|
386
|
-
if argtype.defined? && ptype.defined? && !any_types_match?(api_map, ptype, argtype)
|
387
|
-
errors.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}")
|
388
|
-
next
|
389
|
-
end
|
390
|
-
end
|
391
|
-
end
|
392
|
-
elsif par.decl == :kwarg
|
393
|
-
errors.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}")
|
394
|
-
next
|
395
|
-
end
|
382
|
+
end
|
383
|
+
if argchain
|
384
|
+
if par.decl != :arg
|
385
|
+
errors.concat kwarg_problems_for sig, argchain, api_map, closure_pin, locals, location, pin, params, idx
|
386
|
+
next
|
387
|
+
else
|
388
|
+
if argchain.node.type == :splat && argchain == arguments.last
|
389
|
+
final_arg = argchain
|
390
|
+
end
|
391
|
+
if (final_arg && final_arg.node.type == :splat)
|
392
|
+
# The final argument given has been seen and was a
|
393
|
+
# splat, which doesn't give us useful types or
|
394
|
+
# arities against positional parameters, so let's
|
395
|
+
# continue on in case there are any required
|
396
|
+
# kwargs we should warn about
|
397
|
+
next
|
396
398
|
end
|
397
|
-
if
|
398
|
-
|
399
|
-
|
399
|
+
if argchain.node.type == :splat && par != sig.parameters.last
|
400
|
+
# we have been given a splat and there are more
|
401
|
+
# arguments to come.
|
402
|
+
|
403
|
+
# @todo Improve this so that we can skip past the
|
404
|
+
# rest of the positional parameters here but still
|
405
|
+
# process the kwargs
|
406
|
+
return errors
|
407
|
+
end
|
408
|
+
ptype = params.key?(par.name) ? params[par.name][:qualified] : ComplexType::UNDEFINED
|
409
|
+
ptype = ptype.self_to_type(par.context)
|
410
|
+
if ptype.nil?
|
411
|
+
# @todo Some level (strong, I guess) should require the param here
|
412
|
+
else
|
413
|
+
argtype = argchain.infer(api_map, closure_pin, locals)
|
414
|
+
if argtype.defined? && ptype.defined? && !any_types_match?(api_map, ptype, argtype)
|
415
|
+
errors.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}")
|
416
|
+
return errors
|
417
|
+
end
|
400
418
|
end
|
401
|
-
all_errors.concat errors
|
402
419
|
end
|
403
|
-
|
420
|
+
elsif par.decl == :kwarg
|
421
|
+
errors.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}")
|
422
|
+
next
|
404
423
|
end
|
405
|
-
base = base.base
|
406
424
|
end
|
407
|
-
|
425
|
+
errors
|
408
426
|
end
|
409
427
|
|
410
428
|
# @param sig [Pin::Signature]
|
@@ -646,7 +664,6 @@ module Solargraph
|
|
646
664
|
# @param parameters [Enumerable<Pin::Parameter>]
|
647
665
|
# @todo need to use generic types in method to choose correct
|
648
666
|
# signature and generate Integer as return type
|
649
|
-
# @sg-ignore
|
650
667
|
# @return [Integer]
|
651
668
|
def required_param_count(parameters)
|
652
669
|
parameters.sum { |param| %i[arg kwarg].include?(param.decl) ? 1 : 0 }
|
@@ -687,12 +704,48 @@ module Solargraph
|
|
687
704
|
args
|
688
705
|
end
|
689
706
|
|
707
|
+
# @return [Set<Integer>]
|
708
|
+
def sg_ignore_lines_processed
|
709
|
+
@sg_ignore_lines_processed ||= Set.new
|
710
|
+
end
|
711
|
+
|
712
|
+
# @return [Set<Integer>]
|
713
|
+
def all_sg_ignore_lines
|
714
|
+
source.associated_comments.select do |_line, text|
|
715
|
+
text.include?('@sg-ignore')
|
716
|
+
end.keys.to_set
|
717
|
+
end
|
718
|
+
|
719
|
+
# @return [Array<Integer>]
|
720
|
+
def unprocessed_sg_ignore_lines
|
721
|
+
(all_sg_ignore_lines - sg_ignore_lines_processed).to_a.sort
|
722
|
+
end
|
723
|
+
|
724
|
+
# @return [Array<Problem>]
|
725
|
+
def unneeded_sgignore_problems
|
726
|
+
return [] unless rules.validate_sg_ignores?
|
727
|
+
|
728
|
+
unprocessed_sg_ignore_lines.map do |line|
|
729
|
+
Problem.new(
|
730
|
+
Location.new(filename, Range.from_to(line, 0, line, 0)),
|
731
|
+
'Unneeded @sg-ignore comment'
|
732
|
+
)
|
733
|
+
end
|
734
|
+
end
|
735
|
+
|
690
736
|
# @param problems [Array<Problem>]
|
691
737
|
# @return [Array<Problem>]
|
692
738
|
def without_ignored problems
|
693
739
|
problems.reject do |problem|
|
694
|
-
node =
|
695
|
-
node &&
|
740
|
+
node = source.node_at(problem.location.range.start.line, problem.location.range.start.column)
|
741
|
+
ignored = node && source.comments_for(node)&.include?('@sg-ignore')
|
742
|
+
unless !ignored || all_sg_ignore_lines.include?(problem.location.range.start.line)
|
743
|
+
# :nocov:
|
744
|
+
Solargraph.assert_or_log(:sg_ignore) { "@sg-ignore accounting issue - node is #{node}" }
|
745
|
+
# :nocov:
|
746
|
+
end
|
747
|
+
sg_ignore_lines_processed.add problem.location.range.start.line if ignored
|
748
|
+
ignored
|
696
749
|
end
|
697
750
|
end
|
698
751
|
end
|
data/lib/solargraph/version.rb
CHANGED
@@ -90,7 +90,6 @@ module Solargraph
|
|
90
90
|
|
91
91
|
# A hash of options supported by the formatter
|
92
92
|
#
|
93
|
-
# @sg-ignore pending https://github.com/castwide/solargraph/pull/905
|
94
93
|
# @return [Hash]
|
95
94
|
def formatter
|
96
95
|
raw_data['formatter']
|
@@ -105,7 +104,6 @@ module Solargraph
|
|
105
104
|
|
106
105
|
# The maximum number of files to parse from the workspace.
|
107
106
|
#
|
108
|
-
# @sg-ignore pending https://github.com/castwide/solargraph/pull/905
|
109
107
|
# @return [Integer]
|
110
108
|
def max_files
|
111
109
|
raw_data['max_files']
|
@@ -151,7 +149,7 @@ module Solargraph
|
|
151
149
|
# @return [Hash{String => Array, Hash, Integer}]
|
152
150
|
def default_config
|
153
151
|
{
|
154
|
-
'include' => ['**/*.rb'],
|
152
|
+
'include' => ['Rakefile', 'Gemfile', '*.gemspec', '**/*.rb'],
|
155
153
|
'exclude' => ['spec/**/*', 'test/**/*', 'vendor/**/*', '.bundle/**/*'],
|
156
154
|
'require' => [],
|
157
155
|
'domains' => [],
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
|
5
|
+
module Solargraph
|
6
|
+
# A workspace consists of the files in a project's directory and the
|
7
|
+
# project's configuration. It provides a Source for each file to be used
|
8
|
+
# in an associated Library or ApiMap.
|
9
|
+
#
|
10
|
+
class Workspace
|
11
|
+
# Manages determining which gemspecs are available in a workspace
|
12
|
+
class RequirePaths
|
13
|
+
attr_reader :directory, :config
|
14
|
+
|
15
|
+
# @param directory [String, nil]
|
16
|
+
# @param config [Config, nil]
|
17
|
+
def initialize directory, config
|
18
|
+
@directory = directory
|
19
|
+
@config = config
|
20
|
+
end
|
21
|
+
|
22
|
+
# Generate require paths from gemspecs if they exist or assume the default
|
23
|
+
# lib directory.
|
24
|
+
#
|
25
|
+
# @return [Array<String>]
|
26
|
+
def generate
|
27
|
+
result = require_paths_from_gemspec_files
|
28
|
+
return configured_require_paths if result.empty?
|
29
|
+
result.concat(config.require_paths.map { |p| File.join(directory, p) }) if config
|
30
|
+
result
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# @return [Array<String>]
|
36
|
+
def require_paths_from_gemspec_files
|
37
|
+
results = []
|
38
|
+
gemspec_file_paths.each do |gemspec_file_path|
|
39
|
+
results.concat require_path_from_gemspec_file(gemspec_file_path)
|
40
|
+
end
|
41
|
+
results
|
42
|
+
end
|
43
|
+
|
44
|
+
# Get an array of all gemspec files in the workspace.
|
45
|
+
#
|
46
|
+
# @return [Array<String>]
|
47
|
+
def gemspec_file_paths
|
48
|
+
return [] if directory.nil?
|
49
|
+
@gemspec_file_paths ||= Dir[File.join(directory, '**/*.gemspec')].select do |gs|
|
50
|
+
config.nil? || config.allow?(gs)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Get additional require paths defined in the configuration.
|
55
|
+
#
|
56
|
+
# @return [Array<String>]
|
57
|
+
def configured_require_paths
|
58
|
+
return ['lib'] unless directory
|
59
|
+
return [File.join(directory, 'lib')] if !config || config.require_paths.empty?
|
60
|
+
config.require_paths.map { |p| File.join(directory, p) }
|
61
|
+
end
|
62
|
+
|
63
|
+
# Generate require paths from gemspecs if they exist or assume the default
|
64
|
+
# lib directory.
|
65
|
+
#
|
66
|
+
# @param gemspec_file_path [String]
|
67
|
+
# @return [Array<String>]
|
68
|
+
def require_path_from_gemspec_file gemspec_file_path
|
69
|
+
base = File.dirname(gemspec_file_path)
|
70
|
+
# HACK: Evaluating gemspec files violates the goal of not running
|
71
|
+
# workspace code, but this is how Gem::Specification.load does it
|
72
|
+
# anyway.
|
73
|
+
cmd = ['ruby', '-e',
|
74
|
+
"require 'rubygems'; " \
|
75
|
+
"require 'json'; " \
|
76
|
+
"spec = eval(File.read('#{gemspec_file_path}'), TOPLEVEL_BINDING, '#{gemspec_file_path}'); " \
|
77
|
+
'return unless Gem::Specification === spec; ' \
|
78
|
+
'puts({name: spec.name, paths: spec.require_paths}.to_json)']
|
79
|
+
# @sg-ignore Unresolved call to capture3 on Module<Open3>
|
80
|
+
o, e, s = Open3.capture3(*cmd)
|
81
|
+
if s.success?
|
82
|
+
begin
|
83
|
+
hash = o && !o.empty? ? JSON.parse(o.split("\n").last) : {}
|
84
|
+
return [] if hash.empty?
|
85
|
+
hash['paths'].map { |path| File.join(base, path) }
|
86
|
+
rescue StandardError => e
|
87
|
+
Solargraph.logger.warn "Error reading #{gemspec_file_path}: [#{e.class}] #{e.message}"
|
88
|
+
[]
|
89
|
+
end
|
90
|
+
else
|
91
|
+
Solargraph.logger.warn "Error reading #{gemspec_file_path}"
|
92
|
+
Solargraph.logger.warn e
|
93
|
+
[]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|