rbi 0.0.1 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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