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 +4 -4
- data/Gemfile +2 -1
- data/lib/rbi/index.rb +32 -5
- data/lib/rbi/model.rb +140 -3
- data/lib/rbi/parser.rb +112 -49
- data/lib/rbi/printer.rb +194 -83
- data/lib/rbi/rewriters/annotate.rb +57 -0
- data/lib/rbi/rewriters/deannotate.rb +45 -0
- data/lib/rbi/rewriters/group_nodes.rb +12 -0
- data/lib/rbi/rewriters/merge_trees.rb +39 -8
- data/lib/rbi/rewriters/remove_known_definitions.rb +143 -0
- data/lib/rbi/rewriters/sort_nodes.rb +45 -19
- data/lib/rbi/version.rb +1 -1
- data/lib/rbi.rb +3 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b81ff4cdb9cb4776c5592c2838906188b7db79620511303a209d6d7ae395c491
|
4
|
+
data.tar.gz: 9da4440f160522287e3e9aca18b4d535759b11ebe59fae994d6f51be473e1e2f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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",
|
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(
|
33
|
-
def index(
|
34
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
137
|
+
attr_accessor :root
|
130
138
|
|
131
139
|
sig { returns(T.nilable(String)) }
|
132
|
-
|
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.
|
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::
|
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|
|
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|
|
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
|
-
|
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 = @
|
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
|