rbi 0.2.0 → 0.2.1

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