rubybreaker 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/README.md +6 -6
  2. data/TUTORIAL.md +64 -34
  3. data/VERSION +1 -1
  4. data/lib/rubybreaker/debug/error.rb +7 -1
  5. data/lib/rubybreaker/runtime/monitor.rb +2 -2
  6. data/lib/rubybreaker/runtime/object_wrapper.rb +32 -1
  7. data/lib/rubybreaker/runtime/overrides.rb +31 -1
  8. data/lib/rubybreaker/runtime/type_system.rb +67 -11
  9. data/test/integrated/tc_checking.rb +35 -5
  10. data/test/integrated/tc_class_methods.rb +4 -1
  11. data/test/integrated/tc_original_behavior.rb +68 -0
  12. data/test/runtime/tc_obj_wrapper.rb +50 -4
  13. data/test/testtask/tc_testtask.rb +1 -1
  14. data/test/ts_integrated.rb +3 -1
  15. data/webpage/images/title.png +0 -0
  16. data/webpage/index.html +6 -6
  17. data/webpage/rdoc/Object.html +5 -1
  18. data/webpage/rdoc/Rake.html +5 -1
  19. data/webpage/rdoc/Rake/RubyBreakerTestTask.html +5 -1
  20. data/webpage/rdoc/RubyBreaker.html +5 -1
  21. data/webpage/rdoc/RubyBreaker/Breakable.html +5 -1
  22. data/webpage/rdoc/RubyBreaker/Broken.html +5 -1
  23. data/webpage/rdoc/RubyBreaker/Context.html +5 -1
  24. data/webpage/rdoc/RubyBreaker/Errors.html +5 -1
  25. data/webpage/rdoc/RubyBreaker/Errors/ArgumentTypeError.html +216 -0
  26. data/webpage/rdoc/RubyBreaker/Errors/{SubtypeFailure.html → ArityError.html} +7 -3
  27. data/webpage/rdoc/RubyBreaker/Errors/InternalError.html +5 -1
  28. data/webpage/rdoc/RubyBreaker/Errors/InvalidSubtypeCheck.html +5 -1
  29. data/webpage/rdoc/RubyBreaker/Errors/InvalidTypeConstruction.html +5 -1
  30. data/webpage/rdoc/RubyBreaker/Errors/ReturnTypeError.html +216 -0
  31. data/webpage/rdoc/RubyBreaker/Errors/TypeError.html +5 -1
  32. data/webpage/rdoc/RubyBreaker/Errors/UserError.html +5 -1
  33. data/webpage/rdoc/RubyBreaker/ObjectPosition.html +5 -1
  34. data/webpage/rdoc/RubyBreaker/Position.html +5 -1
  35. data/webpage/rdoc/RubyBreaker/RDocSupport.html +5 -1
  36. data/webpage/rdoc/RubyBreaker/RubyTypeUtils.html +5 -1
  37. data/webpage/rdoc/RubyBreaker/Runtime.html +10 -1
  38. data/webpage/rdoc/RubyBreaker/Runtime/Inspector.html +5 -1
  39. data/webpage/rdoc/RubyBreaker/Runtime/MethodInfo.html +5 -1
  40. data/webpage/rdoc/RubyBreaker/Runtime/Monitor.html +7 -3
  41. data/webpage/rdoc/RubyBreaker/Runtime/MonitorInstaller.html +5 -1
  42. data/webpage/rdoc/RubyBreaker/Runtime/MonitorSwitch.html +5 -1
  43. data/webpage/rdoc/RubyBreaker/Runtime/ObjectWrapper.html +37 -2
  44. data/webpage/rdoc/RubyBreaker/Runtime/Pluggable.html +5 -1
  45. data/webpage/rdoc/RubyBreaker/Runtime/TypeSigParser.html +5 -1
  46. data/webpage/rdoc/RubyBreaker/Runtime/TypeSigUnparser.html +5 -1
  47. data/webpage/rdoc/RubyBreaker/Runtime/TypeSystem.html +103 -16
  48. data/webpage/rdoc/RubyBreaker/TypeComparer.html +5 -1
  49. data/webpage/rdoc/RubyBreaker/TypeDefs.html +5 -1
  50. data/webpage/rdoc/RubyBreaker/TypeDefs/AnyType.html +5 -1
  51. data/webpage/rdoc/RubyBreaker/TypeDefs/BlockType.html +5 -1
  52. data/webpage/rdoc/RubyBreaker/TypeDefs/DuckType.html +5 -1
  53. data/webpage/rdoc/RubyBreaker/TypeDefs/FusionType.html +5 -1
  54. data/webpage/rdoc/RubyBreaker/TypeDefs/MethodListType.html +5 -1
  55. data/webpage/rdoc/RubyBreaker/TypeDefs/MethodType.html +5 -1
  56. data/webpage/rdoc/RubyBreaker/TypeDefs/NilType.html +5 -1
  57. data/webpage/rdoc/RubyBreaker/TypeDefs/NominalType.html +5 -1
  58. data/webpage/rdoc/RubyBreaker/TypeDefs/OptionalType.html +5 -1
  59. data/webpage/rdoc/RubyBreaker/TypeDefs/OrType.html +5 -1
  60. data/webpage/rdoc/RubyBreaker/TypeDefs/SelfType.html +5 -1
  61. data/webpage/rdoc/RubyBreaker/TypeDefs/Type.html +5 -1
  62. data/webpage/rdoc/RubyBreaker/TypeDefs/VarLengthType.html +5 -1
  63. data/webpage/rdoc/RubyBreaker/TypeUnparser.html +5 -1
  64. data/webpage/rdoc/RubyBreaker/Typing.html +5 -1
  65. data/webpage/rdoc/RubyBreaker/Util.html +5 -1
  66. data/webpage/rdoc/Test.html +5 -1
  67. data/webpage/rdoc/Test/Unit.html +5 -1
  68. data/webpage/rdoc/created.rid +8 -8
  69. data/webpage/rdoc/index.html +5 -1
  70. data/webpage/rdoc/js/search_index.js +1 -1
  71. data/webpage/rdoc/table_of_contents.html +39 -31
  72. data/webpage/tutorial.html +69 -39
  73. metadata +8 -5
data/README.md CHANGED
@@ -3,11 +3,11 @@
3
3
  RubyBreaker is a dynamic type documentation tool written in pure Ruby. It
4
4
  provides the framework for dynamically instrumenting a Ruby program to
5
5
  monitor objects during the execution and document the observed type
6
- information. In other words, RubyBreaker "breaks" Ruby code out of its
7
- obscurity and wildness (as in "code breaking" or "horse breaking") by
8
- auto-documenting type information. The type documentation generated by
9
- RubyBreaker is also an executable Ruby code that can be used as an input to
10
- subsequent analyses.
6
+ information. In addition, it can perform early dynamic type checking.
7
+ In other words, RubyBreaker helps Ruby programs "break" out of obscurities
8
+ and convolutions by documenting the type information. The type
9
+ documentation generated by RubyBreaker is also an executable Ruby code that
10
+ can be used as an input to subsequent analyses.
11
11
 
12
12
  The primary goal of RubyBreaker is to assign a type signature to every
13
13
  method in selected modules and classes. A type signature is written in the
@@ -16,7 +16,7 @@ used in Ruby Core Library Doc. No manual code change is required. Overall,
16
16
  this tool should help Ruby programmers document their code more rigorously
17
17
  and effectively.
18
18
 
19
- Currently, RubyBreaker *cannot*
19
+ Currently, RubyBreaker *does not*
20
20
 
21
21
  * Auto-document block arguments (inherent)
22
22
  * Support parametric polymorphic types
data/TUTORIAL.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Tutorial
2
2
 
3
- This tutorial will describe the basic usage of the tool, the RubyBreaker
4
- Type Annotation Language, and the RubyBreaker Type System.
3
+ This tutorial will describe the basic usages of the tool and the types
4
+ supported by RubyBreaker.
5
5
 
6
6
  ## Usage
7
7
 
@@ -11,16 +11,36 @@ does require a minimum change in the Rakefile (but no code change in the
11
11
  source program) but is better for a long-term maintenance. Regardless of the
12
12
  mode you choose to run, no source code change is required.
13
13
 
14
+ ### Running RubyBreaker
15
+
14
16
  Let us briefly see how RubyBreaker can be run directly as a command-line
15
17
  program to understand the overall concept of the tool. We will explain how
16
- to use RubyBreaker in a Rakefile later.
18
+ to use RubyBreaker in a Rakefile later. The following is the banner of
19
+ RubyBreaker when running in the command-line mode:
20
+
21
+ Usage: rubybreaker.rb [options] prog[.rb]
22
+ -b, --break MODULES Specify modules/classes to 'break'
23
+ -c, --check MODULES Specify modules/classes to check
24
+ -l, --libs LIBRARIES Specify libraries to load
25
+ --debug Run in debug mode
26
+ --style STYLE Select type signature style - underscore or camelize
27
+ --io-file FILE Specify I/O file
28
+ --save-output Save output to file
29
+ -s, --[no-]stdout Show output on screen
30
+ -a, --[no-]append Append output to input file
31
+ -v, --verbose Show messages in detail
32
+ -h, --help Show this help text
33
+
34
+ #### Example
35
+
36
+ Here is an example of running Rubybreaker as a command.
17
37
 
18
38
  $ rubybreaker -v -s -l lib.rb -b A,B prog.rb
19
39
 
20
- The above command runs RubyBreaker in verbose mode (`-v`) and will display
40
+ This runs RubyBreaker in verbose mode (`-v`) and will display
21
41
  the output on the screen (`-s`). Before RubyBreaker runs `prog.rb`, it will
22
42
  import (`-l`) `lib.rb` and _break_ (`-b`) classes `A` and `B`.
23
- Here is `lib.rb`:
43
+ Here is the source of `lib.rb`:
24
44
 
25
45
  class A
26
46
  def foo(x)
@@ -33,12 +53,12 @@ Here is `lib.rb`:
33
53
  end
34
54
  end
35
55
 
36
- And, `prog.rb` simply imports the library file and executes it:
56
+ And, `prog.rb` simply imports the library file and executes `A#foo` with a
57
+ number:
37
58
 
38
59
  require "lib"
39
60
  A.new.foo(1)
40
61
 
41
- This example will show how `A#foo` method is given a type by RubyBreaker.
42
62
  After running the command shown above, the following output will be
43
63
  generated and displayed on the screen:
44
64
 
@@ -46,11 +66,12 @@ generated and displayed on the screen:
46
66
  typesig("foo(fixnum[to_s]) -> string")
47
67
  end
48
68
 
69
+ This example shows how RubyBreaker documents the type of `A#foo`.
49
70
  Here, the `typesig` method call registers `foo` as a method type that takes
50
71
  an object that has `Fixnum#to_s` method and returns a `String`. This
51
72
  method is made available simply by importing `rubybreaker`. Now, assume
52
73
  that an additional code, `B.new.bar(A.new,1)`, is added at the end of
53
- `prog.rb`. The subsequent run will generate the following result:
74
+ `prog.rb`. It generate the following result:
54
75
 
55
76
  class A
56
77
  typesig("foo(fixnum[to_s]) -> string")
@@ -67,6 +88,20 @@ the program behaves correctly (for those test runs) as intended by the
67
88
  programmer. This assumption is not a strong requirement, but is necessary to
68
89
  obtain precise and accurate type information.
69
90
 
91
+ #### Early Dynamic Type Checking
92
+
93
+ RubyBreaker gives you an option to run the early dynamic type check. If a
94
+ method is documented and its module is specified to be _type checked_, then
95
+ RubyBreaker will verify if the argument types and the return type are
96
+ appropriate at runtime. To allow this feature in command-line, use `-c`
97
+ (checking) option:
98
+
99
+ rubybreaker -l lib.rb -c A prog.rb
100
+
101
+ Here, the library code `lib.rb` will be imported (`-l`) and class `A` will
102
+ be instrumented for type checking (`-c`) during the execution of `prog.rb`.
103
+ We will explain how to run type checking in non-command mode.
104
+
70
105
  ### Using Ruby Unit Testing Framework
71
106
 
72
107
  Instead of manually inserting the entry point indicator in the source
@@ -133,6 +168,27 @@ If this is the route you are taking, there needs no editing of the source
133
168
  program whatsoever. This task will take care of instrumenting the specified
134
169
  modules and classes at proper moments.
135
170
 
171
+ #### Early Dynamic Type Checking in Rakefile
172
+
173
+ Previously, we explained how to perform type checking in the command mode.
174
+ You can also specify which modules/classes to type check in the Rakfile. The
175
+ following shows an example usage of `check` option.
176
+
177
+ require "rubybreaker/task"
178
+ ...
179
+ desc "Run RubyBreaker"
180
+ Rake::RubyBreakerTestTask.new(:"rubybreaker") do |t|
181
+ t.libs << "lib"
182
+ t.test_files = ["test/foo/tc_foo1.rb"]
183
+ # ...Other test task options..
184
+ t.rubybreaker_opts << "-v" # run in verbose mode
185
+ t.break = ["Class1", "Class2", ...] # specify classes to monitor
186
+ t.check = ["Class3", ....] # specify classes to check
187
+ end
188
+
189
+ Alternatively, you can use `RubyBreaker.check()` to specify classes to type
190
+ check in the setup code of the test case.
191
+
136
192
  ## Type Annotation
137
193
 
138
194
  The annotation language used in RubyBreaker resembles the method
@@ -289,29 +345,3 @@ compatibility between the return types and "promote" the method type to a
289
345
  method list type by spliting the type signature into two (or more in
290
346
  subsequent "promotions").
291
347
 
292
- ### Early Dynamic Type Checking
293
-
294
- RubyBreaker gives you an option to run the early dynamic type check. If a
295
- method is documented and its module is specified to be _type checked_, then
296
- RubyBreaker will verify if the argument types and the return type are
297
- appropriate at runtime. To allow this feature in command-line, use `-c`
298
- (checking) option:
299
-
300
- rubybreaker -l lib.rb -c A prog.rb
301
-
302
- Or use it in Rakefile:
303
-
304
- require "rubybreaker/task"
305
- ...
306
- desc "Run RubyBreaker"
307
- Rake::RubyBreakerTestTask.new(:"rubybreaker") do |t|
308
- t.libs << "lib"
309
- t.test_files = ["test/foo/tc_foo1.rb"]
310
- # ...Other test task options..
311
- t.rubybreaker_opts << "-v" # run in verbose mode
312
- t.break = ["Class1", "Class2", ...] # specify classes to monitor
313
- t.check = ["Class3", ....] # specify classes to check
314
- end
315
-
316
- Alternatively, you can use `RubyBreaker.check()` to specify classes to type
317
- check in the setup code of the test case.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.7
1
+ 0.0.8
@@ -51,7 +51,13 @@ module RubyBreaker
51
51
  class TypeError < UserError
52
52
  end
53
53
 
54
- class SubtypeFailure < TypeError
54
+ class ArityError < TypeError
55
+ end
56
+
57
+ class ArgumentTypeError < TypeError
58
+ end
59
+
60
+ class ReturnTypeError < TypeError
55
61
  end
56
62
 
57
63
  end
@@ -93,7 +93,7 @@ module RubyBreaker
93
93
  when :check
94
94
  mm.check_before_method(obj, meth_info)
95
95
  end
96
- rescue Errors::TypeError => e
96
+ rescue ::Exception => e
97
97
  # Trap it, turn on the global monitor and then re-raise the
98
98
  # exception
99
99
  GLOBAL_MONITOR_SWITCH.turn_on()
@@ -121,7 +121,7 @@ module RubyBreaker
121
121
  when :check
122
122
  mm.check_after_method(obj, meth_info)
123
123
  end
124
- rescue Errors::TypeError => e
124
+ rescue ::Exception => e
125
125
  # Trap it, turn on the global monitor and then re-raise the
126
126
  # exception
127
127
  GLOBAL_MONITOR_SWITCH.turn_on()
@@ -70,14 +70,45 @@ module RubyBreaker
70
70
  def method_missing(mname,*args,&blk)
71
71
  ::RubyBreaker.log("Method_missing for #{mname}")
72
72
  if GLOBAL_MONITOR_SWITCH.switch
73
+
74
+ # Be safe and turn the switch off
75
+ GLOBAL_MONITOR_SWITCH.turn_off
76
+
73
77
  # Must handle send method specially (do not track them)
74
78
  if [:"__send__", :send].include?(mname)
75
79
  mname = args[0]
76
80
  args = args[1..-1]
77
81
  end
78
82
  @__rubybreaker_type.add_meth(mname)
83
+
84
+ # If self is not subject to breaking, then no need to send the
85
+ # wrapped arguments. This part is super IMPORTANT. Otherwise many
86
+ # native code stuff won't work including Numeric#+.
87
+ obj = self.__rubybreaker_obj
88
+ is_obj_mod = (obj.class == ::Class or obj.class == ::Module)
89
+ mod = is_obj_mod ? Runtime.eigen_class(obj) : obj.class
90
+
91
+ # Monitor map doesn't exist MEANS it's not being monitored.
92
+ unless MONITOR_MAP[mod]
93
+ args.map! do |arg|
94
+ if arg.respond_to?(WRAPPED_INDICATOR)
95
+ arg.__rubybreaker_obj
96
+ else
97
+ arg
98
+ end
99
+ end
100
+ end
101
+
102
+ # Turn on the global switch again
103
+ GLOBAL_MONITOR_SWITCH.turn_on
104
+
105
+ # And call the original method
79
106
  retval = @__rubybreaker_obj.send(mname, *args, &blk)
80
- retval = ObjectWrapper.new(retval)
107
+
108
+ # No need to wrap the object again...if it's wrapped already
109
+ unless retval.respond_to?(WRAPPED_INDICATOR)
110
+ retval = ObjectWrapper.new(retval)
111
+ end
81
112
  else
82
113
  retval = @__rubybreaker_obj.send(mname, *args, &blk)
83
114
  end
@@ -19,6 +19,11 @@ module RubyBreaker
19
19
  # indicate overridden methods.
20
20
  OVERRIDE_PREFIX = "__rubybreaker"
21
21
 
22
+ # Prohibit these module/class+method from being overriden
23
+ BLACKLIST = {
24
+ String => [:to_s, :<<, :concat, :gsub],
25
+ }
26
+
22
27
  end
23
28
 
24
29
  end
@@ -53,8 +58,33 @@ end
53
58
 
54
59
  end
55
60
 
61
+ [Symbol, Numeric, Fixnum, Float, Integer, Bignum, String].each do |mod|
62
+ mod.instance_methods(false).each do |meth_name|
63
+ black_list = RubyBreaker::Runtime::BLACKLIST
64
+ next if black_list[mod] && black_list[mod].include?(meth_name)
65
+ alias_name = "#{RubyBreaker::Runtime::OVERRIDE_PREFIX}_#{mod.object_id}" +
66
+ "_#{meth_name}"
67
+ mod.module_eval <<-EOS
68
+
69
+ alias :"#{alias_name}" :"#{meth_name}"
70
+
71
+ def #{meth_name}(*args)
72
+ args = args.map do |arg|
73
+ if arg.respond_to?(RubyBreaker::Runtime::WRAPPED_INDICATOR)
74
+ arg.__rubybreaker_obj
75
+ else
76
+ arg
77
+ end
78
+ end
79
+ send(:"#{RubyBreaker::Runtime::OVERRIDE_PREFIX}_#{mod.object_id}_#{meth_name}",*args)
80
+ end
81
+
82
+ EOS
83
+ end
84
+ end
85
+
56
86
  # TODO: add more modules here as necessary!
57
- [Object, Numeric, String, Symbol, Enumerable, Array, Hash].each do |mod|
87
+ [Object, Enumerable, Array, Hash].each do |mod|
58
88
 
59
89
  [:"==", :equal?, :eql?].each do |meth_name|
60
90
 
@@ -171,11 +171,33 @@ module RubyBreaker
171
171
 
172
172
  # This method creates the prefix for the type error message.
173
173
  def type_error_msg_prefix(mod, meth_name)
174
+ # Match eigen class and returns a prefix that is a class method
175
+ # using dot (.) instead of double colons (::).
174
176
  result = /#<Class:(.+)>/.match("#{mod}")
175
177
  prefix = result ? "#{result[1]}." : "#{mod}#"
176
178
  return "#{prefix}#{meth_name}"
177
179
  end
178
180
 
181
+ # This method performs the arity check.
182
+ def arity_check(num_of_args, meth_type)
183
+ arg_types = meth_type.arg_types
184
+
185
+ opt = false
186
+ varlen = false
187
+
188
+ arg_types.each_with_index do |arg_type, i|
189
+ if arg_type.kind_of?(OptionalType)
190
+ opt = true
191
+ elsif arg_type.kind_of?(VarLengthType)
192
+ varlen = true
193
+ end
194
+ end
195
+ check = (opt && num_of_args <= arg_types.size) ||
196
+ (varlen && num_of_args >= arg_types.size) ||
197
+ num_of_args == arg_types.size
198
+ return check
199
+ end
200
+
179
201
  public
180
202
 
181
203
  # This method is invoked before the original method is executed.
@@ -189,19 +211,55 @@ module RubyBreaker
189
211
  # Let's take things out of the MethodInfo object
190
212
  meth_name = meth_info.meth_name
191
213
  args = meth_info.args
192
- blk = meth_info.blk
193
- ret = meth_info.ret
214
+ # blk = meth_info.blk
194
215
 
195
216
  # Get the registered method type for this method
196
217
  meth_type = meth_type_map[meth_name]
197
218
 
198
- args.each_with_index do |arg, i|
199
- arg_type = NominalType.new(arg.class)
200
- if !arg_type.subtype_of?(meth_type.arg_types[i])
219
+ # Do an arity check first.
220
+ if !arity_check(args.size, meth_type)
221
+ msg = type_error_msg_prefix(mod, meth_name) +
222
+ " has an arity of #{meth_type.arg_types.size} " +
223
+ "but #{args.size} arguments were passed in"
224
+ raise Errors::ArityError.new(msg)
225
+ end
226
+
227
+ # Remember what the last formal argument type was so that, if it is
228
+ # a variable length argument type, we use to check the remaining
229
+ # arguments.
230
+ last_supertype = nil
231
+
232
+ # Check actual arguments up until the last position of the formal
233
+ # argument type. If the number of the formal arguments is less than
234
+ # actual arguments, it means the last formal argument is a variable
235
+ # length. If it's the other way around, there are optional
236
+ # arguments.
237
+ meth_type.arg_types.each_with_index do |supertype, i|
238
+ if supertype.kind_of?(OptionalType) ||
239
+ supertype.kind_of?(VarLengthType)
240
+ supertype = supertype.type
241
+ end
242
+ last_supertype = supertype
243
+ break if i >= args.size
244
+ subtype = NominalType.new(args[i].class)
245
+ if !subtype.subtype_of?(supertype)
201
246
  msg = type_error_msg_prefix(mod, meth_name) +
202
247
  "'s #{Util.ordinalize(i+1)} argument " +
203
- "does not have type #{arg_type.unparse()}."
204
- raise Errors::TypeError.new(msg)
248
+ "does not have type #{supertype.unparse()}."
249
+ raise Errors::ArgumentTypeError.new(msg)
250
+ end
251
+ end
252
+
253
+ # Handle the remaining actual arguments
254
+ if meth_type.arg_types.size < args.size
255
+ for i in meth_type.arg_types.size..args.size-1
256
+ subtype = NominalType.new(args[i].class)
257
+ if !subtype.subtype_of?(last_supertype)
258
+ msg = type_error_msg_prefix(mod, meth_name) +
259
+ "'s #{Util.ordinalize(i+1)} argument " +
260
+ "does not have type #{last_supertype.unparse()}."
261
+ raise Errors::ArgumentTypeError.new(msg)
262
+ end
205
263
  end
206
264
  end
207
265
 
@@ -217,8 +275,6 @@ module RubyBreaker
217
275
 
218
276
  # Let's take things out of the MethodInfo object
219
277
  meth_name = meth_info.meth_name
220
- args = meth_info.args
221
- blk = meth_info.blk
222
278
  ret = meth_info.ret
223
279
 
224
280
  # Get the registered method type for this method
@@ -228,7 +284,7 @@ module RubyBreaker
228
284
  if !meth_type.ret_type.subtype_of?(ret_type)
229
285
  msg = type_error_msg_prefix(mod, meth_name) +
230
286
  " return value does not have type #{ret_type.unparse()}."
231
- raise Errors::TypeError.new(msg)
287
+ raise Errors::ReturnTypeError.new(msg)
232
288
  end
233
289
  end
234
290
 
@@ -251,7 +307,7 @@ module RubyBreaker
251
307
  ret = meth_info.ret
252
308
 
253
309
  args = args.map do |arg|
254
- if arg.kind_of?(TrueClass) || arg.kind_of?(FalseClass)
310
+ if arg == nil || arg.kind_of?(TrueClass) || arg.kind_of?(FalseClass)
255
311
  # XXX: would overrides resolve this issue?
256
312
  arg
257
313
  else
@@ -12,6 +12,9 @@ class IntegratedCheckingTest < Test::Unit::TestCase
12
12
  typesig("f2(fixnum[to_s]) -> string")
13
13
  def f2(x); x.to_s end
14
14
 
15
+ typesig("g2([to_s]) -> string")
16
+ def g2(x); x.to_s end
17
+
15
18
  typesig("f3(fixnum[foo, to_s]) -> string")
16
19
  def f3(x); x.to_s end
17
20
 
@@ -21,6 +24,12 @@ class IntegratedCheckingTest < Test::Unit::TestCase
21
24
  typesig("f5(fixnum, fixnum) -> fixnum")
22
25
  def f5(x,y); x+y end
23
26
 
27
+ typesig("f6(fixnum, fixnum?, fixnum*) -> fixnum")
28
+ def f6(x, y=1, *z)
29
+ z = eval(z.join("+"))
30
+ x + y + (z ? z : 0)
31
+ end
32
+
24
33
  end
25
34
 
26
35
  def setup()
@@ -32,25 +41,46 @@ class IntegratedCheckingTest < Test::Unit::TestCase
32
41
  assert_nothing_thrown do
33
42
  a.f1(2)
34
43
  end
35
- assert_raise RubyBreaker::Errors::TypeError do
44
+ assert_raise RubyBreaker::Errors::ArgumentTypeError do
36
45
  a.f1("2")
37
46
  end
38
47
  end
39
48
 
49
+ def test_arity()
50
+ a = A.new
51
+ assert_raise Errors::ArityError do
52
+ a.f1(1, 2)
53
+ end
54
+ assert_nothing_thrown do
55
+ a.f6(1, 2)
56
+ a.f6(1, 2, 3)
57
+ a.f6(1, 2, 3, 4)
58
+ a.f6(1, 2, 3, 4, 5)
59
+ end
60
+ end
61
+
40
62
  def test_duck()
63
+ a = A.new
64
+ assert_nothing_thrown do
65
+ a.g2(2)
66
+ a.g2("2")
67
+ end
68
+ end
69
+
70
+ def test_fusion()
41
71
  a = A.new
42
72
  assert_nothing_thrown do
43
73
  a.f2(2)
44
74
  a.f3(2) # This will pass too
45
75
  end
46
- assert_raise RubyBreaker::Errors::TypeError do
76
+ assert_raise RubyBreaker::Errors::ArgumentTypeError do
47
77
  a.f3("2")
48
78
  end
49
79
  end
50
80
 
51
81
  def test_ret()
52
82
  a = A.new
53
- assert_raise RubyBreaker::Errors::TypeError do
83
+ assert_raise RubyBreaker::Errors::ReturnTypeError do
54
84
  a.f4(2)
55
85
  end
56
86
  end
@@ -60,10 +90,10 @@ class IntegratedCheckingTest < Test::Unit::TestCase
60
90
  assert_nothing_thrown do
61
91
  a.f5(1, 2)
62
92
  end
63
- assert_raise RubyBreaker::Errors::TypeError do
93
+ assert_raise RubyBreaker::Errors::ArgumentTypeError do
64
94
  a.f5("1", 2)
65
95
  end
66
- assert_raise RubyBreaker::Errors::TypeError do
96
+ assert_raise RubyBreaker::Errors::ArgumentTypeError do
67
97
  a.f5(1, "2")
68
98
  end
69
99
  end