rbi 0.0.7 → 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 97c295f819a7f55175bf02dc1722a7a6ad29c82f92c7f9be2636a47652d0b39f
4
- data.tar.gz: f0ab6cb29d0e2a1a0ef78452543258761664d081845b042c0f5f1fc2f74f9773
3
+ metadata.gz: 727196c54429957687b3624d4b5a895e526973b045689a7689edb9cd73c97ca9
4
+ data.tar.gz: 115a6efabd78950aa980d0b03e3b9910f474ad03cdf04f7a87b56ed38bdc30b7
5
5
  SHA512:
6
- metadata.gz: cb97cd66fd4231f3609efede4b98f8590cf7fff1382b4dbae1997161a8b2aed5ed10e6bc388d8388a16dc71792ba5b5ec32561b37f353cc8b77cbfb778542a60
7
- data.tar.gz: a44902b6c3a669c0d706952459b2ebdbe78bbfdcf75ed50b0d16274f9fafa2cba20adb7ce0b70bf2011765edcfe9ea4cf823bb973618e4babd091fdc5e3fafd8
6
+ metadata.gz: 7357ca0bb3e8700a95d1ea6e285e710f3545a9016e36bc0a64fe0682da80328d5c645b4d6cd3d14bba14e7ccd6b9134c225944ce27625de6a26a995f5283d173
7
+ data.tar.gz: 71559ec6d855d46d3ac7bb074dbfd24fa024aaa6563dd241a7c286b66d138bf18f6c6e688ee33df99bdc240cbd6f683ebc068cb5c98b7e2b37dde6d4c9debca8
data/Gemfile CHANGED
@@ -8,6 +8,7 @@ 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)
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
@@ -144,6 +151,16 @@ module RBI
144
151
  end
145
152
  end
146
153
 
154
+ class RequiresAncestor
155
+ extend T::Sig
156
+ include Indexable
157
+
158
+ sig { override.returns(T::Array[String]) }
159
+ def index_ids
160
+ [to_s]
161
+ end
162
+ end
163
+
147
164
  class Helper
148
165
  extend T::Sig
149
166
  include Indexable
@@ -154,6 +171,16 @@ module RBI
154
171
  end
155
172
  end
156
173
 
174
+ class Send
175
+ extend T::Sig
176
+ include Indexable
177
+
178
+ sig { override.returns(T::Array[String]) }
179
+ def index_ids
180
+ ["#{parent_scope&.fully_qualified_name}.#{method}"]
181
+ end
182
+ end
183
+
157
184
  class TStructConst
158
185
  extend T::Sig
159
186
  include Indexable
data/lib/rbi/model.rb CHANGED
@@ -914,6 +914,106 @@ module RBI
914
914
  end
915
915
  end
916
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
+
917
1017
  # Sorbet's sigs
918
1018
 
919
1019
  class Sig < Node
@@ -1261,4 +1361,28 @@ module RBI
1261
1361
  "#{parent_scope&.fully_qualified_name}.mixes_in_class_methods(#{names.join(", ")})"
1262
1362
  end
1263
1363
  end
1364
+
1365
+ class RequiresAncestor < NodeWithComments
1366
+ extend T::Sig
1367
+
1368
+ sig { returns(String) }
1369
+ attr_reader :name
1370
+
1371
+ sig do
1372
+ params(
1373
+ name: String,
1374
+ loc: T.nilable(Loc),
1375
+ comments: T::Array[Comment]
1376
+ ).void
1377
+ end
1378
+ def initialize(name, loc: nil, comments: [])
1379
+ super(loc: loc, comments: comments)
1380
+ @name = name
1381
+ end
1382
+
1383
+ sig { override.returns(String) }
1384
+ def to_s
1385
+ "#{parent_scope&.fully_qualified_name}.requires_ancestor(#{name})"
1386
+ end
1387
+ end
1264
1388
  end
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 << BlankLine.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) }
@@ -297,10 +278,10 @@ module RBI
297
278
  symbols = node.children[2..-1].map { |child| child.children[0] }
298
279
  AttrAccessor.new(*symbols, sigs: current_sigs, loc: loc, comments: comments)
299
280
  when :include
300
- names = node.children[2..-1].map { |child| parse_name(child) }
281
+ names = node.children[2..-1].map { |child| parse_expr(child) }
301
282
  Include.new(*names, loc: loc, comments: comments)
302
283
  when :extend
303
- names = node.children[2..-1].map { |child| parse_name(child) }
284
+ names = node.children[2..-1].map { |child| parse_expr(child) }
304
285
  Extend.new(*names, loc: loc, comments: comments)
305
286
  when :abstract!, :sealed!, :interface!
306
287
  Helper.new(method_name.to_s.delete_suffix("!"), loc: loc, comments: comments)
@@ -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]
@@ -345,6 +344,8 @@ module RBI
345
344
  parse_sig(node)
346
345
  when :enums
347
346
  parse_enum(node)
347
+ when :requires_ancestor
348
+ parse_requires_ancestor(node)
348
349
  else
349
350
  raise ParseError.new("Unsupported block node type `#{name}`", node_loc(node))
350
351
  end
@@ -428,6 +429,14 @@ module RBI
428
429
  enum
429
430
  end
430
431
 
432
+ sig { params(node: AST::Node).returns(RequiresAncestor) }
433
+ def parse_requires_ancestor(node)
434
+ name = parse_name(node.children[2])
435
+ ra = RequiresAncestor.new(name)
436
+ ra.loc = node_loc(node)
437
+ ra
438
+ end
439
+
431
440
  sig { params(node: AST::Node).returns(Loc) }
432
441
  def node_loc(node)
433
442
  Loc.from_ast_loc(@file, node.location)
@@ -435,7 +444,7 @@ module RBI
435
444
 
436
445
  sig { params(node: AST::Node).returns(T::Array[Comment]) }
437
446
  def node_comments(node)
438
- comments = @comments[node.location]
447
+ comments = @nodes_comments_assoc[node.location]
439
448
  return [] unless comments
440
449
  comments.map do |comment|
441
450
  text = comment.text[1..-1].strip
@@ -455,6 +464,60 @@ module RBI
455
464
  @last_sigs.clear
456
465
  sigs
457
466
  end
467
+
468
+ sig { void }
469
+ def assoc_dangling_comments
470
+ last_line = T.let(nil, T.nilable(Integer))
471
+ (@comments - @nodes_comments_assoc.values.flatten).each do |comment|
472
+ comment_line = comment.location.last_line
473
+ text = comment.text[1..-1].strip
474
+ loc = Loc.from_ast_loc(@file, comment.location)
475
+
476
+ if last_line && comment_line > last_line + 1
477
+ # Preserve empty lines in file headers
478
+ tree.comments << BlankLine.new(loc: loc)
479
+ end
480
+
481
+ tree.comments << Comment.new(text, loc: loc)
482
+ last_line = comment_line
483
+ end
484
+ end
485
+
486
+ sig { void }
487
+ def separate_header_comments
488
+ return if @nodes_comments_assoc.empty?
489
+
490
+ keep = []
491
+ node = T.must(@nodes_comments_assoc.keys.first)
492
+ comments = T.must(@nodes_comments_assoc.values.first)
493
+
494
+ last_line = T.let(nil, T.nilable(Integer))
495
+ comments.reverse.each do |comment|
496
+ comment_line = comment.location.last_line
497
+
498
+ break if last_line && comment_line < last_line - 1 ||
499
+ !last_line && comment_line < node.first_line - 1
500
+
501
+ keep << comment
502
+ last_line = comment_line
503
+ end
504
+
505
+ @nodes_comments_assoc[node] = keep.reverse
506
+ end
507
+
508
+ sig { void }
509
+ def set_root_tree_loc
510
+ first_loc = tree.nodes.first&.loc
511
+ last_loc = tree.nodes.last&.loc
512
+
513
+ @tree.loc = Loc.new(
514
+ file: @file,
515
+ begin_line: first_loc&.begin_line || 0,
516
+ begin_column: first_loc&.begin_column || 0,
517
+ end_line: last_loc&.end_line || 0,
518
+ end_column: last_loc&.end_column || 0
519
+ )
520
+ end
458
521
  end
459
522
 
460
523
  class ConstBuilder < ASTVisitor
data/lib/rbi/printer.rb CHANGED
@@ -11,14 +11,28 @@ module RBI
11
11
  sig { returns(T.nilable(Node)) }
12
12
  attr_reader :previous_node
13
13
 
14
- sig { params(out: T.any(IO, StringIO), indent: Integer, print_locs: T::Boolean).void }
15
- def initialize(out: $stdout, indent: 0, print_locs: false)
14
+ sig { returns(Integer) }
15
+ attr_reader :current_indent
16
+
17
+ sig { returns(T.nilable(Integer)) }
18
+ attr_reader :max_line_length
19
+
20
+ sig do
21
+ params(
22
+ out: T.any(IO, StringIO),
23
+ indent: Integer,
24
+ print_locs: T::Boolean,
25
+ max_line_length: T.nilable(Integer)
26
+ ).void
27
+ end
28
+ def initialize(out: $stdout, indent: 0, print_locs: false, max_line_length: nil)
16
29
  super()
17
30
  @out = out
18
31
  @current_indent = indent
19
32
  @print_locs = print_locs
20
33
  @in_visibility_group = T.let(false, T::Boolean)
21
34
  @previous_node = T.let(nil, T.nilable(Node))
35
+ @max_line_length = max_line_length
22
36
  end
23
37
 
24
38
  # Printing
@@ -103,16 +117,23 @@ module RBI
103
117
  end
104
118
  end
105
119
 
106
- sig { params(out: T.any(IO, StringIO), indent: Integer, print_locs: T::Boolean).void }
107
- def print(out: $stdout, indent: 0, print_locs: false)
108
- p = Printer.new(out: out, indent: indent, print_locs: print_locs)
120
+ sig do
121
+ params(
122
+ out: T.any(IO, StringIO),
123
+ indent: Integer,
124
+ print_locs: T::Boolean,
125
+ max_line_length: T.nilable(Integer)
126
+ ).void
127
+ end
128
+ def print(out: $stdout, indent: 0, print_locs: false, max_line_length: nil)
129
+ p = Printer.new(out: out, indent: indent, print_locs: print_locs, max_line_length: max_line_length)
109
130
  p.visit_file(self)
110
131
  end
111
132
 
112
- sig { params(indent: Integer, print_locs: T::Boolean).returns(String) }
113
- def string(indent: 0, print_locs: false)
133
+ sig { params(indent: Integer, print_locs: T::Boolean, max_line_length: T.nilable(Integer)).returns(String) }
134
+ def string(indent: 0, print_locs: false, max_line_length: nil)
114
135
  out = StringIO.new
115
- print(out: out, indent: indent, print_locs: print_locs)
136
+ print(out: out, indent: indent, print_locs: print_locs, max_line_length: max_line_length)
116
137
  out.string
117
138
  end
118
139
  end
@@ -123,16 +144,23 @@ module RBI
123
144
  sig { abstract.params(v: Printer).void }
124
145
  def accept_printer(v); end
125
146
 
126
- sig { params(out: T.any(IO, StringIO), indent: Integer, print_locs: T::Boolean).void }
127
- def print(out: $stdout, indent: 0, print_locs: false)
128
- p = Printer.new(out: out, indent: indent, print_locs: print_locs)
147
+ sig do
148
+ params(
149
+ out: T.any(IO, StringIO),
150
+ indent: Integer,
151
+ print_locs: T::Boolean,
152
+ max_line_length: T.nilable(Integer)
153
+ ).void
154
+ end
155
+ def print(out: $stdout, indent: 0, print_locs: false, max_line_length: nil)
156
+ p = Printer.new(out: out, indent: indent, print_locs: print_locs, max_line_length: max_line_length)
129
157
  p.visit(self)
130
158
  end
131
159
 
132
- sig { params(indent: Integer, print_locs: T::Boolean).returns(String) }
133
- def string(indent: 0, print_locs: false)
160
+ sig { params(indent: Integer, print_locs: T::Boolean, max_line_length: T.nilable(Integer)).returns(String) }
161
+ def string(indent: 0, print_locs: false, max_line_length: nil)
134
162
  out = StringIO.new
135
- print(out: out, indent: indent, print_locs: print_locs)
163
+ print(out: out, indent: indent, print_locs: print_locs, max_line_length: max_line_length)
136
164
  out.string
137
165
  end
138
166
 
@@ -555,72 +583,65 @@ module RBI
555
583
  end
556
584
  end
557
585
 
558
- class Sig
586
+ class Send
559
587
  extend T::Sig
560
588
 
561
589
  sig { override.params(v: Printer).void }
562
590
  def accept_printer(v)
591
+ print_blank_line_before(v)
592
+
563
593
  v.printl("# #{loc}") if loc && v.print_locs
564
- if oneline?
565
- v.printt("sig { ")
566
- else
567
- v.printl("sig do")
568
- v.indent
569
- end
570
- v.print("abstract.") if is_abstract
571
- v.print("override.") if is_override
572
- v.print("overridable.") if is_overridable
573
- unless type_params.empty?
574
- v.print("type_parameters(")
575
- type_params.each_with_index do |param, index|
576
- v.print(":#{param}")
577
- v.print(", ") if index < type_params.length - 1
594
+ v.visit_all(comments)
595
+ v.printt(method)
596
+ unless args.empty?
597
+ v.print(" ")
598
+ args.each_with_index do |arg, index|
599
+ v.visit(arg)
600
+ v.print(", ") if index < args.size - 1
578
601
  end
579
- v.print(").")
580
602
  end
581
- unless params.empty?
582
- if inline_params?
583
- v.print("params(")
584
- params.each_with_index do |param, index|
585
- v.print(", ") if index > 0
586
- v.visit(param)
587
- end
588
- v.print(").")
603
+ v.printn
604
+ end
605
+ end
606
+
607
+ class Arg
608
+ extend T::Sig
609
+
610
+ sig { override.params(v: Printer).void }
611
+ def accept_printer(v)
612
+ v.print(value)
613
+ end
614
+ end
615
+
616
+ class KwArg
617
+ extend T::Sig
618
+
619
+ sig { override.params(v: Printer).void }
620
+ def accept_printer(v)
621
+ v.print(keyword)
622
+ v.print(": ")
623
+ v.print(value)
624
+ end
625
+ end
626
+
627
+ class Sig
628
+ extend T::Sig
629
+
630
+ sig { override.params(v: Printer).void }
631
+ def accept_printer(v)
632
+ v.printl("# #{loc}") if loc && v.print_locs
633
+ max_line_length = v.max_line_length
634
+ if oneline? && max_line_length.nil?
635
+ print_as_line(v)
636
+ elsif max_line_length
637
+ line = string(indent: v.current_indent)
638
+ if line.length <= max_line_length
639
+ v.print(line)
589
640
  else
590
- v.printl("params(")
591
- v.indent
592
- params.each_with_index do |param, pindex|
593
- v.printt
594
- v.visit(param)
595
- v.print(",") if pindex < params.size - 1
596
- param.comments_lines.each_with_index do |comment, cindex|
597
- if cindex == 0
598
- v.print(" ")
599
- else
600
- param.print_comment_leading_space(v, last: pindex == params.size - 1)
601
- end
602
- v.print("# #{comment}")
603
- end
604
- v.printn
605
- end
606
- v.dedent
607
- v.printt(").")
641
+ print_as_block(v)
608
642
  end
609
- end
610
- if return_type && return_type != "void"
611
- v.print("returns(#{return_type})")
612
- else
613
- v.print("void")
614
- end
615
- if checked
616
- v.print(".checked(:#{checked})")
617
- end
618
- if oneline?
619
- v.printn(" }")
620
643
  else
621
- v.printn
622
- v.dedent
623
- v.printl("end")
644
+ print_as_block(v)
624
645
  end
625
646
  end
626
647
 
@@ -633,6 +654,90 @@ module RBI
633
654
  def inline_params?
634
655
  params.all? { |p| p.comments.empty? }
635
656
  end
657
+
658
+ private
659
+
660
+ sig { returns(T::Array[String]) }
661
+ def sig_modifiers
662
+ modifiers = T.let([], T::Array[String])
663
+ modifiers << "abstract" if is_abstract
664
+ modifiers << "override" if is_override
665
+ modifiers << "overridable" if is_overridable
666
+ modifiers << "type_parameters(#{type_params.map { |type| ":#{type}" }.join(", ")})" if type_params.any?
667
+ modifiers << "checked(:#{checked})" if checked
668
+ modifiers
669
+ end
670
+
671
+ sig { params(v: Printer).void }
672
+ def print_as_line(v)
673
+ v.printt("sig { ")
674
+ sig_modifiers.each do |modifier|
675
+ v.print("#{modifier}.")
676
+ end
677
+ unless params.empty?
678
+ v.print("params(")
679
+ params.each_with_index do |param, index|
680
+ v.print(", ") if index > 0
681
+ v.visit(param)
682
+ end
683
+ v.print(").")
684
+ end
685
+ if return_type && return_type != "void"
686
+ v.print("returns(#{return_type})")
687
+ else
688
+ v.print("void")
689
+ end
690
+ v.printn(" }")
691
+ end
692
+
693
+ sig { params(v: Printer).void }
694
+ def print_as_block(v)
695
+ modifiers = sig_modifiers
696
+
697
+ v.printl("sig do")
698
+ v.indent
699
+ if modifiers.any?
700
+ v.printl(T.must(modifiers.first))
701
+ v.indent
702
+ modifiers[1..]&.each do |modifier|
703
+ v.printl(".#{modifier}")
704
+ end
705
+ end
706
+
707
+ if params.any?
708
+ v.printt
709
+ v.print(".") if modifiers.any?
710
+ v.printn("params(")
711
+ v.indent
712
+ params.each_with_index do |param, pindex|
713
+ v.printt
714
+ v.visit(param)
715
+ v.print(",") if pindex < params.size - 1
716
+ param.comments_lines.each_with_index do |comment, cindex|
717
+ if cindex == 0
718
+ v.print(" ")
719
+ else
720
+ param.print_comment_leading_space(v, last: pindex == params.size - 1)
721
+ end
722
+ v.print("# #{comment}")
723
+ end
724
+ v.printn
725
+ end
726
+ v.dedent
727
+ v.printt(")")
728
+ end
729
+ v.printt if params.empty?
730
+ v.print(".") if modifiers.any? || params.any?
731
+ if return_type && return_type != "void"
732
+ v.print("returns(#{return_type})")
733
+ else
734
+ v.print("void")
735
+ end
736
+ v.printn
737
+ v.dedent
738
+ v.dedent if modifiers.any?
739
+ v.printl("end")
740
+ end
636
741
  end
637
742
 
638
743
  class SigParam
@@ -753,4 +858,17 @@ module RBI
753
858
  false
754
859
  end
755
860
  end
861
+
862
+ class RequiresAncestor
863
+ extend T::Sig
864
+
865
+ sig { override.params(v: Printer).void }
866
+ def accept_printer(v)
867
+ print_blank_line_before(v)
868
+
869
+ v.printl("# #{loc}") if loc && v.print_locs
870
+ v.visit_all(comments)
871
+ v.printl("requires_ancestor { #{name} }")
872
+ end
873
+ end
756
874
  end
@@ -49,12 +49,18 @@ module RBI
49
49
  case self
50
50
  when Include, Extend
51
51
  Group::Kind::Mixins
52
+ when RequiresAncestor
53
+ Group::Kind::RequiredAncestors
52
54
  when Helper
53
55
  Group::Kind::Helpers
54
56
  when TypeMember
55
57
  Group::Kind::TypeMembers
56
58
  when MixesInClassMethods
57
59
  Group::Kind::MixesInClassMethods
60
+ when Send
61
+ Group::Kind::Sends
62
+ when Attr
63
+ Group::Kind::Attrs
58
64
  when TStructField
59
65
  Group::Kind::TStructFields
60
66
  when TEnumBlock
@@ -67,6 +73,8 @@ module RBI
67
73
  else
68
74
  Group::Kind::Methods
69
75
  end
76
+ when SingletonClass
77
+ Group::Kind::SingletonClasses
70
78
  when Scope, Const
71
79
  Group::Kind::Consts
72
80
  else
@@ -90,13 +98,17 @@ module RBI
90
98
  class Kind < T::Enum
91
99
  enums do
92
100
  Mixins = new
101
+ RequiredAncestors = new
93
102
  Helpers = new
94
103
  TypeMembers = new
95
104
  MixesInClassMethods = new
105
+ Sends = new
106
+ Attrs = new
96
107
  TStructFields = new
97
108
  TEnums = new
98
109
  Inits = new
99
110
  Methods = new
111
+ SingletonClasses = new
100
112
  Consts = new
101
113
  end
102
114
  end
@@ -512,6 +512,15 @@ module RBI
512
512
  end
513
513
  end
514
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
+
515
524
  class TStructField
516
525
  extend T::Sig
517
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: Tree,
54
+ index: Index
55
+ ).returns([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: 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[Node]).void }
74
+ def visit_all(nodes)
75
+ nodes.dup.each { |node| visit(node) }
76
+ end
77
+
78
+ sig { override.params(node: T.nilable(Node)).void }
79
+ def visit(node)
80
+ return unless node
81
+
82
+ case node
83
+ when 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 Tree
88
+ visit_all(node.nodes)
89
+ when 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: Indexable).returns(T.nilable(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: Node, previous: 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, Node
134
+ const :duplicate_of, 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
@@ -8,14 +8,22 @@ module RBI
8
8
 
9
9
  sig { override.params(node: T.nilable(Node)).void }
10
10
  def visit(node)
11
+ sort_node_names!(node) if node
12
+
11
13
  return unless node.is_a?(Tree)
12
14
  visit_all(node.nodes)
13
15
  original_order = node.nodes.map.with_index.to_h
14
16
  node.nodes.sort! do |a, b|
17
+ # First we try to compare the nodes by their node rank (based on the node type)
15
18
  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
19
+ next res if res != 0 # we can sort the nodes by their rank, let's stop here
20
+
21
+ # Then, if the nodes ranks are the same (res == 0), we try to compare the nodes by their name
22
+ res = node_name(a) <=> node_name(b)
23
+ next res if res && res != 0 # we can sort the nodes by their name, let's stop here
24
+
25
+ # Finally, if the two nodes have the same rank and the same name or at least one node is anonymous then,
26
+ T.must(original_order[a]) <=> T.must(original_order[b]) # we keep the original order
19
27
  end
20
28
  end
21
29
 
@@ -26,22 +34,26 @@ module RBI
26
34
  case node
27
35
  when Group then group_rank(node.kind)
28
36
  when Include, Extend then 10
37
+ when RequiresAncestor then 15
29
38
  when Helper then 20
30
39
  when TypeMember then 30
31
40
  when MixesInClassMethods then 40
32
- when TStructField then 50
33
- when TEnumBlock then 60
41
+ when Send then 50
42
+ when TStructField then 60
43
+ when TEnumBlock then 70
44
+ when Attr then 75
34
45
  when Method
35
46
  if node.name == "initialize"
36
- 71
47
+ 81
37
48
  elsif !node.is_singleton
38
- 72
49
+ 82
39
50
  else
40
- 73
51
+ 83
41
52
  end
42
- when Scope, Const then 80
53
+ when SingletonClass then 90
54
+ when Scope, Const then 100
43
55
  else
44
- 100
56
+ 110
45
57
  end
46
58
  end
47
59
 
@@ -49,14 +61,18 @@ module RBI
49
61
  def group_rank(kind)
50
62
  case kind
51
63
  when Group::Kind::Mixins then 0
52
- when Group::Kind::Helpers then 1
53
- when Group::Kind::TypeMembers then 2
54
- 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
64
+ when Group::Kind::RequiredAncestors then 1
65
+ when Group::Kind::Helpers then 2
66
+ when Group::Kind::TypeMembers then 3
67
+ when Group::Kind::MixesInClassMethods then 4
68
+ when Group::Kind::Sends then 5
69
+ when Group::Kind::TStructFields then 6
70
+ when Group::Kind::TEnums then 7
71
+ when Group::Kind::Attrs then 8
72
+ when Group::Kind::Inits then 9
73
+ when Group::Kind::Methods then 10
74
+ when Group::Kind::SingletonClasses then 11
75
+ when Group::Kind::Consts then 12
60
76
  else
61
77
  T.absurd(kind)
62
78
  end
@@ -65,8 +81,18 @@ module RBI
65
81
  sig { params(node: Node).returns(T.nilable(String)) }
66
82
  def node_name(node)
67
83
  case node
68
- when Module, Class, Struct, Const, Method, Helper, TStructField
84
+ when Module, Class, Struct, Const, Method, Helper, TStructField, RequiresAncestor
69
85
  node.name
86
+ when Attr
87
+ node.names.first.to_s
88
+ end
89
+ end
90
+
91
+ sig { params(node: Node).void }
92
+ def sort_node_names!(node)
93
+ case node
94
+ when Attr
95
+ node.names.sort!
70
96
  end
71
97
  end
72
98
  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.7"
5
+ VERSION = "0.0.11"
6
6
  end
data/lib/rbi.rb CHANGED
@@ -19,6 +19,7 @@ require "rbi/rewriters/merge_trees"
19
19
  require "rbi/rewriters/nest_singleton_methods"
20
20
  require "rbi/rewriters/nest_non_public_methods"
21
21
  require "rbi/rewriters/group_nodes"
22
+ require "rbi/rewriters/remove_known_definitions"
22
23
  require "rbi/rewriters/sort_nodes"
23
24
  require "rbi/parser"
24
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.7
4
+ version: 0.0.11
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-11-16 00:00:00.000000000 Z
11
+ date: 2022-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ast
@@ -89,6 +89,7 @@ files:
89
89
  - lib/rbi/rewriters/merge_trees.rb
90
90
  - lib/rbi/rewriters/nest_non_public_methods.rb
91
91
  - lib/rbi/rewriters/nest_singleton_methods.rb
92
+ - lib/rbi/rewriters/remove_known_definitions.rb
92
93
  - lib/rbi/rewriters/sort_nodes.rb
93
94
  - lib/rbi/version.rb
94
95
  - lib/rbi/visitor.rb