solargraph 0.54.1 → 0.56.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/plugins.yml +2 -0
- data/.github/workflows/typecheck.yml +3 -1
- data/.gitignore +2 -0
- data/CHANGELOG.md +70 -0
- data/README.md +13 -3
- data/lib/solargraph/api_map/cache.rb +10 -1
- data/lib/solargraph/api_map/index.rb +175 -0
- data/lib/solargraph/api_map/store.rb +79 -126
- data/lib/solargraph/api_map.rb +247 -93
- data/lib/solargraph/bench.rb +17 -1
- data/lib/solargraph/complex_type/type_methods.rb +13 -1
- data/lib/solargraph/complex_type/unique_type.rb +118 -9
- data/lib/solargraph/complex_type.rb +48 -18
- data/lib/solargraph/convention/base.rb +3 -3
- data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +60 -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 +5 -3
- data/lib/solargraph/doc_map.rb +274 -56
- data/lib/solargraph/equality.rb +33 -0
- data/lib/solargraph/gem_pins.rb +53 -37
- data/lib/solargraph/language_server/host/message_worker.rb +31 -11
- data/lib/solargraph/language_server/host.rb +24 -13
- data/lib/solargraph/language_server/message/base.rb +19 -12
- data/lib/solargraph/language_server/message/extended/check_gem_version.rb +2 -0
- data/lib/solargraph/language_server/message/extended/document.rb +5 -2
- data/lib/solargraph/language_server/message/extended/document_gems.rb +3 -3
- 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 +13 -11
- data/lib/solargraph/location.rb +28 -0
- data/lib/solargraph/logging.rb +1 -0
- data/lib/solargraph/parser/comment_ripper.rb +12 -6
- data/lib/solargraph/parser/flow_sensitive_typing.rb +227 -0
- data/lib/solargraph/parser/node_methods.rb +15 -1
- data/lib/solargraph/parser/node_processor.rb +3 -1
- data/lib/solargraph/parser/parser_gem/class_methods.rb +16 -14
- 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/alias_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
- data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +4 -2
- data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +4 -2
- data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +23 -2
- data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -3
- data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +21 -0
- data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +4 -2
- data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +4 -3
- data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +29 -6
- data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +41 -0
- data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -3
- data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +28 -16
- data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -0
- data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -0
- data/lib/solargraph/parser/parser_gem/node_processors.rb +10 -0
- data/lib/solargraph/parser/region.rb +1 -1
- data/lib/solargraph/parser.rb +1 -0
- data/lib/solargraph/pin/base.rb +326 -43
- data/lib/solargraph/pin/base_variable.rb +18 -10
- data/lib/solargraph/pin/block.rb +3 -3
- data/lib/solargraph/pin/breakable.rb +9 -0
- data/lib/solargraph/pin/callable.rb +77 -6
- data/lib/solargraph/pin/closure.rb +18 -1
- data/lib/solargraph/pin/common.rb +5 -0
- data/lib/solargraph/pin/delegated_method.rb +20 -1
- data/lib/solargraph/pin/documenting.rb +16 -0
- data/lib/solargraph/pin/keyword.rb +7 -2
- data/lib/solargraph/pin/local_variable.rb +15 -6
- data/lib/solargraph/pin/method.rb +172 -42
- data/lib/solargraph/pin/namespace.rb +17 -9
- data/lib/solargraph/pin/parameter.rb +61 -11
- data/lib/solargraph/pin/proxy_type.rb +12 -6
- data/lib/solargraph/pin/reference/override.rb +10 -6
- data/lib/solargraph/pin/reference/require.rb +2 -2
- data/lib/solargraph/pin/signature.rb +42 -0
- data/lib/solargraph/pin/singleton.rb +1 -1
- data/lib/solargraph/pin/symbol.rb +3 -2
- data/lib/solargraph/pin/until.rb +18 -0
- data/lib/solargraph/pin/while.rb +18 -0
- data/lib/solargraph/pin.rb +4 -1
- data/lib/solargraph/pin_cache.rb +185 -0
- data/lib/solargraph/position.rb +16 -0
- data/lib/solargraph/range.rb +16 -0
- data/lib/solargraph/rbs_map/conversions.rb +199 -66
- data/lib/solargraph/rbs_map/core_fills.rb +32 -16
- data/lib/solargraph/rbs_map/core_map.rb +34 -11
- data/lib/solargraph/rbs_map/stdlib_map.rb +15 -5
- data/lib/solargraph/rbs_map.rb +74 -16
- data/lib/solargraph/shell.rb +19 -18
- data/lib/solargraph/source/chain/array.rb +12 -8
- data/lib/solargraph/source/chain/block_symbol.rb +1 -1
- data/lib/solargraph/source/chain/block_variable.rb +1 -1
- data/lib/solargraph/source/chain/call.rb +62 -25
- data/lib/solargraph/source/chain/constant.rb +1 -1
- data/lib/solargraph/source/chain/hash.rb +9 -3
- data/lib/solargraph/source/chain/head.rb +1 -1
- data/lib/solargraph/source/chain/if.rb +6 -1
- data/lib/solargraph/source/chain/link.rb +19 -5
- data/lib/solargraph/source/chain/literal.rb +27 -2
- data/lib/solargraph/source/chain/or.rb +1 -1
- data/lib/solargraph/source/chain/z_super.rb +1 -1
- data/lib/solargraph/source/chain.rb +106 -62
- 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/source_map/mapper.rb +9 -5
- data/lib/solargraph/source_map.rb +0 -17
- 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/views/_method.erb +10 -10
- data/lib/solargraph/views/_namespace.erb +3 -3
- data/lib/solargraph/views/document.erb +10 -10
- data/lib/solargraph/workspace/config.rb +7 -3
- data/lib/solargraph/workspace.rb +16 -6
- data/lib/solargraph/yard_map/mapper/to_constant.rb +5 -2
- data/lib/solargraph/yard_map/mapper/to_method.rb +55 -15
- data/lib/solargraph/yard_map/mapper/to_namespace.rb +5 -2
- data/lib/solargraph/yard_map/mapper.rb +5 -3
- data/lib/solargraph/yard_map/to_method.rb +4 -2
- data/lib/solargraph/yardoc.rb +6 -9
- data/lib/solargraph.rb +20 -1
- data/rbs/fills/tuple.rbs +150 -0
- data/rbs_collection.yaml +19 -0
- data/solargraph.gemspec +6 -5
- metadata +56 -29
- data/lib/solargraph/cache.rb +0 -77
|
@@ -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,13 +48,20 @@ 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
|
|
49
55
|
key_types.concat(subs[0].map { |u| ComplexType.new([u]) })
|
|
50
56
|
# @sg-ignore
|
|
51
57
|
subtypes.concat(subs[1].map { |u| ComplexType.new([u]) })
|
|
58
|
+
elsif parameters_type == :list && name == 'Hash'
|
|
59
|
+
# Treat Hash<A, B> as Hash{A => B}
|
|
60
|
+
if subs.length != 2
|
|
61
|
+
raise ComplexTypeError, "Bad hash type: name=#{name}, substring=#{substring} - must have exactly two parameters"
|
|
62
|
+
end
|
|
63
|
+
key_types.concat(subs[0].map { |u| ComplexType.new([u]) })
|
|
64
|
+
subtypes.concat(subs[1].map { |u| ComplexType.new([u]) })
|
|
52
65
|
else
|
|
53
66
|
subtypes.concat subs
|
|
54
67
|
end
|
|
@@ -67,19 +80,63 @@ module Solargraph
|
|
|
67
80
|
end
|
|
68
81
|
raise "Please remove leading :: and set rooted instead - #{name.inspect}" if name.start_with?('::')
|
|
69
82
|
@name = name
|
|
70
|
-
@
|
|
71
|
-
|
|
83
|
+
@parameters_type = parameters_type
|
|
84
|
+
if implicit_union?
|
|
85
|
+
@key_types = key_types.uniq
|
|
86
|
+
@subtypes = subtypes.uniq
|
|
87
|
+
else
|
|
88
|
+
@key_types = key_types
|
|
89
|
+
@subtypes = subtypes
|
|
90
|
+
end
|
|
72
91
|
@rooted = rooted
|
|
73
92
|
@all_params = []
|
|
74
|
-
@all_params.concat key_types
|
|
75
|
-
@all_params.concat subtypes
|
|
76
|
-
|
|
93
|
+
@all_params.concat @key_types
|
|
94
|
+
@all_params.concat @subtypes
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def implicit_union?
|
|
98
|
+
# @todo use api_map to establish number of generics in type;
|
|
99
|
+
# if only one is allowed but multiple are passed in, treat
|
|
100
|
+
# those as implicit unions
|
|
101
|
+
['Hash', 'Array', 'Set', '_ToAry', 'Enumerable', '_Each'].include?(name) && parameters_type != :fixed
|
|
77
102
|
end
|
|
78
103
|
|
|
79
104
|
def to_s
|
|
80
105
|
tag
|
|
81
106
|
end
|
|
82
107
|
|
|
108
|
+
def simplify_literals
|
|
109
|
+
transform do |t|
|
|
110
|
+
next t unless t.literal?
|
|
111
|
+
t.recreate(new_name: t.non_literal_name)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def literal?
|
|
116
|
+
non_literal_name != name
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def non_literal_name
|
|
120
|
+
@non_literal_name ||= determine_non_literal_name
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def determine_non_literal_name
|
|
124
|
+
# https://github.com/ruby/rbs/blob/master/docs/syntax.md
|
|
125
|
+
#
|
|
126
|
+
# _literal_ ::= _string-literal_
|
|
127
|
+
# | _symbol-literal_
|
|
128
|
+
# | _integer-literal_
|
|
129
|
+
# | `true`
|
|
130
|
+
# | `false`
|
|
131
|
+
return name if name.empty?
|
|
132
|
+
return 'NilClass' if name == 'nil'
|
|
133
|
+
return 'Boolean' if ['true', 'false'].include?(name)
|
|
134
|
+
return 'Symbol' if name[0] == ':'
|
|
135
|
+
return 'String' if ['"', "'"].include?(name[0])
|
|
136
|
+
return 'Integer' if name.match?(/^-?\d+$/)
|
|
137
|
+
name
|
|
138
|
+
end
|
|
139
|
+
|
|
83
140
|
def eql?(other)
|
|
84
141
|
self.class == other.class &&
|
|
85
142
|
@name == other.name &&
|
|
@@ -107,11 +164,17 @@ module Solargraph
|
|
|
107
164
|
def rbs_name
|
|
108
165
|
if name == 'undefined'
|
|
109
166
|
'untyped'
|
|
167
|
+
elsif literal?
|
|
168
|
+
name
|
|
110
169
|
else
|
|
111
170
|
rooted_name
|
|
112
171
|
end
|
|
113
172
|
end
|
|
114
173
|
|
|
174
|
+
def desc
|
|
175
|
+
rooted_tags
|
|
176
|
+
end
|
|
177
|
+
|
|
115
178
|
# @return [String]
|
|
116
179
|
def to_rbs
|
|
117
180
|
if duck_type?
|
|
@@ -173,6 +236,23 @@ module Solargraph
|
|
|
173
236
|
name == GENERIC_TAG_NAME || all_params.any?(&:generic?)
|
|
174
237
|
end
|
|
175
238
|
|
|
239
|
+
# @param api_map [ApiMap] The ApiMap that performs qualification
|
|
240
|
+
# @param atype [ComplexType] type which may be assigned to this type
|
|
241
|
+
def can_assign?(api_map, atype)
|
|
242
|
+
logger.debug { "UniqueType#can_assign?(self=#{rooted_tags.inspect}, atype=#{atype.rooted_tags.inspect})" }
|
|
243
|
+
downcasted_atype = atype.downcast_to_literal_if_possible
|
|
244
|
+
out = downcasted_atype.all? do |autype|
|
|
245
|
+
autype.name == name || api_map.super_and_sub?(name, autype.name)
|
|
246
|
+
end
|
|
247
|
+
logger.debug { "UniqueType#can_assign?(self=#{rooted_tags.inspect}, atype=#{atype.rooted_tags.inspect}) => #{out}" }
|
|
248
|
+
out
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# @return [UniqueType]
|
|
252
|
+
def downcast_to_literal_if_possible
|
|
253
|
+
SINGLE_SUBTYPE.fetch(rooted_tag, self)
|
|
254
|
+
end
|
|
255
|
+
|
|
176
256
|
# @param generics_to_resolve [Enumerable<String>]
|
|
177
257
|
# @param context_type [UniqueType, nil]
|
|
178
258
|
# @param resolved_generic_values [Hash{String => ComplexType}] Added to as types are encountered or resolved
|
|
@@ -233,9 +313,26 @@ module Solargraph
|
|
|
233
313
|
|
|
234
314
|
transform(name) do |t|
|
|
235
315
|
if t.name == GENERIC_TAG_NAME
|
|
236
|
-
|
|
316
|
+
generic_name = t.subtypes.first&.name
|
|
317
|
+
idx = definitions.generics.index(generic_name)
|
|
237
318
|
next t if idx.nil?
|
|
238
|
-
context_type.
|
|
319
|
+
if context_type.parameters_type == :hash
|
|
320
|
+
if idx == 0
|
|
321
|
+
next ComplexType.new(context_type.key_types)
|
|
322
|
+
elsif idx == 1
|
|
323
|
+
next ComplexType.new(context_type.subtypes)
|
|
324
|
+
else
|
|
325
|
+
next ComplexType::UNDEFINED
|
|
326
|
+
end
|
|
327
|
+
elsif context_type.all?(&:implicit_union?)
|
|
328
|
+
if idx == 0 && !context_type.all_params.empty?
|
|
329
|
+
ComplexType.new(context_type.all_params)
|
|
330
|
+
else
|
|
331
|
+
ComplexType::UNDEFINED
|
|
332
|
+
end
|
|
333
|
+
else
|
|
334
|
+
context_type.all_params[idx] || definitions.generic_defaults[generic_name] || ComplexType::UNDEFINED
|
|
335
|
+
end
|
|
239
336
|
else
|
|
240
337
|
t
|
|
241
338
|
end
|
|
@@ -361,6 +458,18 @@ module Solargraph
|
|
|
361
458
|
|
|
362
459
|
UNDEFINED = UniqueType.new('undefined', rooted: false)
|
|
363
460
|
BOOLEAN = UniqueType.new('Boolean', rooted: true)
|
|
461
|
+
TRUE = UniqueType.new('true', rooted: true)
|
|
462
|
+
FALSE = UniqueType.new('false', rooted: true)
|
|
463
|
+
NIL = UniqueType.new('nil', rooted: true)
|
|
464
|
+
# @type [Hash{String => UniqueType}]
|
|
465
|
+
SINGLE_SUBTYPE = {
|
|
466
|
+
'::TrueClass' => UniqueType::TRUE,
|
|
467
|
+
'::FalseClass' => UniqueType::FALSE,
|
|
468
|
+
'::NilClass' => UniqueType::NIL
|
|
469
|
+
}.freeze
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
include Logging
|
|
364
473
|
end
|
|
365
474
|
end
|
|
366
475
|
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
|
|
@@ -20,12 +20,12 @@ module Solargraph
|
|
|
20
20
|
EMPTY_ENVIRON
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
# The Environ for a
|
|
23
|
+
# The Environ for a DocMap.
|
|
24
24
|
# Subclasses can override this method.
|
|
25
25
|
#
|
|
26
|
-
# @param
|
|
26
|
+
# @param doc_map [DocMap]
|
|
27
27
|
# @return [Environ]
|
|
28
|
-
def global
|
|
28
|
+
def global doc_map
|
|
29
29
|
EMPTY_ENVIRON
|
|
30
30
|
end
|
|
31
31
|
end
|
|
@@ -0,0 +1,60 @@
|
|
|
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
|
+
|
|
29
|
+
struct_node = if node.children[2].type == :block
|
|
30
|
+
node.children[2].children[0]
|
|
31
|
+
else
|
|
32
|
+
node.children[2]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
struct_definition_node?(struct_node)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def class_name
|
|
40
|
+
if node.children[0]
|
|
41
|
+
Parser::NodeMethods.unpack_name(node.children[0]) + "::#{node.children[1]}"
|
|
42
|
+
else
|
|
43
|
+
node.children[1].to_s
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
# @return [Parser::AST::Node]
|
|
50
|
+
def struct_node
|
|
51
|
+
if node.children[2].type == :block
|
|
52
|
+
node.children[2].children[0]
|
|
53
|
+
else
|
|
54
|
+
node.children[2]
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
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
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
module Convention
|
|
5
|
+
module StructDefinition
|
|
6
|
+
autoload :StructDefintionNode, 'solargraph/convention/struct_definition/struct_definition_node'
|
|
7
|
+
autoload :StructAssignmentNode, 'solargraph/convention/struct_definition/struct_assignment_node'
|
|
8
|
+
|
|
9
|
+
module NodeProcessors
|
|
10
|
+
class StructNode < Parser::NodeProcessor::Base
|
|
11
|
+
def process
|
|
12
|
+
return if struct_definition_node.nil?
|
|
13
|
+
|
|
14
|
+
loc = get_node_location(node)
|
|
15
|
+
nspin = Solargraph::Pin::Namespace.new(
|
|
16
|
+
type: :class,
|
|
17
|
+
location: loc,
|
|
18
|
+
closure: region.closure,
|
|
19
|
+
name: struct_definition_node.class_name,
|
|
20
|
+
comments: comments_for(node),
|
|
21
|
+
visibility: :public,
|
|
22
|
+
gates: region.closure.gates.freeze
|
|
23
|
+
)
|
|
24
|
+
pins.push nspin
|
|
25
|
+
|
|
26
|
+
# define initialize method
|
|
27
|
+
initialize_method_pin = Pin::Method.new(
|
|
28
|
+
name: 'initialize',
|
|
29
|
+
parameters: [],
|
|
30
|
+
scope: :instance,
|
|
31
|
+
location: get_node_location(node),
|
|
32
|
+
closure: nspin,
|
|
33
|
+
visibility: :private,
|
|
34
|
+
comments: comments_for(node)
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
pins.push initialize_method_pin
|
|
38
|
+
|
|
39
|
+
struct_definition_node.attributes.map do |attribute_node, attribute_name|
|
|
40
|
+
initialize_method_pin.parameters.push(
|
|
41
|
+
Pin::Parameter.new(
|
|
42
|
+
name: attribute_name,
|
|
43
|
+
decl: struct_definition_node.keyword_init? ? :kwarg : :arg,
|
|
44
|
+
location: get_node_location(attribute_node),
|
|
45
|
+
closure: initialize_method_pin
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# define attribute accessors and instance variables
|
|
51
|
+
struct_definition_node.attributes.each do |attribute_node, attribute_name|
|
|
52
|
+
[attribute_name, "#{attribute_name}="].each do |name|
|
|
53
|
+
method_pin = Pin::Method.new(
|
|
54
|
+
name: name,
|
|
55
|
+
parameters: [],
|
|
56
|
+
scope: :instance,
|
|
57
|
+
location: get_node_location(attribute_node),
|
|
58
|
+
closure: nspin,
|
|
59
|
+
comments: attribute_comments(attribute_node, attribute_name),
|
|
60
|
+
visibility: :public
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
pins.push method_pin
|
|
64
|
+
|
|
65
|
+
next unless name.include?('=') # setter
|
|
66
|
+
pins.push Pin::InstanceVariable.new(name: "@#{attribute_name}",
|
|
67
|
+
closure: method_pin,
|
|
68
|
+
location: get_node_location(attribute_node),
|
|
69
|
+
comments: attribute_comments(attribute_node, attribute_name))
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
process_children region.update(closure: nspin, visibility: :public)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
# @return [StructDefintionNode, nil]
|
|
79
|
+
def struct_definition_node
|
|
80
|
+
@struct_definition_node ||= if StructDefintionNode.valid?(node)
|
|
81
|
+
StructDefintionNode.new(node)
|
|
82
|
+
elsif StructAssignmentNode.valid?(node)
|
|
83
|
+
StructAssignmentNode.new(node)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# @param attribute_node [Parser::AST::Node]
|
|
88
|
+
# @return [String, nil]
|
|
89
|
+
def attribute_comments(attribute_node, attribute_name)
|
|
90
|
+
struct_comments = comments_for(attribute_node)
|
|
91
|
+
return if struct_comments.nil? || struct_comments.empty?
|
|
92
|
+
|
|
93
|
+
struct_comments.split("\n").find do |row|
|
|
94
|
+
row.include?(attribute_name)
|
|
95
|
+
end&.gsub('@param', '@return')&.gsub(attribute_name, '')
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -10,7 +10,9 @@ module Solargraph
|
|
|
10
10
|
autoload :Gemfile, 'solargraph/convention/gemfile'
|
|
11
11
|
autoload :Gemspec, 'solargraph/convention/gemspec'
|
|
12
12
|
autoload :Rakefile, 'solargraph/convention/rakefile'
|
|
13
|
+
autoload :StructDefinition, 'solargraph/convention/struct_definition'
|
|
13
14
|
|
|
15
|
+
# @type [Set<Convention::Base>]
|
|
14
16
|
@@conventions = Set.new
|
|
15
17
|
|
|
16
18
|
# @param convention [Class<Convention::Base>]
|
|
@@ -29,12 +31,12 @@ module Solargraph
|
|
|
29
31
|
result
|
|
30
32
|
end
|
|
31
33
|
|
|
32
|
-
# @param yard_map [
|
|
34
|
+
# @param yard_map [DocMap]
|
|
33
35
|
# @return [Environ]
|
|
34
|
-
def self.for_global(
|
|
36
|
+
def self.for_global(doc_map)
|
|
35
37
|
result = Environ.new
|
|
36
38
|
@@conventions.each do |conv|
|
|
37
|
-
result.merge conv.global(
|
|
39
|
+
result.merge conv.global(doc_map)
|
|
38
40
|
end
|
|
39
41
|
result
|
|
40
42
|
end
|