solargraph 0.58.2 → 0.59.0.dev.1

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 (154) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +3 -0
  3. data/.github/workflows/linting.yml +4 -5
  4. data/.github/workflows/plugins.yml +40 -36
  5. data/.github/workflows/rspec.yml +45 -13
  6. data/.github/workflows/typecheck.yml +2 -2
  7. data/.gitignore +0 -1
  8. data/.rubocop_todo.yml +27 -49
  9. data/CHANGELOG.md +1 -7
  10. data/README.md +3 -3
  11. data/Rakefile +1 -0
  12. data/lib/solargraph/api_map/cache.rb +3 -3
  13. data/lib/solargraph/api_map/constants.rb +13 -3
  14. data/lib/solargraph/api_map/index.rb +22 -11
  15. data/lib/solargraph/api_map/source_to_yard.rb +13 -1
  16. data/lib/solargraph/api_map/store.rb +11 -8
  17. data/lib/solargraph/api_map.rb +105 -50
  18. data/lib/solargraph/complex_type/conformance.rb +176 -0
  19. data/lib/solargraph/complex_type/type_methods.rb +16 -2
  20. data/lib/solargraph/complex_type/unique_type.rb +170 -20
  21. data/lib/solargraph/complex_type.rb +119 -14
  22. data/lib/solargraph/convention/data_definition/data_definition_node.rb +3 -1
  23. data/lib/solargraph/convention/data_definition.rb +4 -1
  24. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +1 -0
  25. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +1 -0
  26. data/lib/solargraph/convention/struct_definition.rb +5 -1
  27. data/lib/solargraph/diagnostics/require_not_found.rb +1 -0
  28. data/lib/solargraph/diagnostics/rubocop.rb +1 -0
  29. data/lib/solargraph/diagnostics/rubocop_helpers.rb +2 -0
  30. data/lib/solargraph/diagnostics/type_check.rb +1 -0
  31. data/lib/solargraph/doc_map.rb +134 -373
  32. data/lib/solargraph/equality.rb +1 -1
  33. data/lib/solargraph/gem_pins.rb +14 -15
  34. data/lib/solargraph/language_server/host/diagnoser.rb +89 -89
  35. data/lib/solargraph/language_server/host/dispatch.rb +1 -0
  36. data/lib/solargraph/language_server/host/message_worker.rb +2 -1
  37. data/lib/solargraph/language_server/host/sources.rb +1 -0
  38. data/lib/solargraph/language_server/host.rb +6 -1
  39. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +2 -7
  40. data/lib/solargraph/language_server/message/extended/document.rb +1 -0
  41. data/lib/solargraph/language_server/message/text_document/completion.rb +2 -0
  42. data/lib/solargraph/language_server/message/text_document/definition.rb +2 -0
  43. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +2 -0
  44. data/lib/solargraph/language_server/message/text_document/formatting.rb +2 -0
  45. data/lib/solargraph/language_server/message/text_document/hover.rb +2 -0
  46. data/lib/solargraph/language_server/message/text_document/signature_help.rb +1 -0
  47. data/lib/solargraph/language_server/message/text_document/type_definition.rb +2 -0
  48. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +2 -0
  49. data/lib/solargraph/library.rb +59 -13
  50. data/lib/solargraph/location.rb +9 -4
  51. data/lib/solargraph/logging.rb +21 -1
  52. data/lib/solargraph/parser/comment_ripper.rb +7 -0
  53. data/lib/solargraph/parser/flow_sensitive_typing.rb +330 -102
  54. data/lib/solargraph/parser/node_processor/base.rb +32 -2
  55. data/lib/solargraph/parser/node_processor.rb +7 -6
  56. data/lib/solargraph/parser/parser_gem/class_methods.rb +28 -10
  57. data/lib/solargraph/parser/parser_gem/node_chainer.rb +31 -6
  58. data/lib/solargraph/parser/parser_gem/node_methods.rb +27 -7
  59. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +4 -4
  60. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +2 -0
  61. data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +9 -0
  62. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +11 -11
  63. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +7 -0
  64. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +36 -6
  65. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +3 -2
  66. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +1 -0
  67. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +3 -1
  68. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +2 -2
  69. data/lib/solargraph/parser/parser_gem/node_processors/or_node.rb +22 -0
  70. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -1
  71. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +2 -1
  72. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +1 -0
  73. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +12 -7
  74. data/lib/solargraph/parser/parser_gem/node_processors/when_node.rb +23 -0
  75. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +5 -1
  76. data/lib/solargraph/parser/parser_gem/node_processors.rb +4 -0
  77. data/lib/solargraph/parser/region.rb +9 -3
  78. data/lib/solargraph/parser/snippet.rb +1 -1
  79. data/lib/solargraph/pin/base.rb +53 -21
  80. data/lib/solargraph/pin/base_variable.rb +312 -20
  81. data/lib/solargraph/pin/block.rb +26 -4
  82. data/lib/solargraph/pin/breakable.rb +5 -1
  83. data/lib/solargraph/pin/callable.rb +50 -3
  84. data/lib/solargraph/pin/closure.rb +2 -6
  85. data/lib/solargraph/pin/common.rb +20 -5
  86. data/lib/solargraph/pin/compound_statement.rb +55 -0
  87. data/lib/solargraph/pin/conversions.rb +2 -1
  88. data/lib/solargraph/pin/delegated_method.rb +15 -4
  89. data/lib/solargraph/pin/documenting.rb +1 -0
  90. data/lib/solargraph/pin/instance_variable.rb +5 -1
  91. data/lib/solargraph/pin/keyword.rb +0 -4
  92. data/lib/solargraph/pin/local_variable.rb +13 -57
  93. data/lib/solargraph/pin/method.rb +90 -42
  94. data/lib/solargraph/pin/method_alias.rb +8 -0
  95. data/lib/solargraph/pin/namespace.rb +7 -1
  96. data/lib/solargraph/pin/parameter.rb +76 -13
  97. data/lib/solargraph/pin/proxy_type.rb +2 -1
  98. data/lib/solargraph/pin/reference/override.rb +1 -1
  99. data/lib/solargraph/pin/reference/superclass.rb +2 -0
  100. data/lib/solargraph/pin/reference.rb +2 -0
  101. data/lib/solargraph/pin/search.rb +1 -0
  102. data/lib/solargraph/pin/signature.rb +8 -0
  103. data/lib/solargraph/pin/symbol.rb +1 -1
  104. data/lib/solargraph/pin/until.rb +1 -1
  105. data/lib/solargraph/pin/while.rb +1 -1
  106. data/lib/solargraph/pin.rb +2 -0
  107. data/lib/solargraph/pin_cache.rb +477 -57
  108. data/lib/solargraph/position.rb +12 -26
  109. data/lib/solargraph/range.rb +6 -6
  110. data/lib/solargraph/rbs_map/conversions.rb +33 -10
  111. data/lib/solargraph/rbs_map/core_map.rb +24 -17
  112. data/lib/solargraph/rbs_map/stdlib_map.rb +34 -5
  113. data/lib/solargraph/rbs_map.rb +74 -20
  114. data/lib/solargraph/shell.rb +73 -28
  115. data/lib/solargraph/source/chain/call.rb +52 -17
  116. data/lib/solargraph/source/chain/constant.rb +2 -0
  117. data/lib/solargraph/source/chain/hash.rb +1 -0
  118. data/lib/solargraph/source/chain/if.rb +1 -0
  119. data/lib/solargraph/source/chain/instance_variable.rb +22 -1
  120. data/lib/solargraph/source/chain/literal.rb +5 -0
  121. data/lib/solargraph/source/chain/or.rb +9 -1
  122. data/lib/solargraph/source/chain.rb +25 -22
  123. data/lib/solargraph/source/change.rb +9 -2
  124. data/lib/solargraph/source/cursor.rb +7 -1
  125. data/lib/solargraph/source/source_chainer.rb +13 -3
  126. data/lib/solargraph/source/updater.rb +4 -0
  127. data/lib/solargraph/source.rb +33 -7
  128. data/lib/solargraph/source_map/clip.rb +13 -2
  129. data/lib/solargraph/source_map/data.rb +4 -1
  130. data/lib/solargraph/source_map/mapper.rb +24 -1
  131. data/lib/solargraph/source_map.rb +14 -6
  132. data/lib/solargraph/type_checker/problem.rb +3 -1
  133. data/lib/solargraph/type_checker/rules.rb +75 -2
  134. data/lib/solargraph/type_checker.rb +111 -30
  135. data/lib/solargraph/version.rb +1 -1
  136. data/lib/solargraph/workspace/config.rb +3 -1
  137. data/lib/solargraph/workspace/gemspecs.rb +367 -0
  138. data/lib/solargraph/workspace/require_paths.rb +1 -0
  139. data/lib/solargraph/workspace.rb +158 -16
  140. data/lib/solargraph/yard_map/helpers.rb +2 -1
  141. data/lib/solargraph/yard_map/mapper/to_method.rb +5 -1
  142. data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -0
  143. data/lib/solargraph/yard_map/mapper.rb +5 -0
  144. data/lib/solargraph/yardoc.rb +33 -23
  145. data/lib/solargraph.rb +24 -3
  146. data/rbs/fills/rubygems/0/dependency.rbs +193 -0
  147. data/rbs/fills/tuple/tuple.rbs +28 -0
  148. data/rbs/shims/ast/0/node.rbs +1 -1
  149. data/rbs/shims/diff-lcs/1.5/diff-lcs.rbs +11 -0
  150. data/solargraph.gemspec +2 -1
  151. metadata +12 -7
  152. data/lib/solargraph/type_checker/checks.rb +0 -124
  153. data/lib/solargraph/type_checker/param_def.rb +0 -37
  154. data/lib/solargraph/yard_map/to_method.rb +0 -89
@@ -11,7 +11,6 @@ module Solargraph
11
11
 
12
12
  attr_reader :all_params, :subtypes, :key_types
13
13
 
14
- # @sg-ignore Fix "Not enough arguments to Module#protected"
15
14
  protected def equality_fields
16
15
  [@name, @all_params, @subtypes, @key_types]
17
16
  end
@@ -46,6 +45,7 @@ module Solargraph
46
45
  parameters_type = nil
47
46
  unless substring.empty?
48
47
  subs = ComplexType.parse(substring[1..-2], partial: true)
48
+ # @sg-ignore Need to add nil check here
49
49
  parameters_type = PARAMETERS_TYPE_BY_STARTING_TAG.fetch(substring[0])
50
50
  if parameters_type == :hash
51
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)
@@ -62,6 +62,7 @@ module Solargraph
62
62
  subtypes.concat subs
63
63
  end
64
64
  end
65
+ # @sg-ignore Need to add nil check here
65
66
  new(name, key_types, subtypes, rooted: rooted, parameters_type: parameters_type)
66
67
  end
67
68
 
@@ -109,6 +110,44 @@ module Solargraph
109
110
  end
110
111
  end
111
112
 
113
+ # @param exclude_types [ComplexType, nil]
114
+ # @param api_map [ApiMap]
115
+ # @return [ComplexType, self]
116
+ def exclude exclude_types, api_map
117
+ return self if exclude_types.nil?
118
+
119
+ types = items - exclude_types.items
120
+ types = [ComplexType::UniqueType::UNDEFINED] if types.empty?
121
+ ComplexType.new(types)
122
+ end
123
+
124
+ # @see https://en.wikipedia.org/wiki/Intersection_type
125
+ #
126
+ # @param intersection_type [ComplexType, ComplexType::UniqueType, nil]
127
+ # @param api_map [ApiMap]
128
+ # @return [self, ComplexType]
129
+ def intersect_with intersection_type, api_map
130
+ return self if intersection_type.nil?
131
+ return intersection_type if undefined?
132
+ types = []
133
+ # try to find common types via conformance
134
+ items.each do |ut|
135
+ intersection_type.each do |int_type|
136
+ if ut.conforms_to?(api_map, int_type, :assignment)
137
+ types << ut
138
+ elsif int_type.conforms_to?(api_map, ut, :assignment)
139
+ types << int_type
140
+ end
141
+ end
142
+ end
143
+ types = [ComplexType::UniqueType::UNDEFINED] if types.empty?
144
+ ComplexType.new(types)
145
+ end
146
+
147
+ def simplifyable_literal?
148
+ literal? && name != 'nil'
149
+ end
150
+
112
151
  def literal?
113
152
  non_literal_name != name
114
153
  end
@@ -118,6 +157,13 @@ module Solargraph
118
157
  @non_literal_name ||= determine_non_literal_name
119
158
  end
120
159
 
160
+ # @return [self]
161
+ def without_nil
162
+ return UniqueType::UNDEFINED if nil_type?
163
+
164
+ self
165
+ end
166
+
121
167
  # @return [String]
122
168
  def determine_non_literal_name
123
169
  # https://github.com/ruby/rbs/blob/master/docs/syntax.md
@@ -131,6 +177,7 @@ module Solargraph
131
177
  return 'NilClass' if name == 'nil'
132
178
  return 'Boolean' if ['true', 'false'].include?(name)
133
179
  return 'Symbol' if name[0] == ':'
180
+ # @sg-ignore Need to add nil check here
134
181
  return 'String' if ['"', "'"].include?(name[0])
135
182
  return 'Integer' if name.match?(/^-?\d+$/)
136
183
  name
@@ -138,17 +185,17 @@ module Solargraph
138
185
 
139
186
  def eql?(other)
140
187
  self.class == other.class &&
141
- # @sg-ignore https://github.com/castwide/solargraph/pull/1114
188
+ # @sg-ignore flow sensitive typing should support .class == .class
142
189
  @name == other.name &&
143
- # @sg-ignore https://github.com/castwide/solargraph/pull/1114
190
+ # @sg-ignore flow sensitive typing should support .class == .class
144
191
  @key_types == other.key_types &&
145
- # @sg-ignore https://github.com/castwide/solargraph/pull/1114
192
+ # @sg-ignore flow sensitive typing should support .class == .class
146
193
  @subtypes == other.subtypes &&
147
- # @sg-ignore https://github.com/castwide/solargraph/pull/1114
194
+ # @sg-ignore flow sensitive typing should support .class == .class
148
195
  @rooted == other.rooted? &&
149
- # @sg-ignore https://github.com/castwide/solargraph/pull/1114
196
+ # @sg-ignore flow sensitive typing should support .class == .class
150
197
  @all_params == other.all_params &&
151
- # @sg-ignore https://github.com/castwide/solargraph/pull/1114
198
+ # @sg-ignore flow sensitive typing should support .class == .class
152
199
  @parameters_type == other.parameters_type
153
200
  end
154
201
 
@@ -156,10 +203,86 @@ module Solargraph
156
203
  eql?(other)
157
204
  end
158
205
 
206
+ # https://www.playfulpython.com/type-hinting-covariance-contra-variance/
207
+
208
+ # "[Expected] type variables that are COVARIANT can be substituted with
209
+ # a more specific [inferred] type without causing errors"
210
+ #
211
+ # "[Expected] type variables that are CONTRAVARIANT can be substituted
212
+ # with a more general [inferred] type without causing errors"
213
+ #
214
+ # "[Expected] types where neither is possible are INVARIANT"
215
+ #
216
+ # @param _situation [:method_call, :return_type]
217
+ # @param default [Symbol] The default variance to return if the type is not one of the special cases
218
+ #
219
+ # @return [:invariant, :covariant, :contravariant]
220
+ def parameter_variance _situation, default = :covariant
221
+ # @todo RBS can specify variance - maybe we can use that info
222
+ # and also let folks specify?
223
+ #
224
+ # Array/Set: ideally invariant, since we don't know if user is
225
+ # going to add new stuff into it or read it. But we don't
226
+ # have a way to specify, so we use covariant
227
+ # Enumerable: covariant: can't be changed, so we can pass
228
+ # in more specific subtypes
229
+ # Hash: read-only would be covariant, read-write would be
230
+ # invariant if we could distinguish that - should default to
231
+ # covariant
232
+ # contravariant?: Proc - can be changed, so we can pass
233
+ # in less specific super types
234
+ if ['Hash', 'Tuple', 'Array', 'Set', 'Enumerable'].include?(name) && fixed_parameters?
235
+ :covariant
236
+ else
237
+ default
238
+ end
239
+ end
240
+
241
+ # Whether this is an RBS interface like _ToAry or _Each.
242
+ def interface?
243
+ name.start_with?('_')
244
+ end
245
+
246
+ # @param other [UniqueType]
247
+ def erased_version_of?(other)
248
+ name == other.name && (all_params.empty? || all_params.all?(&:undefined?))
249
+ end
250
+
251
+ # @param api_map [ApiMap]
252
+ # @param expected [ComplexType::UniqueType, ComplexType]
253
+ # @param situation [:method_call, :assignment, :return_type]
254
+ # @param rules [Array<:allow_subtype_skew, :allow_empty_params, :allow_reverse_match, :allow_any_match, :allow_undefined, :allow_unresolved_generic>]
255
+ # @param variance [:invariant, :covariant, :contravariant]
256
+ def conforms_to?(api_map, expected, situation, rules = [],
257
+ variance: erased_variance(situation))
258
+ return true if undefined? && rules.include?(:allow_undefined)
259
+
260
+ # @todo teach this to validate duck types as inferred type
261
+ return true if duck_type?
262
+
263
+ # complex types as expectations are unions - we only need to
264
+ # match one of their unique types
265
+ expected.any? do |expected_unique_type|
266
+ # :nocov:
267
+ unless expected_unique_type.instance_of?(UniqueType)
268
+ raise "Expected type must be a UniqueType, got #{expected_unique_type.class} in #{expected.inspect}"
269
+ end
270
+ # :nocov:
271
+ conformance = Conformance.new(api_map, self, expected_unique_type, situation,
272
+ rules, variance: variance)
273
+ conformance.conforms_to_unique_type?
274
+ end
275
+ end
276
+
159
277
  def hash
160
278
  [self.class, @name, @key_types, @sub_types, @rooted, @all_params, @parameters_type].hash
161
279
  end
162
280
 
281
+ # @return [self]
282
+ def erase_parameters
283
+ UniqueType.new(name, rooted: rooted?, parameters_type: parameters_type)
284
+ end
285
+
163
286
  # @return [Array<UniqueType>]
164
287
  def items
165
288
  [self]
@@ -181,6 +304,7 @@ module Solargraph
181
304
  rooted_tags
182
305
  end
183
306
 
307
+ # @sg-ignore Need better if/elseanalysis
184
308
  # @return [String]
185
309
  def to_rbs
186
310
  if duck_type?
@@ -190,7 +314,7 @@ module Solargraph
190
314
  elsif name.downcase == 'nil'
191
315
  'nil'
192
316
  elsif name == GENERIC_TAG_NAME
193
- all_params.first.name
317
+ all_params.first&.name
194
318
  elsif ['Class', 'Module'].include?(name)
195
319
  rbs_name
196
320
  elsif ['Tuple', 'Array'].include?(name) && fixed_parameters?
@@ -242,16 +366,13 @@ module Solargraph
242
366
  name == GENERIC_TAG_NAME || all_params.any?(&:generic?)
243
367
  end
244
368
 
245
- # @param api_map [ApiMap] The ApiMap that performs qualification
246
- # @param atype [ComplexType] type which may be assigned to this type
247
- def can_assign?(api_map, atype)
248
- logger.debug { "UniqueType#can_assign?(self=#{rooted_tags.inspect}, atype=#{atype.rooted_tags.inspect})" }
249
- downcasted_atype = atype.downcast_to_literal_if_possible
250
- out = downcasted_atype.all? do |autype|
251
- autype.name == name || api_map.super_and_sub?(name, autype.name)
252
- end
253
- logger.debug { "UniqueType#can_assign?(self=#{rooted_tags.inspect}, atype=#{atype.rooted_tags.inspect}) => #{out}" }
254
- out
369
+ def nullable?
370
+ nil_type?
371
+ end
372
+
373
+ # @yieldreturn [Boolean]
374
+ def all? &block
375
+ block.yield self
255
376
  end
256
377
 
257
378
  # @return [UniqueType]
@@ -260,15 +381,18 @@ module Solargraph
260
381
  end
261
382
 
262
383
  # @param generics_to_resolve [Enumerable<String>]
263
- # @param context_type [UniqueType, nil]
384
+ # @param context_type [ComplexType, UniqueType, nil]
264
385
  # @param resolved_generic_values [Hash{String => ComplexType, ComplexType::UniqueType}] Added to as types are encountered or resolved
265
386
  # @return [UniqueType, ComplexType]
266
387
  def resolve_generics_from_context generics_to_resolve, context_type, resolved_generic_values: {}
267
388
  if name == ComplexType::GENERIC_TAG_NAME
268
389
  type_param = subtypes.first&.name
390
+ # @sg-ignore flow sensitive typing needs to eliminate literal from union with [:bar].include?(foo)
269
391
  return self unless generics_to_resolve.include? type_param
392
+ # @sg-ignore flow sensitive typing needs to eliminate literal from union with [:bar].include?(foo)
270
393
  unless context_type.nil? || !resolved_generic_values[type_param].nil?
271
394
  new_binding = true
395
+ # @sg-ignore flow sensitive typing needs to eliminate literal from union with [:bar].include?(foo)
272
396
  resolved_generic_values[type_param] = context_type
273
397
  end
274
398
  if new_binding
@@ -276,6 +400,7 @@ module Solargraph
276
400
  complex_type.resolve_generics_from_context(generics_to_resolve, nil, resolved_generic_values: resolved_generic_values)
277
401
  end
278
402
  end
403
+ # @sg-ignore flow sensitive typing needs to eliminate literal from union with [:bar].include?(foo)
279
404
  return resolved_generic_values[type_param] || self
280
405
  end
281
406
 
@@ -286,7 +411,7 @@ module Solargraph
286
411
  end
287
412
 
288
413
  # @param generics_to_resolve [Enumerable<String>]
289
- # @param context_type [UniqueType, nil]
414
+ # @param context_type [UniqueType, ComplexType, nil]
290
415
  # @param resolved_generic_values [Hash{String => ComplexType}]
291
416
  # @yieldreturn [Array<ComplexType>]
292
417
  # @return [Array<ComplexType>]
@@ -337,6 +462,7 @@ module Solargraph
337
462
  ComplexType::UNDEFINED
338
463
  end
339
464
  else
465
+ # @sg-ignore Need to add nil check here
340
466
  context_type.all_params[idx] || definitions.generic_defaults[generic_name] || ComplexType::UNDEFINED
341
467
  end
342
468
  else
@@ -352,6 +478,13 @@ module Solargraph
352
478
  [block.yield(self)]
353
479
  end
354
480
 
481
+ # @yieldparam t [self]
482
+ # @yieldreturn [self]
483
+ # @return [Enumerable<self>]
484
+ def each &block
485
+ [self].each &block
486
+ end
487
+
355
488
  # @return [Array<UniqueType>]
356
489
  def to_a
357
490
  [self]
@@ -370,6 +503,7 @@ module Solargraph
370
503
  new_key_types ||= @key_types
371
504
  new_subtypes ||= @subtypes
372
505
  make_rooted = @rooted if make_rooted.nil?
506
+ # @sg-ignore flow sensitive typing needs better handling of ||= on lvars
373
507
  UniqueType.new(new_name, new_key_types, new_subtypes, rooted: make_rooted, parameters_type: parameters_type)
374
508
  end
375
509
 
@@ -443,6 +577,22 @@ module Solargraph
443
577
  end
444
578
  end
445
579
 
580
+ # @yieldreturn [Boolean]
581
+ def any? &block
582
+ block.yield self
583
+ end
584
+
585
+ # @return [ComplexType]
586
+ def reduce_class_type
587
+ new_items = items.flat_map do |type|
588
+ next type unless ['Module', 'Class'].include?(type.name)
589
+ next type if type.all_params.empty?
590
+
591
+ type.all_params
592
+ end
593
+ ComplexType.new(new_items)
594
+ end
595
+
446
596
  def all_rooted?
447
597
  return true if name == GENERIC_TAG_NAME
448
598
  rooted? && all_params.all?(&:rooted?)
@@ -9,6 +9,7 @@ module Solargraph
9
9
  # include TypeMethods
10
10
  include Equality
11
11
 
12
+ autoload :Conformance, 'solargraph/complex_type/conformance'
12
13
  autoload :TypeMethods, 'solargraph/complex_type/type_methods'
13
14
  autoload :UniqueType, 'solargraph/complex_type/unique_type'
14
15
 
@@ -19,13 +20,15 @@ module Solargraph
19
20
  items = types.flat_map(&:items).uniq(&:to_s)
20
21
  if items.any? { |i| i.name == 'false' } && items.any? { |i| i.name == 'true' }
21
22
  items.delete_if { |i| i.name == 'false' || i.name == 'true' }
22
- items.unshift(ComplexType::BOOLEAN)
23
+ items.unshift(UniqueType::BOOLEAN)
23
24
  end
25
+ # @type [Array<UniqueType>]
24
26
  items = [UniqueType::UNDEFINED] if items.any?(&:undefined?)
27
+ # @todo shouldn't need this cast - if statement above adds an 'Array' type
28
+ # @type [Array<UniqueType>]
25
29
  @items = items
26
30
  end
27
31
 
28
- # @sg-ignore Fix "Not enough arguments to Module#protected"
29
32
  protected def equality_fields
30
33
  [self.class, items]
31
34
  end
@@ -44,7 +47,7 @@ module Solargraph
44
47
  end
45
48
 
46
49
  # @param generics_to_resolve [Enumerable<String>]]
47
- # @param context_type [UniqueType, nil]
50
+ # @param context_type [ComplexType, ComplexType::UniqueType, nil]
48
51
  # @param resolved_generic_values [Hash{String => ComplexType}] Added to as types are encountered or resolved
49
52
  # @return [self]
50
53
  def resolve_generics_from_context generics_to_resolve, context_type, resolved_generic_values: {}
@@ -65,7 +68,7 @@ module Solargraph
65
68
  (@items.length > 1 ? ')' : ''))
66
69
  end
67
70
 
68
- # @param dst [ComplexType]
71
+ # @param dst [ComplexType, ComplexType::UniqueType]
69
72
  # @return [ComplexType]
70
73
  def self_to_type dst
71
74
  object_type_dst = dst.reduce_class_type
@@ -76,9 +79,13 @@ module Solargraph
76
79
  end
77
80
 
78
81
  # @yieldparam [UniqueType]
82
+ # @yieldreturn [UniqueType]
79
83
  # @return [Array<UniqueType>]
80
- def map &block
81
- @items.map &block
84
+ # @sg-ignore Declared return type
85
+ # ::Array<::Solargraph::ComplexType::UniqueType> does not match
86
+ # inferred type ::Array<::Proc> for Solargraph::ComplexType#map
87
+ def map(&block)
88
+ @items.map(&block)
82
89
  end
83
90
 
84
91
  # @yieldparam [UniqueType]
@@ -99,12 +106,6 @@ module Solargraph
99
106
  end
100
107
  end
101
108
 
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
-
108
109
  # @param new_name [String, nil]
109
110
  # @param make_rooted [Boolean, nil]
110
111
  # @param new_key_types [Array<ComplexType>, nil]
@@ -194,6 +195,60 @@ module Solargraph
194
195
  rooted_tags
195
196
  end
196
197
 
198
+ # @param api_map [ApiMap]
199
+ # @param expected [ComplexType, ComplexType::UniqueType]
200
+ # @param situation [:method_call, :return_type, :assignment]
201
+ # @param allow_subtype_skew [Boolean] if false, check if any
202
+ # subtypes of the expected type match the inferred type
203
+ # @param allow_reverse_match [Boolean] if true, check if any subtypes
204
+ # of the expected type match the inferred type
205
+ # @param allow_empty_params [Boolean] if true, allow a general
206
+ # inferred type without parameters to conform to a more specific
207
+ # expected type
208
+ # @param allow_any_match [Boolean] if true, any unique type
209
+ # matched in the inferred qualifies as a match
210
+ # @param allow_undefined [Boolean] if true, treat undefined as a
211
+ # wildcard that matches anything
212
+ # @param rules [Array<:allow_subtype_skew, :allow_empty_params, :allow_reverse_match, :allow_any_match, :allow_undefined, :allow_unresolved_generic, :allow_unmatched_interface>]
213
+ # @param variance [:invariant, :covariant, :contravariant]
214
+ # @return [Boolean]
215
+ def conforms_to?(api_map, expected,
216
+ situation,
217
+ rules = [],
218
+ variance: erased_variance(situation))
219
+ expected = expected.downcast_to_literal_if_possible
220
+ inferred = downcast_to_literal_if_possible
221
+
222
+ return duck_types_match?(api_map, expected, inferred) if expected.duck_type?
223
+
224
+ if rules.include? :allow_any_match
225
+ inferred.any? do |inf|
226
+ inf.conforms_to?(api_map, expected, situation, rules,
227
+ variance: variance)
228
+ end
229
+ else
230
+ inferred.all? do |inf|
231
+ inf.conforms_to?(api_map, expected, situation, rules,
232
+ variance: variance)
233
+ end
234
+ end
235
+ end
236
+
237
+ # @param api_map [ApiMap]
238
+ # @param expected [ComplexType, UniqueType]
239
+ # @param inferred [ComplexType, UniqueType]
240
+ # @return [Boolean]
241
+ def duck_types_match? api_map, expected, inferred
242
+ raise ArgumentError, 'Expected type must be duck type' unless expected.duck_type?
243
+ expected.each do |exp|
244
+ next unless exp.duck_type?
245
+ quack = exp.to_s[1..]
246
+ # @sg-ignore Need to add nil check here
247
+ return false if api_map.get_method_stack(inferred.namespace, quack, scope: inferred.scope).empty?
248
+ end
249
+ true
250
+ end
251
+
197
252
  # @return [String]
198
253
  def rooted_tags
199
254
  map(&:rooted_tag).join(', ')
@@ -252,6 +307,13 @@ module Solargraph
252
307
  @items.any?(&:nil_type?)
253
308
  end
254
309
 
310
+ # @return [ComplexType]
311
+ def without_nil
312
+ new_items = @items.reject(&:nil_type?)
313
+ return ComplexType::UNDEFINED if new_items.empty?
314
+ ComplexType.new(new_items)
315
+ end
316
+
255
317
  # @return [Array<ComplexType>]
256
318
  def all_params
257
319
  @items.first.all_params || []
@@ -274,6 +336,13 @@ module Solargraph
274
336
  all?(&:all_rooted?)
275
337
  end
276
338
 
339
+ # @param other [ComplexType, UniqueType]
340
+ def erased_version_of?(other)
341
+ return false if items.length != 1 || other.items.length != 1
342
+
343
+ @items.first.erased_version_of?(other.items.first)
344
+ end
345
+
277
346
  # every top-level type has resolved to be fully qualified; see
278
347
  # #all_rooted? to check their subtypes as well
279
348
  def rooted?
@@ -286,6 +355,40 @@ module Solargraph
286
355
  @items.all?(&:rooted?)
287
356
  end
288
357
 
358
+ # @param exclude_types [ComplexType, nil]
359
+ # @param api_map [ApiMap]
360
+ # @return [ComplexType, self]
361
+ def exclude exclude_types, api_map
362
+ return self if exclude_types.nil?
363
+
364
+ types = items - exclude_types.items
365
+ types = [ComplexType::UniqueType::UNDEFINED] if types.empty?
366
+ ComplexType.new(types)
367
+ end
368
+
369
+ # @see https://en.wikipedia.org/wiki/Intersection_type
370
+ #
371
+ # @param intersection_type [ComplexType, ComplexType::UniqueType, nil]
372
+ # @param api_map [ApiMap]
373
+ # @return [self, ComplexType::UniqueType]
374
+ def intersect_with intersection_type, api_map
375
+ return self if intersection_type.nil?
376
+ return intersection_type if undefined?
377
+ types = []
378
+ # try to find common types via conformance
379
+ items.each do |ut|
380
+ intersection_type.each do |int_type|
381
+ if int_type.conforms_to?(api_map, ut, :assignment)
382
+ types << int_type
383
+ elsif ut.conforms_to?(api_map, int_type, :assignment)
384
+ types << ut
385
+ end
386
+ end
387
+ end
388
+ types = [ComplexType::UniqueType::UNDEFINED] if types.empty?
389
+ ComplexType.new(types)
390
+ end
391
+
289
392
  protected
290
393
 
291
394
  # @return [ComplexType]
@@ -320,17 +423,18 @@ module Solargraph
320
423
  # # @todo Need ability to use a literal true as a type below
321
424
  # # @param partial [Boolean] True if the string is part of a another type
322
425
  # # @return [Array<UniqueType>]
323
- # @todo To be able to select the right signature above,
426
+ # @sg-ignore To be able to select the right signature above,
324
427
  # Chain::Call needs to know the decl type (:arg, :optarg,
325
428
  # :kwarg, etc) of the arguments given, instead of just having
326
429
  # an array of Chains as the arguments.
327
430
  def parse *strings, partial: false
328
- # @type [Hash{Array<String> => ComplexType}]
431
+ # @type [Hash{Array<String> => ComplexType, Array<ComplexType::UniqueType>}]
329
432
  @cache ||= {}
330
433
  unless partial
331
434
  cached = @cache[strings]
332
435
  return cached unless cached.nil?
333
436
  end
437
+ # @types [Array<ComplexType::UniqueType>]
334
438
  types = []
335
439
  key_types = nil
336
440
  strings.each do |type_string|
@@ -351,6 +455,7 @@ module Solargraph
351
455
  elsif base.end_with?('=')
352
456
  raise ComplexTypeError, "Invalid hash thing" unless key_types.nil?
353
457
  # types.push ComplexType.new([UniqueType.new(base[0..-2].strip)])
458
+ # @sg-ignore Need to add nil check here
354
459
  types.push UniqueType.parse(base[0..-2].strip, subtype_string)
355
460
  # @todo this should either expand key_type's type
356
461
  # automatically or complain about not being
@@ -66,7 +66,7 @@ module Solargraph
66
66
  end.compact
67
67
  end
68
68
 
69
- # @return [Parser::AST::Node]
69
+ # @return [Parser::AST::Node, nil]
70
70
  def body_node
71
71
  node.children[2]
72
72
  end
@@ -81,8 +81,10 @@ module Solargraph
81
81
  node.children[1]
82
82
  end
83
83
 
84
+ # @sg-ignore Need to add nil check here
84
85
  # @return [Array<Parser::AST::Node>]
85
86
  def data_attribute_nodes
87
+ # @sg-ignore Need to add nil check here
86
88
  data_node.children[2..-1]
87
89
  end
88
90
  end
@@ -17,6 +17,7 @@ module Solargraph
17
17
  type: :class,
18
18
  location: loc,
19
19
  closure: region.closure,
20
+ # @sg-ignore flow sensitive typing needs to handle attrs
20
21
  name: data_definition_node.class_name,
21
22
  comments: comments_for(node),
22
23
  visibility: :public,
@@ -39,6 +40,7 @@ module Solargraph
39
40
  # Solargraph::SourceMap::Clip#complete_keyword_parameters does not seem to currently take into account [Pin::Method#signatures] hence we only one for :kwarg
40
41
  pins.push initialize_method_pin
41
42
 
43
+ # @sg-ignore flow sensitive typing needs to handle attrs
42
44
  data_definition_node.attributes.map do |attribute_node, attribute_name|
43
45
  initialize_method_pin.parameters.push(
44
46
  Pin::Parameter.new(
@@ -51,6 +53,7 @@ module Solargraph
51
53
  end
52
54
 
53
55
  # define attribute readers and instance variables
56
+ # @sg-ignore flow sensitive typing needs to handle attrs
54
57
  data_definition_node.attributes.each do |attribute_node, attribute_name|
55
58
  name = attribute_name.to_s
56
59
  method_pin = Pin::Method.new(
@@ -78,7 +81,7 @@ module Solargraph
78
81
 
79
82
  private
80
83
 
81
- # @return [DataDefintionNode, nil]
84
+ # @return [DataDefinition::DataDefintionNode, DataDefinition::DataAssignmentNode, nil]
82
85
  def data_definition_node
83
86
  @data_definition_node ||= if DataDefintionNode.match?(node)
84
87
  DataDefintionNode.new(node)
@@ -22,6 +22,7 @@ module Solargraph
22
22
  # s(:def, :foo,
23
23
  # s(:args),
24
24
  # s(:send, nil, :bar))))
25
+ #
25
26
  # @param node [Parser::AST::Node]
26
27
  def match?(node)
27
28
  return false unless node&.type == :casgn
@@ -92,6 +92,7 @@ module Solargraph
92
92
  node.children[1]
93
93
  end
94
94
 
95
+ # @sg-ignore Need to add nil check here
95
96
  # @return [Array<Parser::AST::Node>]
96
97
  def struct_attribute_nodes
97
98
  struct_node.children[2..-1]
@@ -17,6 +17,7 @@ module Solargraph
17
17
  type: :class,
18
18
  location: loc,
19
19
  closure: region.closure,
20
+ # @sg-ignore flow sensitive typing needs to handle attrs
20
21
  name: struct_definition_node.class_name,
21
22
  docstring: docstring,
22
23
  visibility: :public,
@@ -39,6 +40,7 @@ module Solargraph
39
40
 
40
41
  pins.push initialize_method_pin
41
42
 
43
+ # @sg-ignore flow sensitive typing needs to handle attrs
42
44
  struct_definition_node.attributes.map do |attribute_node, attribute_name|
43
45
  initialize_method_pin.parameters.push(
44
46
  Pin::Parameter.new(
@@ -52,6 +54,7 @@ module Solargraph
52
54
  end
53
55
 
54
56
  # define attribute accessors and instance variables
57
+ # @sg-ignore flow sensitive typing needs to handle attrs
55
58
  struct_definition_node.attributes.each do |attribute_node, attribute_name|
56
59
  [attribute_name, "#{attribute_name}="].each do |name|
57
60
  docs = docstring.tags.find { |t| t.tag_name == 'param' && t.name == attribute_name }
@@ -102,7 +105,7 @@ module Solargraph
102
105
 
103
106
  private
104
107
 
105
- # @return [StructDefintionNode, StructAssignmentNode, nil]
108
+ # @return [StructDefinition::StructDefintionNode, StructDefinition::StructAssignmentNode, nil]
106
109
  def struct_definition_node
107
110
  @struct_definition_node ||= if StructDefintionNode.match?(node)
108
111
  StructDefintionNode.new(node)
@@ -121,6 +124,7 @@ module Solargraph
121
124
  # @return [YARD::Docstring]
122
125
  def parse_comments
123
126
  struct_comments = comments_for(node) || ''
127
+ # @sg-ignore Need to add nil check here
124
128
  struct_definition_node.attributes.each do |attr_node, attr_name|
125
129
  comment = comments_for(attr_node)
126
130
  next if comment.nil?
@@ -10,6 +10,7 @@ module Solargraph
10
10
  return [] unless source.parsed? && source.synchronized?
11
11
  result = []
12
12
  refs = {}
13
+ # @sg-ignore Need to add nil check here
13
14
  map = api_map.source_map(source.filename)
14
15
  map.requires.each { |ref| refs[ref.name] = ref }
15
16
  api_map.missing_docs.each do |r|
@@ -25,6 +25,7 @@ module Solargraph
25
25
  def diagnose source, _api_map
26
26
  @source = source
27
27
  require_rubocop(rubocop_version)
28
+ # @sg-ignore Need to add nil check here
28
29
  options, paths = generate_options(source.filename, source.code)
29
30
  store = RuboCop::ConfigStore.new
30
31
  runner = RuboCop::Runner.new(options, store)