rbi 0.1.13 → 0.2.0

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.
data/lib/rbi/type.rb ADDED
@@ -0,0 +1,765 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RBI
5
+ # The base class for all RBI types.
6
+ class Type
7
+ extend T::Sig
8
+ extend T::Helpers
9
+
10
+ abstract!
11
+
12
+ # Simple
13
+
14
+ # A type that represents a simple class name like `String` or `Foo`.
15
+ #
16
+ # It can also be a qualified name like `::Foo` or `Foo::Bar`.
17
+ class Simple < Type
18
+ extend T::Sig
19
+
20
+ sig { returns(String) }
21
+ attr_reader :name
22
+
23
+ sig { params(name: String).void }
24
+ def initialize(name)
25
+ super()
26
+ @name = name
27
+ end
28
+
29
+ sig { override.params(other: BasicObject).returns(T::Boolean) }
30
+ def ==(other)
31
+ Simple === other && @name == other.name
32
+ end
33
+
34
+ sig { override.returns(String) }
35
+ def to_rbi
36
+ @name
37
+ end
38
+ end
39
+
40
+ # Literals
41
+
42
+ # `T.anything`.
43
+ class Anything < Type
44
+ extend T::Sig
45
+
46
+ sig { override.params(other: BasicObject).returns(T::Boolean) }
47
+ def ==(other)
48
+ Anything === other
49
+ end
50
+
51
+ sig { override.returns(String) }
52
+ def to_rbi
53
+ "T.anything"
54
+ end
55
+ end
56
+
57
+ # `T.attached_class`.
58
+ class AttachedClass < Type
59
+ extend T::Sig
60
+
61
+ sig { override.params(other: BasicObject).returns(T::Boolean) }
62
+ def ==(other)
63
+ AttachedClass === other
64
+ end
65
+
66
+ sig { override.returns(String) }
67
+ def to_rbi
68
+ "T.attached_class"
69
+ end
70
+ end
71
+
72
+ # `T::Boolean`.
73
+ class Boolean < Type
74
+ extend T::Sig
75
+
76
+ sig { override.params(other: BasicObject).returns(T::Boolean) }
77
+ def ==(other)
78
+ Boolean === other
79
+ end
80
+
81
+ sig { override.returns(String) }
82
+ def to_rbi
83
+ "T::Boolean"
84
+ end
85
+ end
86
+
87
+ # `T.noreturn`.
88
+ class NoReturn < Type
89
+ extend T::Sig
90
+
91
+ sig { override.params(other: BasicObject).returns(T::Boolean) }
92
+ def ==(other)
93
+ NoReturn === other
94
+ end
95
+
96
+ sig { override.returns(String) }
97
+ def to_rbi
98
+ "T.noreturn"
99
+ end
100
+ end
101
+
102
+ # `T.self_type`.
103
+ class SelfType < Type
104
+ extend T::Sig
105
+
106
+ sig { override.params(other: BasicObject).returns(T::Boolean) }
107
+ def ==(other)
108
+ SelfType === other
109
+ end
110
+
111
+ sig { override.returns(String) }
112
+ def to_rbi
113
+ "T.self_type"
114
+ end
115
+ end
116
+
117
+ # `T.untyped`.
118
+ class Untyped < Type
119
+ extend T::Sig
120
+
121
+ sig { override.params(other: BasicObject).returns(T::Boolean) }
122
+ def ==(other)
123
+ Untyped === other
124
+ end
125
+
126
+ sig { override.returns(String) }
127
+ def to_rbi
128
+ "T.untyped"
129
+ end
130
+ end
131
+
132
+ # `void`.
133
+ class Void < Type
134
+ extend T::Sig
135
+
136
+ sig { override.params(other: BasicObject).returns(T::Boolean) }
137
+ def ==(other)
138
+ Void === other
139
+ end
140
+
141
+ sig { override.returns(String) }
142
+ def to_rbi
143
+ "void"
144
+ end
145
+ end
146
+
147
+ # Composites
148
+
149
+ # The class of another type like `T::Class[Foo]`.
150
+ class Class < Type
151
+ extend T::Sig
152
+
153
+ sig { returns(Type) }
154
+ attr_reader :type
155
+
156
+ sig { params(type: Type).void }
157
+ def initialize(type)
158
+ super()
159
+ @type = type
160
+ end
161
+
162
+ sig { override.params(other: BasicObject).returns(T::Boolean) }
163
+ def ==(other)
164
+ Class === other && @type == other.type
165
+ end
166
+
167
+ sig { override.returns(String) }
168
+ def to_rbi
169
+ "T::Class[#{@type}]"
170
+ end
171
+ end
172
+
173
+ # The singleton class of another type like `T.class_of(Foo)`.
174
+ class ClassOf < Type
175
+ extend T::Sig
176
+
177
+ sig { returns(Simple) }
178
+ attr_reader :type
179
+
180
+ sig { returns(T.nilable(Type)) }
181
+ attr_reader :type_parameter
182
+
183
+ sig { params(type: Simple, type_parameter: T.nilable(Type)).void }
184
+ def initialize(type, type_parameter = nil)
185
+ super()
186
+ @type = type
187
+ @type_parameter = type_parameter
188
+ end
189
+
190
+ sig { override.params(other: BasicObject).returns(T::Boolean) }
191
+ def ==(other)
192
+ ClassOf === other && @type == other.type && @type_parameter == other.type_parameter
193
+ end
194
+
195
+ sig { override.returns(String) }
196
+ def to_rbi
197
+ if @type_parameter
198
+ "T.class_of(#{@type.to_rbi})[#{@type_parameter.to_rbi}]"
199
+ else
200
+ "T.class_of(#{@type.to_rbi})"
201
+ end
202
+ end
203
+ end
204
+
205
+ # A type that can be `nil` like `T.nilable(String)`.
206
+ class Nilable < Type
207
+ extend T::Sig
208
+
209
+ sig { returns(Type) }
210
+ attr_reader :type
211
+
212
+ sig { params(type: Type).void }
213
+ def initialize(type)
214
+ super()
215
+ @type = type
216
+ end
217
+
218
+ sig { override.params(other: BasicObject).returns(T::Boolean) }
219
+ def ==(other)
220
+ Nilable === other && @type == other.type
221
+ end
222
+
223
+ sig { override.returns(String) }
224
+ def to_rbi
225
+ "T.nilable(#{@type.to_rbi})"
226
+ end
227
+ end
228
+
229
+ # A type that is composed of multiple types like `T.all(String, Integer)`.
230
+ class Composite < Type
231
+ extend T::Sig
232
+ extend T::Helpers
233
+
234
+ abstract!
235
+
236
+ sig { returns(T::Array[Type]) }
237
+ attr_reader :types
238
+
239
+ sig { params(types: T::Array[Type]).void }
240
+ def initialize(types)
241
+ super()
242
+ @types = types
243
+ end
244
+
245
+ sig { override.params(other: BasicObject).returns(T::Boolean) }
246
+ def ==(other)
247
+ self.class === other && @types.sort_by(&:to_rbi) == other.types.sort_by(&:to_rbi)
248
+ end
249
+ end
250
+
251
+ # A type that is intersection of multiple types like `T.all(String, Integer)`.
252
+ class All < Composite
253
+ extend T::Sig
254
+
255
+ sig { override.returns(String) }
256
+ def to_rbi
257
+ "T.all(#{@types.map(&:to_rbi).join(", ")})"
258
+ end
259
+ end
260
+
261
+ # A type that is union of multiple types like `T.any(String, Integer)`.
262
+ class Any < Composite
263
+ extend T::Sig
264
+
265
+ sig { override.returns(String) }
266
+ def to_rbi
267
+ "T.any(#{@types.map(&:to_rbi).join(", ")})"
268
+ end
269
+
270
+ sig { returns(T::Boolean) }
271
+ def nilable?
272
+ @types.any? { |type| type.nilable? || (type.is_a?(Simple) && type.name == "NilClass") }
273
+ end
274
+ end
275
+
276
+ # Generics
277
+
278
+ # A generic type like `T::Array[String]` or `T::Hash[Symbol, Integer]`.
279
+ class Generic < Type
280
+ extend T::Sig
281
+
282
+ sig { returns(String) }
283
+ attr_reader :name
284
+
285
+ sig { returns(T::Array[Type]) }
286
+ attr_reader :params
287
+
288
+ sig { params(name: String, params: Type).void }
289
+ def initialize(name, *params)
290
+ super()
291
+ @name = name
292
+ @params = T.let(params, T::Array[Type])
293
+ end
294
+
295
+ sig { override.params(other: BasicObject).returns(T::Boolean) }
296
+ def ==(other)
297
+ Generic === other && @name == other.name && @params == other.params
298
+ end
299
+
300
+ sig { override.returns(String) }
301
+ def to_rbi
302
+ "#{@name}[#{@params.map(&:to_rbi).join(", ")}]"
303
+ end
304
+ end
305
+
306
+ # A type parameter like `T.type_parameter(:U)`.
307
+ class TypeParameter < Type
308
+ extend T::Sig
309
+
310
+ sig { returns(Symbol) }
311
+ attr_reader :name
312
+
313
+ sig { params(name: Symbol).void }
314
+ def initialize(name)
315
+ super()
316
+ @name = name
317
+ end
318
+
319
+ sig { override.params(other: BasicObject).returns(T::Boolean) }
320
+ def ==(other)
321
+ TypeParameter === other && @name == other.name
322
+ end
323
+
324
+ sig { override.returns(String) }
325
+ def to_rbi
326
+ "T.type_parameter(#{@name.inspect})"
327
+ end
328
+ end
329
+
330
+ # Tuples and shapes
331
+
332
+ # A tuple type like `[String, Integer]`.
333
+ class Tuple < Type
334
+ extend T::Sig
335
+
336
+ sig { returns(T::Array[Type]) }
337
+ attr_reader :types
338
+
339
+ sig { params(types: T::Array[Type]).void }
340
+ def initialize(types)
341
+ super()
342
+ @types = types
343
+ end
344
+
345
+ sig { override.params(other: BasicObject).returns(T::Boolean) }
346
+ def ==(other)
347
+ Tuple === other && @types == other.types
348
+ end
349
+
350
+ sig { override.returns(String) }
351
+ def to_rbi
352
+ "[#{@types.map(&:to_rbi).join(", ")}]"
353
+ end
354
+ end
355
+
356
+ # A shape type like `{name: String, age: Integer}`.
357
+ class Shape < Type
358
+ extend T::Sig
359
+
360
+ sig { returns(T::Hash[T.any(String, Symbol), Type]) }
361
+ attr_reader :types
362
+
363
+ sig { params(types: T::Hash[T.any(String, Symbol), Type]).void }
364
+ def initialize(types)
365
+ super()
366
+ @types = types
367
+ end
368
+
369
+ sig { override.params(other: BasicObject).returns(T::Boolean) }
370
+ def ==(other)
371
+ Shape === other && @types.sort_by { |t| t.first.to_s } == other.types.sort_by { |t| t.first.to_s }
372
+ end
373
+
374
+ sig { override.returns(String) }
375
+ def to_rbi
376
+ if @types.empty?
377
+ "{}"
378
+ else
379
+ "{ " + @types.map { |name, type| "#{name}: #{type.to_rbi}" }.join(", ") + " }"
380
+ end
381
+ end
382
+ end
383
+
384
+ # Proc
385
+
386
+ # A proc type like `T.proc.void`.
387
+ class Proc < Type
388
+ extend T::Sig
389
+
390
+ sig { returns(T::Hash[Symbol, Type]) }
391
+ attr_reader :proc_params
392
+
393
+ sig { returns(Type) }
394
+ attr_reader :proc_returns
395
+
396
+ sig { returns(T.nilable(Type)) }
397
+ attr_reader :proc_bind
398
+
399
+ sig { void }
400
+ def initialize
401
+ super
402
+ @proc_params = T.let({}, T::Hash[Symbol, Type])
403
+ @proc_returns = T.let(Type.void, Type)
404
+ @proc_bind = T.let(nil, T.nilable(Type))
405
+ end
406
+
407
+ sig { override.params(other: BasicObject).returns(T::Boolean) }
408
+ def ==(other)
409
+ return false unless Proc === other
410
+ return false unless @proc_params == other.proc_params
411
+ return false unless @proc_returns == other.proc_returns
412
+ return false unless @proc_bind == other.proc_bind
413
+
414
+ true
415
+ end
416
+
417
+ sig { params(params: Type).returns(T.self_type) }
418
+ def params(**params)
419
+ @proc_params = params
420
+ self
421
+ end
422
+
423
+ sig { params(type: T.untyped).returns(T.self_type) }
424
+ def returns(type)
425
+ @proc_returns = type
426
+ self
427
+ end
428
+
429
+ sig { returns(T.self_type) }
430
+ def void
431
+ @proc_returns = RBI::Type.void
432
+ self
433
+ end
434
+
435
+ sig { params(type: T.untyped).returns(T.self_type) }
436
+ def bind(type)
437
+ @proc_bind = type
438
+ self
439
+ end
440
+
441
+ sig { override.returns(String) }
442
+ def to_rbi
443
+ rbi = +"T.proc"
444
+
445
+ if @proc_bind
446
+ rbi << ".bind(#{@proc_bind})"
447
+ end
448
+
449
+ unless @proc_params.empty?
450
+ rbi << ".params("
451
+ rbi << @proc_params.map { |name, type| "#{name}: #{type.to_rbi}" }.join(", ")
452
+ rbi << ")"
453
+ end
454
+
455
+ rbi << case @proc_returns
456
+ when Void
457
+ ".void"
458
+ else
459
+ ".returns(#{@proc_returns})"
460
+ end
461
+
462
+ rbi
463
+ end
464
+ end
465
+
466
+ # Type builder
467
+
468
+ class << self
469
+ extend T::Sig
470
+
471
+ # Simple
472
+
473
+ # Builds a simple type like `String` or `::Foo::Bar`.
474
+ #
475
+ # It raises a `NameError` if the name is not a valid Ruby class identifier.
476
+ sig { params(name: String).returns(Simple) }
477
+ def simple(name)
478
+ # TODO: should we allow creating the instance anyway and move this to a `validate!` method?
479
+ raise NameError, "Invalid type name: `#{name}`" unless valid_identifier?(name)
480
+
481
+ Simple.new(name)
482
+ end
483
+
484
+ # Literals
485
+
486
+ # Builds a type that represents `T.anything`.
487
+ sig { returns(Anything) }
488
+ def anything
489
+ Anything.new
490
+ end
491
+
492
+ # Builds a type that represents `T.attached_class`.
493
+ sig { returns(AttachedClass) }
494
+ def attached_class
495
+ AttachedClass.new
496
+ end
497
+
498
+ # Builds a type that represents `T::Boolean`.
499
+ sig { returns(Boolean) }
500
+ def boolean
501
+ Boolean.new
502
+ end
503
+
504
+ # Builds a type that represents `T.noreturn`.
505
+ sig { returns(NoReturn) }
506
+ def noreturn
507
+ NoReturn.new
508
+ end
509
+
510
+ # Builds a type that represents `T.self_type`.
511
+ sig { returns(SelfType) }
512
+ def self_type
513
+ SelfType.new
514
+ end
515
+
516
+ # Builds a type that represents `T.untyped`.
517
+ sig { returns(Untyped) }
518
+ def untyped
519
+ Untyped.new
520
+ end
521
+
522
+ # Builds a type that represents `void`.
523
+ sig { returns(Void) }
524
+ def void
525
+ Void.new
526
+ end
527
+
528
+ # Composites
529
+
530
+ # Builds a type that represents the class of another type like `T::Class[Foo]`.
531
+ sig { params(type: Type).returns(Class) }
532
+ def t_class(type)
533
+ Class.new(type)
534
+ end
535
+
536
+ # Builds a type that represents the singleton class of another type like `T.class_of(Foo)`.
537
+ sig { params(type: Simple, type_parameter: T.nilable(Type)).returns(ClassOf) }
538
+ def class_of(type, type_parameter = nil)
539
+ ClassOf.new(type, type_parameter)
540
+ end
541
+
542
+ # Builds a type that represents a nilable of another type like `T.nilable(String)`.
543
+ #
544
+ # Note that this method transforms types such as `T.nilable(T.untyped)` into `T.untyped`, so
545
+ # it may return something other than a `RBI::Type::Nilable`.
546
+ sig { params(type: Type).returns(Type) }
547
+ def nilable(type)
548
+ # TODO: should we move this logic to a `flatten!`, `normalize!` or `simplify!` method?
549
+ return type if type.is_a?(Untyped)
550
+
551
+ if type.nilable?
552
+ type
553
+ else
554
+ Nilable.new(type)
555
+ end
556
+ end
557
+
558
+ # Builds a type that represents an intersection of multiple types like `T.all(String, Integer)`.
559
+ #
560
+ # Note that this method transforms types such as `T.all(String, String)` into `String`, so
561
+ # it may return something other than a `All`.
562
+ sig { params(type1: Type, type2: Type, types: Type).returns(Type) }
563
+ def all(type1, type2, *types)
564
+ types = [type1, type2, *types]
565
+
566
+ # TODO: should we move this logic to a `flatten!`, `normalize!` or `simplify!` method?
567
+ flattened = types.flatten.flat_map do |type|
568
+ case type
569
+ when All
570
+ type.types
571
+ else
572
+ type
573
+ end
574
+ end.uniq
575
+
576
+ if flattened.size == 1
577
+ T.must(flattened.first)
578
+ else
579
+ raise ArgumentError, "RBI::Type.all should have at least 2 types supplied" if flattened.size < 2
580
+
581
+ All.new(flattened)
582
+ end
583
+ end
584
+
585
+ # Builds a type that represents a union of multiple types like `T.any(String, Integer)`.
586
+ #
587
+ # Note that this method transforms types such as `T.any(String, NilClass)` into `T.nilable(String)`, so
588
+ # it may return something other than a `Any`.
589
+ sig { params(type1: Type, type2: Type, types: Type).returns(Type) }
590
+ def any(type1, type2, *types)
591
+ types = [type1, type2, *types]
592
+
593
+ # TODO: should we move this logic to a `flatten!`, `normalize!` or `simplify!` method?
594
+ flattened = types.flatten.flat_map do |type|
595
+ case type
596
+ when Any
597
+ type.types
598
+ else
599
+ type
600
+ end
601
+ end
602
+
603
+ is_nilable = T.let(false, T::Boolean)
604
+
605
+ types = flattened.filter_map do |type|
606
+ case type
607
+ when Simple
608
+ if type.name == "NilClass"
609
+ is_nilable = true
610
+ nil
611
+ else
612
+ type
613
+ end
614
+ when Nilable
615
+ is_nilable = true
616
+ type.type
617
+ else
618
+ type
619
+ end
620
+ end.uniq
621
+
622
+ has_true_class = types.any? { |type| type.is_a?(Simple) && type.name == "TrueClass" }
623
+ has_false_class = types.any? { |type| type.is_a?(Simple) && type.name == "FalseClass" }
624
+
625
+ if has_true_class && has_false_class
626
+ types = types.reject { |type| type.is_a?(Simple) && (type.name == "TrueClass" || type.name == "FalseClass") }
627
+ types << boolean
628
+ end
629
+
630
+ type = case types.size
631
+ when 0
632
+ if is_nilable
633
+ is_nilable = false
634
+ simple("NilClass")
635
+ else
636
+ raise ArgumentError, "RBI::Type.any should have at least 2 types supplied"
637
+ end
638
+ when 1
639
+ T.must(types.first)
640
+ else
641
+ Any.new(types)
642
+ end
643
+
644
+ if is_nilable
645
+ nilable(type)
646
+ else
647
+ type
648
+ end
649
+ end
650
+
651
+ # Generics
652
+
653
+ # Builds a type that represents a generic type like `T::Array[String]` or `T::Hash[Symbol, Integer]`.
654
+ sig { params(name: String, params: T.any(Type, T::Array[Type])).returns(Generic) }
655
+ def generic(name, *params)
656
+ T.unsafe(Generic).new(name, *params.flatten)
657
+ end
658
+
659
+ # Builds a type that represents a type parameter like `T.type_parameter(:U)`.
660
+ sig { params(name: Symbol).returns(TypeParameter) }
661
+ def type_parameter(name)
662
+ TypeParameter.new(name)
663
+ end
664
+
665
+ # Tuples and shapes
666
+
667
+ # Builds a type that represents a tuple type like `[String, Integer]`.
668
+ sig { params(types: T.any(Type, T::Array[Type])).returns(Tuple) }
669
+ def tuple(*types)
670
+ Tuple.new(types.flatten)
671
+ end
672
+
673
+ # Builds a type that represents a shape type like `{name: String, age: Integer}`.
674
+ sig { params(types: T::Hash[T.any(String, Symbol), Type]).returns(Shape) }
675
+ def shape(types = {})
676
+ Shape.new(types)
677
+ end
678
+
679
+ # Proc
680
+
681
+ # Builds a type that represents a proc type like `T.proc.void`.
682
+ sig { returns(Proc) }
683
+ def proc
684
+ Proc.new
685
+ end
686
+
687
+ # We mark the constructor as `protected` because we want to force the use of factories on `Type` to create types
688
+ protected :new
689
+
690
+ private
691
+
692
+ sig { params(name: String).returns(T::Boolean) }
693
+ def valid_identifier?(name)
694
+ Prism.parse("class self::#{name.delete_prefix("::")}; end").success?
695
+ end
696
+ end
697
+
698
+ sig { void }
699
+ def initialize
700
+ @nilable = T.let(false, T::Boolean)
701
+ end
702
+
703
+ # Returns a new type that is `nilable` if it is not already.
704
+ #
705
+ # If the type is already nilable, it returns itself.
706
+ # ```ruby
707
+ # type = RBI::Type.simple("String")
708
+ # type.to_rbi # => "String"
709
+ # type.nilable.to_rbi # => "T.nilable(String)"
710
+ # type.nilable.nilable.to_rbi # => "T.nilable(String)"
711
+ # ```
712
+ sig { returns(Type) }
713
+ def nilable
714
+ Type.nilable(self)
715
+ end
716
+
717
+ # Returns the non-nilable version of the type.
718
+ # If the type is already non-nilable, it returns itself.
719
+ # If the type is nilable, it returns the inner type.
720
+ #
721
+ # ```ruby
722
+ # type = RBI::Type.nilable(RBI::Type.simple("String"))
723
+ # type.to_rbi # => "T.nilable(String)"
724
+ # type.non_nilable.to_rbi # => "String"
725
+ # type.non_nilable.non_nilable.to_rbi # => "String"
726
+ # ```
727
+ sig { returns(Type) }
728
+ def non_nilable
729
+ # TODO: Should this logic be moved into a builder method?
730
+ case self
731
+ when Nilable
732
+ type
733
+ else
734
+ self
735
+ end
736
+ end
737
+
738
+ # Returns whether the type is nilable.
739
+ sig { returns(T::Boolean) }
740
+ def nilable?
741
+ is_a?(Nilable)
742
+ end
743
+
744
+ sig { abstract.params(other: BasicObject).returns(T::Boolean) }
745
+ def ==(other); end
746
+
747
+ sig { params(other: BasicObject).returns(T::Boolean) }
748
+ def eql?(other)
749
+ self == other
750
+ end
751
+
752
+ sig { override.returns(Integer) }
753
+ def hash
754
+ to_rbi.hash
755
+ end
756
+
757
+ sig { abstract.returns(String) }
758
+ def to_rbi; end
759
+
760
+ sig { override.returns(String) }
761
+ def to_s
762
+ to_rbi
763
+ end
764
+ end
765
+ end