steep-activesupport-4 1.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (164) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.gitmodules +0 -0
  4. data/CHANGELOG.md +1032 -0
  5. data/LICENSE +21 -0
  6. data/README.md +260 -0
  7. data/Rakefile +227 -0
  8. data/Steepfile +68 -0
  9. data/bin/console +14 -0
  10. data/bin/generate-diagnostics-docs.rb +112 -0
  11. data/bin/mem_graph.rb +67 -0
  12. data/bin/mem_prof.rb +102 -0
  13. data/bin/output_rebaseline.rb +34 -0
  14. data/bin/output_test.rb +60 -0
  15. data/bin/rbs +20 -0
  16. data/bin/rbs-inline +19 -0
  17. data/bin/setup +9 -0
  18. data/bin/stackprof_test.rb +19 -0
  19. data/bin/steep +19 -0
  20. data/bin/steep-check.rb +251 -0
  21. data/bin/steep-prof +16 -0
  22. data/doc/narrowing.md +195 -0
  23. data/doc/shape.md +194 -0
  24. data/exe/steep +18 -0
  25. data/guides/README.md +5 -0
  26. data/guides/src/gem-rbs-collection/gem-rbs-collection.md +126 -0
  27. data/guides/src/getting-started/getting-started.md +163 -0
  28. data/guides/src/nil-optional/nil-optional.md +195 -0
  29. data/lib/steep/annotation_parser.rb +199 -0
  30. data/lib/steep/ast/annotation/collection.rb +172 -0
  31. data/lib/steep/ast/annotation.rb +137 -0
  32. data/lib/steep/ast/builtin.rb +104 -0
  33. data/lib/steep/ast/ignore.rb +148 -0
  34. data/lib/steep/ast/node/type_application.rb +88 -0
  35. data/lib/steep/ast/node/type_assertion.rb +81 -0
  36. data/lib/steep/ast/types/any.rb +35 -0
  37. data/lib/steep/ast/types/boolean.rb +45 -0
  38. data/lib/steep/ast/types/bot.rb +35 -0
  39. data/lib/steep/ast/types/class.rb +43 -0
  40. data/lib/steep/ast/types/factory.rb +557 -0
  41. data/lib/steep/ast/types/helper.rb +40 -0
  42. data/lib/steep/ast/types/instance.rb +42 -0
  43. data/lib/steep/ast/types/intersection.rb +93 -0
  44. data/lib/steep/ast/types/literal.rb +59 -0
  45. data/lib/steep/ast/types/logic.rb +84 -0
  46. data/lib/steep/ast/types/name.rb +128 -0
  47. data/lib/steep/ast/types/nil.rb +41 -0
  48. data/lib/steep/ast/types/proc.rb +117 -0
  49. data/lib/steep/ast/types/record.rb +79 -0
  50. data/lib/steep/ast/types/self.rb +43 -0
  51. data/lib/steep/ast/types/shared_instance.rb +11 -0
  52. data/lib/steep/ast/types/top.rb +35 -0
  53. data/lib/steep/ast/types/tuple.rb +60 -0
  54. data/lib/steep/ast/types/union.rb +97 -0
  55. data/lib/steep/ast/types/var.rb +65 -0
  56. data/lib/steep/ast/types/void.rb +35 -0
  57. data/lib/steep/cli.rb +401 -0
  58. data/lib/steep/diagnostic/deprecated/else_on_exhaustive_case.rb +20 -0
  59. data/lib/steep/diagnostic/deprecated/unknown_constant_assigned.rb +28 -0
  60. data/lib/steep/diagnostic/helper.rb +18 -0
  61. data/lib/steep/diagnostic/lsp_formatter.rb +78 -0
  62. data/lib/steep/diagnostic/result_printer2.rb +48 -0
  63. data/lib/steep/diagnostic/ruby.rb +1221 -0
  64. data/lib/steep/diagnostic/signature.rb +570 -0
  65. data/lib/steep/drivers/annotations.rb +52 -0
  66. data/lib/steep/drivers/check.rb +339 -0
  67. data/lib/steep/drivers/checkfile.rb +210 -0
  68. data/lib/steep/drivers/diagnostic_printer.rb +105 -0
  69. data/lib/steep/drivers/init.rb +66 -0
  70. data/lib/steep/drivers/langserver.rb +56 -0
  71. data/lib/steep/drivers/print_project.rb +113 -0
  72. data/lib/steep/drivers/stats.rb +203 -0
  73. data/lib/steep/drivers/utils/driver_helper.rb +143 -0
  74. data/lib/steep/drivers/utils/jobs_option.rb +26 -0
  75. data/lib/steep/drivers/vendor.rb +27 -0
  76. data/lib/steep/drivers/watch.rb +194 -0
  77. data/lib/steep/drivers/worker.rb +58 -0
  78. data/lib/steep/equatable.rb +23 -0
  79. data/lib/steep/expectations.rb +228 -0
  80. data/lib/steep/index/rbs_index.rb +350 -0
  81. data/lib/steep/index/signature_symbol_provider.rb +185 -0
  82. data/lib/steep/index/source_index.rb +167 -0
  83. data/lib/steep/interface/block.rb +103 -0
  84. data/lib/steep/interface/builder.rb +843 -0
  85. data/lib/steep/interface/function.rb +1090 -0
  86. data/lib/steep/interface/method_type.rb +330 -0
  87. data/lib/steep/interface/shape.rb +239 -0
  88. data/lib/steep/interface/substitution.rb +159 -0
  89. data/lib/steep/interface/type_param.rb +115 -0
  90. data/lib/steep/located_value.rb +20 -0
  91. data/lib/steep/method_name.rb +42 -0
  92. data/lib/steep/module_helper.rb +24 -0
  93. data/lib/steep/node_helper.rb +273 -0
  94. data/lib/steep/path_helper.rb +30 -0
  95. data/lib/steep/project/dsl.rb +268 -0
  96. data/lib/steep/project/group.rb +31 -0
  97. data/lib/steep/project/options.rb +63 -0
  98. data/lib/steep/project/pattern.rb +59 -0
  99. data/lib/steep/project/target.rb +92 -0
  100. data/lib/steep/project.rb +78 -0
  101. data/lib/steep/rake_task.rb +132 -0
  102. data/lib/steep/range_extension.rb +29 -0
  103. data/lib/steep/server/base_worker.rb +97 -0
  104. data/lib/steep/server/change_buffer.rb +73 -0
  105. data/lib/steep/server/custom_methods.rb +77 -0
  106. data/lib/steep/server/delay_queue.rb +45 -0
  107. data/lib/steep/server/interaction_worker.rb +492 -0
  108. data/lib/steep/server/lsp_formatter.rb +455 -0
  109. data/lib/steep/server/master.rb +912 -0
  110. data/lib/steep/server/target_group_files.rb +205 -0
  111. data/lib/steep/server/type_check_controller.rb +366 -0
  112. data/lib/steep/server/type_check_worker.rb +303 -0
  113. data/lib/steep/server/work_done_progress.rb +64 -0
  114. data/lib/steep/server/worker_process.rb +176 -0
  115. data/lib/steep/services/completion_provider.rb +802 -0
  116. data/lib/steep/services/content_change.rb +61 -0
  117. data/lib/steep/services/file_loader.rb +74 -0
  118. data/lib/steep/services/goto_service.rb +441 -0
  119. data/lib/steep/services/hover_provider/rbs.rb +88 -0
  120. data/lib/steep/services/hover_provider/ruby.rb +221 -0
  121. data/lib/steep/services/hover_provider/singleton_methods.rb +20 -0
  122. data/lib/steep/services/path_assignment.rb +46 -0
  123. data/lib/steep/services/signature_help_provider.rb +202 -0
  124. data/lib/steep/services/signature_service.rb +428 -0
  125. data/lib/steep/services/stats_calculator.rb +68 -0
  126. data/lib/steep/services/type_check_service.rb +394 -0
  127. data/lib/steep/services/type_name_completion.rb +236 -0
  128. data/lib/steep/signature/validator.rb +651 -0
  129. data/lib/steep/source/ignore_ranges.rb +69 -0
  130. data/lib/steep/source.rb +691 -0
  131. data/lib/steep/subtyping/cache.rb +30 -0
  132. data/lib/steep/subtyping/check.rb +1113 -0
  133. data/lib/steep/subtyping/constraints.rb +341 -0
  134. data/lib/steep/subtyping/relation.rb +101 -0
  135. data/lib/steep/subtyping/result.rb +324 -0
  136. data/lib/steep/subtyping/variable_variance.rb +89 -0
  137. data/lib/steep/test.rb +9 -0
  138. data/lib/steep/thread_waiter.rb +43 -0
  139. data/lib/steep/type_construction.rb +5183 -0
  140. data/lib/steep/type_inference/block_params.rb +416 -0
  141. data/lib/steep/type_inference/case_when.rb +303 -0
  142. data/lib/steep/type_inference/constant_env.rb +56 -0
  143. data/lib/steep/type_inference/context.rb +195 -0
  144. data/lib/steep/type_inference/logic_type_interpreter.rb +613 -0
  145. data/lib/steep/type_inference/method_call.rb +193 -0
  146. data/lib/steep/type_inference/method_params.rb +531 -0
  147. data/lib/steep/type_inference/multiple_assignment.rb +194 -0
  148. data/lib/steep/type_inference/send_args.rb +712 -0
  149. data/lib/steep/type_inference/type_env.rb +341 -0
  150. data/lib/steep/type_inference/type_env_builder.rb +138 -0
  151. data/lib/steep/typing.rb +321 -0
  152. data/lib/steep/version.rb +3 -0
  153. data/lib/steep.rb +369 -0
  154. data/manual/annotations.md +181 -0
  155. data/manual/ignore.md +20 -0
  156. data/manual/ruby-diagnostics.md +1879 -0
  157. data/sample/Steepfile +22 -0
  158. data/sample/lib/conference.rb +49 -0
  159. data/sample/lib/length.rb +35 -0
  160. data/sample/sig/conference.rbs +42 -0
  161. data/sample/sig/generics.rbs +15 -0
  162. data/sample/sig/length.rbs +34 -0
  163. data/steep-activesupport-4.gemspec +55 -0
  164. metadata +437 -0
@@ -0,0 +1,416 @@
1
+ module Steep
2
+ module TypeInference
3
+ class BlockParams
4
+ class Param
5
+ attr_reader :var
6
+ attr_reader :type
7
+ attr_reader :value
8
+ attr_reader :node
9
+
10
+ def initialize(var:, type:, value:, node:)
11
+ @var = var
12
+ @type = type
13
+ @value = value
14
+ @node = node
15
+ end
16
+
17
+ def ==(other)
18
+ other.is_a?(self.class) && other.var == var && other.type == type && other.value == value && other.node == node
19
+ end
20
+
21
+ alias eql? ==
22
+
23
+ def hash
24
+ self.class.hash ^ var.hash ^ type.hash ^ value.hash ^ node.hash
25
+ end
26
+
27
+ def each_param(&block)
28
+ if block
29
+ yield self
30
+ else
31
+ enum_for :each_param
32
+ end
33
+ end
34
+ end
35
+
36
+ class MultipleParam
37
+ attr_reader :node
38
+
39
+ attr_reader :params
40
+
41
+ def initialize(node:, params:)
42
+ @params = params
43
+ @node = node
44
+ end
45
+
46
+ def ==(other)
47
+ other.is_a?(self.class) &&
48
+ other.node == node &&
49
+ other.params == params
50
+ end
51
+
52
+ alias eql? ==
53
+
54
+ def hash
55
+ self.class.hash ^ node.hash ^ params.hash
56
+ end
57
+
58
+ def variable_types
59
+ each_param.with_object({}) do |param, hash| #$ Hash[Symbol, AST::Types::t?]
60
+ var_name = param.var || next
61
+ hash[var_name] = param.type
62
+ end
63
+ end
64
+
65
+ def each_param(&block)
66
+ if block
67
+ params.each do |param|
68
+ case param
69
+ when Param
70
+ yield param
71
+ when MultipleParam
72
+ param.each_param(&block)
73
+ end
74
+ end
75
+ else
76
+ enum_for :each_param
77
+ end
78
+ end
79
+
80
+ def type
81
+ types = params.map do |param|
82
+ param.type or return
83
+ end
84
+
85
+ AST::Types::Tuple.new(types: types)
86
+ end
87
+ end
88
+
89
+ attr_reader :leading_params
90
+ attr_reader :optional_params
91
+ attr_reader :rest_param
92
+ attr_reader :trailing_params
93
+ attr_reader :block_param
94
+
95
+ def initialize(leading_params:, optional_params:, rest_param:, trailing_params:, block_param:)
96
+ @leading_params = leading_params
97
+ @optional_params = optional_params
98
+ @rest_param = rest_param
99
+ @trailing_params = trailing_params
100
+ @block_param = block_param
101
+ end
102
+
103
+ def params
104
+ [].tap do |params|
105
+ params.push(*leading_params)
106
+ params.push(*optional_params)
107
+ params.push rest_param if rest_param
108
+ params.push(*trailing_params)
109
+ params.push(block_param) if block_param
110
+ end
111
+ end
112
+
113
+ def self.from_node(node, annotations:)
114
+ # @type var leading_params: Array[Param | MultipleParam]
115
+ leading_params = []
116
+ # @type var optional_params: Array[Param]
117
+ optional_params = []
118
+ # @type var rest_param: Param?
119
+ rest_param = nil
120
+ # @type var trailing_params: Array[Param | MultipleParam]
121
+ trailing_params = []
122
+ # @type var block_param: Param?
123
+ block_param = nil
124
+
125
+ default_params = leading_params
126
+
127
+ node.children.each do |arg|
128
+ case
129
+ when arg.type == :mlhs
130
+ default_params << from_multiple(arg, annotations)
131
+ when arg.type == :procarg0 && arg.children.size > 1
132
+ default_params << from_multiple(arg, annotations)
133
+ else
134
+ var = arg.children[0]
135
+ type = annotations.var_type(lvar: var)
136
+ case arg.type
137
+ when :arg
138
+ default_params << Param.new(var: var, type: type, value: nil, node: arg)
139
+ when :procarg0
140
+ var = arg.children[0]
141
+ if var.is_a?(Symbol)
142
+ default_params << Param.new(var: var, type: type, value: nil, node: arg)
143
+ else
144
+ var = var.children[0]
145
+ default_params << Param.new(var: var, type: type, value: nil, node: arg)
146
+ end
147
+ when :optarg
148
+ default_params = trailing_params
149
+ optional_params << Param.new(var: var, type: type, value: arg.children.last, node: arg)
150
+ when :restarg
151
+ default_params = trailing_params
152
+ rest_param = Param.new(var: var, type: type, value: nil, node: arg)
153
+ when :blockarg
154
+ block_param = Param.new(var: var, type: type, value: nil, node: arg)
155
+ break
156
+ end
157
+ end
158
+ end
159
+
160
+ new(
161
+ leading_params: leading_params,
162
+ optional_params: optional_params,
163
+ rest_param: rest_param,
164
+ trailing_params: trailing_params,
165
+ block_param: block_param
166
+ )
167
+ end
168
+
169
+ def params_type(hint: nil)
170
+ params_type0(hint: hint) or params_type0(hint: nil)
171
+ end
172
+
173
+ def params_type0(hint:)
174
+ # @type var leadings: Array[AST::Types::t]
175
+ # @type var optionals: Array[AST::Types::t]
176
+
177
+ if hint
178
+ case
179
+ when leading_params.size == hint.required.size
180
+ leadings = leading_params.map.with_index do |param, index|
181
+ param.type || hint.required[index]
182
+ end
183
+ when !hint.rest && hint.optional.empty? && leading_params.size > hint.required.size
184
+ leadings = leading_params.take(hint.required.size).map.with_index do |param, index|
185
+ param.type || hint.required[index]
186
+ end
187
+ when !hint.rest && hint.optional.empty? && leading_params.size < hint.required.size
188
+ leadings = leading_params.map.with_index do |param, index|
189
+ param.type || hint.required[index]
190
+ end + hint.required.drop(leading_params.size)
191
+ else
192
+ return nil
193
+ end
194
+
195
+ case
196
+ when optional_params.size == hint.optional.size
197
+ optionals = optional_params.map.with_index do |param, index|
198
+ param.type || hint.optional[index]
199
+ end
200
+ when !hint.rest && optional_params.size > hint.optional.size
201
+ optionals = optional_params.take(hint.optional.size).map.with_index do |param, index|
202
+ param.type || hint.optional[index]
203
+ end
204
+ when !hint.rest && optional_params.size < hint.optional.size
205
+ optionals = optional_params.map.with_index do |param, index|
206
+ param.type || hint.optional[index]
207
+ end + hint.optional.drop(optional_params.size)
208
+ else
209
+ return nil
210
+ end
211
+
212
+ if rest_param
213
+ if hint.rest
214
+ if rest_type = rest_param.type
215
+ if AST::Builtin::Array.instance_type?(rest_type)
216
+ rest_type.is_a?(AST::Types::Name::Instance) or raise
217
+ rest = rest_type.args.first or raise
218
+ end
219
+ end
220
+
221
+ rest ||= hint.rest
222
+ end
223
+ end
224
+ else
225
+ leadings = leading_params.map {|param| param.type || AST::Types::Any.instance }
226
+ optionals = optional_params.map {|param| param.type || AST::Types::Any.instance }
227
+
228
+ if rest_param
229
+ if rest_type = rest_param.type
230
+ if array = AST::Builtin::Array.instance_type?(rest_type)
231
+ rest = array.args.first or raise
232
+ end
233
+ end
234
+ rest ||= AST::Types::Any.instance
235
+ end
236
+ end
237
+
238
+ Interface::Function::Params.build(
239
+ required: leadings,
240
+ optional: optionals,
241
+ rest: rest
242
+ )
243
+ end
244
+
245
+ def zip(params_type, block, factory:)
246
+ if trailing_params.any?
247
+ Steep.logger.error "Block definition with trailing required parameters are not supported yet"
248
+ end
249
+
250
+ # @type var zip: Array[[Param | MultipleParam, AST::Types::t]]
251
+ zip = []
252
+
253
+ if params_type.nil? || untyped_args?(params_type)
254
+ params.each do |param|
255
+ if param == rest_param
256
+ zip << [param, AST::Builtin::Array.instance_type(fill_untyped: true)]
257
+ else
258
+ zip << [param, AST::Builtin.any_type]
259
+ end
260
+ end
261
+ return zip
262
+ end
263
+
264
+ if expandable? && (type = expandable_params?(params_type, factory))
265
+ case
266
+ when AST::Builtin::Array.instance_type?(type)
267
+ type.is_a?(AST::Types::Name::Instance) or raise
268
+
269
+ type_arg = type.args.fetch(0)
270
+ params.each do |param|
271
+ unless param == rest_param
272
+ zip << [param, AST::Types::Union.build(types: [type_arg, AST::Builtin.nil_type])]
273
+ else
274
+ zip << [param, AST::Builtin::Array.instance_type(type_arg)]
275
+ end
276
+ end
277
+ when type.is_a?(AST::Types::Tuple)
278
+ types = type.types.dup
279
+ (leading_params + optional_params).each do |param|
280
+ ty = types.shift
281
+ if ty
282
+ zip << [param, ty]
283
+ else
284
+ zip << [param, AST::Types::Nil.instance]
285
+ end
286
+ end
287
+
288
+ if rest_param
289
+ if types.any?
290
+ union = AST::Types::Union.build(types: types)
291
+ zip << [rest_param, AST::Builtin::Array.instance_type(union)]
292
+ else
293
+ zip << [rest_param, AST::Types::Nil.instance]
294
+ end
295
+ end
296
+ end
297
+ else
298
+ types = params_type.flat_unnamed_params
299
+
300
+ (leading_params + optional_params).each do |param|
301
+ typ = types.shift&.last || params_type.rest
302
+
303
+ if typ
304
+ zip << [param, typ]
305
+ else
306
+ zip << [param, AST::Builtin.nil_type]
307
+ end
308
+ end
309
+
310
+ if rest_param
311
+ if types.empty?
312
+ array = AST::Builtin::Array.instance_type(params_type.rest || AST::Builtin.any_type)
313
+ zip << [rest_param, array]
314
+ else
315
+ union_members = types.map(&:last)
316
+ union_members << params_type.rest if params_type.rest
317
+ union = AST::Types::Union.build(types: union_members)
318
+ array = AST::Builtin::Array.instance_type(union)
319
+ zip << [rest_param, array]
320
+ end
321
+ end
322
+ end
323
+
324
+ if block_param
325
+ if block
326
+ proc_type = AST::Types::Proc.new(type: block.type, block: nil, self_type: block.self_type)
327
+ if block.optional?
328
+ proc_type = AST::Types::Union.build(types: [proc_type, AST::Builtin.nil_type])
329
+ end
330
+
331
+ zip << [block_param, proc_type]
332
+ else
333
+ zip << [block_param, AST::Builtin.nil_type]
334
+ end
335
+ end
336
+
337
+ zip
338
+ end
339
+
340
+ def expandable_params?(params_type, factory)
341
+ if params_type.flat_unnamed_params.size == 1
342
+ type = params_type.required.first or raise
343
+ type = factory.deep_expand_alias(type) || type
344
+
345
+ case type
346
+ when AST::Types::Tuple
347
+ type
348
+ when AST::Types::Name::Base
349
+ if AST::Builtin::Array.instance_type?(type)
350
+ type
351
+ end
352
+ end
353
+ end
354
+ end
355
+
356
+ def expandable?
357
+ case
358
+ when leading_params.size + trailing_params.size > 1
359
+ true
360
+ when (leading_params.any? || trailing_params.any?) && rest_param
361
+ true
362
+ when params.size == 1 && params.fetch(0).node.type == :arg
363
+ true
364
+ else
365
+ false
366
+ end
367
+ end
368
+
369
+ def each(&block)
370
+ if block
371
+ params.each(&block)
372
+ else
373
+ enum_for :each
374
+ end
375
+ end
376
+
377
+ def each_single_param()
378
+ each do |param|
379
+ case param
380
+ when Param
381
+ yield param
382
+ when MultipleParam
383
+ param.each_param do |p|
384
+ yield p
385
+ end
386
+ end
387
+ end
388
+ end
389
+
390
+ def self.from_multiple(node, annotations)
391
+ # @type var params: Array[Param | MultipleParam]
392
+ params = []
393
+
394
+ node.children.each do |child|
395
+ if child.type == :mlhs
396
+ params << from_multiple(child, annotations)
397
+ else
398
+ var = child.children.first
399
+
400
+ raise unless var.is_a?(Symbol)
401
+ type = annotations.var_type(lvar: var)
402
+
403
+ params << Param.new(var: var, node: child, value: nil, type: type)
404
+ end
405
+ end
406
+
407
+ MultipleParam.new(node: node, params: params)
408
+ end
409
+
410
+ def untyped_args?(params)
411
+ flat = params.flat_unnamed_params
412
+ flat.size == 1 && flat.dig(0, 1)&.is_a?(AST::Types::Any)
413
+ end
414
+ end
415
+ end
416
+ end
@@ -0,0 +1,303 @@
1
+ module Steep
2
+ module TypeInference
3
+ class CaseWhen
4
+ class WhenPatterns
5
+ include NodeHelper
6
+
7
+ attr_reader :logic, :initial_constr, :unreachable_clause, :pattern_results
8
+
9
+ def initialize(logic, initial_constr, unreachable_clause, assignment_node)
10
+ @logic = logic
11
+ @initial_constr = initial_constr
12
+ @unreachable_clause = unreachable_clause
13
+ @assignment_node = assignment_node
14
+
15
+ @pattern_results = []
16
+ end
17
+
18
+ def add_pattern(pat)
19
+ test_node = pat.updated(:send, [pat, :===, assignment_node])
20
+
21
+ latest_constr, unreachable_pattern = latest_result
22
+
23
+ type, constr = yield(test_node, latest_constr, unreachable_pattern)
24
+ truthy_result, falsy_result = logic.eval(env: latest_constr.context.type_env, node: test_node)
25
+
26
+ pattern_results << [pat, truthy_result, falsy_result]
27
+ end
28
+
29
+ def latest_result
30
+ if (_, truthy, falsy = pattern_results.last)
31
+ [
32
+ initial_constr.update_type_env { falsy.env },
33
+ falsy.unreachable
34
+ ]
35
+ else
36
+ [initial_constr, unreachable_clause]
37
+ end
38
+ end
39
+
40
+ def body_result
41
+ raise if pattern_results.empty?
42
+
43
+ type_envs = pattern_results.map {|_, truthy, _| truthy.env }
44
+ env = initial_constr.context.type_env.join(*type_envs)
45
+
46
+ env = yield(env) || env
47
+
48
+ [
49
+ initial_constr.update_type_env { env },
50
+ unreachable_clause || pattern_results.all? {|_, truthy, _| truthy.unreachable }
51
+ ]
52
+ end
53
+
54
+ def falsy_result
55
+ (_, _, falsy = pattern_results.last) or raise
56
+
57
+ [
58
+ initial_constr.update_type_env { falsy.env },
59
+ unreachable_clause || falsy.unreachable
60
+ ]
61
+ end
62
+
63
+ def assignment_node()
64
+ clone_node(@assignment_node)
65
+ end
66
+ end
67
+
68
+ include NodeHelper
69
+ extend NodeHelper
70
+
71
+ def self.type_check(constr, node, logic, hint:, condition:)
72
+ case_when = new(node, logic) do |condition_node|
73
+ constr.synthesize(condition_node)
74
+ end
75
+
76
+ case_when.when_clauses() do |when_pats, patterns, body_node, loc|
77
+ patterns.each do |pat|
78
+ when_pats.add_pattern(pat) {|test, constr| constr.synthesize(test) }
79
+ end
80
+
81
+ body_constr, body_unreachable = when_pats.body_result() do |env|
82
+ case_when.propagate_value_node_type(env)
83
+ end
84
+
85
+ if body_node
86
+ body_constr = body_constr.for_branch(body_node)
87
+ type, body_constr = body_constr.synthesize(body_node, hint: hint, condition: condition)
88
+ else
89
+ type = AST::Builtin.nil_type
90
+ end
91
+
92
+ body_result = LogicTypeInterpreter::Result.new(
93
+ type: type,
94
+ env: body_constr.context.type_env,
95
+ unreachable: body_unreachable
96
+ )
97
+
98
+ falsy_constr, falsy_unreachable = when_pats.falsy_result
99
+ next_result = LogicTypeInterpreter::Result.new(
100
+ type: AST::Builtin.any_type, # Unused for falsy pattern
101
+ env: falsy_constr.context.type_env,
102
+ unreachable: falsy_unreachable
103
+ )
104
+
105
+ [body_result, next_result]
106
+ end
107
+
108
+ case_when.else_clause do |else_node, constr|
109
+ constr.synthesize(else_node, hint: hint, condition: condition)
110
+ end
111
+
112
+ case_when.result()
113
+ end
114
+
115
+ attr_reader :location, :node, :condition_node, :when_nodes, :else_node
116
+ attr_reader :initial_constr, :logic, :clause_results, :else_result
117
+ attr_reader :assignment_node, :value_node, :var_name
118
+
119
+ def initialize(node, logic)
120
+ @node = node
121
+
122
+ condition_node, when_nodes, else_node, location = deconstruct_case_node!(node)
123
+ condition_node or raise "CaseWhen works for case-when syntax with condition node"
124
+
125
+ @condition_node = condition_node
126
+ @when_nodes = when_nodes
127
+ @else_node = else_node
128
+ @location = location
129
+ @logic = logic
130
+ @clause_results = []
131
+
132
+ type, constr = yield(condition_node)
133
+
134
+ @var_name = "__case_when:#{SecureRandom.alphanumeric(5)}__".to_sym
135
+ @value_node, @assignment_node = rewrite_condition_node(var_name, condition_node)
136
+
137
+ @initial_constr = constr.update_type_env do |env|
138
+ env.merge(local_variable_types: { var_name => [type, nil] })
139
+ end
140
+ end
141
+
142
+ def when_clauses()
143
+ when_nodes.each do |when_node|
144
+ clause_constr, unreachable = latest_result
145
+
146
+ patterns, body, loc = deconstruct_when_node!(when_node)
147
+
148
+ when_pats = WhenPatterns.new(
149
+ logic,
150
+ clause_constr,
151
+ unreachable,
152
+ assignment_node
153
+ )
154
+
155
+ body_result, next_result = yield(
156
+ when_pats,
157
+ patterns,
158
+ body,
159
+ loc
160
+ )
161
+
162
+ if body_result.unreachable
163
+ if body_result.type.is_a?(AST::Types::Any) || initial_constr.no_subtyping?(sub_type: body_result.type, super_type: AST::Builtin.bottom_type)
164
+ typing.add_error(
165
+ Diagnostic::Ruby::UnreachableValueBranch.new(
166
+ node: when_node,
167
+ type: body_result.type,
168
+ location: loc.keyword
169
+ )
170
+ )
171
+ end
172
+ end
173
+
174
+ clause_results << [body_result, next_result]
175
+ end
176
+ end
177
+
178
+ def else_clause()
179
+ unless else_loc = has_else_clause?
180
+ return
181
+ end
182
+
183
+ constr, unreachable = latest_result
184
+
185
+ constr = constr.update_type_env do |env|
186
+ propagate_value_node_type(env) || env
187
+ end
188
+
189
+ @else_result =
190
+ if else_node
191
+ yield(else_node, constr)
192
+ else
193
+ TypeConstruction::Pair.new(type: AST::Builtin.nil_type, constr: constr)
194
+ end
195
+
196
+ else_result or raise
197
+
198
+ if unreachable
199
+ if else_result.type.is_a?(AST::Types::Any) || initial_constr.no_subtyping?(sub_type: else_result.type, super_type: AST::Builtin.bottom_type)
200
+ typing.add_error(
201
+ Diagnostic::Ruby::UnreachableValueBranch.new(
202
+ node: else_node || node,
203
+ type: else_result.type,
204
+ location: else_loc
205
+ )
206
+ )
207
+ end
208
+ end
209
+ end
210
+
211
+ def latest_result
212
+ if (_, falsy_result = clause_results.last)
213
+ [
214
+ initial_constr.update_type_env { falsy_result.env },
215
+ falsy_result.unreachable
216
+ ]
217
+ else
218
+ [initial_constr, false]
219
+ end
220
+ end
221
+
222
+ def result
223
+ results = clause_results.filter_map do |body, _|
224
+ unless body.unreachable
225
+ body
226
+ end
227
+ end
228
+ next_constr, next_clause_unreachable = latest_result
229
+
230
+ unless next_clause_unreachable
231
+ if else_result
232
+ results << LogicTypeInterpreter::Result.new(
233
+ type: else_result.type,
234
+ env: else_result.context.type_env,
235
+ unreachable: false # Unused
236
+ )
237
+ else
238
+ results << LogicTypeInterpreter::Result.new(
239
+ type: AST::Builtin.nil_type,
240
+ env: next_constr.context.type_env,
241
+ unreachable: false
242
+ )
243
+ end
244
+ end
245
+
246
+ results.reject! { _1.type.is_a?(AST::Types::Bot) }
247
+
248
+ types = results.map {|result| result.type }
249
+ envs = results.map {|result| result.env }
250
+
251
+ [
252
+ types,
253
+ envs
254
+ ]
255
+ end
256
+
257
+ def has_else_clause?
258
+ location.else
259
+ end
260
+
261
+ def typing
262
+ logic.typing
263
+ end
264
+
265
+ def rewrite_condition_node(var_name, node)
266
+ case node.type
267
+ when :lvasgn
268
+ name, rhs = node.children
269
+ value, rhs = rewrite_condition_node(var_name, rhs)
270
+ [value, node.updated(nil, [name, rhs])]
271
+ when :lvar
272
+ name, = node.children
273
+ [
274
+ nil,
275
+ node.updated(:lvasgn, [name, node.updated(:lvar, [var_name])])
276
+ ]
277
+ when :begin
278
+ *children, last = node.children
279
+ value_node, last = rewrite_condition_node(var_name, last)
280
+ [
281
+ value_node,
282
+ node.updated(nil, children.push(last))
283
+ ]
284
+ else
285
+ [
286
+ node,
287
+ node.updated(:lvar, [var_name])
288
+ ]
289
+ end
290
+ end
291
+
292
+ def propagate_value_node_type(env)
293
+ if value_node
294
+ if (call = initial_constr.typing.method_calls[value_node]).is_a?(MethodCall::Typed)
295
+ if env[value_node]
296
+ env.merge(pure_method_calls: { value_node => [call, env[var_name]] })
297
+ end
298
+ end
299
+ end
300
+ end
301
+ end
302
+ end
303
+ end