rbs 0.5.0 → 0.9.1

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -0
  3. data/Gemfile +2 -0
  4. data/Rakefile +2 -3
  5. data/docs/stdlib.md +0 -2
  6. data/docs/syntax.md +6 -3
  7. data/goodcheck.yml +65 -0
  8. data/lib/rbs.rb +1 -0
  9. data/lib/rbs/ast/comment.rb +6 -0
  10. data/lib/rbs/ast/declarations.rb +44 -6
  11. data/lib/rbs/cli.rb +21 -3
  12. data/lib/rbs/constant_table.rb +1 -1
  13. data/lib/rbs/definition_builder.rb +290 -124
  14. data/lib/rbs/environment.rb +50 -37
  15. data/lib/rbs/errors.rb +68 -25
  16. data/lib/rbs/factory.rb +14 -0
  17. data/lib/rbs/location.rb +15 -0
  18. data/lib/rbs/parser.y +89 -28
  19. data/lib/rbs/prototype/rb.rb +2 -2
  20. data/lib/rbs/prototype/rbi.rb +1 -1
  21. data/lib/rbs/prototype/runtime.rb +1 -1
  22. data/lib/rbs/substitution.rb +6 -2
  23. data/lib/rbs/test.rb +82 -3
  24. data/lib/rbs/test/errors.rb +5 -1
  25. data/lib/rbs/test/hook.rb +133 -259
  26. data/lib/rbs/test/observer.rb +17 -0
  27. data/lib/rbs/test/setup.rb +37 -20
  28. data/lib/rbs/test/setup_helper.rb +29 -0
  29. data/lib/rbs/test/spy.rb +0 -321
  30. data/lib/rbs/test/tester.rb +118 -0
  31. data/lib/rbs/test/type_check.rb +42 -5
  32. data/lib/rbs/validator.rb +4 -0
  33. data/lib/rbs/version.rb +1 -1
  34. data/lib/rbs/writer.rb +2 -2
  35. data/schema/decls.json +21 -10
  36. data/stdlib/builtin/enumerable.rbs +2 -2
  37. data/stdlib/builtin/proc.rbs +1 -2
  38. data/stdlib/json/json.rbs +6 -0
  39. data/stdlib/logger/formatter.rbs +23 -0
  40. data/stdlib/logger/log_device.rbs +39 -0
  41. data/stdlib/logger/logger.rbs +507 -0
  42. data/stdlib/logger/period.rbs +7 -0
  43. data/stdlib/logger/severity.rbs +8 -0
  44. data/stdlib/pty/pty.rbs +159 -0
  45. metadata +13 -3
  46. data/lib/rbs/test/test_helper.rb +0 -180
@@ -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,60 +1,77 @@
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
+ end
20
+
21
+ if filter.empty?
16
22
  STDERR.puts "rbs/test/setup handles the following environment variables:"
17
23
  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
24
  STDERR.puts " [OPTIONAL] RBS_TEST_SKIP: skip testing classes"
19
25
  STDERR.puts " [OPTIONAL] RBS_TEST_OPT: options for signatures (`-r` for libraries or `-I` for signatures)"
20
26
  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"
27
+ 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
28
  exit 1
23
29
  end
24
30
 
25
- hooks = []
26
-
27
- env = RBS::Environment.new
28
-
29
31
  loader = RBS::EnvironmentLoader.new
30
32
  OptionParser.new do |opts|
31
33
  opts.on("-r [LIB]") do |name| loader.add(library: name) end
32
34
  opts.on("-I [DIR]") do |dir| loader.add(path: Pathname(dir)) end
33
35
  end.parse!(opts)
34
- loader.load(env: env)
35
36
 
36
- env = env.resolve_type_names
37
+ env = RBS::Environment.from_loader(loader).resolve_type_names
37
38
 
38
39
  def match(filter, name)
39
40
  if filter.end_with?("*")
40
- name.start_with?(filter[0, filter.size - 1]) || name == filter[0, filter.size-3]
41
+ size = filter.size
42
+ name.start_with?(filter[0, size - 1]) || name == filter[0, size-3]
41
43
  else
42
44
  filter == name
43
45
  end
44
46
  end
45
47
 
48
+ def to_absolute_typename(type_name)
49
+ RBS::Factory.new().type_name(type_name).absolute!
50
+ end
51
+
52
+ tester = RBS::Test::Tester.new(env: env)
53
+
54
+ module_name = Module.instance_method(:name)
55
+
46
56
  TracePoint.trace :end do |tp|
47
- class_name = tp.self.name
57
+ class_name = module_name.bind(tp.self).call&.yield_self {|name| to_absolute_typename name }
48
58
 
49
59
  if class_name
50
- if filter.any? {|f| match(f, class_name) } && skips.none? {|f| match(f, class_name) }
51
- type_name = RBS::Namespace.parse(class_name).absolute!.to_type_name
52
- if hooks.none? {|hook| hook.klass == tp.self }
53
- if env.class_decls.key?(type_name)
60
+ if filter.any? {|f| match(to_absolute_typename(f).to_s, class_name.to_s) } && skips.none? {|f| match(f, class_name.to_s) }
61
+ unless tester.targets.include?(tp.self)
62
+ if env.class_decls.key?(class_name)
54
63
  logger.info "Setting up hooks for #{class_name}"
55
- hooks << RBS::Test::Hook.install(env, tp.self, logger: logger).verify_all.raise_on_error!(raise_on_error)
64
+ tester.install!(tp.self, sample_size: sample_size)
56
65
  end
57
66
  end
58
67
  end
59
68
  end
60
69
  end
70
+
71
+ at_exit do
72
+ if $!.nil? || $!.is_a?(SystemExit) && $!.success?
73
+ if tester.targets.empty?
74
+ logger.warn "No type checker was installed!"
75
+ end
76
+ end
77
+ end
@@ -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