rbi 0.0.1 → 0.0.5

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.
@@ -0,0 +1,629 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RBI
5
+ module Rewriters
6
+ # Merge two RBI trees together
7
+ #
8
+ # Be this `Tree`:
9
+ # ~~~rb
10
+ # class Foo
11
+ # attr_accessor :a
12
+ # def m; end
13
+ # C = 10
14
+ # end
15
+ # ~~~
16
+ #
17
+ # Merged with this one:
18
+ # ~~~rb
19
+ # class Foo
20
+ # attr_reader :a
21
+ # def m(x); end
22
+ # C = 10
23
+ # end
24
+ # ~~~
25
+ #
26
+ # Compatible definitions are merged together while incompatible definitions are moved into a `ConflictTree`:
27
+ # ~~~rb
28
+ # class Foo
29
+ # <<<<<<< left
30
+ # attr_accessor :a
31
+ # def m; end
32
+ # =======
33
+ # attr_reader :a
34
+ # def m(x); end
35
+ # >>>>>>> right
36
+ # C = 10
37
+ # end
38
+ # ~~~
39
+ class Merge
40
+ extend T::Sig
41
+
42
+ class Keep < ::T::Enum
43
+ enums do
44
+ NONE = new
45
+ LEFT = new
46
+ RIGHT = new
47
+ end
48
+ end
49
+
50
+ sig { params(left: Tree, right: Tree, left_name: String, right_name: String, keep: Keep).returns(Tree) }
51
+ def self.merge_trees(left, right, left_name: "left", right_name: "right", keep: Keep::NONE)
52
+ left.nest_singleton_methods!
53
+ right.nest_singleton_methods!
54
+ rewriter = Rewriters::Merge.new(left_name: left_name, right_name: right_name, keep: keep)
55
+ rewriter.merge(left)
56
+ rewriter.merge(right)
57
+ tree = rewriter.tree
58
+ ConflictTreeMerger.new.visit(tree)
59
+ tree
60
+ end
61
+
62
+ sig { returns(Tree) }
63
+ attr_reader :tree
64
+
65
+ sig { params(left_name: String, right_name: String, keep: Keep).void }
66
+ def initialize(left_name: "left", right_name: "right", keep: Keep::NONE)
67
+ @left_name = left_name
68
+ @right_name = right_name
69
+ @keep = keep
70
+ @tree = T.let(Tree.new, Tree)
71
+ @scope_stack = T.let([@tree], T::Array[Tree])
72
+ end
73
+
74
+ sig { params(tree: Tree).returns(T::Array[Conflict]) }
75
+ def merge(tree)
76
+ v = TreeMerger.new(@tree, left_name: @left_name, right_name: @right_name, keep: @keep)
77
+ v.visit(tree)
78
+ v.conflicts
79
+ end
80
+
81
+ # Used for logging / error displaying purpose
82
+ class Conflict < T::Struct
83
+ extend T::Sig
84
+
85
+ const :left, Node
86
+ const :right, Node
87
+ const :left_name, String
88
+ const :right_name, String
89
+
90
+ sig { returns(String) }
91
+ def to_s
92
+ "Conflicting definitions for `#{left}`"
93
+ end
94
+ end
95
+
96
+ class TreeMerger < Visitor
97
+ extend T::Sig
98
+
99
+ sig { returns(T::Array[Conflict]) }
100
+ attr_reader :conflicts
101
+
102
+ sig { params(output: Tree, left_name: String, right_name: String, keep: Keep).void }
103
+ def initialize(output, left_name: "left", right_name: "right", keep: Keep::NONE)
104
+ super()
105
+ @tree = output
106
+ @index = T.let(output.index, Index)
107
+ @scope_stack = T.let([@tree], T::Array[Tree])
108
+ @left_name = left_name
109
+ @right_name = right_name
110
+ @keep = keep
111
+ @conflicts = T.let([], T::Array[Conflict])
112
+ end
113
+
114
+ sig { override.params(node: T.nilable(Node)).void }
115
+ def visit(node)
116
+ return unless node
117
+
118
+ case node
119
+ when Scope
120
+ prev = previous_definition(node)
121
+
122
+ if prev.is_a?(Scope)
123
+ if node.compatible_with?(prev)
124
+ prev.merge_with(node)
125
+ elsif @keep == Keep::LEFT
126
+ # do nothing it's already merged
127
+ elsif @keep == Keep::RIGHT
128
+ prev = replace_scope_header(prev, node)
129
+ else
130
+ make_conflict_scope(prev, node)
131
+ end
132
+ @scope_stack << prev
133
+ else
134
+ copy = node.dup_empty
135
+ current_scope << copy
136
+ @scope_stack << copy
137
+ end
138
+ visit_all(node.nodes)
139
+ @scope_stack.pop
140
+ when Tree
141
+ current_scope.merge_with(node)
142
+ visit_all(node.nodes)
143
+ when Indexable
144
+ prev = previous_definition(node)
145
+ if prev
146
+ if node.compatible_with?(prev)
147
+ prev.merge_with(node)
148
+ elsif @keep == Keep::LEFT
149
+ # do nothing it's already merged
150
+ elsif @keep == Keep::RIGHT
151
+ prev.replace(node)
152
+ else
153
+ make_conflict_tree(prev, node)
154
+ end
155
+ else
156
+ current_scope << node.dup
157
+ end
158
+ end
159
+ end
160
+
161
+ private
162
+
163
+ sig { returns(Tree) }
164
+ def current_scope
165
+ T.must(@scope_stack.last)
166
+ end
167
+
168
+ sig { params(node: Node).returns(T.nilable(Node)) }
169
+ def previous_definition(node)
170
+ case node
171
+ when Indexable
172
+ node.index_ids.each do |id|
173
+ others = @index[id]
174
+ return others.last unless others.empty?
175
+ end
176
+ end
177
+ nil
178
+ end
179
+
180
+ sig { params(left: Scope, right: Scope).void }
181
+ def make_conflict_scope(left, right)
182
+ @conflicts << Conflict.new(left: left, right: right, left_name: @left_name, right_name: @right_name)
183
+ scope_conflict = ScopeConflict.new(left: left, right: right, left_name: @left_name, right_name: @right_name)
184
+ left.replace(scope_conflict)
185
+ end
186
+
187
+ sig { params(left: Node, right: Node).void }
188
+ def make_conflict_tree(left, right)
189
+ @conflicts << Conflict.new(left: left, right: right, left_name: @left_name, right_name: @right_name)
190
+ tree = left.parent_conflict_tree
191
+ unless tree
192
+ tree = ConflictTree.new(left_name: @left_name, right_name: @right_name)
193
+ left.replace(tree)
194
+ tree.left << left
195
+ end
196
+ tree.right << right
197
+ end
198
+
199
+ sig { params(left: Scope, right: Scope).returns(Scope) }
200
+ def replace_scope_header(left, right)
201
+ right_copy = right.dup_empty
202
+ left.replace(right_copy)
203
+ left.nodes.each do |node|
204
+ right_copy << node
205
+ end
206
+ @index.index(right_copy)
207
+ right_copy
208
+ end
209
+ end
210
+
211
+ # Merge adjacent conflict trees
212
+ #
213
+ # Transform this:
214
+ # ~~~rb
215
+ # class Foo
216
+ # <<<<<<< left
217
+ # def m1; end
218
+ # =======
219
+ # def m1(a); end
220
+ # >>>>>>> right
221
+ # <<<<<<< left
222
+ # def m2(a); end
223
+ # =======
224
+ # def m2; end
225
+ # >>>>>>> right
226
+ # end
227
+ # ~~~
228
+ #
229
+ # Into this:
230
+ # ~~~rb
231
+ # class Foo
232
+ # <<<<<<< left
233
+ # def m1; end
234
+ # def m2(a); end
235
+ # =======
236
+ # def m1(a); end
237
+ # def m2; end
238
+ # >>>>>>> right
239
+ # end
240
+ # ~~~
241
+ class ConflictTreeMerger < Visitor
242
+ sig { override.params(node: T.nilable(Node)).void }
243
+ def visit(node)
244
+ visit_all(node.nodes) if node.is_a?(Tree)
245
+ end
246
+
247
+ sig { override.params(nodes: T::Array[Node]).void }
248
+ def visit_all(nodes)
249
+ last_conflict_tree = T.let(nil, T.nilable(ConflictTree))
250
+ nodes.dup.each do |node|
251
+ if node.is_a?(ConflictTree)
252
+ if last_conflict_tree
253
+ merge_conflict_trees(last_conflict_tree.left, node.left)
254
+ merge_conflict_trees(last_conflict_tree.right, node.right)
255
+ node.detach
256
+ next
257
+ else
258
+ last_conflict_tree = node
259
+ end
260
+ end
261
+
262
+ visit(node)
263
+ end
264
+ end
265
+
266
+ private
267
+
268
+ sig { params(left: Tree, right: Tree).void }
269
+ def merge_conflict_trees(left, right)
270
+ right.nodes.dup.each do |node|
271
+ left << node
272
+ end
273
+ end
274
+ end
275
+ end
276
+ end
277
+
278
+ class Node
279
+ extend T::Sig
280
+
281
+ # Can `self` and `_other` be merged into a single definition?
282
+ sig { params(_other: Node).returns(T::Boolean) }
283
+ def compatible_with?(_other)
284
+ true
285
+ end
286
+
287
+ # Merge `self` and `other` into a single definition
288
+ sig { params(other: Node).void }
289
+ def merge_with(other); end
290
+
291
+ sig { returns(T.nilable(ConflictTree)) }
292
+ def parent_conflict_tree
293
+ parent = T.let(parent_tree, T.nilable(Node))
294
+ while parent
295
+ return parent if parent.is_a?(ConflictTree)
296
+ parent = parent.parent_tree
297
+ end
298
+ nil
299
+ end
300
+ end
301
+
302
+ class NodeWithComments
303
+ extend T::Sig
304
+
305
+ sig { override.params(other: Node).void }
306
+ def merge_with(other)
307
+ return unless other.is_a?(NodeWithComments)
308
+ other.comments.each do |comment|
309
+ comments << comment unless comments.include?(comment)
310
+ end
311
+ end
312
+ end
313
+
314
+ class Tree
315
+ extend T::Sig
316
+
317
+ sig { params(other: Tree).returns(Tree) }
318
+ def merge(other)
319
+ Rewriters::Merge.merge_trees(self, other)
320
+ end
321
+ end
322
+
323
+ class Scope
324
+ extend T::Sig
325
+
326
+ # Duplicate `self` scope without its body
327
+ sig { returns(T.self_type) }
328
+ def dup_empty
329
+ case self
330
+ when Module
331
+ Module.new(name, loc: loc, comments: comments)
332
+ when Class
333
+ Class.new(name, superclass_name: superclass_name, loc: loc, comments: comments)
334
+ when Struct
335
+ Struct.new(name, members: members, keyword_init: keyword_init, loc: loc, comments: comments)
336
+ when SingletonClass
337
+ SingletonClass.new(loc: loc, comments: comments)
338
+ else
339
+ raise "Can't duplicate node #{self}"
340
+ end
341
+ end
342
+ end
343
+
344
+ class Class
345
+ extend T::Sig
346
+
347
+ sig { override.params(other: Node).returns(T::Boolean) }
348
+ def compatible_with?(other)
349
+ other.is_a?(Class) && superclass_name == other.superclass_name
350
+ end
351
+ end
352
+
353
+ class Module
354
+ extend T::Sig
355
+
356
+ sig { override.params(other: Node).returns(T::Boolean) }
357
+ def compatible_with?(other)
358
+ other.is_a?(Module)
359
+ end
360
+ end
361
+
362
+ class Struct
363
+ extend T::Sig
364
+
365
+ sig { override.params(other: Node).returns(T::Boolean) }
366
+ def compatible_with?(other)
367
+ other.is_a?(Struct) && members == other.members && keyword_init == other.keyword_init
368
+ end
369
+ end
370
+
371
+ class Const
372
+ extend T::Sig
373
+
374
+ sig { override.params(other: Node).returns(T::Boolean) }
375
+ def compatible_with?(other)
376
+ other.is_a?(Const) && name == other.name && value == other.value
377
+ end
378
+ end
379
+
380
+ class Attr
381
+ extend T::Sig
382
+
383
+ sig { override.params(other: Node).returns(T::Boolean) }
384
+ def compatible_with?(other)
385
+ return false unless other.is_a?(Attr)
386
+ return false unless names == other.names
387
+ sigs.empty? || other.sigs.empty? || sigs == other.sigs
388
+ end
389
+
390
+ sig { override.params(other: Node).void }
391
+ def merge_with(other)
392
+ return unless other.is_a?(Attr)
393
+ super
394
+ other.sigs.each do |sig|
395
+ sigs << sig unless sigs.include?(sig)
396
+ end
397
+ end
398
+ end
399
+
400
+ class AttrReader
401
+ extend T::Sig
402
+
403
+ sig { override.params(other: Node).returns(T::Boolean) }
404
+ def compatible_with?(other)
405
+ other.is_a?(AttrReader) && super
406
+ end
407
+ end
408
+
409
+ class AttrWriter
410
+ extend T::Sig
411
+
412
+ sig { override.params(other: Node).returns(T::Boolean) }
413
+ def compatible_with?(other)
414
+ other.is_a?(AttrWriter) && super
415
+ end
416
+ end
417
+
418
+ class AttrAccessor
419
+ extend T::Sig
420
+
421
+ sig { override.params(other: Node).returns(T::Boolean) }
422
+ def compatible_with?(other)
423
+ other.is_a?(AttrAccessor) && super
424
+ end
425
+ end
426
+
427
+ class Method
428
+ extend T::Sig
429
+
430
+ sig { override.params(other: Node).returns(T::Boolean) }
431
+ def compatible_with?(other)
432
+ return false unless other.is_a?(Method)
433
+ return false unless name == other.name
434
+ return false unless params == other.params
435
+ sigs.empty? || other.sigs.empty? || sigs == other.sigs
436
+ end
437
+
438
+ sig { override.params(other: Node).void }
439
+ def merge_with(other)
440
+ return unless other.is_a?(Method)
441
+ super
442
+ other.sigs.each do |sig|
443
+ sigs << sig unless sigs.include?(sig)
444
+ end
445
+ end
446
+ end
447
+
448
+ class Mixin
449
+ extend T::Sig
450
+
451
+ sig { override.params(other: Node).returns(T::Boolean) }
452
+ def compatible_with?(other)
453
+ other.is_a?(Mixin) && names == other.names
454
+ end
455
+ end
456
+
457
+ class Include
458
+ extend T::Sig
459
+
460
+ sig { override.params(other: Node).returns(T::Boolean) }
461
+ def compatible_with?(other)
462
+ other.is_a?(Include) && super
463
+ end
464
+ end
465
+
466
+ class Extend
467
+ extend T::Sig
468
+
469
+ sig { override.params(other: Node).returns(T::Boolean) }
470
+ def compatible_with?(other)
471
+ other.is_a?(Extend) && super
472
+ end
473
+ end
474
+
475
+ class MixesInClassMethods
476
+ extend T::Sig
477
+
478
+ sig { override.params(other: Node).returns(T::Boolean) }
479
+ def compatible_with?(other)
480
+ other.is_a?(MixesInClassMethods) && super
481
+ end
482
+ end
483
+
484
+ class Helper
485
+ extend T::Sig
486
+
487
+ sig { override.params(other: Node).returns(T::Boolean) }
488
+ def compatible_with?(other)
489
+ other.is_a?(Helper) && name == other.name
490
+ end
491
+ end
492
+
493
+ class TStructField
494
+ extend T::Sig
495
+
496
+ sig { override.params(other: Node).returns(T::Boolean) }
497
+ def compatible_with?(other)
498
+ other.is_a?(TStructField) && name == other.name && type == other.type && default == other.default
499
+ end
500
+ end
501
+
502
+ class TStructConst
503
+ extend T::Sig
504
+
505
+ sig { override.params(other: Node).returns(T::Boolean) }
506
+ def compatible_with?(other)
507
+ other.is_a?(TStructConst) && super
508
+ end
509
+ end
510
+
511
+ class TEnumBlock
512
+ extend T::Sig
513
+
514
+ sig { override.params(other: Node).void }
515
+ def merge_with(other)
516
+ return unless other.is_a?(TEnumBlock)
517
+ super
518
+ other.names.each do |name|
519
+ names << name unless names.include?(name)
520
+ end
521
+ end
522
+ end
523
+
524
+ class TStructProp
525
+ extend T::Sig
526
+
527
+ sig { override.params(other: Node).returns(T::Boolean) }
528
+ def compatible_with?(other)
529
+ other.is_a?(TStructProp) && super
530
+ end
531
+ end
532
+
533
+ # A tree showing incompatibles nodes
534
+ #
535
+ # Is rendered as a merge conflict between `left` and` right`:
536
+ # ~~~rb
537
+ # class Foo
538
+ # <<<<<<< left
539
+ # def m1; end
540
+ # def m2(a); end
541
+ # =======
542
+ # def m1(a); end
543
+ # def m2; end
544
+ # >>>>>>> right
545
+ # end
546
+ # ~~~
547
+ class ConflictTree < Tree
548
+ extend T::Sig
549
+
550
+ sig { returns(Tree) }
551
+ attr_reader :left, :right
552
+
553
+ sig { params(left_name: String, right_name: String).void }
554
+ def initialize(left_name: "left", right_name: "right")
555
+ super()
556
+ @left_name = left_name
557
+ @right_name = right_name
558
+ @left = T.let(Tree.new, Tree)
559
+ @left.parent_tree = self
560
+ @right = T.let(Tree.new, Tree)
561
+ @right.parent_tree = self
562
+ end
563
+
564
+ sig { override.params(v: Printer).void }
565
+ def accept_printer(v)
566
+ v.printl("<<<<<<< #{@left_name}")
567
+ v.visit(left)
568
+ v.printl("=======")
569
+ v.visit(right)
570
+ v.printl(">>>>>>> #{@right_name}")
571
+ end
572
+ end
573
+
574
+ # A conflict between two scope headers
575
+ #
576
+ # Is rendered as a merge conflict between `left` and` right` for scope definitions:
577
+ # ~~~rb
578
+ # <<<<<<< left
579
+ # class Foo
580
+ # =======
581
+ # module Foo
582
+ # >>>>>>> right
583
+ # def m1; end
584
+ # end
585
+ # ~~~
586
+ class ScopeConflict < Tree
587
+ extend T::Sig
588
+
589
+ sig { returns(Scope) }
590
+ attr_reader :left, :right
591
+
592
+ sig do
593
+ params(
594
+ left: Scope,
595
+ right: Scope,
596
+ left_name: String,
597
+ right_name: String
598
+ ).void
599
+ end
600
+ def initialize(left:, right:, left_name: "left", right_name: "right")
601
+ super()
602
+ @left = left
603
+ @right = right
604
+ @left_name = left_name
605
+ @right_name = right_name
606
+ end
607
+
608
+ sig { override.params(v: Printer).void }
609
+ def accept_printer(v)
610
+ previous_node = v.previous_node
611
+ v.printn if previous_node && (!previous_node.oneline? || !oneline?)
612
+
613
+ v.printl("# #{loc}") if loc && v.print_locs
614
+ v.visit_all(comments)
615
+
616
+ v.printl("<<<<<<< #{@left_name}")
617
+ left.print_header(v)
618
+ v.printl("=======")
619
+ right.print_header(v)
620
+ v.printl(">>>>>>> #{@right_name}")
621
+ left.print_body(v)
622
+ end
623
+
624
+ sig { override.returns(T::Boolean) }
625
+ def oneline?
626
+ left.oneline?
627
+ end
628
+ end
629
+ end
@@ -0,0 +1,63 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RBI
5
+ module Rewriters
6
+ class NestNonPublicMethods < Visitor
7
+ extend T::Sig
8
+
9
+ sig { override.params(node: T.nilable(Node)).void }
10
+ def visit(node)
11
+ return unless node
12
+
13
+ case node
14
+ when Tree
15
+ public_group = VisibilityGroup.new(Public.new)
16
+ protected_group = VisibilityGroup.new(Protected.new)
17
+ private_group = VisibilityGroup.new(Private.new)
18
+
19
+ node.nodes.dup.each do |child|
20
+ visit(child)
21
+ next unless child.is_a?(Method)
22
+ child.detach
23
+ case child.visibility
24
+ when Protected
25
+ protected_group << child
26
+ when Private
27
+ private_group << child
28
+ else
29
+ public_group << child
30
+ end
31
+ end
32
+
33
+ node << public_group unless public_group.empty?
34
+ node << protected_group unless protected_group.empty?
35
+ node << private_group unless private_group.empty?
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ class Tree
42
+ extend T::Sig
43
+
44
+ sig { void }
45
+ def nest_non_public_methods!
46
+ visitor = Rewriters::NestNonPublicMethods.new
47
+ visitor.visit(self)
48
+ end
49
+ end
50
+
51
+ class VisibilityGroup < Tree
52
+ extend T::Sig
53
+
54
+ sig { returns(Visibility) }
55
+ attr_reader :visibility
56
+
57
+ sig { params(visibility: Visibility).void }
58
+ def initialize(visibility)
59
+ super()
60
+ @visibility = visibility
61
+ end
62
+ end
63
+ end