typeprof 0.10.0 → 0.14.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8d68099e4f5d60a45f949d6bfc3a47e8b7d3b7ba66a0794f25f5fe61628de1d1
4
- data.tar.gz: 22792d1acb9b16a4150f1afbaf29d59987bf29772908b23075e6668df81a4309
3
+ metadata.gz: d5828bf5dda203756a758bba9809d3b1fe81b284cc1373f00ff75ca175afe998
4
+ data.tar.gz: c274d8bda556407a33c70227a4caea57f1ae91f79ee546f9076d6ffac01891a5
5
5
  SHA512:
6
- metadata.gz: 42faa4e3b5c64fd5976e66067bf13339c530785914caad77bc0a405939bc914e9adf78bfbd0aea57a829cb21aed3c6dfc09f8f57def85b09312facd82d33b5a9
7
- data.tar.gz: bc6f115fbf1d5292286f04f09d745cfb0c13688a71fdae730d0cfdd903ad8f9f3f9019e532c37b4cf23fe41f39c9a1683f92f28af68716ddf7e625a0efca33f8
6
+ metadata.gz: e32f209894645a18b36a24c2347e86e249ffa0bcd2c93565d1920551ef143e73f6f3c58011250a545d761e9212aee032e99a44503e4dad5e1975d73de412d3c9
7
+ data.tar.gz: f3463cbae97beff6b359acb14a3a871f83d0ed37332e46ce38d846b2c486a9af3e6864a5450fe82e0abae79763b71ed55a26f1ab03d6a04aa9eb62232dd435fe
@@ -7,7 +7,7 @@ jobs:
7
7
  strategy:
8
8
  fail-fast: false
9
9
  matrix:
10
- ruby-version: [2.7.1, head]
10
+ ruby-version: [2.7, 3.0, head]
11
11
  runs-on: ubuntu-latest
12
12
  steps:
13
13
  - uses: actions/checkout@v2
data/Gemfile.lock CHANGED
@@ -1,25 +1,25 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- typeprof (0.10.0)
5
- rbs (>= 0.20.1)
4
+ typeprof (0.14.1)
5
+ rbs (>= 1.2.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.2)
12
- power_assert (1.2.0)
11
+ docile (1.4.0)
12
+ power_assert (2.0.0)
13
13
  rake (13.0.1)
14
- rbs (0.20.1)
15
- simplecov (0.20.0)
14
+ rbs (1.2.0)
15
+ simplecov (0.21.2)
16
16
  docile (~> 1.1)
17
17
  simplecov-html (~> 0.11)
18
18
  simplecov_json_formatter (~> 0.1)
19
19
  simplecov-html (0.12.3)
20
- simplecov_json_formatter (0.1.2)
21
- stackprof (0.2.16)
22
- test-unit (3.3.7)
20
+ simplecov_json_formatter (0.1.3)
21
+ stackprof (0.2.17)
22
+ test-unit (3.4.1)
23
23
  power_assert
24
24
 
25
25
  PLATFORMS
@@ -36,4 +36,4 @@ DEPENDENCIES
36
36
  typeprof!
37
37
 
38
38
  BUNDLED WITH
39
- 2.2.1
39
+ 2.3.0.dev
data/doc/demo.md CHANGED
@@ -39,7 +39,7 @@ class Object
39
39
  end
40
40
  ```
41
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).
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 | Intger
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.
@@ -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
@@ -238,6 +250,8 @@ module TypeProf
238
250
  end
239
251
 
240
252
  def initialize
253
+ @entrypoints = []
254
+
241
255
  @worklist = Utils::WorkList.new
242
256
 
243
257
  @ep2env = {}
@@ -272,6 +286,10 @@ module TypeProf
272
286
  @anonymous_struct_gen_id = 0
273
287
  end
274
288
 
289
+ def add_entrypoint(iseq)
290
+ @entrypoints << iseq
291
+ end
292
+
275
293
  attr_reader :return_envs, :loaded_features, :rbs_reader
276
294
 
277
295
  def get_env(ep)
@@ -878,56 +896,68 @@ module TypeProf
878
896
  iter_counter = 0
879
897
  stat_eps = Utils::MutableSet.new
880
898
 
881
- while true
882
- until @worklist.empty?
883
- ep = @worklist.deletemin
884
-
885
- iter_counter += 1
886
- if Config.options[:show_indicator]
887
- tick2 = Time.now
888
- if tick2 - tick >= 1
889
- tick = tick2
890
- $stderr << "\rType Profiling... (%d instructions @ %s)\e[K" % [iter_counter, ep.source_location]
891
- $stderr.flush
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
892
921
  end
893
- end
894
922
 
895
- if (Config.max_sec && Time.now - start_time >= Config.max_sec) || (Config.max_iter && Config.max_iter <= iter_counter)
896
- @terminated = true
897
- break
898
- end
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
899
927
 
900
- stat_eps << ep
901
- step(ep)
902
- end
928
+ stat_eps << ep
929
+ step(ep)
930
+ end
903
931
 
904
- break if @terminated
932
+ break if @terminated
905
933
 
906
- break unless Config.options[:stub_execution]
934
+ break unless Config.options[:stub_execution]
907
935
 
908
- begin
909
- iseq, (kind, dummy_continuation) = @pending_execution.first
910
- break if !iseq
911
- @pending_execution.delete(iseq)
912
- end while @executed_iseqs.include?(iseq)
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)
913
941
 
914
- puts "DEBUG: trigger stub execution (#{ iseq&.name || "(nil)" }): rest #{ @pending_execution.size }" if Config.verbose >= 2
942
+ puts "DEBUG: trigger stub execution (#{ iseq&.name || "(nil)" }): rest #{ @pending_execution.size }" if Config.verbose >= 2
915
943
 
916
- break if !iseq
917
- case kind
918
- when :method
919
- meth, ep, env = dummy_continuation
920
- merge_env(ep, env)
921
- add_iseq_method_call!(meth, ep.ctx)
922
-
923
- when :block
924
- blk, epenvs = dummy_continuation
925
- epenvs.each do |ep, env|
944
+ break if !iseq
945
+ case kind
946
+ when :method
947
+ meth, ep, env = dummy_continuation
926
948
  merge_env(ep, env)
927
- add_block_to_ctx!(blk.block_body, ep.ctx)
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
928
957
  end
929
958
  end
930
959
  end
960
+
931
961
  $stderr.print "\r\e[K" if Config.options[:show_indicator]
932
962
 
933
963
  stat_eps
@@ -1245,7 +1275,13 @@ module TypeProf
1245
1275
  end
1246
1276
  if cbase.is_a?(Type::Class)
1247
1277
  klass = new_class(cbase, id, [], superclass, ep.ctx.iseq.absolute_path)
1248
- add_superclass_type_args!(klass, superclass.type_params.map { Type.any }) if superclass
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
1249
1285
  else
1250
1286
  klass = Type.any
1251
1287
  end
@@ -1288,7 +1324,7 @@ module TypeProf
1288
1324
  end
1289
1325
  end
1290
1326
  return
1291
- when :getlocal_send_branch
1327
+ when :recv_getlocal_send_branch
1292
1328
  getlocal_operands, send_operands, branch_operands = operands
1293
1329
  env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
1294
1330
  recvs = Type.any if recvs == Type.bot
@@ -1316,6 +1352,39 @@ module TypeProf
1316
1352
  end
1317
1353
  end
1318
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
1319
1388
  when :send_branch
1320
1389
  send_operands, branch_operands = operands
1321
1390
  env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
@@ -1419,7 +1488,7 @@ module TypeProf
1419
1488
  end
1420
1489
  _type, _iseq, cont, stack_depth = tmp_ep.ctx.iseq.catch_table[tmp_ep.pc]&.find {|type,| type == :break }
1421
1490
  if cont
1422
- nenv = @return_envs[tmp_ep]
1491
+ nenv = @return_envs[tmp_ep] || env
1423
1492
  nenv, = nenv.pop(nenv.stack.size - stack_depth)
1424
1493
  nenv = nenv.push(ty)
1425
1494
  tmp_ep = tmp_ep.jump(cont)
@@ -1778,8 +1847,10 @@ module TypeProf
1778
1847
  when :nop
1779
1848
  when :setn
1780
1849
  idx, = operands
1781
- env, (ty,) = env.pop(1)
1782
- env = env.setn(idx, ty).push(ty)
1850
+ if idx >= 1
1851
+ env, (ty,) = env.pop(1)
1852
+ env = env.setn(idx, ty).push(ty)
1853
+ end
1783
1854
  when :topn
1784
1855
  idx, = operands
1785
1856
  env = env.topn(idx)
@@ -2091,10 +2162,10 @@ module TypeProf
2091
2162
  end
2092
2163
  end
2093
2164
 
2094
- 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)
2095
2166
  blk.each_child do |blk|
2096
2167
  if blk.is_a?(Type::Proc)
2097
- 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)
2098
2169
  else
2099
2170
  warn(ep, "non-proc is passed as a block")
2100
2171
  ctn[Type.any, ep, env]
@@ -2192,10 +2263,13 @@ module TypeProf
2192
2263
  farg_tys = @method_signatures[ctx]
2193
2264
  ret_ty = @return_values[ctx] || Type.bot
2194
2265
 
2266
+ untyped = farg_tys.include_untyped?(self) || ret_ty.include_untyped?(self)
2267
+
2195
2268
  farg_tys = farg_tys.screen_name(ctx.iseq, self)
2196
2269
  ret_ty = ret_ty.screen_name(self)
2197
2270
  ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
2198
- "#{ (farg_tys.empty? ? "" : "#{ farg_tys } ") }-> #{ ret_ty }"
2271
+
2272
+ ["#{ (farg_tys.empty? ? "" : "#{ farg_tys } ") }-> #{ ret_ty }", untyped]
2199
2273
  end
2200
2274
  end
2201
2275
  end