rbs 0.3.1 → 0.8.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.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +7 -1
  3. data/.gitignore +1 -1
  4. data/CHANGELOG.md +39 -0
  5. data/COPYING +1 -1
  6. data/Gemfile +16 -2
  7. data/README.md +87 -48
  8. data/Rakefile +54 -22
  9. data/bin/rbs-prof +9 -0
  10. data/bin/run_in_md.rb +49 -0
  11. data/bin/test_runner.rb +0 -2
  12. data/docs/sigs.md +6 -6
  13. data/docs/stdlib.md +3 -5
  14. data/docs/syntax.md +6 -3
  15. data/goodcheck.yml +65 -0
  16. data/lib/rbs.rb +3 -0
  17. data/lib/rbs/ast/declarations.rb +115 -14
  18. data/lib/rbs/ast/members.rb +41 -17
  19. data/lib/rbs/cli.rb +315 -122
  20. data/lib/rbs/constant.rb +4 -4
  21. data/lib/rbs/constant_table.rb +51 -45
  22. data/lib/rbs/definition.rb +175 -59
  23. data/lib/rbs/definition_builder.rb +802 -604
  24. data/lib/rbs/environment.rb +352 -210
  25. data/lib/rbs/environment_walker.rb +14 -23
  26. data/lib/rbs/errors.rb +184 -3
  27. data/lib/rbs/factory.rb +14 -0
  28. data/lib/rbs/parser.y +95 -27
  29. data/lib/rbs/prototype/rb.rb +119 -117
  30. data/lib/rbs/prototype/rbi.rb +5 -3
  31. data/lib/rbs/prototype/runtime.rb +34 -7
  32. data/lib/rbs/substitution.rb +12 -1
  33. data/lib/rbs/test.rb +82 -3
  34. data/lib/rbs/test/errors.rb +5 -1
  35. data/lib/rbs/test/hook.rb +133 -259
  36. data/lib/rbs/test/observer.rb +17 -0
  37. data/lib/rbs/test/setup.rb +35 -19
  38. data/lib/rbs/test/setup_helper.rb +29 -0
  39. data/lib/rbs/test/spy.rb +0 -321
  40. data/lib/rbs/test/tester.rb +116 -0
  41. data/lib/rbs/test/type_check.rb +43 -7
  42. data/lib/rbs/type_name_resolver.rb +58 -0
  43. data/lib/rbs/types.rb +94 -2
  44. data/lib/rbs/validator.rb +51 -0
  45. data/lib/rbs/variance_calculator.rb +12 -2
  46. data/lib/rbs/version.rb +1 -1
  47. data/lib/rbs/writer.rb +127 -91
  48. data/rbs.gemspec +0 -9
  49. data/schema/annotation.json +14 -0
  50. data/schema/comment.json +26 -0
  51. data/schema/decls.json +353 -0
  52. data/schema/function.json +87 -0
  53. data/schema/location.json +56 -0
  54. data/schema/members.json +248 -0
  55. data/schema/methodType.json +44 -0
  56. data/schema/types.json +299 -0
  57. data/stdlib/benchmark/benchmark.rbs +151 -151
  58. data/stdlib/builtin/encoding.rbs +2 -0
  59. data/stdlib/builtin/enumerable.rbs +4 -4
  60. data/stdlib/builtin/enumerator.rbs +3 -1
  61. data/stdlib/builtin/fiber.rbs +5 -1
  62. data/stdlib/builtin/file.rbs +0 -3
  63. data/stdlib/builtin/io.rbs +4 -4
  64. data/stdlib/builtin/proc.rbs +1 -2
  65. data/stdlib/builtin/symbol.rbs +1 -1
  66. data/stdlib/builtin/thread.rbs +2 -2
  67. data/stdlib/csv/csv.rbs +4 -6
  68. data/stdlib/fiber/fiber.rbs +117 -0
  69. data/stdlib/json/json.rbs +1 -1
  70. data/stdlib/logger/formatter.rbs +23 -0
  71. data/stdlib/logger/log_device.rbs +39 -0
  72. data/stdlib/logger/logger.rbs +507 -0
  73. data/stdlib/logger/period.rbs +7 -0
  74. data/stdlib/logger/severity.rbs +8 -0
  75. data/stdlib/mutex_m/mutex_m.rbs +77 -0
  76. data/stdlib/pathname/pathname.rbs +6 -6
  77. data/stdlib/prime/integer-extension.rbs +1 -1
  78. data/stdlib/prime/prime.rbs +44 -44
  79. data/stdlib/pty/pty.rbs +159 -0
  80. data/stdlib/tmpdir/tmpdir.rbs +1 -1
  81. metadata +28 -116
  82. data/lib/rbs/test/test_helper.rb +0 -183
@@ -0,0 +1,116 @@
1
+ module RBS
2
+ module Test
3
+ class Tester
4
+ attr_reader :env
5
+ attr_reader :checkers
6
+
7
+ def initialize(env:)
8
+ @env = env
9
+ @checkers = []
10
+ end
11
+
12
+ def factory
13
+ @factory ||= Factory.new
14
+ end
15
+
16
+ def builder
17
+ @builder ||= DefinitionBuilder.new(env: env)
18
+ end
19
+
20
+ def install!(klass, sample_size:)
21
+ RBS.logger.info { "Installing runtime type checker in #{klass}..." }
22
+
23
+ type_name = factory.type_name(klass.name).absolute!
24
+
25
+ builder.build_instance(type_name).tap do |definition|
26
+ instance_key = new_key(type_name, "InstanceChecker")
27
+ Observer.register(instance_key, MethodCallTester.new(klass, builder, definition, kind: :instance, sample_size: sample_size))
28
+
29
+ definition.methods.each do |name, method|
30
+ if method.implemented_in == type_name
31
+ RBS.logger.info { "Setting up method hook in ##{name}..." }
32
+ Hook.hook_instance_method klass, name, key: instance_key
33
+ end
34
+ end
35
+ end
36
+
37
+ builder.build_singleton(type_name).tap do |definition|
38
+ singleton_key = new_key(type_name, "SingletonChecker")
39
+ Observer.register(singleton_key, MethodCallTester.new(klass.singleton_class, builder, definition, kind: :singleton, sample_size: sample_size))
40
+
41
+ definition.methods.each do |name, method|
42
+ if method.implemented_in == type_name || name == :new
43
+ RBS.logger.info { "Setting up method hook in .#{name}..." }
44
+ Hook.hook_singleton_method klass, name, key: singleton_key
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ def new_key(type_name, prefix)
51
+ "#{prefix}__#{type_name}__#{SecureRandom.hex(10)}"
52
+ end
53
+
54
+ class TypeError < Exception
55
+ attr_reader :errors
56
+
57
+ def initialize(errors)
58
+ @errors = errors
59
+
60
+ super "TypeError: #{errors.map {|e| Errors.to_string(e) }.join(", ")}"
61
+ end
62
+ end
63
+
64
+ class MethodCallTester
65
+ attr_reader :self_class
66
+ attr_reader :definition
67
+ attr_reader :builder
68
+ attr_reader :kind
69
+ attr_reader :sample_size
70
+
71
+ def initialize(self_class, builder, definition, kind:, sample_size:)
72
+ @self_class = self_class
73
+ @definition = definition
74
+ @builder = builder
75
+ @kind = kind
76
+ @sample_size = sample_size
77
+ end
78
+
79
+ def env
80
+ builder.env
81
+ end
82
+
83
+ def check
84
+ @check ||= TypeCheck.new(self_class: self_class, builder: builder, sample_size: sample_size)
85
+ end
86
+
87
+ def format_method_name(name)
88
+ case kind
89
+ when :instance
90
+ "##{name}"
91
+ when :singleton
92
+ ".#{name}"
93
+ end
94
+ end
95
+
96
+ def call(receiver, trace)
97
+ method_name = trace.method_name
98
+ method = definition.methods[method_name]
99
+ if method
100
+ RBS.logger.debug { "Type checking `#{self_class}#{format_method_name(method_name)}`..."}
101
+ errors = check.overloaded_call(method, format_method_name(method_name), trace, errors: [])
102
+
103
+ if errors.empty?
104
+ RBS.logger.debug { "No type error detected 👏" }
105
+ else
106
+ RBS.logger.debug { "Detected type error 🚨" }
107
+ raise TypeError.new(errors)
108
+ end
109
+ else
110
+ RBS.logger.error { "Type checking `#{self_class}#{method_name}` call but no method found in definition" }
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -3,10 +3,38 @@ module RBS
3
3
  class TypeCheck
4
4
  attr_reader :self_class
5
5
  attr_reader :builder
6
+ attr_reader :sample_size
6
7
 
7
- def initialize(self_class:, builder:)
8
+ DEFAULT_SAMPLE_SIZE = 100
9
+
10
+ def initialize(self_class:, builder:, sample_size:)
8
11
  @self_class = self_class
9
12
  @builder = builder
13
+ @sample_size = sample_size
14
+ end
15
+
16
+ def overloaded_call(method, method_name, call, errors:)
17
+ es = method.method_types.map do |method_type|
18
+ es = method_call(method_name, method_type, call, errors: [])
19
+
20
+ if es.empty?
21
+ return errors
22
+ else
23
+ es
24
+ end
25
+ end
26
+
27
+ if es.size == 1
28
+ errors.push(*es[0])
29
+ else
30
+ errors << Errors::UnresolvedOverloadingError.new(
31
+ klass: self_class,
32
+ method_name: method_name,
33
+ method_types: method.method_types
34
+ )
35
+ end
36
+
37
+ errors
10
38
  end
11
39
 
12
40
  def method_call(method_name, method_type, call, errors:)
@@ -56,7 +84,7 @@ module RBS
56
84
  end
57
85
 
58
86
  def return(method_name, method_type, fun, call, errors, return_error:)
59
- unless call.exception
87
+ if call.return?
60
88
  unless value(call.return_value, fun.return_type)
61
89
  errors << return_error.new(klass: self_class,
62
90
  method_name: method_name,
@@ -151,6 +179,10 @@ module RBS
151
179
  end
152
180
  end
153
181
 
182
+ def sample(array)
183
+ sample_size && (array.size > sample_size) ? array.sample(sample_size) : array
184
+ end
185
+
154
186
  def value(val, type)
155
187
  case type
156
188
  when Types::Bases::Any
@@ -175,9 +207,14 @@ module RBS
175
207
  klass = Object.const_get(type.name.to_s)
176
208
  case
177
209
  when klass == ::Array
178
- Test.call(val, IS_AP, klass) && val.all? {|v| value(v, type.args[0]) }
210
+ Test.call(val, IS_AP, klass) && sample(val).yield_self do |val|
211
+ val.all? {|v| value(v, type.args[0]) }
212
+ end
179
213
  when klass == ::Hash
180
- Test.call(val, IS_AP, klass) && val.all? {|k, v| value(k, type.args[0]) && value(v, type.args[1]) }
214
+ Test.call(val, IS_AP, klass) && sample(val.keys).yield_self do |keys|
215
+ values = val.values_at(*keys)
216
+ keys.all? {|key| value(key, type.args[0]) } && values.all? {|v| value(v, type.args[1]) }
217
+ end
181
218
  when klass == ::Range
182
219
  Test.call(val, IS_AP, klass) && value(val.begin, type.args[0]) && value(val.end, type.args[0])
183
220
  when klass == ::Enumerator
@@ -198,7 +235,7 @@ module RBS
198
235
  end
199
236
  end
200
237
 
201
- values.all? do |v|
238
+ sample(values).all? do |v|
202
239
  if v.size == 1
203
240
  # Only one block argument.
204
241
  value(v[0], type.args[0]) || value(v, type.args[0])
@@ -220,8 +257,7 @@ module RBS
220
257
  val == klass
221
258
  when Types::Interface
222
259
  methods = Set.new(Test.call(val, METHODS))
223
- decl = builder.env.find_class(type.name)
224
- if (definition = builder.build_interface(type.name, decl))
260
+ if (definition = builder.build_interface(type.name))
225
261
  definition.methods.each_key.all? do |method_name|
226
262
  methods.member?(method_name)
227
263
  end
@@ -0,0 +1,58 @@
1
+ module RBS
2
+ class TypeNameResolver
3
+ Query = Struct.new(:type_name, :context, keyword_init: true)
4
+
5
+ attr_reader :all_names
6
+ attr_reader :cache
7
+
8
+ def initialize()
9
+ @all_names = Set[]
10
+ @cache = {}
11
+ end
12
+
13
+ def self.from_env(env)
14
+ new.add_names(env.class_decls.keys)
15
+ .add_names(env.interface_decls.keys)
16
+ .add_names(env.alias_decls.keys)
17
+ end
18
+
19
+ def add_names(names)
20
+ all_names.merge(names)
21
+ self
22
+ end
23
+
24
+ def try_cache(query)
25
+ cache.fetch(query) do
26
+ result = yield
27
+ cache[query] = result
28
+ end
29
+ end
30
+
31
+ def resolve(type_name, context:)
32
+ if type_name.absolute?
33
+ return type_name
34
+ end
35
+
36
+ query = Query.new(type_name: type_name, context: context)
37
+ try_cache(query) do
38
+ path_head, *path_tail = type_name.to_namespace.path
39
+ name_head = TypeName.new(name: path_head, namespace: Namespace.empty)
40
+
41
+ absolute_head = context.each.find do |namespace|
42
+ full_name = name_head.with_prefix(namespace)
43
+ has_name?(full_name) and break full_name
44
+ end
45
+
46
+ if absolute_head
47
+ has_name?(Namespace.new(path: absolute_head.to_namespace.path.push(*path_tail), absolute: true).to_type_name)
48
+ end
49
+ end
50
+ end
51
+
52
+ def has_name?(full_name)
53
+ if all_names.include?(full_name)
54
+ full_name
55
+ end
56
+ end
57
+ end
58
+ end
@@ -12,6 +12,12 @@ module RBS
12
12
  end
13
13
  end
14
14
 
15
+ module NoTypeName
16
+ def map_type_name
17
+ self
18
+ end
19
+ end
20
+
15
21
  module EmptyEachType
16
22
  def each_type
17
23
  if block_given?
@@ -43,6 +49,7 @@ module RBS
43
49
  include NoFreeVariables
44
50
  include NoSubst
45
51
  include EmptyEachType
52
+ include NoTypeName
46
53
 
47
54
  def to_json(*a)
48
55
  klass = to_s.to_sym
@@ -82,7 +89,11 @@ module RBS
82
89
  class Top < Base; end
83
90
  class Bottom < Base; end
84
91
  class Self < Base; end
85
- class Instance < Base; end
92
+ class Instance < Base
93
+ def sub(s)
94
+ s.apply(self)
95
+ end
96
+ end
86
97
  class Class < Base; end
87
98
  end
88
99
 
@@ -90,6 +101,8 @@ module RBS
90
101
  attr_reader :name
91
102
  attr_reader :location
92
103
 
104
+ include NoTypeName
105
+
93
106
  def initialize(name:, location:)
94
107
  @name = name
95
108
  @location = location
@@ -174,6 +187,13 @@ module RBS
174
187
  end
175
188
 
176
189
  include EmptyEachType
190
+
191
+ def map_type_name
192
+ ClassSingleton.new(
193
+ name: yield(name, location, self),
194
+ location: location
195
+ )
196
+ end
177
197
  end
178
198
 
179
199
  module Application
@@ -235,6 +255,14 @@ module RBS
235
255
  args: args.map {|ty| ty.sub(s) },
236
256
  location: location)
237
257
  end
258
+
259
+ def map_type_name(&block)
260
+ Interface.new(
261
+ name: yield(name, location, self),
262
+ args: args.map {|type| type.map_type_name(&block) },
263
+ location: location
264
+ )
265
+ end
238
266
  end
239
267
 
240
268
  class ClassInstance
@@ -257,6 +285,14 @@ module RBS
257
285
  args: args.map {|ty| ty.sub(s) },
258
286
  location: location)
259
287
  end
288
+
289
+ def map_type_name(&block)
290
+ ClassInstance.new(
291
+ name: yield(name, location, self),
292
+ args: args.map {|type| type.map_type_name(&block) },
293
+ location: location
294
+ )
295
+ end
260
296
  end
261
297
 
262
298
  class Alias
@@ -290,6 +326,13 @@ module RBS
290
326
  end
291
327
 
292
328
  include EmptyEachType
329
+
330
+ def map_type_name
331
+ Alias.new(
332
+ name: yield(name, location, self),
333
+ location: location
334
+ )
335
+ end
293
336
  end
294
337
 
295
338
  class Tuple
@@ -343,6 +386,13 @@ module RBS
343
386
  enum_for :each_type
344
387
  end
345
388
  end
389
+
390
+ def map_type_name(&block)
391
+ Tuple.new(
392
+ types: types.map {|type| type.map_type_name(&block) },
393
+ location: location
394
+ )
395
+ end
346
396
  end
347
397
 
348
398
  class Record
@@ -401,6 +451,13 @@ module RBS
401
451
  enum_for :each_type
402
452
  end
403
453
  end
454
+
455
+ def map_type_name(&block)
456
+ Record.new(
457
+ fields: fields.transform_values {|ty| ty.map_type_name(&block) },
458
+ location: location
459
+ )
460
+ end
404
461
  end
405
462
 
406
463
  class Optional
@@ -449,6 +506,13 @@ module RBS
449
506
  enum_for :each_type
450
507
  end
451
508
  end
509
+
510
+ def map_type_name(&block)
511
+ Optional.new(
512
+ type: type.map_type_name(&block),
513
+ location: location
514
+ )
515
+ end
452
516
  end
453
517
 
454
518
  class Union
@@ -510,6 +574,13 @@ module RBS
510
574
  enum_for :map_type
511
575
  end
512
576
  end
577
+
578
+ def map_type_name(&block)
579
+ Union.new(
580
+ types: types.map {|type| type.map_type_name(&block) },
581
+ location: location
582
+ )
583
+ end
513
584
  end
514
585
 
515
586
  class Intersection
@@ -572,6 +643,13 @@ module RBS
572
643
  enum_for :map_type
573
644
  end
574
645
  end
646
+
647
+ def map_type_name(&block)
648
+ Intersection.new(
649
+ types: types.map {|type| type.map_type_name(&block) },
650
+ location: location
651
+ )
652
+ end
575
653
  end
576
654
 
577
655
  class Function
@@ -648,7 +726,7 @@ module RBS
648
726
  other.required_keywords == required_keywords &&
649
727
  other.optional_keywords == optional_keywords &&
650
728
  other.rest_keywords == rest_keywords &&
651
- return_type == return_type
729
+ other.return_type == return_type
652
730
  end
653
731
 
654
732
  alias eql? ==
@@ -710,6 +788,12 @@ module RBS
710
788
  end
711
789
  end
712
790
 
791
+ def map_type_name(&block)
792
+ map_type do |type|
793
+ type.map_type_name(&block)
794
+ end
795
+ end
796
+
713
797
  def each_type
714
798
  if block_given?
715
799
  required_positionals.each {|param| yield param.type }
@@ -899,6 +983,13 @@ module RBS
899
983
  enum_for :each_type
900
984
  end
901
985
  end
986
+
987
+ def map_type_name(&block)
988
+ Proc.new(
989
+ type: type.map_type_name(&block),
990
+ location: location
991
+ )
992
+ end
902
993
  end
903
994
 
904
995
  class Literal
@@ -923,6 +1014,7 @@ module RBS
923
1014
  include NoFreeVariables
924
1015
  include NoSubst
925
1016
  include EmptyEachType
1017
+ include NoTypeName
926
1018
 
927
1019
  def to_json(*a)
928
1020
  { class: :literal, literal: literal.inspect, location: location }.to_json(*a)