rbi 0.0.7 → 0.0.11

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