raap 0.3.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,35 +6,39 @@ module RaaP
6
6
  @fun = fun
7
7
  end
8
8
 
9
- def pick_arguments(size: 10, eval: true)
10
- a = recursive_pick(build_args_type, size:, eval:)
11
- k = recursive_pick(build_kwargs_type, size:, eval:)
9
+ def pick_arguments(size: 10)
10
+ SymbolicCaller.new(arguments_to_symbolic_call(size:)).eval
11
+ end
12
+
13
+ def arguments_to_symbolic_call(size: 10)
14
+ a = to_symbolic_call_recursive(build_args_type, size:)
15
+ k = to_symbolic_call_recursive(build_kwargs_type, size:)
12
16
 
13
17
  [a, k]
14
18
  end
15
19
 
16
20
  private
17
21
 
18
- def recursive_pick(type, size:, eval:)
22
+ def to_symbolic_call_recursive(type, size:)
19
23
  case
20
24
  when type.nil?
21
25
  nil
22
26
  when type.respond_to?(:each_pair)
23
- type.each_pair.to_h { |k, v| [k, recursive_pick(v, size:, eval:)] }
27
+ type.each_pair.to_h { |k, v| [k, to_symbolic_call_recursive(v, size:)] }
24
28
  when type.respond_to?(:each)
25
- type.each.map { |v| recursive_pick(v, size:, eval:) }
29
+ type.each.map { |v| to_symbolic_call_recursive(v, size:) }
26
30
  else
27
- type.pick(size:, eval:)
31
+ type.to_symbolic_call(size:)
28
32
  end
29
33
  end
30
34
 
31
35
  def build_args_type
32
36
  reqs = @fun.required_positionals.map { |param| Type.new(param.type) }
33
37
  tras = @fun.trailing_positionals.map { |param| Type.new(param.type) }
34
- sampled_optional_positionals = @fun.optional_positionals.sample(Random.rand(@fun.optional_positionals.length + 1))
38
+ sampled_optional_positionals = @fun.optional_positionals.take(Random.rand(@fun.optional_positionals.length + 1))
35
39
  opts = sampled_optional_positionals.map { |param| Type.new(param.type) }
36
40
  rest = []
37
- if param = @fun.rest_positionals
41
+ if (param = @fun.rest_positionals)
38
42
  rest = Array.new(Random.rand(0..3)) { Type.new(param.type) }
39
43
  end
40
44
  [reqs, opts, rest, tras].flatten
@@ -45,7 +49,7 @@ module RaaP
45
49
  rand = Random.rand(@fun.optional_keywords.length + 1)
46
50
  opts = @fun.optional_keywords.to_a.sample(rand).to_h { |name, param| [name, Type.new(param.type)] }
47
51
  kwargs = reqs.to_h.merge(opts)
48
- if param = @fun.rest_keywords
52
+ if (param = @fun.rest_keywords)
49
53
  keys = Array.new(Random.rand(0..3)) do
50
54
  random_key = nil
51
55
  loop do
@@ -2,8 +2,8 @@
2
2
 
3
3
  module RaaP
4
4
  class MethodProperty
5
- class Stats < Struct.new(:success, :skip, :exception)
6
- def initialize(success: 0, skip: 0, exception: 0)
5
+ class Stats < Struct.new(:success, :skip, :exception, :break)
6
+ def initialize(success: 0, skip: 0, exception: 0, break: false)
7
7
  super
8
8
  end
9
9
  end
@@ -23,12 +23,26 @@ module RaaP
23
23
  Timeout.timeout(@timeout) do
24
24
  catch(:break) do
25
25
  @size_step.each do |size|
26
- yield call(size: size, stats: stats)
26
+ call(size:, stats:).tap do |ret|
27
+ case ret
28
+ when Result::Success
29
+ stats.success += 1
30
+ when Result::Failure
31
+ # no count
32
+ when Result::Skip
33
+ stats.skip += 1
34
+ when Result::Exception
35
+ stats.exception += 1
36
+ end
37
+
38
+ yield ret
39
+ end
27
40
  end
28
41
  end
29
42
  end
30
43
  rescue Timeout::Error => exception
31
- RaaP.logger.warn "Timeout: #{exception}"
44
+ stats.break = true
45
+ RaaP.logger.info "Timeout: #{exception}"
32
46
  end
33
47
  stats
34
48
  end
@@ -36,61 +50,75 @@ module RaaP
36
50
  private
37
51
 
38
52
  def call(size:, stats:)
39
- receiver_value = @receiver_type.pick(size: size, eval: false)
40
- args, kwargs, block = @method_type.pick_arguments(size: size, eval: false)
53
+ if @method_type.rbs.type.each_type.find { |t| t.instance_of?(::RBS::Types::Bases::Any) }
54
+ RaaP.logger.info { "Skip type check since `#{@method_type.rbs}` includes `untyped`" }
55
+ stats.break = true
56
+ throw :break
57
+ end
58
+ receiver_value = @receiver_type.to_symbolic_call(size:)
59
+ args, kwargs, block = @method_type.arguments_to_symbolic_call(size:)
41
60
  # @type var symbolic_call: symbolic_call
42
61
  symbolic_call = [:call, receiver_value, @method_name, args, kwargs, block]
43
62
  symbolic_caller = SymbolicCaller.new(symbolic_call, allow_private: @allow_private)
44
63
  begin
45
64
  # ensure symbolic_call
46
- check = false
65
+ check = [:failure]
47
66
  if return_type.instance_of?(::RBS::Types::Bases::Bottom)
48
67
  begin
49
68
  return_value = symbolic_caller.eval
50
69
  rescue StandardError, NotImplementedError
51
- check = true
70
+ check = [:success]
52
71
  return_value = Value::Bottom.new
72
+ rescue Timeout::ExitException
73
+ raise
74
+ rescue Exception => e # rubocop:disable Lint/RescueException
75
+ RaaP.logger.error("[#{e.class}] class is not supported to check `bot` type")
76
+ raise
53
77
  end
54
78
  else
55
79
  return_value = symbolic_caller.eval
56
- check = check_return(receiver_value:, return_value:, method_type: @method_type)
80
+ check = check_return(receiver_value:, return_value:)
57
81
  end
58
- if check
59
- stats.success += 1
82
+ case check
83
+ in [:success]
60
84
  Result::Success.new(symbolic_call:, return_value:)
61
- else
85
+ in [:failure]
62
86
  Result::Failure.new(symbolic_call:, return_value:)
87
+ in [:exception, exception]
88
+ Result::Exception.new(symbolic_call:, exception:)
63
89
  end
64
90
  rescue TypeError => exception
65
91
  Result::Failure.new(symbolic_call:, return_value:, exception:)
66
92
  end
67
93
 
68
94
  # not ensure symbolic_call
69
- rescue NoMethodError => exception
70
- stats.skip += 1
95
+ rescue NoMethodError, NotImplementedError => exception
71
96
  Result::Skip.new(symbolic_call:, exception:)
72
97
  rescue NameError => e
98
+ RaaP.logger.error("[#{e.class}] #{e.detailed_message}")
73
99
  msg = e.name.nil? ? '' : "for `#{BindCall.to_s(e.receiver)}::#{e.name}`"
74
- RaaP.logger.error("Implementation is not found #{msg} maybe.")
100
+ RaaP.logger.warn("Implementation is not found #{msg} maybe.")
101
+ RaaP.logger.debug(e.backtrace&.join("\n"))
102
+ stats.break = true
75
103
  throw :break
76
- rescue NotImplementedError => exception
77
- stats.skip += 1
78
- Result::Skip.new(symbolic_call:, exception:)
79
104
  rescue SystemStackError => exception
80
- stats.skip += 1
81
- RaaP.logger.warn "Found recursive type definition."
105
+ RaaP.logger.info "Found recursive type definition."
82
106
  Result::Skip.new(symbolic_call:, exception:)
83
107
  rescue => exception
84
- stats.exception += 1
85
108
  Result::Exception.new(symbolic_call:, exception:)
86
109
  end
87
110
 
88
- def check_return(receiver_value:, return_value:, method_type:)
111
+ def check_return(receiver_value:, return_value:)
89
112
  if BindCall.is_a?(receiver_value, Module)
90
113
  if BindCall.is_a?(return_type, ::RBS::Types::ClassSingleton)
91
114
  # ::RBS::Test::TypeCheck cannot support to check singleton class
92
- return receiver_value == return_value
115
+ if receiver_value == return_value
116
+ [:success]
117
+ else
118
+ [:failure]
119
+ end
93
120
  end
121
+
94
122
  self_class = receiver_value
95
123
  instance_class = receiver_value
96
124
  else
@@ -98,18 +126,22 @@ module RaaP
98
126
  instance_class = BindCall.class(receiver_value)
99
127
  end
100
128
  type_check = ::RBS::Test::TypeCheck.new(
101
- self_class: self_class,
102
- instance_class: instance_class,
129
+ self_class:,
130
+ instance_class:,
103
131
  class_class: Module,
104
132
  builder: RBS.builder,
105
133
  sample_size: 100,
106
134
  unchecked_classes: []
107
135
  )
108
136
  begin
109
- type_check.value(return_value, return_type)
137
+ if type_check.value(return_value, return_type)
138
+ [:success]
139
+ else
140
+ [:failure]
141
+ end
110
142
  rescue => e
111
- $stderr.puts "Type check fail by `(#{e.class}) #{e.message}`"
112
- false
143
+ RaaP.logger.debug("Type check fail by `(#{e.class}) #{e.message}`")
144
+ [:exception, e]
113
145
  end
114
146
  end
115
147
 
@@ -16,25 +16,32 @@ module RaaP
16
16
  else
17
17
  raise "bad method #{method}"
18
18
  end
19
- ts = TypeSubstitution.new(type_params_decl + rbs.type_params, type_args)
19
+
20
+ params = (type_params_decl + rbs.type_params).uniq
21
+ ts = TypeSubstitution.new(params, type_args)
20
22
 
21
23
  @rbs = ts.method_type_sub(rbs, self_type:, instance_type:, class_type:)
22
24
  @fun_type = FunctionType.new(@rbs.type)
23
25
  end
24
26
 
25
- def pick_arguments(size: 10, eval: true)
26
- args, kwargs = @fun_type.pick_arguments(size: size, eval:)
27
- block = pick_block(size: size, eval:)
27
+ def pick_arguments(size: 10)
28
+ SymbolicCaller.new(arguments_to_symbolic_call(size:)).eval
29
+ end
30
+
31
+ def arguments_to_symbolic_call(size: 10)
32
+ args, kwargs = @fun_type.arguments_to_symbolic_call(size:)
33
+ block = pick_block(size:)
28
34
 
29
35
  [args, kwargs, block]
30
36
  end
31
37
 
32
- def pick_block(size: 10, eval: true)
38
+ def pick_block(size: 10)
33
39
  block = @rbs.block
34
40
  return nil if block.nil?
35
41
  return nil if (block.required == false) && [true, false].sample
36
42
 
37
- Proc.new { Type.new(block.type.return_type).pick(size:, eval:) }
43
+ fixed_return_value = Type.new(block.type.return_type).pick(size:)
44
+ Proc.new { fixed_return_value }
38
45
  end
39
46
 
40
47
  def check_return(return_value)
data/lib/raap/minitest.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'minitest'
2
4
 
3
5
  module RaaP
@@ -11,9 +13,13 @@ module RaaP
11
13
  method_type = RaaP::MethodType.new(type)
12
14
  size_step.each do |size|
13
15
  # TODO assert_send_type
14
- args, kwargs, _block = method_type.pick_arguments(size: size)
16
+ args, kwargs, _block = method_type.pick_arguments(size:)
15
17
  return_value = yield(*args, **kwargs)
16
- assert method_type.check_return(return_value), "return value: #{BindCall.inspect(return_value)}[#{BindCall.class(return_value)}] is not match with `#{method_type.rbs.type.return_type}`"
18
+ i = BindCall.inspect(return_value)
19
+ c = BindCall.class(return_value)
20
+ r = method_type.rbs.type.return_type
21
+ msg = "return value: #{i}[#{c}] is not match with `#{r}`"
22
+ assert method_type.check_return(return_value), msg
17
23
  end
18
24
  else
19
25
  # forall("Integer", "String") { |int, str| Foo.new.int_str(int, str) }
@@ -24,7 +30,7 @@ module RaaP
24
30
  end
25
31
  end
26
32
  size_step.each do |size|
27
- values = types.map { |type| type.pick(size: size) }
33
+ values = types.map { |t| t.pick(size:) }
28
34
  assert yield(*values)
29
35
  end
30
36
  end
data/lib/raap/rbs.rb CHANGED
@@ -19,5 +19,24 @@ module RaaP
19
19
 
20
20
  ::RBS::Parser.parse_type(type, require_eof: true) or raise
21
21
  end
22
+
23
+ def self.parse_method_type(method_type)
24
+ raise ArgumentError, "empty method type" if method_type == ""
25
+
26
+ ::RBS::Parser.parse_method_type(method_type, require_eof: true) or raise
27
+ end
28
+
29
+ def self.find_alias_decl(type_name, method_name)
30
+ env.class_decls[type_name].decls.each do |d|
31
+ d.decl.members.each do |member|
32
+ case member
33
+ when ::RBS::AST::Members::Alias
34
+ return member if member.new_name == method_name
35
+ end
36
+ end
37
+ end
38
+
39
+ nil
40
+ end
22
41
  end
23
42
  end
data/lib/raap/result.rb CHANGED
@@ -2,22 +2,98 @@
2
2
 
3
3
  module RaaP
4
4
  module Result
5
- module CalledStr
5
+ module ReturnValueWithType
6
+ def return_value_with_type
7
+ return_type = return_value_to_type(return_value)
8
+ type = if return_type.empty? || return_type == 'nil'
9
+ ''
10
+ else
11
+ "[#{return_type}]"
12
+ end
13
+ "#{BindCall.inspect(return_value)}#{type}"
14
+ end
15
+
16
+ private
17
+
18
+ def return_value_to_type(val)
19
+ case val
20
+ when nil
21
+ 'nil'
22
+ when true, false
23
+ "bool"
24
+ when Array
25
+ elem = if val.empty?
26
+ 'untyped'
27
+ else
28
+ return_value_to_type(val.first)
29
+ end
30
+ "Array[#{elem}]"
31
+ when Hash
32
+ key = val.empty? ? 'untyped' : return_value_to_type(val.keys.first)
33
+ value = val.empty? ? 'untyped' : return_value_to_type(val.values.first)
34
+ "Hash[#{key}, #{value}]"
35
+ when Enumerator
36
+ elem =
37
+ begin
38
+ return_value_to_type(val.peek)
39
+ rescue StandardError
40
+ 'untyped'
41
+ end
42
+ ret =
43
+ if val.size == Float::INFINITY
44
+ 'bot'
45
+ else
46
+ begin
47
+ val.each {}
48
+ .tap { val.rewind }
49
+ .then { return_value_to_type(_1) }
50
+ rescue StandardError
51
+ 'untyped'
52
+ end
53
+ end
54
+ "Enumerator[#{elem}, #{ret}]"
55
+ else
56
+ "#{BindCall.class(val)}"
57
+ end
58
+ rescue StandardError => e
59
+ RaaP.logger.debug("[#{e.class}] #{e.exception.detailed_message}")
60
+ RaaP.logger.debug(e.exception.backtrace&.join("\n"))
61
+ "raised #{e.class} with check return_type"
62
+ end
63
+ end
64
+
65
+ class Success < Data.define(:symbolic_call, :return_value)
66
+ include ReturnValueWithType
67
+
6
68
  def called_str
7
69
  scr = SymbolicCaller.new(symbolic_call)
8
- "#{scr.call_str} -> #{return_value.inspect}[#{return_value.class}]"
70
+ "#{scr.call_str} -> #{return_value_with_type}"
9
71
  end
10
72
  end
11
73
 
12
- Success = Data.define(:symbolic_call, :return_value)
13
- Success.include CalledStr
14
- Failure = Data.define(:symbolic_call, :return_value, :exception) do
74
+ class Failure < Data.define(:symbolic_call, :return_value, :exception)
75
+ include ReturnValueWithType
76
+
15
77
  def initialize(exception: nil, **)
16
78
  super
17
79
  end
80
+
81
+ def called_str
82
+ scr = SymbolicCaller.new(symbolic_call)
83
+ return_type =
84
+ if exception
85
+ "raised #{exception.class}"
86
+ else
87
+ return_value_with_type
88
+ end
89
+ "#{scr.call_str} -> #{return_type}"
90
+ end
91
+ end
92
+
93
+ class Skip < Data.define(:symbolic_call, :exception)
94
+ end
95
+
96
+ class Exception < Data.define(:symbolic_call, :exception)
18
97
  end
19
- Failure.include CalledStr
20
- Skip = Data.define(:symbolic_call, :exception)
21
- Exception = Data.define(:symbolic_call, :exception)
22
98
  end
23
99
  end
data/lib/raap/sized.rb CHANGED
@@ -4,6 +4,7 @@ module RaaP
4
4
  class Sized
5
5
  def initialize(&block)
6
6
  raise LocalJumpError, "no block given" unless block
7
+
7
8
  @block = block
8
9
  @such_that = nil
9
10
  end
@@ -28,6 +29,7 @@ module RaaP
28
29
  picked = yield(skip)
29
30
  such_that = @such_that
30
31
  return picked if such_that.nil? || such_that.call(picked)
32
+
31
33
  skip += 1
32
34
  raise "too many skips" unless skip < 100
33
35
  end
@@ -18,6 +18,7 @@ module RaaP
18
18
  class SymbolicCaller
19
19
  class Var
20
20
  attr_reader :name
21
+
21
22
  def initialize(name)
22
23
  @name = name
23
24
  end
@@ -46,13 +47,13 @@ module RaaP
46
47
  end
47
48
 
48
49
  def call_str
49
- symbolic_call => [:call, receiver, Symbol => method_name, Array => args, Hash => kwargs, b]
50
+ symbolic_call => [:call, receiver, Symbol => method_name, Array => args, Hash => kwargs, block]
50
51
  receiver = try_eval(receiver)
51
52
  args, kwargs, block = try_eval([args, kwargs, block])
52
53
 
53
54
  a = []
54
- a << args.map(&:inspect).join(', ') if !args.empty?
55
- a << kwargs.map { |k ,v| "#{k}: #{BindCall.inspect(v)}" }.join(', ') if !kwargs.empty?
55
+ a << args.map(&BindCall.method(:inspect)).join(', ') if !args.empty?
56
+ a << kwargs.map { |k, v| "#{k}: #{BindCall.inspect(v)}" }.join(', ') if !kwargs.empty?
56
57
  argument_str = a.join(', ')
57
58
  block_str = block ? "{ }" : nil
58
59
 
@@ -61,7 +62,7 @@ module RaaP
61
62
 
62
63
  def to_lines
63
64
  [].tap do |lines|
64
- walk do |symbolic_call|
65
+ walk do |symbolic_call, is_last|
65
66
  symbolic_call => [:call, receiver_value, method_name, args, kwargs, block]
66
67
 
67
68
  is_mod = receiver_value.is_a?(Module)
@@ -72,7 +73,7 @@ module RaaP
72
73
  var_eq = "#{var} = "
73
74
  receiver = ''
74
75
  when BindCall.instance_of?(receiver_value, Var)
75
- var_eq = ""
76
+ var_eq = "#{receiver_value} = "
76
77
  var = Var.new(receiver_value.name)
77
78
  receiver = var + '.'
78
79
  when is_mod
@@ -80,16 +81,32 @@ module RaaP
80
81
  var_eq = "#{var} = "
81
82
  receiver = receiver_value.name + '.'
82
83
  else
83
- var_eq = ""
84
+ var_eq = ''
84
85
  receiver = Var.new(printable(receiver_value)) + '.'
85
86
  end
86
87
 
88
+ var_eq = '' if is_last
89
+
87
90
  arguments = []
88
- arguments << args.map { |a| printable(a) } if !args.empty?
89
- arguments << kwargs.map{|k,v| "#{k}: #{printable(v)}" }.join(', ') if !kwargs.empty?
91
+ if !args.empty?
92
+ # The reproduction code is kept short and executable.
93
+ if [Value::Interface, Value::Intersection].include?(receiver_value)
94
+ if args.all? { |a| a.respond_to?(:free_variables) } && !args.all? { |a| a.free_variables.empty? }
95
+ # FIXME: Type arguments are not yet supported.
96
+ args.each do |a|
97
+ lines << "# Free variables: #{a.free_variables.to_a.join(', ')}"
98
+ end
99
+ end
100
+ arguments << args.map { |a| printable(a.to_s) }
101
+ else
102
+ arguments << args.map { |a| printable(a) }
103
+ end
104
+ end
105
+ arguments << kwargs.map { |k, v| "#{k}: #{printable(v)}" }.join(', ') if !kwargs.empty?
90
106
  block_str = block ? " { }" : ""
91
107
 
92
- lines << "#{var_eq}#{receiver}#{method_name}(#{arguments.join(', ')})#{block_str}"
108
+ line = "#{var_eq}#{receiver}#{method_name}(#{arguments.join(', ')})#{block_str}"
109
+ lines << line
93
110
 
94
111
  var
95
112
  end
@@ -105,25 +122,24 @@ module RaaP
105
122
  end
106
123
 
107
124
  def walk(&)
108
- _walk(@symbolic_call, &)
125
+ _walk(@symbolic_call, true, &)
109
126
  end
110
127
 
111
- def _walk(symbolic_call, &block)
128
+ def _walk(symbolic_call, is_last, &block)
112
129
  return symbolic_call if BindCall::instance_of?(symbolic_call, BasicObject)
113
130
  return symbolic_call if !BindCall.respond_to?(symbolic_call, :deconstruct) && !BindCall.respond_to?(symbolic_call, :deconstruct_keys)
114
131
 
115
132
  case symbolic_call
116
133
  in [:call, receiver, Symbol => method_name, Array => args, Hash => kwargs, b]
117
- receiver = _walk(receiver, &block)
118
- args = _walk(args, &block) if !args.empty?
119
- kwargs = _walk(kwargs, &block) if !kwargs.empty?
120
- block.call [:call, receiver, method_name, args, kwargs, b]
121
- in Var
122
- symbolic_call.name
134
+ receiver = _walk(receiver, false, &block)
135
+ args = _walk(args, false, &block) if !args.empty?
136
+ kwargs = _walk(kwargs, false, &block) if !kwargs.empty?
137
+ block.call [:call, receiver, method_name, args, kwargs, b], is_last
123
138
  in Array
124
- symbolic_call.map { |sc| _walk(sc, &block) }
139
+ symbolic_call.map { |sc| _walk(sc, false, &block) }
125
140
  in Hash
126
- symbolic_call.transform_values { |sc| _walk(sc, &block) }
141
+ symbolic_call.transform_keys { |k| _walk(k, false, &block) }
142
+ .transform_values! { |v| _walk(v, false, &block) }
127
143
  else
128
144
  symbolic_call
129
145
  end
@@ -143,15 +159,11 @@ module RaaP
143
159
  end
144
160
 
145
161
  def printable(obj)
146
- if obj in [:call, _, Symbol, Array, Hash, _]
147
- return _walk(obj)
148
- end
149
-
150
162
  case obj
151
163
  when Var
152
164
  obj.name
153
165
  # Object from which it can get strings that can be eval with `#inspect`
154
- when Symbol, Integer, Float, Regexp, nil, true, false
166
+ when Symbol, Integer, Float, Regexp, Range, nil, true, false
155
167
  obj.inspect
156
168
  when String
157
169
  obj.inspect.gsub('"', "'") or raise
@@ -165,9 +177,9 @@ module RaaP
165
177
  when Array
166
178
  "[#{obj.map { |o| printable(o) }.join(', ')}]"
167
179
  when Module
168
- BindCall.name(obj) or raise
180
+ BindCall.name(obj) or raise "`#class` method returns nil"
169
181
  else
170
- var_name(obj.class)
182
+ var_name(BindCall.class(obj))
171
183
  end
172
184
  end
173
185
  end