typeprof 0.9.1 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (188) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +1 -1
  3. data/Gemfile.lock +6 -5
  4. data/doc/demo.md +2 -2
  5. data/doc/todo.md +133 -0
  6. data/lib/typeprof/analyzer.rb +163 -77
  7. data/lib/typeprof/block.rb +39 -4
  8. data/lib/typeprof/builtin.rb +195 -70
  9. data/lib/typeprof/cli.rb +7 -0
  10. data/lib/typeprof/config.rb +30 -14
  11. data/lib/typeprof/container-type.rb +24 -0
  12. data/lib/typeprof/export.rb +134 -74
  13. data/lib/typeprof/import.rb +87 -39
  14. data/lib/typeprof/iseq.rb +23 -4
  15. data/lib/typeprof/method.rb +29 -7
  16. data/lib/typeprof/type.rb +75 -13
  17. data/lib/typeprof/version.rb +1 -1
  18. data/smoke/alias.rb +4 -4
  19. data/smoke/alias2.rb +3 -1
  20. data/smoke/arguments.rb +2 -2
  21. data/smoke/arguments2.rb +5 -5
  22. data/smoke/array-each.rb +1 -1
  23. data/smoke/array-each3.rb +1 -1
  24. data/smoke/array-map.rb +1 -1
  25. data/smoke/array-map2.rb +1 -1
  26. data/smoke/array-map3.rb +3 -3
  27. data/smoke/array-mul.rb +2 -2
  28. data/smoke/array-plus1.rb +1 -1
  29. data/smoke/array-plus2.rb +1 -0
  30. data/smoke/array-range-aref.rb +11 -11
  31. data/smoke/array-replace.rb +1 -1
  32. data/smoke/array1.rb +5 -5
  33. data/smoke/array10.rb +1 -1
  34. data/smoke/array11.rb +1 -1
  35. data/smoke/array12.rb +1 -1
  36. data/smoke/array14.rb +1 -1
  37. data/smoke/array15.rb +1 -1
  38. data/smoke/array2.rb +2 -2
  39. data/smoke/array3.rb +1 -0
  40. data/smoke/array6.rb +2 -1
  41. data/smoke/array8.rb +1 -1
  42. data/smoke/array9.rb +1 -1
  43. data/smoke/attr-module.rb +1 -0
  44. data/smoke/attr-vis.rb +43 -0
  45. data/smoke/attr-vis.rbs +4 -0
  46. data/smoke/attr.rb +2 -2
  47. data/smoke/block-ambiguous.rb +4 -4
  48. data/smoke/block-args1-rest.rb +6 -5
  49. data/smoke/block-args1.rb +5 -5
  50. data/smoke/block-args2-rest.rb +6 -5
  51. data/smoke/block-args2.rb +5 -5
  52. data/smoke/block-args3-rest.rb +7 -6
  53. data/smoke/block-args3.rb +6 -6
  54. data/smoke/block-blockarg.rb +3 -3
  55. data/smoke/block-kwarg.rb +4 -4
  56. data/smoke/block1.rb +1 -1
  57. data/smoke/block10.rb +1 -1
  58. data/smoke/block11.rb +2 -2
  59. data/smoke/block2.rb +1 -1
  60. data/smoke/block3.rb +1 -1
  61. data/smoke/block5.rb +1 -0
  62. data/smoke/block_given.rb +37 -0
  63. data/smoke/class_eval.rb +22 -0
  64. data/smoke/class_method.rb +2 -2
  65. data/smoke/class_method2.rb +2 -2
  66. data/smoke/constant2.rb +3 -2
  67. data/smoke/context-sensitive1.rb +1 -1
  68. data/smoke/cvar.rb +3 -2
  69. data/smoke/define_method.rb +2 -2
  70. data/smoke/define_method3.rb +1 -0
  71. data/smoke/define_method4.rb +1 -1
  72. data/smoke/define_method6.rb +19 -0
  73. data/smoke/define_method7.rb +18 -0
  74. data/smoke/demo.rb +6 -6
  75. data/smoke/demo1.rb +1 -1
  76. data/smoke/demo11.rb +1 -1
  77. data/smoke/demo2.rb +1 -1
  78. data/smoke/demo3.rb +1 -1
  79. data/smoke/demo4.rb +3 -3
  80. data/smoke/demo5.rb +1 -1
  81. data/smoke/demo6.rb +2 -1
  82. data/smoke/demo7.rb +1 -1
  83. data/smoke/demo9.rb +1 -0
  84. data/smoke/dummy-execution1.rb +1 -1
  85. data/smoke/dummy-execution2.rb +1 -1
  86. data/smoke/dummy_element.rb +1 -1
  87. data/smoke/ensure1.rb +1 -1
  88. data/smoke/enum_for.rb +15 -0
  89. data/smoke/enum_for2.rb +17 -0
  90. data/smoke/extended.rb +38 -0
  91. data/smoke/fib.rb +2 -2
  92. data/smoke/flow1.rb +1 -1
  93. data/smoke/flow10.rb +17 -0
  94. data/smoke/flow2.rb +1 -1
  95. data/smoke/flow3.rb +1 -1
  96. data/smoke/flow5.rb +1 -1
  97. data/smoke/flow6.rb +1 -1
  98. data/smoke/flow7.rb +1 -1
  99. data/smoke/flow8.rb +1 -1
  100. data/smoke/flow9.rb +1 -1
  101. data/smoke/function.rb +1 -1
  102. data/smoke/gvar.rb +1 -1
  103. data/smoke/gvar2.rb +1 -1
  104. data/smoke/hash-fetch.rb +3 -3
  105. data/smoke/included.rb +38 -0
  106. data/smoke/inheritance.rb +4 -4
  107. data/smoke/inherited.rb +26 -0
  108. data/smoke/initialize.rb +3 -2
  109. data/smoke/instance_eval.rb +2 -2
  110. data/smoke/instance_eval4.rb +12 -0
  111. data/smoke/int_times.rb +1 -1
  112. data/smoke/integer.rb +1 -1
  113. data/smoke/ivar.rb +3 -2
  114. data/smoke/ivar2.rb +2 -2
  115. data/smoke/ivar3.rb +2 -1
  116. data/smoke/ivar4.rb +1 -0
  117. data/smoke/kernel-class.rb +1 -1
  118. data/smoke/keyword4.rb +1 -1
  119. data/smoke/kwrest.rb +1 -0
  120. data/smoke/kwsplat1.rb +2 -2
  121. data/smoke/kwsplat2.rb +1 -1
  122. data/smoke/manual-rbs.rb +2 -1
  123. data/smoke/manual-rbs3.rb +1 -0
  124. data/smoke/method_in_branch.rb +1 -1
  125. data/smoke/method_missing.rb +4 -3
  126. data/smoke/module3.rb +1 -1
  127. data/smoke/module4.rb +1 -0
  128. data/smoke/module5.rb +1 -1
  129. data/smoke/module_function1.rb +3 -2
  130. data/smoke/module_function2.rb +3 -2
  131. data/smoke/multiple-include.rb +1 -0
  132. data/smoke/next1.rb +1 -1
  133. data/smoke/object-send1.rb +3 -3
  134. data/smoke/optional1.rb +1 -1
  135. data/smoke/optional2.rb +1 -1
  136. data/smoke/optional3.rb +1 -1
  137. data/smoke/parameterizedd-self.rb +2 -1
  138. data/smoke/prepend1.rb +33 -0
  139. data/smoke/prepend2.rb +10 -0
  140. data/smoke/prepend2.rbs +9 -0
  141. data/smoke/primitive_method.rb +19 -0
  142. data/smoke/proc4.rb +1 -1
  143. data/smoke/public.rb +4 -0
  144. data/smoke/range.rb +1 -1
  145. data/smoke/rbs-attr.rb +2 -2
  146. data/smoke/rbs-proc2.rb +1 -1
  147. data/smoke/rbs-proc3.rb +1 -1
  148. data/smoke/rbs-tyvar4.rb +3 -2
  149. data/smoke/rbs-tyvar6.rb +3 -3
  150. data/smoke/redo1.rb +1 -1
  151. data/smoke/redo2.rb +1 -1
  152. data/smoke/rescue1.rb +1 -1
  153. data/smoke/rescue2.rb +1 -1
  154. data/smoke/rescue3.rb +1 -0
  155. data/smoke/rescue4.rb +1 -1
  156. data/smoke/respond_to.rb +1 -1
  157. data/smoke/rest1.rb +2 -2
  158. data/smoke/rest2.rb +1 -1
  159. data/smoke/rest3.rb +6 -6
  160. data/smoke/rest4.rb +2 -2
  161. data/smoke/rest5.rb +1 -1
  162. data/smoke/rest6.rb +1 -1
  163. data/smoke/retry1.rb +2 -2
  164. data/smoke/simple.rb +12 -0
  165. data/smoke/step.rb +3 -3
  166. data/smoke/struct-keyword_init.rb +6 -16
  167. data/smoke/struct.rb +1 -1
  168. data/smoke/struct2.rb +1 -1
  169. data/smoke/struct3.rb +1 -1
  170. data/smoke/struct4.rb +1 -1
  171. data/smoke/struct5.rb +2 -2
  172. data/smoke/struct6.rb +2 -2
  173. data/smoke/struct7.rb +1 -1
  174. data/smoke/super1.rb +4 -4
  175. data/smoke/super3.rb +3 -2
  176. data/smoke/super4.rb +7 -5
  177. data/smoke/super5.rb +6 -4
  178. data/smoke/symbol-proc-attr.rb +1 -1
  179. data/smoke/tap1.rb +2 -2
  180. data/smoke/toplevel.rb +1 -1
  181. data/smoke/type_var.rb +3 -3
  182. data/smoke/user-demo.rb +1 -1
  183. data/smoke/wrong-extend.rb +1 -0
  184. data/smoke/wrong-include.rb +1 -0
  185. data/smoke/wrong-include2.rb +1 -1
  186. data/testbed/goodcheck-Gemfile.lock +1 -1
  187. data/typeprof.gemspec +1 -1
  188. metadata +24 -5
data/lib/typeprof/cli.rb CHANGED
@@ -20,6 +20,7 @@ module TypeProf
20
20
  options = {}
21
21
  dir_filter = nil
22
22
  gem_rbs_features = []
23
+ gem_repo_dirs = []
23
24
  show_version = false
24
25
  max_sec = max_iter = nil
25
26
 
@@ -31,6 +32,7 @@ module TypeProf
31
32
  opt.on("--version", "Display typeprof version") { show_version = true }
32
33
  opt.on("-I DIR", "Add DIR to the load/require path") {|v| $LOAD_PATH << v }
33
34
  opt.on("-r FEATURE", "Require RBS of the FEATURE gem") {|v| gem_rbs_features << v }
35
+ opt.on("--repo DIR", "Add DIR to the RBS repository") {|v| gem_repo_dirs << v }
34
36
 
35
37
  opt.separator ""
36
38
  opt.separator "Analysis output options:"
@@ -46,8 +48,12 @@ module TypeProf
46
48
  dir_filter ||= ConfigData::DEFAULT_DIR_FILTER
47
49
  dir_filter << [:exclude, File.expand_path(dir)]
48
50
  end
51
+ opt.on("--exclude-untyped", "Exclude (comment out) all entries including untyped") {|v| options[:exclude_untyped] = v }
52
+ opt.on("--[no-]show-typeprof-version", "Display TypeProf version in a header") {|v| options[:show_typeprof_version] = v }
49
53
  opt.on("--[no-]show-errors", "Display possible errors found during the analysis") {|v| options[:show_errors] = v }
50
54
  opt.on("--[no-]show-untyped", "Display \"Foo | untyped\" instead of \"Foo\"") {|v| options[:show_untyped] = v }
55
+ opt.on("--[no-]show-parameter-names", "Display parameter names for methods") {|v| options[:show_parameter_names] = v }
56
+ opt.on("--[no-]show-source-locations", "Display definition source locations for methods") {|v| options[:show_source_locations] = v }
51
57
 
52
58
  opt.separator ""
53
59
  opt.separator "Analysis limit options:"
@@ -85,6 +91,7 @@ module TypeProf
85
91
  rbs_files: rbs_files,
86
92
  output: output,
87
93
  gem_rbs_features: gem_rbs_features,
94
+ gem_repo_dirs: gem_repo_dirs,
88
95
  verbose: verbose,
89
96
  dir_filter: dir_filter,
90
97
  max_sec: max_sec,
@@ -6,6 +6,7 @@ module TypeProf
6
6
  :rbs_files,
7
7
  :output,
8
8
  :gem_rbs_features,
9
+ :gem_repo_dirs,
9
10
  :verbose,
10
11
  :dir_filter,
11
12
  :max_iter,
@@ -14,17 +15,29 @@ module TypeProf
14
15
  keyword_init: true
15
16
  )
16
17
 
18
+ class TypeProfError < StandardError
19
+ def report(output)
20
+ output.puts "# Analysis Error"
21
+ output.puts message
22
+ end
23
+ end
24
+
17
25
  class ConfigData
18
26
  def initialize(**opt)
19
27
  opt[:output] ||= $stdout
20
28
  opt[:gem_rbs_features] ||= []
29
+ opt[:gem_repo_dirs] ||= []
21
30
  opt[:dir_filter] ||= DEFAULT_DIR_FILTER
22
31
  opt[:verbose] ||= 0
23
32
  opt[:options] ||= {}
24
33
  opt[:options] = {
34
+ exclude_untyped: false,
35
+ show_typeprof_version: true,
25
36
  show_indicator: true,
26
37
  show_untyped: false,
27
38
  show_errors: false,
39
+ show_parameter_names: true,
40
+ show_source_locations: false,
28
41
  stub_execution: true,
29
42
  type_depth_limit: 5,
30
43
  stackprof: nil,
@@ -67,9 +80,19 @@ module TypeProf
67
80
  Import.import_library(scratch, feature)
68
81
  end
69
82
 
70
- prologue_ctx = Context.new(nil, nil, nil)
71
- prologue_ep = ExecutionPoint.new(prologue_ctx, -1, nil)
72
- prologue_env = Env.new(StaticEnv.new(:top, Type.nil, false, true), [], [], Utils::HashWrapper.new({}))
83
+ rbs_files = []
84
+ rbs_codes = []
85
+ Config.rbs_files.each do |rbs|
86
+ if rbs.is_a?(Array) # [String name, String content]
87
+ rbs_codes << rbs
88
+ else
89
+ rbs_files << rbs
90
+ end
91
+ end
92
+ Import.import_rbs_files(scratch, rbs_files)
93
+ rbs_codes.each do |name, content|
94
+ Import.import_rbs_code(scratch, name, content)
95
+ end
73
96
 
74
97
  Config.rb_files.each do |rb|
75
98
  if rb.is_a?(Array) # [String name, String content]
@@ -77,17 +100,7 @@ module TypeProf
77
100
  else
78
101
  iseq = ISeq.compile(rb)
79
102
  end
80
- ep, env = TypeProf.starting_state(iseq)
81
- scratch.merge_env(ep, env)
82
- scratch.add_callsite!(ep.ctx, prologue_ep, prologue_env) {|ty, ep| }
83
- end
84
-
85
- Config.rbs_files.each do |rbs|
86
- if rbs.is_a?(Array) # [String name, String content]
87
- Import.import_rbs_code(scratch, *rbs)
88
- else
89
- Import.import_rbs_file(scratch, rbs)
90
- end
103
+ scratch.add_entrypoint(iseq)
91
104
  end
92
105
 
93
106
  result = scratch.type_profile
@@ -100,6 +113,9 @@ module TypeProf
100
113
  end
101
114
  end
102
115
 
116
+ rescue TypeProfError => exc
117
+ exc.report(Config.output)
118
+
103
119
  ensure
104
120
  if Config.options[:stackprof] && defined?(StackProf)
105
121
  StackProf.stop
@@ -45,6 +45,12 @@ module TypeProf
45
45
  Type::Cell.new(Type::Cell::Elements.new([Type.bot] * klass.type_params.size), base_type)
46
46
  end
47
47
  end
48
+
49
+ def include_untyped?(scratch)
50
+ return true if @base_type.include_untyped?(scratch)
51
+ return true if @elems.include_untyped?(scratch)
52
+ false
53
+ end
48
54
  end
49
55
 
50
56
  # The most basic container type for default type parameter class
@@ -196,6 +202,10 @@ module TypeProf
196
202
  end
197
203
  Elements.new(elems)
198
204
  end
205
+
206
+ def include_untyped?(scratch)
207
+ return @elems.any? {|ty| ty.include_untyped?(scratch) }
208
+ end
199
209
  end
200
210
  end
201
211
 
@@ -531,6 +541,12 @@ module TypeProf
531
541
  return rest_ary_ty, following_tys
532
542
  end
533
543
  end
544
+
545
+ def include_untyped?(scratch)
546
+ return true if @lead_tys.any? {|ty| ty.include_untyped?(scratch) }
547
+ return true if @rest_ty.include_untyped?(scratch)
548
+ false
549
+ end
534
550
  end
535
551
  end
536
552
 
@@ -790,6 +806,14 @@ module TypeProf
790
806
  end
791
807
  kw_tys
792
808
  end
809
+
810
+ def include_untyped?(scratch)
811
+ @map_tys.each do |key, val|
812
+ return true if key.include_untyped?(scratch)
813
+ return true if val.include_untyped?(scratch)
814
+ end
815
+ false
816
+ end
793
817
  end
794
818
  end
795
819
 
@@ -26,6 +26,10 @@ module TypeProf
26
26
  end
27
27
 
28
28
  def show_message(terminated, output)
29
+ if Config.options[:show_typeprof_version]
30
+ output.puts "# TypeProf #{ VERSION }"
31
+ output.puts
32
+ end
29
33
  if terminated
30
34
  output.puts "# CAUTION: Type profiling was terminated prematurely because of the limitation"
31
35
  output.puts
@@ -94,6 +98,10 @@ module TypeProf
94
98
  if class_def.klass_obj.superclass != :__root__ && class_def.klass_obj.superclass
95
99
  omit = class_def.klass_obj.superclass == Type::Builtin[:obj] || class_def.klass_obj == Type::Builtin[:obj]
96
100
  superclass = omit ? nil : @scratch.get_class_name(class_def.klass_obj.superclass)
101
+ type_args = class_def.klass_obj.superclass_type_args
102
+ if type_args && !type_args.empty?
103
+ superclass += "[#{ type_args.map {|ty| ty.screen_name(@scratch) }.join(", ") }]"
104
+ end
97
105
  end
98
106
 
99
107
  @scratch.namespace = class_def.name
@@ -105,19 +113,20 @@ module TypeProf
105
113
  consts[name] = ty.screen_name(@scratch)
106
114
  end
107
115
 
108
- included_mods = class_def.modules[false].filter_map do |mod_def, _type_args, absolute_paths|
109
- next if absolute_paths.all? {|path| !path || Config.check_dir_filter(path) == :exclude }
110
- Type::Instance.new(mod_def.klass_obj).screen_name(@scratch)
111
- end
112
-
113
- extended_mods = class_def.modules[true].filter_map do |mod_def, _type_args, absolute_paths|
114
- next if absolute_paths.all? {|path| !path || Config.check_dir_filter(path) == :exclude }
115
- Type::Instance.new(mod_def.klass_obj).screen_name(@scratch)
116
+ modules = class_def.modules.to_h do |kind, mods|
117
+ mods = mods.to_h do |singleton, mods|
118
+ mods = mods.filter_map do |mod_def, _type_args, absolute_paths|
119
+ next if absolute_paths.all? {|path| !path || Config.check_dir_filter(path) == :exclude }
120
+ Type::Instance.new(mod_def.klass_obj).screen_name(@scratch)
121
+ end
122
+ [singleton, mods]
123
+ end
124
+ [kind, mods]
116
125
  end
117
126
 
118
- explicit_methods = {}
119
- iseq_methods = {}
120
- attr_methods = {}
127
+ visibilities = {}
128
+ source_locations = {}
129
+ methods = {}
121
130
  ivars = class_def.ivars.dump
122
131
  cvars = class_def.cvars.dump
123
132
 
@@ -128,36 +137,53 @@ module TypeProf
128
137
  ctxs = @iseq_method_to_ctxs[mdef]
129
138
  next unless ctxs
130
139
 
131
- ctxs.each do |ctx|
132
- next if mid != ctx.mid
133
- next if Config.check_dir_filter(ctx.iseq.absolute_path) == :exclude
140
+ ctx = ctxs.find {|ctx| ctx.mid == mid } || ctxs.first
134
141
 
135
- method_name = ctx.mid
136
- method_name = "self.#{ method_name }" if singleton
142
+ next if Config.check_dir_filter(ctx.iseq.absolute_path) == :exclude
137
143
 
138
- iseq_methods[method_name] ||= [true, []]
139
- iseq_methods[method_name][0] &&= mdef.pub_meth
140
- iseq_methods[method_name][1] << @scratch.show_method_signature(ctx)
144
+ method_name = mid
145
+ method_name = "self.#{ method_name }" if singleton
146
+
147
+ key = [:iseq, method_name]
148
+ visibilities[key] ||= mdef.pub_meth
149
+ source_locations[key] ||= ctx.iseq.source_location(0)
150
+ (methods[key] ||= []) << @scratch.show_method_signature(ctx)
151
+ when AliasMethodDef
152
+ alias_name, orig_name = mid, mdef.orig_mid
153
+ if singleton
154
+ alias_name = "self.#{ alias_name }"
155
+ orig_name = "self.#{ orig_name }"
141
156
  end
157
+ key = [:alias, alias_name]
158
+ visibilities[key] ||= mdef.pub_meth
159
+ source_locations[key] ||= mdef.def_ep&.source_location
160
+ methods[key] = orig_name
142
161
  when AttrMethodDef
143
- next if !mdef.absolute_path || Config.check_dir_filter(mdef.absolute_path) == :exclude
162
+ next if !mdef.def_ep
163
+ absolute_path = mdef.def_ep.ctx.iseq.absolute_path
164
+ next if !absolute_path || Config.check_dir_filter(absolute_path) == :exclude
144
165
  mid = mid.to_s[0..-2].to_sym if mid.to_s.end_with?("=")
145
166
  method_name = mid
146
167
  method_name = "self.#{ mid }" if singleton
147
168
  method_name = [method_name, :"@#{ mid }" != mdef.ivar]
148
- if attr_methods[method_name]
149
- if attr_methods[method_name][0] != mdef.kind
150
- attr_methods[method_name][0] = :accessor
169
+ key = [:attr, method_name]
170
+ visibilities[key] ||= mdef.pub_meth
171
+ source_locations[key] ||= mdef.def_ep.source_location
172
+ if methods[key]
173
+ if methods[key][0] != mdef.kind
174
+ methods[key][0] = :accessor
151
175
  end
152
176
  else
153
177
  entry = ivars[[singleton, mdef.ivar]]
154
178
  ty = entry ? entry.type : Type.any
155
- attr_methods[method_name] = [mdef.kind, ty.screen_name(@scratch)]
179
+ methods[key] = [mdef.kind, ty.screen_name(@scratch), ty.include_untyped?(@scratch)]
156
180
  end
157
181
  when TypedMethodDef
158
182
  if mdef.rbs_source
159
183
  method_name, sigs = mdef.rbs_source
160
- explicit_methods[method_name] = sigs
184
+ key = [:rbs, method_name]
185
+ methods[key] = sigs
186
+ visibilities[key] ||= mdef.pub_meth
161
187
  end
162
188
  end
163
189
  end
@@ -168,7 +194,7 @@ module TypeProf
168
194
  ty = entry.type
169
195
  next unless var.to_s.start_with?("@")
170
196
  var = "self.#{ var }" if singleton
171
- next if attr_methods[[singleton ? "self.#{ var.to_s[1..] }" : var.to_s[1..].to_sym, false]]
197
+ next if methods[[:attr, [singleton ? "self.#{ var.to_s[1..] }" : var.to_s[1..].to_sym, false]]]
172
198
  next if entry.rbs_declared
173
199
  [var, ty.screen_name(@scratch)]
174
200
  end.compact
@@ -180,7 +206,9 @@ module TypeProf
180
206
  end.compact
181
207
 
182
208
  if !class_def.absolute_path || Config.check_dir_filter(class_def.absolute_path) == :exclude
183
- return nil if consts.empty? && included_mods.empty? && extended_mods.empty? && ivars.empty? && cvars.empty? && iseq_methods.empty? && attr_methods.empty? && inner_classes.empty?
209
+ if methods.keys.all? {|type,| type == :rbs }
210
+ return nil if consts.empty? && modules[:before][true].empty? && modules[:before][false].empty? && modules[:after][true].empty? && modules[:after][false].empty? && ivars.empty? && cvars.empty? && inner_classes.empty?
211
+ end
184
212
  end
185
213
 
186
214
  @scratch.namespace = nil
@@ -190,18 +218,17 @@ module TypeProf
190
218
  name: class_def.name,
191
219
  superclass: superclass,
192
220
  consts: consts,
193
- included_mods: included_mods,
194
- extended_mods: extended_mods,
221
+ modules: modules,
195
222
  ivars: ivars,
196
223
  cvars: cvars,
197
- attr_methods: attr_methods,
198
- explicit_methods: explicit_methods,
199
- iseq_methods: iseq_methods,
224
+ methods: methods,
225
+ visibilities: visibilities,
226
+ source_locations: source_locations,
200
227
  inner_classes: inner_classes,
201
228
  )
202
229
  end
203
230
 
204
- ClassData = Struct.new(:kind, :name, :superclass, :consts, :included_mods, :extended_mods, :ivars, :cvars, :attr_methods, :explicit_methods, :iseq_methods, :inner_classes, keyword_init: true)
231
+ ClassData = Struct.new(:kind, :name, :superclass, :consts, :modules, :ivars, :cvars, :methods, :visibilities, :source_locations, :inner_classes, keyword_init: true)
205
232
 
206
233
  def show(stat_eps, output)
207
234
  # make the class hierarchy
@@ -218,7 +245,16 @@ module TypeProf
218
245
 
219
246
  output.puts "# Classes" # and Modules
220
247
 
221
- show_class_hierarchy(0, hierarchy, output, true)
248
+ prev_nil = true
249
+ show_class_hierarchy(0, hierarchy).each do |line|
250
+ if line == nil
251
+ output.puts line unless prev_nil
252
+ prev_nil = true
253
+ else
254
+ output.puts line
255
+ prev_nil = false
256
+ end
257
+ end
222
258
 
223
259
  if ENV["TP_STAT"]
224
260
  output.puts ""
@@ -246,13 +282,13 @@ module TypeProf
246
282
  end.compact
247
283
  end
248
284
 
249
- def show_class_hierarchy(depth, hierarchy, output, first)
285
+ def show_class_hierarchy(depth, hierarchy)
286
+ lines = []
250
287
  hierarchy.each do |class_data|
251
- output.puts unless first
252
- first = false
253
-
254
- show_class_data(depth, class_data, output)
288
+ lines << nil
289
+ lines.concat show_class_data(depth, class_data)
255
290
  end
291
+ lines
256
292
  end
257
293
 
258
294
  def show_const(namespace, path)
@@ -262,53 +298,77 @@ module TypeProf
262
298
  path[i..].join("::")
263
299
  end
264
300
 
265
- def show_class_data(depth, class_data, output)
301
+ def show_class_data(depth, class_data)
266
302
  indent = " " * depth
267
303
  name = class_data.name.last
268
304
  superclass = " < " + class_data.superclass if class_data.superclass
269
- output.puts indent + "#{ class_data.kind } #{ name }#{ superclass }"
270
- first = true
305
+ first_line = indent + "#{ class_data.kind } #{ name }#{ superclass }"
306
+ lines = []
271
307
  class_data.consts.each do |name, ty|
272
- output.puts indent + " #{ name }: #{ ty }"
273
- first = false
274
- end
275
- class_data.included_mods.sort.each do |mod|
276
- output.puts indent + " include #{ mod }"
277
- first = false
308
+ lines << (indent + " #{ name }: #{ ty }")
278
309
  end
279
- class_data.extended_mods.sort.each do |mod|
280
- output.puts indent + " extend #{ mod }"
281
- first = false
310
+ class_data.modules.each do |kind, mods|
311
+ mods.each do |singleton, mods|
312
+ case
313
+ when kind == :before && singleton then directive = nil
314
+ when kind == :before && !singleton then directive = "prepend"
315
+ when kind == :after && singleton then directive = "extend"
316
+ when kind == :after && !singleton then directive = "include"
317
+ end
318
+ mods.each do |mod|
319
+ lines << (indent + " #{ directive } #{ mod }") if directive
320
+ end
321
+ end
282
322
  end
283
323
  class_data.ivars.each do |var, ty|
284
- output.puts indent + " #{ var }: #{ ty }" unless var.start_with?("_")
285
- first = false
324
+ lines << (indent + " #{ var }: #{ ty }") unless var.start_with?("_")
286
325
  end
287
326
  class_data.cvars.each do |var, ty|
288
- output.puts indent + " #{ var }: #{ ty }"
289
- first = false
327
+ lines << (indent + " #{ var }: #{ ty }")
290
328
  end
291
- class_data.attr_methods.each do |(method_name, hidden), (kind, ty)|
292
- output.puts indent + " attr_#{ kind } #{ method_name }#{ hidden ? "()" : "" }: #{ ty }"
293
- first = false
294
- end
295
- class_data.explicit_methods.each do |method_name, sigs|
296
- sigs = sigs.sort.join("\n" + indent + "#" + " " * (method_name.size + 6) + "| ")
297
- output.puts indent + "# def #{ method_name }: #{ sigs }"
298
- first = false
299
- end
300
- prev_pub_meth = true
301
- class_data.iseq_methods.each do |method_name, (pub_meth, sigs)|
302
- sigs = sigs.sort.join("\n" + indent + " " * (method_name.size + 7) + "| ")
303
- if prev_pub_meth != pub_meth
304
- output.puts indent + " #{ pub_meth ? "public" : "private" }"
305
- prev_pub_meth = pub_meth
329
+ lines << nil
330
+ prev_vis = true
331
+ class_data.methods.each do |key, arg|
332
+ vis = class_data.visibilities[key]
333
+ if prev_vis != vis
334
+ lines << nil
335
+ lines << (indent + " #{ vis ? "public" : "private" }")
336
+ prev_vis = vis
337
+ end
338
+ source_location = class_data.source_locations[key]
339
+ if Config.options[:show_source_locations] && source_location
340
+ lines << nil
341
+ lines << (indent + " # #{ source_location }")
342
+ end
343
+ type, (method_name, hidden) = key
344
+ case type
345
+ when :attr
346
+ kind, ty, untyped = *arg
347
+ exclude = Config.options[:exclude_untyped] && untyped ? "#" : " " # XXX
348
+ lines << (indent + "#{ exclude } attr_#{ kind } #{ method_name }#{ hidden ? "()" : "" }: #{ ty }")
349
+ when :rbs
350
+ sigs = arg.sort.join("\n" + indent + "#" + " " * (method_name.size + 5) + "| ")
351
+ lines << (indent + "# def #{ method_name }: #{ sigs }")
352
+ when :iseq
353
+ sigs = []
354
+ untyped = false
355
+ arg.each do |sig, untyped0|
356
+ sigs << sig
357
+ untyped ||= untyped0
358
+ end
359
+ sigs = sigs.sort.join("\n" + indent + " " * (method_name.size + 6) + "| ")
360
+ exclude = Config.options[:exclude_untyped] && untyped ? "#" : " " # XXX
361
+ lines << (indent + "#{ exclude } def #{ method_name }: #{ sigs }")
362
+ when :alias
363
+ orig_name = arg
364
+ lines << (indent + " alias #{ method_name } #{ orig_name }")
306
365
  end
307
- output.puts indent + " def #{ method_name }: #{ sigs }"
308
- first = false
309
366
  end
310
- show_class_hierarchy(depth + 1, class_data.inner_classes, output, first)
311
- output.puts indent + "end"
367
+ lines.concat show_class_hierarchy(depth + 1, class_data.inner_classes)
368
+ lines.shift until lines.empty? || lines.first
369
+ lines.pop until lines.empty? || lines.last
370
+ lines.unshift first_line
371
+ lines << (indent + "end")
312
372
  end
313
373
  end
314
374
  end