rbi 0.0.6 → 0.0.10

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: 1232511f52f703972d6946a815937759221dcae1b15f762dad7e214760c5551b
4
- data.tar.gz: 66df06e9f8f8975b6260eca5b3a8756a5126fa6f76e3ffcdd2a9d58cf4f179a2
3
+ metadata.gz: b81ff4cdb9cb4776c5592c2838906188b7db79620511303a209d6d7ae395c491
4
+ data.tar.gz: 9da4440f160522287e3e9aca18b4d535759b11ebe59fae994d6f51be473e1e2f
5
5
  SHA512:
6
- metadata.gz: ec7d1e8a75d67d1ff57f13d29e60e25c71f650ef0b948e0b617ab9bed4a4fc7ccbf0cb44d4746532077378dfc848a8eef9a4e244e082e7aef3e6971036a8f7c5
7
- data.tar.gz: ac6d329c267dd116df762972b7baa4f3d43277b4a26e3c05e42290a55aca33182149d9361ca4622d50bc63587661f4d99af284626ee72f7f809f8d7f3c78c251
6
+ metadata.gz: c7b23d94ec81f396057fe0aeb60f75d46799241ec61e097096f6b65cd9d1730fe6ab3bab0442e2f44c62c82e31481d5c26d127b84715ec8fd74fcee939d96857
7
+ data.tar.gz: 1fee917b50d5931574db3159349765d5a0d3b50ed12628afdf458eb011fd37bd140025588fdceb2124afc3ff7c5e8073b45bdd2ecfc3eb601ee2be6788f0fd29
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
16
  gem("sorbet", ">= 0.5.9204", require: false)
16
- gem("tapioca", require: false, github: "Shopify/tapioca", branch: "master")
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
@@ -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
@@ -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
@@ -1248,4 +1361,28 @@ module RBI
1248
1361
  "#{parent_scope&.fully_qualified_name}.mixes_in_class_methods(#{names.join(", ")})"
1249
1362
  end
1250
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
1251
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 << 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) }
@@ -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