typeprof 0.9.2 → 0.14.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.
- 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 +201 -104
- data/lib/typeprof/block.rb +39 -4
- data/lib/typeprof/builtin.rb +217 -70
- data/lib/typeprof/cli.rb +7 -0
- data/lib/typeprof/config.rb +30 -14
- data/lib/typeprof/container-type.rb +24 -0
- data/lib/typeprof/export.rb +134 -74
- data/lib/typeprof/import.rb +87 -39
- data/lib/typeprof/iseq.rb +116 -41
- 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/break4.rb +17 -0
- data/smoke/class_eval.rb +22 -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/define_method7.rb +18 -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/extended.rb +38 -0
- data/smoke/fib.rb +2 -2
- data/smoke/flow1.rb +1 -1
- data/smoke/flow10.rb +17 -0
- data/smoke/flow11.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/included.rb +38 -0
- data/smoke/inheritance.rb +4 -4
- data/smoke/inherited.rb +26 -0
- data/smoke/initialize.rb +3 -2
- data/smoke/instance_eval.rb +2 -2
- data/smoke/instance_eval4.rb +12 -0
- 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 +1 -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 +1 -0
- data/smoke/manual-rbs3.rb +1 -0
- 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 +1 -1
- 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 +25 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c03cc70d70c73d14871e07c098b0689e11afc8e309662a8d4402c6f2ecbc5d02
|
4
|
+
data.tar.gz: '097a04710ef19e6c8f29304c901dd8569e9e74a436dd1d76a5bf515df7fdfa41'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f5348942872848edec6b679db2a9afa60b746a6d3908f7066dfcd3a551d04ab728ea1494a5bdcf66867d7bc47ba0dce6ce29e2d29833babb5413262c8da92ca1
|
7
|
+
data.tar.gz: 9b214f8a4a24fa433a9c3d7fb205246b161c723206a73db257eb57b3905b34ab7ef378324a7786b63daccdf763616667c2d5d6fcbaf98ff6ff455f26f956082e
|
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.13.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
@@ -42,6 +42,10 @@ module TypeProf
|
|
42
42
|
"<builtin>"
|
43
43
|
end
|
44
44
|
end
|
45
|
+
|
46
|
+
def replace_cref(cref)
|
47
|
+
Context.new(@iseq, cref, @mid)
|
48
|
+
end
|
45
49
|
end
|
46
50
|
|
47
51
|
class TypedContext
|
@@ -61,6 +65,10 @@ module TypeProf
|
|
61
65
|
"<typed-context:#{ @mid }>"
|
62
66
|
end
|
63
67
|
end
|
68
|
+
|
69
|
+
def replace_cref(cref)
|
70
|
+
# What to do?
|
71
|
+
end
|
64
72
|
end
|
65
73
|
|
66
74
|
class ExecutionPoint
|
@@ -86,6 +94,10 @@ module TypeProf
|
|
86
94
|
ExecutionPoint.new(@ctx, @pc + 1, @outer)
|
87
95
|
end
|
88
96
|
|
97
|
+
def replace_cref(cref)
|
98
|
+
ExecutionPoint.new(@ctx.replace_cref(cref), @pc, @outer)
|
99
|
+
end
|
100
|
+
|
89
101
|
def source_location
|
90
102
|
@ctx.source_location(@pc)
|
91
103
|
end
|
@@ -117,30 +129,6 @@ module TypeProf
|
|
117
129
|
end
|
118
130
|
end
|
119
131
|
|
120
|
-
class TopStaticEnv
|
121
|
-
include Utils::StructuralEquality
|
122
|
-
|
123
|
-
def recv_ty
|
124
|
-
Type.bot
|
125
|
-
end
|
126
|
-
|
127
|
-
def blk_ty
|
128
|
-
Type.nil
|
129
|
-
end
|
130
|
-
|
131
|
-
def mod_func
|
132
|
-
false
|
133
|
-
end
|
134
|
-
|
135
|
-
def pub_meth
|
136
|
-
true
|
137
|
-
end
|
138
|
-
|
139
|
-
def merge(other)
|
140
|
-
raise unless other.is_a?(TopStaticEnv)
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
132
|
class Env
|
145
133
|
include Utils::StructuralEquality
|
146
134
|
|
@@ -246,6 +234,11 @@ module TypeProf
|
|
246
234
|
Env.new(senv, @locals, @stack, @type_params)
|
247
235
|
end
|
248
236
|
|
237
|
+
def replace_blk_ty(ty)
|
238
|
+
senv = StaticEnv.new(@static_env.recv_ty, ty, @static_env.mod_func, @static_env.pub_meth)
|
239
|
+
Env.new(senv, @locals, @stack, @type_params)
|
240
|
+
end
|
241
|
+
|
249
242
|
def inspect
|
250
243
|
"Env[#{ @static_env.inspect }, locals:#{ @locals.inspect }, stack:#{ @stack.inspect }, type_params:#{ (@type_params&.internal_hash).inspect }]"
|
251
244
|
end
|
@@ -257,6 +250,8 @@ module TypeProf
|
|
257
250
|
end
|
258
251
|
|
259
252
|
def initialize
|
253
|
+
@entrypoints = []
|
254
|
+
|
260
255
|
@worklist = Utils::WorkList.new
|
261
256
|
|
262
257
|
@ep2env = {}
|
@@ -291,6 +286,10 @@ module TypeProf
|
|
291
286
|
@anonymous_struct_gen_id = 0
|
292
287
|
end
|
293
288
|
|
289
|
+
def add_entrypoint(iseq)
|
290
|
+
@entrypoints << iseq
|
291
|
+
end
|
292
|
+
|
294
293
|
attr_reader :return_envs, :loaded_features, :rbs_reader
|
295
294
|
|
296
295
|
def get_env(ep)
|
@@ -319,7 +318,10 @@ module TypeProf
|
|
319
318
|
def initialize(kind, name, absolute_path)
|
320
319
|
raise unless name.is_a?(Array)
|
321
320
|
@kind = kind
|
322
|
-
@modules = {
|
321
|
+
@modules = {
|
322
|
+
:before => { true => [], false => [] }, # before = include/extend
|
323
|
+
:after => { true => [], false => [] }, # after = prepend
|
324
|
+
}
|
323
325
|
@name = name
|
324
326
|
@consts = {}
|
325
327
|
@methods = {}
|
@@ -332,13 +334,13 @@ module TypeProf
|
|
332
334
|
attr_reader :kind, :modules, :consts, :methods, :ivars, :cvars, :absolute_path
|
333
335
|
attr_accessor :name, :klass_obj
|
334
336
|
|
335
|
-
def
|
336
|
-
mod_, module_type_args, absolute_paths = @modules[singleton].find {|m,| m == mod }
|
337
|
+
def mix_module(kind, mod, type_args, singleton, absolute_path)
|
338
|
+
mod_, module_type_args, absolute_paths = @modules[kind][singleton].find {|m,| m == mod }
|
337
339
|
if mod_
|
338
|
-
raise "inconsistent include/extend type args in RBS?" if module_type_args != type_args && type_args != [] && type_args != nil
|
340
|
+
raise "inconsistent #{ kind == :after ? "include/extend" : "prepend" } type args in RBS?" if module_type_args != type_args && type_args != [] && type_args != nil
|
339
341
|
else
|
340
342
|
absolute_paths = Utils::MutableSet.new
|
341
|
-
@modules[singleton].unshift([mod, type_args, absolute_paths])
|
343
|
+
@modules[kind][singleton].unshift([mod, type_args, absolute_paths])
|
342
344
|
end
|
343
345
|
absolute_paths << absolute_path
|
344
346
|
end
|
@@ -355,10 +357,8 @@ module TypeProf
|
|
355
357
|
@consts[name] = [ty, absolute_path]
|
356
358
|
end
|
357
359
|
|
358
|
-
def
|
359
|
-
|
360
|
-
yield subst, direct if mthds&.include?(mthd)
|
361
|
-
@modules[singleton].each do |mod_def, type_args,|
|
360
|
+
def adjust_substitution_for_module(mods, mid, mthd, subst, &blk)
|
361
|
+
mods.each do |mod_def, type_args,|
|
362
362
|
if mod_def.klass_obj.type_params && type_args
|
363
363
|
subst2 = {}
|
364
364
|
mod_def.klass_obj.type_params.zip(type_args) do |(tyvar, *), tyarg|
|
@@ -370,13 +370,28 @@ module TypeProf
|
|
370
370
|
end
|
371
371
|
end
|
372
372
|
|
373
|
+
def adjust_substitution(singleton, mid, mthd, subst, direct, &blk)
|
374
|
+
adjust_substitution_for_module(@modules[:before][singleton], mid, mthd, subst, &blk)
|
375
|
+
|
376
|
+
mthds = @methods[[singleton, mid]]
|
377
|
+
yield subst, direct if mthds&.include?(mthd)
|
378
|
+
|
379
|
+
adjust_substitution_for_module(@modules[:after][singleton], mid, mthd, subst, &blk)
|
380
|
+
end
|
381
|
+
|
373
382
|
def search_method(singleton, mid, visited, &blk)
|
374
383
|
# Currently, circular inclusion of modules is allowed
|
375
384
|
return if visited[self]
|
376
385
|
visited[self] = true
|
386
|
+
|
387
|
+
@modules[:before][singleton].each do |mod_def,|
|
388
|
+
mod_def.search_method(false, mid, visited, &blk)
|
389
|
+
end
|
390
|
+
|
377
391
|
mthds = @methods[[singleton, mid]]
|
378
392
|
yield mthds, @klass_obj, singleton if mthds
|
379
|
-
|
393
|
+
|
394
|
+
@modules[:after][singleton].each do |mod_def,|
|
380
395
|
mod_def.search_method(false, mid, visited, &blk)
|
381
396
|
end
|
382
397
|
end
|
@@ -405,17 +420,17 @@ module TypeProf
|
|
405
420
|
end
|
406
421
|
end
|
407
422
|
|
408
|
-
def
|
409
|
-
return if
|
423
|
+
def mix_module(kind, mixing_mod, mixed_mod, type_args, singleton, caller_ep)
|
424
|
+
return if mixed_mod == Type.any
|
410
425
|
|
411
|
-
|
412
|
-
|
413
|
-
if
|
414
|
-
|
415
|
-
if
|
416
|
-
|
426
|
+
mixing_mod = @class_defs[mixing_mod.idx]
|
427
|
+
mixed_mod.each_child do |mixed_mod|
|
428
|
+
if mixed_mod.is_a?(Type::Class)
|
429
|
+
mixed_mod = @class_defs[mixed_mod.idx]
|
430
|
+
if mixed_mod && mixed_mod.kind == :module
|
431
|
+
mixing_mod.mix_module(kind, mixed_mod, type_args, singleton, caller_ep ? caller_ep.ctx.iseq.absolute_path : nil)
|
417
432
|
else
|
418
|
-
warn(caller_ep, "
|
433
|
+
warn(caller_ep, "attempted to #{ kind == :after ? "include/extend" : "prepend" } non-module; ignored")
|
419
434
|
end
|
420
435
|
end
|
421
436
|
end
|
@@ -602,12 +617,12 @@ module TypeProf
|
|
602
617
|
mdef
|
603
618
|
end
|
604
619
|
|
605
|
-
def add_attr_method(klass,
|
620
|
+
def add_attr_method(klass, mid, ivar, kind, pub_meth, ep)
|
606
621
|
if kind == :reader || kind == :accessor
|
607
|
-
add_method(klass, mid, false, AttrMethodDef.new(ivar, :reader,
|
622
|
+
add_method(klass, mid, false, AttrMethodDef.new(ivar, :reader, pub_meth, ep))
|
608
623
|
end
|
609
624
|
if kind == :writer || kind == :accessor
|
610
|
-
add_method(klass, :"#{ mid }=", false, AttrMethodDef.new(ivar, :writer,
|
625
|
+
add_method(klass, :"#{ mid }=", false, AttrMethodDef.new(ivar, :writer, pub_meth, ep))
|
611
626
|
end
|
612
627
|
end
|
613
628
|
|
@@ -619,22 +634,22 @@ module TypeProf
|
|
619
634
|
add_method(klass, mid, true, ISeqMethodDef.new(iseq, cref, outer_ep, pub_meth))
|
620
635
|
end
|
621
636
|
|
622
|
-
def set_custom_method(klass, mid, impl)
|
623
|
-
set_method(klass, mid, false, CustomMethodDef.new(impl))
|
637
|
+
def set_custom_method(klass, mid, impl, pub_meth = true)
|
638
|
+
set_method(klass, mid, false, CustomMethodDef.new(impl, pub_meth))
|
624
639
|
end
|
625
640
|
|
626
|
-
def set_singleton_custom_method(klass, mid, impl)
|
627
|
-
set_method(klass, mid, true, CustomMethodDef.new(impl))
|
641
|
+
def set_singleton_custom_method(klass, mid, impl, pub_meth = true)
|
642
|
+
set_method(klass, mid, true, CustomMethodDef.new(impl, pub_meth))
|
628
643
|
end
|
629
644
|
|
630
|
-
def alias_method(klass, singleton,
|
645
|
+
def alias_method(klass, singleton, alias_mid, orig_mid, ep)
|
631
646
|
if klass == Type.any
|
632
647
|
self
|
633
648
|
else
|
634
|
-
mdefs = get_method(klass, singleton,
|
649
|
+
mdefs = get_method(klass, singleton, orig_mid)
|
635
650
|
if mdefs
|
636
651
|
mdefs.each do |mdef|
|
637
|
-
@class_defs[klass.idx].add_method(
|
652
|
+
@class_defs[klass.idx].add_method(alias_mid, singleton, AliasMethodDef.new(orig_mid, mdef, ep))
|
638
653
|
end
|
639
654
|
end
|
640
655
|
end
|
@@ -723,7 +738,7 @@ module TypeProf
|
|
723
738
|
if ep
|
724
739
|
if entry.rbs_declared
|
725
740
|
unless Type.match?(ty, entry.type)
|
726
|
-
scratch.warn(ep, "inconsistent assignment to RBS-declared
|
741
|
+
scratch.warn(ep, "inconsistent assignment to RBS-declared variable")
|
727
742
|
return
|
728
743
|
end
|
729
744
|
end
|
@@ -881,56 +896,68 @@ module TypeProf
|
|
881
896
|
iter_counter = 0
|
882
897
|
stat_eps = Utils::MutableSet.new
|
883
898
|
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
899
|
+
prologue_ctx = Context.new(nil, nil, nil)
|
900
|
+
prologue_ep = ExecutionPoint.new(prologue_ctx, -1, nil)
|
901
|
+
prologue_env = Env.new(StaticEnv.new(Type.bot, Type.nil, false, true), [], [], Utils::HashWrapper.new({}))
|
902
|
+
|
903
|
+
until @entrypoints.empty?
|
904
|
+
iseq = @entrypoints.shift
|
905
|
+
ep, env = TypeProf.starting_state(iseq)
|
906
|
+
merge_env(ep, env)
|
907
|
+
add_callsite!(ep.ctx, prologue_ep, prologue_env) {|ty, ep| }
|
908
|
+
|
909
|
+
while true
|
910
|
+
until @worklist.empty?
|
911
|
+
ep = @worklist.deletemin
|
912
|
+
|
913
|
+
iter_counter += 1
|
914
|
+
if Config.options[:show_indicator]
|
915
|
+
tick2 = Time.now
|
916
|
+
if tick2 - tick >= 1
|
917
|
+
tick = tick2
|
918
|
+
$stderr << "\rType Profiling... (%d instructions @ %s)\e[K" % [iter_counter, ep.source_location]
|
919
|
+
$stderr.flush
|
920
|
+
end
|
895
921
|
end
|
896
|
-
end
|
897
922
|
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
923
|
+
if (Config.max_sec && Time.now - start_time >= Config.max_sec) || (Config.max_iter && Config.max_iter <= iter_counter)
|
924
|
+
@terminated = true
|
925
|
+
break
|
926
|
+
end
|
902
927
|
|
903
|
-
|
904
|
-
|
905
|
-
|
928
|
+
stat_eps << ep
|
929
|
+
step(ep)
|
930
|
+
end
|
906
931
|
|
907
|
-
|
932
|
+
break if @terminated
|
908
933
|
|
909
|
-
|
934
|
+
break unless Config.options[:stub_execution]
|
910
935
|
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
936
|
+
begin
|
937
|
+
iseq, (kind, dummy_continuation) = @pending_execution.first
|
938
|
+
break if !iseq
|
939
|
+
@pending_execution.delete(iseq)
|
940
|
+
end while @executed_iseqs.include?(iseq)
|
916
941
|
|
917
|
-
|
942
|
+
puts "DEBUG: trigger stub execution (#{ iseq&.name || "(nil)" }): rest #{ @pending_execution.size }" if Config.verbose >= 2
|
918
943
|
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
merge_env(ep, env)
|
924
|
-
add_iseq_method_call!(meth, ep.ctx)
|
925
|
-
|
926
|
-
when :block
|
927
|
-
blk, epenvs = dummy_continuation
|
928
|
-
epenvs.each do |ep, env|
|
944
|
+
break if !iseq
|
945
|
+
case kind
|
946
|
+
when :method
|
947
|
+
meth, ep, env = dummy_continuation
|
929
948
|
merge_env(ep, env)
|
930
|
-
|
949
|
+
add_iseq_method_call!(meth, ep.ctx)
|
950
|
+
|
951
|
+
when :block
|
952
|
+
blk, epenvs = dummy_continuation
|
953
|
+
epenvs.each do |ep, env|
|
954
|
+
merge_env(ep, env)
|
955
|
+
add_block_to_ctx!(blk.block_body, ep.ctx)
|
956
|
+
end
|
931
957
|
end
|
932
958
|
end
|
933
959
|
end
|
960
|
+
|
934
961
|
$stderr.print "\r\e[K" if Config.options[:show_indicator]
|
935
962
|
|
936
963
|
stat_eps
|
@@ -1076,7 +1103,7 @@ module TypeProf
|
|
1076
1103
|
lead_tys = env.locals[0, lead_num].map {|ty| globalize_type(ty, env, ep) }
|
1077
1104
|
opt_tys = opt.size > 1 ? env.locals[lead_num, opt.size - 1].map {|ty| globalize_type(ty, env, ep) } : []
|
1078
1105
|
if rest_start # XXX:squash
|
1079
|
-
ty = globalize_type(env.locals[
|
1106
|
+
ty = globalize_type(env.locals[rest_start], env, ep)
|
1080
1107
|
rest_ty = Type.bot
|
1081
1108
|
ty.each_child_global do |ty|
|
1082
1109
|
if ty.is_a?(Type::Array)
|
@@ -1111,6 +1138,7 @@ module TypeProf
|
|
1111
1138
|
end
|
1112
1139
|
end
|
1113
1140
|
kw_rest_ty = globalize_type(env.locals[kw_rest], env, ep) if kw_rest
|
1141
|
+
kw_rest_ty = nil if kw_rest_ty == Type.nil
|
1114
1142
|
if block_start
|
1115
1143
|
blk_ty = globalize_type(env.locals[block_start], env, ep)
|
1116
1144
|
elsif iseq.type == :method
|
@@ -1247,7 +1275,13 @@ module TypeProf
|
|
1247
1275
|
end
|
1248
1276
|
if cbase.is_a?(Type::Class)
|
1249
1277
|
klass = new_class(cbase, id, [], superclass, ep.ctx.iseq.absolute_path)
|
1250
|
-
|
1278
|
+
if superclass
|
1279
|
+
add_superclass_type_args!(klass, superclass.type_params.map { Type.any })
|
1280
|
+
|
1281
|
+
# inherited hook
|
1282
|
+
aargs = ActualArguments.new([klass], nil, {}, Type.nil)
|
1283
|
+
do_send(superclass, :inherited, aargs, ep, env) {|_ret_ty, _ep| }
|
1284
|
+
end
|
1251
1285
|
else
|
1252
1286
|
klass = Type.any
|
1253
1287
|
end
|
@@ -1290,7 +1324,7 @@ module TypeProf
|
|
1290
1324
|
end
|
1291
1325
|
end
|
1292
1326
|
return
|
1293
|
-
when :
|
1327
|
+
when :recv_getlocal_send_branch
|
1294
1328
|
getlocal_operands, send_operands, branch_operands = operands
|
1295
1329
|
env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
|
1296
1330
|
recvs = Type.any if recvs == Type.bot
|
@@ -1318,6 +1352,64 @@ module TypeProf
|
|
1318
1352
|
end
|
1319
1353
|
end
|
1320
1354
|
return
|
1355
|
+
when :arg_getlocal_send_branch
|
1356
|
+
getlocal_operands, send_operands, branch_operands = operands
|
1357
|
+
env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
|
1358
|
+
raise if aargs.lead_tys.size != 1
|
1359
|
+
aarg = aargs.lead_tys[0]
|
1360
|
+
aarg = Type.any if aarg == Type.bot
|
1361
|
+
recvs.each_child do |recv|
|
1362
|
+
aarg.each_child do |aarg|
|
1363
|
+
aargs_tmp = ActualArguments.new([aarg], nil, {}, aargs.blk_ty)
|
1364
|
+
do_send(recv, mid, aargs_tmp, ep, env) do |ret_ty, ep, env|
|
1365
|
+
env, ret_ty, = localize_type(ret_ty, env, ep)
|
1366
|
+
|
1367
|
+
branchtype, target, = branch_operands
|
1368
|
+
# branchtype: :if or :unless or :nil
|
1369
|
+
ep_then = ep.next
|
1370
|
+
ep_else = ep.jump(target)
|
1371
|
+
|
1372
|
+
var_idx, _scope_idx, _escaped = getlocal_operands
|
1373
|
+
flow_env = env.local_update(-var_idx+2, aarg)
|
1374
|
+
|
1375
|
+
case ret_ty
|
1376
|
+
when Type::Instance.new(Type::Builtin[:true])
|
1377
|
+
merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
|
1378
|
+
when Type::Instance.new(Type::Builtin[:false])
|
1379
|
+
merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
|
1380
|
+
else
|
1381
|
+
merge_env(ep_then, env)
|
1382
|
+
merge_env(ep_else, env)
|
1383
|
+
end
|
1384
|
+
end
|
1385
|
+
end
|
1386
|
+
end
|
1387
|
+
return
|
1388
|
+
when :send_branch
|
1389
|
+
send_operands, branch_operands = operands
|
1390
|
+
env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
|
1391
|
+
recvs = Type.any if recvs == Type.bot
|
1392
|
+
recvs.each_child do |recv|
|
1393
|
+
do_send(recv, mid, aargs, ep, env) do |ret_ty, ep, env|
|
1394
|
+
env, ret_ty, = localize_type(ret_ty, env, ep)
|
1395
|
+
|
1396
|
+
branchtype, target, = branch_operands
|
1397
|
+
# branchtype: :if or :unless or :nil
|
1398
|
+
ep_then = ep.next
|
1399
|
+
ep_else = ep.jump(target)
|
1400
|
+
|
1401
|
+
case ret_ty
|
1402
|
+
when Type::Instance.new(Type::Builtin[:true])
|
1403
|
+
merge_env(branchtype == :if ? ep_else : ep_then, env)
|
1404
|
+
when Type::Instance.new(Type::Builtin[:false])
|
1405
|
+
merge_env(branchtype == :if ? ep_then : ep_else, env)
|
1406
|
+
else
|
1407
|
+
merge_env(ep_then, env)
|
1408
|
+
merge_env(ep_else, env)
|
1409
|
+
end
|
1410
|
+
end
|
1411
|
+
end
|
1412
|
+
return
|
1321
1413
|
when :invokeblock
|
1322
1414
|
env, recvs, mid, aargs = setup_actual_arguments(:block, operands, ep, env)
|
1323
1415
|
blk = env.static_env.blk_ty
|
@@ -1396,7 +1488,7 @@ module TypeProf
|
|
1396
1488
|
end
|
1397
1489
|
_type, _iseq, cont, stack_depth = tmp_ep.ctx.iseq.catch_table[tmp_ep.pc]&.find {|type,| type == :break }
|
1398
1490
|
if cont
|
1399
|
-
nenv = @return_envs[tmp_ep]
|
1491
|
+
nenv = @return_envs[tmp_ep] || env
|
1400
1492
|
nenv, = nenv.pop(nenv.stack.size - stack_depth)
|
1401
1493
|
nenv = nenv.push(ty)
|
1402
1494
|
tmp_ep = tmp_ep.jump(cont)
|
@@ -1755,8 +1847,10 @@ module TypeProf
|
|
1755
1847
|
when :nop
|
1756
1848
|
when :setn
|
1757
1849
|
idx, = operands
|
1758
|
-
|
1759
|
-
|
1850
|
+
if idx >= 1
|
1851
|
+
env, (ty,) = env.pop(1)
|
1852
|
+
env = env.setn(idx, ty).push(ty)
|
1853
|
+
end
|
1760
1854
|
when :topn
|
1761
1855
|
idx, = operands
|
1762
1856
|
env = env.topn(idx)
|
@@ -2068,10 +2162,10 @@ module TypeProf
|
|
2068
2162
|
end
|
2069
2163
|
end
|
2070
2164
|
|
2071
|
-
def do_invoke_block(blk, aargs, ep, env, replace_recv_ty: nil, &ctn)
|
2165
|
+
def do_invoke_block(blk, aargs, ep, env, replace_recv_ty: nil, replace_cref: nil, &ctn)
|
2072
2166
|
blk.each_child do |blk|
|
2073
2167
|
if blk.is_a?(Type::Proc)
|
2074
|
-
blk.block_body.do_call(aargs, ep, env, self, replace_recv_ty: replace_recv_ty, &ctn)
|
2168
|
+
blk.block_body.do_call(aargs, ep, env, self, replace_recv_ty: replace_recv_ty, replace_cref: replace_cref, &ctn)
|
2075
2169
|
else
|
2076
2170
|
warn(ep, "non-proc is passed as a block")
|
2077
2171
|
ctn[Type.any, ep, env]
|
@@ -2130,7 +2224,7 @@ module TypeProf
|
|
2130
2224
|
|
2131
2225
|
bsig ||= BlockSignature.new([], [], nil, Type.nil)
|
2132
2226
|
|
2133
|
-
bsig = bsig.screen_name(self)
|
2227
|
+
bsig = bsig.screen_name(nil, self)
|
2134
2228
|
ret_ty = ret_ty.screen_name(self)
|
2135
2229
|
ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
|
2136
2230
|
|
@@ -2157,7 +2251,7 @@ module TypeProf
|
|
2157
2251
|
end
|
2158
2252
|
end
|
2159
2253
|
|
2160
|
-
farg_tys = farg_tys ? farg_tys.screen_name(self) : "(unknown)"
|
2254
|
+
farg_tys = farg_tys ? farg_tys.screen_name(nil, self) : "(unknown)"
|
2161
2255
|
ret_ty = ret_ty.screen_name(self)
|
2162
2256
|
ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
|
2163
2257
|
|
@@ -2169,10 +2263,13 @@ module TypeProf
|
|
2169
2263
|
farg_tys = @method_signatures[ctx]
|
2170
2264
|
ret_ty = @return_values[ctx] || Type.bot
|
2171
2265
|
|
2172
|
-
|
2266
|
+
untyped = farg_tys.include_untyped?(self) || ret_ty.include_untyped?(self)
|
2267
|
+
|
2268
|
+
farg_tys = farg_tys.screen_name(ctx.iseq, self)
|
2173
2269
|
ret_ty = ret_ty.screen_name(self)
|
2174
2270
|
ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
|
2175
|
-
|
2271
|
+
|
2272
|
+
["#{ (farg_tys.empty? ? "" : "#{ farg_tys } ") }-> #{ ret_ty }", untyped]
|
2176
2273
|
end
|
2177
2274
|
end
|
2178
2275
|
end
|