steep 0.40.0 → 0.44.0

Sign up to get free protection for your applications and to get access to all the features.
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,413 @@
1
+ module Steep
2
+ module Services
3
+ class TypeCheckService
4
+ attr_reader :project
5
+ attr_reader :signature_validation_diagnostics
6
+ attr_reader :source_files
7
+ attr_reader :signature_services
8
+
9
+ class SourceFile
10
+ attr_reader :path
11
+ attr_reader :target
12
+ attr_reader :content
13
+ attr_reader :node
14
+ attr_reader :typing
15
+ attr_reader :errors
16
+
17
+ def initialize(path:, node:, content:, typing:, errors:)
18
+ @path = path
19
+ @node = node
20
+ @content = content
21
+ @typing = typing
22
+ @errors = errors
23
+ end
24
+
25
+ def self.with_syntax_error(path:, content:, error:)
26
+ new(path: path, node: false, content: content, errors: [error], typing: nil)
27
+ end
28
+
29
+ def self.with_typing(path:, content:, typing:, node:)
30
+ new(path: path, node: node, content: content, errors: nil, typing: typing)
31
+ end
32
+
33
+ def self.no_data(path:, content:)
34
+ new(path: path, content: content, node: false, errors: nil, typing: nil)
35
+ end
36
+
37
+ def update_content(content)
38
+ self.class.new(
39
+ path: path,
40
+ content: content,
41
+ node: node,
42
+ errors: errors,
43
+ typing: typing
44
+ )
45
+ end
46
+
47
+ def diagnostics
48
+ errors || typing&.errors || []
49
+ end
50
+ end
51
+
52
+ class TargetRequest
53
+ attr_reader :target
54
+ attr_reader :source_paths
55
+
56
+ def initialize(target:)
57
+ @target = target
58
+ @source_paths = Set[]
59
+ @signature_updated = false
60
+ end
61
+
62
+ def signature_updated!(value = true)
63
+ @signature_updated = value
64
+ self
65
+ end
66
+
67
+ def signature_updated?
68
+ @signature_updated
69
+ end
70
+
71
+ def empty?
72
+ !signature_updated? && source_paths.empty?
73
+ end
74
+
75
+ def ==(other)
76
+ other.is_a?(TargetRequest) &&
77
+ other.target == target &&
78
+ other.source_paths == source_paths &&
79
+ other.signature_updated? == signature_updated?
80
+ end
81
+
82
+ alias eql? ==
83
+
84
+ def hash
85
+ self.class.hash ^ target.hash ^ source_paths.hash ^ @signature_updated.hash
86
+ end
87
+ end
88
+
89
+ def initialize(project:)
90
+ @project = project
91
+
92
+ @source_files = {}
93
+ @signature_services = project.targets.each.with_object({}) do |target, hash|
94
+ loader = Project::Target.construct_env_loader(options: target.options, project: project)
95
+ hash[target.name] = SignatureService.load_from(loader)
96
+ end
97
+ @signature_validation_diagnostics = project.targets.each.with_object({}) do |target, hash|
98
+ hash[target.name] = {}
99
+ end
100
+ end
101
+
102
+ def signature_diagnostics
103
+ signature_diagnostics = {}
104
+
105
+ project.targets.each do |target|
106
+ service = signature_services[target.name]
107
+
108
+ service.each_rbs_path do |path|
109
+ signature_diagnostics[path] ||= []
110
+ end
111
+
112
+ case service.status
113
+ when SignatureService::SyntaxErrorStatus, SignatureService::AncestorErrorStatus
114
+ service.status.diagnostics.group_by {|diag| Pathname(diag.location.buffer.name) }.each do |path, diagnostics|
115
+ signature_diagnostics[path].push(*diagnostics)
116
+ end
117
+ when SignatureService::LoadedStatus
118
+ validation_diagnostics = signature_validation_diagnostics[target.name] || {}
119
+ validation_diagnostics.each do |path, diagnostics|
120
+ signature_diagnostics[path].push(*diagnostics)
121
+ end
122
+ end
123
+ end
124
+
125
+ signature_diagnostics
126
+ end
127
+
128
+ def has_diagnostics?
129
+ each_diagnostics.count > 0
130
+ end
131
+
132
+ def diagnostics
133
+ each_diagnostics.to_h
134
+ end
135
+
136
+ def each_diagnostics(&block)
137
+ if block
138
+ signature_diagnostics.each do |path, diagnostics|
139
+ yield [path, diagnostics]
140
+ end
141
+
142
+ source_files.each_value do |file|
143
+ yield [file.path, file.diagnostics]
144
+ end
145
+ else
146
+ enum_for :each_diagnostics
147
+ end
148
+ end
149
+
150
+ def update(changes:)
151
+ requests = project.targets.each_with_object({}.compare_by_identity) do |target, hash|
152
+ hash[target] = TargetRequest.new(target: target)
153
+ end
154
+
155
+ Steep.measure "#update_signature" do
156
+ update_signature(changes: changes, requests: requests)
157
+ end
158
+
159
+ Steep.measure "#update_sources" do
160
+ update_sources(changes: changes, requests: requests)
161
+ end
162
+
163
+ requests.transform_keys(&:name).reject {|_, request| request.empty? }
164
+ end
165
+
166
+ def update_and_check(changes:, assignment:, &block)
167
+ requests = update(changes: changes)
168
+
169
+ signatures = requests.each_value.with_object(Set[]) do |request, sigs|
170
+ if request.signature_updated?
171
+ service = signature_services[request.target.name]
172
+ sigs.merge(service.each_rbs_path)
173
+ end
174
+ end
175
+
176
+ signatures.each do |path|
177
+ if assignment =~ path
178
+ validate_signature(path: path, &block)
179
+ end
180
+ end
181
+
182
+ requests.each_value do |request|
183
+ request.source_paths.each do |path|
184
+ if assignment =~ path
185
+ typecheck_source(path: path, target: request.target, &block)
186
+ end
187
+ end
188
+ end
189
+ end
190
+
191
+ def validate_signature(path:, &block)
192
+ Steep.logger.tagged "#validate_signature(path=#{path})" do
193
+ Steep.measure "validation" do
194
+ accumulated_diagnostics = []
195
+
196
+ project.targets.each do |target|
197
+ service = signature_services[target.name]
198
+
199
+ next unless target.possible_signature_file?(path) || service.env_rbs_paths.include?(path)
200
+
201
+ case service.status
202
+ when SignatureService::SyntaxErrorStatus
203
+ diagnostics = service.status.diagnostics.select do |diag|
204
+ Pathname(diag.location.buffer.name) == path &&
205
+ (diag.is_a?(Diagnostic::Signature::SyntaxError) || diag.is_a?(Diagnostic::Signature::UnexpectedError))
206
+ end
207
+ accumulated_diagnostics.push(*diagnostics)
208
+ unless diagnostics.empty?
209
+ yield [path, accumulated_diagnostics]
210
+ end
211
+
212
+ when SignatureService::AncestorErrorStatus
213
+ diagnostics = service.status.diagnostics.select {|diag| Pathname(diag.location.buffer.name) == path }
214
+ accumulated_diagnostics.push(*diagnostics)
215
+ yield [path, accumulated_diagnostics]
216
+
217
+ when SignatureService::LoadedStatus
218
+ validator = Signature::Validator.new(checker: service.current_subtyping)
219
+ type_names = service.type_names(paths: Set[path], env: service.latest_env).to_set
220
+
221
+ unless type_names.empty?
222
+ Steep.measure2 "Validating #{type_names.size} types" do |sampler|
223
+ type_names.each do |type_name|
224
+ sampler.sample(type_name.to_s) do
225
+ case
226
+ when type_name.class?
227
+ validator.validate_one_class(type_name)
228
+ when type_name.interface?
229
+ validator.validate_one_interface(type_name)
230
+ when type_name.alias?
231
+ validator.validate_one_alias(type_name)
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end
237
+
238
+ const_decls = service.const_decls(paths: Set[path], env: service.latest_env)
239
+ unless const_decls.empty?
240
+ Steep.measure2 "Validating #{const_decls.size} constants" do |sampler|
241
+ const_decls.each do |name, entry|
242
+ sampler.sample(name.to_s) do
243
+ validator.validate_one_constant(name, entry)
244
+ end
245
+ end
246
+ end
247
+ end
248
+
249
+ global_decls = service.global_decls(paths: Set[path])
250
+ unless global_decls.empty?
251
+ Steep.measure2 "Validating #{global_decls.size} globals" do |sampler|
252
+ global_decls.each do |name, entry|
253
+ sampler.sample(name.to_s) do
254
+ validator.validate_one_global(name, entry)
255
+ end
256
+ end
257
+ end
258
+ end
259
+
260
+ diagnostics = validator.each_error.select {|error| Pathname(error.location.buffer.name) == path }
261
+ accumulated_diagnostics.push(*diagnostics)
262
+ yield [path, accumulated_diagnostics]
263
+ end
264
+
265
+ signature_validation_diagnostics[target.name][path] = diagnostics
266
+ end
267
+ end
268
+ end
269
+ end
270
+
271
+ def typecheck_source(path:, target: project.target_for_source_path(path), &block)
272
+ Steep.logger.tagged "#typecheck_source(path=#{path})" do
273
+ Steep.measure "typecheck" do
274
+ signature_service = signature_services[target.name]
275
+ subtyping = signature_service.current_subtyping
276
+
277
+ if subtyping
278
+ text = source_files[path].content
279
+ file = type_check_file(target: target, subtyping: subtyping, path: path, text: text)
280
+ yield [file.path, file.diagnostics]
281
+ source_files[path] = file
282
+ end
283
+ end
284
+ end
285
+ end
286
+
287
+ def update_signature(changes:, requests:)
288
+ Steep.logger.tagged "#update_signature" do
289
+ project.targets.each do |target|
290
+ signature_service = signature_services[target.name]
291
+ signature_changes = changes.filter {|path, _| target.possible_signature_file?(path) }
292
+
293
+ unless signature_changes.empty?
294
+ requests[target].signature_updated!
295
+ signature_service.update(signature_changes)
296
+ end
297
+ end
298
+ end
299
+ end
300
+
301
+ def update_sources(changes:, requests:)
302
+ requests.each_value do |request|
303
+ source_files
304
+ .select {|path, file| request.target.possible_source_file?(path) }
305
+ .each do |path, file|
306
+ (changes[path] ||= []).prepend(ContentChange.string(file.content))
307
+ end
308
+ end
309
+
310
+ changes.each do |path, changes|
311
+ target = project.target_for_source_path(path)
312
+
313
+ if target
314
+ file = source_files[path] || SourceFile.no_data(path: path, content: "")
315
+ content = changes.inject(file.content) {|text, change| change.apply_to(text) }
316
+ source_files[path] = file.update_content(content)
317
+ requests[target].source_paths << path
318
+ end
319
+ end
320
+ end
321
+
322
+ def type_check_file(target:, subtyping:, path:, text:)
323
+ Steep.logger.tagged "#type_check_file(#{path}@#{target.name})" do
324
+ source = Source.parse(text, path: path, factory: subtyping.factory)
325
+ typing = TypeCheckService.type_check(source: source, subtyping: subtyping)
326
+ SourceFile.with_typing(path: path, content: text, node: source.node, typing: typing)
327
+ end
328
+ rescue AnnotationParser::SyntaxError => exn
329
+ error = Diagnostic::Ruby::SyntaxError.new(message: exn.message, location: exn.location)
330
+ SourceFile.with_syntax_error(path: path, content: text, error: error)
331
+ rescue ::Parser::SyntaxError => exn
332
+ error = Diagnostic::Ruby::SyntaxError.new(message: exn.message, location: exn.diagnostic.location)
333
+ SourceFile.with_syntax_error(path: path, content: text, error: error)
334
+ rescue EncodingError => exn
335
+ SourceFile.no_data(path: path, content: "")
336
+ end
337
+
338
+ def self.type_check(source:, subtyping:)
339
+ annotations = source.annotations(block: source.node, factory: subtyping.factory, current_module: RBS::Namespace.root)
340
+ const_env = TypeInference::ConstantEnv.new(factory: subtyping.factory, context: [RBS::Namespace.root])
341
+ type_env = TypeInference::TypeEnv.build(annotations: annotations,
342
+ subtyping: subtyping,
343
+ const_env: const_env,
344
+ signatures: subtyping.factory.env)
345
+ lvar_env = TypeInference::LocalVariableTypeEnv.empty(
346
+ subtyping: subtyping,
347
+ self_type: AST::Builtin::Object.instance_type,
348
+ instance_type: AST::Builtin::Object.instance_type,
349
+ class_type: AST::Builtin::Object.module_type
350
+ ).annotate(annotations)
351
+
352
+ context = TypeInference::Context.new(
353
+ block_context: nil,
354
+ module_context: TypeInference::Context::ModuleContext.new(
355
+ instance_type: AST::Builtin::Object.instance_type,
356
+ module_type: AST::Builtin::Object.module_type,
357
+ implement_name: nil,
358
+ current_namespace: RBS::Namespace.root,
359
+ const_env: const_env,
360
+ class_name: AST::Builtin::Object.module_name,
361
+ instance_definition: subtyping.factory.definition_builder.build_instance(AST::Builtin::Object.module_name),
362
+ module_definition: subtyping.factory.definition_builder.build_singleton(AST::Builtin::Object.module_name)
363
+ ),
364
+ method_context: nil,
365
+ break_context: nil,
366
+ self_type: AST::Builtin::Object.instance_type,
367
+ type_env: type_env,
368
+ lvar_env: lvar_env,
369
+ call_context: TypeInference::MethodCall::TopLevelContext.new
370
+ )
371
+
372
+ typing = Typing.new(source: source, root_context: context)
373
+
374
+ construction = TypeConstruction.new(
375
+ checker: subtyping,
376
+ annotations: annotations,
377
+ source: source,
378
+ context: context,
379
+ typing: typing
380
+ )
381
+
382
+ construction.synthesize(source.node) if source.node
383
+
384
+ typing
385
+ end
386
+
387
+ def source_file?(path)
388
+ if source_files.key?(path)
389
+ project.target_for_source_path(path)
390
+ end
391
+ end
392
+
393
+ def signature_file?(path)
394
+ relative_path = project.relative_path(path)
395
+ targets = signature_services.select {|_, sig| sig.files.key?(relative_path) || sig.env_rbs_paths.include?(path) }
396
+ unless targets.empty?
397
+ targets.keys
398
+ end
399
+ end
400
+
401
+ def app_signature_file?(path)
402
+ target_names = signature_services.select {|_, sig| sig.files.key?(path) }.keys
403
+ unless target_names.empty?
404
+ target_names
405
+ end
406
+ end
407
+
408
+ def lib_signature_file?(path)
409
+ signature_services.each_value.any? {|sig| sig.env_rbs_paths.include?(path) }
410
+ end
411
+ end
412
+ end
413
+ end
@@ -61,15 +61,169 @@ module Steep
61
61
  validator.validate_type type, context: [RBS::Namespace.root]
62
62
  end
63
63
 
64
+ def ancestor_to_type(ancestor)
65
+ case ancestor
66
+ when RBS::Definition::Ancestor::Instance
67
+ args = ancestor.args.map {|type| checker.factory.type(type) }
68
+
69
+ case
70
+ when ancestor.name.interface?
71
+ AST::Types::Name::Interface.new(name: ancestor.name, args: args, location: nil)
72
+ when ancestor.name.class?
73
+ AST::Types::Name::Instance.new(name: ancestor.name, args: args, location: nil)
74
+ else
75
+ raise "#{ancestor.name}"
76
+ end
77
+ else
78
+ raise "Unexpected ancestor: #{ancestor.inspect}"
79
+ end
80
+ end
81
+
82
+ def mixin_constraints(definition, mixin_ancestors, immediate_self_types:)
83
+ relations = []
84
+
85
+ self_type = checker.factory.type(definition.self_type)
86
+ if immediate_self_types && !immediate_self_types.empty?
87
+ self_type = AST::Types::Intersection.build(
88
+ types: immediate_self_types.map {|st| ancestor_to_type(st) }.push(self_type),
89
+ location: nil
90
+ )
91
+ end
92
+
93
+ mixin_ancestors.each do |ancestor|
94
+ args = ancestor.args.map {|type| checker.factory.type(type) }
95
+ ancestor_ancestors = builder.ancestor_builder.one_instance_ancestors(ancestor.name)
96
+ self_constraints = ancestor_ancestors.self_types.map do |self_ancestor|
97
+ s = Interface::Substitution.build(ancestor_ancestors.params, args)
98
+ ancestor_to_type(self_ancestor).subst(s)
99
+ end
100
+
101
+ self_constraints.each do |constraint|
102
+ relations << [
103
+ Subtyping::Relation.new(sub_type: self_type, super_type: constraint),
104
+ ancestor
105
+ ]
106
+ end
107
+ end
108
+
109
+ relations
110
+ end
111
+
64
112
  def validate_one_class(name)
65
113
  rescue_validation_errors(name) do
66
- Steep.logger.debug "Validating class definition `#{name}`..."
114
+ Steep.logger.debug { "Validating class definition `#{name}`..." }
115
+
67
116
  Steep.logger.tagged "#{name}" do
68
- builder.build_instance(name).each_type do |type|
69
- validate_type type
117
+ builder.build_instance(name).tap do |definition|
118
+ definition.instance_variables.each do |name, var|
119
+ if parent = var.parent_variable
120
+ var_type = checker.factory.type(var.type)
121
+ parent_type = checker.factory.type(parent.type)
122
+
123
+ relation = Subtyping::Relation.new(sub_type: var_type, super_type: parent_type)
124
+ result1 = checker.check(
125
+ relation,
126
+ self_type: AST::Types::Self.new,
127
+ instance_type: AST::Types::Instance.new,
128
+ class_type: AST::Types::Class.new,
129
+ constraints: Subtyping::Constraints.empty
130
+ )
131
+ result2 = checker.check(
132
+ relation.flip,
133
+ self_type: AST::Types::Self.new,
134
+ instance_type: AST::Types::Instance.new,
135
+ class_type: AST::Types::Class.new,
136
+ constraints: Subtyping::Constraints.empty
137
+ )
138
+
139
+ unless result1.success? and result2.success?
140
+ @errors << Diagnostic::Signature::InstanceVariableTypeError.new(
141
+ name: name,
142
+ location: var.type.location,
143
+ var_type: var_type,
144
+ parent_type: parent_type
145
+ )
146
+ end
147
+ end
148
+ end
149
+
150
+ ancestors = builder.ancestor_builder.one_instance_ancestors(name)
151
+ mixin_constraints(definition, ancestors.included_modules, immediate_self_types: ancestors.self_types).each do |relation, ancestor|
152
+ checker.check(
153
+ relation,
154
+ self_type: AST::Types::Self.new,
155
+ instance_type: AST::Types::Instance.new,
156
+ class_type: AST::Types::Class.new,
157
+ constraints: Subtyping::Constraints.empty
158
+ ).else do
159
+ @errors << Diagnostic::Signature::ModuleSelfTypeError.new(
160
+ name: name,
161
+ location: ancestor.source&.location || raise,
162
+ ancestor: ancestor,
163
+ relation: relation
164
+ )
165
+ end
166
+ end
167
+
168
+ definition.each_type do |type|
169
+ validate_type type
170
+ end
70
171
  end
71
- builder.build_singleton(name).each_type do |type|
72
- validate_type type
172
+
173
+ builder.build_singleton(name).tap do |definition|
174
+ definition.instance_variables.each do |name, var|
175
+ if parent = var.parent_variable
176
+ var_type = checker.factory.type(var.type)
177
+ parent_type = checker.factory.type(parent.type)
178
+
179
+ relation = Subtyping::Relation.new(sub_type: var_type, super_type: parent_type)
180
+ result1 = checker.check(
181
+ relation,
182
+ self_type: AST::Types::Self.new,
183
+ instance_type: AST::Types::Instance.new,
184
+ class_type: AST::Types::Class.new,
185
+ constraints: Subtyping::Constraints.empty
186
+ )
187
+ result2 = checker.check(
188
+ relation.flip,
189
+ self_type: AST::Types::Self.new,
190
+ instance_type: AST::Types::Instance.new,
191
+ class_type: AST::Types::Class.new,
192
+ constraints: Subtyping::Constraints.empty
193
+ )
194
+
195
+ unless result1.success? and result2.success?
196
+ @errors << Diagnostic::Signature::InstanceVariableTypeError.new(
197
+ name: name,
198
+ location: var.type.location,
199
+ var_type: var_type,
200
+ parent_type: parent_type
201
+ )
202
+ end
203
+ end
204
+ end
205
+
206
+ ancestors = builder.ancestor_builder.one_singleton_ancestors(name)
207
+ mixin_constraints(definition, ancestors.extended_modules, immediate_self_types: ancestors.self_types).each do |relation, ancestor|
208
+ checker.check(
209
+ relation,
210
+ self_type: AST::Types::Self.new,
211
+ instance_type: AST::Types::Instance.new,
212
+ class_type: AST::Types::Class.new,
213
+ constraints: Subtyping::Constraints.empty
214
+ ).else do
215
+ @errors << Diagnostic::Signature::ModuleSelfTypeError.new(
216
+ name: name,
217
+ location: ancestor.source&.location || raise,
218
+ ancestor: ancestor,
219
+ relation: relation
220
+ )
221
+ end
222
+ end
223
+
224
+ definition.each_type do |type|
225
+ validate_type type
226
+ end
73
227
  end
74
228
  end
75
229
  end
@@ -98,102 +252,50 @@ module Steep
98
252
 
99
253
  def validate_const
100
254
  env.constant_decls.each do |name, entry|
101
- rescue_validation_errors do
102
- Steep.logger.debug "Validating constant `#{name}`..."
103
- builder.ensure_namespace!(name.namespace, location: entry.decl.location)
104
- validate_type entry.decl.type
105
- end
255
+ validate_one_constant(name, entry)
256
+ end
257
+ end
258
+
259
+ def validate_one_constant(name, entry)
260
+ rescue_validation_errors do
261
+ Steep.logger.debug "Validating constant `#{name}`..."
262
+ builder.ensure_namespace!(name.namespace, location: entry.decl.location)
263
+ validate_type entry.decl.type
106
264
  end
107
265
  end
108
266
 
109
267
  def validate_global
110
268
  env.global_decls.each do |name, entry|
111
- rescue_validation_errors do
112
- Steep.logger.debug "Validating global `#{name}`..."
113
- validate_type entry.decl.type
269
+ validate_one_global(name, entry)
270
+ end
271
+ end
272
+
273
+ def validate_one_global(name, entry)
274
+ rescue_validation_errors do
275
+ Steep.logger.debug "Validating global `#{name}`..."
276
+ validate_type entry.decl.type
277
+ end
278
+ end
279
+
280
+ def validate_one_alias(name)
281
+ rescue_validation_errors(name) do
282
+ Steep.logger.debug "Validating alias `#{name}`..."
283
+ builder.expand_alias(name).tap do |type|
284
+ validate_type(type)
114
285
  end
115
286
  end
116
287
  end
117
288
 
118
289
  def validate_alias
119
290
  env.alias_decls.each do |name, entry|
120
- rescue_validation_errors(name) do
121
- Steep.logger.debug "Validating alias `#{name}`..."
122
- builder.expand_alias(name).tap do |type|
123
- validate_type(type)
124
- end
125
- end
291
+ validate_one_alias(name)
126
292
  end
127
293
  end
128
294
 
129
295
  def rescue_validation_errors(type_name = nil)
130
296
  yield
131
- rescue RBS::InvalidTypeApplicationError => exn
132
- @errors << Diagnostic::Signature::InvalidTypeApplication.new(
133
- name: exn.type_name,
134
- args: exn.args.map {|ty| factory.type(ty) },
135
- params: exn.params,
136
- location: exn.location
137
- )
138
- rescue RBS::NoTypeFoundError,
139
- RBS::NoSuperclassFoundError,
140
- RBS::NoMixinFoundError,
141
- RBS::NoSelfTypeFoundError => exn
142
- @errors << Diagnostic::Signature::UnknownTypeName.new(
143
- name: exn.type_name,
144
- location: exn.location
145
- )
146
- rescue RBS::InvalidOverloadMethodError => exn
147
- @errors << Diagnostic::Signature::InvalidMethodOverload.new(
148
- class_name: exn.type_name,
149
- method_name: exn.method_name,
150
- location: exn.members[0].location
151
- )
152
- rescue RBS::DuplicatedMethodDefinitionError => exn
153
- @errors << Diagnostic::Signature::DuplicatedMethodDefinition.new(
154
- class_name: type_name,
155
- method_name: exn.method_name,
156
- location: exn.location
157
- )
158
- rescue RBS::DuplicatedInterfaceMethodDefinitionError => exn
159
- @errors << Diagnostic::Signature::DuplicatedMethodDefinition.new(
160
- class_name: type_name,
161
- method_name: exn.method_name,
162
- location: exn.member.location
163
- )
164
- rescue RBS::UnknownMethodAliasError => exn
165
- @errors << Diagnostic::Signature::UnknownMethodAlias.new(
166
- class_name: type_name,
167
- method_name: exn.original_name,
168
- location: exn.location
169
- )
170
- rescue RBS::RecursiveAliasDefinitionError => exn
171
- @errors << Diagnostic::Signature::RecursiveAlias.new(
172
- class_name: exn.type.name,
173
- names: exn.defs.map(&:name),
174
- location: exn.defs[0].original.location
175
- )
176
- rescue RBS::RecursiveAncestorError => exn
177
- @errors << Diagnostic::Signature::RecursiveAncestor.new(
178
- ancestors: exn.ancestors,
179
- location: exn.location
180
- )
181
- rescue RBS::SuperclassMismatchError => exn
182
- @errors << Diagnostic::Signature::SuperclassMismatch.new(
183
- name: exn.name,
184
- location: exn.entry.primary.decl.location
185
- )
186
- rescue RBS::GenericParameterMismatchError => exn
187
- @errors << Diagnostic::Signature::GenericParameterMismatch.new(
188
- name: exn.name,
189
- location: exn.decl.location
190
- )
191
- rescue RBS::InvalidVarianceAnnotationError => exn
192
- @errors << Diagnostic::Signature::InvalidVarianceAnnotation.new(
193
- name: exn.type_name,
194
- param: exn.param,
195
- location: exn.location
196
- )
297
+ rescue RBS::ErrorBase => exn
298
+ @errors << Diagnostic::Signature.from_rbs_error(exn, factory: factory)
197
299
  end
198
300
  end
199
301
  end