rbi 0.1.14 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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