steep 0.37.0 → 0.42.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (221) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +1 -1
  3. data/CHANGELOG.md +34 -0
  4. data/Rakefile +5 -2
  5. data/bin/output_rebaseline.rb +34 -0
  6. data/bin/output_test.rb +53 -0
  7. data/lib/steep.rb +95 -14
  8. data/lib/steep/ast/types/bot.rb +1 -1
  9. data/lib/steep/ast/types/class.rb +4 -0
  10. data/lib/steep/ast/types/factory.rb +10 -0
  11. data/lib/steep/ast/types/logic.rb +16 -3
  12. data/lib/steep/ast/types/top.rb +1 -1
  13. data/lib/steep/cli.rb +31 -7
  14. data/lib/steep/diagnostic/helper.rb +17 -0
  15. data/lib/steep/diagnostic/lsp_formatter.rb +16 -0
  16. data/lib/steep/diagnostic/ruby.rb +619 -0
  17. data/lib/steep/diagnostic/signature.rb +357 -0
  18. data/lib/steep/drivers/annotations.rb +19 -28
  19. data/lib/steep/drivers/check.rb +182 -60
  20. data/lib/steep/drivers/diagnostic_printer.rb +99 -0
  21. data/lib/steep/drivers/langserver.rb +3 -8
  22. data/lib/steep/drivers/print_project.rb +10 -9
  23. data/lib/steep/drivers/stats.rb +124 -32
  24. data/lib/steep/drivers/trace_printer.rb +5 -1
  25. data/lib/steep/drivers/utils/jobs_count.rb +9 -0
  26. data/lib/steep/drivers/validate.rb +31 -13
  27. data/lib/steep/drivers/watch.rb +69 -48
  28. data/lib/steep/drivers/worker.rb +16 -8
  29. data/lib/steep/expectations.rb +159 -0
  30. data/lib/steep/index/rbs_index.rb +334 -0
  31. data/lib/steep/index/signature_symbol_provider.rb +162 -0
  32. data/lib/steep/index/source_index.rb +100 -0
  33. data/lib/steep/project.rb +0 -30
  34. data/lib/steep/project/dsl.rb +5 -3
  35. data/lib/steep/project/options.rb +4 -4
  36. data/lib/steep/project/pattern.rb +56 -0
  37. data/lib/steep/project/target.rb +9 -214
  38. data/lib/steep/range_extension.rb +29 -0
  39. data/lib/steep/server/base_worker.rb +43 -7
  40. data/lib/steep/server/change_buffer.rb +63 -0
  41. data/lib/steep/server/interaction_worker.rb +73 -56
  42. data/lib/steep/server/master.rb +245 -109
  43. data/lib/steep/server/type_check_worker.rb +122 -0
  44. data/lib/steep/server/worker_process.rb +17 -15
  45. data/lib/steep/{project → services}/completion_provider.rb +3 -3
  46. data/lib/steep/services/content_change.rb +61 -0
  47. data/lib/steep/services/file_loader.rb +48 -0
  48. data/lib/steep/{project → services}/hover_content.rb +14 -16
  49. data/lib/steep/services/path_assignment.rb +29 -0
  50. data/lib/steep/services/signature_service.rb +369 -0
  51. data/lib/steep/services/stats_calculator.rb +69 -0
  52. data/lib/steep/services/type_check_service.rb +342 -0
  53. data/lib/steep/signature/validator.rb +174 -32
  54. data/lib/steep/subtyping/check.rb +248 -47
  55. data/lib/steep/subtyping/constraints.rb +2 -2
  56. data/lib/steep/type_construction.rb +565 -295
  57. data/lib/steep/type_inference/constant_env.rb +5 -1
  58. data/lib/steep/type_inference/local_variable_type_env.rb +26 -12
  59. data/lib/steep/type_inference/logic_type_interpreter.rb +99 -26
  60. data/lib/steep/type_inference/type_env.rb +43 -17
  61. data/lib/steep/typing.rb +8 -2
  62. data/lib/steep/version.rb +1 -1
  63. data/smoke/alias/a.rb +0 -3
  64. data/smoke/alias/b.rb +0 -1
  65. data/smoke/alias/c.rb +0 -2
  66. data/smoke/alias/test_expectations.yml +96 -0
  67. data/smoke/and/a.rb +0 -3
  68. data/smoke/and/test_expectations.yml +31 -0
  69. data/smoke/array/a.rb +0 -3
  70. data/smoke/array/b.rb +0 -2
  71. data/smoke/array/c.rb +0 -1
  72. data/smoke/array/test_expectations.yml +103 -0
  73. data/smoke/block/a.rb +0 -2
  74. data/smoke/block/b.rb +0 -2
  75. data/smoke/block/d.rb +0 -4
  76. data/smoke/block/test_expectations.yml +125 -0
  77. data/smoke/case/a.rb +0 -3
  78. data/smoke/case/test_expectations.yml +47 -0
  79. data/smoke/class/a.rb +0 -3
  80. data/smoke/class/c.rb +0 -1
  81. data/smoke/class/f.rb +0 -1
  82. data/smoke/class/g.rb +0 -2
  83. data/smoke/class/i.rb +0 -2
  84. data/smoke/class/test_expectations.yml +120 -0
  85. data/smoke/const/a.rb +0 -3
  86. data/smoke/const/b.rb +7 -0
  87. data/smoke/const/b.rbs +5 -0
  88. data/smoke/const/test_expectations.yml +139 -0
  89. data/smoke/diagnostics-rbs-duplicated/Steepfile +5 -0
  90. data/smoke/diagnostics-rbs-duplicated/a.rbs +5 -0
  91. data/smoke/diagnostics-rbs-duplicated/test_expectations.yml +13 -0
  92. data/smoke/diagnostics-rbs/Steepfile +8 -0
  93. data/smoke/diagnostics-rbs/duplicated-method-definition.rbs +20 -0
  94. data/smoke/diagnostics-rbs/generic-parameter-mismatch.rbs +7 -0
  95. data/smoke/diagnostics-rbs/invalid-method-overload.rbs +3 -0
  96. data/smoke/diagnostics-rbs/invalid-type-application.rbs +7 -0
  97. data/smoke/diagnostics-rbs/invalid_variance_annotation.rbs +3 -0
  98. data/smoke/diagnostics-rbs/recursive-alias.rbs +5 -0
  99. data/smoke/diagnostics-rbs/recursive-class.rbs +8 -0
  100. data/smoke/diagnostics-rbs/superclass-mismatch.rbs +7 -0
  101. data/smoke/diagnostics-rbs/test_expectations.yml +231 -0
  102. data/smoke/diagnostics-rbs/unknown-method-alias.rbs +3 -0
  103. data/smoke/diagnostics-rbs/unknown-type-name-2.rbs +5 -0
  104. data/smoke/diagnostics-rbs/unknown-type-name.rbs +13 -0
  105. data/smoke/diagnostics/Steepfile +5 -0
  106. data/smoke/diagnostics/a.rbs +26 -0
  107. data/smoke/diagnostics/argument_type_mismatch.rb +1 -0
  108. data/smoke/diagnostics/block_body_type_mismatch.rb +1 -0
  109. data/smoke/diagnostics/block_type_mismatch.rb +3 -0
  110. data/smoke/diagnostics/break_type_mismatch.rb +1 -0
  111. data/smoke/diagnostics/else_on_exhaustive_case.rb +12 -0
  112. data/smoke/diagnostics/incompatible_annotation.rb +6 -0
  113. data/smoke/diagnostics/incompatible_argument.rb +1 -0
  114. data/smoke/diagnostics/incompatible_assignment.rb +8 -0
  115. data/smoke/diagnostics/method_arity_mismatch.rb +11 -0
  116. data/smoke/diagnostics/method_body_type_mismatch.rb +6 -0
  117. data/smoke/diagnostics/method_definition_missing.rb +2 -0
  118. data/smoke/diagnostics/method_return_type_annotation_mismatch.rb +7 -0
  119. data/smoke/diagnostics/missing_keyword.rb +1 -0
  120. data/smoke/diagnostics/no_method.rb +1 -0
  121. data/smoke/diagnostics/required_block_missing.rb +1 -0
  122. data/smoke/diagnostics/return_type_mismatch.rb +6 -0
  123. data/smoke/diagnostics/test_expectations.yml +477 -0
  124. data/smoke/diagnostics/unexpected_block_given.rb +1 -0
  125. data/smoke/diagnostics/unexpected_dynamic_method.rb +3 -0
  126. data/smoke/diagnostics/unexpected_jump.rb +4 -0
  127. data/smoke/diagnostics/unexpected_jump_value.rb +3 -0
  128. data/smoke/diagnostics/unexpected_keyword.rb +1 -0
  129. data/smoke/diagnostics/unexpected_splat.rb +1 -0
  130. data/smoke/diagnostics/unexpected_yield.rb +6 -0
  131. data/smoke/diagnostics/unknown_constant_assigned.rb +7 -0
  132. data/smoke/diagnostics/unresolved_overloading.rb +1 -0
  133. data/smoke/diagnostics/unsatisfiable_constraint.rb +7 -0
  134. data/smoke/diagnostics/unsupported_syntax.rb +2 -0
  135. data/smoke/dstr/a.rb +0 -1
  136. data/smoke/dstr/test_expectations.yml +13 -0
  137. data/smoke/ensure/a.rb +0 -4
  138. data/smoke/ensure/test_expectations.yml +62 -0
  139. data/smoke/enumerator/a.rb +0 -6
  140. data/smoke/enumerator/b.rb +0 -3
  141. data/smoke/enumerator/test_expectations.yml +135 -0
  142. data/smoke/extension/a.rb +0 -1
  143. data/smoke/extension/b.rb +0 -2
  144. data/smoke/extension/c.rb +0 -1
  145. data/smoke/extension/f.rb +2 -0
  146. data/smoke/extension/f.rbs +3 -0
  147. data/smoke/extension/test_expectations.yml +73 -0
  148. data/smoke/hash/b.rb +0 -1
  149. data/smoke/hash/c.rb +0 -3
  150. data/smoke/hash/d.rb +0 -1
  151. data/smoke/hash/e.rb +0 -1
  152. data/smoke/hash/test_expectations.yml +81 -0
  153. data/smoke/hello/hello.rb +0 -2
  154. data/smoke/hello/test_expectations.yml +25 -0
  155. data/smoke/if/a.rb +0 -2
  156. data/smoke/if/test_expectations.yml +34 -0
  157. data/smoke/implements/a.rb +0 -2
  158. data/smoke/implements/test_expectations.yml +23 -0
  159. data/smoke/initialize/test_expectations.yml +1 -0
  160. data/smoke/integer/a.rb +0 -7
  161. data/smoke/integer/test_expectations.yml +101 -0
  162. data/smoke/interface/a.rb +0 -2
  163. data/smoke/interface/test_expectations.yml +23 -0
  164. data/smoke/kwbegin/a.rb +0 -1
  165. data/smoke/kwbegin/test_expectations.yml +17 -0
  166. data/smoke/lambda/a.rb +1 -4
  167. data/smoke/lambda/test_expectations.yml +39 -0
  168. data/smoke/literal/a.rb +0 -5
  169. data/smoke/literal/b.rb +0 -2
  170. data/smoke/literal/test_expectations.yml +106 -0
  171. data/smoke/map/test_expectations.yml +1 -0
  172. data/smoke/method/a.rb +0 -5
  173. data/smoke/method/b.rb +0 -1
  174. data/smoke/method/test_expectations.yml +90 -0
  175. data/smoke/module/a.rb +0 -2
  176. data/smoke/module/b.rb +0 -2
  177. data/smoke/module/c.rb +0 -1
  178. data/smoke/module/d.rb +0 -1
  179. data/smoke/module/f.rb +0 -2
  180. data/smoke/module/test_expectations.yml +75 -0
  181. data/smoke/regexp/a.rb +0 -38
  182. data/smoke/regexp/b.rb +0 -26
  183. data/smoke/regexp/test_expectations.yml +615 -0
  184. data/smoke/regression/set_divide.rb +0 -4
  185. data/smoke/regression/test_expectations.yml +43 -0
  186. data/smoke/rescue/a.rb +0 -5
  187. data/smoke/rescue/test_expectations.yml +79 -0
  188. data/smoke/self/a.rb +0 -2
  189. data/smoke/self/test_expectations.yml +23 -0
  190. data/smoke/skip/skip.rb +0 -2
  191. data/smoke/skip/test_expectations.yml +23 -0
  192. data/smoke/stdout/test_expectations.yml +1 -0
  193. data/smoke/super/a.rb +0 -4
  194. data/smoke/super/test_expectations.yml +79 -0
  195. data/smoke/toplevel/a.rb +0 -1
  196. data/smoke/toplevel/test_expectations.yml +15 -0
  197. data/smoke/tsort/Steepfile +2 -0
  198. data/smoke/tsort/a.rb +0 -3
  199. data/smoke/tsort/test_expectations.yml +63 -0
  200. data/smoke/type_case/a.rb +0 -4
  201. data/smoke/type_case/test_expectations.yml +48 -0
  202. data/smoke/unexpected/Steepfile +5 -0
  203. data/smoke/unexpected/test_expectations.yml +25 -0
  204. data/smoke/unexpected/unexpected.rb +1 -0
  205. data/smoke/unexpected/unexpected.rbs +3 -0
  206. data/smoke/yield/a.rb +0 -3
  207. data/smoke/yield/b.rb +6 -0
  208. data/smoke/yield/test_expectations.yml +68 -0
  209. data/steep.gemspec +4 -3
  210. metadata +144 -29
  211. data/bin/smoke_runner.rb +0 -139
  212. data/lib/steep/drivers/signature_error_printer.rb +0 -25
  213. data/lib/steep/errors.rb +0 -565
  214. data/lib/steep/project/file_loader.rb +0 -68
  215. data/lib/steep/project/signature_file.rb +0 -33
  216. data/lib/steep/project/source_file.rb +0 -129
  217. data/lib/steep/server/code_worker.rb +0 -137
  218. data/lib/steep/server/signature_worker.rb +0 -152
  219. data/lib/steep/server/utils.rb +0 -69
  220. data/lib/steep/signature/errors.rb +0 -82
  221. data/lib/steep/type_assignability.rb +0 -367
@@ -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
@@ -0,0 +1,342 @@
1
+ module Steep
2
+ module Services
3
+ class TypeCheckService
4
+ attr_reader :project
5
+ attr_reader :assignment
6
+ attr_reader :signature_validation_diagnostics
7
+ attr_reader :source_files
8
+ attr_reader :signature_services
9
+
10
+ class SourceFile
11
+ attr_reader :path
12
+ attr_reader :target
13
+ attr_reader :content
14
+ attr_reader :node
15
+ attr_reader :typing
16
+ attr_reader :errors
17
+
18
+ def initialize(path:, node:, content:, typing:, errors:)
19
+ @path = path
20
+ @node = node
21
+ @content = content
22
+ @typing = typing
23
+ @errors = errors
24
+ end
25
+
26
+ def self.with_syntax_error(path:, content:, error:)
27
+ new(path: path, node: false, content: content, errors: [error], typing: nil)
28
+ end
29
+
30
+ def self.with_typing(path:, content:, typing:, node:)
31
+ new(path: path, node: node, content: content, errors: nil, typing: typing)
32
+ end
33
+
34
+ def self.no_data(path:, content:)
35
+ new(path: path, content: content, node: false, errors: nil, typing: nil)
36
+ end
37
+
38
+ def update_content(content)
39
+ self.class.new(
40
+ path: path,
41
+ content: content,
42
+ node: node,
43
+ errors: errors,
44
+ typing: typing
45
+ )
46
+ end
47
+
48
+ def diagnostics
49
+ errors || typing&.errors || []
50
+ end
51
+ end
52
+
53
+ def initialize(project:, assignment:)
54
+ @project = project
55
+ @assignment = assignment
56
+
57
+ @signature_validation_diagnostics = {}
58
+ @source_files = {}
59
+ @signature_services = project.targets.each.with_object({}) do |target, hash|
60
+ loader = Project::Target.construct_env_loader(options: target.options)
61
+ hash[target.name] = SignatureService.load_from(loader)
62
+ end
63
+
64
+ @no_type_checking = false
65
+ end
66
+
67
+ def no_type_checking!
68
+ @no_type_checking = true
69
+ self
70
+ end
71
+
72
+ def no_type_checking?
73
+ @no_type_checking
74
+ end
75
+
76
+ def signature_diagnostics
77
+ signature_diagnostics = {}
78
+
79
+ project.targets.each do |target|
80
+ service = signature_services[target.name]
81
+
82
+ service.each_rbs_path do |path|
83
+ if assignment =~ path
84
+ signature_diagnostics[path] ||= []
85
+ end
86
+ end
87
+
88
+ case service.status
89
+ when SignatureService::SyntaxErrorStatus, SignatureService::AncestorErrorStatus
90
+ service.status.diagnostics.group_by {|diag| Pathname(diag.location.buffer.name) }.each do |path, diagnostics|
91
+ if assignment =~ path
92
+ signature_diagnostics[path].push(*diagnostics)
93
+ end
94
+ end
95
+ when SignatureService::LoadedStatus
96
+ validation_diagnostics = signature_validation_diagnostics[target.name] || {}
97
+ validation_diagnostics.each do |path, diagnostics|
98
+ if assignment =~ path
99
+ signature_diagnostics[path].push(*diagnostics)
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ signature_diagnostics
106
+ end
107
+
108
+ def has_diagnostics?
109
+ each_diagnostics.count > 0
110
+ end
111
+
112
+ def diagnostics
113
+ each_diagnostics.to_h
114
+ end
115
+
116
+ def each_diagnostics(&block)
117
+ if block
118
+ signature_diagnostics.each do |path, diagnostics|
119
+ yield [path, diagnostics]
120
+ end
121
+
122
+ source_files.each_value do |file|
123
+ yield [file.path, file.diagnostics]
124
+ end
125
+ else
126
+ enum_for :each_diagnostics
127
+ end
128
+ end
129
+
130
+ def update(changes:, &block)
131
+ updated_targets = Steep.measure "#update_signature" do
132
+ update_signature(changes: changes, &block)
133
+ end
134
+ Steep.measure2 "Type check target" do |sampler|
135
+ project.targets.each do |target|
136
+ sampler.sample target.name.to_s do
137
+ update_target(target: target, changes: changes, updated: updated_targets.include?(target), &block)
138
+ end
139
+ end
140
+ end
141
+ end
142
+
143
+ def update_signature(changes:, &block)
144
+ Steep.logger.tagged "#update_signature" do
145
+ updated_targets = []
146
+
147
+ Steep.measure2 "Update signatures" do |sampler|
148
+ project.targets.each do |target|
149
+ sampler.sample target.name.to_s do
150
+ signature_service = signature_services[target.name]
151
+ signature_changes = changes.filter {|path, _| target.possible_signature_file?(path) }
152
+
153
+ unless signature_changes.empty?
154
+ updated_targets << target
155
+ signature_service.update(signature_changes)
156
+ end
157
+ end
158
+ end
159
+ end
160
+
161
+ Steep.measure "signature validations" do
162
+ accumulated_diagnostics = {}
163
+
164
+ updated_targets.each do |target|
165
+ service = signature_services[target.name]
166
+
167
+ next if no_type_checking?
168
+
169
+ case service.status
170
+ when SignatureService::SyntaxErrorStatus, SignatureService::AncestorErrorStatus
171
+ service.status.diagnostics.group_by {|diag| Pathname(diag.location.buffer.name) }.each do |path, diagnostics|
172
+ if assignment =~ path
173
+ array = accumulated_diagnostics[path] ||= []
174
+ array.push(*diagnostics)
175
+ yield [path, array]
176
+ end
177
+ end
178
+ when SignatureService::LoadedStatus
179
+ validator = Signature::Validator.new(checker: service.current_subtyping)
180
+ paths = service.each_rbs_path.select {|path| assignment =~ path }.to_set
181
+ type_names = service.type_names(paths: paths, env: service.latest_env).to_set
182
+
183
+ Steep.measure2 "Validating #{type_names.size} types from #{paths.size} files" do |sampler|
184
+ type_names.each do |type_name|
185
+ sampler.sample type_name.to_s do
186
+ case
187
+ when type_name.class?
188
+ validator.validate_one_class(type_name)
189
+ when type_name.interface?
190
+ validator.validate_one_interface(type_name)
191
+ when type_name.alias?
192
+ validator.validate_one_alias(type_name)
193
+ end
194
+ end
195
+ end
196
+ end
197
+
198
+ Steep.measure "Validating const and globals" do
199
+ validator.validate_const()
200
+ validator.validate_global()
201
+ end
202
+
203
+ target_diagnostics = validator.each_error.group_by {|error| Pathname(error.location.buffer.name) }
204
+ signature_validation_diagnostics[target.name] = target_diagnostics
205
+
206
+ paths.each do |path|
207
+ array = (accumulated_diagnostics[path] ||= [])
208
+ if ds = target_diagnostics[path]
209
+ array.push(*ds)
210
+ end
211
+ yield [path, array]
212
+ end
213
+ end
214
+ end
215
+ end
216
+
217
+ updated_targets
218
+ end
219
+ end
220
+
221
+ def update_target(changes:, target:, updated:, &block)
222
+ Steep.logger.tagged "#update_target" do
223
+ contents = {}
224
+
225
+ if updated
226
+ source_files.each do |path, file|
227
+ if target.possible_source_file?(path)
228
+ contents[path] = file.content
229
+ end
230
+ end
231
+
232
+ changes.each do |path, changes|
233
+ if target.possible_source_file?(path)
234
+ text = contents[path] || ""
235
+ contents[path] = changes.inject(text) {|text, change| change.apply_to(text) }
236
+ end
237
+ end
238
+ else
239
+ changes.each do |path, changes|
240
+ if target.possible_source_file?(path)
241
+ text = source_files[path]&.content || ""
242
+ contents[path] = changes.inject(text) {|text, change| change.apply_to(text) }
243
+ end
244
+ end
245
+ end
246
+
247
+ signature_service = signature_services[target.name]
248
+ subtyping = signature_service.current_subtyping
249
+
250
+ Steep.measure2 "Type check sources" do |sampler|
251
+ contents.each do |path, text|
252
+ if assignment =~ path
253
+ if subtyping
254
+ file = sampler.sample(path.to_s) do
255
+ type_check_file(target: target, subtyping: subtyping, path: path, text: text)
256
+ end
257
+ yield [file.path, file.diagnostics]
258
+ else
259
+ if source_files.key?(path)
260
+ file = source_files[path]&.update_content(text)
261
+ else
262
+ file = SourceFile.no_data(path: path, content: text)
263
+ yield [file.path, []]
264
+ end
265
+ end
266
+
267
+ source_files[path] = file
268
+ end
269
+ end
270
+ end
271
+ end
272
+ end
273
+
274
+ def type_check_file(target:, subtyping:, path:, text:)
275
+ Steep.logger.tagged "#type_check_file(#{path}@#{target.name})" do
276
+ if no_type_checking?
277
+ SourceFile.no_data(path: path, content: text)
278
+ else
279
+ source = Source.parse(text, path: path, factory: subtyping.factory)
280
+ typing = TypeCheckService.type_check(source: source, subtyping: subtyping)
281
+ SourceFile.with_typing(path: path, content: text, node: source.node, typing: typing)
282
+ end
283
+ end
284
+ rescue AnnotationParser::SyntaxError => exn
285
+ SourceFile.no_data(path: path, content: text)
286
+ # SourceFile.with_syntax_error(path: path, content: text, error: exn)
287
+ rescue ::Parser::SyntaxError, EncodingError => exn
288
+ SourceFile.no_data(path: path, content: text)
289
+ # SourceFile.with_syntax_error(path: path, content: text, error: exn)
290
+ end
291
+
292
+ def self.type_check(source:, subtyping:)
293
+ annotations = source.annotations(block: source.node, factory: subtyping.factory, current_module: RBS::Namespace.root)
294
+ const_env = TypeInference::ConstantEnv.new(factory: subtyping.factory, context: [RBS::Namespace.root])
295
+ type_env = TypeInference::TypeEnv.build(annotations: annotations,
296
+ subtyping: subtyping,
297
+ const_env: const_env,
298
+ signatures: subtyping.factory.env)
299
+ lvar_env = TypeInference::LocalVariableTypeEnv.empty(
300
+ subtyping: subtyping,
301
+ self_type: AST::Builtin::Object.instance_type,
302
+ instance_type: AST::Builtin::Object.instance_type,
303
+ class_type: AST::Builtin::Object.module_type
304
+ ).annotate(annotations)
305
+
306
+ context = TypeInference::Context.new(
307
+ block_context: nil,
308
+ module_context: TypeInference::Context::ModuleContext.new(
309
+ instance_type: AST::Builtin::Object.instance_type,
310
+ module_type: AST::Builtin::Object.module_type,
311
+ implement_name: nil,
312
+ current_namespace: RBS::Namespace.root,
313
+ const_env: const_env,
314
+ class_name: AST::Builtin::Object.module_name,
315
+ instance_definition: subtyping.factory.definition_builder.build_instance(AST::Builtin::Object.module_name),
316
+ module_definition: subtyping.factory.definition_builder.build_singleton(AST::Builtin::Object.module_name)
317
+ ),
318
+ method_context: nil,
319
+ break_context: nil,
320
+ self_type: AST::Builtin::Object.instance_type,
321
+ type_env: type_env,
322
+ lvar_env: lvar_env,
323
+ call_context: TypeInference::MethodCall::TopLevelContext.new
324
+ )
325
+
326
+ typing = Typing.new(source: source, root_context: context)
327
+
328
+ construction = TypeConstruction.new(
329
+ checker: subtyping,
330
+ annotations: annotations,
331
+ source: source,
332
+ context: context,
333
+ typing: typing
334
+ )
335
+
336
+ construction.synthesize(source.node) if source.node
337
+
338
+ typing
339
+ end
340
+ end
341
+ end
342
+ end
@@ -61,22 +61,176 @@ 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
- rescue_validation_errors do
66
- Steep.logger.debug "Validating class definition `#{name}`..."
113
+ rescue_validation_errors(name) do
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
76
230
  end
77
231
 
78
232
  def validate_one_interface(name)
79
- rescue_validation_errors do
233
+ rescue_validation_errors(name) do
80
234
  Steep.logger.debug "Validating interface `#{name}`..."
81
235
  Steep.logger.tagged "#{name}" do
82
236
  builder.build_interface(name).each_type do |type|
@@ -115,37 +269,25 @@ module Steep
115
269
  end
116
270
  end
117
271
 
272
+ def validate_one_alias(name)
273
+ rescue_validation_errors(name) do
274
+ Steep.logger.debug "Validating alias `#{name}`..."
275
+ builder.expand_alias(name).tap do |type|
276
+ validate_type(type)
277
+ end
278
+ end
279
+ end
280
+
118
281
  def validate_alias
119
282
  env.alias_decls.each do |name, entry|
120
- rescue_validation_errors do
121
- Steep.logger.debug "Validating alias `#{name}`..."
122
- builder.expand_alias(name).tap do |type|
123
- validate_type(type)
124
- end
125
- end
283
+ validate_one_alias(name)
126
284
  end
127
285
  end
128
286
 
129
- def rescue_validation_errors
287
+ def rescue_validation_errors(type_name = nil)
130
288
  yield
131
- rescue RBS::InvalidTypeApplicationError => exn
132
- @errors << Errors::InvalidTypeApplicationError.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, RBS::NoSuperclassFoundError, RBS::NoMixinFoundError => exn
139
- @errors << Errors::UnknownTypeNameError.new(
140
- name: exn.type_name,
141
- location: exn.location
142
- )
143
- rescue RBS::InvalidOverloadMethodError => exn
144
- @errors << Errors::InvalidMethodOverloadError.new(
145
- class_name: exn.type_name,
146
- method_name: exn.method_name,
147
- location: exn.members[0].location
148
- )
289
+ rescue RBS::ErrorBase => exn
290
+ @errors << Diagnostic::Signature.from_rbs_error(exn, factory: factory)
149
291
  end
150
292
  end
151
293
  end