rbi 0.0.1 → 0.0.2

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