rbi 0.0.5 → 0.0.9

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fb13c49bd19a95d439f7249770add05929979892daa3ba9c55de8241a596a8d2
4
- data.tar.gz: bc772396cd7997b9443782775854993cb9e4b0d1241415020c258aa8f49186b2
3
+ metadata.gz: e263b24187c9a9f490f5c29a249d22d2ee564a2a59aaea3f79eb62cbff8ac24f
4
+ data.tar.gz: c5bd9aed9462d42b8dddef6b6c8bfe001224747b49ee1ad187721daf9ac946d3
5
5
  SHA512:
6
- metadata.gz: 9990c9fd5fa2bc0925f06ce07f02079165f431e9535874d0204b0e2afbc6ad1919578e3ecd13a33b69ef575ec84911fadb34d3a71df917437fd46182bb07aa6e
7
- data.tar.gz: 2818b3db38a03e58eb8bd6dd160ca50fac237242192620a6910dd73c451a3ee90e5dd483502e721870329724f0b4237899225d3c4e5f429b37abd667e0183c87
6
+ metadata.gz: 1174676242d2ed3cfdebc28bc6eb70789d249cf10c4e5d8fceab47c58024b8db4a3f4bad9609c7543e510e6d85b6f321fc0ee65ce8d4204fee417bafb1e44510
7
+ data.tar.gz: 29f7a0b48aa474e3f970bba94e85fc6621ed9449f99c7249c2056a79ae5418b0c9b0e5e6ee97b37c5244e83c1514851a59a998a932e3c4ef36ef308b61036662
data/Gemfile CHANGED
@@ -8,10 +8,11 @@ gemspec
8
8
  group(:development, :test) do
9
9
  gem("byebug")
10
10
  gem("minitest")
11
+ gem("minitest-reporters")
11
12
  gem("rake", "~> 13.0")
12
13
  gem("rubocop", "~> 1.7", require: false)
13
14
  gem("rubocop-shopify", require: false)
14
15
  gem("rubocop-sorbet", require: false)
15
- gem("sorbet", require: false)
16
- gem("tapioca", require: false, github: "Shopify/tapioca", branch: "master")
16
+ gem("sorbet", ">= 0.5.9204", require: false)
17
+ gem("tapioca", "0.5.2", require: false)
17
18
  end
data/lib/rbi/index.rb CHANGED
@@ -29,9 +29,9 @@ module RBI
29
29
  @index[id] ||= []
30
30
  end
31
31
 
32
- sig { params(node: T.all(Indexable, Node)).void }
33
- def index(node)
34
- node.index_ids.each { |id| self[id] << node }
32
+ sig { params(nodes: Node).void }
33
+ def index(*nodes)
34
+ nodes.each { |node| visit(node) }
35
35
  end
36
36
 
37
37
  sig { override.params(node: T.nilable(Node)).void }
@@ -40,14 +40,21 @@ module RBI
40
40
 
41
41
  case node
42
42
  when Scope
43
- index(node)
43
+ index_node(node)
44
44
  visit_all(node.nodes)
45
45
  when Tree
46
46
  visit_all(node.nodes)
47
47
  when Indexable
48
- index(node)
48
+ index_node(node)
49
49
  end
50
50
  end
51
+
52
+ private
53
+
54
+ sig { params(node: T.all(Indexable, Node)).void }
55
+ def index_node(node)
56
+ node.index_ids.each { |id| self[id] << node }
57
+ end
51
58
  end
52
59
 
53
60
  class Tree
@@ -154,6 +161,16 @@ module RBI
154
161
  end
155
162
  end
156
163
 
164
+ class Send
165
+ extend T::Sig
166
+ include Indexable
167
+
168
+ sig { override.returns(T::Array[String]) }
169
+ def index_ids
170
+ ["#{parent_scope&.fully_qualified_name}.#{method}"]
171
+ end
172
+ end
173
+
157
174
  class TStructConst
158
175
  extend T::Sig
159
176
  include Indexable
data/lib/rbi/model.rb CHANGED
@@ -66,7 +66,8 @@ module RBI
66
66
  end
67
67
  end
68
68
 
69
- class EmptyComment < Comment
69
+ # An arbitrary blank line that can be added both in trees and comments
70
+ class BlankLine < Comment
70
71
  extend T::Sig
71
72
 
72
73
  sig { params(loc: T.nilable(Loc)).void }
@@ -89,6 +90,13 @@ module RBI
89
90
  super(loc: loc)
90
91
  @comments = comments
91
92
  end
93
+
94
+ sig { returns(T::Array[String]) }
95
+ def annotations
96
+ comments
97
+ .select { |comment| comment.text.start_with?("@") }
98
+ .map { |comment| T.must(comment.text[1..]) }
99
+ end
92
100
  end
93
101
 
94
102
  class Tree < NodeWithComments
@@ -126,10 +134,10 @@ module RBI
126
134
  extend T::Sig
127
135
 
128
136
  sig { returns(Tree) }
129
- attr_reader :root
137
+ attr_accessor :root
130
138
 
131
139
  sig { returns(T.nilable(String)) }
132
- attr_reader :strictness
140
+ attr_accessor :strictness
133
141
 
134
142
  sig { returns(T::Array[Comment]) }
135
143
  attr_accessor :comments
@@ -152,6 +160,11 @@ module RBI
152
160
  def <<(node)
153
161
  @root << node
154
162
  end
163
+
164
+ sig { returns(T::Boolean) }
165
+ def empty?
166
+ @root.empty?
167
+ end
155
168
  end
156
169
 
157
170
  # Scopes
@@ -901,6 +914,106 @@ module RBI
901
914
  end
902
915
  end
903
916
 
917
+ # Sends
918
+
919
+ class Send < NodeWithComments
920
+ extend T::Sig
921
+
922
+ sig { returns(String) }
923
+ attr_reader :method
924
+
925
+ sig { returns(T::Array[Arg]) }
926
+ attr_reader :args
927
+
928
+ sig do
929
+ params(
930
+ method: String,
931
+ args: T::Array[Arg],
932
+ loc: T.nilable(Loc),
933
+ comments: T::Array[Comment],
934
+ block: T.nilable(T.proc.params(node: Send).void)
935
+ ).void
936
+ end
937
+ def initialize(method, args = [], loc: nil, comments: [], &block)
938
+ super(loc: loc, comments: comments)
939
+ @method = method
940
+ @args = args
941
+ block&.call(self)
942
+ end
943
+
944
+ sig { params(arg: Arg).void }
945
+ def <<(arg)
946
+ @args << arg
947
+ end
948
+
949
+ sig { params(other: T.nilable(Object)).returns(T::Boolean) }
950
+ def ==(other)
951
+ Send === other && method == other.method && args == other.args
952
+ end
953
+
954
+ sig { returns(String) }
955
+ def to_s
956
+ "#{parent_scope&.fully_qualified_name}.#{method}(#{args.join(", ")})"
957
+ end
958
+ end
959
+
960
+ class Arg < Node
961
+ extend T::Sig
962
+
963
+ sig { returns(String) }
964
+ attr_reader :value
965
+
966
+ sig do
967
+ params(
968
+ value: String,
969
+ loc: T.nilable(Loc)
970
+ ).void
971
+ end
972
+ def initialize(value, loc: nil)
973
+ super(loc: loc)
974
+ @value = value
975
+ end
976
+
977
+ sig { params(other: T.nilable(Object)).returns(T::Boolean) }
978
+ def ==(other)
979
+ Arg === other && value == other.value
980
+ end
981
+
982
+ sig { returns(String) }
983
+ def to_s
984
+ value
985
+ end
986
+ end
987
+
988
+ class KwArg < Arg
989
+ extend T::Sig
990
+
991
+ sig { returns(String) }
992
+ attr_reader :keyword
993
+
994
+ sig do
995
+ params(
996
+ keyword: String,
997
+ value: String,
998
+ loc: T.nilable(Loc)
999
+ ).void
1000
+ end
1001
+ def initialize(keyword, value, loc: nil)
1002
+ super(value, loc: loc)
1003
+ @keyword = keyword
1004
+ end
1005
+
1006
+ sig { params(other: T.nilable(Object)).returns(T::Boolean) }
1007
+ def ==(other)
1008
+ KwArg === other && value == other.value && keyword == other.keyword
1009
+ end
1010
+
1011
+ sig { returns(String) }
1012
+ def to_s
1013
+ "#{keyword}: #{value}"
1014
+ end
1015
+ end
1016
+
904
1017
  # Sorbet's sigs
905
1018
 
906
1019
  class Sig < Node
data/lib/rbi/parser.rb CHANGED
@@ -43,11 +43,23 @@ module RBI
43
43
  Parser.new.parse_file(path)
44
44
  end
45
45
 
46
+ sig { params(paths: T::Array[String]).returns(T::Array[Tree]) }
47
+ def self.parse_files(paths)
48
+ parser = Parser.new
49
+ paths.map { |path| parser.parse_file(path) }
50
+ end
51
+
46
52
  sig { params(string: String).returns(Tree) }
47
53
  def parse_string(string)
48
54
  parse(string, file: "-")
49
55
  end
50
56
 
57
+ sig { params(strings: T::Array[String]).returns(T::Array[Tree]) }
58
+ def self.parse_strings(strings)
59
+ parser = Parser.new
60
+ strings.map { |string| parser.parse_string(string) }
61
+ end
62
+
51
63
  sig { params(path: String).returns(Tree) }
52
64
  def parse_file(path)
53
65
  parse(::File.read(path), file: path)
@@ -59,10 +71,9 @@ module RBI
59
71
  def parse(content, file:)
60
72
  node, comments = Unparser.parse_with_comments(content)
61
73
  assoc = ::Parser::Source::Comment.associate_locations(node, comments)
62
- builder = TreeBuilder.new(file: file, comments: assoc)
63
- builder.separate_header_comments
74
+ builder = TreeBuilder.new(file: file, comments: comments, nodes_comments_assoc: assoc)
64
75
  builder.visit(node)
65
- builder.assoc_dangling_comments(comments)
76
+ builder.post_process
66
77
  builder.tree
67
78
  rescue ::Parser::SyntaxError => e
68
79
  raise ParseError.new(e.message, Loc.from_ast_loc(file, e.diagnostic.location))
@@ -105,16 +116,26 @@ module RBI
105
116
  sig do
106
117
  params(
107
118
  file: String,
108
- comments: T::Hash[::Parser::Source::Map, T::Array[::Parser::Source::Comment]]
119
+ comments: T::Array[::Parser::Source::Comment],
120
+ nodes_comments_assoc: T::Hash[::Parser::Source::Map, T::Array[::Parser::Source::Comment]]
109
121
  ).void
110
122
  end
111
- def initialize(file:, comments: {})
123
+ def initialize(file:, comments: [], nodes_comments_assoc: {})
112
124
  super()
113
125
  @file = file
114
126
  @comments = comments
127
+ @nodes_comments_assoc = nodes_comments_assoc
115
128
  @tree = T.let(Tree.new, Tree)
116
129
  @scopes_stack = T.let([@tree], T::Array[Tree])
117
130
  @last_sigs = T.let([], T::Array[RBI::Sig])
131
+
132
+ separate_header_comments
133
+ end
134
+
135
+ sig { void }
136
+ def post_process
137
+ assoc_dangling_comments
138
+ set_root_tree_loc
118
139
  end
119
140
 
120
141
  sig { override.params(node: T.nilable(Object)).void }
@@ -146,46 +167,6 @@ module RBI
146
167
  end
147
168
  end
148
169
 
149
- sig { void }
150
- def separate_header_comments
151
- return if @comments.empty?
152
-
153
- keep = []
154
- node = T.must(@comments.keys.first)
155
- comments = T.must(@comments.values.first)
156
-
157
- last_line = T.let(nil, T.nilable(Integer))
158
- comments.reverse.each do |comment|
159
- comment_line = comment.location.last_line
160
-
161
- break if last_line && comment_line < last_line - 1 ||
162
- !last_line && comment_line < node.first_line - 1
163
-
164
- keep << comment
165
- last_line = comment_line
166
- end
167
-
168
- @comments[node] = keep.reverse
169
- end
170
-
171
- sig { params(comments: T::Array[::Parser::Source::Comment]).void }
172
- def assoc_dangling_comments(comments)
173
- last_line = T.let(nil, T.nilable(Integer))
174
- (comments - @comments.values.flatten).each do |comment|
175
- comment_line = comment.location.last_line
176
- text = comment.text[1..-1].strip
177
- loc = Loc.from_ast_loc(@file, comment.location)
178
-
179
- if last_line && comment_line > last_line + 1
180
- # Preserve empty lines in file headers
181
- tree.comments << EmptyComment.new(loc: loc)
182
- end
183
-
184
- tree.comments << Comment.new(text, loc: loc)
185
- last_line = comment_line
186
- end
187
- end
188
-
189
170
  private
190
171
 
191
172
  sig { params(node: AST::Node).returns(Scope) }
@@ -332,10 +313,28 @@ module RBI
332
313
  name, type, default_value = parse_tstruct_prop(node)
333
314
  TStructConst.new(name, type, default: default_value, loc: loc, comments: comments)
334
315
  else
335
- raise ParseError.new("Unsupported send node with name `#{method_name}`", loc)
316
+ args = parse_send_args(node)
317
+ Send.new(method_name.to_s, args, loc: loc, comments: comments)
336
318
  end
337
319
  end
338
320
 
321
+ sig { params(node: AST::Node).returns(T::Array[Arg]) }
322
+ def parse_send_args(node)
323
+ args = T.let([], T::Array[Arg])
324
+ node.children[2..-1].each do |child|
325
+ if child.type == :kwargs
326
+ child.children.each do |pair|
327
+ keyword = pair.children.first.children.last.to_s
328
+ value = parse_expr(pair.children.last)
329
+ args << KwArg.new(keyword, value)
330
+ end
331
+ else
332
+ args << Arg.new(parse_expr(child))
333
+ end
334
+ end
335
+ args
336
+ end
337
+
339
338
  sig { params(node: AST::Node).returns(T.nilable(RBI::Node)) }
340
339
  def parse_block(node)
341
340
  name = node.children[0].children[1]
@@ -435,7 +434,7 @@ module RBI
435
434
 
436
435
  sig { params(node: AST::Node).returns(T::Array[Comment]) }
437
436
  def node_comments(node)
438
- comments = @comments[node.location]
437
+ comments = @nodes_comments_assoc[node.location]
439
438
  return [] unless comments
440
439
  comments.map do |comment|
441
440
  text = comment.text[1..-1].strip
@@ -455,6 +454,60 @@ module RBI
455
454
  @last_sigs.clear
456
455
  sigs
457
456
  end
457
+
458
+ sig { void }
459
+ def assoc_dangling_comments
460
+ last_line = T.let(nil, T.nilable(Integer))
461
+ (@comments - @nodes_comments_assoc.values.flatten).each do |comment|
462
+ comment_line = comment.location.last_line
463
+ text = comment.text[1..-1].strip
464
+ loc = Loc.from_ast_loc(@file, comment.location)
465
+
466
+ if last_line && comment_line > last_line + 1
467
+ # Preserve empty lines in file headers
468
+ tree.comments << BlankLine.new(loc: loc)
469
+ end
470
+
471
+ tree.comments << Comment.new(text, loc: loc)
472
+ last_line = comment_line
473
+ end
474
+ end
475
+
476
+ sig { void }
477
+ def separate_header_comments
478
+ return if @nodes_comments_assoc.empty?
479
+
480
+ keep = []
481
+ node = T.must(@nodes_comments_assoc.keys.first)
482
+ comments = T.must(@nodes_comments_assoc.values.first)
483
+
484
+ last_line = T.let(nil, T.nilable(Integer))
485
+ comments.reverse.each do |comment|
486
+ comment_line = comment.location.last_line
487
+
488
+ break if last_line && comment_line < last_line - 1 ||
489
+ !last_line && comment_line < node.first_line - 1
490
+
491
+ keep << comment
492
+ last_line = comment_line
493
+ end
494
+
495
+ @nodes_comments_assoc[node] = keep.reverse
496
+ end
497
+
498
+ sig { void }
499
+ def set_root_tree_loc
500
+ first_loc = tree.nodes.first&.loc
501
+ last_loc = tree.nodes.last&.loc
502
+
503
+ @tree.loc = Loc.new(
504
+ file: @file,
505
+ begin_line: first_loc&.begin_line || 0,
506
+ begin_column: first_loc&.begin_column || 0,
507
+ end_line: last_loc&.end_line || 0,
508
+ end_column: last_loc&.end_column || 0
509
+ )
510
+ end
458
511
  end
459
512
 
460
513
  class ConstBuilder < ASTVisitor
data/lib/rbi/printer.rb CHANGED
@@ -136,6 +136,15 @@ module RBI
136
136
  out.string
137
137
  end
138
138
 
139
+ sig { params(v: Printer).void }
140
+ def print_blank_line_before(v)
141
+ previous_node = v.previous_node
142
+ return unless previous_node
143
+ return if previous_node.is_a?(BlankLine)
144
+ return if previous_node.oneline? && oneline?
145
+ v.printn
146
+ end
147
+
139
148
  sig { returns(T::Boolean) }
140
149
  def oneline?
141
150
  true
@@ -171,7 +180,7 @@ module RBI
171
180
  end
172
181
  end
173
182
 
174
- class EmptyComment
183
+ class BlankLine
175
184
  extend T::Sig
176
185
 
177
186
  sig { override.params(v: Printer).void }
@@ -201,8 +210,7 @@ module RBI
201
210
 
202
211
  sig { override.params(v: Printer).void }
203
212
  def accept_printer(v)
204
- previous_node = v.previous_node
205
- v.printn if previous_node && (!previous_node.oneline? || !oneline?)
213
+ print_blank_line_before(v)
206
214
 
207
215
  v.printl("# #{loc}") if loc && v.print_locs
208
216
  v.visit_all(comments)
@@ -295,8 +303,7 @@ module RBI
295
303
 
296
304
  sig { override.params(v: Printer).void }
297
305
  def accept_printer(v)
298
- previous_node = v.previous_node
299
- v.printn if previous_node && (!previous_node.oneline? || !oneline?)
306
+ print_blank_line_before(v)
300
307
 
301
308
  v.printl("# #{loc}") if loc && v.print_locs
302
309
  v.visit_all(comments)
@@ -309,8 +316,7 @@ module RBI
309
316
 
310
317
  sig { override.params(v: Printer).void }
311
318
  def accept_printer(v)
312
- previous_node = v.previous_node
313
- v.printn if previous_node && (!previous_node.oneline? || !oneline?)
319
+ print_blank_line_before(v)
314
320
 
315
321
  v.visit_all(comments)
316
322
  sigs.each { |sig| v.visit(sig) }
@@ -346,8 +352,7 @@ module RBI
346
352
 
347
353
  sig { override.params(v: Printer).void }
348
354
  def accept_printer(v)
349
- previous_node = v.previous_node
350
- v.printn if previous_node && (!previous_node.oneline? || !oneline?)
355
+ print_blank_line_before(v)
351
356
 
352
357
  v.visit_all(comments)
353
358
  v.visit_all(sigs)
@@ -521,8 +526,7 @@ module RBI
521
526
 
522
527
  sig { override.params(v: Printer).void }
523
528
  def accept_printer(v)
524
- previous_node = v.previous_node
525
- v.printn if previous_node && (!previous_node.oneline? || !oneline?)
529
+ print_blank_line_before(v)
526
530
 
527
531
  v.printl("# #{loc}") if loc && v.print_locs
528
532
  v.visit_all(comments)
@@ -543,8 +547,7 @@ module RBI
543
547
 
544
548
  sig { override.params(v: Printer).void }
545
549
  def accept_printer(v)
546
- previous_node = v.previous_node
547
- v.printn if previous_node && (!previous_node.oneline? || !oneline?)
550
+ print_blank_line_before(v)
548
551
 
549
552
  v.printl("# #{loc}") if loc && v.print_locs
550
553
  v.visit_all(comments)
@@ -552,6 +555,47 @@ module RBI
552
555
  end
553
556
  end
554
557
 
558
+ class Send
559
+ extend T::Sig
560
+
561
+ sig { override.params(v: Printer).void }
562
+ def accept_printer(v)
563
+ print_blank_line_before(v)
564
+
565
+ v.printl("# #{loc}") if loc && v.print_locs
566
+ v.visit_all(comments)
567
+ v.printt(method)
568
+ unless args.empty?
569
+ v.print(" ")
570
+ args.each_with_index do |arg, index|
571
+ v.visit(arg)
572
+ v.print(", ") if index < args.size - 1
573
+ end
574
+ end
575
+ v.printn
576
+ end
577
+ end
578
+
579
+ class Arg
580
+ extend T::Sig
581
+
582
+ sig { override.params(v: Printer).void }
583
+ def accept_printer(v)
584
+ v.print(value)
585
+ end
586
+ end
587
+
588
+ class KwArg
589
+ extend T::Sig
590
+
591
+ sig { override.params(v: Printer).void }
592
+ def accept_printer(v)
593
+ v.print(keyword)
594
+ v.print(": ")
595
+ v.print(value)
596
+ end
597
+ end
598
+
555
599
  class Sig
556
600
  extend T::Sig
557
601
 
@@ -659,8 +703,7 @@ module RBI
659
703
 
660
704
  sig { override.params(v: Printer).void }
661
705
  def accept_printer(v)
662
- previous_node = v.previous_node
663
- v.printn if previous_node && (!previous_node.oneline? || !oneline?)
706
+ print_blank_line_before(v)
664
707
 
665
708
  v.printl("# #{loc}") if loc && v.print_locs
666
709
  v.visit_all(comments)
@@ -699,8 +742,7 @@ module RBI
699
742
 
700
743
  sig { override.params(v: Printer).void }
701
744
  def accept_printer(v)
702
- previous_node = v.previous_node
703
- v.printn if previous_node && (!previous_node.oneline? || !oneline?)
745
+ print_blank_line_before(v)
704
746
 
705
747
  v.printl("# #{loc}") if loc && v.print_locs
706
748
  v.visit_all(comments)
@@ -713,8 +755,7 @@ module RBI
713
755
 
714
756
  sig { override.params(v: Printer).void }
715
757
  def accept_printer(v)
716
- previous_node = v.previous_node
717
- v.printn if previous_node && (!previous_node.oneline? || !oneline?)
758
+ print_blank_line_before(v)
718
759
 
719
760
  v.printl("# #{loc}") if loc && v.print_locs
720
761
  v.visit_all(comments)
@@ -0,0 +1,57 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RBI
5
+ module Rewriters
6
+ class Annotate < Visitor
7
+ extend T::Sig
8
+
9
+ sig { params(annotation: String, annotate_scopes: T::Boolean, annotate_properties: T::Boolean).void }
10
+ def initialize(annotation, annotate_scopes: false, annotate_properties: false)
11
+ super()
12
+ @annotation = annotation
13
+ @annotate_scopes = annotate_scopes
14
+ @annotate_properties = annotate_properties
15
+ end
16
+
17
+ sig { override.params(node: T.nilable(Node)).void }
18
+ def visit(node)
19
+ case node
20
+ when Scope
21
+ annotate_node(node) if @annotate_scopes || root?(node)
22
+ when Const, Attr, Method, TStructField, TypeMember
23
+ annotate_node(node) if @annotate_properties
24
+ end
25
+ visit_all(node.nodes) if node.is_a?(Tree)
26
+ end
27
+
28
+ private
29
+
30
+ sig { params(node: NodeWithComments).void }
31
+ def annotate_node(node)
32
+ return if node.annotations.one?(@annotation)
33
+ node.comments << Comment.new("@#{@annotation}")
34
+ end
35
+
36
+ sig { params(node: Node).returns(T::Boolean) }
37
+ def root?(node)
38
+ parent = node.parent_tree
39
+ parent.is_a?(Tree) && parent.parent_tree.nil?
40
+ end
41
+ end
42
+ end
43
+
44
+ class Tree
45
+ extend T::Sig
46
+
47
+ sig { params(annotation: String, annotate_scopes: T::Boolean, annotate_properties: T::Boolean).void }
48
+ def annotate!(annotation, annotate_scopes: false, annotate_properties: false)
49
+ visitor = Rewriters::Annotate.new(
50
+ annotation,
51
+ annotate_scopes: annotate_scopes,
52
+ annotate_properties: annotate_properties
53
+ )
54
+ visitor.visit(self)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,45 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RBI
5
+ module Rewriters
6
+ class Deannotate < Visitor
7
+ extend T::Sig
8
+
9
+ sig { params(annotation: String).void }
10
+ def initialize(annotation)
11
+ super()
12
+ @annotation = annotation
13
+ end
14
+
15
+ sig { override.params(node: T.nilable(Node)).void }
16
+ def visit(node)
17
+ case node
18
+ when Scope, Const, Attr, Method, TStructField, TypeMember
19
+ deannotate_node(node)
20
+ end
21
+ visit_all(node.nodes) if node.is_a?(Tree)
22
+ end
23
+
24
+ private
25
+
26
+ sig { params(node: NodeWithComments).void }
27
+ def deannotate_node(node)
28
+ return unless node.annotations.one?(@annotation)
29
+ node.comments.reject! do |comment|
30
+ comment.text == "@#{@annotation}"
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ class Tree
37
+ extend T::Sig
38
+
39
+ sig { params(annotation: String).void }
40
+ def deannotate!(annotation)
41
+ visitor = Rewriters::Deannotate.new(annotation)
42
+ visitor.visit(self)
43
+ end
44
+ end
45
+ end
@@ -55,6 +55,8 @@ module RBI
55
55
  Group::Kind::TypeMembers
56
56
  when MixesInClassMethods
57
57
  Group::Kind::MixesInClassMethods
58
+ when Send
59
+ Group::Kind::Sends
58
60
  when TStructField
59
61
  Group::Kind::TStructFields
60
62
  when TEnumBlock
@@ -67,6 +69,8 @@ module RBI
67
69
  else
68
70
  Group::Kind::Methods
69
71
  end
72
+ when SingletonClass
73
+ Group::Kind::SingletonClasses
70
74
  when Scope, Const
71
75
  Group::Kind::Consts
72
76
  else
@@ -93,10 +97,12 @@ module RBI
93
97
  Helpers = new
94
98
  TypeMembers = new
95
99
  MixesInClassMethods = new
100
+ Sends = new
96
101
  TStructFields = new
97
102
  TEnums = new
98
103
  Inits = new
99
104
  Methods = new
105
+ SingletonClasses = new
100
106
  Consts = new
101
107
  end
102
108
  end
@@ -47,7 +47,7 @@ module RBI
47
47
  end
48
48
  end
49
49
 
50
- sig { params(left: Tree, right: Tree, left_name: String, right_name: String, keep: Keep).returns(Tree) }
50
+ sig { params(left: Tree, right: Tree, left_name: String, right_name: String, keep: Keep).returns(MergeTree) }
51
51
  def self.merge_trees(left, right, left_name: "left", right_name: "right", keep: Keep::NONE)
52
52
  left.nest_singleton_methods!
53
53
  right.nest_singleton_methods!
@@ -59,7 +59,7 @@ module RBI
59
59
  tree
60
60
  end
61
61
 
62
- sig { returns(Tree) }
62
+ sig { returns(MergeTree) }
63
63
  attr_reader :tree
64
64
 
65
65
  sig { params(left_name: String, right_name: String, keep: Keep).void }
@@ -67,15 +67,15 @@ module RBI
67
67
  @left_name = left_name
68
68
  @right_name = right_name
69
69
  @keep = keep
70
- @tree = T.let(Tree.new, Tree)
70
+ @tree = T.let(MergeTree.new, MergeTree)
71
71
  @scope_stack = T.let([@tree], T::Array[Tree])
72
72
  end
73
73
 
74
- sig { params(tree: Tree).returns(T::Array[Conflict]) }
74
+ sig { params(tree: Tree).void }
75
75
  def merge(tree)
76
76
  v = TreeMerger.new(@tree, left_name: @left_name, right_name: @right_name, keep: @keep)
77
77
  v.visit(tree)
78
- v.conflicts
78
+ @tree.conflicts.concat(v.conflicts)
79
79
  end
80
80
 
81
81
  # Used for logging / error displaying purpose
@@ -314,9 +314,31 @@ module RBI
314
314
  class Tree
315
315
  extend T::Sig
316
316
 
317
- sig { params(other: Tree).returns(Tree) }
318
- def merge(other)
319
- Rewriters::Merge.merge_trees(self, other)
317
+ sig { params(other: Tree, left_name: String, right_name: String, keep: Rewriters::Merge::Keep).returns(MergeTree) }
318
+ def merge(other, left_name: "left", right_name: "right", keep: Rewriters::Merge::Keep::NONE)
319
+ Rewriters::Merge.merge_trees(self, other, left_name: left_name, right_name: right_name, keep: keep)
320
+ end
321
+ end
322
+
323
+ # A tree that _might_ contain conflicts
324
+ class MergeTree < Tree
325
+ extend T::Sig
326
+
327
+ sig { returns(T::Array[Rewriters::Merge::Conflict]) }
328
+ attr_reader :conflicts
329
+
330
+ sig do
331
+ params(
332
+ loc: T.nilable(Loc),
333
+ comments: T::Array[Comment],
334
+ conflicts: T::Array[Rewriters::Merge::Conflict],
335
+ block: T.nilable(T.proc.params(node: Tree).void)
336
+ ).void
337
+ end
338
+ def initialize(loc: nil, comments: [], conflicts: [], &block)
339
+ super(loc: loc, comments: comments)
340
+ @conflicts = conflicts
341
+ block&.call(self)
320
342
  end
321
343
  end
322
344
 
@@ -490,6 +512,15 @@ module RBI
490
512
  end
491
513
  end
492
514
 
515
+ class Send
516
+ extend T::Sig
517
+
518
+ sig { override.params(other: Node).returns(T::Boolean) }
519
+ def compatible_with?(other)
520
+ other.is_a?(Send) && method == other.method && args == other.args
521
+ end
522
+ end
523
+
493
524
  class TStructField
494
525
  extend T::Sig
495
526
 
@@ -0,0 +1,143 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RBI
5
+ module Rewriters
6
+ # Remove all definitions existing in the index from the current tree
7
+ #
8
+ # Let's create an `Index` from two different `Tree`s:
9
+ # ~~~rb
10
+ # tree1 = Parse.parse_string(<<~RBI)
11
+ # class Foo
12
+ # def foo; end
13
+ # end
14
+ # RBI
15
+ #
16
+ # tree2 = Parse.parse_string(<<~RBI)
17
+ # FOO = 10
18
+ # RBI
19
+ #
20
+ # index = Index.index(tree1, tree2)
21
+ # ~~~
22
+ #
23
+ # We can use `RemoveKnownDefinitions` to remove the definitions found in the `index` from the `Tree` to clean:
24
+ # ~~~rb
25
+ # tree_to_clean = Parser.parse_string(<<~RBI)
26
+ # class Foo
27
+ # def foo; end
28
+ # def bar; end
29
+ # end
30
+ # FOO = 10
31
+ # BAR = 42
32
+ # RBI
33
+ #
34
+ # cleaned_tree, operations = RemoveKnownDefinitions.remove(tree_to_clean, index)
35
+ #
36
+ # assert_equal(<<~RBI, cleaned_tree)
37
+ # class Foo
38
+ # def bar; end
39
+ # end
40
+ # BAR = 42
41
+ # RBI
42
+ #
43
+ # assert_equal(<<~OPERATIONS, operations.join("\n"))
44
+ # Deleted ::Foo#foo at -:2:2-2-16 (duplicate from -:2:2-2:16)
45
+ # Deleted ::FOO at -:5:0-5:8 (duplicate from -:1:0-1:8)
46
+ # OPERATIONS
47
+ # ~~~
48
+ class RemoveKnownDefinitions < Visitor
49
+ extend T::Sig
50
+
51
+ sig do
52
+ params(
53
+ tree: RBI::Tree,
54
+ index: RBI::Index
55
+ ).returns([RBI::Tree, T::Array[Operation]])
56
+ end
57
+ def self.remove(tree, index)
58
+ v = RemoveKnownDefinitions.new(index)
59
+ v.visit(tree)
60
+ [tree, v.operations]
61
+ end
62
+
63
+ sig { returns(T::Array[Operation]) }
64
+ attr_reader :operations
65
+
66
+ sig { params(index: RBI::Index).void }
67
+ def initialize(index)
68
+ super()
69
+ @index = index
70
+ @operations = T.let([], T::Array[Operation])
71
+ end
72
+
73
+ sig { params(nodes: T::Array[RBI::Node]).void }
74
+ def visit_all(nodes)
75
+ nodes.dup.each { |node| visit(node) }
76
+ end
77
+
78
+ sig { override.params(node: T.nilable(RBI::Node)).void }
79
+ def visit(node)
80
+ return unless node
81
+
82
+ case node
83
+ when RBI::Scope
84
+ visit_all(node.nodes)
85
+ previous = previous_definition_for(node)
86
+ delete_node(node, previous) if previous && can_delete_node?(node, previous)
87
+ when RBI::Tree
88
+ visit_all(node.nodes)
89
+ when RBI::Indexable
90
+ previous = previous_definition_for(node)
91
+ delete_node(node, previous) if previous && can_delete_node?(node, previous)
92
+ end
93
+ end
94
+
95
+ private
96
+
97
+ sig { params(node: RBI::Indexable).returns(T.nilable(RBI::Node)) }
98
+ def previous_definition_for(node)
99
+ node.index_ids.each do |id|
100
+ previous = @index[id].first
101
+ return previous if previous
102
+ end
103
+ nil
104
+ end
105
+
106
+ sig { params(node: Node, previous: Node).returns(T::Boolean) }
107
+ def can_delete_node?(node, previous)
108
+ return false unless node.class == previous.class
109
+
110
+ case node
111
+ when Scope
112
+ node.empty?
113
+ when Attr
114
+ previous = T.cast(previous, Attr)
115
+ node.names == previous.names && node.sigs == previous.sigs
116
+ when Method
117
+ previous = T.cast(previous, Method)
118
+ node.params == previous.params && node.sigs == previous.sigs
119
+ else
120
+ true
121
+ end
122
+ end
123
+
124
+ sig { params(node: RBI::Node, previous: RBI::Node).void }
125
+ def delete_node(node, previous)
126
+ node.detach
127
+ @operations << Operation.new(deleted_node: node, duplicate_of: previous)
128
+ end
129
+
130
+ class Operation < T::Struct
131
+ extend T::Sig
132
+
133
+ const :deleted_node, RBI::Node
134
+ const :duplicate_of, RBI::Node
135
+
136
+ sig { returns(String) }
137
+ def to_s
138
+ "Deleted #{duplicate_of} at #{deleted_node.loc} (duplicate from #{duplicate_of.loc})"
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
@@ -12,10 +12,16 @@ module RBI
12
12
  visit_all(node.nodes)
13
13
  original_order = node.nodes.map.with_index.to_h
14
14
  node.nodes.sort! do |a, b|
15
+ # First we try to compare the nodes by their node rank (based on the node type)
15
16
  res = node_rank(a) <=> node_rank(b)
16
- res = node_name(a) <=> node_name(b) if res == 0
17
- res = (original_order[a] || 0) <=> (original_order[b] || 0) if res == 0
18
- res || 0
17
+ next res if res != 0 # we can sort the nodes by their rank, let's stop here
18
+
19
+ # Then, if the nodes ranks are the same (res == 0), we try to compare the nodes by their name
20
+ res = node_name(a) <=> node_name(b)
21
+ next res if res && res != 0 # we can sort the nodes by their name, let's stop here
22
+
23
+ # Finally, if the two nodes have the same rank and the same name or at least one node is anonymous then,
24
+ T.must(original_order[a]) <=> T.must(original_order[b]) # we keep the original order
19
25
  end
20
26
  end
21
27
 
@@ -29,19 +35,21 @@ module RBI
29
35
  when Helper then 20
30
36
  when TypeMember then 30
31
37
  when MixesInClassMethods then 40
32
- when TStructField then 50
33
- when TEnumBlock then 60
38
+ when Send then 50
39
+ when TStructField then 60
40
+ when TEnumBlock then 70
34
41
  when Method
35
42
  if node.name == "initialize"
36
- 71
43
+ 81
37
44
  elsif !node.is_singleton
38
- 72
45
+ 82
39
46
  else
40
- 73
47
+ 83
41
48
  end
42
- when Scope, Const then 80
49
+ when SingletonClass then 90
50
+ when Scope, Const then 100
43
51
  else
44
- 100
52
+ 110
45
53
  end
46
54
  end
47
55
 
@@ -52,11 +60,13 @@ module RBI
52
60
  when Group::Kind::Helpers then 1
53
61
  when Group::Kind::TypeMembers then 2
54
62
  when Group::Kind::MixesInClassMethods then 3
55
- when Group::Kind::TStructFields then 4
56
- when Group::Kind::TEnums then 5
57
- when Group::Kind::Inits then 6
58
- when Group::Kind::Methods then 7
59
- when Group::Kind::Consts then 8
63
+ when Group::Kind::Sends then 5
64
+ when Group::Kind::TStructFields then 6
65
+ when Group::Kind::TEnums then 7
66
+ when Group::Kind::Inits then 8
67
+ when Group::Kind::Methods then 9
68
+ when Group::Kind::SingletonClasses then 10
69
+ when Group::Kind::Consts then 11
60
70
  else
61
71
  T.absurd(kind)
62
72
  end
data/lib/rbi/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module RBI
5
- VERSION = "0.0.5"
5
+ VERSION = "0.0.9"
6
6
  end
data/lib/rbi.rb CHANGED
@@ -13,10 +13,13 @@ require "rbi/model"
13
13
  require "rbi/visitor"
14
14
  require "rbi/index"
15
15
  require "rbi/rewriters/add_sig_templates"
16
+ require "rbi/rewriters/annotate"
17
+ require "rbi/rewriters/deannotate"
16
18
  require "rbi/rewriters/merge_trees"
17
19
  require "rbi/rewriters/nest_singleton_methods"
18
20
  require "rbi/rewriters/nest_non_public_methods"
19
21
  require "rbi/rewriters/group_nodes"
22
+ require "rbi/rewriters/remove_known_definitions"
20
23
  require "rbi/rewriters/sort_nodes"
21
24
  require "rbi/parser"
22
25
  require "rbi/printer"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandre Terrasa
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-10-05 00:00:00.000000000 Z
11
+ date: 2021-11-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ast
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: 0.5.9204
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: 0.5.9204
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: unparser
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -83,10 +83,13 @@ files:
83
83
  - lib/rbi/parser.rb
84
84
  - lib/rbi/printer.rb
85
85
  - lib/rbi/rewriters/add_sig_templates.rb
86
+ - lib/rbi/rewriters/annotate.rb
87
+ - lib/rbi/rewriters/deannotate.rb
86
88
  - lib/rbi/rewriters/group_nodes.rb
87
89
  - lib/rbi/rewriters/merge_trees.rb
88
90
  - lib/rbi/rewriters/nest_non_public_methods.rb
89
91
  - lib/rbi/rewriters/nest_singleton_methods.rb
92
+ - lib/rbi/rewriters/remove_known_definitions.rb
90
93
  - lib/rbi/rewriters/sort_nodes.rb
91
94
  - lib/rbi/version.rb
92
95
  - lib/rbi/visitor.rb