rbi 0.0.2 → 0.0.6
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/README.md +1 -1
- data/lib/rbi/model.rb +429 -57
- data/lib/rbi/parser.rb +207 -88
- data/lib/rbi/printer.rb +148 -35
- data/lib/rbi/rewriters/add_sig_templates.rb +71 -0
- data/lib/rbi/rewriters/merge_trees.rb +50 -8
- data/lib/rbi/rewriters/nest_non_public_methods.rb +5 -5
- data/lib/rbi/rewriters/sort_nodes.rb +16 -16
- data/lib/rbi/version.rb +1 -2
- data/lib/rbi.rb +1 -0
- metadata +6 -19
data/lib/rbi/parser.rb
CHANGED
@@ -1,13 +1,24 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "
|
4
|
+
require "parser"
|
5
5
|
|
6
6
|
module RBI
|
7
|
-
class
|
7
|
+
class ParseError < StandardError
|
8
8
|
extend T::Sig
|
9
9
|
|
10
|
-
|
10
|
+
sig { returns(Loc) }
|
11
|
+
attr_reader :location
|
12
|
+
|
13
|
+
sig { params(message: String, location: Loc).void }
|
14
|
+
def initialize(message, location)
|
15
|
+
super(message)
|
16
|
+
@location = location
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Parser
|
21
|
+
extend T::Sig
|
11
22
|
|
12
23
|
# opt-in to most recent AST format
|
13
24
|
::Parser::Builders::Default.emit_lambda = true
|
@@ -16,6 +27,12 @@ module RBI
|
|
16
27
|
::Parser::Builders::Default.emit_index = true
|
17
28
|
::Parser::Builders::Default.emit_arg_inside_procarg0 = true
|
18
29
|
|
30
|
+
sig { void }
|
31
|
+
def initialize
|
32
|
+
# Delay load unparser and only if it has not been loaded already.
|
33
|
+
require "unparser" unless defined?(::Unparser)
|
34
|
+
end
|
35
|
+
|
19
36
|
sig { params(string: String).returns(Tree) }
|
20
37
|
def self.parse_string(string)
|
21
38
|
Parser.new.parse_string(string)
|
@@ -29,15 +46,11 @@ module RBI
|
|
29
46
|
sig { params(string: String).returns(Tree) }
|
30
47
|
def parse_string(string)
|
31
48
|
parse(string, file: "-")
|
32
|
-
rescue ::Parser::SyntaxError => e
|
33
|
-
raise Error, e.message
|
34
49
|
end
|
35
50
|
|
36
51
|
sig { params(path: String).returns(Tree) }
|
37
52
|
def parse_file(path)
|
38
53
|
parse(::File.read(path), file: path)
|
39
|
-
rescue ::Parser::SyntaxError => e
|
40
|
-
raise Error, e.message
|
41
54
|
end
|
42
55
|
|
43
56
|
private
|
@@ -47,9 +60,12 @@ module RBI
|
|
47
60
|
node, comments = Unparser.parse_with_comments(content)
|
48
61
|
assoc = ::Parser::Source::Comment.associate_locations(node, comments)
|
49
62
|
builder = TreeBuilder.new(file: file, comments: assoc)
|
63
|
+
builder.separate_header_comments
|
50
64
|
builder.visit(node)
|
51
65
|
builder.assoc_dangling_comments(comments)
|
52
66
|
builder.tree
|
67
|
+
rescue ::Parser::SyntaxError => e
|
68
|
+
raise ParseError.new(e.message, Loc.from_ast_loc(file, e.diagnostic.location))
|
53
69
|
end
|
54
70
|
end
|
55
71
|
|
@@ -70,12 +86,12 @@ module RBI
|
|
70
86
|
private
|
71
87
|
|
72
88
|
sig { params(node: AST::Node).returns(String) }
|
73
|
-
def
|
89
|
+
def parse_name(node)
|
74
90
|
T.must(ConstBuilder.visit(node))
|
75
91
|
end
|
76
92
|
|
77
93
|
sig { params(node: AST::Node).returns(String) }
|
78
|
-
def
|
94
|
+
def parse_expr(node)
|
79
95
|
Unparser.unparse(node)
|
80
96
|
end
|
81
97
|
end
|
@@ -89,10 +105,10 @@ module RBI
|
|
89
105
|
sig do
|
90
106
|
params(
|
91
107
|
file: String,
|
92
|
-
comments: T
|
108
|
+
comments: T::Hash[::Parser::Source::Map, T::Array[::Parser::Source::Comment]]
|
93
109
|
).void
|
94
110
|
end
|
95
|
-
def initialize(file:, comments:
|
111
|
+
def initialize(file:, comments: {})
|
96
112
|
super()
|
97
113
|
@file = file
|
98
114
|
@comments = comments
|
@@ -106,130 +122,171 @@ module RBI
|
|
106
122
|
return unless node.is_a?(AST::Node)
|
107
123
|
case node.type
|
108
124
|
when :module, :class, :sclass
|
109
|
-
|
125
|
+
scope = parse_scope(node)
|
126
|
+
current_scope << scope
|
127
|
+
@scopes_stack << scope
|
128
|
+
visit_all(node.children)
|
129
|
+
@scopes_stack.pop
|
110
130
|
when :casgn
|
111
|
-
|
131
|
+
current_scope << parse_const_assign(node)
|
112
132
|
when :def, :defs
|
113
|
-
|
133
|
+
current_scope << parse_def(node)
|
114
134
|
when :send
|
115
|
-
|
135
|
+
node = parse_send(node)
|
136
|
+
current_scope << node if node
|
116
137
|
when :block
|
117
|
-
|
138
|
+
node = parse_block(node)
|
139
|
+
if node.is_a?(Sig)
|
140
|
+
@last_sigs << node
|
141
|
+
elsif node
|
142
|
+
current_scope << node
|
143
|
+
end
|
118
144
|
else
|
119
145
|
visit_all(node.children)
|
120
146
|
end
|
121
147
|
end
|
122
148
|
|
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
|
+
|
123
171
|
sig { params(comments: T::Array[::Parser::Source::Comment]).void }
|
124
172
|
def assoc_dangling_comments(comments)
|
125
|
-
|
126
|
-
comments.each do |comment|
|
173
|
+
last_line = T.let(nil, T.nilable(Integer))
|
174
|
+
(comments - @comments.values.flatten).each do |comment|
|
175
|
+
comment_line = comment.location.last_line
|
127
176
|
text = comment.text[1..-1].strip
|
128
|
-
loc =
|
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
|
+
|
129
184
|
tree.comments << Comment.new(text, loc: loc)
|
185
|
+
last_line = comment_line
|
130
186
|
end
|
131
187
|
end
|
132
188
|
|
133
189
|
private
|
134
190
|
|
135
|
-
sig { params(node: AST::Node).
|
136
|
-
def
|
191
|
+
sig { params(node: AST::Node).returns(Scope) }
|
192
|
+
def parse_scope(node)
|
137
193
|
loc = node_loc(node)
|
138
194
|
comments = node_comments(node)
|
139
195
|
|
140
|
-
|
196
|
+
case node.type
|
141
197
|
when :module
|
142
|
-
name =
|
198
|
+
name = parse_name(node.children[0])
|
143
199
|
Module.new(name, loc: loc, comments: comments)
|
144
200
|
when :class
|
145
|
-
name =
|
201
|
+
name = parse_name(node.children[0])
|
146
202
|
superclass_name = ConstBuilder.visit(node.children[1])
|
147
203
|
Class.new(name, superclass_name: superclass_name, loc: loc, comments: comments)
|
148
204
|
when :sclass
|
149
205
|
SingletonClass.new(loc: loc, comments: comments)
|
150
206
|
else
|
151
|
-
raise "Unsupported node
|
207
|
+
raise ParseError.new("Unsupported scope node type `#{node.type}`", loc)
|
152
208
|
end
|
153
|
-
|
209
|
+
end
|
154
210
|
|
155
|
-
|
156
|
-
|
157
|
-
|
211
|
+
sig { params(node: AST::Node).returns(RBI::Node) }
|
212
|
+
def parse_const_assign(node)
|
213
|
+
node_value = node.children[2]
|
214
|
+
if struct_definition?(node_value)
|
215
|
+
parse_struct(node)
|
216
|
+
else
|
217
|
+
name = parse_name(node)
|
218
|
+
value = parse_expr(node_value)
|
219
|
+
loc = node_loc(node)
|
220
|
+
comments = node_comments(node)
|
221
|
+
Const.new(name, value, loc: loc, comments: comments)
|
222
|
+
end
|
158
223
|
end
|
159
224
|
|
160
|
-
sig { params(node: AST::Node).
|
161
|
-
def
|
162
|
-
name = visit_name(node)
|
163
|
-
value = visit_expr(node.children[2])
|
225
|
+
sig { params(node: AST::Node).returns(Method) }
|
226
|
+
def parse_def(node)
|
164
227
|
loc = node_loc(node)
|
165
|
-
comments = node_comments(node)
|
166
|
-
|
167
|
-
current_scope << Const.new(name, value, loc: loc, comments: comments)
|
168
|
-
end
|
169
228
|
|
170
|
-
|
171
|
-
def visit_def(node)
|
172
|
-
current_scope << case node.type
|
229
|
+
case node.type
|
173
230
|
when :def
|
174
231
|
Method.new(
|
175
232
|
node.children[0].to_s,
|
176
|
-
params: node.children[1].children.map { |child|
|
233
|
+
params: node.children[1].children.map { |child| parse_param(child) },
|
177
234
|
sigs: current_sigs,
|
178
|
-
loc:
|
235
|
+
loc: loc,
|
179
236
|
comments: node_comments(node)
|
180
237
|
)
|
181
238
|
when :defs
|
182
239
|
Method.new(
|
183
240
|
node.children[1].to_s,
|
184
|
-
params: node.children[2].children.map { |child|
|
241
|
+
params: node.children[2].children.map { |child| parse_param(child) },
|
185
242
|
is_singleton: true,
|
186
243
|
sigs: current_sigs,
|
187
|
-
loc:
|
244
|
+
loc: loc,
|
188
245
|
comments: node_comments(node)
|
189
246
|
)
|
190
247
|
else
|
191
|
-
raise "Unsupported node
|
248
|
+
raise ParseError.new("Unsupported def node type `#{node.type}`", loc)
|
192
249
|
end
|
193
250
|
end
|
194
251
|
|
195
252
|
sig { params(node: AST::Node).returns(Param) }
|
196
|
-
def
|
253
|
+
def parse_param(node)
|
197
254
|
name = node.children[0].to_s
|
198
255
|
loc = node_loc(node)
|
199
256
|
comments = node_comments(node)
|
200
257
|
|
201
258
|
case node.type
|
202
259
|
when :arg
|
203
|
-
|
260
|
+
ReqParam.new(name, loc: loc, comments: comments)
|
204
261
|
when :optarg
|
205
|
-
value =
|
262
|
+
value = parse_expr(node.children[1])
|
206
263
|
OptParam.new(name, value, loc: loc, comments: comments)
|
207
264
|
when :restarg
|
208
265
|
RestParam.new(name, loc: loc, comments: comments)
|
209
266
|
when :kwarg
|
210
267
|
KwParam.new(name, loc: loc, comments: comments)
|
211
268
|
when :kwoptarg
|
212
|
-
value =
|
269
|
+
value = parse_expr(node.children[1])
|
213
270
|
KwOptParam.new(name, value, loc: loc, comments: comments)
|
214
271
|
when :kwrestarg
|
215
272
|
KwRestParam.new(name, loc: loc, comments: comments)
|
216
273
|
when :blockarg
|
217
274
|
BlockParam.new(name, loc: loc, comments: comments)
|
218
275
|
else
|
219
|
-
raise "Unsupported node
|
276
|
+
raise ParseError.new("Unsupported param node type `#{node.type}`", loc)
|
220
277
|
end
|
221
278
|
end
|
222
279
|
|
223
|
-
sig { params(node: AST::Node).
|
224
|
-
def
|
280
|
+
sig { params(node: AST::Node).returns(T.nilable(RBI::Node)) }
|
281
|
+
def parse_send(node)
|
225
282
|
recv = node.children[0]
|
226
|
-
return if recv && recv != :self
|
283
|
+
return nil if recv && recv != :self
|
227
284
|
|
228
285
|
method_name = node.children[1]
|
229
286
|
loc = node_loc(node)
|
230
287
|
comments = node_comments(node)
|
231
288
|
|
232
|
-
|
289
|
+
case method_name
|
233
290
|
when :attr_reader
|
234
291
|
symbols = node.children[2..-1].map { |child| child.children[0] }
|
235
292
|
AttrReader.new(*symbols, sigs: current_sigs, loc: loc, comments: comments)
|
@@ -240,53 +297,114 @@ module RBI
|
|
240
297
|
symbols = node.children[2..-1].map { |child| child.children[0] }
|
241
298
|
AttrAccessor.new(*symbols, sigs: current_sigs, loc: loc, comments: comments)
|
242
299
|
when :include
|
243
|
-
names = node.children[2..-1].map { |child|
|
300
|
+
names = node.children[2..-1].map { |child| parse_name(child) }
|
244
301
|
Include.new(*names, loc: loc, comments: comments)
|
245
302
|
when :extend
|
246
|
-
names = node.children[2..-1].map { |child|
|
303
|
+
names = node.children[2..-1].map { |child| parse_name(child) }
|
247
304
|
Extend.new(*names, loc: loc, comments: comments)
|
248
305
|
when :abstract!, :sealed!, :interface!
|
249
306
|
Helper.new(method_name.to_s.delete_suffix("!"), loc: loc, comments: comments)
|
250
307
|
when :mixes_in_class_methods
|
251
|
-
names = node.children[2..-1].map { |child|
|
308
|
+
names = node.children[2..-1].map { |child| parse_name(child) }
|
252
309
|
MixesInClassMethods.new(*names, loc: loc, comments: comments)
|
253
310
|
when :public, :protected, :private
|
254
|
-
Visibility.new(method_name, loc: loc)
|
311
|
+
visibility = Visibility.new(method_name, loc: loc)
|
312
|
+
nested_node = node.children[2]
|
313
|
+
case nested_node&.type
|
314
|
+
when :def, :defs
|
315
|
+
method = parse_def(nested_node)
|
316
|
+
method.visibility = visibility
|
317
|
+
method
|
318
|
+
when :send
|
319
|
+
snode = parse_send(nested_node)
|
320
|
+
raise ParseError.new("Unexpected token `private` before `#{nested_node.type}`", loc) unless snode.is_a?(Attr)
|
321
|
+
snode.visibility = visibility
|
322
|
+
snode
|
323
|
+
when nil
|
324
|
+
visibility
|
325
|
+
else
|
326
|
+
raise ParseError.new("Unexpected token `private` before `#{nested_node.type}`", loc)
|
327
|
+
end
|
255
328
|
when :prop
|
256
|
-
name, type, default_value =
|
329
|
+
name, type, default_value = parse_tstruct_prop(node)
|
257
330
|
TStructProp.new(name, type, default: default_value, loc: loc, comments: comments)
|
258
331
|
when :const
|
259
|
-
name, type, default_value =
|
332
|
+
name, type, default_value = parse_tstruct_prop(node)
|
260
333
|
TStructConst.new(name, type, default: default_value, loc: loc, comments: comments)
|
261
334
|
else
|
262
|
-
raise "Unsupported
|
335
|
+
raise ParseError.new("Unsupported send node with name `#{method_name}`", loc)
|
263
336
|
end
|
264
337
|
end
|
265
338
|
|
266
|
-
sig { params(node: AST::Node).
|
267
|
-
def
|
339
|
+
sig { params(node: AST::Node).returns(T.nilable(RBI::Node)) }
|
340
|
+
def parse_block(node)
|
268
341
|
name = node.children[0].children[1]
|
269
342
|
|
270
343
|
case name
|
271
344
|
when :sig
|
272
|
-
|
345
|
+
parse_sig(node)
|
273
346
|
when :enums
|
274
|
-
|
347
|
+
parse_enum(node)
|
275
348
|
else
|
276
|
-
raise "Unsupported node
|
349
|
+
raise ParseError.new("Unsupported block node type `#{name}`", node_loc(node))
|
277
350
|
end
|
278
351
|
end
|
279
352
|
|
353
|
+
sig { params(node: AST::Node).returns(T::Boolean) }
|
354
|
+
def struct_definition?(node)
|
355
|
+
(node.type == :send && node.children[0]&.type == :const && node.children[0].children[1] == :Struct) ||
|
356
|
+
(node.type == :block && struct_definition?(node.children[0]))
|
357
|
+
end
|
358
|
+
|
359
|
+
sig { params(node: AST::Node).returns(RBI::Struct) }
|
360
|
+
def parse_struct(node)
|
361
|
+
name = parse_name(node)
|
362
|
+
loc = node_loc(node)
|
363
|
+
comments = node_comments(node)
|
364
|
+
|
365
|
+
send = node.children[2]
|
366
|
+
body = []
|
367
|
+
|
368
|
+
if send.type == :block
|
369
|
+
if send.children[2].type == :begin
|
370
|
+
body = send.children[2].children
|
371
|
+
else
|
372
|
+
body << send.children[2]
|
373
|
+
end
|
374
|
+
send = send.children[0]
|
375
|
+
end
|
376
|
+
|
377
|
+
members = []
|
378
|
+
keyword_init = T.let(false, T::Boolean)
|
379
|
+
send.children[2..].each do |child|
|
380
|
+
if child.type == :sym
|
381
|
+
members << child.children[0]
|
382
|
+
elsif child.type == :kwargs
|
383
|
+
pair = child.children[0]
|
384
|
+
if pair.children[0].children[0] == :keyword_init
|
385
|
+
keyword_init = true if pair.children[1].type == :true
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
struct = Struct.new(name, members: members, keyword_init: keyword_init, loc: loc, comments: comments)
|
391
|
+
@scopes_stack << struct
|
392
|
+
visit_all(body)
|
393
|
+
@scopes_stack.pop
|
394
|
+
|
395
|
+
struct
|
396
|
+
end
|
397
|
+
|
280
398
|
sig { params(node: AST::Node).returns([String, String, T.nilable(String)]) }
|
281
|
-
def
|
399
|
+
def parse_tstruct_prop(node)
|
282
400
|
name = node.children[2].children[0].to_s
|
283
|
-
type =
|
401
|
+
type = parse_expr(node.children[3])
|
284
402
|
has_default = node.children[4]
|
285
403
|
&.children&.fetch(0, nil)
|
286
404
|
&.children&.fetch(0, nil)
|
287
405
|
&.children&.fetch(0, nil) == :default
|
288
406
|
default_value = if has_default
|
289
|
-
|
407
|
+
parse_expr(node.children.fetch(4, nil)
|
290
408
|
&.children&.fetch(0, nil)
|
291
409
|
&.children&.fetch(1, nil))
|
292
410
|
end
|
@@ -294,17 +412,17 @@ module RBI
|
|
294
412
|
end
|
295
413
|
|
296
414
|
sig { params(node: AST::Node).returns(Sig) }
|
297
|
-
def
|
415
|
+
def parse_sig(node)
|
298
416
|
sig = SigBuilder.build(node)
|
299
417
|
sig.loc = node_loc(node)
|
300
418
|
sig
|
301
419
|
end
|
302
420
|
|
303
421
|
sig { params(node: AST::Node).returns(TEnumBlock) }
|
304
|
-
def
|
422
|
+
def parse_enum(node)
|
305
423
|
enum = TEnumBlock.new
|
306
424
|
node.children[2].children.each do |child|
|
307
|
-
enum <<
|
425
|
+
enum << parse_name(child)
|
308
426
|
end
|
309
427
|
enum.loc = node_loc(node)
|
310
428
|
enum
|
@@ -312,28 +430,16 @@ module RBI
|
|
312
430
|
|
313
431
|
sig { params(node: AST::Node).returns(Loc) }
|
314
432
|
def node_loc(node)
|
315
|
-
|
316
|
-
end
|
317
|
-
|
318
|
-
sig { params(ast_loc: ::Parser::Source::Map).returns(Loc) }
|
319
|
-
def ast_to_rbi_loc(ast_loc)
|
320
|
-
Loc.new(
|
321
|
-
file: @file,
|
322
|
-
begin_line: ast_loc.line,
|
323
|
-
begin_column: ast_loc.column,
|
324
|
-
end_line: ast_loc.last_line,
|
325
|
-
end_column: ast_loc.last_column
|
326
|
-
)
|
433
|
+
Loc.from_ast_loc(@file, node.location)
|
327
434
|
end
|
328
435
|
|
329
436
|
sig { params(node: AST::Node).returns(T::Array[Comment]) }
|
330
437
|
def node_comments(node)
|
331
|
-
return [] unless @comments
|
332
438
|
comments = @comments[node.location]
|
333
439
|
return [] unless comments
|
334
440
|
comments.map do |comment|
|
335
441
|
text = comment.text[1..-1].strip
|
336
|
-
loc =
|
442
|
+
loc = Loc.from_ast_loc(@file, comment.location)
|
337
443
|
Comment.new(text, loc: loc)
|
338
444
|
end
|
339
445
|
end
|
@@ -434,11 +540,11 @@ module RBI
|
|
434
540
|
when :params
|
435
541
|
node.children[2].children.each do |child|
|
436
542
|
name = child.children[0].children[0].to_s
|
437
|
-
type =
|
543
|
+
type = parse_expr(child.children[1])
|
438
544
|
@current << SigParam.new(name, type)
|
439
545
|
end
|
440
546
|
when :returns
|
441
|
-
@current.return_type =
|
547
|
+
@current.return_type = parse_expr(node.children[2])
|
442
548
|
when :void
|
443
549
|
@current.return_type = nil
|
444
550
|
else
|
@@ -446,4 +552,17 @@ module RBI
|
|
446
552
|
end
|
447
553
|
end
|
448
554
|
end
|
555
|
+
|
556
|
+
class Loc
|
557
|
+
sig { params(file: String, ast_loc: T.any(::Parser::Source::Map, ::Parser::Source::Range)).returns(Loc) }
|
558
|
+
def self.from_ast_loc(file, ast_loc)
|
559
|
+
Loc.new(
|
560
|
+
file: file,
|
561
|
+
begin_line: ast_loc.line,
|
562
|
+
begin_column: ast_loc.column,
|
563
|
+
end_line: ast_loc.last_line,
|
564
|
+
end_column: ast_loc.last_column
|
565
|
+
)
|
566
|
+
end
|
567
|
+
end
|
449
568
|
end
|