rbs 0.3.1 → 0.8.0

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