rbi 0.0.6 → 0.0.10

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