typeprof 0.9.0 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +1 -1
- data/Gemfile.lock +6 -5
- data/doc/demo.md +2 -2
- data/doc/todo.md +133 -0
- data/lib/typeprof/analyzer.rb +89 -37
- data/lib/typeprof/block.rb +34 -0
- data/lib/typeprof/builtin.rb +169 -66
- data/lib/typeprof/cli.rb +7 -0
- data/lib/typeprof/config.rb +25 -3
- data/lib/typeprof/container-type.rb +24 -0
- data/lib/typeprof/export.rb +130 -69
- data/lib/typeprof/import.rb +82 -38
- data/lib/typeprof/iseq.rb +23 -4
- data/lib/typeprof/method.rb +29 -7
- data/lib/typeprof/type.rb +75 -13
- data/lib/typeprof/version.rb +1 -1
- data/smoke/alias.rb +4 -4
- data/smoke/alias2.rb +3 -1
- data/smoke/arguments.rb +2 -2
- data/smoke/arguments2.rb +5 -5
- data/smoke/array-each.rb +1 -1
- data/smoke/array-each3.rb +1 -1
- data/smoke/array-map.rb +1 -1
- data/smoke/array-map2.rb +1 -1
- data/smoke/array-map3.rb +3 -3
- data/smoke/array-mul.rb +2 -2
- data/smoke/array-plus1.rb +1 -1
- data/smoke/array-plus2.rb +1 -0
- data/smoke/array-range-aref.rb +11 -11
- data/smoke/array-replace.rb +1 -1
- data/smoke/array1.rb +5 -5
- data/smoke/array10.rb +1 -1
- data/smoke/array11.rb +1 -1
- data/smoke/array12.rb +1 -1
- data/smoke/array14.rb +1 -1
- data/smoke/array15.rb +1 -1
- data/smoke/array2.rb +2 -2
- data/smoke/array3.rb +1 -0
- data/smoke/array6.rb +2 -1
- data/smoke/array8.rb +1 -1
- data/smoke/array9.rb +1 -1
- data/smoke/attr-module.rb +1 -0
- data/smoke/attr-vis.rb +43 -0
- data/smoke/attr-vis.rbs +4 -0
- data/smoke/attr.rb +2 -2
- data/smoke/block-ambiguous.rb +4 -4
- data/smoke/block-args1-rest.rb +6 -5
- data/smoke/block-args1.rb +5 -5
- data/smoke/block-args2-rest.rb +6 -5
- data/smoke/block-args2.rb +5 -5
- data/smoke/block-args3-rest.rb +7 -6
- data/smoke/block-args3.rb +6 -6
- data/smoke/block-blockarg.rb +3 -3
- data/smoke/block-kwarg.rb +4 -4
- data/smoke/block1.rb +1 -1
- data/smoke/block10.rb +1 -1
- data/smoke/block11.rb +2 -2
- data/smoke/block2.rb +1 -1
- data/smoke/block3.rb +1 -1
- data/smoke/block5.rb +1 -0
- data/smoke/block_given.rb +37 -0
- data/smoke/class_method.rb +2 -2
- data/smoke/class_method2.rb +2 -2
- data/smoke/constant2.rb +3 -2
- data/smoke/context-sensitive1.rb +1 -1
- data/smoke/cvar.rb +3 -2
- data/smoke/define_method.rb +2 -2
- data/smoke/define_method3.rb +1 -0
- data/smoke/define_method4.rb +1 -1
- data/smoke/define_method6.rb +19 -0
- data/smoke/demo.rb +6 -6
- data/smoke/demo1.rb +1 -1
- data/smoke/demo11.rb +1 -1
- data/smoke/demo2.rb +1 -1
- data/smoke/demo3.rb +1 -1
- data/smoke/demo4.rb +3 -3
- data/smoke/demo5.rb +1 -1
- data/smoke/demo6.rb +2 -1
- data/smoke/demo7.rb +1 -1
- data/smoke/demo9.rb +1 -0
- data/smoke/dummy-execution1.rb +1 -1
- data/smoke/dummy-execution2.rb +1 -1
- data/smoke/dummy_element.rb +1 -1
- data/smoke/ensure1.rb +1 -1
- data/smoke/enum_for.rb +15 -0
- data/smoke/enum_for2.rb +17 -0
- data/smoke/fib.rb +2 -2
- data/smoke/flow1.rb +1 -1
- data/smoke/flow10.rb +17 -0
- data/smoke/flow2.rb +1 -1
- data/smoke/flow3.rb +1 -1
- data/smoke/flow5.rb +1 -1
- data/smoke/flow6.rb +1 -1
- data/smoke/flow7.rb +1 -1
- data/smoke/flow8.rb +1 -1
- data/smoke/flow9.rb +1 -1
- data/smoke/function.rb +1 -1
- data/smoke/gvar.rb +1 -1
- data/smoke/gvar2.rb +1 -1
- data/smoke/hash-fetch.rb +3 -3
- data/smoke/inheritance.rb +4 -4
- data/smoke/initialize.rb +3 -2
- data/smoke/instance_eval.rb +1 -1
- data/smoke/int_times.rb +1 -1
- data/smoke/integer.rb +1 -1
- data/smoke/ivar.rb +3 -2
- data/smoke/ivar2.rb +2 -2
- data/smoke/ivar3.rb +2 -1
- data/smoke/ivar4.rb +21 -0
- data/smoke/kernel-class.rb +1 -1
- data/smoke/keyword4.rb +1 -1
- data/smoke/kwrest.rb +1 -0
- data/smoke/kwsplat1.rb +2 -2
- data/smoke/kwsplat2.rb +1 -1
- data/smoke/manual-rbs.rb +2 -1
- data/smoke/manual-rbs3.rb +1 -0
- data/smoke/method_in_branch.rb +1 -1
- data/smoke/method_missing.rb +4 -3
- data/smoke/module3.rb +1 -1
- data/smoke/module4.rb +1 -0
- data/smoke/module5.rb +1 -1
- data/smoke/module_function1.rb +3 -2
- data/smoke/module_function2.rb +3 -2
- data/smoke/multiple-include.rb +1 -0
- data/smoke/next1.rb +1 -1
- data/smoke/object-send1.rb +3 -3
- data/smoke/optional1.rb +1 -1
- data/smoke/optional2.rb +1 -1
- data/smoke/optional3.rb +1 -1
- data/smoke/parameterizedd-self.rb +2 -1
- data/smoke/prepend1.rb +33 -0
- data/smoke/prepend2.rb +10 -0
- data/smoke/prepend2.rbs +9 -0
- data/smoke/primitive_method.rb +19 -0
- data/smoke/proc4.rb +1 -1
- data/smoke/public.rb +4 -0
- data/smoke/range.rb +1 -1
- data/smoke/rbs-attr.rb +2 -2
- data/smoke/rbs-proc2.rb +1 -1
- data/smoke/rbs-proc3.rb +1 -1
- data/smoke/rbs-tyvar4.rb +3 -2
- data/smoke/rbs-tyvar6.rb +3 -3
- data/smoke/redo1.rb +1 -1
- data/smoke/redo2.rb +1 -1
- data/smoke/rescue1.rb +1 -1
- data/smoke/rescue2.rb +1 -1
- data/smoke/rescue3.rb +1 -0
- data/smoke/rescue4.rb +1 -1
- data/smoke/respond_to.rb +1 -1
- data/smoke/rest1.rb +2 -2
- data/smoke/rest2.rb +1 -1
- data/smoke/rest3.rb +6 -6
- data/smoke/rest4.rb +2 -2
- data/smoke/rest5.rb +1 -1
- data/smoke/rest6.rb +1 -1
- data/smoke/retry1.rb +2 -2
- data/smoke/simple.rb +12 -0
- data/smoke/step.rb +3 -3
- data/smoke/struct-keyword_init.rb +6 -16
- data/smoke/struct.rb +1 -1
- data/smoke/struct2.rb +1 -1
- data/smoke/struct3.rb +1 -1
- data/smoke/struct4.rb +1 -1
- data/smoke/struct5.rb +2 -2
- data/smoke/struct6.rb +2 -2
- data/smoke/struct7.rb +1 -1
- data/smoke/super1.rb +4 -4
- data/smoke/super3.rb +3 -2
- data/smoke/super4.rb +7 -5
- data/smoke/super5.rb +6 -4
- data/smoke/symbol-proc-attr.rb +1 -1
- data/smoke/tap1.rb +2 -2
- data/smoke/toplevel.rb +1 -1
- data/smoke/type_var.rb +3 -3
- data/smoke/user-demo.rb +1 -1
- data/smoke/wrong-extend.rb +1 -0
- data/smoke/wrong-include.rb +1 -0
- data/smoke/wrong-include2.rb +1 -1
- data/testbed/goodcheck-Gemfile.lock +1 -1
- data/typeprof.gemspec +1 -1
- metadata +19 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 239b17dbe8d61c729346c5dde966f6abff491951c88bc33c57c4c34cc99dc497
|
4
|
+
data.tar.gz: 2696c83796a6af5c498cca59a3d98b7faccaf187fe60c0057ac3f052622009ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23a7a3313dadd2d0c63a5d83a3afd112893134a049861e6a9e11cf5097aaa78c19494326b6a07b44cf4d93f5dec5a2c893b91fe2c4f5571726229d1e8dc13229
|
7
|
+
data.tar.gz: 3273f1bb24290af32db23aaae8cafa84905d4d2d576b5ab96178130fe843b73e4365827b60d47a022a2812621f7fe09a3801c32c205c9805516873ed9ac88659
|
data/.github/workflows/main.yml
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
typeprof (0.
|
5
|
-
rbs (>= 0.
|
4
|
+
typeprof (0.12.0)
|
5
|
+
rbs (>= 1.0.0)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
10
|
coverage-helpers (1.0.0)
|
11
|
-
docile (1.3.
|
11
|
+
docile (1.3.4)
|
12
12
|
power_assert (1.2.0)
|
13
13
|
rake (13.0.1)
|
14
|
-
rbs (0.
|
14
|
+
rbs (1.0.3)
|
15
15
|
simplecov (0.20.0)
|
16
16
|
docile (~> 1.1)
|
17
17
|
simplecov-html (~> 0.11)
|
@@ -24,6 +24,7 @@ GEM
|
|
24
24
|
|
25
25
|
PLATFORMS
|
26
26
|
ruby
|
27
|
+
x86_64-linux
|
27
28
|
|
28
29
|
DEPENDENCIES
|
29
30
|
coverage-helpers
|
@@ -35,4 +36,4 @@ DEPENDENCIES
|
|
35
36
|
typeprof!
|
36
37
|
|
37
38
|
BUNDLED WITH
|
38
|
-
2.2.
|
39
|
+
2.2.3
|
data/doc/demo.md
CHANGED
@@ -39,7 +39,7 @@ class Object
|
|
39
39
|
end
|
40
40
|
```
|
41
41
|
|
42
|
-
|
42
|
+
You 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
43
|
|
44
44
|
## A simple demo to generate the signature prototype of "User" class
|
45
45
|
|
@@ -190,7 +190,7 @@ p [:a, :b, :c] #=> [:a, :b, :c]
|
|
190
190
|
# A Hash is a "type-to-type" map
|
191
191
|
h = { "int" => 1, "float" => 1.0 }
|
192
192
|
p h #=> {String=>Float | Integer}
|
193
|
-
p h["int"] #=> Float |
|
193
|
+
p h["int"] #=> Float | Integer
|
194
194
|
|
195
195
|
# Symbol-key hashes (a.k.a. records) can have distinct types for each key as Symbols are concrete
|
196
196
|
h = { int: 1, float: 1.0 }
|
data/doc/todo.md
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
# TypeProf milestones and TODOs
|
2
|
+
|
3
|
+
## Big milestones
|
4
|
+
|
5
|
+
### Rails support
|
6
|
+
|
7
|
+
There are many known issues for the analysis of typical Ruby programs including Rails apps.
|
8
|
+
|
9
|
+
* The main difficulty is that they use many language extensions like `ActiveSupport`.
|
10
|
+
Some features (for example, `blank?` and `Time.now + 1.day`) are trivial to support,
|
11
|
+
but others (for example, `ActiveSupport::Concern` and Zeitwerk) will require special support.
|
12
|
+
* The other difficulty is that they heavily use meta-programming features like `ActiveRecord`.
|
13
|
+
It dynamically defines some methods based on external data (such as DB schema) from the code.
|
14
|
+
|
15
|
+
Currently, projects called [`gem_rbs`](https://github.com/ruby/gem_rbs) and [`rbs_rails`](https://github.com/pocke/rbs_rails) are in progress.
|
16
|
+
The former provides several RBS files for some major gems including Rails.
|
17
|
+
The latter is a tool to generate RBS prototype of a target Rails application by introspection (executing it and monitoring DB schema, etc).
|
18
|
+
TypeProf can use their results to improve analysis precision and performance.
|
19
|
+
|
20
|
+
What we need to do:
|
21
|
+
|
22
|
+
* Experimentally apply TypeProf to some Rails programs and identify problems
|
23
|
+
* Make TypeProf able to work together with `rbs_rails` for supporting trivial core extensions and `ActiveRecord`.
|
24
|
+
* Implement special support for some fundamental language extensions of Rails like `ActiveSupport::Concern`.
|
25
|
+
(It would be best if TypeProf has a plugin system and if we can factor out the special support as a plugin for Rails.)
|
26
|
+
|
27
|
+
### Error detection and diagnosis feature
|
28
|
+
|
29
|
+
At present, TypeProf focuses on generation of RBS prototype from no-type-annotated Ruby code.
|
30
|
+
However, it is possible for TypeProf to report possible errors found during the analysis.
|
31
|
+
In fact, an option `-v` experimentally shows possible errors found.
|
32
|
+
There are some reasons why it is disabled by default:
|
33
|
+
|
34
|
+
* (1) There are too many false positives.
|
35
|
+
* (2) Some kind of error reporting is not implemented yet.
|
36
|
+
* (3) Some reported errors are difficult for a user to understand.
|
37
|
+
|
38
|
+
For (1), we will research how we can avoid false positives to support typical Ruby coding patterns as much as possible.
|
39
|
+
The primary way is to improve the analysis precision, e.g., enhancing flow-sensitive analysis.
|
40
|
+
If the S/N ratio of an error type is too low, we need to consider to suppress the kind of reports.
|
41
|
+
Also, we may try allowing users to guide TypeProf to analyze their program well.
|
42
|
+
(The simplest way is to write inline type casts in the code, but we need to find more Ruby/RBS way.)
|
43
|
+
We may also explore a "TypeProf-friendly coding style" which TypeProf can analyze well.
|
44
|
+
(In principle, the plainer code is, the better TypeProf can analyze.)
|
45
|
+
|
46
|
+
For (2), currently, TypeProf checks the argument types of a call to a method whose type signature is declared in RBS.
|
47
|
+
However, it does not check the return type yet. Redefinition of constants should be warned too.
|
48
|
+
We will survey what errors and warnings TypeProf can print, and evaluate the S/N ratio of each report.
|
49
|
+
|
50
|
+
For (3), since TypeProf uses whole program analysis, an error may be reported at a very different place from its root bug.
|
51
|
+
Thus, if TypeProf shows a possible type error, a diagnosis feature is needed to answer why TypeProf thinks that the error may occur.
|
52
|
+
TypeProf has already implemented a very primitive diagnosis feature, `Kernel#p`, to check what type an expression has.
|
53
|
+
Another idea is to create a pseudo backtrace why TypeProf thought the possible type error may occur.
|
54
|
+
We should consider this feature with LSP support.
|
55
|
+
|
56
|
+
### Performance improvement
|
57
|
+
|
58
|
+
Currently, TypeProf is painfully slow. Even if a target application is small.
|
59
|
+
|
60
|
+
The main reason is that TypeProf analyzes not only the application code but also library code:
|
61
|
+
if an application requires `"foo"`, TypeProf actually loads `foo.rb` even from a gem,
|
62
|
+
and furthermore, if `foo.rb` requires `"bar"`, it loads `bar.rb` recursively.
|
63
|
+
|
64
|
+
RBS will help to stop this cascade;
|
65
|
+
when an application requires `"foo"`, TypeProf loads `sig/foo.rbs` instead of `foo.rb` if the `foo` gem contains both.
|
66
|
+
Such a RBS file is optional for TypeProf but required for Steep.
|
67
|
+
So, we think many gems will eventually equip their RBS declarations.
|
68
|
+
|
69
|
+
That being said, we should continue to improve the analysis performance of TypeProf. We have some ideas.
|
70
|
+
|
71
|
+
* Unfortunately, TypeProf often analyzes one method more than once when it accepts multiple types.
|
72
|
+
As TypeProf squashes the argument types to a union, this duplicated analysis is not necessarily needed.
|
73
|
+
But when TypeProf first analyzes a method, it is difficult to determine if the method will accept another type in further analysis.
|
74
|
+
So, we need good heuristics to guess whether a method accepts multiple types or not, and if so, delay its analysis.
|
75
|
+
* Currently, TypeProf executes the bytecode instructions step by step.
|
76
|
+
This requires creating an environment object after each instruction, which is very heavy.
|
77
|
+
Many environment creations can be omitted by executing each basic block instead of each instruction.
|
78
|
+
(Basic block execution will also make flow-sensitive analysis easier.)
|
79
|
+
* The slowest calculation in TypeProf is to create an instance of a Type class.
|
80
|
+
The creation uses memoization; TypeProf keeps all Type instances created so far, and reuses them if already exist.
|
81
|
+
However, it is very heavy to check if an instance already exists or not.
|
82
|
+
(Currently, it is very simply implemented by a big Hash table.)
|
83
|
+
We've already improved the memoization routine several times but looks like it is still the No.1 bottleneck.
|
84
|
+
We need to investigate and try improving more.
|
85
|
+
* TypeProf heavily uses Hash objects (including above) mainly to represent a set.
|
86
|
+
A union of sets is done by `Hash#merge`, which takes O(n).
|
87
|
+
A more lightweight data structure may make TypeProf faster.
|
88
|
+
(But clever structure often has a big constant term, so we need to evaluate the performance carefully.)
|
89
|
+
* Reusing an old analysis and incrementally updating it will bring a super big improvement.
|
90
|
+
This would be especially helpful for LSP support, so we need to tackle it after the analysis approach is mature.
|
91
|
+
|
92
|
+
### Language Server Protocol (LSP) support
|
93
|
+
|
94
|
+
In the future, we want TypeProf to serve as a language server to show the result in IDE in real-time.
|
95
|
+
However, the current analysis approach is too slow for IDE. So we need to improve the performance first.
|
96
|
+
|
97
|
+
Even if TypeProf becomes fast enough, its approach has a fundamental problem.
|
98
|
+
Since TypeProf uses whole program analysis, one edit may cause a cascade of propagation:
|
99
|
+
if a user write `foo(42)`, an Integer is propagated to a method `foo`,
|
100
|
+
and if `foo` passes its argument to a method `bar`, it is propagated to `bar`, ...
|
101
|
+
So, a breakthrough for LSP may be still needed, e.g, limiting the propagation range in real-time analysis,
|
102
|
+
assuming that a type interface of module boundary is fixed, etc.
|
103
|
+
|
104
|
+
## Relatively smaller TODOs
|
105
|
+
|
106
|
+
* Support more RBS features
|
107
|
+
* TypeProf does not deal with some RBS types well yet.
|
108
|
+
* For example, the `instance` type is handled as `untyped.
|
109
|
+
* The `self` type is handled well only when it is used as a return type.
|
110
|
+
* Using a value of the `void` type should be warned appropriately.
|
111
|
+
* RBS's `interface` is supported just like a module (i.e., `include _Foo` is explicitly required in RBS),
|
112
|
+
but it should be checked structually (i.e., it should be determined as a method set.)
|
113
|
+
* The variance of type parameters is currently ignored.
|
114
|
+
|
115
|
+
* Support more Ruby features
|
116
|
+
* Some meta-programming features like `Class.new`, `Object#method`, etc.
|
117
|
+
* It is possible to support `Class.new` by per-allocation-site approach:
|
118
|
+
e.g., In TypeProf, `A = Class.new; B = Class.new` will create two classes, but `2.times { Class.new }` will create one class.
|
119
|
+
* The analysis precision can be improved more for some Ruby features like pattern matching, keyword arguments, etc.
|
120
|
+
* For example, `foo(*args, k:1)` is currently compiled as if it is `foo(*(args + [{ :k => 1 }]))` into Ruby bytecode.
|
121
|
+
This mixes the keyword arguments to a rest array, and makes it difficult for TypeProf to track the keyword arguments.
|
122
|
+
* Support Enumerator as an Array-type container.
|
123
|
+
* Support `Module#protect` (but RBS does not yet).
|
124
|
+
* More heuristics may help such as `==` returns a bool regardless to its receiver and argument types.
|
125
|
+
|
126
|
+
* Make TypeProf more useful as a tool
|
127
|
+
* Currently, TypeProf provides only the analysis engine and a minimal set of features.
|
128
|
+
* The analysis result would be useful not only to generate RBS prototype
|
129
|
+
but also identifying the source location of a method definition, listing callsites of a method,
|
130
|
+
searching a method call by its argument types, etc.
|
131
|
+
* Sometimes, TypeProf prints very big union type, such as `Integer | Float | Complex | Rational | ...`.
|
132
|
+
Worse, the same big type is printed multiple times.
|
133
|
+
It may be useful to factor out such a long type by using type alias, for example.
|
data/lib/typeprof/analyzer.rb
CHANGED
@@ -222,6 +222,11 @@ module TypeProf
|
|
222
222
|
Env.new(senv, @locals, @stack, @type_params)
|
223
223
|
end
|
224
224
|
|
225
|
+
def replace_blk_ty(ty)
|
226
|
+
senv = StaticEnv.new(@static_env.recv_ty, ty, @static_env.mod_func, @static_env.pub_meth)
|
227
|
+
Env.new(senv, @locals, @stack, @type_params)
|
228
|
+
end
|
229
|
+
|
225
230
|
def inspect
|
226
231
|
"Env[#{ @static_env.inspect }, locals:#{ @locals.inspect }, stack:#{ @stack.inspect }, type_params:#{ (@type_params&.internal_hash).inspect }]"
|
227
232
|
end
|
@@ -295,7 +300,10 @@ module TypeProf
|
|
295
300
|
def initialize(kind, name, absolute_path)
|
296
301
|
raise unless name.is_a?(Array)
|
297
302
|
@kind = kind
|
298
|
-
@modules = {
|
303
|
+
@modules = {
|
304
|
+
:before => { true => [], false => [] }, # before = include/extend
|
305
|
+
:after => { true => [], false => [] }, # after = prepend
|
306
|
+
}
|
299
307
|
@name = name
|
300
308
|
@consts = {}
|
301
309
|
@methods = {}
|
@@ -308,13 +316,13 @@ module TypeProf
|
|
308
316
|
attr_reader :kind, :modules, :consts, :methods, :ivars, :cvars, :absolute_path
|
309
317
|
attr_accessor :name, :klass_obj
|
310
318
|
|
311
|
-
def
|
312
|
-
mod_, module_type_args, absolute_paths = @modules[singleton].find {|m,| m == mod }
|
319
|
+
def mix_module(kind, mod, type_args, singleton, absolute_path)
|
320
|
+
mod_, module_type_args, absolute_paths = @modules[kind][singleton].find {|m,| m == mod }
|
313
321
|
if mod_
|
314
|
-
raise "inconsistent include/extend type args in RBS?" if module_type_args != type_args && type_args != [] && type_args != nil
|
322
|
+
raise "inconsistent #{ kind == :after ? "include/extend" : "prepend" } type args in RBS?" if module_type_args != type_args && type_args != [] && type_args != nil
|
315
323
|
else
|
316
324
|
absolute_paths = Utils::MutableSet.new
|
317
|
-
@modules[singleton].unshift([mod, type_args, absolute_paths])
|
325
|
+
@modules[kind][singleton].unshift([mod, type_args, absolute_paths])
|
318
326
|
end
|
319
327
|
absolute_paths << absolute_path
|
320
328
|
end
|
@@ -331,10 +339,8 @@ module TypeProf
|
|
331
339
|
@consts[name] = [ty, absolute_path]
|
332
340
|
end
|
333
341
|
|
334
|
-
def
|
335
|
-
|
336
|
-
yield subst, direct if mthds&.include?(mthd)
|
337
|
-
@modules[singleton].each do |mod_def, type_args,|
|
342
|
+
def adjust_substitution_for_module(mods, mid, mthd, subst, &blk)
|
343
|
+
mods.each do |mod_def, type_args,|
|
338
344
|
if mod_def.klass_obj.type_params && type_args
|
339
345
|
subst2 = {}
|
340
346
|
mod_def.klass_obj.type_params.zip(type_args) do |(tyvar, *), tyarg|
|
@@ -346,13 +352,28 @@ module TypeProf
|
|
346
352
|
end
|
347
353
|
end
|
348
354
|
|
355
|
+
def adjust_substitution(singleton, mid, mthd, subst, direct, &blk)
|
356
|
+
adjust_substitution_for_module(@modules[:before][singleton], mid, mthd, subst, &blk)
|
357
|
+
|
358
|
+
mthds = @methods[[singleton, mid]]
|
359
|
+
yield subst, direct if mthds&.include?(mthd)
|
360
|
+
|
361
|
+
adjust_substitution_for_module(@modules[:after][singleton], mid, mthd, subst, &blk)
|
362
|
+
end
|
363
|
+
|
349
364
|
def search_method(singleton, mid, visited, &blk)
|
350
365
|
# Currently, circular inclusion of modules is allowed
|
351
366
|
return if visited[self]
|
352
367
|
visited[self] = true
|
368
|
+
|
369
|
+
@modules[:before][singleton].each do |mod_def,|
|
370
|
+
mod_def.search_method(false, mid, visited, &blk)
|
371
|
+
end
|
372
|
+
|
353
373
|
mthds = @methods[[singleton, mid]]
|
354
374
|
yield mthds, @klass_obj, singleton if mthds
|
355
|
-
|
375
|
+
|
376
|
+
@modules[:after][singleton].each do |mod_def,|
|
356
377
|
mod_def.search_method(false, mid, visited, &blk)
|
357
378
|
end
|
358
379
|
end
|
@@ -381,17 +402,17 @@ module TypeProf
|
|
381
402
|
end
|
382
403
|
end
|
383
404
|
|
384
|
-
def
|
385
|
-
return if
|
405
|
+
def mix_module(kind, mixing_mod, mixed_mod, type_args, singleton, caller_ep)
|
406
|
+
return if mixed_mod == Type.any
|
386
407
|
|
387
|
-
|
388
|
-
|
389
|
-
if
|
390
|
-
|
391
|
-
if
|
392
|
-
|
408
|
+
mixing_mod = @class_defs[mixing_mod.idx]
|
409
|
+
mixed_mod.each_child do |mixed_mod|
|
410
|
+
if mixed_mod.is_a?(Type::Class)
|
411
|
+
mixed_mod = @class_defs[mixed_mod.idx]
|
412
|
+
if mixed_mod && mixed_mod.kind == :module
|
413
|
+
mixing_mod.mix_module(kind, mixed_mod, type_args, singleton, caller_ep ? caller_ep.ctx.iseq.absolute_path : nil)
|
393
414
|
else
|
394
|
-
warn(caller_ep, "
|
415
|
+
warn(caller_ep, "attempted to #{ kind == :after ? "include/extend" : "prepend" } non-module; ignored")
|
395
416
|
end
|
396
417
|
end
|
397
418
|
end
|
@@ -578,12 +599,12 @@ module TypeProf
|
|
578
599
|
mdef
|
579
600
|
end
|
580
601
|
|
581
|
-
def add_attr_method(klass,
|
602
|
+
def add_attr_method(klass, mid, ivar, kind, pub_meth, ep)
|
582
603
|
if kind == :reader || kind == :accessor
|
583
|
-
add_method(klass, mid, false, AttrMethodDef.new(ivar, :reader,
|
604
|
+
add_method(klass, mid, false, AttrMethodDef.new(ivar, :reader, pub_meth, ep))
|
584
605
|
end
|
585
606
|
if kind == :writer || kind == :accessor
|
586
|
-
add_method(klass, :"#{ mid }=", false, AttrMethodDef.new(ivar, :writer,
|
607
|
+
add_method(klass, :"#{ mid }=", false, AttrMethodDef.new(ivar, :writer, pub_meth, ep))
|
587
608
|
end
|
588
609
|
end
|
589
610
|
|
@@ -595,22 +616,22 @@ module TypeProf
|
|
595
616
|
add_method(klass, mid, true, ISeqMethodDef.new(iseq, cref, outer_ep, pub_meth))
|
596
617
|
end
|
597
618
|
|
598
|
-
def set_custom_method(klass, mid, impl)
|
599
|
-
set_method(klass, mid, false, CustomMethodDef.new(impl))
|
619
|
+
def set_custom_method(klass, mid, impl, pub_meth = true)
|
620
|
+
set_method(klass, mid, false, CustomMethodDef.new(impl, pub_meth))
|
600
621
|
end
|
601
622
|
|
602
|
-
def set_singleton_custom_method(klass, mid, impl)
|
603
|
-
set_method(klass, mid, true, CustomMethodDef.new(impl))
|
623
|
+
def set_singleton_custom_method(klass, mid, impl, pub_meth = true)
|
624
|
+
set_method(klass, mid, true, CustomMethodDef.new(impl, pub_meth))
|
604
625
|
end
|
605
626
|
|
606
|
-
def alias_method(klass, singleton,
|
627
|
+
def alias_method(klass, singleton, alias_mid, orig_mid, ep)
|
607
628
|
if klass == Type.any
|
608
629
|
self
|
609
630
|
else
|
610
|
-
mdefs = get_method(klass, singleton,
|
631
|
+
mdefs = get_method(klass, singleton, orig_mid)
|
611
632
|
if mdefs
|
612
633
|
mdefs.each do |mdef|
|
613
|
-
@class_defs[klass.idx].add_method(
|
634
|
+
@class_defs[klass.idx].add_method(alias_mid, singleton, AliasMethodDef.new(orig_mid, mdef, ep))
|
614
635
|
end
|
615
636
|
end
|
616
637
|
end
|
@@ -689,7 +710,9 @@ module TypeProf
|
|
689
710
|
entry = @tbl[site] ||= Entry.new(false, {}, Type.bot, Utils::MutableSet.new)
|
690
711
|
entry.read_continuations[ep] = ctn
|
691
712
|
entry.absolute_paths << ep.ctx.iseq.absolute_path if ep.ctx.is_a?(Context)
|
692
|
-
|
713
|
+
ty = entry.type
|
714
|
+
ty = Type.nil if ty == Type.bot
|
715
|
+
ctn[ty, ep]
|
693
716
|
end
|
694
717
|
|
695
718
|
def add_write!(site, ty, ep, scratch)
|
@@ -697,7 +720,7 @@ module TypeProf
|
|
697
720
|
if ep
|
698
721
|
if entry.rbs_declared
|
699
722
|
unless Type.match?(ty, entry.type)
|
700
|
-
scratch.warn(ep, "inconsistent assignment to RBS-declared
|
723
|
+
scratch.warn(ep, "inconsistent assignment to RBS-declared variable")
|
701
724
|
return
|
702
725
|
end
|
703
726
|
end
|
@@ -1050,7 +1073,7 @@ module TypeProf
|
|
1050
1073
|
lead_tys = env.locals[0, lead_num].map {|ty| globalize_type(ty, env, ep) }
|
1051
1074
|
opt_tys = opt.size > 1 ? env.locals[lead_num, opt.size - 1].map {|ty| globalize_type(ty, env, ep) } : []
|
1052
1075
|
if rest_start # XXX:squash
|
1053
|
-
ty = globalize_type(env.locals[
|
1076
|
+
ty = globalize_type(env.locals[rest_start], env, ep)
|
1054
1077
|
rest_ty = Type.bot
|
1055
1078
|
ty.each_child_global do |ty|
|
1056
1079
|
if ty.is_a?(Type::Array)
|
@@ -1085,6 +1108,7 @@ module TypeProf
|
|
1085
1108
|
end
|
1086
1109
|
end
|
1087
1110
|
kw_rest_ty = globalize_type(env.locals[kw_rest], env, ep) if kw_rest
|
1111
|
+
kw_rest_ty = nil if kw_rest_ty == Type.nil
|
1088
1112
|
if block_start
|
1089
1113
|
blk_ty = globalize_type(env.locals[block_start], env, ep)
|
1090
1114
|
elsif iseq.type == :method
|
@@ -1264,7 +1288,7 @@ module TypeProf
|
|
1264
1288
|
end
|
1265
1289
|
end
|
1266
1290
|
return
|
1267
|
-
when :
|
1291
|
+
when :getlocal_send_branch
|
1268
1292
|
getlocal_operands, send_operands, branch_operands = operands
|
1269
1293
|
env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
|
1270
1294
|
recvs = Type.any if recvs == Type.bot
|
@@ -1292,6 +1316,31 @@ module TypeProf
|
|
1292
1316
|
end
|
1293
1317
|
end
|
1294
1318
|
return
|
1319
|
+
when :send_branch
|
1320
|
+
send_operands, branch_operands = operands
|
1321
|
+
env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
|
1322
|
+
recvs = Type.any if recvs == Type.bot
|
1323
|
+
recvs.each_child do |recv|
|
1324
|
+
do_send(recv, mid, aargs, ep, env) do |ret_ty, ep, env|
|
1325
|
+
env, ret_ty, = localize_type(ret_ty, env, ep)
|
1326
|
+
|
1327
|
+
branchtype, target, = branch_operands
|
1328
|
+
# branchtype: :if or :unless or :nil
|
1329
|
+
ep_then = ep.next
|
1330
|
+
ep_else = ep.jump(target)
|
1331
|
+
|
1332
|
+
case ret_ty
|
1333
|
+
when Type::Instance.new(Type::Builtin[:true])
|
1334
|
+
merge_env(branchtype == :if ? ep_else : ep_then, env)
|
1335
|
+
when Type::Instance.new(Type::Builtin[:false])
|
1336
|
+
merge_env(branchtype == :if ? ep_then : ep_else, env)
|
1337
|
+
else
|
1338
|
+
merge_env(ep_then, env)
|
1339
|
+
merge_env(ep_else, env)
|
1340
|
+
end
|
1341
|
+
end
|
1342
|
+
end
|
1343
|
+
return
|
1295
1344
|
when :invokeblock
|
1296
1345
|
env, recvs, mid, aargs = setup_actual_arguments(:block, operands, ep, env)
|
1297
1346
|
blk = env.static_env.blk_ty
|
@@ -2104,7 +2153,7 @@ module TypeProf
|
|
2104
2153
|
|
2105
2154
|
bsig ||= BlockSignature.new([], [], nil, Type.nil)
|
2106
2155
|
|
2107
|
-
bsig = bsig.screen_name(self)
|
2156
|
+
bsig = bsig.screen_name(nil, self)
|
2108
2157
|
ret_ty = ret_ty.screen_name(self)
|
2109
2158
|
ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
|
2110
2159
|
|
@@ -2131,7 +2180,7 @@ module TypeProf
|
|
2131
2180
|
end
|
2132
2181
|
end
|
2133
2182
|
|
2134
|
-
farg_tys = farg_tys ? farg_tys.screen_name(self) : "(unknown)"
|
2183
|
+
farg_tys = farg_tys ? farg_tys.screen_name(nil, self) : "(unknown)"
|
2135
2184
|
ret_ty = ret_ty.screen_name(self)
|
2136
2185
|
ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
|
2137
2186
|
|
@@ -2143,10 +2192,13 @@ module TypeProf
|
|
2143
2192
|
farg_tys = @method_signatures[ctx]
|
2144
2193
|
ret_ty = @return_values[ctx] || Type.bot
|
2145
2194
|
|
2146
|
-
|
2195
|
+
untyped = farg_tys.include_untyped?(self) || ret_ty.include_untyped?(self)
|
2196
|
+
|
2197
|
+
farg_tys = farg_tys.screen_name(ctx.iseq, self)
|
2147
2198
|
ret_ty = ret_ty.screen_name(self)
|
2148
2199
|
ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
|
2149
|
-
|
2200
|
+
|
2201
|
+
["#{ (farg_tys.empty? ? "" : "#{ farg_tys } ") }-> #{ ret_ty }", untyped]
|
2150
2202
|
end
|
2151
2203
|
end
|
2152
2204
|
end
|