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.
Files changed (189) 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 +201 -104
  7. data/lib/typeprof/block.rb +39 -4
  8. data/lib/typeprof/builtin.rb +217 -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 +116 -41
  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/break4.rb +17 -0
  64. data/smoke/class_eval.rb +22 -0
  65. data/smoke/class_method.rb +2 -2
  66. data/smoke/class_method2.rb +2 -2
  67. data/smoke/constant2.rb +3 -2
  68. data/smoke/context-sensitive1.rb +1 -1
  69. data/smoke/cvar.rb +3 -2
  70. data/smoke/define_method.rb +2 -2
  71. data/smoke/define_method3.rb +1 -0
  72. data/smoke/define_method4.rb +1 -1
  73. data/smoke/define_method6.rb +19 -0
  74. data/smoke/define_method7.rb +18 -0
  75. data/smoke/demo.rb +6 -6
  76. data/smoke/demo1.rb +1 -1
  77. data/smoke/demo11.rb +1 -1
  78. data/smoke/demo2.rb +1 -1
  79. data/smoke/demo3.rb +1 -1
  80. data/smoke/demo4.rb +3 -3
  81. data/smoke/demo5.rb +1 -1
  82. data/smoke/demo6.rb +2 -1
  83. data/smoke/demo7.rb +1 -1
  84. data/smoke/demo9.rb +1 -0
  85. data/smoke/dummy-execution1.rb +1 -1
  86. data/smoke/dummy-execution2.rb +1 -1
  87. data/smoke/dummy_element.rb +1 -1
  88. data/smoke/ensure1.rb +1 -1
  89. data/smoke/enum_for.rb +15 -0
  90. data/smoke/enum_for2.rb +17 -0
  91. data/smoke/extended.rb +38 -0
  92. data/smoke/fib.rb +2 -2
  93. data/smoke/flow1.rb +1 -1
  94. data/smoke/flow10.rb +17 -0
  95. data/smoke/flow11.rb +17 -0
  96. data/smoke/flow2.rb +1 -1
  97. data/smoke/flow3.rb +1 -1
  98. data/smoke/flow5.rb +1 -1
  99. data/smoke/flow6.rb +1 -1
  100. data/smoke/flow7.rb +1 -1
  101. data/smoke/flow8.rb +1 -1
  102. data/smoke/flow9.rb +1 -1
  103. data/smoke/function.rb +1 -1
  104. data/smoke/gvar.rb +1 -1
  105. data/smoke/gvar2.rb +1 -1
  106. data/smoke/hash-fetch.rb +3 -3
  107. data/smoke/included.rb +38 -0
  108. data/smoke/inheritance.rb +4 -4
  109. data/smoke/inherited.rb +26 -0
  110. data/smoke/initialize.rb +3 -2
  111. data/smoke/instance_eval.rb +2 -2
  112. data/smoke/instance_eval4.rb +12 -0
  113. data/smoke/int_times.rb +1 -1
  114. data/smoke/integer.rb +1 -1
  115. data/smoke/ivar.rb +3 -2
  116. data/smoke/ivar2.rb +2 -2
  117. data/smoke/ivar3.rb +2 -1
  118. data/smoke/ivar4.rb +1 -0
  119. data/smoke/kernel-class.rb +1 -1
  120. data/smoke/keyword4.rb +1 -1
  121. data/smoke/kwrest.rb +1 -0
  122. data/smoke/kwsplat1.rb +2 -2
  123. data/smoke/kwsplat2.rb +1 -1
  124. data/smoke/manual-rbs.rb +1 -0
  125. data/smoke/manual-rbs3.rb +1 -0
  126. data/smoke/method_missing.rb +4 -3
  127. data/smoke/module3.rb +1 -1
  128. data/smoke/module4.rb +1 -0
  129. data/smoke/module5.rb +1 -1
  130. data/smoke/module_function1.rb +3 -2
  131. data/smoke/module_function2.rb +3 -2
  132. data/smoke/multiple-include.rb +1 -0
  133. data/smoke/next1.rb +1 -1
  134. data/smoke/object-send1.rb +3 -3
  135. data/smoke/optional1.rb +1 -1
  136. data/smoke/optional2.rb +1 -1
  137. data/smoke/optional3.rb +1 -1
  138. data/smoke/parameterizedd-self.rb +2 -1
  139. data/smoke/prepend1.rb +33 -0
  140. data/smoke/prepend2.rb +10 -0
  141. data/smoke/prepend2.rbs +9 -0
  142. data/smoke/primitive_method.rb +19 -0
  143. data/smoke/proc4.rb +1 -1
  144. data/smoke/public.rb +4 -0
  145. data/smoke/range.rb +1 -1
  146. data/smoke/rbs-attr.rb +2 -2
  147. data/smoke/rbs-proc2.rb +1 -1
  148. data/smoke/rbs-proc3.rb +1 -1
  149. data/smoke/rbs-tyvar4.rb +3 -2
  150. data/smoke/rbs-tyvar6.rb +3 -3
  151. data/smoke/redo1.rb +1 -1
  152. data/smoke/redo2.rb +1 -1
  153. data/smoke/rescue1.rb +1 -1
  154. data/smoke/rescue2.rb +1 -1
  155. data/smoke/rescue3.rb +1 -0
  156. data/smoke/rescue4.rb +1 -1
  157. data/smoke/respond_to.rb +1 -1
  158. data/smoke/rest1.rb +2 -2
  159. data/smoke/rest2.rb +1 -1
  160. data/smoke/rest3.rb +6 -6
  161. data/smoke/rest4.rb +2 -2
  162. data/smoke/rest5.rb +1 -1
  163. data/smoke/rest6.rb +1 -1
  164. data/smoke/retry1.rb +2 -2
  165. data/smoke/simple.rb +1 -1
  166. data/smoke/step.rb +3 -3
  167. data/smoke/struct-keyword_init.rb +6 -16
  168. data/smoke/struct.rb +1 -1
  169. data/smoke/struct2.rb +1 -1
  170. data/smoke/struct3.rb +1 -1
  171. data/smoke/struct4.rb +1 -1
  172. data/smoke/struct5.rb +2 -2
  173. data/smoke/struct6.rb +2 -2
  174. data/smoke/struct7.rb +1 -1
  175. data/smoke/super1.rb +4 -4
  176. data/smoke/super3.rb +3 -2
  177. data/smoke/super4.rb +7 -5
  178. data/smoke/super5.rb +6 -4
  179. data/smoke/symbol-proc-attr.rb +1 -1
  180. data/smoke/tap1.rb +2 -2
  181. data/smoke/toplevel.rb +1 -1
  182. data/smoke/type_var.rb +3 -3
  183. data/smoke/user-demo.rb +1 -1
  184. data/smoke/wrong-extend.rb +1 -0
  185. data/smoke/wrong-include.rb +1 -0
  186. data/smoke/wrong-include2.rb +1 -1
  187. data/testbed/goodcheck-Gemfile.lock +1 -1
  188. data/typeprof.gemspec +1 -1
  189. metadata +25 -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(TopStaticEnv.new, [], [], 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 + 5) + "| ")
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 + 6) + "| ")
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