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,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