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