raap 0.6.0 → 0.7.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -0
- data/README.md +55 -1
- data/lib/raap/cli.rb +12 -0
- data/lib/raap/coverage.rb +188 -0
- data/lib/raap/function_type.rb +31 -11
- data/lib/raap/method_property.rb +32 -1
- data/lib/raap/method_type.rb +12 -3
- data/lib/raap/rbs.rb +42 -0
- data/lib/raap/type.rb +28 -20
- data/lib/raap/type_substitution.rb +4 -0
- data/lib/raap/value/interface.rb +9 -20
- data/lib/raap/value/intersection.rb +6 -7
- data/lib/raap/value/variable.rb +1 -1
- data/lib/raap/version.rb +1 -1
- data/lib/raap.rb +1 -0
- data/public/example.webp +0 -0
- data/sig/raap.rbs +52 -11
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13f734d53db009621045979dfc7d705b14c2f378625ff36067784995e8889134
|
4
|
+
data.tar.gz: 472adf32ff8ea739d83a5b41c6a62e80149b29e34d44536df844010eb2e8cb90
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc3782c7a65e098eef727163ccc069ca27b339eeaadc7be44abd2eac94c78fa74443c413364e579998dd49aa11c5be38bd27f6504f92759580ec893dd54a3e97
|
7
|
+
data.tar.gz: 68e133fa67a03d4c84ab264c1e0fd1c40204da8995a552e99912eb61c1cddb14d9c81bac26c4aa04615d9833dfa74ad64bd212071dd3195dacad6ae85bd23b54
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# RaaP
|
2
2
|
|
3
|
-
<img src="https://raw.githubusercontent.com/ksss/raap/main/public/jacket.webp" width=
|
3
|
+
<img src="https://raw.githubusercontent.com/ksss/raap/main/public/jacket.webp" width=500/>
|
4
4
|
|
5
5
|
## RBS as a Property
|
6
6
|
|
@@ -14,6 +14,10 @@ The return value of the method is checked to see if it matches the type, if not,
|
|
14
14
|
|
15
15
|
If you write an RBS, it becomes a test case.
|
16
16
|
|
17
|
+
## Example
|
18
|
+
|
19
|
+
<img src="https://raw.githubusercontent.com/ksss/raap/main/public/example.webp" width=700/>
|
20
|
+
|
17
21
|
## Concept
|
18
22
|
|
19
23
|
If you has next signature.
|
@@ -88,6 +92,20 @@ $ raap $(cat test/raap.txt) # You can manage the methods to be tested in a fil
|
|
88
92
|
- info: A somewhat visualized representation of the execution state.
|
89
93
|
- debug: All information including stack traces.
|
90
94
|
|
95
|
+
## Coverage
|
96
|
+
|
97
|
+
RaaP randomly selects a type and generates a value for optional and union types.
|
98
|
+
|
99
|
+
By default, displays the coverage of the type that actually produced the value.
|
100
|
+
|
101
|
+
The coverage display can help you determine if a correction is needed.
|
102
|
+
|
103
|
+
### Meaning of colored types
|
104
|
+
|
105
|
+
- 🔴 Red: A type that has never been used to generate or validate a value.
|
106
|
+
- 🟡 Yellow: It is systematically difficult to determine if the type was used or not. (FIXME)
|
107
|
+
- 🟢 Green: The type used to generate and validate the value.
|
108
|
+
|
91
109
|
## Size
|
92
110
|
|
93
111
|
Random values are determined based on size.
|
@@ -150,6 +168,42 @@ You can specify size of step like `Integer#step: (to: Integer, by: Integer)`.
|
|
150
168
|
By default, raap only validates public methods.
|
151
169
|
However, by setting this option, it is possible to intentionally validate private methods in the RBS.
|
152
170
|
|
171
|
+
### `--preload path`
|
172
|
+
|
173
|
+
Simply call `Kernel.load`.
|
174
|
+
However, this simplifies the preliminary preparation.
|
175
|
+
|
176
|
+
#### Preload examples
|
177
|
+
|
178
|
+
1) Check signature of aws-sdk-s3
|
179
|
+
|
180
|
+
```rb
|
181
|
+
$ cat preload.rb
|
182
|
+
require 'aws-sdk-s3'
|
183
|
+
|
184
|
+
# Register initialize of `Aws::S3::Client` class.
|
185
|
+
RaaP::Type.register("Aws::S3::Client") do
|
186
|
+
[:call, Aws::S3::Client, :new, [], { stub_responses: true }, nil]
|
187
|
+
end
|
188
|
+
|
189
|
+
$ raap --preload ./preload.rb 'Aws::S3::Client#get_object'
|
190
|
+
```
|
191
|
+
|
192
|
+
2) Output ruby coverage by simplecov
|
193
|
+
|
194
|
+
```rb
|
195
|
+
$ cat preload.rb
|
196
|
+
require 'simplecov'
|
197
|
+
|
198
|
+
SimpleCov.command_name 'raap'
|
199
|
+
SimpleCov.start do
|
200
|
+
enable_coverage :branch
|
201
|
+
add_filter "/test/"
|
202
|
+
end
|
203
|
+
|
204
|
+
$ bundle exec raap --preload ./preload.rb -r my_class 'MyClass'
|
205
|
+
```
|
206
|
+
|
153
207
|
## First support is CLI
|
154
208
|
|
155
209
|
In RaaP, usage through the CLI is the primary support focus, and efforts are made to maintain compatibility. The use of the library's code (such as `RaaP::Type`) does not guarantee compatibility.
|
data/lib/raap/cli.rb
CHANGED
@@ -12,6 +12,7 @@ module RaaP
|
|
12
12
|
:size_to,
|
13
13
|
:size_by,
|
14
14
|
:allow_private,
|
15
|
+
:coverage,
|
15
16
|
keyword_init: true
|
16
17
|
)
|
17
18
|
|
@@ -44,6 +45,7 @@ module RaaP
|
|
44
45
|
size_from: 0,
|
45
46
|
size_to: 99,
|
46
47
|
size_by: 1,
|
48
|
+
coverage: true,
|
47
49
|
allow_private: false,
|
48
50
|
)
|
49
51
|
@argv = argv
|
@@ -80,6 +82,12 @@ module RaaP
|
|
80
82
|
o.on('--allow-private', "default: #{@option.allow_private}") do
|
81
83
|
@option.allow_private = true
|
82
84
|
end
|
85
|
+
o.on('--preload path', 'Kernel.load path') do |path|
|
86
|
+
Kernel.load path
|
87
|
+
end
|
88
|
+
o.on('--[no-]coverage', "Show coverage for RBS (default: #{@option.coverage})") do |arg|
|
89
|
+
@option.coverage = arg
|
90
|
+
end
|
83
91
|
end.parse!(@argv)
|
84
92
|
|
85
93
|
@option.dirs.each do |dir|
|
@@ -300,6 +308,7 @@ module RaaP
|
|
300
308
|
timeout: @option.timeout,
|
301
309
|
allow_private: @option.allow_private,
|
302
310
|
)
|
311
|
+
RaaP::Coverage.start(method_type) if @option.coverage
|
303
312
|
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
304
313
|
stats = prop.run do |called|
|
305
314
|
case called
|
@@ -337,6 +346,9 @@ module RaaP
|
|
337
346
|
end
|
338
347
|
end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
339
348
|
puts
|
349
|
+
RaaP::Coverage.show($stdout) if @option.coverage
|
350
|
+
puts
|
351
|
+
|
340
352
|
time_diff = end_time - start_time
|
341
353
|
time = ", time: #{(time_diff * 1000).round}ms"
|
342
354
|
stats_log = "success: #{stats.success}, skip: #{stats.skip}, exception: #{stats.exception}#{time}"
|
@@ -0,0 +1,188 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RaaP
|
4
|
+
module Coverage
|
5
|
+
class Writer
|
6
|
+
def initialize(method_type, cov)
|
7
|
+
@method_type = method_type
|
8
|
+
@cov = cov
|
9
|
+
@cur = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def write(io)
|
13
|
+
RaaP.logger.debug { "Coverage: #{@cov}" }
|
14
|
+
ml = @method_type.location
|
15
|
+
unless ml
|
16
|
+
RaaP.logger.warn("No location information for `#{@method_type}`")
|
17
|
+
return
|
18
|
+
end
|
19
|
+
if ml.key?(:keyword)
|
20
|
+
# attr_{reader,writer,accessor}
|
21
|
+
phantom_member = RBS.parse_member(ml.source)
|
22
|
+
case phantom_member
|
23
|
+
when ::RBS::AST::Members::Attribute
|
24
|
+
unless phantom_member.location
|
25
|
+
RaaP.logger.warn("No location information for `#{phantom_member}`")
|
26
|
+
return
|
27
|
+
end
|
28
|
+
write_type(io, "return", phantom_member.type, :abs)
|
29
|
+
io.write(slice(@cur, @cur...phantom_member.location.end_pos))
|
30
|
+
else
|
31
|
+
RaaP.logger.error("#{phantom_member.class} is not supported")
|
32
|
+
return
|
33
|
+
end
|
34
|
+
else
|
35
|
+
# def name: () -> type
|
36
|
+
phantom_method_type = RBS.parse_method_type(ml.source)
|
37
|
+
phantom_method_type.type.yield_self do |fun|
|
38
|
+
case fun
|
39
|
+
when ::RBS::Types::Function
|
40
|
+
fun.required_positionals.each_with_index { |param, i| write_param(io, "req_#{i}", param, :abs) }
|
41
|
+
fun.optional_positionals.each_with_index { |param, i| write_param(io, "opt_#{i}", param, :opt) }
|
42
|
+
fun.rest_positionals&.yield_self { |param| write_param(io, "rest", param, :opt) }
|
43
|
+
fun.trailing_positionals.each_with_index { |param, i| write_param(io, "trail_#{i}", param, :abs) }
|
44
|
+
fun.required_keywords.each { |key, param| write_param(io, "keyreq_#{key}", param, :abs) }
|
45
|
+
fun.optional_keywords.each { |key, param| write_param(io, "key_#{key}", param, :opt) }
|
46
|
+
fun.rest_keywords&.yield_self { |param| write_param(io, "keyrest", param, :opt) }
|
47
|
+
# when ::RBS::Types::UntypedFunction
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
phantom_method_type.block&.yield_self do |b|
|
52
|
+
b.type.each_param.with_index { |param, i| write_param(io, "block_param_#{i}", param, :opt) }
|
53
|
+
write_type(io, "block_return", b.type.return_type, :abs)
|
54
|
+
end
|
55
|
+
write_type(io, "return", phantom_method_type.type.return_type, :abs)
|
56
|
+
raise unless phantom_method_type.location
|
57
|
+
|
58
|
+
io.write(slice(@cur, @cur...phantom_method_type.location.end_pos))
|
59
|
+
end
|
60
|
+
|
61
|
+
io.puts
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def slice(start, range)
|
67
|
+
ml = @method_type.location
|
68
|
+
raise unless ml
|
69
|
+
|
70
|
+
ml.source[start, range.end - range.begin] or raise
|
71
|
+
end
|
72
|
+
|
73
|
+
def write_param(io, position, param, accuracy)
|
74
|
+
write_type(io, position, param.type, accuracy)
|
75
|
+
end
|
76
|
+
|
77
|
+
def write_type(io, position, type, accuracy)
|
78
|
+
unless type.location
|
79
|
+
RaaP.logger.warn("No location information for `#{type}`")
|
80
|
+
return
|
81
|
+
end
|
82
|
+
io.write(slice(@cur, @cur...type.location.start_pos))
|
83
|
+
@cur = type.location.start_pos
|
84
|
+
case type
|
85
|
+
when ::RBS::Types::Tuple, ::RBS::Types::Union
|
86
|
+
name = type.class.name.split('::').last.downcase
|
87
|
+
type.types.each_with_index do |t, i|
|
88
|
+
t.location or raise
|
89
|
+
io.write(slice(@cur, @cur...t.location.start_pos)) # ( or [
|
90
|
+
@cur = t.location.start_pos
|
91
|
+
write_type(io, "#{position}_#{name}_#{i}", t, accuracy)
|
92
|
+
end
|
93
|
+
when ::RBS::Types::Optional
|
94
|
+
raise unless type.location
|
95
|
+
|
96
|
+
write_type(io, "#{position}_optional_left", type.type, accuracy)
|
97
|
+
io.write(slice(@cur, @cur...(type.location.end_pos - 1)))
|
98
|
+
@cur = type.location.end_pos - 1
|
99
|
+
if @cov.include?("#{position}_optional_right".to_sym)
|
100
|
+
io.write(green('?'))
|
101
|
+
else
|
102
|
+
io.write(red('?'))
|
103
|
+
end
|
104
|
+
raise unless type.location
|
105
|
+
|
106
|
+
@cur = type.location.end_pos
|
107
|
+
when ::RBS::Types::Variable
|
108
|
+
case accuracy
|
109
|
+
when :abs
|
110
|
+
io.write(green(type.name.to_s))
|
111
|
+
when :opt
|
112
|
+
# Variables are substed so raap don't know if they've been used.
|
113
|
+
io.write(yellow(type.name.to_s))
|
114
|
+
end
|
115
|
+
raise unless type.location
|
116
|
+
|
117
|
+
@cur = type.location.end_pos
|
118
|
+
else
|
119
|
+
raise unless type.location
|
120
|
+
|
121
|
+
if @cov.include?(position.to_sym)
|
122
|
+
io.write(green(type.location.source))
|
123
|
+
else
|
124
|
+
io.write(red(type.location.source))
|
125
|
+
end
|
126
|
+
@cur = type.location.end_pos
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def green(str) = "\e[32m#{str}\e[0m"
|
131
|
+
def red(str) = "\e[1;4;41m#{str}\e[0m"
|
132
|
+
def yellow(str) = "\e[93m#{str}\e[0m"
|
133
|
+
end
|
134
|
+
|
135
|
+
class << self
|
136
|
+
def start(method_type)
|
137
|
+
@cov = Set.new
|
138
|
+
@method_type = method_type
|
139
|
+
end
|
140
|
+
|
141
|
+
def running?
|
142
|
+
!!@cov
|
143
|
+
end
|
144
|
+
|
145
|
+
# position: req_0
|
146
|
+
# type: union_1
|
147
|
+
def log(position)
|
148
|
+
return unless running?
|
149
|
+
|
150
|
+
cov << position.to_sym
|
151
|
+
end
|
152
|
+
|
153
|
+
def cov
|
154
|
+
@cov or raise
|
155
|
+
end
|
156
|
+
|
157
|
+
def show(io)
|
158
|
+
return unless running?
|
159
|
+
|
160
|
+
writer = Writer.new(@method_type, cov)
|
161
|
+
writer.write(io)
|
162
|
+
end
|
163
|
+
|
164
|
+
def new_type_with_log(position, type)
|
165
|
+
case type
|
166
|
+
when ::RBS::Types::Tuple
|
167
|
+
# FIXME: Support Union in Tuple
|
168
|
+
type.types.each_with_index do |_t, i|
|
169
|
+
log("#{position}_tuple_#{i}")
|
170
|
+
end
|
171
|
+
Type.new(type)
|
172
|
+
when ::RBS::Types::Union
|
173
|
+
i = Random.rand(type.types.length)
|
174
|
+
new_type_with_log("#{position}_union_#{i}", type.types[i])
|
175
|
+
when ::RBS::Types::Optional
|
176
|
+
if Random.rand(2).zero?
|
177
|
+
new_type_with_log("#{position}_optional_left", type.type)
|
178
|
+
else
|
179
|
+
new_type_with_log("#{position}_optional_right", ::RBS::Types::Bases::Nil.new(location: nil))
|
180
|
+
end
|
181
|
+
else
|
182
|
+
log(position)
|
183
|
+
Type.new(type)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
data/lib/raap/function_type.rb
CHANGED
@@ -26,42 +26,62 @@ module RaaP
|
|
26
26
|
when type.respond_to?(:each_pair)
|
27
27
|
type.each_pair.to_h { |k, v| [k, to_symbolic_call_recursive(v, size: size)] }
|
28
28
|
when type.respond_to?(:each)
|
29
|
-
type.
|
29
|
+
type.map { |v| to_symbolic_call_recursive(v, size: size) }
|
30
30
|
else
|
31
31
|
type.to_symbolic_call(size: size)
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
35
|
def build_args_type
|
36
|
-
reqs = @fun.required_positionals.map
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
reqs = @fun.required_positionals.map.with_index do |param, i|
|
37
|
+
build_type_with_coverage("req_#{i}", param)
|
38
|
+
end
|
39
|
+
|
40
|
+
take_num = Random.rand(@fun.optional_positionals.length + 1)
|
41
|
+
opts = @fun.optional_positionals.take(take_num).map.each_with_index do |param, i|
|
42
|
+
build_type_with_coverage("opt_#{i}", param)
|
43
|
+
end
|
44
|
+
|
40
45
|
rest = []
|
41
|
-
if (
|
42
|
-
rest = Array.new(Random.rand(
|
46
|
+
if (rest_param = @fun.rest_positionals)
|
47
|
+
rest = Array.new(Random.rand(4)) do
|
48
|
+
build_type_with_coverage("rest", rest_param)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
tras = @fun.trailing_positionals.map.with_index do |param, i|
|
53
|
+
build_type_with_coverage("trail_#{i}", param)
|
43
54
|
end
|
55
|
+
|
44
56
|
[reqs, opts, rest, tras].flatten
|
45
57
|
end
|
46
58
|
|
47
59
|
def build_kwargs_type
|
48
|
-
reqs = @fun.required_keywords.
|
60
|
+
reqs = @fun.required_keywords.keys.to_h do |key|
|
61
|
+
[key, build_type_with_coverage("keyreq_#{key}", @fun.required_keywords[key])]
|
62
|
+
end
|
49
63
|
rand = Random.rand(@fun.optional_keywords.length + 1)
|
50
|
-
opts = @fun.optional_keywords.to_a.sample(rand).to_h
|
64
|
+
opts = @fun.optional_keywords.to_a.sample(rand).to_h do |key, param|
|
65
|
+
[key, build_type_with_coverage("key_#{key}", param)]
|
66
|
+
end
|
51
67
|
kwargs = reqs.to_h.merge(opts)
|
52
68
|
if (param = @fun.rest_keywords)
|
53
|
-
keys = Array.new(Random.rand(
|
69
|
+
keys = Array.new(Random.rand(4)) do
|
54
70
|
random_key = nil
|
55
71
|
loop do
|
56
72
|
# @type var random_key: Symbol
|
57
73
|
random_key = Type.new("Symbol").pick(size: 6)
|
58
74
|
break unless kwargs.key?(random_key)
|
59
75
|
end
|
60
|
-
[random_key,
|
76
|
+
[random_key, build_type_with_coverage("keyrest", param)]
|
61
77
|
end
|
62
78
|
kwargs.merge!(keys.to_h)
|
63
79
|
end
|
64
80
|
kwargs
|
65
81
|
end
|
82
|
+
|
83
|
+
def build_type_with_coverage(position, param)
|
84
|
+
Coverage.new_type_with_log(position, param.type)
|
85
|
+
end
|
66
86
|
end
|
67
87
|
end
|
data/lib/raap/method_property.rb
CHANGED
@@ -67,8 +67,9 @@ module RaaP
|
|
67
67
|
begin
|
68
68
|
return_value = symbolic_caller.eval
|
69
69
|
rescue StandardError, NotImplementedError
|
70
|
-
check = [:success]
|
71
70
|
return_value = Value::Bottom.new
|
71
|
+
coverage("return", return_value, return_type)
|
72
|
+
check = [:success]
|
72
73
|
rescue Timeout::ExitException
|
73
74
|
raise
|
74
75
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
@@ -113,6 +114,7 @@ module RaaP
|
|
113
114
|
if BindCall.is_a?(return_type, ::RBS::Types::ClassSingleton)
|
114
115
|
# ::RBS::Test::TypeCheck cannot support to check singleton class
|
115
116
|
if receiver_value == return_value
|
117
|
+
coverage("return", return_value, return_type)
|
116
118
|
[:success]
|
117
119
|
else
|
118
120
|
[:failure]
|
@@ -135,6 +137,7 @@ module RaaP
|
|
135
137
|
)
|
136
138
|
begin
|
137
139
|
if type_check.value(return_value, return_type)
|
140
|
+
coverage("return", return_value, return_type, type_check)
|
138
141
|
[:success]
|
139
142
|
else
|
140
143
|
[:failure]
|
@@ -148,5 +151,33 @@ module RaaP
|
|
148
151
|
def return_type
|
149
152
|
@method_type.rbs.type.return_type
|
150
153
|
end
|
154
|
+
|
155
|
+
def coverage(position, return_value, return_type, type_check = nil)
|
156
|
+
return unless Coverage.running?
|
157
|
+
|
158
|
+
case return_type
|
159
|
+
when ::RBS::Types::Tuple
|
160
|
+
return_type.types.zip(return_value).each_with_index do |(type, value), i|
|
161
|
+
if type_check&.value(value, type)
|
162
|
+
coverage("#{position}_tuple_#{i}", value, type, type_check)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
when ::RBS::Types::Union
|
166
|
+
return_type.types.each_with_index do |type, i|
|
167
|
+
if type_check&.value(return_value, type)
|
168
|
+
coverage("#{position}_union_#{i}", return_value, type, type_check)
|
169
|
+
break
|
170
|
+
end
|
171
|
+
end
|
172
|
+
when ::RBS::Types::Optional
|
173
|
+
if return_value.nil?
|
174
|
+
Coverage.log("#{position}_optional_right")
|
175
|
+
else
|
176
|
+
coverage("#{position}_optional_left", return_value, return_type.type, type_check)
|
177
|
+
end
|
178
|
+
else
|
179
|
+
Coverage.log(position)
|
180
|
+
end
|
181
|
+
end
|
151
182
|
end
|
152
183
|
end
|
data/lib/raap/method_type.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module RaaP
|
4
4
|
class MethodType
|
5
|
-
attr_reader :rbs
|
5
|
+
attr_reader :rbs, :original_rbs
|
6
6
|
|
7
7
|
def initialize(method, type_params_decl: [], type_args: [], self_type: nil, instance_type: nil, class_type: nil)
|
8
8
|
rbs =
|
@@ -20,8 +20,10 @@ module RaaP
|
|
20
20
|
params = (type_params_decl + rbs.type_params).uniq
|
21
21
|
ts = TypeSubstitution.new(params, type_args)
|
22
22
|
|
23
|
+
@original_rbs = rbs
|
23
24
|
@rbs = ts.method_type_sub(rbs, self_type: self_type, instance_type: instance_type, class_type: class_type)
|
24
|
-
|
25
|
+
function_or_untypedfunction = __skip__ = @rbs.type
|
26
|
+
@fun_type = FunctionType.new(function_or_untypedfunction)
|
25
27
|
end
|
26
28
|
|
27
29
|
def pick_arguments(size: 10)
|
@@ -41,7 +43,14 @@ module RaaP
|
|
41
43
|
return nil if (block.required == false) && [true, false].sample
|
42
44
|
|
43
45
|
fixed_return_value = Type.new(block.type.return_type).pick(size: size)
|
44
|
-
Proc.new
|
46
|
+
Proc.new do
|
47
|
+
block.type.each_param.with_index do |_param, i|
|
48
|
+
Coverage.log("block_param_#{i}")
|
49
|
+
end
|
50
|
+
|
51
|
+
Coverage.new_type_with_log("block_return", block.type.return_type)
|
52
|
+
fixed_return_value
|
53
|
+
end
|
45
54
|
end
|
46
55
|
|
47
56
|
def check_return(return_value)
|
data/lib/raap/rbs.rb
CHANGED
@@ -26,6 +26,48 @@ module RaaP
|
|
26
26
|
::RBS::Parser.parse_method_type(method_type, require_eof: true) or raise
|
27
27
|
end
|
28
28
|
|
29
|
+
def self.parse_member(member_type)
|
30
|
+
_, _, decls = ::RBS::Parser.parse_signature(<<~RBS)
|
31
|
+
module MemberScope
|
32
|
+
#{member_type}
|
33
|
+
end
|
34
|
+
RBS
|
35
|
+
decl = decls.first or raise
|
36
|
+
raise unless decl.is_a?(::RBS::AST::Declarations::Module)
|
37
|
+
|
38
|
+
member = decl.members.first or raise
|
39
|
+
raise unless member.is_a?(::RBS::AST::Members::Attribute)
|
40
|
+
|
41
|
+
member.tap do |m|
|
42
|
+
m = __skip__ = m
|
43
|
+
_shift_location(m.type, -m.location.start_pos)
|
44
|
+
_shift_location(m, -m.location.start_pos)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self._shift_location(localable, shift)
|
49
|
+
return if localable.location.nil?
|
50
|
+
|
51
|
+
l = localable.instance_variable_get("@location")
|
52
|
+
localable.instance_variable_set(
|
53
|
+
"@location",
|
54
|
+
::RBS::Location.new(
|
55
|
+
buffer: ::RBS::Buffer.new(
|
56
|
+
name: l.buffer.name,
|
57
|
+
content: l.buffer.content[-shift..l.end_pos],
|
58
|
+
),
|
59
|
+
start_pos: l.start_pos + shift,
|
60
|
+
end_pos: l.end_pos + shift,
|
61
|
+
)
|
62
|
+
)
|
63
|
+
case localable
|
64
|
+
when ::RBS::Types::Union
|
65
|
+
localable.types.each { |t| _shift_location(t, shift) }
|
66
|
+
when ::RBS::Types::Optional
|
67
|
+
_shift_location(localable.type, shift)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
29
71
|
def self.find_alias_decl(type_name, method_name)
|
30
72
|
env.class_decls[type_name].decls.each do |d|
|
31
73
|
d.decl.members.each do |member|
|
data/lib/raap/type.rb
CHANGED
@@ -24,6 +24,7 @@ module RaaP
|
|
24
24
|
def self.register(type_name, &block)
|
25
25
|
raise ArgumentError, "block is required" unless block
|
26
26
|
|
27
|
+
type_name = "::#{type_name}" if !type_name.start_with?("::")
|
27
28
|
GENERATORS[type_name] = __skip__ = block
|
28
29
|
end
|
29
30
|
|
@@ -69,11 +70,12 @@ module RaaP
|
|
69
70
|
t = instance.args[0] ? Type.new(instance.args[0], range: range) : Type.random
|
70
71
|
array(t)
|
71
72
|
end
|
72
|
-
register("::Binding") {
|
73
|
+
register("::Binding") { binding }
|
73
74
|
register("::Complex") { complex }
|
74
|
-
register("::Data") {
|
75
|
+
register("::Data") { Data.define }
|
75
76
|
register("::Encoding") { encoding }
|
76
|
-
register("::FalseClass") {
|
77
|
+
register("::FalseClass") { false }
|
78
|
+
register("::File") { File.open("/dev/null") }
|
77
79
|
register("::Float") { float }
|
78
80
|
register("::Hash") do
|
79
81
|
instance = __skip__ = type
|
@@ -82,18 +84,18 @@ module RaaP
|
|
82
84
|
dict(key, value)
|
83
85
|
end
|
84
86
|
register("::Integer") { integer }
|
85
|
-
register("::IO") {
|
86
|
-
register("::Method") {
|
87
|
-
register("::NilClass") {
|
88
|
-
register("::Proc") {
|
87
|
+
register("::IO") { $stdout }
|
88
|
+
register("::Method") { temp_method_object }
|
89
|
+
register("::NilClass") { nil }
|
90
|
+
register("::Proc") { Proc.new {} }
|
89
91
|
register("::Rational") { rational }
|
90
92
|
register("::Regexp") { sized { |size| Regexp.new(string.pick(size: size)) } }
|
91
93
|
register("::String") { string }
|
92
|
-
register("::Struct") {
|
94
|
+
register("::Struct") { Struct.new(:foo, :bar).new }
|
93
95
|
register("::Symbol") { symbol }
|
94
|
-
register("::Time") {
|
95
|
-
register("::TrueClass") {
|
96
|
-
register("::UnboundMethod") {
|
96
|
+
register("::Time") { [:call, Time, :now, [], {}, nil] }
|
97
|
+
register("::TrueClass") { true }
|
98
|
+
register("::UnboundMethod") { temp_method_object.unbind }
|
97
99
|
|
98
100
|
attr_reader :type
|
99
101
|
attr_reader :range
|
@@ -176,7 +178,7 @@ module RaaP
|
|
176
178
|
Object.const_get(type.name.to_s)
|
177
179
|
when ::RBS::Types::ClassInstance
|
178
180
|
case gen = GENERATORS[type.name.absolute!.to_s]
|
179
|
-
in Proc then
|
181
|
+
in Proc then pick_by_generator(gen, size: size)
|
180
182
|
in nil then to_symbolic_call_from_initialize(type, size: size)
|
181
183
|
end
|
182
184
|
when ::RBS::Types::Record
|
@@ -186,7 +188,7 @@ module RaaP
|
|
186
188
|
when ::RBS::Types::Literal
|
187
189
|
type.literal
|
188
190
|
when ::RBS::Types::Bases::Bool
|
189
|
-
bool
|
191
|
+
bool
|
190
192
|
when ::RBS::Types::Bases::Any
|
191
193
|
Type.random.to_symbolic_call(size: size)
|
192
194
|
when ::RBS::Types::Bases::Nil
|
@@ -198,6 +200,16 @@ module RaaP
|
|
198
200
|
|
199
201
|
private
|
200
202
|
|
203
|
+
def pick_by_generator(gen, size:)
|
204
|
+
ret = instance_exec(&gen)
|
205
|
+
case ret
|
206
|
+
when Sized
|
207
|
+
ret.pick(size: size)
|
208
|
+
else
|
209
|
+
ret
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
201
213
|
def to_symbolic_call_from_initialize(type, size:)
|
202
214
|
type_name = type.name.absolute!
|
203
215
|
const = Object.const_get(type_name.to_s)
|
@@ -301,16 +313,12 @@ module RaaP
|
|
301
313
|
end
|
302
314
|
|
303
315
|
def encoding
|
304
|
-
|
305
|
-
|
306
|
-
[:call, Encoding, :find, [e.name], {}, nil]
|
307
|
-
end
|
316
|
+
e = Encoding.list.sample or raise
|
317
|
+
[:call, Encoding, :find, [e.name], {}, nil]
|
308
318
|
end
|
309
319
|
|
310
320
|
def bool
|
311
|
-
|
312
|
-
Random.rand(2) == 0
|
313
|
-
end
|
321
|
+
Random.rand(2) == 0
|
314
322
|
end
|
315
323
|
|
316
324
|
def temp_method_object
|
@@ -25,6 +25,10 @@ module RaaP
|
|
25
25
|
instance_type = instance_type.is_a?(::String) ? RBS.parse_type(instance_type) : instance_type
|
26
26
|
class_type = class_type.is_a?(::String) ? RBS.parse_type(class_type) : class_type
|
27
27
|
sub = build
|
28
|
+
if sub.empty? && self_type.nil? && instance_type.nil? && class_type.nil?
|
29
|
+
return method_type
|
30
|
+
end
|
31
|
+
|
28
32
|
::RBS::MethodType.new(
|
29
33
|
type_params: [],
|
30
34
|
type: method_type.type.sub(sub).then { |ty| sub(ty, self_type: self_type, instance_type: instance_type, class_type: class_type) },
|
data/lib/raap/value/interface.rb
CHANGED
@@ -2,9 +2,9 @@
|
|
2
2
|
|
3
3
|
module RaaP
|
4
4
|
module Value
|
5
|
-
class Interface
|
5
|
+
class Interface
|
6
6
|
class << self
|
7
|
-
def define_method_from_interface(
|
7
|
+
def define_method_from_interface(base_mod, type, size: 3)
|
8
8
|
type = type.is_a?(::String) ? RBS.parse_type(type) : type
|
9
9
|
unless type.instance_of?(::RBS::Types::Interface)
|
10
10
|
::Kernel.raise ::TypeError, "not an interface type: #{type}"
|
@@ -22,7 +22,7 @@ module RaaP
|
|
22
22
|
ts = TypeSubstitution.new(type_params, type.args)
|
23
23
|
subed_method_type = ts.method_type_sub(method_type, self_type: self_type, instance_type: instance_type, class_type: class_type)
|
24
24
|
|
25
|
-
BindCall.define_method(
|
25
|
+
BindCall.define_method(base_mod, name) do |*_, &b|
|
26
26
|
@fixed_return_value ||= {}
|
27
27
|
@fixed_return_value[name] ||= if self_type == subed_method_type.type.return_type
|
28
28
|
self
|
@@ -30,16 +30,13 @@ module RaaP
|
|
30
30
|
Type.new(subed_method_type.type.return_type).pick(size: size)
|
31
31
|
end
|
32
32
|
# @type var b: Proc?
|
33
|
-
if b
|
33
|
+
if b && subed_method_type.block && subed_method_type.block.type.is_a?(::RBS::Types::Function)
|
34
34
|
@fixed_block_arguments ||= {}
|
35
|
-
@fixed_block_arguments[name] ||=
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
else
|
41
|
-
[]
|
42
|
-
end
|
35
|
+
@fixed_block_arguments[name] ||= size.times.map do
|
36
|
+
FunctionType.new(subed_method_type.block.type)
|
37
|
+
.pick_arguments(size: size)
|
38
|
+
end
|
39
|
+
|
43
40
|
@fixed_block_arguments[name].each do |a, kw|
|
44
41
|
b.call(*a, **kw)
|
45
42
|
end
|
@@ -68,14 +65,6 @@ module RaaP
|
|
68
65
|
@size = size
|
69
66
|
end
|
70
67
|
|
71
|
-
def respond_to?(name, _include_all = false)
|
72
|
-
@definition.methods.has_key?(name.to_sym)
|
73
|
-
end
|
74
|
-
|
75
|
-
def class
|
76
|
-
Interface
|
77
|
-
end
|
78
|
-
|
79
68
|
def inspect
|
80
69
|
"#<interface @type=`#{@type}` @methods=#{@definition.methods.keys} @size=#{@size}>"
|
81
70
|
end
|
@@ -17,19 +17,18 @@ module RaaP
|
|
17
17
|
raise ArgumentError, "intersection type must have at least one class instance type in `#{instances}`"
|
18
18
|
end
|
19
19
|
|
20
|
-
base = instances.find { |c| c.is_a?(::Class) } ||
|
20
|
+
base = instances.find { |c| c.is_a?(::Class) } || Object
|
21
21
|
|
22
22
|
c = Class.new(base) do
|
23
23
|
instances.select { |i| !i.is_a?(::Class) }.each do |m|
|
24
24
|
include(m)
|
25
25
|
end
|
26
26
|
|
27
|
-
|
28
|
-
t
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
Interface.define_method_from_interface(self, interface, size: size)
|
27
|
+
type.types.each do |t|
|
28
|
+
case t
|
29
|
+
when ::RBS::Types::Interface
|
30
|
+
Interface.define_method_from_interface(self, t, size: size)
|
31
|
+
end
|
33
32
|
end
|
34
33
|
end
|
35
34
|
type = ::RBS::Types::ClassInstance.new(name: TypeName(base.name), args: [], location: nil)
|
data/lib/raap/value/variable.rb
CHANGED
data/lib/raap/version.rb
CHANGED
data/lib/raap.rb
CHANGED
@@ -23,6 +23,7 @@ module RaaP
|
|
23
23
|
|
24
24
|
autoload :BindCall, "raap/bind_call"
|
25
25
|
autoload :CLI, "raap/cli"
|
26
|
+
autoload :Coverage, "raap/coverage"
|
26
27
|
autoload :FunctionType, "raap/function_type"
|
27
28
|
autoload :MethodProperty, "raap/method_property"
|
28
29
|
autoload :MethodType, "raap/method_type"
|
data/public/example.webp
ADDED
Binary file
|
data/sig/raap.rbs
CHANGED
@@ -19,15 +19,15 @@ module RaaP
|
|
19
19
|
|
20
20
|
class CLI
|
21
21
|
class Option < ::Struct[untyped]
|
22
|
-
def self.new: (?dirs: ::Array[String], ?requires: ::Array[String], ?libraries: ::Array[String], ?timeout: (Integer | Float | nil), ?size_from: ::Integer, ?size_to: ::Integer, ?size_by: ::Integer, ?allow_private: bool) -> instance
|
22
|
+
def self.new: (?dirs: ::Array[String], ?requires: ::Array[String], ?libraries: ::Array[String], ?timeout: (Integer | Float | nil), ?size_from: ::Integer, ?size_to: ::Integer, ?size_by: ::Integer, ?allow_private: bool, ?coverage: bool) -> instance
|
23
23
|
|
24
|
-
def self.[]: (?dirs: ::Array[String], ?requires: ::Array[String], ?libraries: ::Array[String], ?timeout: (Integer | Float | nil), ?size_from: ::Integer, ?size_to: ::Integer, ?size_by: ::Integer, ?allow_private: bool) -> instance
|
24
|
+
def self.[]: (?dirs: ::Array[String], ?requires: ::Array[String], ?libraries: ::Array[String], ?timeout: (Integer | Float | nil), ?size_from: ::Integer, ?size_to: ::Integer, ?size_by: ::Integer, ?allow_private: bool, ?coverage: bool) -> instance
|
25
25
|
|
26
26
|
def self.keyword_init?: () -> true
|
27
27
|
|
28
|
-
def self.members: () -> [ :dirs, :requires, :library, :timeout, :size_from, :size_to, :size_by, :allow_private]
|
28
|
+
def self.members: () -> [ :dirs, :requires, :library, :timeout, :size_from, :size_to, :size_by, :allow_private, :coverage]
|
29
29
|
|
30
|
-
def members: () -> [ :dirs, :requires, :library, :timeout, :size_from, :size_to, :size_by, :allow_private]
|
30
|
+
def members: () -> [ :dirs, :requires, :library, :timeout, :size_from, :size_to, :size_by, :allow_private, :coverage]
|
31
31
|
|
32
32
|
attr_accessor dirs: ::Array[String]
|
33
33
|
|
@@ -44,6 +44,8 @@ module RaaP
|
|
44
44
|
attr_accessor size_by: ::Integer
|
45
45
|
|
46
46
|
attr_accessor allow_private: bool
|
47
|
+
|
48
|
+
attr_accessor coverage: bool
|
47
49
|
end
|
48
50
|
|
49
51
|
type property_result = [Integer, Symbol, ::RBS::MethodType, StringIO?]
|
@@ -71,6 +73,39 @@ module RaaP
|
|
71
73
|
def report: () -> Integer
|
72
74
|
end
|
73
75
|
|
76
|
+
module Coverage
|
77
|
+
type locs = [::RBS::Buffer::loc, ::RBS::Buffer::loc]
|
78
|
+
type accuracy = :abs | :opt
|
79
|
+
class Writer
|
80
|
+
@method_type: ::RBS::MethodType
|
81
|
+
@cov: ::Set[Symbol]
|
82
|
+
@cur: Integer
|
83
|
+
|
84
|
+
def initialize: (::RBS::MethodType, Set[Symbol]) -> void
|
85
|
+
def write: (IO) -> void
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def method_type_location: () -> ::RBS::Location[untyped, untyped]
|
90
|
+
def slice: (Integer, Range[Integer]) -> String
|
91
|
+
def write_param: (IO, String, ::RBS::Types::Function::Param, accuracy) -> void
|
92
|
+
def write_type: (IO, String, ::RBS::Types::t, accuracy) -> void
|
93
|
+
def green: (String) -> String
|
94
|
+
def red: (String) -> String
|
95
|
+
def yellow: (String) -> String
|
96
|
+
end
|
97
|
+
|
98
|
+
self.@cov: Set[Symbol]?
|
99
|
+
self.@method_type: ::RBS::MethodType
|
100
|
+
|
101
|
+
def self.start: (::RBS::MethodType) -> void
|
102
|
+
def self.running?: () -> bool
|
103
|
+
def self.log: (String | Symbol) -> void
|
104
|
+
def self.cov: () -> Set[Symbol]
|
105
|
+
def self.show: (IO) -> void
|
106
|
+
def self.new_type_with_log: (String, ::RBS::Types::t) -> Type
|
107
|
+
end
|
108
|
+
|
74
109
|
class FunctionType
|
75
110
|
@fun: ::RBS::Types::Function
|
76
111
|
|
@@ -83,6 +118,7 @@ module RaaP
|
|
83
118
|
def to_symbolic_call_recursive: (untyped, size: Integer) -> untyped
|
84
119
|
def build_args_type: () -> Array[Type]
|
85
120
|
def build_kwargs_type: () -> Hash[Symbol, Type]
|
121
|
+
def build_type_with_coverage: (String, ::RBS::Types::Function::Param) -> Type
|
86
122
|
end
|
87
123
|
|
88
124
|
class MethodProperty
|
@@ -108,10 +144,13 @@ module RaaP
|
|
108
144
|
def call: (size: Integer, stats: Stats) -> (Result::Success | Result::Failure | Result::Skip | Result::Exception)
|
109
145
|
def check_return: (receiver_value: untyped, return_value: untyped) -> ([Symbol] | [Symbol, Exception])
|
110
146
|
def return_type: () -> RBS::Types::t
|
147
|
+
def original_return_type: () -> RBS::Types::t
|
148
|
+
def coverage: (String, untyped, RBS::Types::t, ?RBS::Test::TypeCheck?) -> void
|
111
149
|
end
|
112
150
|
|
113
151
|
class MethodType
|
114
152
|
attr_reader rbs: ::RBS::MethodType
|
153
|
+
attr_reader original_rbs: ::RBS::MethodType
|
115
154
|
@fun_type: FunctionType
|
116
155
|
|
117
156
|
def initialize: (::RBS::MethodType | String method, ?type_params_decl: Array[untyped], ?type_args: Array[untyped], ?self_type: ::RBS::Types::ClassInstance?, ?instance_type: ::RBS::Types::ClassInstance?, ?class_type: ::RBS::Types::ClassSingleton?) -> void
|
@@ -134,6 +173,8 @@ module RaaP
|
|
134
173
|
def self.loader: () -> ::RBS::EnvironmentLoader
|
135
174
|
def self.parse_type: (String) -> ::RBS::Types::t
|
136
175
|
def self.parse_method_type: (String) -> ::RBS::MethodType
|
176
|
+
def self.parse_member: (String) -> ::RBS::AST::Members::Attribute
|
177
|
+
def self._shift_location: (untyped, Integer) -> void
|
137
178
|
def self.find_alias_decl: (::RBS::TypeName, Symbol) -> ::RBS::AST::Members::Alias?
|
138
179
|
end
|
139
180
|
|
@@ -242,7 +283,7 @@ module RaaP
|
|
242
283
|
GENERATORS: Hash[String, ^() -> Sized[untyped]]
|
243
284
|
SIMPLE_SOURCE: Array[String]
|
244
285
|
|
245
|
-
def self.register: (String) { () [self: instance] ->
|
286
|
+
def self.register: (String) { () [self: instance] -> untyped } -> void
|
246
287
|
def self.random: () -> Type
|
247
288
|
def self.random_without_basic_object: () -> Type
|
248
289
|
def self.call_new_from: (Module, ::RBS::Types::ClassInstance, size: Integer) -> symbolic_call
|
@@ -278,8 +319,8 @@ module RaaP
|
|
278
319
|
def symbol: () -> Sized[Symbol]
|
279
320
|
def array: (Type) -> Sized[Array[untyped]]
|
280
321
|
def dict: (Type, Type) -> Sized[Hash[untyped, untyped]]
|
281
|
-
def encoding: () ->
|
282
|
-
def bool: () ->
|
322
|
+
def encoding: () -> symbolic_call
|
323
|
+
def bool: () -> bool
|
283
324
|
def temp_method_object: () -> ::Method
|
284
325
|
end
|
285
326
|
|
@@ -289,19 +330,19 @@ module RaaP
|
|
289
330
|
def class: () -> class
|
290
331
|
end
|
291
332
|
|
292
|
-
class Interface
|
333
|
+
class Interface
|
293
334
|
@type: ::RBS::Types::Interface
|
294
335
|
@size: Integer
|
295
336
|
@definition: ::RBS::Definition
|
296
337
|
|
297
|
-
def self.define_method_from_interface: (
|
338
|
+
def self.define_method_from_interface: (::Module base_class, String | ::RBS::Types::Interface type, ?size: Integer) -> void
|
298
339
|
def initialize: (String | ::RBS::Types::Interface | String, ?size: Integer) -> void
|
299
340
|
def respond_to?: (Symbol, ?boolish) -> bool
|
300
341
|
def inspect: () -> String
|
301
342
|
def class: () -> class
|
302
343
|
end
|
303
344
|
|
304
|
-
|
345
|
+
module Intersection
|
305
346
|
@type: ::RBS::Types::Intersection
|
306
347
|
@children: Array[Type]
|
307
348
|
@size: Integer
|
@@ -328,7 +369,7 @@ module RaaP
|
|
328
369
|
def class: () -> class
|
329
370
|
end
|
330
371
|
|
331
|
-
class Variable
|
372
|
+
class Variable
|
332
373
|
attr_reader type: ::RBS::Types::Variable
|
333
374
|
|
334
375
|
def initialize: (::RBS::Types::Variable | String | Symbol) -> void
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: raap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ksss
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-05-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rbs
|
@@ -57,6 +57,7 @@ files:
|
|
57
57
|
- lib/raap.rb
|
58
58
|
- lib/raap/bind_call.rb
|
59
59
|
- lib/raap/cli.rb
|
60
|
+
- lib/raap/coverage.rb
|
60
61
|
- lib/raap/function_type.rb
|
61
62
|
- lib/raap/method_property.rb
|
62
63
|
- lib/raap/method_type.rb
|
@@ -77,6 +78,7 @@ files:
|
|
77
78
|
- lib/raap/value/void.rb
|
78
79
|
- lib/raap/version.rb
|
79
80
|
- lib/shims.rb
|
81
|
+
- public/example.webp
|
80
82
|
- public/jacket.webp
|
81
83
|
- rbs_collection.lock.yaml
|
82
84
|
- rbs_collection.yaml
|
@@ -102,7 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
104
|
- !ruby/object:Gem::Version
|
103
105
|
version: '0'
|
104
106
|
requirements: []
|
105
|
-
rubygems_version: 3.
|
107
|
+
rubygems_version: 3.5.9
|
106
108
|
signing_key:
|
107
109
|
specification_version: 4
|
108
110
|
summary: RBS as a Property
|