steep-relaxed 1.9.3.3

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 (165) 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/STDGEM_DEPENDENCIES.txt +59 -0
  9. data/Steepfile +68 -0
  10. data/bin/console +14 -0
  11. data/bin/generate-diagnostics-docs.rb +112 -0
  12. data/bin/mem_graph.rb +67 -0
  13. data/bin/mem_prof.rb +102 -0
  14. data/bin/output_rebaseline.rb +34 -0
  15. data/bin/output_test.rb +60 -0
  16. data/bin/rbs +20 -0
  17. data/bin/rbs-inline +19 -0
  18. data/bin/setup +9 -0
  19. data/bin/stackprof_test.rb +19 -0
  20. data/bin/steep +19 -0
  21. data/bin/steep-check.rb +251 -0
  22. data/bin/steep-prof +16 -0
  23. data/doc/narrowing.md +195 -0
  24. data/doc/shape.md +194 -0
  25. data/exe/steep +18 -0
  26. data/guides/README.md +5 -0
  27. data/guides/src/gem-rbs-collection/gem-rbs-collection.md +126 -0
  28. data/guides/src/getting-started/getting-started.md +163 -0
  29. data/guides/src/nil-optional/nil-optional.md +195 -0
  30. data/lib/steep/annotation_parser.rb +199 -0
  31. data/lib/steep/ast/annotation/collection.rb +172 -0
  32. data/lib/steep/ast/annotation.rb +137 -0
  33. data/lib/steep/ast/builtin.rb +104 -0
  34. data/lib/steep/ast/ignore.rb +148 -0
  35. data/lib/steep/ast/node/type_application.rb +88 -0
  36. data/lib/steep/ast/node/type_assertion.rb +81 -0
  37. data/lib/steep/ast/types/any.rb +35 -0
  38. data/lib/steep/ast/types/boolean.rb +45 -0
  39. data/lib/steep/ast/types/bot.rb +35 -0
  40. data/lib/steep/ast/types/class.rb +43 -0
  41. data/lib/steep/ast/types/factory.rb +557 -0
  42. data/lib/steep/ast/types/helper.rb +40 -0
  43. data/lib/steep/ast/types/instance.rb +42 -0
  44. data/lib/steep/ast/types/intersection.rb +93 -0
  45. data/lib/steep/ast/types/literal.rb +59 -0
  46. data/lib/steep/ast/types/logic.rb +84 -0
  47. data/lib/steep/ast/types/name.rb +128 -0
  48. data/lib/steep/ast/types/nil.rb +41 -0
  49. data/lib/steep/ast/types/proc.rb +117 -0
  50. data/lib/steep/ast/types/record.rb +79 -0
  51. data/lib/steep/ast/types/self.rb +43 -0
  52. data/lib/steep/ast/types/shared_instance.rb +11 -0
  53. data/lib/steep/ast/types/top.rb +35 -0
  54. data/lib/steep/ast/types/tuple.rb +60 -0
  55. data/lib/steep/ast/types/union.rb +97 -0
  56. data/lib/steep/ast/types/var.rb +65 -0
  57. data/lib/steep/ast/types/void.rb +35 -0
  58. data/lib/steep/cli.rb +401 -0
  59. data/lib/steep/diagnostic/deprecated/else_on_exhaustive_case.rb +20 -0
  60. data/lib/steep/diagnostic/deprecated/unknown_constant_assigned.rb +28 -0
  61. data/lib/steep/diagnostic/helper.rb +18 -0
  62. data/lib/steep/diagnostic/lsp_formatter.rb +78 -0
  63. data/lib/steep/diagnostic/result_printer2.rb +48 -0
  64. data/lib/steep/diagnostic/ruby.rb +1221 -0
  65. data/lib/steep/diagnostic/signature.rb +570 -0
  66. data/lib/steep/drivers/annotations.rb +52 -0
  67. data/lib/steep/drivers/check.rb +339 -0
  68. data/lib/steep/drivers/checkfile.rb +210 -0
  69. data/lib/steep/drivers/diagnostic_printer.rb +105 -0
  70. data/lib/steep/drivers/init.rb +66 -0
  71. data/lib/steep/drivers/langserver.rb +56 -0
  72. data/lib/steep/drivers/print_project.rb +113 -0
  73. data/lib/steep/drivers/stats.rb +203 -0
  74. data/lib/steep/drivers/utils/driver_helper.rb +143 -0
  75. data/lib/steep/drivers/utils/jobs_option.rb +26 -0
  76. data/lib/steep/drivers/vendor.rb +27 -0
  77. data/lib/steep/drivers/watch.rb +194 -0
  78. data/lib/steep/drivers/worker.rb +58 -0
  79. data/lib/steep/equatable.rb +23 -0
  80. data/lib/steep/expectations.rb +228 -0
  81. data/lib/steep/index/rbs_index.rb +350 -0
  82. data/lib/steep/index/signature_symbol_provider.rb +185 -0
  83. data/lib/steep/index/source_index.rb +167 -0
  84. data/lib/steep/interface/block.rb +103 -0
  85. data/lib/steep/interface/builder.rb +843 -0
  86. data/lib/steep/interface/function.rb +1090 -0
  87. data/lib/steep/interface/method_type.rb +330 -0
  88. data/lib/steep/interface/shape.rb +239 -0
  89. data/lib/steep/interface/substitution.rb +159 -0
  90. data/lib/steep/interface/type_param.rb +115 -0
  91. data/lib/steep/located_value.rb +20 -0
  92. data/lib/steep/method_name.rb +42 -0
  93. data/lib/steep/module_helper.rb +24 -0
  94. data/lib/steep/node_helper.rb +273 -0
  95. data/lib/steep/path_helper.rb +30 -0
  96. data/lib/steep/project/dsl.rb +268 -0
  97. data/lib/steep/project/group.rb +31 -0
  98. data/lib/steep/project/options.rb +63 -0
  99. data/lib/steep/project/pattern.rb +59 -0
  100. data/lib/steep/project/target.rb +92 -0
  101. data/lib/steep/project.rb +78 -0
  102. data/lib/steep/rake_task.rb +132 -0
  103. data/lib/steep/range_extension.rb +29 -0
  104. data/lib/steep/server/base_worker.rb +97 -0
  105. data/lib/steep/server/change_buffer.rb +73 -0
  106. data/lib/steep/server/custom_methods.rb +77 -0
  107. data/lib/steep/server/delay_queue.rb +45 -0
  108. data/lib/steep/server/interaction_worker.rb +492 -0
  109. data/lib/steep/server/lsp_formatter.rb +455 -0
  110. data/lib/steep/server/master.rb +922 -0
  111. data/lib/steep/server/target_group_files.rb +205 -0
  112. data/lib/steep/server/type_check_controller.rb +366 -0
  113. data/lib/steep/server/type_check_worker.rb +303 -0
  114. data/lib/steep/server/work_done_progress.rb +64 -0
  115. data/lib/steep/server/worker_process.rb +176 -0
  116. data/lib/steep/services/completion_provider.rb +802 -0
  117. data/lib/steep/services/content_change.rb +61 -0
  118. data/lib/steep/services/file_loader.rb +74 -0
  119. data/lib/steep/services/goto_service.rb +441 -0
  120. data/lib/steep/services/hover_provider/rbs.rb +88 -0
  121. data/lib/steep/services/hover_provider/ruby.rb +221 -0
  122. data/lib/steep/services/hover_provider/singleton_methods.rb +20 -0
  123. data/lib/steep/services/path_assignment.rb +46 -0
  124. data/lib/steep/services/signature_help_provider.rb +202 -0
  125. data/lib/steep/services/signature_service.rb +428 -0
  126. data/lib/steep/services/stats_calculator.rb +68 -0
  127. data/lib/steep/services/type_check_service.rb +394 -0
  128. data/lib/steep/services/type_name_completion.rb +236 -0
  129. data/lib/steep/signature/validator.rb +651 -0
  130. data/lib/steep/source/ignore_ranges.rb +69 -0
  131. data/lib/steep/source.rb +691 -0
  132. data/lib/steep/subtyping/cache.rb +30 -0
  133. data/lib/steep/subtyping/check.rb +1113 -0
  134. data/lib/steep/subtyping/constraints.rb +341 -0
  135. data/lib/steep/subtyping/relation.rb +101 -0
  136. data/lib/steep/subtyping/result.rb +324 -0
  137. data/lib/steep/subtyping/variable_variance.rb +89 -0
  138. data/lib/steep/test.rb +9 -0
  139. data/lib/steep/thread_waiter.rb +43 -0
  140. data/lib/steep/type_construction.rb +5183 -0
  141. data/lib/steep/type_inference/block_params.rb +416 -0
  142. data/lib/steep/type_inference/case_when.rb +303 -0
  143. data/lib/steep/type_inference/constant_env.rb +56 -0
  144. data/lib/steep/type_inference/context.rb +195 -0
  145. data/lib/steep/type_inference/logic_type_interpreter.rb +613 -0
  146. data/lib/steep/type_inference/method_call.rb +193 -0
  147. data/lib/steep/type_inference/method_params.rb +531 -0
  148. data/lib/steep/type_inference/multiple_assignment.rb +194 -0
  149. data/lib/steep/type_inference/send_args.rb +712 -0
  150. data/lib/steep/type_inference/type_env.rb +341 -0
  151. data/lib/steep/type_inference/type_env_builder.rb +138 -0
  152. data/lib/steep/typing.rb +321 -0
  153. data/lib/steep/version.rb +3 -0
  154. data/lib/steep.rb +369 -0
  155. data/manual/annotations.md +181 -0
  156. data/manual/ignore.md +20 -0
  157. data/manual/ruby-diagnostics.md +1879 -0
  158. data/sample/Steepfile +22 -0
  159. data/sample/lib/conference.rb +49 -0
  160. data/sample/lib/length.rb +35 -0
  161. data/sample/sig/conference.rbs +42 -0
  162. data/sample/sig/generics.rbs +15 -0
  163. data/sample/sig/length.rbs +34 -0
  164. data/steep-relaxed.gemspec +56 -0
  165. metadata +340 -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