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,17 @@
1
+ module RBS
2
+ module Test
3
+ module Observer
4
+ @@observers = {}
5
+
6
+ class <<self
7
+ def notify(key, *args)
8
+ @@observers[key]&.call(*args)
9
+ end
10
+
11
+ def register(key, object = nil, &block)
12
+ @@observers[key] = object || block
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,58 +1,74 @@
1
1
  require "rbs"
2
2
  require "rbs/test"
3
-
4
3
  require "optparse"
5
4
  require "shellwords"
6
5
 
6
+ include RBS::Test::SetupHelper
7
+
7
8
  logger = Logger.new(STDERR)
8
9
 
9
10
  begin
10
11
  opts = Shellwords.shellsplit(ENV["RBS_TEST_OPT"] || "-I sig")
11
- filter = ENV.fetch("RBS_TEST_TARGET").split(",")
12
- skips = (ENV["RBS_TEST_SKIP"] || "").split(",")
13
- logger.level = (ENV["RBS_TEST_LOGLEVEL"] || "info")
14
- raise_on_error = ENV["RBS_TEST_RAISE"]
15
- rescue
12
+ filter = ENV.fetch('RBS_TEST_TARGET').split(',').map! { |e| e.strip }
13
+ skips = (ENV['RBS_TEST_SKIP'] || '').split(',').map! { |e| e.strip }
14
+ RBS.logger_level = (ENV["RBS_TEST_LOGLEVEL"] || "info")
15
+ sample_size = get_sample_size(ENV['RBS_TEST_SAMPLE_SIZE'] || '')
16
+ rescue InvalidSampleSizeError => exception
17
+ RBS.logger.error exception.message
18
+ exit 1
19
+ rescue Exception => e
20
+ raise e.message
16
21
  STDERR.puts "rbs/test/setup handles the following environment variables:"
17
22
  STDERR.puts " [REQUIRED] RBS_TEST_TARGET: test target class name, `Foo::Bar,Foo::Baz` for each class or `Foo::*` for all classes under `Foo`"
18
23
  STDERR.puts " [OPTIONAL] RBS_TEST_SKIP: skip testing classes"
19
24
  STDERR.puts " [OPTIONAL] RBS_TEST_OPT: options for signatures (`-r` for libraries or `-I` for signatures)"
20
25
  STDERR.puts " [OPTIONAL] RBS_TEST_LOGLEVEL: one of debug|info|warn|error|fatal (defaults to info)"
21
- STDERR.puts " [OPTIONAL] RBS_TEST_RAISE: specify any value to raise an exception when type error is detected"
26
+ STDERR.puts " [OPTIONAL] RBS_TEST_SAMPLE_SIZE: sets the amount of values in a collection to be type-checked (Set to `ALL` to type check all the values)"
22
27
  exit 1
23
28
  end
24
29
 
25
- hooks = []
26
-
27
- env = RBS::Environment.new
28
-
29
30
  loader = RBS::EnvironmentLoader.new
30
31
  OptionParser.new do |opts|
31
32
  opts.on("-r [LIB]") do |name| loader.add(library: name) end
32
33
  opts.on("-I [DIR]") do |dir| loader.add(path: Pathname(dir)) end
33
34
  end.parse!(opts)
34
- loader.load(env: env)
35
+
36
+ env = RBS::Environment.from_loader(loader).resolve_type_names
35
37
 
36
38
  def match(filter, name)
37
39
  if filter.end_with?("*")
38
- name.start_with?(filter[0, filter.size - 1]) || name == filter[0, filter.size-3]
40
+ size = filter.size
41
+ name.start_with?(filter[0, size - 1]) || name == filter[0, size-3]
39
42
  else
40
43
  filter == name
41
44
  end
42
45
  end
43
46
 
47
+ def to_absolute_typename(type_name)
48
+ RBS::Factory.new().type_name(type_name).absolute!
49
+ end
50
+
51
+ tester = RBS::Test::Tester.new(env: env)
52
+
44
53
  TracePoint.trace :end do |tp|
45
- class_name = tp.self.name
54
+ class_name = tp.self.name&.yield_self {|name| to_absolute_typename name }
46
55
 
47
56
  if class_name
48
- if filter.any? {|f| match(f, class_name) } && skips.none? {|f| match(f, class_name) }
49
- type_name = RBS::Namespace.parse(class_name).absolute!.to_type_name
50
- if hooks.none? {|hook| hook.klass == tp.self }
51
- if env.find_class(type_name)
57
+ if filter.any? {|f| match(to_absolute_typename(f).to_s, class_name.to_s) } && skips.none? {|f| match(f, class_name.to_s) }
58
+ if tester.checkers.none? {|hook| hook.klass == tp.self }
59
+ if env.class_decls.key?(class_name)
52
60
  logger.info "Setting up hooks for #{class_name}"
53
- hooks << RBS::Test::Hook.install(env, tp.self, logger: logger).verify_all.raise_on_error!(raise_on_error)
61
+ tester.install!(tp.self, sample_size: sample_size)
54
62
  end
55
63
  end
56
64
  end
57
65
  end
58
66
  end
67
+
68
+ at_exit do
69
+ if $!.nil? || $!.is_a?(SystemExit) && $!.success?
70
+ logger.warn "No type checker was installed! " if tester.checkers.empty?
71
+ end
72
+ end
73
+
74
+
@@ -0,0 +1,29 @@
1
+ module RBS
2
+ module Test
3
+ module SetupHelper
4
+ class InvalidSampleSizeError < StandardError
5
+ attr_reader :string
6
+
7
+ def initialize(string)
8
+ @string = string
9
+ super("Sample size should be a positive integer: `#{string}`")
10
+ end
11
+ end
12
+
13
+ DEFAULT_SAMPLE_SIZE = 100
14
+
15
+ def get_sample_size(string)
16
+ case string
17
+ when ""
18
+ DEFAULT_SAMPLE_SIZE
19
+ when 'ALL'
20
+ nil
21
+ else
22
+ int_size = string.to_i
23
+ raise InvalidSampleSizeError.new(string) unless int_size.positive?
24
+ int_size
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,325 +1,4 @@
1
1
  module RBS
2
2
  module Test
3
- module Spy
4
- def self.singleton_method(object, method_name)
5
- spy = SingletonSpy.new(object: object, method_name: method_name)
6
-
7
- if block_given?
8
- begin
9
- spy.setup
10
- yield spy
11
- ensure
12
- spy.reset
13
- end
14
- else
15
- spy
16
- end
17
- end
18
-
19
- def self.instance_method(mod, method_name)
20
- spy = InstanceSpy.new(mod: mod, method_name: method_name)
21
-
22
- if block_given?
23
- begin
24
- spy.setup
25
- yield spy
26
- ensure
27
- spy.reset
28
- end
29
- else
30
- spy
31
- end
32
- end
33
-
34
- def self.wrap(object, method_name)
35
- spy = WrapSpy.new(object: object, method_name: method_name)
36
-
37
- if block_given?
38
- begin
39
- yield spy, spy.wrapped_object
40
- end
41
- else
42
- spy
43
- end
44
- end
45
-
46
- class SingletonSpy
47
- attr_accessor :callback
48
- attr_reader :method_name
49
- attr_reader :object
50
-
51
- def initialize(object:, method_name:)
52
- @object = object
53
- @method_name = method_name
54
- @callback = -> (_) { }
55
- end
56
-
57
- def setup
58
- spy = self
59
-
60
- object.singleton_class.class_eval do
61
- remove_method spy.method_name
62
- define_method spy.method_name, spy.spy()
63
- end
64
- end
65
-
66
- def spy()
67
- spy = self
68
-
69
- -> (*args, &block) do
70
- return_value = nil
71
- exception = nil
72
- block_calls = []
73
-
74
- spy_block = if block
75
- Object.new.instance_eval do |fresh|
76
- -> (*block_args) do
77
- block_exn = nil
78
- block_return = nil
79
-
80
- begin
81
- block_return = if self.equal?(fresh)
82
- # no instance eval
83
- block.call(*block_args)
84
- else
85
- self.instance_exec(*block_args, &block)
86
- end
87
- rescue Exception => exn
88
- block_exn = exn
89
- end
90
-
91
- block_calls << ArgumentsReturn.new(
92
- arguments: block_args,
93
- return_value: block_return,
94
- exception: block_exn
95
- )
96
-
97
- if block_exn
98
- raise block_exn
99
- else
100
- block_return
101
- end
102
- end.ruby2_keywords
103
- end
104
- end
105
-
106
- begin
107
- return_value = super(*args, &spy_block)
108
- rescue Exception => exn
109
- exception = exn
110
- end
111
-
112
- trace = CallTrace.new(
113
- method_name: spy.method_name,
114
- method_call: ArgumentsReturn.new(
115
- arguments: args,
116
- return_value: return_value,
117
- exception: exception,
118
- ),
119
- block_calls: block_calls,
120
- block_given: block != nil
121
- )
122
-
123
- spy.callback.call(trace)
124
-
125
- if exception
126
- raise exception
127
- else
128
- return_value
129
- end
130
- end.ruby2_keywords
131
- end
132
-
133
- def reset
134
- if object.singleton_class.methods.include?(method_name)
135
- object.singleton_class.remove_method method_name
136
- end
137
- end
138
- end
139
-
140
- class InstanceSpy
141
- attr_accessor :callback
142
- attr_reader :mod
143
- attr_reader :method_name
144
- attr_reader :original_method
145
-
146
- def initialize(mod:, method_name:)
147
- @mod = mod
148
- @method_name = method_name
149
- @original_method = mod.instance_method(method_name)
150
- @callback = -> (_) { }
151
- end
152
-
153
- def setup
154
- spy = self
155
-
156
- mod.class_eval do
157
- remove_method spy.method_name
158
- define_method spy.method_name, spy.spy()
159
- end
160
- end
161
-
162
- def reset
163
- spy = self
164
-
165
- mod.class_eval do
166
- remove_method spy.method_name
167
- define_method spy.method_name, spy.original_method
168
- end
169
- end
170
-
171
- def spy
172
- spy = self
173
-
174
- -> (*args, &block) do
175
- return_value = nil
176
- exception = nil
177
- block_calls = []
178
-
179
- spy_block = if block
180
- Object.new.instance_eval do |fresh|
181
- -> (*block_args) do
182
- block_exn = nil
183
- block_return = nil
184
-
185
- begin
186
- block_return = if self.equal?(fresh)
187
- # no instance eval
188
- block.call(*block_args)
189
- else
190
- self.instance_exec(*block_args, &block)
191
- end
192
- rescue Exception => exn
193
- block_exn = exn
194
- end
195
-
196
- block_calls << ArgumentsReturn.new(
197
- arguments: block_args,
198
- return_value: block_return,
199
- exception: block_exn
200
- )
201
-
202
- if block_exn
203
- raise block_exn
204
- else
205
- block_return
206
- end
207
- end.ruby2_keywords
208
- end
209
- end
210
-
211
- begin
212
- return_value = spy.original_method.bind_call(self, *args, &spy_block)
213
- rescue Exception => exn
214
- exception = exn
215
- end
216
-
217
- trace = CallTrace.new(
218
- method_name: spy.method_name,
219
- method_call: ArgumentsReturn.new(
220
- arguments: args,
221
- return_value: return_value,
222
- exception: exception,
223
- ),
224
- block_calls: block_calls,
225
- block_given: block != nil
226
- )
227
-
228
- spy.callback.call(trace)
229
-
230
- if exception
231
- raise exception
232
- else
233
- return_value
234
- end
235
- end.ruby2_keywords
236
- end
237
- end
238
-
239
- class WrapSpy
240
- attr_accessor :callback
241
- attr_reader :object
242
- attr_reader :method_name
243
-
244
- def initialize(object:, method_name:)
245
- @callback = -> (_) { }
246
- @object = object
247
- @method_name = method_name
248
- end
249
-
250
- def wrapped_object
251
- spy = self
252
-
253
- Class.new(BasicObject) do
254
- define_method(:method_missing) do |name, *args, &block|
255
- spy.object.__send__(name, *args, &block)
256
- end
257
-
258
- define_method(spy.method_name, -> (*args, &block) {
259
- return_value = nil
260
- exception = nil
261
- block_calls = []
262
-
263
- spy_block = if block
264
- Object.new.instance_eval do |fresh|
265
- -> (*block_args) do
266
- block_exn = nil
267
- block_return = nil
268
-
269
- begin
270
- block_return = if self.equal?(fresh)
271
- # no instance eval
272
- block.call(*block_args)
273
- else
274
- self.instance_exec(*block_args, &block)
275
- end
276
- rescue Exception => exn
277
- block_exn = exn
278
- end
279
-
280
- block_calls << ArgumentsReturn.new(
281
- arguments: block_args,
282
- return_value: block_return,
283
- exception: block_exn
284
- )
285
-
286
- if block_exn
287
- raise block_exn
288
- else
289
- block_return
290
- end
291
- end.ruby2_keywords
292
- end
293
- end
294
-
295
- begin
296
- return_value = spy.object.__send__(spy.method_name, *args, &spy_block)
297
- rescue ::Exception => exn
298
- exception = exn
299
- end
300
-
301
- trace = CallTrace.new(
302
- method_name: spy.method_name,
303
- method_call: ArgumentsReturn.new(
304
- arguments: args,
305
- return_value: return_value,
306
- exception: exception,
307
- ),
308
- block_calls: block_calls,
309
- block_given: block != nil
310
- )
311
-
312
- spy.callback.call(trace)
313
-
314
- if exception
315
- spy.object.__send__(:raise, exception)
316
- else
317
- return_value
318
- end
319
- }.ruby2_keywords)
320
- end.new()
321
- end
322
- end
323
- end
324
3
  end
325
4
  end