steep 1.4.0 → 1.5.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.vscode/steep-shared.code-snippets +41 -0
  4. data/CHANGELOG.md +37 -0
  5. data/Gemfile +2 -5
  6. data/Gemfile.lock +20 -17
  7. data/Gemfile.steep +1 -1
  8. data/Gemfile.steep.lock +6 -6
  9. data/Rakefile +198 -0
  10. data/Steepfile +3 -1
  11. data/lib/steep/ast/builtin.rb +9 -7
  12. data/lib/steep/ast/node/type_application.rb +13 -5
  13. data/lib/steep/ast/node/type_assertion.rb +28 -9
  14. data/lib/steep/ast/types/factory.rb +39 -7
  15. data/lib/steep/cli.rb +2 -1
  16. data/lib/steep/diagnostic/deprecated/else_on_exhaustive_case.rb +20 -0
  17. data/lib/steep/diagnostic/lsp_formatter.rb +3 -3
  18. data/lib/steep/diagnostic/ruby.rb +73 -12
  19. data/lib/steep/drivers/annotations.rb +1 -0
  20. data/lib/steep/drivers/check.rb +18 -13
  21. data/lib/steep/drivers/checkfile.rb +1 -1
  22. data/lib/steep/drivers/diagnostic_printer.rb +6 -4
  23. data/lib/steep/drivers/init.rb +2 -1
  24. data/lib/steep/drivers/print_project.rb +3 -1
  25. data/lib/steep/drivers/stats.rb +1 -1
  26. data/lib/steep/drivers/utils/driver_helper.rb +10 -8
  27. data/lib/steep/drivers/utils/jobs_option.rb +6 -1
  28. data/lib/steep/drivers/validate.rb +9 -5
  29. data/lib/steep/drivers/watch.rb +8 -3
  30. data/lib/steep/expectations.rb +144 -75
  31. data/lib/steep/index/signature_symbol_provider.rb +22 -13
  32. data/lib/steep/node_helper.rb +172 -0
  33. data/lib/steep/server/base_worker.rb +2 -1
  34. data/lib/steep/server/change_buffer.rb +17 -15
  35. data/lib/steep/server/interaction_worker.rb +20 -0
  36. data/lib/steep/server/lsp_formatter.rb +20 -1
  37. data/lib/steep/server/master.rb +51 -36
  38. data/lib/steep/server/type_check_worker.rb +18 -2
  39. data/lib/steep/server/worker_process.rb +19 -2
  40. data/lib/steep/services/completion_provider.rb +189 -3
  41. data/lib/steep/services/file_loader.rb +1 -1
  42. data/lib/steep/services/goto_service.rb +123 -27
  43. data/lib/steep/services/signature_help_provider.rb +1 -6
  44. data/lib/steep/signature/validator.rb +6 -1
  45. data/lib/steep/source.rb +165 -108
  46. data/lib/steep/subtyping/check.rb +5 -3
  47. data/lib/steep/subtyping/variable_variance.rb +11 -0
  48. data/lib/steep/thread_waiter.rb +35 -0
  49. data/lib/steep/type_construction.rb +416 -171
  50. data/lib/steep/type_inference/block_params.rb +50 -9
  51. data/lib/steep/type_inference/context.rb +4 -0
  52. data/lib/steep/type_inference/context_array.rb +6 -6
  53. data/lib/steep/type_inference/logic_type_interpreter.rb +202 -68
  54. data/lib/steep/typing.rb +5 -4
  55. data/lib/steep/version.rb +1 -1
  56. data/lib/steep.rb +21 -14
  57. data/sample/Steepfile +1 -0
  58. data/sig/shims/bundler.rbs +3 -0
  59. data/sig/shims/language-server_protocol.rbs +151 -10
  60. data/sig/shims/parser/nodes.rbs +210 -0
  61. data/sig/shims/parser.rbs +10 -0
  62. data/sig/steep/ast/builtin.rbs +2 -2
  63. data/sig/steep/ast/node/type_application.rbs +2 -2
  64. data/sig/steep/ast/node/type_assertion.rbs +8 -2
  65. data/sig/steep/ast/types/factory.rbs +28 -1
  66. data/sig/steep/diagnostic/deprecated/else_on_exhaustive_case.rbs +13 -0
  67. data/sig/steep/diagnostic/lsp_formatter.rbs +5 -2
  68. data/sig/steep/diagnostic/ruby.rbs +76 -6
  69. data/sig/steep/drivers/annotations.rbs +5 -5
  70. data/sig/steep/drivers/check.rbs +11 -11
  71. data/sig/steep/drivers/diagnostic_printer.rbs +9 -9
  72. data/sig/steep/drivers/init.rbs +6 -6
  73. data/sig/steep/drivers/print_project.rbs +4 -4
  74. data/sig/steep/drivers/utils/driver_helper.rbs +8 -6
  75. data/sig/steep/drivers/validate.rbs +4 -4
  76. data/sig/steep/drivers/watch.rbs +1 -1
  77. data/sig/steep/expectations.rbs +72 -0
  78. data/sig/steep/index/signature_symbol_provider.rbs +22 -10
  79. data/sig/steep/interface/block.rbs +2 -0
  80. data/sig/steep/interface/function.rbs +2 -2
  81. data/sig/steep/node_helper.rbs +56 -0
  82. data/sig/steep/path_helper.rbs +1 -1
  83. data/sig/steep/project/options.rbs +1 -1
  84. data/sig/steep/range_extension.rbs +2 -2
  85. data/sig/steep/server/master.rbs +16 -2
  86. data/sig/steep/server/type_check_worker.rbs +5 -1
  87. data/sig/steep/server/worker_process.rbs +5 -1
  88. data/sig/steep/services/completion_provider.rbs +31 -1
  89. data/sig/steep/services/goto_service.rbs +80 -19
  90. data/sig/steep/source.rbs +27 -4
  91. data/sig/steep/subtyping/variable_variance.rbs +9 -9
  92. data/sig/steep/thread_waiter.rbs +13 -0
  93. data/sig/steep/type_construction.rbs +26 -9
  94. data/sig/steep/type_inference/block_params.rbs +13 -1
  95. data/sig/steep/type_inference/context.rbs +5 -1
  96. data/sig/steep/type_inference/context_array.rbs +16 -15
  97. data/sig/steep/type_inference/logic_type_interpreter.rbs +36 -6
  98. data/sig/steep/type_inference/type_env_builder.rbs +4 -0
  99. data/sig/steep/typing.rbs +22 -20
  100. data/sig/steep.rbs +14 -13
  101. data/smoke/and/a.rb +1 -1
  102. data/smoke/and/test_expectations.yml +5 -7
  103. data/smoke/diagnostics/incompatible_annotation.rb +1 -1
  104. data/smoke/diagnostics/test_expectations.yml +2 -2
  105. data/smoke/enumerator/a.rb +0 -7
  106. data/smoke/enumerator/b.rb +0 -2
  107. data/smoke/enumerator/test_expectations.yml +17 -105
  108. data/smoke/lambda/a.rb +0 -5
  109. data/smoke/lambda/test_expectations.yml +0 -22
  110. data/smoke/type_case/test_expectations.yml +10 -0
  111. data/steep.gemspec +2 -2
  112. metadata +16 -9
data/lib/steep/source.rb CHANGED
@@ -1,15 +1,19 @@
1
1
  module Steep
2
2
  class Source
3
+ attr_reader :buffer
3
4
  attr_reader :path
4
5
  attr_reader :node
5
6
  attr_reader :mapping
7
+ attr_reader :comments
6
8
 
7
9
  extend NodeHelper
8
10
 
9
- def initialize(path:, node:, mapping:)
11
+ def initialize(buffer:, path:, node:, mapping:, comments:)
12
+ @buffer = buffer
10
13
  @path = path
11
14
  @node = node
12
15
  @mapping = mapping
16
+ @comments = comments
13
17
  end
14
18
 
15
19
  class Builder < ::Parser::Builders::Default
@@ -24,7 +28,7 @@ module Steep
24
28
  end
25
29
 
26
30
  def self.new_parser
27
- ::Parser::Ruby31.new(Builder.new).tap do |parser|
31
+ ::Parser::Ruby32.new(Builder.new).tap do |parser|
28
32
  parser.diagnostics.all_errors_are_fatal = true
29
33
  parser.diagnostics.ignore_warnings = true
30
34
  end
@@ -42,6 +46,10 @@ module Steep
42
46
  buffer = RBS::Buffer.new(name: path, content: source_code)
43
47
  annotation_parser = AnnotationParser.new(factory: factory)
44
48
 
49
+ comments = comments.sort_by do |comment|
50
+ comment.loc.expression.begin_pos
51
+ end
52
+
45
53
  comments.each do |comment|
46
54
  if comment.inline?
47
55
  content = comment.text.delete_prefix('#')
@@ -67,7 +75,7 @@ module Steep
67
75
  end
68
76
  end
69
77
 
70
- map = {}
78
+ map = {} #: Hash[Parser::AST::Node, Array[AST::Annotation::t]]
71
79
  map.compare_by_identity
72
80
 
73
81
  if node
@@ -80,50 +88,62 @@ module Steep
80
88
  map[node] << annot
81
89
  end
82
90
 
83
- new(path: path, node: node, mapping: map)
91
+ new(buffer: buffer, path: path, node: node, mapping: map, comments: comments)
84
92
  end
85
93
 
86
94
  def self.construct_mapping(node:, annotations:, mapping:, line_range: nil)
87
95
  case node.type
88
96
  when :if
97
+ cond_node, truthy_node, falsy_node, loc = deconstruct_if_node!(node)
98
+
89
99
  if node.loc.respond_to?(:question)
90
100
  # Skip ternary operator
101
+
91
102
  each_child_node node do |child|
92
103
  construct_mapping(node: child, annotations: annotations, mapping: mapping, line_range: nil)
93
104
  end
94
105
  else
95
- if node.loc.expression.begin_pos == node.loc.keyword.begin_pos
96
- construct_mapping(node: node.children[0],
97
- annotations: annotations,
98
- mapping: mapping,
99
- line_range: nil)
100
-
101
- if node.children[1]
102
- if node.loc.keyword.source == "if" || node.loc.keyword.source == "elsif"
103
- then_start = node.loc.begin&.last_line || node.children[0].loc.last_line
104
- then_end = node.children[2] ? node.loc.else.line : node.loc.last_line
106
+ if_loc = loc #: NodeHelper::condition_loc
107
+
108
+ if if_loc.expression.begin_pos == if_loc.keyword.begin_pos
109
+ construct_mapping(node: cond_node,annotations: annotations, mapping: mapping, line_range: nil)
110
+
111
+ if truthy_node
112
+ if if_loc.keyword.source == "if" || if_loc.keyword.source == "elsif"
113
+ # if foo
114
+ # bar <=
115
+ # end
116
+ then_start = if_loc.begin&.last_line || cond_node.loc.last_line
117
+ then_end = if_loc.else&.line || if_loc.last_line
105
118
  else
106
- then_start = node.loc.else.last_line
107
- then_end = node.loc.last_line
119
+ # unless foo
120
+ # else
121
+ # bar <=
122
+ # end
123
+ if_loc.else or raise
124
+ then_start = if_loc.else.last_line
125
+ then_end = loc.last_line
108
126
  end
109
- construct_mapping(node: node.children[1],
110
- annotations: annotations,
111
- mapping: mapping,
112
- line_range: then_start...then_end)
127
+ construct_mapping(node: truthy_node, annotations: annotations, mapping: mapping, line_range: then_start...then_end)
113
128
  end
114
129
 
115
- if node.children[2]
116
- if node.loc.keyword.source == "if" || node.loc.keyword.source == "elsif"
117
- else_start = node.loc.else.last_line
118
- else_end = node.loc.last_line
130
+ if falsy_node
131
+ if if_loc.keyword.source == "if" || if_loc.keyword.source == "elsif"
132
+ # if foo
133
+ # else
134
+ # bar <=
135
+ # end
136
+ if_loc.else or raise
137
+ else_start = if_loc.else.last_line
138
+ else_end = if_loc.last_line
119
139
  else
120
- else_start = node.loc.begin&.last_line || node.children[0].loc.last_line
121
- else_end = node.children[1] ? node.loc.else.line : node.loc.last_line
140
+ # unless foo
141
+ # bar <=
142
+ # end
143
+ else_start = if_loc.begin&.last_line || cond_node.loc.last_line
144
+ else_end = if_loc.else&.line || if_loc.last_line
122
145
  end
123
- construct_mapping(node: node.children[2],
124
- annotations: annotations,
125
- mapping: mapping,
126
- line_range: else_start...else_end)
146
+ construct_mapping(node: falsy_node, annotations: annotations, mapping: mapping, line_range: else_start...else_end)
127
147
  end
128
148
 
129
149
  else
@@ -135,22 +155,19 @@ module Steep
135
155
  end
136
156
 
137
157
  when :while, :until
138
- if node.loc.expression.begin_pos == node.loc.keyword.begin_pos
139
- construct_mapping(node: node.children[0],
140
- annotations: annotations,
141
- mapping: mapping,
142
- line_range: nil)
143
-
144
- if node.children[1]
145
- body_start = node.children[0].loc.last_line
146
- body_end = node.loc.end.line
147
-
148
- construct_mapping(node: node.children[1],
149
- annotations: annotations,
150
- mapping: mapping,
151
- line_range: body_start...body_end)
152
- end
158
+ cond_node, body_node, loc = deconstruct_whileish_node!(node)
153
159
 
160
+ if loc.expression.begin_pos == loc.keyword.begin_pos
161
+ # prefix while
162
+ loc.end or raise
163
+ construct_mapping(node: cond_node, annotations: annotations, mapping: mapping, line_range: nil)
164
+
165
+ if body_node
166
+ body_start = cond_node.loc.last_line
167
+ body_end = loc.end.line
168
+
169
+ construct_mapping(node: body_node, annotations: annotations, mapping: mapping, line_range: body_start...body_end)
170
+ end
154
171
  else
155
172
  # postfix while
156
173
  each_child_node(node) do |child|
@@ -159,70 +176,63 @@ module Steep
159
176
  end
160
177
 
161
178
  when :while_post, :until_post
162
- construct_mapping(node: node.children[0],
163
- annotations: annotations,
164
- mapping: mapping,
165
- line_range: nil)
166
-
167
- if node.children[1]
168
- body_start = node.loc.expression.line
169
- body_end = node.loc.keyword.line
170
-
171
- construct_mapping(node: node.children[1],
172
- annotations: annotations,
173
- mapping: mapping,
174
- line_range: body_start...body_end)
179
+ cond_node, body_node, loc = deconstruct_whileish_node!(node)
180
+
181
+ construct_mapping(node: cond_node, annotations: annotations, mapping: mapping, line_range: nil)
182
+
183
+ if body_node
184
+ body_start = loc.expression.line
185
+ body_end = loc.keyword.line
186
+ construct_mapping(node: body_node, annotations: annotations, mapping: mapping, line_range: body_start...body_end)
175
187
  end
176
188
 
177
189
  when :case
178
- if node.children[0]
179
- construct_mapping(node: node.children[0], annotations: annotations, mapping: mapping, line_range: nil)
190
+ cond_node, when_nodes, else_node, loc = deconstruct_case_node!(node)
191
+
192
+ if cond_node
193
+ construct_mapping(node: cond_node, annotations: annotations, mapping: mapping, line_range: nil)
180
194
  end
181
195
 
182
- if node.children.last
183
- else_node = node.children.last
184
- else_start = node.loc.else.last_line
185
- else_end = node.loc.end.line
196
+ if else_node
197
+ loc.else or raise
198
+ loc.end or raise
186
199
 
187
- construct_mapping(node: else_node,
188
- annotations: annotations,
189
- mapping: mapping,
190
- line_range: else_start...else_end)
200
+ else_start = loc.else.last_line
201
+ else_end = loc.end.line
202
+
203
+ construct_mapping(node: else_node, annotations: annotations, mapping: mapping, line_range: else_start...else_end)
191
204
  end
192
205
 
193
- node.children.drop(1).each do |child|
194
- if child&.type == :when
206
+ when_nodes.each do |child|
207
+ if child.type == :when
195
208
  construct_mapping(node: child, annotations: annotations, mapping: mapping, line_range: nil)
196
209
  end
197
210
  end
198
211
 
199
212
  when :when
200
- last_cond = node.children[-2]
201
- body = node.children.last
213
+ operands, body, loc = deconstruct_when_node!(node)
214
+ last_cond = operands.last or raise
202
215
 
203
- node.children.take(node.children.size-1).each do |child|
216
+ operands.each do |child|
204
217
  construct_mapping(node: child, annotations: annotations, mapping: mapping, line_range: nil)
205
218
  end
206
219
 
207
220
  if body
208
- cond_end = last_cond.loc.last_line+1
221
+ cond_end = loc.begin&.last_line || last_cond.loc.last_line+1
209
222
  body_end = body.loc.last_line
210
- construct_mapping(node: body,
211
- annotations: annotations,
212
- mapping: mapping,
213
- line_range: cond_end...body_end)
223
+ construct_mapping(node: body, annotations: annotations, mapping: mapping, line_range: cond_end...body_end)
214
224
  end
215
225
 
216
226
  when :rescue
217
- if node.children.last
218
- else_node = node.children.last
219
- else_start = node.loc.else.last_line
220
- else_end = node.loc.last_line
221
-
222
- construct_mapping(node: else_node,
223
- annotations: annotations,
224
- mapping: mapping,
225
- line_range: else_start...else_end)
227
+ body, resbodies, else_node, loc = deconstruct_rescue_node!(node)
228
+
229
+ if else_node
230
+ loc.else or raise
231
+
232
+ else_start = loc.else.last_line
233
+ else_end = loc.last_line
234
+
235
+ construct_mapping(node: else_node, annotations: annotations, mapping: mapping, line_range: else_start...else_end)
226
236
  end
227
237
 
228
238
  each_child_node(node) do |child|
@@ -236,17 +246,13 @@ module Steep
236
246
  end
237
247
 
238
248
  associated_annotations, other_annotations = annotations.partition do |annot|
249
+ location = node.loc
250
+ annot.line or next
251
+
239
252
  case node.type
240
- when :def, :module, :class, :block, :ensure, :defs
241
- loc = node.loc
242
- loc.line <= annot.line && annot.line < loc.last_line
243
-
244
- when :resbody
245
- if node.loc.keyword.begin_pos == node.loc.expression.begin_pos
246
- # skip postfix rescue
247
- loc = node.loc
248
- loc.line <= annot.line && annot.line < loc.last_line
249
- end
253
+ when :def, :module, :class, :block, :ensure, :defs, :resbody
254
+ location = node.loc
255
+ location.line <= annot.line && annot.line < location.last_line
250
256
  else
251
257
  if line_range
252
258
  line_range.begin <= annot.line && annot.line < line_range.end
@@ -307,7 +313,7 @@ module Steep
307
313
  case node.type
308
314
  when :dstr, :str
309
315
  if node.location.respond_to?(:heredoc_body)
310
- yield [node, *parents]
316
+ yield [[node, *parents], _ = node.location]
311
317
  end
312
318
  end
313
319
 
@@ -322,10 +328,10 @@ module Steep
322
328
  end
323
329
 
324
330
  def find_heredoc_nodes(line, column, position)
325
- each_heredoc_node() do |nodes|
331
+ each_heredoc_node() do |nodes, location|
326
332
  node = nodes[0]
327
333
 
328
- range = node.location.heredoc_body&.yield_self do |r|
334
+ range = location.heredoc_body&.yield_self do |r|
329
335
  r.begin_pos..r.end_pos
330
336
  end
331
337
 
@@ -377,6 +383,20 @@ module Steep
377
383
  end
378
384
  end
379
385
 
386
+ def find_comment(line:, column:)
387
+ pos = buffer.loc_to_pos([line, column])
388
+
389
+ comment = comments.bsearch do |comment|
390
+ pos <= comment.loc.expression.end_pos
391
+ end
392
+
393
+ if comment
394
+ if comment.loc.expression.begin_pos < pos
395
+ comment
396
+ end
397
+ end
398
+ end
399
+
380
400
  def self.delete_defs(node, allow_list)
381
401
  case node.type
382
402
  when :def
@@ -417,7 +437,7 @@ module Steep
417
437
  mapping[node_] << annot
418
438
  end
419
439
 
420
- Source.new(path: path, node: node_, mapping: mapping)
440
+ Source.new(buffer: buffer, path: path, node: node_, mapping: mapping, comments: comments)
421
441
  else
422
442
  self
423
443
  end
@@ -445,7 +465,8 @@ module Steep
445
465
  when :return, :break, :next
446
466
  # Skip
447
467
  when :begin
448
- if node.loc.begin
468
+ location = node.loc #: Parser::Source::Map & Parser::AST::_Collection
469
+ if location.begin
449
470
  # paren
450
471
  child_assertions = comments.except(last_line)
451
472
  node = map_child_node(node) {|child| insert_type_node(child, child_assertions) }
@@ -468,7 +489,11 @@ module Steep
468
489
  nil,
469
490
  [
470
491
  map_child_node(send) {|child| insert_type_node(child, child_assertions) },
471
- *children.map {|child| insert_type_node(child, child_assertions) }
492
+ *children.map do |child|
493
+ if child
494
+ insert_type_node(child, child_assertions)
495
+ end
496
+ end
472
497
  ]
473
498
  )
474
499
  when :numblock
@@ -490,9 +515,35 @@ module Steep
490
515
  end
491
516
  end
492
517
 
493
- adjust_location(
494
- map_child_node(node, nil) {|child| insert_type_node(child, comments) }
495
- )
518
+ case node.type
519
+ when :class
520
+ class_name, super_class, class_body = node.children
521
+ adjust_location(
522
+ node.updated(
523
+ nil,
524
+ [
525
+ class_name,
526
+ super_class,
527
+ class_body ? insert_type_node(class_body, comments) : nil
528
+ ]
529
+ )
530
+ )
531
+ when :module
532
+ module_name, module_body = node.children
533
+ adjust_location(
534
+ node.updated(
535
+ nil,
536
+ [
537
+ module_name,
538
+ module_body ? insert_type_node(module_body, comments) : nil
539
+ ]
540
+ )
541
+ )
542
+ else
543
+ adjust_location(
544
+ map_child_node(node, nil) {|child| insert_type_node(child, comments) }
545
+ )
546
+ end
496
547
  end
497
548
 
498
549
  def self.sendish_node?(node)
@@ -509,8 +560,14 @@ module Steep
509
560
  end
510
561
 
511
562
  if send_node
512
- if send_node.location.dot
513
- send_node.location.selector.line
563
+ receiver_node, name, _, location = deconstruct_send_node!(send_node)
564
+
565
+ if receiver_node
566
+ if location.dot
567
+ location.selector.line
568
+ end
569
+ else
570
+ location.selector.line
514
571
  end
515
572
  end
516
573
  end
@@ -637,7 +637,7 @@ module Steep
637
637
 
638
638
  All(relation) do |result|
639
639
  method_pairs.each do |method_name, method_relation|
640
- result.add(relation) do
640
+ result.add(method_relation) do
641
641
  check_method(method_name, method_relation)
642
642
  end
643
643
  end
@@ -808,7 +808,9 @@ module Steep
808
808
  All(relation) do |result|
809
809
  type_relation = Relation.new(sub_type: sub_type.type, super_type: super_type.type)
810
810
 
811
- case ret = expand_block_given(name, Relation.new(sub_type: sub_type.block, super_type: super_type.block))
811
+ ret = expand_block_given(name, Relation.new(sub_type: sub_type.block, super_type: super_type.block))
812
+
813
+ case ret
812
814
  when true
813
815
  result.add(type_relation) { check_function(name, type_relation) }
814
816
  when Relation
@@ -822,7 +824,7 @@ module Steep
822
824
  end
823
825
  end
824
826
  when Result::Failure
825
- result.add { ret }
827
+ result.add(ret.relation) { ret }
826
828
  end
827
829
  end
828
830
  end
@@ -63,6 +63,17 @@ module Steep
63
63
  covariants << type.name
64
64
  contravariants << type.name
65
65
  end
66
+ when AST::Types::Proc
67
+ type.type.params.each_type do |type|
68
+ add_type(type, variance: variance, covariants: contravariants, contravariants: covariants)
69
+ end
70
+ add_type(type.type.return_type, variance: variance, covariants: covariants, contravariants: contravariants)
71
+ if type.block
72
+ type.block.type.params.each_type do |type|
73
+ add_type(type, variance: variance, covariants: covariants, contravariants: contravariants)
74
+ end
75
+ add_type(type.type.return_type, variance: variance, covariants: contravariants, contravariants: covariants)
76
+ end
66
77
  when AST::Types::Union, AST::Types::Intersection, AST::Types::Tuple
67
78
  type.types.each do |ty|
68
79
  add_type(ty, variance: variance, covariants: covariants, contravariants: contravariants)
@@ -0,0 +1,35 @@
1
+ module Steep
2
+ class ThreadWaiter
3
+ attr_reader :objects, :queue, :waiter_threads
4
+
5
+ def initialize(objects)
6
+ @objects = objects
7
+ @queue = Thread::Queue.new()
8
+ @waiter_threads = Set[].compare_by_identity
9
+
10
+ objects.each do |object|
11
+ thread = yield(object)
12
+
13
+ waiter_thread = Thread.new(thread) do |thread|
14
+ Thread.current.report_on_exception = false
15
+
16
+ begin
17
+ thread.join
18
+ ensure
19
+ queue << [object, thread]
20
+ end
21
+ end
22
+
23
+ waiter_threads << waiter_thread
24
+ end
25
+ end
26
+
27
+ def wait_one
28
+ unless waiter_threads.empty?
29
+ obj, th = queue.pop()
30
+ waiter_threads.delete(th)
31
+ obj
32
+ end
33
+ end
34
+ end
35
+ end