steep 0.40.0 → 0.44.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 (169) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +38 -0
  3. data/Gemfile +1 -0
  4. data/bin/output_rebaseline.rb +15 -30
  5. data/bin/output_test.rb +23 -57
  6. data/lib/steep.rb +89 -15
  7. data/lib/steep/annotation_parser.rb +10 -2
  8. data/lib/steep/ast/types/class.rb +4 -0
  9. data/lib/steep/cli.rb +31 -6
  10. data/lib/steep/diagnostic/ruby.rb +13 -8
  11. data/lib/steep/diagnostic/signature.rb +152 -2
  12. data/lib/steep/drivers/annotations.rb +18 -36
  13. data/lib/steep/drivers/check.rb +140 -31
  14. data/lib/steep/drivers/diagnostic_printer.rb +20 -11
  15. data/lib/steep/drivers/langserver.rb +4 -8
  16. data/lib/steep/drivers/print_project.rb +10 -9
  17. data/lib/steep/drivers/stats.rb +135 -119
  18. data/lib/steep/drivers/utils/driver_helper.rb +35 -0
  19. data/lib/steep/drivers/utils/jobs_count.rb +9 -0
  20. data/lib/steep/drivers/validate.rb +29 -18
  21. data/lib/steep/drivers/watch.rb +55 -49
  22. data/lib/steep/drivers/worker.rb +11 -8
  23. data/lib/steep/expectations.rb +159 -0
  24. data/lib/steep/index/signature_symbol_provider.rb +23 -1
  25. data/lib/steep/index/source_index.rb +55 -5
  26. data/lib/steep/interface/block.rb +4 -0
  27. data/lib/steep/project.rb +0 -30
  28. data/lib/steep/project/dsl.rb +5 -3
  29. data/lib/steep/project/pattern.rb +56 -0
  30. data/lib/steep/project/target.rb +11 -227
  31. data/lib/steep/server/base_worker.rb +1 -3
  32. data/lib/steep/server/change_buffer.rb +63 -0
  33. data/lib/steep/server/interaction_worker.rb +72 -57
  34. data/lib/steep/server/master.rb +652 -234
  35. data/lib/steep/server/type_check_worker.rb +304 -0
  36. data/lib/steep/server/worker_process.rb +16 -11
  37. data/lib/steep/{project → services}/completion_provider.rb +5 -5
  38. data/lib/steep/services/content_change.rb +61 -0
  39. data/lib/steep/services/file_loader.rb +48 -0
  40. data/lib/steep/services/goto_service.rb +321 -0
  41. data/lib/steep/{project → services}/hover_content.rb +19 -20
  42. data/lib/steep/services/path_assignment.rb +27 -0
  43. data/lib/steep/services/signature_service.rb +403 -0
  44. data/lib/steep/services/stats_calculator.rb +69 -0
  45. data/lib/steep/services/type_check_service.rb +413 -0
  46. data/lib/steep/signature/validator.rb +187 -85
  47. data/lib/steep/source.rb +21 -18
  48. data/lib/steep/subtyping/check.rb +246 -45
  49. data/lib/steep/subtyping/constraints.rb +4 -4
  50. data/lib/steep/type_construction.rb +428 -193
  51. data/lib/steep/type_inference/block_params.rb +1 -1
  52. data/lib/steep/type_inference/context.rb +22 -0
  53. data/lib/steep/type_inference/local_variable_type_env.rb +26 -12
  54. data/lib/steep/type_inference/logic.rb +1 -1
  55. data/lib/steep/type_inference/logic_type_interpreter.rb +4 -4
  56. data/lib/steep/type_inference/type_env.rb +43 -17
  57. data/lib/steep/version.rb +1 -1
  58. data/smoke/alias/test_expectations.yml +96 -0
  59. data/smoke/and/test_expectations.yml +31 -0
  60. data/smoke/array/test_expectations.yml +103 -0
  61. data/smoke/block/test_expectations.yml +125 -0
  62. data/smoke/case/test_expectations.yml +47 -0
  63. data/smoke/class/test_expectations.yml +120 -0
  64. data/smoke/const/test_expectations.yml +129 -0
  65. data/smoke/diagnostics-rbs-duplicated/test_expectations.yml +13 -0
  66. data/smoke/diagnostics-rbs/Steepfile +7 -4
  67. data/smoke/diagnostics-rbs/test_expectations.yml +231 -0
  68. data/smoke/diagnostics-rbs/unknown-type-name-2.rbs +5 -0
  69. data/smoke/{broken → diagnostics-ruby-unsat}/Steepfile +0 -0
  70. data/smoke/diagnostics-ruby-unsat/a.rbs +3 -0
  71. data/smoke/diagnostics-ruby-unsat/test_expectations.yml +27 -0
  72. data/smoke/{diagnostics → diagnostics-ruby-unsat}/unsatisfiable_constraint.rb +0 -1
  73. data/smoke/diagnostics/a.rbs +0 -4
  74. data/smoke/diagnostics/test_expectations.yml +451 -0
  75. data/smoke/dstr/test_expectations.yml +13 -0
  76. data/smoke/ensure/test_expectations.yml +62 -0
  77. data/smoke/enumerator/test_expectations.yml +135 -0
  78. data/smoke/extension/f.rb +2 -0
  79. data/smoke/extension/f.rbs +3 -0
  80. data/smoke/extension/test_expectations.yml +73 -0
  81. data/smoke/hash/test_expectations.yml +81 -0
  82. data/smoke/hello/test_expectations.yml +25 -0
  83. data/smoke/if/test_expectations.yml +34 -0
  84. data/smoke/implements/b.rb +13 -0
  85. data/smoke/implements/b.rbs +12 -0
  86. data/smoke/implements/test_expectations.yml +23 -0
  87. data/smoke/initialize/test_expectations.yml +1 -0
  88. data/smoke/integer/test_expectations.yml +101 -0
  89. data/smoke/interface/test_expectations.yml +23 -0
  90. data/smoke/kwbegin/test_expectations.yml +17 -0
  91. data/smoke/lambda/test_expectations.yml +39 -0
  92. data/smoke/literal/test_expectations.yml +106 -0
  93. data/smoke/map/test_expectations.yml +1 -0
  94. data/smoke/method/test_expectations.yml +90 -0
  95. data/smoke/module/test_expectations.yml +75 -0
  96. data/smoke/regexp/test_expectations.yml +615 -0
  97. data/smoke/regression/issue_328.rb +1 -0
  98. data/smoke/regression/issue_328.rbs +0 -0
  99. data/smoke/regression/issue_332.rb +11 -0
  100. data/smoke/regression/issue_332.rbs +19 -0
  101. data/smoke/regression/issue_372.rb +8 -0
  102. data/smoke/regression/issue_372.rbs +4 -0
  103. data/smoke/regression/masgn.rb +4 -0
  104. data/smoke/regression/test_expectations.yml +60 -0
  105. data/smoke/regression/thread.rb +7 -0
  106. data/smoke/rescue/test_expectations.yml +79 -0
  107. data/smoke/self/test_expectations.yml +23 -0
  108. data/smoke/skip/test_expectations.yml +23 -0
  109. data/smoke/stdout/test_expectations.yml +1 -0
  110. data/smoke/super/test_expectations.yml +69 -0
  111. data/smoke/toplevel/test_expectations.yml +15 -0
  112. data/smoke/tsort/Steepfile +2 -0
  113. data/smoke/tsort/test_expectations.yml +63 -0
  114. data/smoke/type_case/test_expectations.yml +48 -0
  115. data/smoke/unexpected/Steepfile +5 -0
  116. data/smoke/unexpected/test_expectations.yml +25 -0
  117. data/smoke/unexpected/unexpected.rb +1 -0
  118. data/smoke/unexpected/unexpected.rbs +3 -0
  119. data/smoke/yield/test_expectations.yml +68 -0
  120. data/steep.gemspec +4 -3
  121. metadata +127 -80
  122. data/lib/steep/project/file_loader.rb +0 -68
  123. data/lib/steep/project/signature_file.rb +0 -39
  124. data/lib/steep/project/source_file.rb +0 -129
  125. data/lib/steep/project/stats_calculator.rb +0 -80
  126. data/lib/steep/server/code_worker.rb +0 -150
  127. data/lib/steep/server/signature_worker.rb +0 -157
  128. data/lib/steep/server/utils.rb +0 -69
  129. data/smoke/alias/test.yaml +0 -73
  130. data/smoke/and/test.yaml +0 -24
  131. data/smoke/array/test.yaml +0 -80
  132. data/smoke/block/test.yaml +0 -96
  133. data/smoke/broken/broken.rb +0 -0
  134. data/smoke/broken/broken.rbs +0 -0
  135. data/smoke/broken/test.yaml +0 -6
  136. data/smoke/case/test.yaml +0 -36
  137. data/smoke/class/test.yaml +0 -89
  138. data/smoke/const/test.yaml +0 -96
  139. data/smoke/diagnostics-rbs-duplicated/test.yaml +0 -10
  140. data/smoke/diagnostics-rbs/test.yaml +0 -142
  141. data/smoke/diagnostics/test.yaml +0 -333
  142. data/smoke/dstr/test.yaml +0 -10
  143. data/smoke/ensure/test.yaml +0 -47
  144. data/smoke/enumerator/test.yaml +0 -100
  145. data/smoke/extension/test.yaml +0 -50
  146. data/smoke/hash/test.yaml +0 -62
  147. data/smoke/hello/test.yaml +0 -18
  148. data/smoke/if/test.yaml +0 -27
  149. data/smoke/implements/test.yaml +0 -16
  150. data/smoke/initialize/test.yaml +0 -4
  151. data/smoke/integer/test.yaml +0 -66
  152. data/smoke/interface/test.yaml +0 -16
  153. data/smoke/kwbegin/test.yaml +0 -14
  154. data/smoke/lambda/test.yaml +0 -28
  155. data/smoke/literal/test.yaml +0 -79
  156. data/smoke/map/test.yaml +0 -4
  157. data/smoke/method/test.yaml +0 -71
  158. data/smoke/module/test.yaml +0 -51
  159. data/smoke/regexp/test.yaml +0 -372
  160. data/smoke/regression/test.yaml +0 -38
  161. data/smoke/rescue/test.yaml +0 -60
  162. data/smoke/self/test.yaml +0 -16
  163. data/smoke/skip/test.yaml +0 -16
  164. data/smoke/stdout/test.yaml +0 -4
  165. data/smoke/super/test.yaml +0 -52
  166. data/smoke/toplevel/test.yaml +0 -12
  167. data/smoke/tsort/test.yaml +0 -32
  168. data/smoke/type_case/test.yaml +0 -33
  169. data/smoke/yield/test.yaml +0 -49
@@ -0,0 +1,403 @@
1
+ module Steep
2
+ module Services
3
+ class SignatureService
4
+ attr_reader :status
5
+
6
+ class SyntaxErrorStatus
7
+ attr_reader :files, :changed_paths, :diagnostics, :last_builder
8
+
9
+ def initialize(files:, changed_paths:, diagnostics:, last_builder:)
10
+ @files = files
11
+ @changed_paths = changed_paths
12
+ @diagnostics = diagnostics
13
+ @last_builder = last_builder
14
+ end
15
+
16
+ def rbs_index
17
+ @rbs_index ||= Index::RBSIndex.new().tap do |index|
18
+ builder = Index::RBSIndex::Builder.new(index: index)
19
+ builder.env(last_builder.env)
20
+ end
21
+ end
22
+ end
23
+
24
+ class AncestorErrorStatus
25
+ attr_reader :files, :changed_paths, :diagnostics, :last_builder
26
+
27
+ def initialize(files:, changed_paths:, diagnostics:, last_builder:)
28
+ @files = files
29
+ @changed_paths = changed_paths
30
+ @diagnostics = diagnostics
31
+ @last_builder = last_builder
32
+ end
33
+
34
+ def rbs_index
35
+ @rbs_index ||= Index::RBSIndex.new().tap do |index|
36
+ builder = Index::RBSIndex::Builder.new(index: index)
37
+ builder.env(last_builder.env)
38
+ end
39
+ end
40
+ end
41
+
42
+ class LoadedStatus
43
+ attr_reader :files, :builder
44
+
45
+ def initialize(files:, builder:)
46
+ @files = files
47
+ @builder = builder
48
+ end
49
+
50
+ def subtyping
51
+ @subtyping ||= Subtyping::Check.new(factory: AST::Types::Factory.new(builder: builder))
52
+ end
53
+
54
+ def rbs_index
55
+ @rbs_index ||= Index::RBSIndex.new().tap do |index|
56
+ builder = Index::RBSIndex::Builder.new(index: index)
57
+ builder.env(self.builder.env)
58
+ end
59
+ end
60
+ end
61
+
62
+ FileStatus = Struct.new(:path, :content, :decls, keyword_init: true)
63
+
64
+ def initialize(env:)
65
+ builder = RBS::DefinitionBuilder.new(env: env)
66
+ @status = LoadedStatus.new(builder: builder, files: {})
67
+ end
68
+
69
+ def self.load_from(loader)
70
+ env = RBS::Environment.from_loader(loader).resolve_type_names
71
+ new(env: env)
72
+ end
73
+
74
+ def env_rbs_paths
75
+ @env_rbs_paths ||= latest_env.buffers.each.with_object(Set[]) do |buffer, set|
76
+ set << Pathname(buffer.name)
77
+ end
78
+ end
79
+
80
+ def each_rbs_path(&block)
81
+ if block
82
+ env_rbs_paths.each do |path|
83
+ unless files.key?(path)
84
+ yield path
85
+ end
86
+ end
87
+
88
+ files.each_key(&block)
89
+ else
90
+ enum_for :each_rbs_path
91
+ end
92
+ end
93
+
94
+ def files
95
+ status.files
96
+ end
97
+
98
+ def pending_changed_paths
99
+ case status
100
+ when LoadedStatus
101
+ Set[]
102
+ when SyntaxErrorStatus, AncestorErrorStatus
103
+ Set.new(status.changed_paths)
104
+ end
105
+ end
106
+
107
+ def latest_env
108
+ latest_builder.env
109
+ end
110
+
111
+ def latest_builder
112
+ case status
113
+ when LoadedStatus
114
+ status.builder
115
+ when SyntaxErrorStatus, AncestorErrorStatus
116
+ status.last_builder
117
+ end
118
+ end
119
+
120
+ def latest_rbs_index
121
+ status.rbs_index
122
+ end
123
+
124
+ def current_subtyping
125
+ if status.is_a?(LoadedStatus)
126
+ status.subtyping
127
+ end
128
+ end
129
+
130
+ def apply_changes(files, changes)
131
+ Steep.logger.tagged "#apply_changes" do
132
+ Steep.measure2 "Applying change" do |sampler|
133
+ changes.each.with_object({}) do |(path, cs), update|
134
+ sampler.sample "#{path}" do
135
+ old_text = files[path]&.content
136
+ content = cs.inject(old_text || "") {|text, change| change.apply_to(text) }
137
+
138
+ buffer = RBS::Buffer.new(name: path, content: content)
139
+
140
+ update[path] = begin
141
+ FileStatus.new(path: path, content: content, decls: RBS::Parser.parse_signature(buffer))
142
+ rescue ArgumentError => exn
143
+ error = Diagnostic::Signature::UnexpectedError.new(
144
+ message: exn.message,
145
+ location: RBS::Location.new(buffer: buffer, start_pos: 0, end_pos: content.size)
146
+ )
147
+ FileStatus.new(path: path, content: content, decls: error)
148
+ rescue RBS::ParsingError => exn
149
+ FileStatus.new(path: path, content: content, decls: exn)
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
156
+
157
+ def update(changes)
158
+ Steep.logger.tagged "#update" do
159
+ updates = apply_changes(files, changes)
160
+ paths = Set.new(updates.each_key)
161
+ paths.merge(pending_changed_paths)
162
+
163
+ if updates.each_value.any? {|file| !file.decls.is_a?(Array) }
164
+ diagnostics = []
165
+
166
+ updates.each_value do |file|
167
+ unless file.decls.is_a?(Array)
168
+ diagnostic = if file.decls.is_a?(Diagnostic::Signature::Base)
169
+ file.decls
170
+ else
171
+ # factory is not used here because the error is a syntax error.
172
+ Diagnostic::Signature.from_rbs_error(file.decls, factory: nil)
173
+ end
174
+ diagnostics << diagnostic
175
+ end
176
+ end
177
+
178
+ @status = SyntaxErrorStatus.new(
179
+ files: self.files.merge(updates),
180
+ diagnostics: diagnostics,
181
+ last_builder: latest_builder,
182
+ changed_paths: paths
183
+ )
184
+ else
185
+ files = self.files.merge(updates)
186
+ updated_files = paths.each.with_object({}) do |path, hash|
187
+ hash[path] = files[path]
188
+ end
189
+ result =
190
+ Steep.measure "#update_env with updated #{paths.size} files" do
191
+ update_env(updated_files, paths: paths)
192
+ end
193
+
194
+ @status = case result
195
+ when Array
196
+ AncestorErrorStatus.new(
197
+ changed_paths: paths,
198
+ last_builder: latest_builder,
199
+ diagnostics: result,
200
+ files: files
201
+ )
202
+ when RBS::DefinitionBuilder::AncestorBuilder
203
+ builder2 = update_builder(ancestor_builder: result, paths: paths)
204
+ LoadedStatus.new(builder: builder2, files: files)
205
+ end
206
+ end
207
+ end
208
+ end
209
+
210
+ def update_env(updated_files, paths:)
211
+ Steep.logger.tagged "#update_env" do
212
+ errors = []
213
+ new_decls = Set[].compare_by_identity
214
+
215
+ env =
216
+ Steep.measure "Deleting out of date decls" do
217
+ latest_env.reject do |decl|
218
+ if decl.location
219
+ paths.include?(decl.location.buffer.name)
220
+ end
221
+ end
222
+ end
223
+
224
+ Steep.measure "Loading new decls" do
225
+ updated_files.each_value do |content|
226
+ case decls = content.decls
227
+ when RBS::ErrorBase
228
+ errors << content.decls
229
+ else
230
+ begin
231
+ content.decls.each do |decl|
232
+ env << decl
233
+ new_decls << decl
234
+ end
235
+ rescue RBS::LoadingError => exn
236
+ errors << exn
237
+ end
238
+ end
239
+ end
240
+ end
241
+
242
+ Steep.measure "validate type params" do
243
+ begin
244
+ env.validate_type_params
245
+ rescue RBS::LoadingError => exn
246
+ errors << exn
247
+ end
248
+ end
249
+
250
+ unless errors.empty?
251
+ return errors.map {|error|
252
+ # Factory will not be used because of the possible error types.
253
+ Diagnostic::Signature.from_rbs_error(error, factory: nil)
254
+ }
255
+ end
256
+
257
+ Steep.measure "resolve type names with #{new_decls.size} top-level decls" do
258
+ env = env.resolve_type_names(only: new_decls)
259
+ end
260
+
261
+ builder = RBS::DefinitionBuilder::AncestorBuilder.new(env: env)
262
+
263
+ Steep.measure("Pre-loading one ancestors") do
264
+ builder.env.class_decls.each_key do |type_name|
265
+ rescue_rbs_error(errors) { builder.one_instance_ancestors(type_name) }
266
+ rescue_rbs_error(errors) { builder.one_singleton_ancestors(type_name) }
267
+ end
268
+ builder.env.interface_decls.each_key do |type_name|
269
+ rescue_rbs_error(errors) { builder.one_interface_ancestors(type_name) }
270
+ end
271
+ end
272
+
273
+ unless errors.empty?
274
+ # Builder won't be used.
275
+ factory = AST::Types::Factory.new(builder: nil)
276
+ return errors.map {|error| Diagnostic::Signature.from_rbs_error(error, factory: factory) }
277
+ end
278
+
279
+ builder
280
+ end
281
+ end
282
+
283
+ def rescue_rbs_error(errors)
284
+ begin
285
+ yield
286
+ rescue RBS::ErrorBase => exn
287
+ errors << exn
288
+ end
289
+ end
290
+
291
+ def update_builder(ancestor_builder:, paths:)
292
+ Steep.measure "#update_builder with #{paths.size} files" do
293
+ changed_names = Set[]
294
+
295
+ old_definition_builder = latest_builder
296
+ old_env = old_definition_builder.env
297
+ old_names = type_names(paths: paths, env: old_env)
298
+ old_ancestor_builder = old_definition_builder.ancestor_builder
299
+ old_graph = RBS::AncestorGraph.new(env: old_env, ancestor_builder: old_ancestor_builder)
300
+ add_descendants(graph: old_graph, names: old_names, set: changed_names)
301
+ add_nested_decls(env: old_env, names: old_names, set: changed_names)
302
+
303
+ new_env = ancestor_builder.env
304
+ new_ancestor_builder = ancestor_builder
305
+ new_names = type_names(paths: paths, env: new_env)
306
+ new_graph = RBS::AncestorGraph.new(env: new_env, ancestor_builder: new_ancestor_builder)
307
+ add_descendants(graph: new_graph, names: new_names, set: changed_names)
308
+ add_nested_decls(env: new_env, names: new_names, set: changed_names)
309
+
310
+ old_definition_builder.update(
311
+ env: new_env,
312
+ ancestor_builder: new_ancestor_builder,
313
+ except: changed_names
314
+ )
315
+ end
316
+ end
317
+
318
+ def type_names(paths:, env:)
319
+ env.declarations.each.with_object(Set[]) do |decl, set|
320
+ if decl.location
321
+ if paths.include?(Pathname(decl.location.buffer.name))
322
+ type_name_from_decl(decl, set: set)
323
+ end
324
+ end
325
+ end
326
+ end
327
+
328
+ def const_decls(paths:, env:)
329
+ env.constant_decls.filter do |_, entry|
330
+ if location = entry.decl.location
331
+ paths.include?(Pathname(location.buffer.name))
332
+ end
333
+ end
334
+ end
335
+
336
+ def global_decls(paths:, env: latest_env)
337
+ env.global_decls.filter do |_, entry|
338
+ if location = entry.decl.location
339
+ paths.include?(Pathname(location.buffer.name))
340
+ end
341
+ end
342
+ end
343
+
344
+ def type_name_from_decl(decl, set:)
345
+ case decl
346
+ when RBS::AST::Declarations::Class, RBS::AST::Declarations::Module, RBS::AST::Declarations::Interface
347
+ set << decl.name
348
+
349
+ decl.members.each do |member|
350
+ if member.is_a?(RBS::AST::Declarations::Base)
351
+ type_name_from_decl(member, set: set)
352
+ end
353
+ end
354
+ when RBS::AST::Declarations::Alias
355
+ set << decl.name
356
+ end
357
+ end
358
+
359
+ def add_descendants(graph:, names:, set:)
360
+ set.merge(names)
361
+ names.each do |name|
362
+ case
363
+ when name.interface?
364
+ graph.each_descendant(RBS::AncestorGraph::InstanceNode.new(type_name: name)) do |node|
365
+ set << node.type_name
366
+ end
367
+ when name.class?
368
+ graph.each_descendant(RBS::AncestorGraph::InstanceNode.new(type_name: name)) do |node|
369
+ set << node.type_name
370
+ end
371
+ graph.each_descendant(RBS::AncestorGraph::SingletonNode.new(type_name: name)) do |node|
372
+ set << node.type_name
373
+ end
374
+ end
375
+ end
376
+ end
377
+
378
+ def add_nested_decls(env:, names:, set:)
379
+ tops = names.each.with_object(Set[]) do |name, tops|
380
+ unless name.namespace.empty?
381
+ tops << name.namespace.path[0]
382
+ end
383
+ end
384
+
385
+ env.class_decls.each_key do |name|
386
+ unless name.namespace.empty?
387
+ if tops.include?(name.namespace.path[0])
388
+ set << name
389
+ end
390
+ end
391
+ end
392
+
393
+ env.interface_decls.each_key do |name|
394
+ unless name.namespace.empty?
395
+ if tops.include?(name.namespace.path[0])
396
+ set << name
397
+ end
398
+ end
399
+ end
400
+ end
401
+ end
402
+ end
403
+ end
@@ -0,0 +1,69 @@
1
+ module Steep
2
+ module Services
3
+ class StatsCalculator
4
+ SuccessStats = Struct.new(:target, :path, :typed_calls_count, :untyped_calls_count, :error_calls_count, keyword_init: true) do
5
+ def as_json
6
+ {
7
+ type: "success",
8
+ target: target.name.to_s,
9
+ path: path.to_s,
10
+ typed_calls: typed_calls_count,
11
+ untyped_calls: untyped_calls_count,
12
+ error_calls: error_calls_count,
13
+ total_calls: typed_calls_count + untyped_calls_count + error_calls_count
14
+ }
15
+ end
16
+ end
17
+ ErrorStats = Struct.new(:target, :path, keyword_init: true) do
18
+ def as_json
19
+ {
20
+ type: "error",
21
+ target: target.name.to_s,
22
+ path: path.to_s
23
+ }
24
+ end
25
+ end
26
+
27
+ attr_reader :service
28
+
29
+ def initialize(service:)
30
+ @service = service
31
+ end
32
+
33
+ def project
34
+ service.project
35
+ end
36
+
37
+ def calc_stats(target, file:)
38
+ if typing = file.typing
39
+ typed = 0
40
+ untyped = 0
41
+ errors = 0
42
+ total = 0
43
+ typing.method_calls.each_value do |call|
44
+ case call
45
+ when TypeInference::MethodCall::Typed
46
+ typed += 1
47
+ when TypeInference::MethodCall::Untyped
48
+ untyped += 1
49
+ when TypeInference::MethodCall::Error, TypeInference::MethodCall::NoMethodError
50
+ errors += 1
51
+ else
52
+ raise
53
+ end
54
+ end
55
+
56
+ SuccessStats.new(
57
+ target: target,
58
+ path: file.path,
59
+ typed_calls_count: typed,
60
+ untyped_calls_count: untyped,
61
+ error_calls_count: errors
62
+ )
63
+ else
64
+ ErrorStats.new(target: target, path: file.path)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end