raap 0.3.0 → 0.5.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.
@@ -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