typeprof 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/doc/demo.md +398 -0
  4. data/doc/doc.ja.md +4 -0
  5. data/doc/doc.md +4 -0
  6. data/lib/typeprof.rb +8 -0
  7. data/lib/typeprof/analyzer.rb +229 -245
  8. data/lib/typeprof/arguments.rb +397 -0
  9. data/lib/typeprof/block.rb +133 -0
  10. data/lib/typeprof/builtin.rb +14 -10
  11. data/lib/typeprof/container-type.rb +94 -17
  12. data/lib/typeprof/export.rb +185 -108
  13. data/lib/typeprof/import.rb +76 -54
  14. data/lib/typeprof/iseq.rb +27 -2
  15. data/lib/typeprof/method.rb +87 -73
  16. data/lib/typeprof/type.rb +125 -309
  17. data/lib/typeprof/version.rb +1 -1
  18. data/smoke/arguments2.rb +1 -1
  19. data/smoke/array-each3.rb +1 -4
  20. data/smoke/array12.rb +1 -1
  21. data/smoke/array6.rb +1 -0
  22. data/smoke/block-ambiguous.rb +36 -0
  23. data/smoke/block-args1-rest.rb +62 -0
  24. data/smoke/block-args1.rb +59 -0
  25. data/smoke/block-args2-rest.rb +62 -0
  26. data/smoke/block-args2.rb +59 -0
  27. data/smoke/block-args3-rest.rb +73 -0
  28. data/smoke/block-args3.rb +70 -0
  29. data/smoke/block-blockarg.rb +27 -0
  30. data/smoke/block-kwarg.rb +52 -0
  31. data/smoke/block11.rb +1 -1
  32. data/smoke/block14.rb +17 -0
  33. data/smoke/block4.rb +2 -2
  34. data/smoke/block5.rb +1 -0
  35. data/smoke/block6.rb +1 -1
  36. data/smoke/block7.rb +0 -2
  37. data/smoke/block8.rb +2 -2
  38. data/smoke/block9.rb +1 -1
  39. data/smoke/blown.rb +1 -1
  40. data/smoke/class-hierarchy.rb +54 -0
  41. data/smoke/class-hierarchy2.rb +27 -0
  42. data/smoke/constant1.rb +11 -6
  43. data/smoke/constant2.rb +2 -0
  44. data/smoke/cvar.rb +1 -0
  45. data/smoke/demo10.rb +1 -1
  46. data/smoke/demo8.rb +2 -2
  47. data/smoke/demo9.rb +1 -3
  48. data/smoke/flow7.rb +1 -7
  49. data/smoke/flow8.rb +13 -0
  50. data/smoke/instance_eval.rb +1 -1
  51. data/smoke/int_times.rb +1 -1
  52. data/smoke/multiple-superclass.rb +4 -0
  53. data/smoke/next2.rb +1 -1
  54. data/smoke/optional3.rb +10 -0
  55. data/smoke/proc4.rb +1 -1
  56. data/smoke/rbs-proc1.rb +9 -0
  57. data/smoke/rbs-proc1.rbs +3 -0
  58. data/smoke/rbs-proc2.rb +20 -0
  59. data/smoke/rbs-proc2.rbs +3 -0
  60. data/smoke/rbs-proc3.rb +13 -0
  61. data/smoke/rbs-proc3.rbs +4 -0
  62. data/smoke/rbs-record.rb +17 -0
  63. data/smoke/rbs-record.rbs +4 -0
  64. data/smoke/rbs-tyvar3.rb +25 -0
  65. data/smoke/rbs-tyvar3.rbs +4 -0
  66. data/smoke/rest2.rb +1 -1
  67. data/smoke/rest5.rb +1 -1
  68. data/smoke/return.rb +1 -1
  69. data/smoke/singleton_method.rb +3 -0
  70. data/smoke/struct.rb +4 -3
  71. data/smoke/struct3.rb +14 -0
  72. data/smoke/symbol-proc.rb +24 -0
  73. metadata +31 -3
  74. data/smoke/variadic1.rb.notyet +0 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3a0f0b09ed34d40d00fc280008ae562863d6cb3e19bd0a421e1fc5ac5023a5ea
4
- data.tar.gz: dd7ee377c5dee6d2ad7bbd274844a5136c59f7fab5f7491cde11fcac628d44c2
3
+ metadata.gz: f38e8b2980db487be260e981e6748eb5ee60ac7dfdc3cc563f0e4bc502629042
4
+ data.tar.gz: 1439f8a9ac7b002f41580b8ad23a84f5802e31c3f5e3596209ebf7b9b94f5639
5
5
  SHA512:
6
- metadata.gz: 66e931b5f52fb1436707623ad1fba698bed0cf6e922971fbd0b5c99e28e4c14f5737e5f92406a457c5b96205ef8ad58f31aa739465f3aa57ca8149695c1bda42
7
- data.tar.gz: 679c4b14a7bad664ba17f2aa27aa999a27584d7b9fdbe40bbb82fc4ea836ef867621a34077de5931c6c26fc9e9c9a2908bb4901914fa587f1b84cdd75dcde0ea
6
+ metadata.gz: d0428fc5aeaf0fc039bc5c2a3d803eac5c7cbfa7f9f5d3fff0e10bc0784ae34dd6f36ce6283ecc2290240cd9de2690f11fe42ab2dcc8bbc48e22b078ed42b898
7
+ data.tar.gz: ef2f1fd77ec22853191dc17e6379d2c3434d6b2d2460032c6e2f6e28cd8b9e70b8ddf939afd346f0e9238c810fbbe82df7e10f6e55598dc3c4b6140f54d8ee0d
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- typeprof (0.3.0)
4
+ typeprof (0.4.0)
5
5
  rbs (>= 0.13.1)
6
6
 
7
7
  GEM
@@ -0,0 +1,398 @@
1
+ # TypeProf demo cases
2
+
3
+ ## A simple demo with a "User" class
4
+
5
+ ```ruby
6
+ def hello_message(user)
7
+ "The name is " + user.name
8
+ end
9
+
10
+ def type_error_demo(user)
11
+ "The age is " + user.age
12
+ end
13
+
14
+ user = User.new(name: "John", age: 20)
15
+
16
+ hello_message(user)
17
+ type_error_demo(user)
18
+ ```
19
+
20
+ ```ruby
21
+ class User
22
+ attr_reader name: String
23
+ attr_reader age: Integer
24
+
25
+ def initialize: (name: String, age: Integer) -> void
26
+ end
27
+ ```
28
+
29
+ Result:
30
+ ```
31
+ $ typeprof test.rb test.rbs
32
+ # Errors
33
+ test.rb:6: [error] failed to resolve overload: String#+
34
+
35
+ # Classes
36
+ class Object
37
+ def hello_message : (User) -> String
38
+ def type_error_demo : (User) -> untyped
39
+ end
40
+ ```
41
+
42
+ Yoy can [try this analysis online](https://mame.github.io/typeprof-playground/#rb=def+hello_message%28user%29%0A++%22The+name+is+%22+%2B+user.name%0Aend%0A%0Adef+type_error_demo%28user%29%0A++%22The+age+is+%22+%2B+user.age%0Aend%0A%0Auser+%3D+User.new%28name%3A+%22John%22%2C+age%3A+20%29%0A%0Ahello_message%28user%29%0Atype_error_demo%28user%29&rbs=class+User%0A++attr_reader+name%3A+String%0A++attr_reader+age%3A+Integer%0A%0A++def+initialize%3A+%28name%3A+String%2C+age%3A+Integer%29+-%3E+void%0Aend).
43
+
44
+ ## A simple demo to generate the signature prototype of "User" class
45
+
46
+ ```ruby
47
+ class User
48
+ def initialize(name:, age:)
49
+ @name, @age = name, age
50
+ end
51
+ attr_reader :name, :age
52
+ end
53
+
54
+ # A test case to tell TypeProf what types are expected by the class and methods
55
+ User.new(name: "John", age: 20)
56
+ ```
57
+
58
+ Result:
59
+ ```
60
+ $ typeprof -v test.rb
61
+ # Classes
62
+ class User
63
+ attr_reader name : String
64
+ attr_reader age : Integer
65
+ def initialize : (name: String, age: Integer) -> [String, Integer]
66
+ end
67
+ ```
68
+
69
+ ## Type inspection by `p` (`Kernel#p`)
70
+
71
+ ```ruby
72
+ p 42 #=> Integer
73
+ p "str" #=> String
74
+ p "str".chars #=> Array[String]
75
+ ```
76
+
77
+ Result:
78
+ ```
79
+ $ typeprof test.rb
80
+ # Revealed types
81
+ # test.rb:1 #=> Integer
82
+ # test.rb:2 #=> String
83
+ # test.rb:3 #=> Array[String]
84
+ ```
85
+
86
+ ## Block with builtin methods
87
+
88
+ ```ruby
89
+ # TypeProf runs this block only once
90
+ 10000000000000.times do |n|
91
+ p n #=> Integer
92
+ end
93
+
94
+ # "each" with Heterogeneous array yields a union type
95
+ [1, 1.0, "str"].each do |e|
96
+ p e #=> Float | Integer | String
97
+ end
98
+
99
+ # You can use the idiom `&:method_name` too
100
+ p [1, 1.0, "str"].map(&:to_s) #=> Array[String]
101
+ ```
102
+
103
+ ## User-defined blocks
104
+
105
+ ```ruby
106
+ def foo(n)
107
+ yield n.to_s
108
+ end
109
+
110
+ foo(42) do |n|
111
+ p n #=> String
112
+ nil
113
+ end
114
+ ```
115
+
116
+ Result:
117
+ ```
118
+ $ typeprof test.rb
119
+ # Revealed types
120
+ # test.rb:6 #=> String
121
+
122
+ # Classes
123
+ class Object
124
+ def foo : (Integer) { (String) -> nil } -> nil
125
+ end
126
+ ```
127
+
128
+ ## Arrays
129
+
130
+ ```ruby
131
+ # A fixed-length array literal generates a "tuple" array
132
+ ary = [1, 1.0]
133
+
134
+ # A tuple array keeps its length, and the association between indexes and elements
135
+ p ary #=> [Integer, Float]
136
+ p ary[0] #=> Integer
137
+ p ary[1] #=> Float
138
+
139
+ # Destructive operation is well handled (in method-local analysis)
140
+ ary[0] = "str"
141
+ p ary #=> [String, Float]
142
+
143
+ # An calculated array generates a "sequence" array
144
+ ary = [1] + [1.0]
145
+
146
+ # A sequence array does not keep length nor association
147
+ p ary #=> Array[Float | Integer]
148
+ p ary[0] #=> Float | Integer
149
+
150
+ # Destructive operation is still handled (but "weak update" is applied)
151
+ ary[0] = "str"
152
+ p ary #=> Array[Float | Integer | String]
153
+ ```
154
+
155
+ ## Multiple return values by using a tuple array
156
+
157
+ ```ruby
158
+ def foo
159
+ return 42, "str"
160
+ end
161
+
162
+ int, str = foo
163
+ p int #=> Integer
164
+ p str #=> String
165
+ ```
166
+
167
+ ## Delegation by using a tuple array
168
+
169
+ ```ruby
170
+ def foo(x, y, z)
171
+ end
172
+
173
+ def proxy(dummy, *args)
174
+ foo(*args)
175
+ end
176
+
177
+ proxy(:dummy, 1, 1.0, "str")
178
+ ```
179
+
180
+ ## Symbols
181
+
182
+ ```ruby
183
+ # Symbols are handled as concrete values instead of abstract ones
184
+ p [:a, :b, :c] #=> [:a, :b, :c]
185
+ ```
186
+
187
+ ## Hashes
188
+
189
+ ```ruby
190
+ # A Hash is a "type-to-type" map
191
+ h = { "int" => 1, "float" => 1.0 }
192
+ p h #=> {String=>Float | Integer}
193
+ p h["int"] #=> Float | Intger
194
+
195
+ # Symbol-key hashes (a.k.a. records) can have distinct types for each key as Symbols are concrete
196
+ h = { int: 1, float: 1.0 }
197
+
198
+ p h #=> {:int=>Integer, :float=>Float}
199
+ p h[:int] #=> Integer
200
+ p h[:float] #=> Float
201
+
202
+ # Symbol-key hash can be appropriately passed to a keyword method
203
+ def foo(int:, float:)
204
+ p [int, float] #=> [Integer, Float]
205
+ end
206
+
207
+ foo(**h)
208
+ ```
209
+
210
+ ## Structs
211
+
212
+ ```ruby
213
+ FooBar = Struct.new(:foo, :bar)
214
+
215
+ obj = FooBar.new(42)
216
+ obj.foo = :dummy
217
+ obj.bar = "str"
218
+ ```
219
+
220
+ Result:
221
+ ```
222
+ $ typeprof test.rb
223
+ # Classes
224
+ class FooBar < Struct
225
+ attr_accessor foo() : :dummy | Integer
226
+ attr_accessor bar() : String?
227
+ end
228
+ ```
229
+
230
+ ## Exceptions
231
+
232
+ ```ruby
233
+ # TypeProf assumes that any exception may be raised anywhere
234
+ def foo
235
+ x = 1
236
+ x = "str"
237
+ x = :sym
238
+ ensure
239
+ p(x) #=> :sym | Integer | String
240
+ end
241
+ ```
242
+
243
+ Result:
244
+ ```
245
+ $ typeprof test.rb
246
+ # Revealed types
247
+ # test.rb:6 #=> :sym | Integer | String
248
+
249
+ # Classes
250
+ class Object
251
+ def foo : -> :sym
252
+ end
253
+ ```
254
+
255
+ ## RBS overloaded methods
256
+
257
+ ```ruby
258
+ # TypeProf selects all overloaded method declarations that matches actual arguments
259
+ p foo(42) #=> Integer
260
+ p foo("str") #=> String
261
+ p foo(1.0) #=> failed to resolve overload: Object#foo
262
+ ```
263
+
264
+ ```
265
+ class Object
266
+ def foo: (Integer) -> Integer
267
+ | (String) -> String
268
+ end
269
+ ```
270
+
271
+ ## Flow-sensitive analysis demo: case/when with class constants
272
+
273
+ ```ruby
274
+ def foo(n)
275
+ case n
276
+ when Integer
277
+ p n #=> Integer
278
+ when String
279
+ p n #=> String
280
+ else
281
+ p n #=> Float
282
+ end
283
+ end
284
+
285
+ foo(42)
286
+ foo(1.0)
287
+ foo("str")
288
+ ```
289
+
290
+ Result:
291
+ ```
292
+ $ typeprof test.rb
293
+ # Revealed types
294
+ # test.rb:4 #=> Integer
295
+ # test.rb:8 #=> Float
296
+ # test.rb:6 #=> String
297
+
298
+ # Classes
299
+ class Object
300
+ def foo : (Float | Integer | String) -> (Float | Integer | String)
301
+ end
302
+ ```
303
+
304
+ ## Flow-sensitive analysis demo: `is_a?` and `respond_to?`
305
+
306
+ ```ruby
307
+ def foo(n)
308
+ if n.is_a?(Integer)
309
+ p n #=> Integer
310
+ else
311
+ p n #=> Float | String
312
+ end
313
+
314
+ if n.respond_to?(:times)
315
+ p n #=> Integer
316
+ else
317
+ p n #=> Float | String
318
+ end
319
+ end
320
+
321
+ foo(42)
322
+ foo(1.0)
323
+ foo("str")
324
+ ```
325
+
326
+ ## Flow-sensitive analysis demo: `x || y`
327
+
328
+ ```ruby
329
+ # ENV["FOO"] returns String? (which means String | nil)
330
+ p ENV["FOO"] #=> String?
331
+
332
+ # Using "|| (default value)" can force it to be non-nil
333
+ p ENV["FOO"] || "default" #=> String
334
+ ```
335
+
336
+ ## Recursion
337
+
338
+ ```ruby
339
+ def fib(x)
340
+ if x <= 1
341
+ x
342
+ else
343
+ fib(x - 1) + fib(x - 2)
344
+ end
345
+ end
346
+
347
+ fib(40000)
348
+ ```
349
+
350
+ Result:
351
+ ```
352
+ $ typeprof test.rb
353
+ # Classes
354
+ class Object
355
+ def fib : (Integer) -> Integer
356
+ end
357
+ ```
358
+
359
+ ## "Stub-execution" that invokes methods without tests
360
+
361
+ ```ruby
362
+ def foo(n)
363
+ # bar is invoked with Integer arguments
364
+ bar(42)
365
+ n
366
+ end
367
+
368
+ def bar(n)
369
+ n
370
+ end
371
+
372
+ # As there is no test code to call methods foo and bar,
373
+ # TypeProf tries to invoke them with "untyped" arguments
374
+ ```
375
+
376
+ Result:
377
+ ```
378
+ $ typeprof test.rb
379
+ # Classes
380
+ class Object
381
+ def foo : (untyped) -> untyped
382
+ def bar : (Integer) -> Integer
383
+ end
384
+ ```
385
+
386
+ ## Library demo
387
+
388
+ ```ruby
389
+ require "pathname"
390
+
391
+ p Pathname("foo") #=> Pathname
392
+ p Pathname("foo").dirname #=> Pathname
393
+ p Pathname("foo").ctime #=> Time
394
+ ```
395
+
396
+ ## More
397
+
398
+ See ruby/typeprof's [smoke](https://github.com/ruby/typeprof/tree/master/smoke) directory.
@@ -1,5 +1,9 @@
1
1
  # TypeProf: 抽象解釈に基づくRubyコードの型解析ツール
2
2
 
3
+ ## とりあえずデモ
4
+
5
+ [demo.md](demo.md) を参照。
6
+
3
7
  ## TypeProfの使い方
4
8
 
5
9
  app.rb を解析する。
data/doc/doc.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # TypeProf: A type analysis tool for Ruby code based on abstract interpretation
2
2
 
3
+ ## Show me demo first
4
+
5
+ See [demo.md](demo.md).
6
+
3
7
  ## How to use TypeProf
4
8
 
5
9
  Analyze app.rb:
@@ -1,12 +1,20 @@
1
+ unless defined?(RubyVM::InstructionSequence)
2
+ puts "Currently, TypeProf can work on a Ruby implementation that supports RubyVM::InstructionSequence, such as CRuby."
3
+ exit 1
4
+ end
5
+
1
6
  module TypeProf end
2
7
 
8
+ require_relative "typeprof/version"
3
9
  require_relative "typeprof/config"
4
10
  require_relative "typeprof/insns-def"
5
11
  require_relative "typeprof/utils"
6
12
  require_relative "typeprof/type"
7
13
  require_relative "typeprof/container-type"
8
14
  require_relative "typeprof/method"
15
+ require_relative "typeprof/block"
9
16
  require_relative "typeprof/iseq"
17
+ require_relative "typeprof/arguments"
10
18
  require_relative "typeprof/analyzer"
11
19
  require_relative "typeprof/import"
12
20
  require_relative "typeprof/export"