rbi 0.0.1 → 0.0.2

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,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