solargraph 0.54.0 → 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.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +58 -0
  3. data/lib/solargraph/api_map/cache.rb +10 -1
  4. data/lib/solargraph/api_map/index.rb +167 -0
  5. data/lib/solargraph/api_map/store.rb +75 -122
  6. data/lib/solargraph/api_map.rb +109 -41
  7. data/lib/solargraph/bench.rb +17 -1
  8. data/lib/solargraph/complex_type/type_methods.rb +17 -11
  9. data/lib/solargraph/complex_type/unique_type.rb +181 -17
  10. data/lib/solargraph/complex_type.rb +103 -24
  11. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +51 -0
  12. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +100 -0
  13. data/lib/solargraph/convention/struct_definition.rb +101 -0
  14. data/lib/solargraph/convention.rb +2 -0
  15. data/lib/solargraph/doc_map.rb +43 -18
  16. data/lib/solargraph/equality.rb +33 -0
  17. data/lib/solargraph/language_server/host/message_worker.rb +51 -5
  18. data/lib/solargraph/language_server/host.rb +13 -11
  19. data/lib/solargraph/language_server/message/base.rb +19 -12
  20. data/lib/solargraph/language_server/message/initialize.rb +3 -1
  21. data/lib/solargraph/language_server/message/text_document/completion.rb +0 -3
  22. data/lib/solargraph/language_server/message/text_document/definition.rb +3 -3
  23. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +3 -3
  24. data/lib/solargraph/language_server/message/text_document/formatting.rb +4 -0
  25. data/lib/solargraph/language_server/message/text_document/hover.rb +1 -1
  26. data/lib/solargraph/language_server/message/text_document/type_definition.rb +3 -3
  27. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +2 -2
  28. data/lib/solargraph/language_server/progress.rb +19 -2
  29. data/lib/solargraph/library.rb +31 -41
  30. data/lib/solargraph/location.rb +29 -1
  31. data/lib/solargraph/parser/comment_ripper.rb +11 -6
  32. data/lib/solargraph/parser/flow_sensitive_typing.rb +226 -0
  33. data/lib/solargraph/parser/node_methods.rb +15 -1
  34. data/lib/solargraph/parser/parser_gem/class_methods.rb +11 -6
  35. data/lib/solargraph/parser/parser_gem/node_chainer.rb +10 -10
  36. data/lib/solargraph/parser/parser_gem/node_methods.rb +6 -4
  37. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
  38. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +23 -19
  39. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +21 -1
  40. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +21 -0
  41. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +8 -2
  42. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +26 -5
  43. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +41 -0
  44. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +1 -1
  45. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +28 -0
  46. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +28 -0
  47. data/lib/solargraph/parser/parser_gem/node_processors.rb +10 -0
  48. data/lib/solargraph/parser.rb +3 -5
  49. data/lib/solargraph/pin/base.rb +47 -17
  50. data/lib/solargraph/pin/base_variable.rb +11 -4
  51. data/lib/solargraph/pin/block.rb +8 -26
  52. data/lib/solargraph/pin/breakable.rb +9 -0
  53. data/lib/solargraph/pin/callable.rb +147 -0
  54. data/lib/solargraph/pin/closure.rb +8 -3
  55. data/lib/solargraph/pin/common.rb +2 -6
  56. data/lib/solargraph/pin/conversions.rb +3 -2
  57. data/lib/solargraph/pin/instance_variable.rb +2 -2
  58. data/lib/solargraph/pin/local_variable.rb +7 -1
  59. data/lib/solargraph/pin/method.rb +76 -48
  60. data/lib/solargraph/pin/namespace.rb +14 -11
  61. data/lib/solargraph/pin/parameter.rb +24 -17
  62. data/lib/solargraph/pin/proxy_type.rb +13 -7
  63. data/lib/solargraph/pin/signature.rb +3 -129
  64. data/lib/solargraph/pin/until.rb +18 -0
  65. data/lib/solargraph/pin/while.rb +18 -0
  66. data/lib/solargraph/pin.rb +7 -1
  67. data/lib/solargraph/position.rb +7 -0
  68. data/lib/solargraph/range.rb +9 -4
  69. data/lib/solargraph/rbs_map/conversions.rb +84 -45
  70. data/lib/solargraph/rbs_map/core_fills.rb +16 -9
  71. data/lib/solargraph/rbs_map.rb +1 -0
  72. data/lib/solargraph/shell.rb +19 -2
  73. data/lib/solargraph/source/chain/array.rb +10 -8
  74. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  75. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  76. data/lib/solargraph/source/chain/call.rb +125 -61
  77. data/lib/solargraph/source/chain/constant.rb +1 -1
  78. data/lib/solargraph/source/chain/hash.rb +8 -2
  79. data/lib/solargraph/source/chain/if.rb +5 -0
  80. data/lib/solargraph/source/chain/link.rb +28 -5
  81. data/lib/solargraph/source/chain/literal.rb +27 -2
  82. data/lib/solargraph/source/chain/or.rb +1 -1
  83. data/lib/solargraph/source/chain/z_super.rb +1 -1
  84. data/lib/solargraph/source/chain.rb +131 -63
  85. data/lib/solargraph/source/cursor.rb +3 -2
  86. data/lib/solargraph/source/source_chainer.rb +2 -2
  87. data/lib/solargraph/source.rb +104 -86
  88. data/lib/solargraph/source_map/clip.rb +8 -6
  89. data/lib/solargraph/source_map/data.rb +30 -0
  90. data/lib/solargraph/source_map.rb +28 -16
  91. data/lib/solargraph/type_checker/checks.rb +4 -0
  92. data/lib/solargraph/type_checker/rules.rb +6 -1
  93. data/lib/solargraph/type_checker.rb +48 -21
  94. data/lib/solargraph/version.rb +1 -1
  95. data/lib/solargraph/views/environment.erb +3 -5
  96. data/lib/solargraph/workspace/config.rb +7 -3
  97. data/lib/solargraph/workspace.rb +1 -1
  98. data/lib/solargraph/yard_map/mapper/to_constant.rb +1 -0
  99. data/lib/solargraph/yard_map/mapper/to_method.rb +42 -15
  100. data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -0
  101. data/lib/solargraph/yard_map/mapper.rb +1 -0
  102. data/lib/solargraph/yardoc.rb +1 -1
  103. data/lib/solargraph.rb +1 -0
  104. data/solargraph.gemspec +5 -5
  105. metadata +41 -25
@@ -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,11 +27,13 @@ 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]
28
34
  rooted = true
35
+ elsif !can_root_name?(name)
36
+ rooted = true
29
37
  else
30
38
  rooted = false
31
39
  end
@@ -40,7 +48,7 @@ module Solargraph
40
48
  subs = ComplexType.parse(substring[1..-2], partial: true)
41
49
  parameters_type = PARAMETERS_TYPE_BY_STARTING_TAG.fetch(substring[0])
42
50
  if parameters_type == :hash
43
- 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)
44
52
  # @todo should be able to resolve map; both types have it
45
53
  # with same return type
46
54
  # @sg-ignore
@@ -63,21 +71,83 @@ module Solargraph
63
71
  if parameters_type.nil?
64
72
  raise "You must supply parameters_type if you provide parameters" unless key_types.empty? && subtypes.empty?
65
73
  end
66
- raise "Please remove leading :: and set rooted instead - #{name}" if name.start_with?('::')
74
+ raise "Please remove leading :: and set rooted instead - #{name.inspect}" if name.start_with?('::')
67
75
  @name = name
68
- @key_types = key_types
69
- @subtypes = subtypes
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
70
84
  @rooted = rooted
71
85
  @all_params = []
72
- @all_params.concat key_types
73
- @all_params.concat subtypes
74
- @parameters_type = parameters_type
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
75
95
  end
76
96
 
77
97
  def to_s
78
98
  tag
79
99
  end
80
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
+
133
+ def eql?(other)
134
+ self.class == other.class &&
135
+ @name == other.name &&
136
+ @key_types == other.key_types &&
137
+ @subtypes == other.subtypes &&
138
+ @rooted == other.rooted? &&
139
+ @all_params == other.all_params &&
140
+ @parameters_type == other.parameters_type
141
+ end
142
+
143
+ def ==(other)
144
+ eql?(other)
145
+ end
146
+
147
+ def hash
148
+ [self.class, @name, @key_types, @sub_types, @rooted, @all_params, @parameters_type].hash
149
+ end
150
+
81
151
  # @return [Array<UniqueType>]
82
152
  def items
83
153
  [self]
@@ -87,11 +157,17 @@ module Solargraph
87
157
  def rbs_name
88
158
  if name == 'undefined'
89
159
  'untyped'
160
+ elsif literal?
161
+ name
90
162
  else
91
163
  rooted_name
92
164
  end
93
165
  end
94
166
 
167
+ def desc
168
+ rooted_tags
169
+ end
170
+
95
171
  # @return [String]
96
172
  def to_rbs
97
173
  if duck_type?
@@ -108,7 +184,11 @@ module Solargraph
108
184
  # tuples don't have a name; they're just [foo, bar, baz].
109
185
  if substring == '()'
110
186
  # but there are no zero element tuples, so we go with an array
111
- 'Array[]'
187
+ if rooted?
188
+ '::Array[]'
189
+ else
190
+ 'Array[]'
191
+ end
112
192
  else
113
193
  # already generated surrounded by []
114
194
  parameters_as_rbs
@@ -149,6 +229,23 @@ module Solargraph
149
229
  name == GENERIC_TAG_NAME || all_params.any?(&:generic?)
150
230
  end
151
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
+
152
249
  # @param generics_to_resolve [Enumerable<String>]
153
250
  # @param context_type [UniqueType, nil]
154
251
  # @param resolved_generic_values [Hash{String => ComplexType}] Added to as types are encountered or resolved
@@ -211,7 +308,23 @@ module Solargraph
211
308
  if t.name == GENERIC_TAG_NAME
212
309
  idx = definitions.generics.index(t.subtypes.first&.name)
213
310
  next t if idx.nil?
214
- context_type.all_params[idx] || ComplexType::UNDEFINED
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
215
328
  else
216
329
  t
217
330
  end
@@ -238,6 +351,7 @@ module Solargraph
238
351
  # @return [self]
239
352
  def recreate(new_name: nil, make_rooted: nil, new_key_types: nil, new_subtypes: nil)
240
353
  raise "Please remove leading :: and set rooted instead - #{new_name}" if new_name&.start_with?('::')
354
+
241
355
  new_name ||= name
242
356
  new_key_types ||= @key_types
243
357
  new_subtypes ||= @subtypes
@@ -278,17 +392,26 @@ module Solargraph
278
392
  new_key_types = @key_types.flat_map { |ct| ct.items.map { |ut| ut.transform(&transform_type) } }
279
393
  new_subtypes = @subtypes.flat_map { |ct| ct.items.map { |ut| ut.transform(&transform_type) } }
280
394
  end
281
- new_type = recreate(new_name: new_name || name, new_key_types: new_key_types, new_subtypes: new_subtypes)
395
+ new_type = recreate(new_name: new_name || name, new_key_types: new_key_types, new_subtypes: new_subtypes, make_rooted: @rooted)
282
396
  yield new_type
283
397
  end
284
398
 
285
- # Transform references to the 'self' type to the specified concrete namespace
286
- # @param dst [String]
287
- # @return [UniqueType]
288
- def self_to dst
399
+ # Generate a ComplexType that fully qualifies this type's namespaces.
400
+ #
401
+ # @param api_map [ApiMap] The ApiMap that performs qualification
402
+ # @param context [String] The namespace from which to resolve names
403
+ # @return [self, ComplexType, UniqueType] The generated ComplexType
404
+ def qualify api_map, context = ''
289
405
  transform do |t|
290
- next t if t.name != 'self'
291
- t.recreate(new_name: dst, new_key_types: [], new_subtypes: [])
406
+ next t if t.name == GENERIC_TAG_NAME
407
+ next t if t.duck_type? || t.void? || t.undefined?
408
+ recon = (t.rooted? ? '' : context)
409
+ fqns = api_map.qualify(t.name, recon)
410
+ if fqns.nil?
411
+ next UniqueType::BOOLEAN if t.tag == 'Boolean'
412
+ next UniqueType::UNDEFINED
413
+ end
414
+ t.recreate(new_name: fqns, make_rooted: true)
292
415
  end
293
416
  end
294
417
 
@@ -296,8 +419,49 @@ module Solargraph
296
419
  @name == 'self' || @key_types.any?(&:selfy?) || @subtypes.any?(&:selfy?)
297
420
  end
298
421
 
422
+ # @param dst [ComplexType]
423
+ # @return [self]
424
+ def self_to_type dst
425
+ object_type_dst = dst.reduce_class_type
426
+ transform do |t|
427
+ next t if t.name != 'self'
428
+ object_type_dst
429
+ end
430
+ end
431
+
432
+ def all_rooted?
433
+ return true if name == GENERIC_TAG_NAME
434
+ rooted? && all_params.all?(&:rooted?)
435
+ end
436
+
437
+ def rooted?
438
+ !can_root_name? || @rooted
439
+ end
440
+
441
+ def can_root_name?(name_to_check = name)
442
+ self.class.can_root_name?(name_to_check)
443
+ end
444
+
445
+ # @param name [String]
446
+ def self.can_root_name?(name)
447
+ # name is not lowercase
448
+ !name.empty? && name != name.downcase
449
+ end
450
+
299
451
  UNDEFINED = UniqueType.new('undefined', rooted: false)
300
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
301
465
  end
302
466
  end
303
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,7 +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
- @items = types.flat_map(&:items).uniq(&:to_s)
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
26
+ end
27
+
28
+ # @sg-ignore Fix "Not enough arguments to Module#protected"
29
+ protected def equality_fields
30
+ [self.class, items]
19
31
  end
20
32
 
21
33
  # @param api_map [ApiMap]
@@ -24,7 +36,8 @@ module Solargraph
24
36
  def qualify api_map, context = ''
25
37
  red = reduce_object
26
38
  types = red.items.map do |t|
27
- next t if ['Boolean', 'nil', 'void', 'undefined'].include?(t.name)
39
+ next t if ['nil', 'void', 'undefined'].include?(t.name)
40
+ next t if ['::Boolean'].include?(t.rooted_name)
28
41
  t.qualify api_map, context
29
42
  end
30
43
  ComplexType.new(types).reduce_object
@@ -52,8 +65,18 @@ module Solargraph
52
65
  (@items.length > 1 ? ')' : ''))
53
66
  end
54
67
 
68
+ # @param dst [ComplexType]
69
+ # @return [ComplexType]
70
+ def self_to_type dst
71
+ object_type_dst = dst.reduce_class_type
72
+ transform do |t|
73
+ next t if t.name != 'self'
74
+ object_type_dst
75
+ end
76
+ end
77
+
55
78
  # @yieldparam [UniqueType]
56
- # @return [Array]
79
+ # @return [Array<UniqueType>]
57
80
  def map &block
58
81
  @items.map &block
59
82
  end
@@ -76,6 +99,12 @@ module Solargraph
76
99
  end
77
100
  end
78
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
+
79
108
  # @return [Integer]
80
109
  def length
81
110
  @items.length
@@ -86,10 +115,6 @@ module Solargraph
86
115
  @items
87
116
  end
88
117
 
89
- def tags
90
- @items.map(&:tag).join(', ')
91
- end
92
-
93
118
  # @param index [Integer]
94
119
  # @return [UniqueType]
95
120
  def [](index)
@@ -130,14 +155,39 @@ module Solargraph
130
155
  map(&:tag).join(', ')
131
156
  end
132
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
+
133
179
  def rooted_tags
134
180
  map(&:rooted_tag).join(', ')
135
181
  end
136
182
 
183
+ # @yieldparam [UniqueType]
137
184
  def all? &block
138
185
  @items.all? &block
139
186
  end
140
187
 
188
+ # @yieldparam [UniqueType]
189
+ # @yieldreturn [Boolean]
190
+ # @return [Boolean]
141
191
  def any? &block
142
192
  @items.compact.any? &block
143
193
  end
@@ -150,6 +200,10 @@ module Solargraph
150
200
  any?(&:generic?)
151
201
  end
152
202
 
203
+ def simplify_literals
204
+ ComplexType.new(map(&:simplify_literals))
205
+ end
206
+
153
207
  # @param new_name [String, nil]
154
208
  # @yieldparam t [UniqueType]
155
209
  # @yieldreturn [UniqueType]
@@ -171,16 +225,7 @@ module Solargraph
171
225
  # @return [ComplexType]
172
226
  def resolve_generics definitions, context_type
173
227
  result = @items.map { |i| i.resolve_generics(definitions, context_type) }
174
- ComplexType.try_parse(*result.map(&:tag))
175
- end
176
-
177
- # @param dst [String]
178
- # @return [ComplexType]
179
- def self_to dst
180
- return self unless selfy?
181
- red = reduce_class(dst)
182
- result = @items.map { |i| i.self_to red }
183
- ComplexType.try_parse(*result.map(&:tag))
228
+ ComplexType.new(result)
184
229
  end
185
230
 
186
231
  def nullable?
@@ -192,14 +237,43 @@ module Solargraph
192
237
  @items.first.all_params || []
193
238
  end
194
239
 
240
+ # @return [ComplexType]
241
+ def reduce_class_type
242
+ new_items = items.flat_map do |type|
243
+ next type unless ['Module', 'Class'].include?(type.name)
244
+
245
+ type.all_params
246
+ end
247
+ ComplexType.new(new_items)
248
+ end
249
+
250
+ # every type and subtype in this union have been resolved to be
251
+ # fully qualified
252
+ def all_rooted?
253
+ all?(&:all_rooted?)
254
+ end
255
+
256
+ # every top-level type has resolved to be fully qualified; see
257
+ # #all_rooted? to check their subtypes as well
258
+ def rooted?
259
+ all?(&:rooted?)
260
+ end
261
+
195
262
  attr_reader :items
196
263
 
264
+ def rooted?
265
+ @items.all?(&:rooted?)
266
+ end
267
+
197
268
  protected
198
269
 
199
270
  # @return [ComplexType]
200
271
  def reduce_object
201
- return self if name != 'Object' || subtypes.empty?
202
- ComplexType.try_parse(reduce_class(subtypes.join(', ')))
272
+ new_items = items.flat_map do |ut|
273
+ next [ut] if ut.name != 'Object' || ut.subtypes.empty?
274
+ ut.subtypes
275
+ end
276
+ ComplexType.new(new_items)
203
277
  end
204
278
 
205
279
  def bottom?
@@ -219,12 +293,17 @@ module Solargraph
219
293
  # Consumers should not need to use this parameter; it should only be
220
294
  # used internally.
221
295
  #
222
- # @param *strings [Array<String>] The type definitions to parse
296
+ # @param strings [Array<String>] The type definitions to parse
223
297
  # @return [ComplexType]
224
- # @overload parse(*strings, partial: false)
225
- # @todo Need ability to use a literal true as a type below
226
- # @param partial [Boolean] True if the string is part of a another type
227
- # @return [Array<UniqueType>]
298
+ # # @overload parse(*strings, partial: false)
299
+ # # @todo Need ability to use a literal true as a type below
300
+ # # @param partial [Boolean] True if the string is part of a another type
301
+ # # @return [Array<UniqueType>]
302
+ # @sg-ignore
303
+ # @todo To be able to select the right signature above,
304
+ # Chain::Call needs to know the decl type (:arg, :optarg,
305
+ # :kwarg, etc) of the arguments given, instead of just having
306
+ # an array of Chains as the arguments.
228
307
  def parse *strings, partial: false
229
308
  # @type [Hash{Array<String> => ComplexType}]
230
309
  @cache ||= {}
@@ -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