rbs 0.5.0 → 0.9.1

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