solargraph 0.54.4 → 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 +18 -0
- data/lib/solargraph/api_map/index.rb +1 -1
- data/lib/solargraph/api_map/store.rb +40 -19
- data/lib/solargraph/api_map.rb +24 -19
- data/lib/solargraph/bench.rb +17 -1
- data/lib/solargraph/complex_type/unique_type.rb +88 -7
- data/lib/solargraph/complex_type.rb +35 -6
- 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 +1 -0
- data/lib/solargraph/doc_map.rb +42 -18
- data/lib/solargraph/language_server/host/message_worker.rb +10 -7
- data/lib/solargraph/language_server/host.rb +1 -0
- data/lib/solargraph/library.rb +2 -1
- data/lib/solargraph/location.rb +8 -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 +14 -0
- data/lib/solargraph/parser/node_processor.rb +0 -1
- data/lib/solargraph/parser/parser_gem/class_methods.rb +11 -6
- data/lib/solargraph/parser/parser_gem/node_chainer.rb +10 -10
- data/lib/solargraph/parser/parser_gem/node_methods.rb +3 -1
- 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/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 +9 -3
- data/lib/solargraph/pin/base_variable.rb +7 -1
- data/lib/solargraph/pin/block.rb +2 -0
- data/lib/solargraph/pin/breakable.rb +9 -0
- data/lib/solargraph/pin/local_variable.rb +7 -1
- data/lib/solargraph/pin/method.rb +20 -18
- data/lib/solargraph/pin/namespace.rb +10 -7
- data/lib/solargraph/pin/parameter.rb +13 -5
- 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/rbs_map/conversions.rb +8 -8
- data/lib/solargraph/rbs_map/core_fills.rb +10 -3
- data/lib/solargraph/source/chain/array.rb +4 -3
- data/lib/solargraph/source/chain/call.rb +46 -17
- data/lib/solargraph/source/chain/constant.rb +1 -1
- data/lib/solargraph/source/chain/hash.rb +3 -2
- data/lib/solargraph/source/chain/link.rb +2 -0
- data/lib/solargraph/source/chain/literal.rb +22 -2
- data/lib/solargraph/source/chain/z_super.rb +1 -1
- data/lib/solargraph/source/chain.rb +77 -47
- data/lib/solargraph/source/source_chainer.rb +2 -2
- data/lib/solargraph/source_map/clip.rb +3 -1
- data/lib/solargraph/type_checker/checks.rb +4 -0
- data/lib/solargraph/type_checker.rb +35 -8
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/yard_map/mapper/to_method.rb +42 -15
- data/lib/solargraph/yardoc.rb +1 -1
- metadata +15 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c8e98755e559a9524b7d057c860e1ebb6d8a46eeb73dd567744f7562a4d49a64
|
4
|
+
data.tar.gz: 33943305fcf0c4c534fd4abf5eb24a937f6390021e681017296aac7bb9546592
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 37c0b331d67d299a865e4201c030dec6c1fd4ca14955012d702a44849bece0497c70449a3594ce8cdfe56669b93fbc80d1b291331a540453409a990344464ac1
|
7
|
+
data.tar.gz: c91397eeb5d2f10c551e97a741acb9a1e8fbae371b810bf375cc6024fc5985d6e2e581fb1d39c62ba9dd7f71b2cc6fa0ca801fcb74c89b5b8717beeaf8af10f0
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
## 0.55.0 - June 3, 2025
|
2
|
+
- Flow-sensitive typing - automatically downcast from is_a? calls (#856)
|
3
|
+
- Tuple enabler: infer literal types and use them for signature selection (#836)
|
4
|
+
- Signature selection improvements (#907)
|
5
|
+
- Add support for Ruby Structs (#939)
|
6
|
+
- [regression] Fix interface change breaking solargraph-rails (#940)
|
7
|
+
- [regression] Add back bundler/require support for solargraph-rails (#941)
|
8
|
+
- Add specs for initialize capabilities (#955)
|
9
|
+
- Create MethodAlias pins from YARD (#945)
|
10
|
+
- MessageWorker prioritizes synchronization (#956)
|
11
|
+
- initialize/new method pin cleanups (#949)
|
12
|
+
- Clip rebinds blocks when cursor is not part of receiver (#958)
|
13
|
+
|
14
|
+
## 0.54.5 - May 17, 2025
|
15
|
+
- Repair unknown encoding errors (#936, #935)
|
16
|
+
- Index arbitrary pinsets (#937)
|
17
|
+
- Separate YARD cache from doc map cache (#938)
|
18
|
+
|
1
19
|
## 0.54.4 - May 14, 2025
|
2
20
|
- Delete files from Library hash (#932)
|
3
21
|
- Clip#define returns variable pins (#934, #933)
|
@@ -1,18 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
3
|
module Solargraph
|
5
4
|
class ApiMap
|
6
5
|
# Queryable collection of Pins representing a Workspace, gems and the Ruby
|
7
6
|
# core.
|
8
7
|
#
|
9
8
|
class Store
|
10
|
-
# @param
|
11
|
-
|
12
|
-
|
13
|
-
@static_index = Index.new(static)
|
14
|
-
@dynamic = dynamic
|
15
|
-
@index = @static_index.merge(dynamic)
|
9
|
+
# @param pinsets [Array<Enumerable<Pin::Base>>]
|
10
|
+
def initialize *pinsets
|
11
|
+
catalog pinsets
|
16
12
|
end
|
17
13
|
|
18
14
|
# @return [Array<Solargraph::Pin::Base>]
|
@@ -20,19 +16,27 @@ module Solargraph
|
|
20
16
|
index.pins
|
21
17
|
end
|
22
18
|
|
23
|
-
# @param
|
24
|
-
# @
|
25
|
-
def update
|
19
|
+
# @param pinsets [Array<Enumerable<Pin::Base>>]
|
20
|
+
# @return [Boolean] True if the index was updated
|
21
|
+
def update *pinsets
|
22
|
+
return catalog(pinsets) if pinsets.length != @pinsets.length
|
23
|
+
|
24
|
+
changed = pinsets.find_index.with_index { |pinset, idx| @pinsets[idx] != pinset }
|
25
|
+
return false unless changed
|
26
|
+
|
26
27
|
# @todo Fix this map
|
27
28
|
@fqns_pins_map = nil
|
28
|
-
if
|
29
|
-
|
30
|
-
|
31
|
-
@
|
29
|
+
return catalog(pinsets) if changed == 0
|
30
|
+
|
31
|
+
pinsets[changed..].each_with_index do |pins, idx|
|
32
|
+
@pinsets[changed + idx] = pins
|
33
|
+
@indexes[changed + idx] = if pins.empty?
|
34
|
+
@indexes[changed + idx - 1]
|
35
|
+
else
|
36
|
+
@indexes[changed + idx - 1].merge(pins)
|
37
|
+
end
|
32
38
|
end
|
33
|
-
|
34
|
-
@index = @static_index.merge(dynamic)
|
35
|
-
self
|
39
|
+
true
|
36
40
|
end
|
37
41
|
|
38
42
|
def to_s
|
@@ -69,6 +73,8 @@ module Solargraph
|
|
69
73
|
return superclass_references[fqns].first if superclass_references.key?(fqns)
|
70
74
|
return 'Object' if fqns != 'BasicObject' && namespace_exists?(fqns)
|
71
75
|
return 'Object' if fqns == 'Boolean'
|
76
|
+
simplified_literal_name = ComplexType.parse("#{fqns}").simplify_literals.name
|
77
|
+
return simplified_literal_name if simplified_literal_name != fqns
|
72
78
|
nil
|
73
79
|
end
|
74
80
|
|
@@ -108,7 +114,7 @@ module Solargraph
|
|
108
114
|
# @param fqns [String]
|
109
115
|
# @return [Enumerable<Solargraph::Pin::Base>]
|
110
116
|
def get_class_variables(fqns)
|
111
|
-
namespace_children(fqns).select{|pin| pin.is_a?(Pin::ClassVariable)}
|
117
|
+
namespace_children(fqns).select { |pin| pin.is_a?(Pin::ClassVariable)}
|
112
118
|
end
|
113
119
|
|
114
120
|
# @return [Enumerable<Solargraph::Pin::Base>]
|
@@ -190,7 +196,22 @@ module Solargraph
|
|
190
196
|
|
191
197
|
private
|
192
198
|
|
193
|
-
|
199
|
+
def index
|
200
|
+
@indexes.last
|
201
|
+
end
|
202
|
+
|
203
|
+
def catalog pinsets
|
204
|
+
@pinsets = pinsets
|
205
|
+
@indexes = []
|
206
|
+
pinsets.each do |pins|
|
207
|
+
if @indexes.last && pins.empty?
|
208
|
+
@indexes.push @indexes.last
|
209
|
+
else
|
210
|
+
@indexes.push(@indexes.last&.merge(pins) || Solargraph::ApiMap::Index.new(pins))
|
211
|
+
end
|
212
|
+
end
|
213
|
+
true
|
214
|
+
end
|
194
215
|
|
195
216
|
# @return [Hash{::Array(String, String) => ::Array<Pin::Namespace>}]
|
196
217
|
def fqns_pins_map
|
data/lib/solargraph/api_map.rb
CHANGED
@@ -66,7 +66,7 @@ module Solargraph
|
|
66
66
|
@source_map_hash = {}
|
67
67
|
implicit.clear
|
68
68
|
cache.clear
|
69
|
-
store.update
|
69
|
+
store.update @@core_map.pins, pins
|
70
70
|
self
|
71
71
|
end
|
72
72
|
|
@@ -85,11 +85,9 @@ module Solargraph
|
|
85
85
|
# @param bench [Bench]
|
86
86
|
# @return [self]
|
87
87
|
def catalog bench
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
@source_map_hash = bench.source_maps.map { |s| [s.filename, s] }.to_h
|
92
|
-
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 || []
|
93
91
|
implicit.clear
|
94
92
|
source_map_hash.each_value do |map|
|
95
93
|
implicit.merge map.environ
|
@@ -98,11 +96,8 @@ module Solargraph
|
|
98
96
|
if @unresolved_requires != unresolved_requires || @doc_map&.uncached_gemspecs&.any?
|
99
97
|
@doc_map = DocMap.new(unresolved_requires, [], bench.workspace.rbs_collection_path) # @todo Implement gem preferences
|
100
98
|
@unresolved_requires = unresolved_requires
|
101
|
-
need_to_uncache = true
|
102
99
|
end
|
103
|
-
store.update
|
104
|
-
@cache.clear if need_to_uncache
|
105
|
-
|
100
|
+
@cache.clear if store.update(@@core_map.pins, @doc_map.pins, implicit.pins, iced_pins, live_pins)
|
106
101
|
@missing_docs = [] # @todo Implement missing docs
|
107
102
|
self
|
108
103
|
end
|
@@ -111,7 +106,7 @@ module Solargraph
|
|
111
106
|
# that this overload of 'protected' will typecheck @sg-ignore
|
112
107
|
# @sg-ignore
|
113
108
|
protected def equality_fields
|
114
|
-
[self.class, @source_map_hash, implicit, @doc_map, @unresolved_requires
|
109
|
+
[self.class, @source_map_hash, implicit, @doc_map, @unresolved_requires]
|
115
110
|
end
|
116
111
|
|
117
112
|
# @return [::Array<Gem::Specification>]
|
@@ -269,15 +264,19 @@ module Solargraph
|
|
269
264
|
# Should not be prefixed with '::'.
|
270
265
|
# @return [String, nil] fully qualified tag
|
271
266
|
def qualify tag, context_tag = ''
|
272
|
-
return tag if ['self', nil].include?(tag)
|
267
|
+
return tag if ['Boolean', 'self', nil].include?(tag)
|
273
268
|
|
274
|
-
context_type = ComplexType.
|
269
|
+
context_type = ComplexType.try_parse(context_tag).force_rooted
|
275
270
|
return unless context_type
|
276
271
|
|
277
272
|
type = ComplexType.try_parse(tag)
|
278
273
|
return unless type
|
274
|
+
return tag if type.literal?
|
275
|
+
|
276
|
+
context_type = ComplexType.try_parse(context_tag)
|
277
|
+
return unless context_type
|
279
278
|
|
280
|
-
fqns = qualify_namespace(type.rooted_namespace, context_type.
|
279
|
+
fqns = qualify_namespace(type.rooted_namespace, context_type.rooted_namespace)
|
281
280
|
return unless fqns
|
282
281
|
|
283
282
|
fqns + type.substring
|
@@ -324,6 +323,11 @@ module Solargraph
|
|
324
323
|
result
|
325
324
|
end
|
326
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
|
+
|
327
331
|
# Get an array of class variable pins for a namespace.
|
328
332
|
#
|
329
333
|
# @param namespace [String] A fully qualified namespace
|
@@ -381,7 +385,7 @@ module Solargraph
|
|
381
385
|
init_pin = get_method_stack(rooted_tag, 'initialize').first
|
382
386
|
next pin unless init_pin
|
383
387
|
|
384
|
-
type = ComplexType
|
388
|
+
type = ComplexType::SELF
|
385
389
|
Pin::Method.new(
|
386
390
|
name: 'new',
|
387
391
|
scope: :class,
|
@@ -390,9 +394,10 @@ module Solargraph
|
|
390
394
|
signatures: init_pin.signatures.map { |sig| sig.proxy(type) },
|
391
395
|
return_type: type,
|
392
396
|
comments: init_pin.comments,
|
393
|
-
closure: init_pin.closure
|
394
|
-
|
395
|
-
|
397
|
+
closure: init_pin.closure,
|
398
|
+
source: init_pin.source,
|
399
|
+
type_location: init_pin.type_location,
|
400
|
+
)
|
396
401
|
end
|
397
402
|
end
|
398
403
|
result.concat inner_get_methods('Kernel', :instance, [:public], deep, skip) if visibility.include?(:private)
|
@@ -819,7 +824,7 @@ module Solargraph
|
|
819
824
|
return pin unless pin.is_a?(Pin::MethodAlias)
|
820
825
|
return nil if @method_alias_stack.include?(pin.path)
|
821
826
|
@method_alias_stack.push pin.path
|
822
|
-
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
|
823
828
|
@method_alias_stack.pop
|
824
829
|
return nil if origin.nil?
|
825
830
|
args = {
|
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
|
@@ -27,7 +27,7 @@ module Solargraph
|
|
27
27
|
# @return [UniqueType]
|
28
28
|
def self.parse name, substring = '', make_rooted: nil
|
29
29
|
if name.start_with?(':::')
|
30
|
-
raise "Illegal prefix: #{name}"
|
30
|
+
raise ComplexTypeError, "Illegal prefix: #{name}"
|
31
31
|
end
|
32
32
|
if name.start_with?('::')
|
33
33
|
name = name[2..-1]
|
@@ -48,7 +48,7 @@ module Solargraph
|
|
48
48
|
subs = ComplexType.parse(substring[1..-2], partial: true)
|
49
49
|
parameters_type = PARAMETERS_TYPE_BY_STARTING_TAG.fetch(substring[0])
|
50
50
|
if parameters_type == :hash
|
51
|
-
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)
|
52
52
|
# @todo should be able to resolve map; both types have it
|
53
53
|
# with same return type
|
54
54
|
# @sg-ignore
|
@@ -73,19 +73,63 @@ module Solargraph
|
|
73
73
|
end
|
74
74
|
raise "Please remove leading :: and set rooted instead - #{name.inspect}" if name.start_with?('::')
|
75
75
|
@name = name
|
76
|
-
@
|
77
|
-
|
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
|
78
84
|
@rooted = rooted
|
79
85
|
@all_params = []
|
80
|
-
@all_params.concat key_types
|
81
|
-
@all_params.concat subtypes
|
82
|
-
|
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
|
83
95
|
end
|
84
96
|
|
85
97
|
def to_s
|
86
98
|
tag
|
87
99
|
end
|
88
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
|
+
|
89
133
|
def eql?(other)
|
90
134
|
self.class == other.class &&
|
91
135
|
@name == other.name &&
|
@@ -113,6 +157,8 @@ module Solargraph
|
|
113
157
|
def rbs_name
|
114
158
|
if name == 'undefined'
|
115
159
|
'untyped'
|
160
|
+
elsif literal?
|
161
|
+
name
|
116
162
|
else
|
117
163
|
rooted_name
|
118
164
|
end
|
@@ -183,6 +229,23 @@ module Solargraph
|
|
183
229
|
name == GENERIC_TAG_NAME || all_params.any?(&:generic?)
|
184
230
|
end
|
185
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
|
+
|
186
249
|
# @param generics_to_resolve [Enumerable<String>]
|
187
250
|
# @param context_type [UniqueType, nil]
|
188
251
|
# @param resolved_generic_values [Hash{String => ComplexType}] Added to as types are encountered or resolved
|
@@ -253,6 +316,12 @@ module Solargraph
|
|
253
316
|
else
|
254
317
|
next ComplexType::UNDEFINED
|
255
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
|
256
325
|
else
|
257
326
|
context_type.all_params[idx] || ComplexType::UNDEFINED
|
258
327
|
end
|
@@ -381,6 +450,18 @@ module Solargraph
|
|
381
450
|
|
382
451
|
UNDEFINED = UniqueType.new('undefined', rooted: false)
|
383
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
|
384
465
|
end
|
385
466
|
end
|
386
467
|
end
|
@@ -16,7 +16,13 @@ module Solargraph
|
|
16
16
|
def initialize types = [UniqueType::UNDEFINED]
|
17
17
|
# @todo @items here should not need an annotation
|
18
18
|
# @type [Array<UniqueType>]
|
19
|
-
|
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
|
20
26
|
end
|
21
27
|
|
22
28
|
# @sg-ignore Fix "Not enough arguments to Module#protected"
|
@@ -70,7 +76,7 @@ module Solargraph
|
|
70
76
|
end
|
71
77
|
|
72
78
|
# @yieldparam [UniqueType]
|
73
|
-
# @return [Array]
|
79
|
+
# @return [Array<UniqueType>]
|
74
80
|
def map &block
|
75
81
|
@items.map &block
|
76
82
|
end
|
@@ -93,6 +99,12 @@ module Solargraph
|
|
93
99
|
end
|
94
100
|
end
|
95
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
|
+
|
96
108
|
# @return [Integer]
|
97
109
|
def length
|
98
110
|
@items.length
|
@@ -103,10 +115,6 @@ module Solargraph
|
|
103
115
|
@items
|
104
116
|
end
|
105
117
|
|
106
|
-
def tags
|
107
|
-
@items.map(&:tag).join(', ')
|
108
|
-
end
|
109
|
-
|
110
118
|
# @param index [Integer]
|
111
119
|
# @return [UniqueType]
|
112
120
|
def [](index)
|
@@ -147,6 +155,23 @@ module Solargraph
|
|
147
155
|
map(&:tag).join(', ')
|
148
156
|
end
|
149
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
|
+
|
150
175
|
def desc
|
151
176
|
rooted_tags
|
152
177
|
end
|
@@ -175,6 +200,10 @@ module Solargraph
|
|
175
200
|
any?(&:generic?)
|
176
201
|
end
|
177
202
|
|
203
|
+
def simplify_literals
|
204
|
+
ComplexType.new(map(&:simplify_literals))
|
205
|
+
end
|
206
|
+
|
178
207
|
# @param new_name [String, nil]
|
179
208
|
# @yieldparam t [UniqueType]
|
180
209
|
# @yieldreturn [UniqueType]
|
@@ -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
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Solargraph
|
4
|
+
module Convention
|
5
|
+
module StructDefinition
|
6
|
+
# A node wrapper for a Struct definition via inheritance.
|
7
|
+
# @example
|
8
|
+
# class MyStruct < Struct.new(:bar, :baz)
|
9
|
+
# def foo
|
10
|
+
# end
|
11
|
+
# end
|
12
|
+
class StructDefintionNode
|
13
|
+
class << self
|
14
|
+
# @example
|
15
|
+
# s(:class,
|
16
|
+
# s(:const, nil, :Foo),
|
17
|
+
# s(:send,
|
18
|
+
# s(:const, nil, :Struct), :new,
|
19
|
+
# s(:sym, :bar),
|
20
|
+
# s(:sym, :baz)),
|
21
|
+
# s(:hash,
|
22
|
+
# s(:pair,
|
23
|
+
# s(:sym, :keyword_init),
|
24
|
+
# s(:true)))),
|
25
|
+
# s(:def, :foo,
|
26
|
+
# s(:args),
|
27
|
+
# s(:send, nil, :bar)))
|
28
|
+
def valid?(node)
|
29
|
+
return false unless node&.type == :class
|
30
|
+
|
31
|
+
struct_definition_node?(node.children[1])
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# @param struct_node [Parser::AST::Node]
|
37
|
+
# @return [Boolean]
|
38
|
+
def struct_definition_node?(struct_node)
|
39
|
+
return false unless struct_node.is_a?(::Parser::AST::Node)
|
40
|
+
return false unless struct_node&.type == :send
|
41
|
+
return false unless struct_node.children[0]&.type == :const
|
42
|
+
return false unless struct_node.children[0].children[1] == :Struct
|
43
|
+
return false unless struct_node.children[1] == :new
|
44
|
+
|
45
|
+
true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [Parser::AST::Node]
|
50
|
+
def initialize(node)
|
51
|
+
@node = node
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [String]
|
55
|
+
def class_name
|
56
|
+
Parser::NodeMethods.unpack_name(node)
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Array<Array(Parser::AST::Node, String)>]
|
60
|
+
def attributes
|
61
|
+
struct_attribute_nodes.map do |struct_def_param|
|
62
|
+
next unless struct_def_param.type == :sym
|
63
|
+
[struct_def_param, struct_def_param.children[0].to_s]
|
64
|
+
end.compact
|
65
|
+
end
|
66
|
+
|
67
|
+
def keyword_init?
|
68
|
+
keyword_init_param = struct_attribute_nodes.find do |struct_def_param|
|
69
|
+
struct_def_param.type == :hash && struct_def_param.children[0].type == :pair &&
|
70
|
+
struct_def_param.children[0].children[0].children[0] == :keyword_init
|
71
|
+
end
|
72
|
+
|
73
|
+
return false if keyword_init_param.nil?
|
74
|
+
|
75
|
+
keyword_init_param.children[0].children[1].type == :true
|
76
|
+
end
|
77
|
+
|
78
|
+
# @return [Parser::AST::Node]
|
79
|
+
def body_node
|
80
|
+
node.children[2]
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
# @return [Parser::AST::Node]
|
86
|
+
attr_reader :node
|
87
|
+
|
88
|
+
# @return [Parser::AST::Node]
|
89
|
+
def struct_node
|
90
|
+
node.children[1]
|
91
|
+
end
|
92
|
+
|
93
|
+
# @return [Array<Parser::AST::Node>]
|
94
|
+
def struct_attribute_nodes
|
95
|
+
struct_node.children[2..-1]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|