solargraph 0.54.1 → 0.55.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/CHANGELOG.md +39 -0
- data/lib/solargraph/api_map/cache.rb +10 -1
- data/lib/solargraph/api_map/index.rb +167 -0
- data/lib/solargraph/api_map/store.rb +71 -122
- data/lib/solargraph/api_map.rb +97 -36
- data/lib/solargraph/bench.rb +17 -1
- data/lib/solargraph/complex_type/type_methods.rb +7 -0
- data/lib/solargraph/complex_type/unique_type.rb +109 -8
- data/lib/solargraph/complex_type.rb +48 -18
- data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +51 -0
- data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +100 -0
- data/lib/solargraph/convention/struct_definition.rb +101 -0
- data/lib/solargraph/convention.rb +2 -0
- data/lib/solargraph/doc_map.rb +43 -18
- data/lib/solargraph/equality.rb +33 -0
- data/lib/solargraph/language_server/host/message_worker.rb +31 -11
- data/lib/solargraph/language_server/host.rb +13 -11
- data/lib/solargraph/language_server/message/base.rb +19 -12
- data/lib/solargraph/language_server/message/initialize.rb +3 -1
- data/lib/solargraph/language_server/message/text_document/completion.rb +0 -3
- data/lib/solargraph/language_server/message/text_document/formatting.rb +4 -0
- data/lib/solargraph/library.rb +7 -8
- data/lib/solargraph/location.rb +15 -0
- data/lib/solargraph/parser/comment_ripper.rb +11 -6
- data/lib/solargraph/parser/flow_sensitive_typing.rb +226 -0
- data/lib/solargraph/parser/node_methods.rb +15 -1
- data/lib/solargraph/parser/parser_gem/class_methods.rb +11 -6
- data/lib/solargraph/parser/parser_gem/node_chainer.rb +17 -23
- data/lib/solargraph/parser/parser_gem/node_methods.rb +5 -3
- data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
- data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +21 -1
- data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +21 -0
- data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -2
- data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +26 -5
- data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +41 -0
- data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +28 -0
- data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +28 -0
- data/lib/solargraph/parser/parser_gem/node_processors.rb +10 -0
- data/lib/solargraph/parser.rb +1 -0
- data/lib/solargraph/pin/base.rb +35 -19
- data/lib/solargraph/pin/base_variable.rb +10 -3
- data/lib/solargraph/pin/block.rb +3 -3
- data/lib/solargraph/pin/breakable.rb +9 -0
- data/lib/solargraph/pin/callable.rb +3 -3
- data/lib/solargraph/pin/local_variable.rb +7 -1
- data/lib/solargraph/pin/method.rb +26 -18
- data/lib/solargraph/pin/namespace.rb +10 -7
- data/lib/solargraph/pin/parameter.rb +15 -6
- data/lib/solargraph/pin/proxy_type.rb +12 -6
- data/lib/solargraph/pin/until.rb +18 -0
- data/lib/solargraph/pin/while.rb +18 -0
- data/lib/solargraph/pin.rb +3 -0
- data/lib/solargraph/position.rb +7 -0
- data/lib/solargraph/range.rb +7 -0
- data/lib/solargraph/rbs_map/conversions.rb +16 -10
- data/lib/solargraph/rbs_map/core_fills.rb +10 -3
- data/lib/solargraph/rbs_map.rb +1 -0
- data/lib/solargraph/shell.rb +2 -0
- data/lib/solargraph/source/chain/array.rb +5 -4
- data/lib/solargraph/source/chain/call.rb +55 -19
- data/lib/solargraph/source/chain/constant.rb +1 -1
- data/lib/solargraph/source/chain/hash.rb +8 -2
- data/lib/solargraph/source/chain/if.rb +5 -0
- data/lib/solargraph/source/chain/link.rb +19 -5
- data/lib/solargraph/source/chain/literal.rb +27 -2
- data/lib/solargraph/source/chain/z_super.rb +1 -1
- data/lib/solargraph/source/chain.rb +107 -63
- data/lib/solargraph/source/cursor.rb +1 -11
- data/lib/solargraph/source/source_chainer.rb +2 -2
- data/lib/solargraph/source.rb +2 -1
- data/lib/solargraph/source_map/clip.rb +4 -2
- data/lib/solargraph/type_checker/checks.rb +4 -0
- data/lib/solargraph/type_checker.rb +41 -14
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/workspace/config.rb +7 -3
- data/lib/solargraph/workspace.rb +1 -1
- data/lib/solargraph/yard_map/mapper/to_constant.rb +1 -0
- data/lib/solargraph/yard_map/mapper/to_method.rb +42 -15
- data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -0
- data/lib/solargraph/yard_map/mapper.rb +1 -0
- data/lib/solargraph/yardoc.rb +1 -1
- data/lib/solargraph.rb +1 -0
- data/solargraph.gemspec +5 -5
- metadata +39 -25
data/lib/solargraph/api_map.rb
CHANGED
|
@@ -12,6 +12,7 @@ module Solargraph
|
|
|
12
12
|
autoload :Cache, 'solargraph/api_map/cache'
|
|
13
13
|
autoload :SourceToYard, 'solargraph/api_map/source_to_yard'
|
|
14
14
|
autoload :Store, 'solargraph/api_map/store'
|
|
15
|
+
autoload :Index, 'solargraph/api_map/index'
|
|
15
16
|
|
|
16
17
|
# @return [Array<String>]
|
|
17
18
|
attr_reader :unresolved_requires
|
|
@@ -48,6 +49,15 @@ module Solargraph
|
|
|
48
49
|
equality_fields.hash
|
|
49
50
|
end
|
|
50
51
|
|
|
52
|
+
def to_s
|
|
53
|
+
self.class.to_s
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# avoid enormous dump
|
|
57
|
+
def inspect
|
|
58
|
+
to_s
|
|
59
|
+
end
|
|
60
|
+
|
|
51
61
|
# @param pins [Array<Pin::Base>]
|
|
52
62
|
# @return [self]
|
|
53
63
|
def index pins
|
|
@@ -56,7 +66,7 @@ module Solargraph
|
|
|
56
66
|
@source_map_hash = {}
|
|
57
67
|
implicit.clear
|
|
58
68
|
cache.clear
|
|
59
|
-
|
|
69
|
+
store.update @@core_map.pins, pins
|
|
60
70
|
self
|
|
61
71
|
end
|
|
62
72
|
|
|
@@ -75,10 +85,9 @@ module Solargraph
|
|
|
75
85
|
# @param bench [Bench]
|
|
76
86
|
# @return [self]
|
|
77
87
|
def catalog bench
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
pins = bench.source_maps.flat_map(&:pins).flatten
|
|
88
|
+
@source_map_hash = bench.source_map_hash
|
|
89
|
+
iced_pins = bench.icebox.flat_map(&:pins)
|
|
90
|
+
live_pins = bench.live_map&.pins || []
|
|
82
91
|
implicit.clear
|
|
83
92
|
source_map_hash.each_value do |map|
|
|
84
93
|
implicit.merge map.environ
|
|
@@ -87,17 +96,17 @@ module Solargraph
|
|
|
87
96
|
if @unresolved_requires != unresolved_requires || @doc_map&.uncached_gemspecs&.any?
|
|
88
97
|
@doc_map = DocMap.new(unresolved_requires, [], bench.workspace.rbs_collection_path) # @todo Implement gem preferences
|
|
89
98
|
@unresolved_requires = unresolved_requires
|
|
90
|
-
need_to_uncache = true
|
|
91
99
|
end
|
|
92
|
-
@
|
|
93
|
-
@cache.clear if need_to_uncache
|
|
94
|
-
|
|
100
|
+
@cache.clear if store.update(@@core_map.pins, @doc_map.pins, implicit.pins, iced_pins, live_pins)
|
|
95
101
|
@missing_docs = [] # @todo Implement missing docs
|
|
96
102
|
self
|
|
97
103
|
end
|
|
98
104
|
|
|
105
|
+
# @todo need to model type def statement in chains as a symbol so
|
|
106
|
+
# that this overload of 'protected' will typecheck @sg-ignore
|
|
107
|
+
# @sg-ignore
|
|
99
108
|
protected def equality_fields
|
|
100
|
-
[self.class, @source_map_hash, implicit, @doc_map, @unresolved_requires
|
|
109
|
+
[self.class, @source_map_hash, implicit, @doc_map, @unresolved_requires]
|
|
101
110
|
end
|
|
102
111
|
|
|
103
112
|
# @return [::Array<Gem::Specification>]
|
|
@@ -183,7 +192,7 @@ module Solargraph
|
|
|
183
192
|
|
|
184
193
|
# @return [Array<Solargraph::Pin::Base>]
|
|
185
194
|
def pins
|
|
186
|
-
store.pins
|
|
195
|
+
store.pins.clone.freeze
|
|
187
196
|
end
|
|
188
197
|
|
|
189
198
|
# An array of pins based on Ruby keywords (`if`, `end`, etc.).
|
|
@@ -255,15 +264,19 @@ module Solargraph
|
|
|
255
264
|
# Should not be prefixed with '::'.
|
|
256
265
|
# @return [String, nil] fully qualified tag
|
|
257
266
|
def qualify tag, context_tag = ''
|
|
258
|
-
return tag if ['self', nil].include?(tag)
|
|
267
|
+
return tag if ['Boolean', 'self', nil].include?(tag)
|
|
259
268
|
|
|
260
|
-
context_type = ComplexType.
|
|
269
|
+
context_type = ComplexType.try_parse(context_tag).force_rooted
|
|
261
270
|
return unless context_type
|
|
262
271
|
|
|
263
272
|
type = ComplexType.try_parse(tag)
|
|
264
273
|
return unless type
|
|
274
|
+
return tag if type.literal?
|
|
275
|
+
|
|
276
|
+
context_type = ComplexType.try_parse(context_tag)
|
|
277
|
+
return unless context_type
|
|
265
278
|
|
|
266
|
-
fqns = qualify_namespace(type.rooted_namespace, context_type.
|
|
279
|
+
fqns = qualify_namespace(type.rooted_namespace, context_type.rooted_namespace)
|
|
267
280
|
return unless fqns
|
|
268
281
|
|
|
269
282
|
fqns + type.substring
|
|
@@ -310,6 +323,11 @@ module Solargraph
|
|
|
310
323
|
result
|
|
311
324
|
end
|
|
312
325
|
|
|
326
|
+
# @see Solargraph::Parser::FlowSensitiveTyping#visible_pins
|
|
327
|
+
def visible_pins(*args, **kwargs, &blk)
|
|
328
|
+
Solargraph::Parser::FlowSensitiveTyping.visible_pins(*args, **kwargs, &blk)
|
|
329
|
+
end
|
|
330
|
+
|
|
313
331
|
# Get an array of class variable pins for a namespace.
|
|
314
332
|
#
|
|
315
333
|
# @param namespace [String] A fully qualified namespace
|
|
@@ -341,8 +359,12 @@ module Solargraph
|
|
|
341
359
|
# @param deep [Boolean] True to include superclasses, mixins, etc.
|
|
342
360
|
# @return [Array<Solargraph::Pin::Method>]
|
|
343
361
|
def get_methods rooted_tag, scope: :instance, visibility: [:public], deep: true
|
|
362
|
+
rooted_type = ComplexType.try_parse(rooted_tag)
|
|
363
|
+
fqns = rooted_type.namespace
|
|
364
|
+
namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
|
|
344
365
|
cached = cache.get_methods(rooted_tag, scope, visibility, deep)
|
|
345
366
|
return cached.clone unless cached.nil?
|
|
367
|
+
# @type [Array<Solargraph::Pin::Method>]
|
|
346
368
|
result = []
|
|
347
369
|
skip = Set.new
|
|
348
370
|
if rooted_tag == ''
|
|
@@ -363,7 +385,7 @@ module Solargraph
|
|
|
363
385
|
init_pin = get_method_stack(rooted_tag, 'initialize').first
|
|
364
386
|
next pin unless init_pin
|
|
365
387
|
|
|
366
|
-
type = ComplexType
|
|
388
|
+
type = ComplexType::SELF
|
|
367
389
|
Pin::Method.new(
|
|
368
390
|
name: 'new',
|
|
369
391
|
scope: :class,
|
|
@@ -372,17 +394,21 @@ module Solargraph
|
|
|
372
394
|
signatures: init_pin.signatures.map { |sig| sig.proxy(type) },
|
|
373
395
|
return_type: type,
|
|
374
396
|
comments: init_pin.comments,
|
|
375
|
-
closure: init_pin.closure
|
|
376
|
-
|
|
377
|
-
|
|
397
|
+
closure: init_pin.closure,
|
|
398
|
+
source: init_pin.source,
|
|
399
|
+
type_location: init_pin.type_location,
|
|
400
|
+
)
|
|
378
401
|
end
|
|
379
402
|
end
|
|
380
403
|
result.concat inner_get_methods('Kernel', :instance, [:public], deep, skip) if visibility.include?(:private)
|
|
381
404
|
result.concat inner_get_methods('Module', scope, visibility, deep, skip) if scope == :module
|
|
382
405
|
end
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
406
|
+
result = resolve_method_aliases(result, visibility)
|
|
407
|
+
if namespace_pin && rooted_tag != rooted_type.name
|
|
408
|
+
result = result.map { |method_pin| method_pin.resolve_generics(namespace_pin, rooted_type) }
|
|
409
|
+
end
|
|
410
|
+
cache.set_methods(rooted_tag, scope, visibility, deep, result)
|
|
411
|
+
result
|
|
386
412
|
end
|
|
387
413
|
|
|
388
414
|
# Get an array of method pins for a complex type.
|
|
@@ -438,8 +464,13 @@ module Solargraph
|
|
|
438
464
|
# @param name [String] Method name to look up
|
|
439
465
|
# @param scope [Symbol] :instance or :class
|
|
440
466
|
# @return [Array<Solargraph::Pin::Method>]
|
|
441
|
-
def get_method_stack rooted_tag, name, scope: :instance
|
|
442
|
-
|
|
467
|
+
def get_method_stack rooted_tag, name, scope: :instance, visibility: [:private, :protected, :public], preserve_generics: false
|
|
468
|
+
rooted_type = ComplexType.parse(rooted_tag)
|
|
469
|
+
fqns = rooted_type.namespace
|
|
470
|
+
namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
|
|
471
|
+
methods = get_methods(rooted_tag, scope: scope, visibility: visibility).select { |p| p.name == name }
|
|
472
|
+
methods = erase_generics(namespace_pin, rooted_type, methods) unless preserve_generics
|
|
473
|
+
methods
|
|
443
474
|
end
|
|
444
475
|
|
|
445
476
|
# Get an array of all suggestions that match the specified path.
|
|
@@ -601,6 +632,7 @@ module Solargraph
|
|
|
601
632
|
rooted_type = ComplexType.parse(rooted_tag).force_rooted
|
|
602
633
|
fqns = rooted_type.namespace
|
|
603
634
|
fqns_generic_params = rooted_type.all_params
|
|
635
|
+
namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
|
|
604
636
|
return [] if no_core && fqns =~ /^(Object|BasicObject|Class|Module)$/
|
|
605
637
|
reqstr = "#{fqns}|#{scope}|#{visibility.sort}|#{deep}"
|
|
606
638
|
return [] if skip.include?(reqstr)
|
|
@@ -615,15 +647,7 @@ module Solargraph
|
|
|
615
647
|
# Store#get_methods doesn't know about full tags, just
|
|
616
648
|
# namespaces; resolving the generics in the method pins is this
|
|
617
649
|
# class' responsibility
|
|
618
|
-
|
|
619
|
-
namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
|
|
620
|
-
methods = if namespace_pin && rooted_tag != fqns
|
|
621
|
-
methods = raw_methods.map do |method_pin|
|
|
622
|
-
method_pin.resolve_generics(namespace_pin, rooted_type)
|
|
623
|
-
end
|
|
624
|
-
else
|
|
625
|
-
raw_methods
|
|
626
|
-
end
|
|
650
|
+
methods = store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name }
|
|
627
651
|
result.concat methods
|
|
628
652
|
if deep
|
|
629
653
|
if scope == :instance
|
|
@@ -768,8 +792,8 @@ module Solargraph
|
|
|
768
792
|
|
|
769
793
|
# Sort an array of pins to put nil or undefined variables last.
|
|
770
794
|
#
|
|
771
|
-
# @param pins [Enumerable<
|
|
772
|
-
# @return [Enumerable<
|
|
795
|
+
# @param pins [Enumerable<Pin::BaseVariable>]
|
|
796
|
+
# @return [Enumerable<Pin::BaseVariable>]
|
|
773
797
|
def prefer_non_nil_variables pins
|
|
774
798
|
result = []
|
|
775
799
|
nil_pins = []
|
|
@@ -800,7 +824,7 @@ module Solargraph
|
|
|
800
824
|
return pin unless pin.is_a?(Pin::MethodAlias)
|
|
801
825
|
return nil if @method_alias_stack.include?(pin.path)
|
|
802
826
|
@method_alias_stack.push pin.path
|
|
803
|
-
origin = get_method_stack(pin.full_context.tag, pin.original, scope: pin.scope).first
|
|
827
|
+
origin = get_method_stack(pin.full_context.tag, pin.original, scope: pin.scope, preserve_generics: true).first
|
|
804
828
|
@method_alias_stack.pop
|
|
805
829
|
return nil if origin.nil?
|
|
806
830
|
args = {
|
|
@@ -809,11 +833,48 @@ module Solargraph
|
|
|
809
833
|
name: pin.name,
|
|
810
834
|
comments: origin.comments,
|
|
811
835
|
scope: origin.scope,
|
|
836
|
+
# context: pin.context,
|
|
812
837
|
visibility: origin.visibility,
|
|
813
838
|
signatures: origin.signatures,
|
|
814
|
-
attribute: origin.attribute
|
|
839
|
+
attribute: origin.attribute?,
|
|
840
|
+
generics: origin.generics,
|
|
841
|
+
return_type: origin.return_type,
|
|
815
842
|
}
|
|
816
843
|
Pin::Method.new **args
|
|
817
844
|
end
|
|
845
|
+
|
|
846
|
+
include Logging
|
|
847
|
+
|
|
848
|
+
private
|
|
849
|
+
|
|
850
|
+
# @param namespace_pin [Pin::Namespace]
|
|
851
|
+
# @param rooted_type [ComplexType]
|
|
852
|
+
# @param pins [Enumerable<Pin::Base>]
|
|
853
|
+
# @return [Array<Pin::Base>]
|
|
854
|
+
def erase_generics(namespace_pin, rooted_type, pins)
|
|
855
|
+
return pins unless should_erase_generics_when_done?(namespace_pin, rooted_type)
|
|
856
|
+
|
|
857
|
+
logger.debug("Erasing generics on namespace_pin=#{namespace_pin} / rooted_type=#{rooted_type}")
|
|
858
|
+
pins.map do |method_pin|
|
|
859
|
+
method_pin.erase_generics(namespace_pin.generics)
|
|
860
|
+
end
|
|
861
|
+
end
|
|
862
|
+
|
|
863
|
+
# @param namespace_pin [Pin::Namespace]
|
|
864
|
+
# @param rooted_type [ComplexType]
|
|
865
|
+
def should_erase_generics_when_done?(namespace_pin, rooted_type)
|
|
866
|
+
has_generics?(namespace_pin) && !can_resolve_generics?(namespace_pin, rooted_type)
|
|
867
|
+
end
|
|
868
|
+
|
|
869
|
+
# @param namespace_pin [Pin::Namespace]
|
|
870
|
+
def has_generics?(namespace_pin)
|
|
871
|
+
namespace_pin && !namespace_pin.generics.empty?
|
|
872
|
+
end
|
|
873
|
+
|
|
874
|
+
# @param namespace_pin [Pin::Namespace]
|
|
875
|
+
# @param rooted_type [ComplexType]
|
|
876
|
+
def can_resolve_generics?(namespace_pin, rooted_type)
|
|
877
|
+
has_generics?(namespace_pin) && !rooted_type.all_params.empty?
|
|
878
|
+
end
|
|
818
879
|
end
|
|
819
880
|
end
|
data/lib/solargraph/bench.rb
CHANGED
|
@@ -11,18 +11,34 @@ module Solargraph
|
|
|
11
11
|
# @return [Workspace]
|
|
12
12
|
attr_reader :workspace
|
|
13
13
|
|
|
14
|
+
# @return [SourceMap]
|
|
15
|
+
attr_reader :live_map
|
|
16
|
+
|
|
14
17
|
# @return [Set<String>]
|
|
15
18
|
attr_reader :external_requires
|
|
16
19
|
|
|
17
20
|
# @param source_maps [Array<SourceMap>, Set<SourceMap>]
|
|
18
21
|
# @param workspace [Workspace]
|
|
22
|
+
# @param live_map [SourceMap, nil]
|
|
19
23
|
# @param external_requires [Array<String>, Set<String>]
|
|
20
|
-
def initialize source_maps: [], workspace: Workspace.new, external_requires: []
|
|
24
|
+
def initialize source_maps: [], workspace: Workspace.new, live_map: nil, external_requires: []
|
|
21
25
|
@source_maps = source_maps.to_set
|
|
22
26
|
@workspace = workspace
|
|
27
|
+
@live_map = live_map
|
|
23
28
|
@external_requires = external_requires.reject { |path| workspace.would_require?(path) }
|
|
24
29
|
.compact
|
|
25
30
|
.to_set
|
|
26
31
|
end
|
|
32
|
+
|
|
33
|
+
# @return [Hash{String => SourceMap}]
|
|
34
|
+
def source_map_hash
|
|
35
|
+
# @todo Work around #to_h bug in current Ruby head (3.5) with #map#to_h
|
|
36
|
+
@source_map_hash ||= source_maps.map { |s| [s.filename, s] }
|
|
37
|
+
.to_h
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def icebox
|
|
41
|
+
@icebox ||= (source_maps - [live_map])
|
|
42
|
+
end
|
|
27
43
|
end
|
|
28
44
|
end
|
|
@@ -129,6 +129,13 @@ module Solargraph
|
|
|
129
129
|
end.call
|
|
130
130
|
end
|
|
131
131
|
|
|
132
|
+
def namespace_type
|
|
133
|
+
return ComplexType.parse('::Object') if duck_type?
|
|
134
|
+
return ComplexType.parse('::NilClass') if nil_type?
|
|
135
|
+
return subtypes.first if (name == 'Class' || name == 'Module') && !subtypes.empty?
|
|
136
|
+
self
|
|
137
|
+
end
|
|
138
|
+
|
|
132
139
|
# @return [String]
|
|
133
140
|
def rooted_namespace
|
|
134
141
|
return namespace unless rooted? && can_root_name?(namespace)
|
|
@@ -7,9 +7,15 @@ module Solargraph
|
|
|
7
7
|
#
|
|
8
8
|
class UniqueType
|
|
9
9
|
include TypeMethods
|
|
10
|
+
include Equality
|
|
10
11
|
|
|
11
12
|
attr_reader :all_params, :subtypes, :key_types
|
|
12
13
|
|
|
14
|
+
# @sg-ignore Fix "Not enough arguments to Module#protected"
|
|
15
|
+
protected def equality_fields
|
|
16
|
+
[@name, @all_params, @subtypes, @key_types]
|
|
17
|
+
end
|
|
18
|
+
|
|
13
19
|
# Create a UniqueType with the specified name and an optional substring.
|
|
14
20
|
# The substring is the parameter section of a parametrized type, e.g.,
|
|
15
21
|
# for the type `Array<String>`, the name is `Array` and the substring is
|
|
@@ -21,7 +27,7 @@ module Solargraph
|
|
|
21
27
|
# @return [UniqueType]
|
|
22
28
|
def self.parse name, substring = '', make_rooted: nil
|
|
23
29
|
if name.start_with?(':::')
|
|
24
|
-
raise "Illegal prefix: #{name}"
|
|
30
|
+
raise ComplexTypeError, "Illegal prefix: #{name}"
|
|
25
31
|
end
|
|
26
32
|
if name.start_with?('::')
|
|
27
33
|
name = name[2..-1]
|
|
@@ -42,7 +48,7 @@ module Solargraph
|
|
|
42
48
|
subs = ComplexType.parse(substring[1..-2], partial: true)
|
|
43
49
|
parameters_type = PARAMETERS_TYPE_BY_STARTING_TAG.fetch(substring[0])
|
|
44
50
|
if parameters_type == :hash
|
|
45
|
-
raise ComplexTypeError, "Bad hash type" unless !subs.is_a?(ComplexType) and subs.length == 2 and !subs[0].is_a?(UniqueType) and !subs[1].is_a?(UniqueType)
|
|
51
|
+
raise ComplexTypeError, "Bad hash type: name=#{name}, substring=#{substring}" unless !subs.is_a?(ComplexType) and subs.length == 2 and !subs[0].is_a?(UniqueType) and !subs[1].is_a?(UniqueType)
|
|
46
52
|
# @todo should be able to resolve map; both types have it
|
|
47
53
|
# with same return type
|
|
48
54
|
# @sg-ignore
|
|
@@ -67,19 +73,63 @@ module Solargraph
|
|
|
67
73
|
end
|
|
68
74
|
raise "Please remove leading :: and set rooted instead - #{name.inspect}" if name.start_with?('::')
|
|
69
75
|
@name = name
|
|
70
|
-
@
|
|
71
|
-
|
|
76
|
+
@parameters_type = parameters_type
|
|
77
|
+
if implicit_union?
|
|
78
|
+
@key_types = key_types.uniq
|
|
79
|
+
@subtypes = subtypes.uniq
|
|
80
|
+
else
|
|
81
|
+
@key_types = key_types
|
|
82
|
+
@subtypes = subtypes
|
|
83
|
+
end
|
|
72
84
|
@rooted = rooted
|
|
73
85
|
@all_params = []
|
|
74
|
-
@all_params.concat key_types
|
|
75
|
-
@all_params.concat subtypes
|
|
76
|
-
|
|
86
|
+
@all_params.concat @key_types
|
|
87
|
+
@all_params.concat @subtypes
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def implicit_union?
|
|
91
|
+
# @todo use api_map to establish number of generics in type;
|
|
92
|
+
# if only one is allowed but multiple are passed in, treat
|
|
93
|
+
# those as implicit unions
|
|
94
|
+
['Hash', 'Array', 'Set', '_ToAry', 'Enumerable', '_Each'].include?(name) && parameters_type != :fixed
|
|
77
95
|
end
|
|
78
96
|
|
|
79
97
|
def to_s
|
|
80
98
|
tag
|
|
81
99
|
end
|
|
82
100
|
|
|
101
|
+
def simplify_literals
|
|
102
|
+
transform do |t|
|
|
103
|
+
next t unless t.literal?
|
|
104
|
+
t.recreate(new_name: t.non_literal_name)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def literal?
|
|
109
|
+
non_literal_name != name
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def non_literal_name
|
|
113
|
+
@non_literal_name ||= determine_non_literal_name
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def determine_non_literal_name
|
|
117
|
+
# https://github.com/ruby/rbs/blob/master/docs/syntax.md
|
|
118
|
+
#
|
|
119
|
+
# _literal_ ::= _string-literal_
|
|
120
|
+
# | _symbol-literal_
|
|
121
|
+
# | _integer-literal_
|
|
122
|
+
# | `true`
|
|
123
|
+
# | `false`
|
|
124
|
+
return name if name.empty?
|
|
125
|
+
return 'NilClass' if name == 'nil'
|
|
126
|
+
return 'Boolean' if ['true', 'false'].include?(name)
|
|
127
|
+
return 'Symbol' if name[0] == ':'
|
|
128
|
+
return 'String' if ['"', "'"].include?(name[0])
|
|
129
|
+
return 'Integer' if name.match?(/^-?\d+$/)
|
|
130
|
+
name
|
|
131
|
+
end
|
|
132
|
+
|
|
83
133
|
def eql?(other)
|
|
84
134
|
self.class == other.class &&
|
|
85
135
|
@name == other.name &&
|
|
@@ -107,11 +157,17 @@ module Solargraph
|
|
|
107
157
|
def rbs_name
|
|
108
158
|
if name == 'undefined'
|
|
109
159
|
'untyped'
|
|
160
|
+
elsif literal?
|
|
161
|
+
name
|
|
110
162
|
else
|
|
111
163
|
rooted_name
|
|
112
164
|
end
|
|
113
165
|
end
|
|
114
166
|
|
|
167
|
+
def desc
|
|
168
|
+
rooted_tags
|
|
169
|
+
end
|
|
170
|
+
|
|
115
171
|
# @return [String]
|
|
116
172
|
def to_rbs
|
|
117
173
|
if duck_type?
|
|
@@ -173,6 +229,23 @@ module Solargraph
|
|
|
173
229
|
name == GENERIC_TAG_NAME || all_params.any?(&:generic?)
|
|
174
230
|
end
|
|
175
231
|
|
|
232
|
+
# @param api_map [ApiMap] The ApiMap that performs qualification
|
|
233
|
+
# @param atype [ComplexType] type which may be assigned to this type
|
|
234
|
+
def can_assign?(api_map, atype)
|
|
235
|
+
logger.debug { "UniqueType#can_assign?(self=#{rooted_tags.inspect}, atype=#{atype.rooted_tags.inspect})" }
|
|
236
|
+
downcasted_atype = atype.downcast_to_literal_if_possible
|
|
237
|
+
out = downcasted_atype.all? do |autype|
|
|
238
|
+
autype.name == name || api_map.super_and_sub?(name, autype.name)
|
|
239
|
+
end
|
|
240
|
+
logger.debug { "UniqueType#can_assign?(self=#{rooted_tags.inspect}, atype=#{atype.rooted_tags.inspect}) => #{out}" }
|
|
241
|
+
out
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# @return [UniqueType]
|
|
245
|
+
def downcast_to_literal_if_possible
|
|
246
|
+
SINGLE_SUBTYPE.fetch(rooted_tag, self)
|
|
247
|
+
end
|
|
248
|
+
|
|
176
249
|
# @param generics_to_resolve [Enumerable<String>]
|
|
177
250
|
# @param context_type [UniqueType, nil]
|
|
178
251
|
# @param resolved_generic_values [Hash{String => ComplexType}] Added to as types are encountered or resolved
|
|
@@ -235,7 +308,23 @@ module Solargraph
|
|
|
235
308
|
if t.name == GENERIC_TAG_NAME
|
|
236
309
|
idx = definitions.generics.index(t.subtypes.first&.name)
|
|
237
310
|
next t if idx.nil?
|
|
238
|
-
context_type.
|
|
311
|
+
if context_type.parameters_type == :hash
|
|
312
|
+
if idx == 0
|
|
313
|
+
next ComplexType.new(context_type.key_types)
|
|
314
|
+
elsif idx == 1
|
|
315
|
+
next ComplexType.new(context_type.subtypes)
|
|
316
|
+
else
|
|
317
|
+
next ComplexType::UNDEFINED
|
|
318
|
+
end
|
|
319
|
+
elsif context_type.all?(&:implicit_union?)
|
|
320
|
+
if idx == 0 && !context_type.all_params.empty?
|
|
321
|
+
ComplexType.new(context_type.all_params)
|
|
322
|
+
else
|
|
323
|
+
ComplexType::UNDEFINED
|
|
324
|
+
end
|
|
325
|
+
else
|
|
326
|
+
context_type.all_params[idx] || ComplexType::UNDEFINED
|
|
327
|
+
end
|
|
239
328
|
else
|
|
240
329
|
t
|
|
241
330
|
end
|
|
@@ -361,6 +450,18 @@ module Solargraph
|
|
|
361
450
|
|
|
362
451
|
UNDEFINED = UniqueType.new('undefined', rooted: false)
|
|
363
452
|
BOOLEAN = UniqueType.new('Boolean', rooted: true)
|
|
453
|
+
TRUE = UniqueType.new('true', rooted: true)
|
|
454
|
+
FALSE = UniqueType.new('false', rooted: true)
|
|
455
|
+
NIL = UniqueType.new('nil', rooted: true)
|
|
456
|
+
# @type [Hash{String => UniqueType}]
|
|
457
|
+
SINGLE_SUBTYPE = {
|
|
458
|
+
'::TrueClass' => UniqueType::TRUE,
|
|
459
|
+
'::FalseClass' => UniqueType::FALSE,
|
|
460
|
+
'::NilClass' => UniqueType::NIL
|
|
461
|
+
}.freeze
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
include Logging
|
|
364
465
|
end
|
|
365
466
|
end
|
|
366
467
|
end
|
|
@@ -7,6 +7,7 @@ module Solargraph
|
|
|
7
7
|
GENERIC_TAG_NAME = 'generic'.freeze
|
|
8
8
|
# @!parse
|
|
9
9
|
# include TypeMethods
|
|
10
|
+
include Equality
|
|
10
11
|
|
|
11
12
|
autoload :TypeMethods, 'solargraph/complex_type/type_methods'
|
|
12
13
|
autoload :UniqueType, 'solargraph/complex_type/unique_type'
|
|
@@ -15,20 +16,18 @@ module Solargraph
|
|
|
15
16
|
def initialize types = [UniqueType::UNDEFINED]
|
|
16
17
|
# @todo @items here should not need an annotation
|
|
17
18
|
# @type [Array<UniqueType>]
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def ==(other)
|
|
27
|
-
self.eql?(other)
|
|
19
|
+
items = types.flat_map(&:items).uniq(&:to_s)
|
|
20
|
+
if items.any? { |i| i.name == 'false' } && items.any? { |i| i.name == 'true' }
|
|
21
|
+
items.delete_if { |i| i.name == 'false' || i.name == 'true' }
|
|
22
|
+
items.unshift(ComplexType::BOOLEAN)
|
|
23
|
+
end
|
|
24
|
+
items = [UniqueType::UNDEFINED] if items.any?(&:undefined?)
|
|
25
|
+
@items = items
|
|
28
26
|
end
|
|
29
27
|
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
# @sg-ignore Fix "Not enough arguments to Module#protected"
|
|
29
|
+
protected def equality_fields
|
|
30
|
+
[self.class, items]
|
|
32
31
|
end
|
|
33
32
|
|
|
34
33
|
# @param api_map [ApiMap]
|
|
@@ -77,7 +76,7 @@ module Solargraph
|
|
|
77
76
|
end
|
|
78
77
|
|
|
79
78
|
# @yieldparam [UniqueType]
|
|
80
|
-
# @return [Array]
|
|
79
|
+
# @return [Array<UniqueType>]
|
|
81
80
|
def map &block
|
|
82
81
|
@items.map &block
|
|
83
82
|
end
|
|
@@ -100,6 +99,12 @@ module Solargraph
|
|
|
100
99
|
end
|
|
101
100
|
end
|
|
102
101
|
|
|
102
|
+
# @param atype [ComplexType] type which may be assigned to this type
|
|
103
|
+
# @param api_map [ApiMap] The ApiMap that performs qualification
|
|
104
|
+
def can_assign?(api_map, atype)
|
|
105
|
+
any? { |ut| ut.can_assign?(api_map, atype) }
|
|
106
|
+
end
|
|
107
|
+
|
|
103
108
|
# @return [Integer]
|
|
104
109
|
def length
|
|
105
110
|
@items.length
|
|
@@ -110,10 +115,6 @@ module Solargraph
|
|
|
110
115
|
@items
|
|
111
116
|
end
|
|
112
117
|
|
|
113
|
-
def tags
|
|
114
|
-
@items.map(&:tag).join(', ')
|
|
115
|
-
end
|
|
116
|
-
|
|
117
118
|
# @param index [Integer]
|
|
118
119
|
# @return [UniqueType]
|
|
119
120
|
def [](index)
|
|
@@ -154,14 +155,39 @@ module Solargraph
|
|
|
154
155
|
map(&:tag).join(', ')
|
|
155
156
|
end
|
|
156
157
|
|
|
158
|
+
def tags
|
|
159
|
+
map(&:tag).join(', ')
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def simple_tags
|
|
163
|
+
simplify_literals.tags
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def literal?
|
|
167
|
+
@items.any?(&:literal?)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# @return [ComplexType]
|
|
171
|
+
def downcast_to_literal_if_possible
|
|
172
|
+
ComplexType.new(items.map(&:downcast_to_literal_if_possible))
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def desc
|
|
176
|
+
rooted_tags
|
|
177
|
+
end
|
|
178
|
+
|
|
157
179
|
def rooted_tags
|
|
158
180
|
map(&:rooted_tag).join(', ')
|
|
159
181
|
end
|
|
160
182
|
|
|
183
|
+
# @yieldparam [UniqueType]
|
|
161
184
|
def all? &block
|
|
162
185
|
@items.all? &block
|
|
163
186
|
end
|
|
164
187
|
|
|
188
|
+
# @yieldparam [UniqueType]
|
|
189
|
+
# @yieldreturn [Boolean]
|
|
190
|
+
# @return [Boolean]
|
|
165
191
|
def any? &block
|
|
166
192
|
@items.compact.any? &block
|
|
167
193
|
end
|
|
@@ -174,6 +200,10 @@ module Solargraph
|
|
|
174
200
|
any?(&:generic?)
|
|
175
201
|
end
|
|
176
202
|
|
|
203
|
+
def simplify_literals
|
|
204
|
+
ComplexType.new(map(&:simplify_literals))
|
|
205
|
+
end
|
|
206
|
+
|
|
177
207
|
# @param new_name [String, nil]
|
|
178
208
|
# @yieldparam t [UniqueType]
|
|
179
209
|
# @yieldreturn [UniqueType]
|
|
@@ -263,7 +293,7 @@ module Solargraph
|
|
|
263
293
|
# Consumers should not need to use this parameter; it should only be
|
|
264
294
|
# used internally.
|
|
265
295
|
#
|
|
266
|
-
# @param
|
|
296
|
+
# @param strings [Array<String>] The type definitions to parse
|
|
267
297
|
# @return [ComplexType]
|
|
268
298
|
# # @overload parse(*strings, partial: false)
|
|
269
299
|
# # @todo Need ability to use a literal true as a type below
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
module Convention
|
|
5
|
+
module StructDefinition
|
|
6
|
+
# A node wrapper for a Struct definition via const assignment.
|
|
7
|
+
# @example
|
|
8
|
+
# MyStruct = Struct.new(:bar, :baz) do
|
|
9
|
+
# def foo
|
|
10
|
+
# end
|
|
11
|
+
# end
|
|
12
|
+
class StructAssignmentNode < StructDefintionNode
|
|
13
|
+
class << self
|
|
14
|
+
# @example
|
|
15
|
+
# s(:casgn, nil, :Foo,
|
|
16
|
+
# s(:block,
|
|
17
|
+
# s(:send,
|
|
18
|
+
# s(:const, nil, :Struct), :new,
|
|
19
|
+
# s(:sym, :bar),
|
|
20
|
+
# s(:sym, :baz)),
|
|
21
|
+
# s(:args),
|
|
22
|
+
# s(:def, :foo,
|
|
23
|
+
# s(:args),
|
|
24
|
+
# s(:send, nil, :bar))))
|
|
25
|
+
def valid?(node)
|
|
26
|
+
return false unless node&.type == :casgn
|
|
27
|
+
return false if node.children[2].nil?
|
|
28
|
+
struct_node = node.children[2].children[0]
|
|
29
|
+
|
|
30
|
+
struct_definition_node?(struct_node)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def class_name
|
|
35
|
+
if node.children[0]
|
|
36
|
+
Parser::NodeMethods.unpack_name(node.children[0]) + "::#{node.children[1]}"
|
|
37
|
+
else
|
|
38
|
+
node.children[1].to_s
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
# @return [Parser::AST::Node]
|
|
45
|
+
def struct_node
|
|
46
|
+
node.children[2].children[0]
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|