steep 0.11.1 → 0.12.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.
Files changed (299) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +27 -0
  3. data/.gitmodules +3 -0
  4. data/CHANGELOG.md +5 -0
  5. data/README.md +48 -90
  6. data/Rakefile +10 -6
  7. data/Steepfile +1 -0
  8. data/bin/setup +1 -0
  9. data/bin/smoke_runner.rb +9 -14
  10. data/exe/rbs +3 -0
  11. data/exe/ruby-signature +3 -0
  12. data/exe/steep +1 -0
  13. data/lib/steep.rb +32 -26
  14. data/lib/steep/annotation_parser.rb +167 -0
  15. data/lib/steep/ast/annotation/collection.rb +7 -7
  16. data/lib/steep/ast/types.rb +60 -0
  17. data/lib/steep/ast/types/any.rb +1 -1
  18. data/lib/steep/ast/types/factory.rb +535 -0
  19. data/lib/steep/ast/types/name.rb +3 -3
  20. data/lib/steep/ast/types/var.rb +1 -1
  21. data/lib/steep/cli.rb +56 -240
  22. data/lib/steep/drivers/annotations.rb +36 -19
  23. data/lib/steep/drivers/check.rb +55 -91
  24. data/lib/steep/drivers/init.rb +54 -0
  25. data/lib/steep/drivers/langserver.rb +241 -150
  26. data/lib/steep/drivers/print_project.rb +56 -0
  27. data/lib/steep/drivers/signature_error_printer.rb +25 -0
  28. data/lib/steep/drivers/trace_printer.rb +25 -0
  29. data/lib/steep/drivers/utils/driver_helper.rb +26 -0
  30. data/lib/steep/drivers/validate.rb +18 -38
  31. data/lib/steep/drivers/vendor.rb +46 -0
  32. data/lib/steep/drivers/watch.rb +78 -140
  33. data/lib/steep/errors.rb +22 -13
  34. data/lib/steep/interface/interface.rb +91 -0
  35. data/lib/steep/interface/method.rb +0 -4
  36. data/lib/steep/interface/method_type.rb +362 -2
  37. data/lib/steep/interface/substitution.rb +22 -0
  38. data/lib/steep/project.rb +25 -233
  39. data/lib/steep/project/dsl.rb +132 -0
  40. data/lib/steep/project/file.rb +93 -76
  41. data/lib/steep/project/file_loader.rb +63 -0
  42. data/lib/steep/project/options.rb +7 -0
  43. data/lib/steep/project/target.rb +190 -0
  44. data/lib/steep/signature/errors.rb +25 -77
  45. data/lib/steep/signature/validator.rb +122 -0
  46. data/lib/steep/source.rb +12 -7
  47. data/lib/steep/subtyping/check.rb +357 -633
  48. data/lib/steep/subtyping/constraints.rb +2 -2
  49. data/lib/steep/subtyping/trace.rb +23 -0
  50. data/lib/steep/type_construction.rb +509 -455
  51. data/lib/steep/type_inference/constant_env.rb +16 -24
  52. data/lib/steep/type_inference/type_env.rb +26 -18
  53. data/lib/steep/version.rb +1 -1
  54. data/sample/Steepfile +6 -0
  55. data/sample/lib/conference.rb +12 -0
  56. data/sample/sig/conference.rbs +6 -0
  57. data/smoke/alias/Steepfile +4 -0
  58. data/smoke/alias/a.rb +2 -2
  59. data/smoke/alias/{a.rbi → a.rbs} +1 -1
  60. data/smoke/and/Steepfile +4 -0
  61. data/smoke/array/Steepfile +4 -0
  62. data/smoke/array/a.rb +2 -2
  63. data/smoke/array/b.rb +4 -4
  64. data/smoke/array/c.rb +2 -2
  65. data/smoke/block/Steepfile +5 -0
  66. data/smoke/block/{a.rbi → a.rbs} +1 -1
  67. data/smoke/block/{c.rbi → c.rbs} +0 -0
  68. data/smoke/block/d.rb +6 -6
  69. data/smoke/case/Steepfile +4 -0
  70. data/smoke/case/a.rb +4 -3
  71. data/smoke/class/Steepfile +4 -0
  72. data/smoke/class/a.rb +1 -4
  73. data/smoke/class/a.rbs +24 -0
  74. data/smoke/class/h.rb +6 -2
  75. data/smoke/class/{h.rbi → h.rbs} +1 -2
  76. data/smoke/class/i.rb +1 -2
  77. data/smoke/class/i.rbs +9 -0
  78. data/smoke/const/Steepfile +4 -0
  79. data/smoke/dstr/Steepfile +4 -0
  80. data/smoke/ensure/Steepfile +4 -0
  81. data/smoke/ensure/a.rb +1 -1
  82. data/smoke/enumerator/Steepfile +4 -0
  83. data/smoke/enumerator/a.rb +7 -7
  84. data/smoke/enumerator/b.rb +6 -6
  85. data/smoke/extension/Steepfile +4 -0
  86. data/smoke/extension/{a.rbi → a.rbs} +2 -2
  87. data/smoke/extension/{e.rbi → e.rbs} +2 -2
  88. data/smoke/hash/Steepfile +4 -0
  89. data/smoke/hash/{a.rbi → a.rbs} +0 -0
  90. data/smoke/hash/b.rb +2 -2
  91. data/smoke/hash/c.rb +1 -1
  92. data/smoke/hash/e.rbs +3 -0
  93. data/smoke/hash/f.rb +1 -1
  94. data/smoke/hello/Steepfile +4 -0
  95. data/smoke/hello/hello.rbs +7 -0
  96. data/smoke/if/Steepfile +4 -0
  97. data/smoke/implements/Steepfile +4 -0
  98. data/smoke/implements/a.rbs +6 -0
  99. data/smoke/initialize/Steepfile +4 -0
  100. data/smoke/initialize/a.rbs +3 -0
  101. data/smoke/integer/Steepfile +4 -0
  102. data/smoke/integer/a.rb +5 -3
  103. data/smoke/interface/Steepfile +4 -0
  104. data/smoke/interface/{a.rbi → a.rbs} +0 -0
  105. data/smoke/kwbegin/Steepfile +4 -0
  106. data/smoke/lambda/Steepfile +4 -0
  107. data/smoke/lambda/a.rb +9 -2
  108. data/smoke/literal/Steepfile +4 -0
  109. data/smoke/literal/{literal_methods.rbi → literal_methods.rbs} +0 -0
  110. data/smoke/map/Steepfile +4 -0
  111. data/smoke/map/a.rb +1 -1
  112. data/smoke/method/Steepfile +4 -0
  113. data/smoke/method/{a.rbi → a.rbs} +0 -0
  114. data/smoke/method/b.rb +1 -4
  115. data/smoke/method/d.rb +1 -0
  116. data/smoke/method/d.rbs +3 -0
  117. data/smoke/module/Steepfile +4 -0
  118. data/smoke/module/a.rb +1 -1
  119. data/smoke/module/a.rbs +16 -0
  120. data/smoke/module/c.rb +1 -1
  121. data/smoke/regexp/Steepfile +4 -0
  122. data/smoke/regexp/a.rb +2 -2
  123. data/smoke/regexp/b.rb +16 -16
  124. data/smoke/regression/Steepfile +5 -0
  125. data/smoke/regression/array.rb +2 -2
  126. data/smoke/regression/hash.rb +2 -2
  127. data/smoke/regression/poly_new.rb +2 -0
  128. data/smoke/regression/poly_new.rbs +4 -0
  129. data/smoke/regression/set_divide.rb +2 -2
  130. data/smoke/rescue/Steepfile +4 -0
  131. data/smoke/rescue/a.rb +1 -1
  132. data/smoke/self/Steepfile +4 -0
  133. data/smoke/self/a.rbs +4 -0
  134. data/smoke/skip/Steepfile +4 -0
  135. data/smoke/stdout/Steepfile +4 -0
  136. data/smoke/stdout/{a.rbi → a.rbs} +1 -1
  137. data/smoke/super/Steepfile +4 -0
  138. data/smoke/super/a.rbs +10 -0
  139. data/smoke/type_case/Steepfile +4 -0
  140. data/smoke/type_case/a.rb +1 -1
  141. data/smoke/yield/Steepfile +4 -0
  142. data/smoke/yield/a.rb +2 -2
  143. data/steep.gemspec +14 -7
  144. data/vendor/ruby-signature/.github/workflows/ruby.yml +27 -0
  145. data/vendor/ruby-signature/.gitignore +12 -0
  146. data/vendor/ruby-signature/.rubocop.yml +15 -0
  147. data/vendor/ruby-signature/BSDL +22 -0
  148. data/vendor/ruby-signature/COPYING +56 -0
  149. data/vendor/ruby-signature/Gemfile +6 -0
  150. data/vendor/ruby-signature/README.md +93 -0
  151. data/vendor/ruby-signature/Rakefile +66 -0
  152. data/vendor/ruby-signature/bin/annotate-with-rdoc +156 -0
  153. data/vendor/ruby-signature/bin/console +14 -0
  154. data/vendor/ruby-signature/bin/query-rdoc +103 -0
  155. data/vendor/ruby-signature/bin/setup +10 -0
  156. data/vendor/ruby-signature/bin/sort +88 -0
  157. data/vendor/ruby-signature/bin/test_runner.rb +17 -0
  158. data/vendor/ruby-signature/docs/CONTRIBUTING.md +97 -0
  159. data/vendor/ruby-signature/docs/sigs.md +148 -0
  160. data/vendor/ruby-signature/docs/stdlib.md +152 -0
  161. data/vendor/ruby-signature/docs/syntax.md +528 -0
  162. data/vendor/ruby-signature/exe/rbs +3 -0
  163. data/vendor/ruby-signature/exe/ruby-signature +7 -0
  164. data/vendor/ruby-signature/lib/ruby/signature.rb +64 -0
  165. data/vendor/ruby-signature/lib/ruby/signature/ast/annotation.rb +29 -0
  166. data/vendor/ruby-signature/lib/ruby/signature/ast/comment.rb +29 -0
  167. data/vendor/ruby-signature/lib/ruby/signature/ast/declarations.rb +391 -0
  168. data/vendor/ruby-signature/lib/ruby/signature/ast/members.rb +364 -0
  169. data/vendor/ruby-signature/lib/ruby/signature/buffer.rb +52 -0
  170. data/vendor/ruby-signature/lib/ruby/signature/builtin_names.rb +54 -0
  171. data/vendor/ruby-signature/lib/ruby/signature/cli.rb +534 -0
  172. data/vendor/ruby-signature/lib/ruby/signature/constant.rb +28 -0
  173. data/vendor/ruby-signature/lib/ruby/signature/constant_table.rb +152 -0
  174. data/vendor/ruby-signature/lib/ruby/signature/definition.rb +172 -0
  175. data/vendor/ruby-signature/lib/ruby/signature/definition_builder.rb +921 -0
  176. data/vendor/ruby-signature/lib/ruby/signature/environment.rb +283 -0
  177. data/vendor/ruby-signature/lib/ruby/signature/environment_loader.rb +138 -0
  178. data/vendor/ruby-signature/lib/ruby/signature/environment_walker.rb +126 -0
  179. data/vendor/ruby-signature/lib/ruby/signature/errors.rb +189 -0
  180. data/vendor/ruby-signature/lib/ruby/signature/location.rb +104 -0
  181. data/vendor/ruby-signature/lib/ruby/signature/method_type.rb +125 -0
  182. data/vendor/ruby-signature/lib/ruby/signature/namespace.rb +93 -0
  183. data/vendor/ruby-signature/lib/ruby/signature/parser.y +1343 -0
  184. data/vendor/ruby-signature/lib/ruby/signature/prototype/rb.rb +441 -0
  185. data/vendor/ruby-signature/lib/ruby/signature/prototype/rbi.rb +579 -0
  186. data/vendor/ruby-signature/lib/ruby/signature/prototype/runtime.rb +383 -0
  187. data/vendor/ruby-signature/lib/ruby/signature/substitution.rb +48 -0
  188. data/vendor/ruby-signature/lib/ruby/signature/test.rb +28 -0
  189. data/vendor/ruby-signature/lib/ruby/signature/test/errors.rb +63 -0
  190. data/vendor/ruby-signature/lib/ruby/signature/test/hook.rb +290 -0
  191. data/vendor/ruby-signature/lib/ruby/signature/test/setup.rb +58 -0
  192. data/vendor/ruby-signature/lib/ruby/signature/test/spy.rb +324 -0
  193. data/vendor/ruby-signature/lib/ruby/signature/test/test_helper.rb +185 -0
  194. data/vendor/ruby-signature/lib/ruby/signature/test/type_check.rb +256 -0
  195. data/vendor/ruby-signature/lib/ruby/signature/type_name.rb +72 -0
  196. data/vendor/ruby-signature/lib/ruby/signature/types.rb +932 -0
  197. data/vendor/ruby-signature/lib/ruby/signature/variance_calculator.rb +140 -0
  198. data/vendor/ruby-signature/lib/ruby/signature/vendorer.rb +49 -0
  199. data/vendor/ruby-signature/lib/ruby/signature/version.rb +5 -0
  200. data/vendor/ruby-signature/lib/ruby/signature/writer.rb +271 -0
  201. data/vendor/ruby-signature/ruby-signature.gemspec +45 -0
  202. data/vendor/ruby-signature/stdlib/abbrev/abbrev.rbs +3 -0
  203. data/vendor/ruby-signature/stdlib/base64/base64.rbs +15 -0
  204. data/vendor/ruby-signature/stdlib/builtin/array.rbs +1997 -0
  205. data/vendor/ruby-signature/stdlib/builtin/basic_object.rbs +280 -0
  206. data/vendor/ruby-signature/stdlib/builtin/binding.rbs +177 -0
  207. data/vendor/ruby-signature/stdlib/builtin/builtin.rbs +35 -0
  208. data/vendor/ruby-signature/stdlib/builtin/class.rbs +145 -0
  209. data/vendor/ruby-signature/stdlib/builtin/comparable.rbs +116 -0
  210. data/vendor/ruby-signature/stdlib/builtin/complex.rbs +400 -0
  211. data/vendor/ruby-signature/stdlib/builtin/constants.rbs +37 -0
  212. data/vendor/ruby-signature/stdlib/builtin/data.rbs +5 -0
  213. data/vendor/ruby-signature/stdlib/builtin/deprecated.rbs +2 -0
  214. data/vendor/ruby-signature/stdlib/builtin/dir.rbs +419 -0
  215. data/vendor/ruby-signature/stdlib/builtin/encoding.rbs +606 -0
  216. data/vendor/ruby-signature/stdlib/builtin/enumerable.rbs +404 -0
  217. data/vendor/ruby-signature/stdlib/builtin/enumerator.rbs +260 -0
  218. data/vendor/ruby-signature/stdlib/builtin/errno.rbs +781 -0
  219. data/vendor/ruby-signature/stdlib/builtin/errors.rbs +582 -0
  220. data/vendor/ruby-signature/stdlib/builtin/exception.rbs +193 -0
  221. data/vendor/ruby-signature/stdlib/builtin/false_class.rbs +40 -0
  222. data/vendor/ruby-signature/stdlib/builtin/fiber.rbs +68 -0
  223. data/vendor/ruby-signature/stdlib/builtin/fiber_error.rbs +12 -0
  224. data/vendor/ruby-signature/stdlib/builtin/file.rbs +476 -0
  225. data/vendor/ruby-signature/stdlib/builtin/file_test.rbs +59 -0
  226. data/vendor/ruby-signature/stdlib/builtin/float.rbs +696 -0
  227. data/vendor/ruby-signature/stdlib/builtin/gc.rbs +121 -0
  228. data/vendor/ruby-signature/stdlib/builtin/hash.rbs +1029 -0
  229. data/vendor/ruby-signature/stdlib/builtin/integer.rbs +710 -0
  230. data/vendor/ruby-signature/stdlib/builtin/io.rbs +683 -0
  231. data/vendor/ruby-signature/stdlib/builtin/kernel.rbs +574 -0
  232. data/vendor/ruby-signature/stdlib/builtin/marshal.rbs +135 -0
  233. data/vendor/ruby-signature/stdlib/builtin/match_data.rbs +141 -0
  234. data/vendor/ruby-signature/stdlib/builtin/math.rbs +66 -0
  235. data/vendor/ruby-signature/stdlib/builtin/method.rbs +182 -0
  236. data/vendor/ruby-signature/stdlib/builtin/module.rbs +248 -0
  237. data/vendor/ruby-signature/stdlib/builtin/nil_class.rbs +82 -0
  238. data/vendor/ruby-signature/stdlib/builtin/numeric.rbs +409 -0
  239. data/vendor/ruby-signature/stdlib/builtin/object.rbs +824 -0
  240. data/vendor/ruby-signature/stdlib/builtin/proc.rbs +426 -0
  241. data/vendor/ruby-signature/stdlib/builtin/process.rbs +354 -0
  242. data/vendor/ruby-signature/stdlib/builtin/random.rbs +93 -0
  243. data/vendor/ruby-signature/stdlib/builtin/range.rbs +226 -0
  244. data/vendor/ruby-signature/stdlib/builtin/rational.rbs +424 -0
  245. data/vendor/ruby-signature/stdlib/builtin/rb_config.rbs +10 -0
  246. data/vendor/ruby-signature/stdlib/builtin/regexp.rbs +131 -0
  247. data/vendor/ruby-signature/stdlib/builtin/ruby_vm.rbs +14 -0
  248. data/vendor/ruby-signature/stdlib/builtin/signal.rbs +55 -0
  249. data/vendor/ruby-signature/stdlib/builtin/string.rbs +770 -0
  250. data/vendor/ruby-signature/stdlib/builtin/string_io.rbs +13 -0
  251. data/vendor/ruby-signature/stdlib/builtin/struct.rbs +40 -0
  252. data/vendor/ruby-signature/stdlib/builtin/symbol.rbs +230 -0
  253. data/vendor/ruby-signature/stdlib/builtin/thread.rbs +1112 -0
  254. data/vendor/ruby-signature/stdlib/builtin/thread_group.rbs +23 -0
  255. data/vendor/ruby-signature/stdlib/builtin/time.rbs +739 -0
  256. data/vendor/ruby-signature/stdlib/builtin/trace_point.rbs +91 -0
  257. data/vendor/ruby-signature/stdlib/builtin/true_class.rbs +46 -0
  258. data/vendor/ruby-signature/stdlib/builtin/unbound_method.rbs +159 -0
  259. data/vendor/ruby-signature/stdlib/builtin/warning.rbs +17 -0
  260. data/vendor/ruby-signature/stdlib/erb/erb.rbs +18 -0
  261. data/vendor/ruby-signature/stdlib/find/find.rbs +44 -0
  262. data/vendor/ruby-signature/stdlib/pathname/pathname.rbs +21 -0
  263. data/vendor/ruby-signature/stdlib/prime/integer-extension.rbs +23 -0
  264. data/vendor/ruby-signature/stdlib/prime/prime.rbs +188 -0
  265. data/vendor/ruby-signature/stdlib/securerandom/securerandom.rbs +9 -0
  266. data/vendor/ruby-signature/stdlib/set/set.rbs +77 -0
  267. data/vendor/ruby-signature/stdlib/tmpdir/tmpdir.rbs +53 -0
  268. metadata +244 -54
  269. data/.travis.yml +0 -7
  270. data/lib/steep/ast/signature/alias.rb +0 -19
  271. data/lib/steep/ast/signature/class.rb +0 -33
  272. data/lib/steep/ast/signature/const.rb +0 -17
  273. data/lib/steep/ast/signature/env.rb +0 -138
  274. data/lib/steep/ast/signature/extension.rb +0 -21
  275. data/lib/steep/ast/signature/gvar.rb +0 -17
  276. data/lib/steep/ast/signature/interface.rb +0 -31
  277. data/lib/steep/ast/signature/members.rb +0 -115
  278. data/lib/steep/ast/signature/module.rb +0 -21
  279. data/lib/steep/drivers/print_interface.rb +0 -94
  280. data/lib/steep/drivers/scaffold.rb +0 -321
  281. data/lib/steep/drivers/utils/each_signature.rb +0 -31
  282. data/lib/steep/interface/abstract.rb +0 -68
  283. data/lib/steep/interface/builder.rb +0 -637
  284. data/lib/steep/interface/instantiated.rb +0 -163
  285. data/lib/steep/interface/ivar_chain.rb +0 -26
  286. data/lib/steep/parser.y +0 -1278
  287. data/lib/steep/project/listener.rb +0 -53
  288. data/smoke/class/a.rbi +0 -24
  289. data/smoke/class/d.rb +0 -9
  290. data/smoke/class/e.rb +0 -12
  291. data/smoke/class/i.rbi +0 -9
  292. data/smoke/hash/e.rbi +0 -3
  293. data/smoke/hello/hello.rbi +0 -7
  294. data/smoke/implements/a.rbi +0 -6
  295. data/smoke/initialize/a.rbi +0 -3
  296. data/smoke/module/a.rbi +0 -16
  297. data/smoke/self/a.rbi +0 -4
  298. data/smoke/super/a.rbi +0 -10
  299. data/stdlib/builtin.rbi +0 -787
@@ -0,0 +1,63 @@
1
+ module Ruby
2
+ module Signature
3
+ module Test
4
+ module Errors
5
+ ArgumentTypeError =
6
+ Struct.new(:klass, :method_name, :method_type, :param, :value, keyword_init: true)
7
+ BlockArgumentTypeError =
8
+ Struct.new(:klass, :method_name, :method_type, :param, :value, keyword_init: true)
9
+ ArgumentError =
10
+ Struct.new(:klass, :method_name, :method_type, keyword_init: true)
11
+ BlockArgumentError =
12
+ Struct.new(:klass, :method_name, :method_type, keyword_init: true)
13
+ ReturnTypeError =
14
+ Struct.new(:klass, :method_name, :method_type, :type, :value, keyword_init: true)
15
+ BlockReturnTypeError =
16
+ Struct.new(:klass, :method_name, :method_type, :type, :value, keyword_init: true)
17
+
18
+ UnexpectedBlockError = Struct.new(:klass, :method_name, :method_type, keyword_init: true)
19
+ MissingBlockError = Struct.new(:klass, :method_name, :method_type, keyword_init: true)
20
+
21
+ UnresolvedOverloadingError = Struct.new(:klass, :method_name, :method_types, keyword_init: true)
22
+
23
+ def self.format_param(param)
24
+ if param.name
25
+ "`#{param.type}` (#{param.name})"
26
+ else
27
+ "`#{param.type}`"
28
+ end
29
+ end
30
+
31
+ def self.inspect_(obj)
32
+ Hook.inspect_(obj)
33
+ end
34
+
35
+ def self.to_string(error)
36
+ method = "#{error.klass.name}#{error.method_name}"
37
+ case error
38
+ when ArgumentTypeError
39
+ "[#{method}] ArgumentTypeError: expected #{format_param error.param} but given `#{inspect_(error.value)}`"
40
+ when BlockArgumentTypeError
41
+ "[#{method}] BlockArgumentTypeError: expected #{format_param error.param} but given `#{inspect_(error.value)}`"
42
+ when ArgumentError
43
+ "[#{method}] ArgumentError: expected method type #{error.method_type}"
44
+ when BlockArgumentError
45
+ "[#{method}] BlockArgumentError: expected method type #{error.method_type}"
46
+ when ReturnTypeError
47
+ "[#{method}] ReturnTypeError: expected `#{error.type}` but returns `#{inspect_(error.value)}`"
48
+ when BlockReturnTypeError
49
+ "[#{method}] BlockReturnTypeError: expected `#{error.type}` but returns `#{inspect_(error.value)}`"
50
+ when UnexpectedBlockError
51
+ "[#{method}] UnexpectedBlockError: unexpected block is given for `#{error.method_type}`"
52
+ when MissingBlockError
53
+ "[#{method}] MissingBlockError: required block is missing for `#{error.method_type}`"
54
+ when UnresolvedOverloadingError
55
+ "[#{method}] UnresolvedOverloadingError: couldn't find a suitable overloading"
56
+ else
57
+ raise "Unexpected error: #{inspect_(error)}"
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,290 @@
1
+ require "ruby/signature"
2
+ require "pp"
3
+
4
+ module Ruby
5
+ module Signature
6
+ module Test
7
+ class Hook
8
+ class Error < Exception
9
+ attr_reader :errors
10
+
11
+ def initialize(errors)
12
+ @errors = errors
13
+ super "Type error detected: [#{errors.map {|e| Errors.to_string(e) }.join(", ")}]"
14
+ end
15
+ end
16
+
17
+ attr_reader :env
18
+ attr_reader :logger
19
+
20
+ attr_reader :instance_module
21
+ attr_reader :instance_methods
22
+ attr_reader :singleton_module
23
+ attr_reader :singleton_methods
24
+
25
+ attr_reader :klass
26
+ attr_reader :errors
27
+
28
+ def builder
29
+ @builder ||= DefinitionBuilder.new(env: env)
30
+ end
31
+
32
+ def typecheck
33
+ @typecheck ||= TypeCheck.new(self_class: klass, builder: builder)
34
+ end
35
+
36
+ def initialize(env, klass, logger:, raise_on_error: false)
37
+ @env = env
38
+ @logger = logger
39
+ @klass = klass
40
+
41
+ @instance_module = Module.new
42
+ @instance_methods = []
43
+
44
+ @singleton_module = Module.new
45
+ @singleton_methods = []
46
+
47
+ @errors = []
48
+
49
+ @raise_on_error = raise_on_error
50
+ end
51
+
52
+ def raise_on_error!(error = true)
53
+ @raise_on_error = error
54
+ self
55
+ end
56
+
57
+ def raise_on_error?
58
+ @raise_on_error
59
+ end
60
+
61
+ def prepend!
62
+ klass.prepend @instance_module
63
+ klass.singleton_class.prepend @singleton_module
64
+
65
+ if block_given?
66
+ yield
67
+ disable
68
+ end
69
+
70
+ self
71
+ end
72
+
73
+ def self.install(env, klass, logger:)
74
+ new(env, klass, logger: logger).prepend!
75
+ end
76
+
77
+ def refinement
78
+ klass = self.klass
79
+ instance_module = self.instance_module
80
+ singleton_module = self.singleton_module
81
+
82
+ Module.new do
83
+ refine klass do
84
+ prepend instance_module
85
+ end
86
+
87
+ refine klass.singleton_class do
88
+ prepend singleton_module
89
+ end
90
+ end
91
+ end
92
+
93
+ def verify_all
94
+ type_name = Namespace.parse(klass.name).to_type_name.absolute!
95
+
96
+ builder.build_instance(type_name).tap do |definition|
97
+ definition.methods.each do |name, method|
98
+ if method.defined_in.name.absolute! == type_name
99
+ unless method.annotations.any? {|a| a.string == "rbs:test:skip" }
100
+ logger.info "Installing a hook on #{type_name}##{name}: #{method.method_types.join(" | ")}"
101
+ verify instance_method: name, types: method.method_types
102
+ else
103
+ logger.info "Skipping test of #{type_name}##{name}"
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ builder.build_singleton(type_name).tap do |definition|
110
+ definition.methods.each do |name, method|
111
+ if method.defined_in&.name&.absolute! == type_name || name == :new
112
+ unless method.annotations.any? {|a| a.string == "rbs:test:skip" }
113
+ logger.info "Installing a hook on #{type_name}.#{name}: #{method.method_types.join(" | ")}"
114
+ verify singleton_method: name, types: method.method_types
115
+ else
116
+ logger.info "Skipping test of #{type_name}.#{name}"
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ self
123
+ end
124
+
125
+ def delegation(name, method_types, method_name)
126
+ hook = self
127
+
128
+ -> (*args, &block) do
129
+ hook.logger.debug { "#{method_name} receives arguments: #{hook.inspect_(args)}" }
130
+
131
+ block_calls = []
132
+
133
+ if block
134
+ original_block = block
135
+
136
+ block = hook.call(Object.new, INSTANCE_EVAL) do |fresh_obj|
137
+ ->(*as) do
138
+ hook.logger.debug { "#{method_name} receives block arguments: #{hook.inspect_(as)}" }
139
+
140
+ ret = if self.equal?(fresh_obj)
141
+ original_block[*as]
142
+ else
143
+ hook.call(self, INSTANCE_EXEC, *as, &original_block)
144
+ end
145
+
146
+ block_calls << ArgumentsReturn.new(
147
+ arguments: as,
148
+ return_value: ret,
149
+ exception: nil
150
+ )
151
+
152
+ hook.logger.debug { "#{method_name} returns from block: #{hook.inspect_(ret)}" }
153
+
154
+ ret
155
+ end.ruby2_keywords
156
+ end
157
+ end
158
+
159
+ method = hook.call(self, METHOD, name)
160
+ klass = hook.call(self, CLASS)
161
+ singleton_klass = begin
162
+ hook.call(self, SINGLETON_CLASS)
163
+ rescue TypeError
164
+ nil
165
+ end
166
+ prepended = klass.ancestors.include?(hook.instance_module) || singleton_klass&.ancestors&.include?(hook.singleton_module)
167
+ result = if prepended
168
+ method.super_method.call(*args, &block)
169
+ else
170
+ # Using refinement
171
+ method.call(*args, &block)
172
+ end
173
+
174
+ hook.logger.debug { "#{method_name} returns: #{hook.inspect_(result)}" }
175
+
176
+ call = CallTrace.new(method_call: ArgumentsReturn.new(arguments: args, return_value: result, exception: nil),
177
+ block_calls: block_calls,
178
+ block_given: block != nil)
179
+
180
+ method_type_errors = method_types.map do |method_type|
181
+ hook.typecheck.method_call(method_name, method_type, call, errors: [])
182
+ end
183
+
184
+ new_errors = []
185
+
186
+ if method_type_errors.none?(&:empty?)
187
+ if (best_errors = hook.find_best_errors(method_type_errors))
188
+ new_errors.push(*best_errors)
189
+ else
190
+ new_errors << Errors::UnresolvedOverloadingError.new(
191
+ klass: hook.klass,
192
+ method_name: method_name,
193
+ method_types: method_types
194
+ )
195
+ end
196
+ end
197
+
198
+ unless new_errors.empty?
199
+ new_errors.each do |error|
200
+ hook.logger.error Errors.to_string(error)
201
+ end
202
+
203
+ hook.errors.push(*new_errors)
204
+
205
+ if hook.raise_on_error?
206
+ raise Error.new(new_errors)
207
+ end
208
+ end
209
+
210
+ result
211
+ end.ruby2_keywords
212
+ end
213
+
214
+ def verify(instance_method: nil, singleton_method: nil, types:)
215
+ method_types = types.map do |type|
216
+ case type
217
+ when String
218
+ Parser.parse_method_type(type)
219
+ else
220
+ type
221
+ end
222
+ end
223
+
224
+ case
225
+ when instance_method
226
+ instance_methods << instance_method
227
+ call(self.instance_module, DEFINE_METHOD, instance_method, &delegation(instance_method, method_types, "##{instance_method}"))
228
+ when singleton_method
229
+ call(self.singleton_module, DEFINE_METHOD, singleton_method, &delegation(singleton_method, method_types, ".#{singleton_method}"))
230
+ end
231
+
232
+ self
233
+ end
234
+
235
+ def find_best_errors(errorss)
236
+ if errorss.size == 1
237
+ errorss[0]
238
+ else
239
+ no_arity_errors = errorss.select do |errors|
240
+ errors.none? do |error|
241
+ error.is_a?(Errors::ArgumentError) ||
242
+ error.is_a?(Errors::BlockArgumentError) ||
243
+ error.is_a?(Errors::MissingBlockError) ||
244
+ error.is_a?(Errors::UnexpectedBlockError)
245
+ end
246
+ end
247
+
248
+ unless no_arity_errors.empty?
249
+ # Choose a error set which doesn't include arity error
250
+ return no_arity_errors[0] if no_arity_errors.size == 1
251
+ end
252
+ end
253
+ end
254
+
255
+ def self.backtrace(skip: 2)
256
+ raise
257
+ rescue => exn
258
+ exn.backtrace.drop(skip)
259
+ end
260
+
261
+ def run
262
+ yield
263
+ self
264
+ ensure
265
+ disable
266
+ end
267
+
268
+ def call(receiver, method, *args, &block)
269
+ method.bind(receiver).call(*args, &block)
270
+ end
271
+
272
+ def inspect_(obj)
273
+ Hook.inspect_(obj)
274
+ end
275
+
276
+ def self.inspect_(obj)
277
+ obj.inspect
278
+ rescue
279
+ INSPECT.bind(obj).call()
280
+ end
281
+
282
+ def disable
283
+ self.instance_module.remove_method(*instance_methods)
284
+ self.singleton_module.remove_method(*singleton_methods)
285
+ self
286
+ end
287
+ end
288
+ end
289
+ end
290
+ end
@@ -0,0 +1,58 @@
1
+ require "ruby/signature"
2
+ require "ruby/signature/test"
3
+
4
+ require "optparse"
5
+ require "shellwords"
6
+
7
+ logger = Logger.new(STDERR)
8
+
9
+ begin
10
+ opts = Shellwords.shellsplit(ENV["RBS_TEST_OPT"] || "-I sig")
11
+ filter = ENV.fetch("RBS_TEST_TARGET").split(",")
12
+ skips = (ENV["RBS_TEST_SKIP"] || "").split(",")
13
+ logger.level = (ENV["RBS_TEST_LOGLEVEL"] || "info")
14
+ raise_on_error = ENV["RBS_TEST_RAISE"]
15
+ rescue
16
+ STDERR.puts "ruby/signature/test/setup handles the following environment variables:"
17
+ STDERR.puts " [REQUIRED] RBS_TEST_TARGET: test target class name, `Foo::Bar,Foo::Baz` for each class or `Foo::*` for all classes under `Foo`"
18
+ STDERR.puts " [OPTIONAL] RBS_TEST_SKIP: skip testing classes"
19
+ STDERR.puts " [OPTIONAL] RBS_TEST_OPT: options for signatures (`-r` for libraries or `-I` for signatures)"
20
+ STDERR.puts " [OPTIONAL] RBS_TEST_LOGLEVEL: one of debug|info|warn|error|fatal (defaults to info)"
21
+ STDERR.puts " [OPTIONAL] RBS_TEST_RAISE: specify any value to raise an exception when type error is detected"
22
+ exit 1
23
+ end
24
+
25
+ hooks = []
26
+
27
+ env = Ruby::Signature::Environment.new
28
+
29
+ loader = Ruby::Signature::EnvironmentLoader.new
30
+ OptionParser.new do |opts|
31
+ opts.on("-r [LIB]") do |name| loader.add(library: name) end
32
+ opts.on("-I [DIR]") do |dir| loader.add(path: Pathname(dir)) end
33
+ end.parse!(opts)
34
+ loader.load(env: env)
35
+
36
+ def match(filter, name)
37
+ if filter.end_with?("*")
38
+ name.start_with?(filter[0, filter.size - 1]) || name == filter[0, filter.size-3]
39
+ else
40
+ filter == name
41
+ end
42
+ end
43
+
44
+ TracePoint.trace :end do |tp|
45
+ class_name = tp.self.name
46
+
47
+ if class_name
48
+ if filter.any? {|f| match(f, class_name) } && skips.none? {|f| match(f, class_name) }
49
+ type_name = Ruby::Signature::Namespace.parse(class_name).absolute!.to_type_name
50
+ if hooks.none? {|hook| hook.klass == tp.self }
51
+ if env.find_class(type_name)
52
+ logger.info "Setting up hooks for #{class_name}"
53
+ hooks << Ruby::Signature::Test::Hook.install(env, tp.self, logger: logger).verify_all.raise_on_error!(raise_on_error)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,324 @@
1
+ module Ruby
2
+ module Signature
3
+ module Test
4
+ module Spy
5
+ def self.singleton_method(object, method_name)
6
+ spy = SingletonSpy.new(object: object, method_name: method_name)
7
+
8
+ if block_given?
9
+ begin
10
+ spy.setup
11
+ yield spy
12
+ ensure
13
+ spy.reset
14
+ end
15
+ else
16
+ spy
17
+ end
18
+ end
19
+
20
+ def self.instance_method(mod, method_name)
21
+ spy = InstanceSpy.new(mod: mod, method_name: method_name)
22
+
23
+ if block_given?
24
+ begin
25
+ spy.setup
26
+ yield spy
27
+ ensure
28
+ spy.reset
29
+ end
30
+ else
31
+ spy
32
+ end
33
+ end
34
+
35
+ def self.wrap(object, method_name)
36
+ spy = WrapSpy.new(object: object, method_name: method_name)
37
+
38
+ if block_given?
39
+ begin
40
+ yield spy, spy.wrapped_object
41
+ end
42
+ else
43
+ spy
44
+ end
45
+ end
46
+
47
+ class SingletonSpy
48
+ attr_accessor :callback
49
+ attr_reader :method_name
50
+ attr_reader :object
51
+
52
+ def initialize(object:, method_name:)
53
+ @object = object
54
+ @method_name = method_name
55
+ @callback = -> (_) { }
56
+ end
57
+
58
+ def setup
59
+ spy = self
60
+
61
+ object.singleton_class.class_eval do
62
+ define_method spy.method_name, spy.spy()
63
+ end
64
+ end
65
+
66
+ def spy()
67
+ spy = self
68
+
69
+ -> (*args, &block) do
70
+ return_value = nil
71
+ exception = nil
72
+ block_calls = []
73
+
74
+ spy_block = if block
75
+ Object.new.instance_eval do |fresh|
76
+ -> (*block_args) do
77
+ block_exn = nil
78
+ block_return = nil
79
+
80
+ begin
81
+ block_return = if self.equal?(fresh)
82
+ # no instance eval
83
+ block.call(*block_args)
84
+ else
85
+ self.instance_exec(*block_args, &block)
86
+ end
87
+ rescue Exception => exn
88
+ block_exn = exn
89
+ end
90
+
91
+ block_calls << ArgumentsReturn.new(
92
+ arguments: block_args,
93
+ return_value: block_return,
94
+ exception: block_exn
95
+ )
96
+
97
+ if block_exn
98
+ raise block_exn
99
+ else
100
+ block_return
101
+ end
102
+ end.ruby2_keywords
103
+ end
104
+ end
105
+
106
+ begin
107
+ return_value = super(*args, &spy_block)
108
+ rescue Exception => exn
109
+ exception = exn
110
+ end
111
+
112
+ trace = CallTrace.new(
113
+ method_name: spy.method_name,
114
+ method_call: ArgumentsReturn.new(
115
+ arguments: args,
116
+ return_value: return_value,
117
+ exception: exception,
118
+ ),
119
+ block_calls: block_calls,
120
+ block_given: block != nil
121
+ )
122
+
123
+ spy.callback.call(trace)
124
+
125
+ if exception
126
+ raise exception
127
+ else
128
+ return_value
129
+ end
130
+ end.ruby2_keywords
131
+ end
132
+
133
+ def reset
134
+ if object.singleton_class.methods.include?(method_name)
135
+ object.singleton_class.remove_method method_name
136
+ end
137
+ end
138
+ end
139
+
140
+ class InstanceSpy
141
+ attr_accessor :callback
142
+ attr_reader :mod
143
+ attr_reader :method_name
144
+ attr_reader :original_method
145
+
146
+ def initialize(mod:, method_name:)
147
+ @mod = mod
148
+ @method_name = method_name
149
+ @original_method = mod.instance_method(method_name)
150
+ @callback = -> (_) { }
151
+ end
152
+
153
+ def setup
154
+ spy = self
155
+
156
+ mod.class_eval do
157
+ define_method spy.method_name, spy.spy()
158
+ end
159
+ end
160
+
161
+ def reset
162
+ spy = self
163
+
164
+ mod.class_eval do
165
+ define_method spy.method_name, spy.original_method
166
+ end
167
+ end
168
+
169
+ def spy
170
+ spy = self
171
+
172
+ -> (*args, &block) do
173
+ return_value = nil
174
+ exception = nil
175
+ block_calls = []
176
+
177
+ spy_block = if block
178
+ Object.new.instance_eval do |fresh|
179
+ -> (*block_args) do
180
+ block_exn = nil
181
+ block_return = nil
182
+
183
+ begin
184
+ block_return = if self.equal?(fresh)
185
+ # no instance eval
186
+ block.call(*block_args)
187
+ else
188
+ self.instance_exec(*block_args, &block)
189
+ end
190
+ rescue Exception => exn
191
+ block_exn = exn
192
+ end
193
+
194
+ block_calls << ArgumentsReturn.new(
195
+ arguments: block_args,
196
+ return_value: block_return,
197
+ exception: block_exn
198
+ )
199
+
200
+ if block_exn
201
+ raise block_exn
202
+ else
203
+ block_return
204
+ end
205
+ end.ruby2_keywords
206
+ end
207
+ end
208
+
209
+ begin
210
+ return_value = spy.original_method.bind_call(self, *args, &spy_block)
211
+ rescue Exception => exn
212
+ exception = exn
213
+ end
214
+
215
+ trace = CallTrace.new(
216
+ method_name: spy.method_name,
217
+ method_call: ArgumentsReturn.new(
218
+ arguments: args,
219
+ return_value: return_value,
220
+ exception: exception,
221
+ ),
222
+ block_calls: block_calls,
223
+ block_given: block != nil
224
+ )
225
+
226
+ spy.callback.call(trace)
227
+
228
+ if exception
229
+ raise exception
230
+ else
231
+ return_value
232
+ end
233
+ end.ruby2_keywords
234
+ end
235
+ end
236
+
237
+ class WrapSpy
238
+ attr_accessor :callback
239
+ attr_reader :object
240
+ attr_reader :method_name
241
+
242
+ def initialize(object:, method_name:)
243
+ @callback = -> (_) { }
244
+ @object = object
245
+ @method_name = method_name
246
+ end
247
+
248
+ def wrapped_object
249
+ spy = self
250
+
251
+ Class.new(BasicObject) do
252
+ define_method(:method_missing) do |name, *args, &block|
253
+ spy.object.__send__(name, *args, &block)
254
+ end
255
+
256
+ define_method(spy.method_name, -> (*args, &block) {
257
+ return_value = nil
258
+ exception = nil
259
+ block_calls = []
260
+
261
+ spy_block = if block
262
+ Object.new.instance_eval do |fresh|
263
+ -> (*block_args) do
264
+ block_exn = nil
265
+ block_return = nil
266
+
267
+ begin
268
+ block_return = if self.equal?(fresh)
269
+ # no instance eval
270
+ block.call(*block_args)
271
+ else
272
+ self.instance_exec(*block_args, &block)
273
+ end
274
+ rescue Exception => exn
275
+ block_exn = exn
276
+ end
277
+
278
+ block_calls << ArgumentsReturn.new(
279
+ arguments: block_args,
280
+ return_value: block_return,
281
+ exception: block_exn
282
+ )
283
+
284
+ if block_exn
285
+ raise block_exn
286
+ else
287
+ block_return
288
+ end
289
+ end.ruby2_keywords
290
+ end
291
+ end
292
+
293
+ begin
294
+ return_value = spy.object.__send__(spy.method_name, *args, &spy_block)
295
+ rescue ::Exception => exn
296
+ exception = exn
297
+ end
298
+
299
+ trace = CallTrace.new(
300
+ method_name: spy.method_name,
301
+ method_call: ArgumentsReturn.new(
302
+ arguments: args,
303
+ return_value: return_value,
304
+ exception: exception,
305
+ ),
306
+ block_calls: block_calls,
307
+ block_given: block != nil
308
+ )
309
+
310
+ spy.callback.call(trace)
311
+
312
+ if exception
313
+ spy.object.__send__(:raise, exception)
314
+ else
315
+ return_value
316
+ end
317
+ }.ruby2_keywords)
318
+ end.new()
319
+ end
320
+ end
321
+ end
322
+ end
323
+ end
324
+ end