rbi 0.2.0 → 0.2.1

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: 8bcb914ddfe3208193a252f650ac6bcbc25c741efa183d28876ad1f0957cd24f
4
- data.tar.gz: db2f129f6925c498e6112e43a7fc44b9a4a6aeb694cd18e97e64e3531bfe7ae4
3
+ metadata.gz: 68328047c11f18c7617e8101a773a3b3b9438be4e36a0b01072274eba2b1c583
4
+ data.tar.gz: 275b2ed35e5486461d2ebf4afae88d7b75b9f26ac1d7d8d9693ff630b17dff3d
5
5
  SHA512:
6
- metadata.gz: 0def2a8d159eba09b59cbd403126a4b198687f018c65acec3d35cfa978d25da1bc97956ccb2d017d81ece08fe8e1f65e06b0ed4968b60fcb3b3e4b5cb497cdfe
7
- data.tar.gz: 7f065932016677c397967f1dc16ca5115d8279ec65d00962e850496f616c16f76954e334611a66132cdeeea889866f5c41ffd2089ee5f9cdf473d77211cbfd80
6
+ metadata.gz: 53e510f0df7f4e44c45686774f3573e06031ba7c81a29f7ff429b2dacb601c42a83be37acca12809161ef7acd50722682ad88483b12d4af8d536c6f709e540b8
7
+ data.tar.gz: 172bbba7e9c3cd29032d4d34a4fda47ce81728b971a8956d742b0d012ca393b3f555109493ec2ebfe1aa3087a142da109061fc07074f8a4b721096cf1884091d
data/Gemfile CHANGED
@@ -10,9 +10,9 @@ group(:development, :test) do
10
10
  gem("minitest")
11
11
  gem("minitest-reporters")
12
12
  gem("rake", "~> 13.2")
13
- gem("rubocop", "~> 1.65", require: false)
13
+ gem("rubocop", "~> 1.66", require: false)
14
14
  gem("rubocop-shopify", require: false)
15
15
  gem("rubocop-sorbet", require: false)
16
16
  gem("sorbet", ">= 0.5.9204", require: false)
17
- gem("tapioca", github: "Shopify/tapioca", branch: "at-bump-rbi", require: false)
17
+ gem("tapioca", require: false)
18
18
  end
@@ -0,0 +1,1036 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RBI
5
+ class RBSPrinter < Visitor
6
+ class Error < RBI::Error; end
7
+
8
+ sig { returns(T::Boolean) }
9
+ attr_accessor :print_locs, :in_visibility_group
10
+
11
+ sig { returns(T.nilable(Node)) }
12
+ attr_reader :previous_node
13
+
14
+ sig { returns(Integer) }
15
+ attr_reader :current_indent
16
+
17
+ sig { params(out: T.any(IO, StringIO), indent: Integer, print_locs: T::Boolean).void }
18
+ def initialize(out: $stdout, indent: 0, print_locs: false)
19
+ super()
20
+ @out = out
21
+ @current_indent = indent
22
+ @print_locs = print_locs
23
+ @in_visibility_group = T.let(false, T::Boolean)
24
+ @previous_node = T.let(nil, T.nilable(Node))
25
+ end
26
+
27
+ # Printing
28
+
29
+ sig { void }
30
+ def indent
31
+ @current_indent += 2
32
+ end
33
+
34
+ sig { void }
35
+ def dedent
36
+ @current_indent -= 2
37
+ end
38
+
39
+ # Print a string without indentation nor `\n` at the end.
40
+ sig { params(string: String).void }
41
+ def print(string)
42
+ @out.print(string)
43
+ end
44
+
45
+ # Print a string without indentation but with a `\n` at the end.
46
+ sig { params(string: T.nilable(String)).void }
47
+ def printn(string = nil)
48
+ print(string) if string
49
+ print("\n")
50
+ end
51
+
52
+ # Print a string with indentation but without a `\n` at the end.
53
+ sig { params(string: T.nilable(String)).void }
54
+ def printt(string = nil)
55
+ print(" " * @current_indent)
56
+ print(string) if string
57
+ end
58
+
59
+ # Print a string with indentation and `\n` at the end.
60
+ sig { params(string: String).void }
61
+ def printl(string)
62
+ printt
63
+ printn(string)
64
+ end
65
+
66
+ sig { override.params(nodes: T::Array[Node]).void }
67
+ def visit_all(nodes)
68
+ previous_node = @previous_node
69
+ @previous_node = nil
70
+ nodes.each do |node|
71
+ visit(node)
72
+ @previous_node = node
73
+ end
74
+ @previous_node = previous_node
75
+ end
76
+
77
+ sig { override.params(file: File).void }
78
+ def visit_file(file)
79
+ unless file.comments.empty?
80
+ visit_all(file.comments)
81
+ end
82
+
83
+ unless file.root.empty? && file.root.comments.empty?
84
+ printn unless file.comments.empty?
85
+ visit(file.root)
86
+ end
87
+ end
88
+
89
+ sig { override.params(node: Comment).void }
90
+ def visit_comment(node)
91
+ lines = node.text.lines
92
+
93
+ if lines.empty?
94
+ printl("#")
95
+ end
96
+
97
+ lines.each do |line|
98
+ text = line.rstrip
99
+ printt("#")
100
+ print(" #{text}") unless text.empty?
101
+ printn
102
+ end
103
+ end
104
+
105
+ sig { override.params(node: BlankLine).void }
106
+ def visit_blank_line(node)
107
+ printn
108
+ end
109
+
110
+ sig { override.params(node: Tree).void }
111
+ def visit_tree(node)
112
+ visit_all(node.comments)
113
+ printn if !node.comments.empty? && !node.empty?
114
+ visit_all(node.nodes)
115
+ end
116
+
117
+ sig { override.params(node: Module).void }
118
+ def visit_module(node)
119
+ visit_scope(node)
120
+ end
121
+
122
+ sig { override.params(node: Class).void }
123
+ def visit_class(node)
124
+ visit_scope(node)
125
+ end
126
+
127
+ sig { override.params(node: Struct).void }
128
+ def visit_struct(node)
129
+ visit_scope(node)
130
+ end
131
+
132
+ sig { override.params(node: SingletonClass).void }
133
+ def visit_singleton_class(node)
134
+ visit_scope(node)
135
+ end
136
+
137
+ sig { params(node: Scope).void }
138
+ def visit_scope(node)
139
+ print_blank_line_before(node)
140
+ print_loc(node)
141
+ visit_all(node.comments)
142
+
143
+ visit_scope_header(node)
144
+ visit_scope_body(node)
145
+ end
146
+
147
+ sig { params(node: Scope).void }
148
+ def visit_scope_header(node)
149
+ node.nodes.grep(Helper).each do |helper|
150
+ visit(Comment.new("@#{helper.name}"))
151
+ end
152
+
153
+ mixins = node.nodes.grep(MixesInClassMethods)
154
+ if mixins.any?
155
+ visit(Comment.new("@mixes_in_class_methods #{mixins.map(&:names).join(", ")}"))
156
+ end
157
+
158
+ ancestors = node.nodes.grep(RequiresAncestor)
159
+ if ancestors.any?
160
+ visit(Comment.new("@requires_ancestor #{ancestors.map(&:name).join(", ")}"))
161
+ end
162
+
163
+ case node
164
+ when TStruct
165
+ printt("class #{node.name}")
166
+ when TEnum
167
+ printt("class #{node.name}")
168
+ when Module
169
+ printt("module #{node.name}")
170
+ when Class
171
+ printt("class #{node.name}")
172
+ superclass = node.superclass_name
173
+ print(" < #{superclass}") if superclass
174
+ when Struct
175
+ printt("#{node.name} = ::Struct.new")
176
+ if !node.members.empty? || node.keyword_init
177
+ print("(")
178
+ args = node.members.map { |member| ":#{member}" }
179
+ args << "keyword_init: true" if node.keyword_init
180
+ print(args.join(", "))
181
+ print(")")
182
+ end
183
+ when SingletonClass
184
+ printt("class << self")
185
+ else
186
+ raise Error, "Unhandled node: #{node.class}"
187
+ end
188
+
189
+ type_params = node.nodes.grep(TypeMember)
190
+ if type_params.any?
191
+ print("[#{type_params.map(&:name).join(", ")}]")
192
+ end
193
+
194
+ if !node.empty? && node.is_a?(Struct)
195
+ print(" do")
196
+ end
197
+ printn
198
+ end
199
+
200
+ sig { params(node: Scope).void }
201
+ def visit_scope_body(node)
202
+ unless node.empty?
203
+ indent
204
+ visit_all(node.nodes)
205
+ dedent
206
+ end
207
+ if !node.is_a?(Struct) || !node.empty?
208
+ printl("end")
209
+ end
210
+ end
211
+
212
+ sig { override.params(node: Const).void }
213
+ def visit_const(node)
214
+ print_blank_line_before(node)
215
+ print_loc(node)
216
+ visit_all(node.comments)
217
+
218
+ type = parse_t_let(node.value)
219
+ if type
220
+ type = parse_type(type)
221
+ printl("#{node.name}: #{type.rbs_string}")
222
+ else
223
+ printl("#{node.name}: untyped")
224
+ end
225
+ end
226
+
227
+ sig { override.params(node: AttrAccessor).void }
228
+ def visit_attr_accessor(node)
229
+ visit_attr(node)
230
+ end
231
+
232
+ sig { override.params(node: AttrReader).void }
233
+ def visit_attr_reader(node)
234
+ visit_attr(node)
235
+ end
236
+
237
+ sig { override.params(node: AttrWriter).void }
238
+ def visit_attr_writer(node)
239
+ visit_attr(node)
240
+ end
241
+
242
+ sig { params(node: Attr).void }
243
+ def visit_attr(node)
244
+ print_blank_line_before(node)
245
+
246
+ node.names.each do |name|
247
+ visit_all(node.comments)
248
+ print_loc(node)
249
+ printt
250
+ unless in_visibility_group || node.visibility.public? || node.visibility.protected?
251
+ self.print(node.visibility.visibility.to_s)
252
+ print(" ")
253
+ end
254
+ case node
255
+ when AttrAccessor
256
+ print("attr_accessor")
257
+ when AttrReader
258
+ print("attr_reader")
259
+ when AttrWriter
260
+ print("attr_writer")
261
+ end
262
+ print(" #{name}")
263
+ first_sig, *_rest = node.sigs # discard remaining signatures
264
+ if first_sig
265
+ print(": ")
266
+ print_attr_sig(node, first_sig)
267
+ else
268
+ print(": untyped")
269
+ end
270
+ printn
271
+ end
272
+ end
273
+
274
+ sig { params(node: RBI::Attr, sig: Sig).void }
275
+ def print_attr_sig(node, sig)
276
+ type = case node
277
+ when AttrAccessor, AttrReader
278
+ parse_type(sig.return_type)
279
+ else
280
+ first_arg = sig.params.first
281
+ if first_arg
282
+ parse_type(first_arg.type)
283
+ else
284
+ Type.untyped
285
+ end
286
+ end
287
+
288
+ print(type.rbs_string)
289
+ end
290
+
291
+ sig { override.params(node: Method).void }
292
+ def visit_method(node)
293
+ print_blank_line_before(node)
294
+ visit_all(node.comments)
295
+
296
+ if node.sigs.any?(&:is_abstract)
297
+ printl("# @abstract")
298
+ end
299
+
300
+ if node.sigs.any?(&:is_override)
301
+ printl("# @override")
302
+ end
303
+
304
+ if node.sigs.any?(&:is_overridable)
305
+ printl("# @overridable")
306
+ end
307
+
308
+ print_loc(node)
309
+ printt
310
+ unless in_visibility_group || node.visibility.public?
311
+ self.print(node.visibility.visibility.to_s)
312
+ print(" ")
313
+ end
314
+ print("def ")
315
+ print("self.") if node.is_singleton
316
+ print(node.name)
317
+ sigs = node.sigs
318
+ print(": ")
319
+ if sigs.any?
320
+ first, *rest = sigs
321
+ print_method_sig(node, T.must(first))
322
+ if rest.any?
323
+ spaces = node.name.size + 4
324
+ rest.each do |sig|
325
+ printn
326
+ printt
327
+ print("#{" " * spaces}| ")
328
+ print_method_sig(node, sig)
329
+ end
330
+ end
331
+ else
332
+ if node.params.any?
333
+ params = node.params.reject { |param| param.is_a?(BlockParam) }
334
+ block = node.params.find { |param| param.is_a?(BlockParam) }
335
+
336
+ print("(")
337
+ params.each_with_index do |param, index|
338
+ print(", ") if index > 0
339
+ visit(param)
340
+ end
341
+ print(") ")
342
+ visit(block)
343
+ end
344
+ print("-> untyped")
345
+ end
346
+ printn
347
+ end
348
+
349
+ sig { params(node: RBI::Method, sig: Sig).void }
350
+ def print_method_sig(node, sig)
351
+ unless sig.type_params.empty?
352
+ print("[#{sig.type_params.map { |t| "TYPE_#{t}" }.join(", ")}] ")
353
+ end
354
+
355
+ block_param = node.params.find { |param| param.is_a?(BlockParam) }
356
+ sig_block_param = sig.params.find { |param| param.name == block_param&.name }
357
+
358
+ sig_params = sig.params.dup
359
+ if block_param
360
+ sig_params.reject! do |param|
361
+ param.name == block_param.name
362
+ end
363
+ end
364
+
365
+ unless sig_params.empty?
366
+ print("(")
367
+ sig_params.each_with_index do |param, index|
368
+ print(", ") if index > 0
369
+ print_sig_param(node, param)
370
+ end
371
+ print(") ")
372
+ end
373
+ if sig_block_param
374
+ block_type = sig_block_param.type
375
+ block_type = Type.parse_string(block_type) if block_type.is_a?(String)
376
+
377
+ block_is_nilable = false
378
+ if block_type.is_a?(Type::Nilable)
379
+ block_is_nilable = true
380
+ block_type = block_type.type
381
+ end
382
+
383
+ type_string = parse_type(block_type).rbs_string.delete_prefix("^")
384
+
385
+ skip = false
386
+ case block_type
387
+ when Type::Untyped
388
+ type_string = "(?) -> untyped"
389
+ block_is_nilable = true
390
+ when Type::Simple
391
+ if block_type.name == "Proc"
392
+ type_string = "(?) -> untyped"
393
+ end
394
+ skip = true if block_type.name == "NilClass"
395
+ end
396
+
397
+ if skip
398
+ # no-op, we skip the block definition
399
+ elsif block_is_nilable
400
+ print("?{ #{type_string} } ")
401
+ else
402
+ print("{ #{type_string} } ")
403
+ end
404
+ end
405
+
406
+ type = parse_type(sig.return_type)
407
+ print("-> #{type.rbs_string}")
408
+
409
+ loc = sig.loc
410
+ print(" # #{loc}") if loc && print_locs
411
+ end
412
+
413
+ sig { override.params(node: ReqParam).void }
414
+ def visit_req_param(node)
415
+ print("untyped #{node.name}")
416
+ end
417
+
418
+ sig { override.params(node: OptParam).void }
419
+ def visit_opt_param(node)
420
+ print("?untyped #{node.name}")
421
+ end
422
+
423
+ sig { override.params(node: RestParam).void }
424
+ def visit_rest_param(node)
425
+ print("*untyped #{node.name}")
426
+ end
427
+
428
+ sig { override.params(node: KwParam).void }
429
+ def visit_kw_param(node)
430
+ print("#{node.name}: untyped")
431
+ end
432
+
433
+ sig { override.params(node: KwOptParam).void }
434
+ def visit_kw_opt_param(node)
435
+ print("?#{node.name}: untyped")
436
+ end
437
+
438
+ sig { override.params(node: KwRestParam).void }
439
+ def visit_kw_rest_param(node)
440
+ print("**#{node.name}: untyped")
441
+ end
442
+
443
+ sig { override.params(node: BlockParam).void }
444
+ def visit_block_param(node)
445
+ print("{ (*untyped) -> untyped } ")
446
+ end
447
+
448
+ sig { override.params(node: Include).void }
449
+ def visit_include(node)
450
+ visit_mixin(node)
451
+ end
452
+
453
+ sig { override.params(node: Extend).void }
454
+ def visit_extend(node)
455
+ visit_mixin(node)
456
+ end
457
+
458
+ sig { params(node: Mixin).void }
459
+ def visit_mixin(node)
460
+ return if node.is_a?(MixesInClassMethods) # no-op, `mixes_in_class_methods` is not supported in RBS
461
+
462
+ print_blank_line_before(node)
463
+ print_loc(node)
464
+ visit_all(node.comments)
465
+
466
+ case node
467
+ when Include
468
+ printt("include")
469
+ when Extend
470
+ printt("extend")
471
+ end
472
+ printn(" #{node.names.join(", ")}")
473
+ end
474
+
475
+ sig { override.params(node: Public).void }
476
+ def visit_public(node)
477
+ visit_visibility(node)
478
+ end
479
+
480
+ sig { override.params(node: Protected).void }
481
+ def visit_protected(node)
482
+ # no-op, `protected` is not supported in RBS
483
+ end
484
+
485
+ sig { override.params(node: Private).void }
486
+ def visit_private(node)
487
+ visit_visibility(node)
488
+ end
489
+
490
+ sig { params(node: Visibility).void }
491
+ def visit_visibility(node)
492
+ print_blank_line_before(node)
493
+ print_loc(node)
494
+ visit_all(node.comments)
495
+
496
+ printl(node.visibility.to_s)
497
+ end
498
+
499
+ sig { override.params(node: Send).void }
500
+ def visit_send(node)
501
+ # no-op, arbitrary sends are not supported in RBS
502
+ end
503
+
504
+ sig { override.params(node: Arg).void }
505
+ def visit_arg(node)
506
+ # no-op
507
+ end
508
+
509
+ sig { override.params(node: KwArg).void }
510
+ def visit_kw_arg(node)
511
+ # no-op
512
+ end
513
+
514
+ sig { override.params(node: TStruct).void }
515
+ def visit_tstruct(node)
516
+ print_blank_line_before(node)
517
+ print_loc(node)
518
+ visit_all(node.comments)
519
+
520
+ visit_scope_header(node)
521
+ nodes = node.nodes.dup
522
+
523
+ sig = Sig.new
524
+ init = Method.new("initialize", sigs: [sig])
525
+ last_field = -1
526
+ nodes.each_with_index do |child, i|
527
+ case child
528
+ when TStructField
529
+ default = child.default
530
+ init << if default
531
+ KwOptParam.new(child.name, default)
532
+ else
533
+ KwParam.new(child.name)
534
+ end
535
+ sig << SigParam.new(child.name, child.type)
536
+ last_field = i
537
+ end
538
+ end
539
+ nodes.insert(last_field + 1, init)
540
+
541
+ indent
542
+ visit_all(nodes)
543
+ dedent
544
+
545
+ printl("end")
546
+ end
547
+
548
+ sig { override.params(node: TStructConst).void }
549
+ def visit_tstruct_const(node)
550
+ # `T::Struct.const` is not supported in RBS instead we generate an attribute reader
551
+ accessor = AttrReader.new(node.name.to_sym, comments: node.comments, sigs: [Sig.new(return_type: node.type)])
552
+ visit_attr_reader(accessor)
553
+ end
554
+
555
+ sig { override.params(node: TStructProp).void }
556
+ def visit_tstruct_prop(node)
557
+ # `T::Struct.prop` is not supported in RBS instead we generate an attribute accessor
558
+ accessor = AttrAccessor.new(node.name.to_sym, comments: node.comments, sigs: [Sig.new(return_type: node.type)])
559
+ visit_attr_accessor(accessor)
560
+ end
561
+
562
+ sig { override.params(node: TEnum).void }
563
+ def visit_tenum(node)
564
+ visit_scope(node)
565
+ end
566
+
567
+ sig { override.params(node: TEnumBlock).void }
568
+ def visit_tenum_block(node)
569
+ node.nodes.each do |child|
570
+ child = if child.is_a?(Const) && child.value == "new"
571
+ parent = node.parent_scope
572
+ Const.new(
573
+ child.name,
574
+ "T.let(nil, #{parent.is_a?(TEnum) ? parent.name : "T.untyped"})",
575
+ comments: child.comments,
576
+ )
577
+ else
578
+ child
579
+ end
580
+ visit(child)
581
+ @previous_node = child
582
+ end
583
+ end
584
+
585
+ sig { override.params(node: TypeMember).void }
586
+ def visit_type_member(node)
587
+ # no-op, we already show them in the scope header
588
+ end
589
+
590
+ sig { override.params(node: Helper).void }
591
+ def visit_helper(node)
592
+ # no-op, we already show them in the scope header
593
+ end
594
+
595
+ sig { override.params(node: MixesInClassMethods).void }
596
+ def visit_mixes_in_class_methods(node)
597
+ visit_mixin(node)
598
+ end
599
+
600
+ sig { override.params(node: Group).void }
601
+ def visit_group(node)
602
+ printn unless previous_node.nil?
603
+ visit_all(node.nodes)
604
+ end
605
+
606
+ sig { override.params(node: VisibilityGroup).void }
607
+ def visit_visibility_group(node)
608
+ self.in_visibility_group = true
609
+ if node.visibility.public?
610
+ printn unless previous_node.nil?
611
+ else
612
+ visit(node.visibility)
613
+ printn
614
+ end
615
+ visit_all(node.nodes)
616
+ self.in_visibility_group = false
617
+ end
618
+
619
+ sig { override.params(node: RequiresAncestor).void }
620
+ def visit_requires_ancestor(node)
621
+ # no-op, we already show them in the scope header
622
+ end
623
+
624
+ sig { override.params(node: ConflictTree).void }
625
+ def visit_conflict_tree(node)
626
+ printl("<<<<<<< #{node.left_name}")
627
+ visit(node.left)
628
+ printl("=======")
629
+ visit(node.right)
630
+ printl(">>>>>>> #{node.right_name}")
631
+ end
632
+
633
+ sig { override.params(node: ScopeConflict).void }
634
+ def visit_scope_conflict(node)
635
+ print_blank_line_before(node)
636
+ print_loc(node)
637
+ visit_all(node.comments)
638
+
639
+ printl("<<<<<<< #{node.left_name}")
640
+ visit_scope_header(node.left)
641
+ printl("=======")
642
+ visit_scope_header(node.right)
643
+ printl(">>>>>>> #{node.right_name}")
644
+ visit_scope_body(node.left)
645
+ end
646
+
647
+ private
648
+
649
+ sig { params(node: Node).void }
650
+ def print_blank_line_before(node)
651
+ previous_node = self.previous_node
652
+ return unless previous_node
653
+ return if previous_node.is_a?(BlankLine)
654
+ return if previous_node.is_a?(TypeMember) # since we skip them
655
+ return if previous_node.is_a?(Helper) # since we skip them
656
+ return if previous_node.is_a?(Protected) # since we skip them
657
+ return if previous_node.is_a?(RequiresAncestor) # since we skip them
658
+ return if previous_node.is_a?(Send) # since we skip them
659
+ return if previous_node.is_a?(Arg) # since we skip them
660
+ return if previous_node.is_a?(KwArg) # since we skip them
661
+ return if previous_node.is_a?(TEnumBlock) # since we skip them
662
+ return if previous_node.is_a?(MixesInClassMethods) # since we skip them
663
+ return if oneline?(previous_node) && oneline?(node)
664
+
665
+ printn
666
+ end
667
+
668
+ sig { params(node: Node).void }
669
+ def print_loc(node)
670
+ loc = node.loc
671
+ printl("# #{loc}") if loc && print_locs
672
+ end
673
+
674
+ sig { params(node: Method, param: SigParam).void }
675
+ def print_sig_param(node, param)
676
+ type = parse_type(param.type).rbs_string
677
+
678
+ orig_param = node.params.find { |p| p.name == param.name }
679
+
680
+ case orig_param
681
+ when ReqParam
682
+ print("#{type} #{param.name}")
683
+ when OptParam
684
+ print("?#{type} #{param.name}")
685
+ when RestParam
686
+ print("*#{type} #{param.name}")
687
+ when KwParam
688
+ print("#{param.name}: #{type}")
689
+ when KwOptParam
690
+ print("?#{param.name}: #{type}")
691
+ when KwRestParam
692
+ print("**#{type} #{param.name}")
693
+ else
694
+ raise Error, "Unexpected param type: #{orig_param.class} for param #{param.name}"
695
+ end
696
+ end
697
+
698
+ sig { params(node: Param, last: T::Boolean).void }
699
+ def print_param_comment_leading_space(node, last:)
700
+ printn
701
+ printt
702
+ print(" " * (node.name.size + 1))
703
+ print(" ") unless last
704
+ case node
705
+ when OptParam
706
+ print(" " * (node.value.size + 3))
707
+ when RestParam, KwParam, BlockParam
708
+ print(" ")
709
+ when KwRestParam
710
+ print(" ")
711
+ when KwOptParam
712
+ print(" " * (node.value.size + 2))
713
+ end
714
+ end
715
+
716
+ sig { params(node: SigParam, last: T::Boolean).void }
717
+ def print_sig_param_comment_leading_space(node, last:)
718
+ printn
719
+ printt
720
+ print(" " * (node.name.size + node.type.to_s.size + 3))
721
+ print(" ") unless last
722
+ end
723
+
724
+ sig { params(node: Node).returns(T::Boolean) }
725
+ def oneline?(node)
726
+ case node
727
+ when ScopeConflict
728
+ oneline?(node.left)
729
+ when Tree
730
+ false
731
+ when Attr
732
+ node.comments.empty?
733
+ when Method
734
+ node.comments.empty? && node.sigs.empty? && node.params.all? { |p| p.comments.empty? }
735
+ when Sig
736
+ node.params.all? { |p| p.comments.empty? }
737
+ when NodeWithComments
738
+ node.comments.empty?
739
+ when VisibilityGroup
740
+ false
741
+ else
742
+ true
743
+ end
744
+ end
745
+
746
+ sig { params(type: T.any(Type, String)).returns(Type) }
747
+ def parse_type(type)
748
+ return type if type.is_a?(Type)
749
+
750
+ Type.parse_string(type)
751
+ rescue Type::Error => e
752
+ raise Error, "Failed to parse type `#{type}` (#{e.message})"
753
+ end
754
+
755
+ # Parse a string containing a `T.let(x, X)` and extract the type
756
+ #
757
+ # Returns `nil` is the string is not a `T.let`.
758
+ sig { params(code: T.nilable(String)).returns(T.nilable(String)) }
759
+ def parse_t_let(code)
760
+ return unless code
761
+
762
+ res = Prism.parse(code)
763
+ return unless res.success?
764
+
765
+ node = res.value
766
+ return unless node.is_a?(Prism::ProgramNode)
767
+
768
+ node = node.statements.body.first
769
+ return unless node.is_a?(Prism::CallNode)
770
+ return unless node.name == :let
771
+ return unless node.receiver&.slice =~ /^(::)?T$/
772
+
773
+ node.arguments&.arguments&.fetch(1, nil)&.slice
774
+ end
775
+
776
+ sig { params(node: Type).returns(T::Boolean) }
777
+ def bare_proc?(node)
778
+ node.is_a?(Type::Simple) && node.name == "Proc"
779
+ end
780
+
781
+ sig { params(node: Type).returns(T::Boolean) }
782
+ def bare_nilable_proc?(node)
783
+ node.is_a?(Type::Nilable) && bare_proc?(node.type)
784
+ end
785
+ end
786
+
787
+ class TypePrinter
788
+ extend T::Sig
789
+
790
+ sig { returns(String) }
791
+ attr_reader :string
792
+
793
+ sig { void }
794
+ def initialize
795
+ @string = T.let(String.new, String)
796
+ end
797
+
798
+ sig { params(node: Type).void }
799
+ def visit(node)
800
+ case node
801
+ when Type::Simple
802
+ visit_simple(node)
803
+ when Type::Boolean
804
+ visit_boolean(node)
805
+ when Type::Generic
806
+ visit_generic(node)
807
+ when Type::Anything
808
+ visit_anything(node)
809
+ when Type::Void
810
+ visit_void(node)
811
+ when Type::NoReturn
812
+ visit_no_return(node)
813
+ when Type::Untyped
814
+ visit_untyped(node)
815
+ when Type::SelfType
816
+ visit_self_type(node)
817
+ when Type::AttachedClass
818
+ visit_attached_class(node)
819
+ when Type::Nilable
820
+ visit_nilable(node)
821
+ when Type::ClassOf
822
+ visit_class_of(node)
823
+ when Type::All
824
+ visit_all(node)
825
+ when Type::Any
826
+ visit_any(node)
827
+ when Type::Tuple
828
+ visit_tuple(node)
829
+ when Type::Shape
830
+ visit_shape(node)
831
+ when Type::Proc
832
+ visit_proc(node)
833
+ when Type::TypeParameter
834
+ visit_type_parameter(node)
835
+ when Type::Class
836
+ visit_class(node)
837
+ else
838
+ raise Error, "Unhandled node: #{node.class}"
839
+ end
840
+ end
841
+
842
+ sig { params(type: Type::Simple).void }
843
+ def visit_simple(type)
844
+ @string << translate_t_type(type.name)
845
+ end
846
+
847
+ sig { params(type: Type::Boolean).void }
848
+ def visit_boolean(type)
849
+ @string << "bool"
850
+ end
851
+
852
+ sig { params(type: Type::Generic).void }
853
+ def visit_generic(type)
854
+ @string << translate_t_type(type.name)
855
+ @string << "["
856
+ type.params.each_with_index do |arg, index|
857
+ visit(arg)
858
+ @string << ", " if index < type.params.size - 1
859
+ end
860
+ @string << "]"
861
+ end
862
+
863
+ sig { params(type: Type::Anything).void }
864
+ def visit_anything(type)
865
+ @string << "top"
866
+ end
867
+
868
+ sig { params(type: Type::Void).void }
869
+ def visit_void(type)
870
+ @string << "void"
871
+ end
872
+
873
+ sig { params(type: Type::NoReturn).void }
874
+ def visit_no_return(type)
875
+ @string << "bot"
876
+ end
877
+
878
+ sig { params(type: Type::Untyped).void }
879
+ def visit_untyped(type)
880
+ @string << "untyped"
881
+ end
882
+
883
+ sig { params(type: Type::SelfType).void }
884
+ def visit_self_type(type)
885
+ @string << "self"
886
+ end
887
+
888
+ sig { params(type: Type::AttachedClass).void }
889
+ def visit_attached_class(type)
890
+ @string << "attached_class"
891
+ end
892
+
893
+ sig { params(type: Type::Nilable).void }
894
+ def visit_nilable(type)
895
+ visit(type.type)
896
+ @string << "?"
897
+ end
898
+
899
+ sig { params(type: Type::ClassOf).void }
900
+ def visit_class_of(type)
901
+ @string << "singleton("
902
+ visit(type.type)
903
+ @string << ")"
904
+ end
905
+
906
+ sig { params(type: Type::All).void }
907
+ def visit_all(type)
908
+ @string << "("
909
+ type.types.each_with_index do |arg, index|
910
+ visit(arg)
911
+ @string << " & " if index < type.types.size - 1
912
+ end
913
+ @string << ")"
914
+ end
915
+
916
+ sig { params(type: Type::Any).void }
917
+ def visit_any(type)
918
+ @string << "("
919
+ type.types.each_with_index do |arg, index|
920
+ visit(arg)
921
+ @string << " | " if index < type.types.size - 1
922
+ end
923
+ @string << ")"
924
+ end
925
+
926
+ sig { params(type: Type::Tuple).void }
927
+ def visit_tuple(type)
928
+ @string << "["
929
+ type.types.each_with_index do |arg, index|
930
+ visit(arg)
931
+ @string << ", " if index < type.types.size - 1
932
+ end
933
+ @string << "]"
934
+ end
935
+
936
+ sig { params(type: Type::Shape).void }
937
+ def visit_shape(type)
938
+ @string << "{"
939
+ type.types.each_with_index do |(key, value), index|
940
+ @string << "#{key}: "
941
+ visit(value)
942
+ @string << ", " if index < type.types.size - 1
943
+ end
944
+ @string << "}"
945
+ end
946
+
947
+ sig { params(type: Type::Proc).void }
948
+ def visit_proc(type)
949
+ @string << "^"
950
+ if type.proc_params.any?
951
+ @string << "("
952
+ type.proc_params.each_with_index do |(key, value), index|
953
+ visit(value)
954
+ @string << " #{key}"
955
+ @string << ", " if index < type.proc_params.size - 1
956
+ end
957
+ @string << ") "
958
+ end
959
+ @string << "-> "
960
+ visit(type.proc_returns)
961
+ end
962
+
963
+ sig { params(type: Type::TypeParameter).void }
964
+ def visit_type_parameter(type)
965
+ @string << "TYPE_#{type.name}"
966
+ end
967
+
968
+ sig { params(type: Type::Class).void }
969
+ def visit_class(type)
970
+ @string << "singleton("
971
+ visit(type.type)
972
+ @string << ")"
973
+ end
974
+
975
+ private
976
+
977
+ sig { params(type_name: String).returns(String) }
978
+ def translate_t_type(type_name)
979
+ case type_name
980
+ when "T::Array"
981
+ "Array"
982
+ when "T::Hash"
983
+ "Hash"
984
+ when "T::Set"
985
+ "Set"
986
+ else
987
+ type_name
988
+ end
989
+ end
990
+ end
991
+
992
+ class File
993
+ extend T::Sig
994
+
995
+ sig { params(out: T.any(IO, StringIO), indent: Integer, print_locs: T::Boolean).void }
996
+ def rbs_print(out: $stdout, indent: 0, print_locs: false)
997
+ p = RBSPrinter.new(out: out, indent: indent, print_locs: print_locs)
998
+ p.visit_file(self)
999
+ end
1000
+
1001
+ sig { params(indent: Integer, print_locs: T::Boolean).returns(String) }
1002
+ def rbs_string(indent: 0, print_locs: false)
1003
+ out = StringIO.new
1004
+ rbs_print(out: out, indent: indent, print_locs: print_locs)
1005
+ out.string
1006
+ end
1007
+ end
1008
+
1009
+ class Node
1010
+ extend T::Sig
1011
+
1012
+ sig { params(out: T.any(IO, StringIO), indent: Integer, print_locs: T::Boolean).void }
1013
+ def rbs_print(out: $stdout, indent: 0, print_locs: false)
1014
+ p = RBSPrinter.new(out: out, indent: indent, print_locs: print_locs)
1015
+ p.visit(self)
1016
+ end
1017
+
1018
+ sig { params(indent: Integer, print_locs: T::Boolean).returns(String) }
1019
+ def rbs_string(indent: 0, print_locs: false)
1020
+ out = StringIO.new
1021
+ rbs_print(out: out, indent: indent, print_locs: print_locs)
1022
+ out.string
1023
+ end
1024
+ end
1025
+
1026
+ class Type
1027
+ extend T::Sig
1028
+
1029
+ sig { returns(String) }
1030
+ def rbs_string
1031
+ p = TypePrinter.new
1032
+ p.visit(self)
1033
+ p.string
1034
+ end
1035
+ end
1036
+ 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.2.0"
5
+ VERSION = "0.2.1"
6
6
  end
data/lib/rbi.rb CHANGED
@@ -33,5 +33,6 @@ require "rbi/parser"
33
33
  require "rbi/type_parser"
34
34
  require "rbi/type_visitor"
35
35
  require "rbi/printer"
36
+ require "rbi/rbs_printer"
36
37
  require "rbi/formatter"
37
38
  require "rbi/version"
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.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandre Terrasa
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-29 00:00:00.000000000 Z
11
+ date: 2024-10-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: prism
@@ -55,6 +55,7 @@ files:
55
55
  - lib/rbi/model.rb
56
56
  - lib/rbi/parser.rb
57
57
  - lib/rbi/printer.rb
58
+ - lib/rbi/rbs_printer.rb
58
59
  - lib/rbi/rewriters/add_sig_templates.rb
59
60
  - lib/rbi/rewriters/annotate.rb
60
61
  - lib/rbi/rewriters/attr_to_methods.rb
@@ -94,7 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
95
  - !ruby/object:Gem::Version
95
96
  version: '0'
96
97
  requirements: []
97
- rubygems_version: 3.5.17
98
+ rubygems_version: 3.5.20
98
99
  signing_key:
99
100
  specification_version: 4
100
101
  summary: RBI generation framework