steep 0.1.0.pre2 → 0.1.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 (119) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +1 -1
  3. data/README.md +146 -33
  4. data/bin/smoke_runner.rb +43 -10
  5. data/lib/steep/ast/annotation/collection.rb +93 -0
  6. data/lib/steep/ast/annotation.rb +131 -0
  7. data/lib/steep/ast/buffer.rb +47 -0
  8. data/lib/steep/ast/location.rb +82 -0
  9. data/lib/steep/ast/method_type.rb +116 -0
  10. data/lib/steep/ast/signature/class.rb +33 -0
  11. data/lib/steep/ast/signature/const.rb +17 -0
  12. data/lib/steep/ast/signature/env.rb +123 -0
  13. data/lib/steep/ast/signature/extension.rb +21 -0
  14. data/lib/steep/ast/signature/gvar.rb +17 -0
  15. data/lib/steep/ast/signature/interface.rb +31 -0
  16. data/lib/steep/ast/signature/members.rb +71 -0
  17. data/lib/steep/ast/signature/module.rb +21 -0
  18. data/lib/steep/ast/type_params.rb +13 -0
  19. data/lib/steep/ast/types/any.rb +39 -0
  20. data/lib/steep/ast/types/bot.rb +39 -0
  21. data/lib/steep/ast/types/class.rb +35 -0
  22. data/lib/steep/ast/types/helper.rb +21 -0
  23. data/lib/steep/ast/types/instance.rb +39 -0
  24. data/lib/steep/ast/types/intersection.rb +74 -0
  25. data/lib/steep/ast/types/name.rb +124 -0
  26. data/lib/steep/ast/types/self.rb +39 -0
  27. data/lib/steep/ast/types/top.rb +39 -0
  28. data/lib/steep/ast/types/union.rb +74 -0
  29. data/lib/steep/ast/types/var.rb +57 -0
  30. data/lib/steep/ast/types/void.rb +35 -0
  31. data/lib/steep/cli.rb +28 -1
  32. data/lib/steep/drivers/annotations.rb +32 -0
  33. data/lib/steep/drivers/check.rb +53 -77
  34. data/lib/steep/drivers/scaffold.rb +303 -0
  35. data/lib/steep/drivers/utils/each_signature.rb +66 -0
  36. data/lib/steep/drivers/utils/validator.rb +115 -0
  37. data/lib/steep/drivers/validate.rb +39 -0
  38. data/lib/steep/errors.rb +291 -19
  39. data/lib/steep/interface/abstract.rb +44 -0
  40. data/lib/steep/interface/builder.rb +470 -0
  41. data/lib/steep/interface/instantiated.rb +126 -0
  42. data/lib/steep/interface/ivar_chain.rb +26 -0
  43. data/lib/steep/interface/method.rb +60 -0
  44. data/lib/steep/{interface.rb → interface/method_type.rb} +111 -100
  45. data/lib/steep/interface/substitution.rb +65 -0
  46. data/lib/steep/module_name.rb +116 -0
  47. data/lib/steep/parser.rb +1314 -814
  48. data/lib/steep/parser.y +536 -175
  49. data/lib/steep/source.rb +220 -25
  50. data/lib/steep/subtyping/check.rb +673 -0
  51. data/lib/steep/subtyping/constraints.rb +275 -0
  52. data/lib/steep/subtyping/relation.rb +41 -0
  53. data/lib/steep/subtyping/result.rb +126 -0
  54. data/lib/steep/subtyping/trace.rb +48 -0
  55. data/lib/steep/subtyping/variable_occurrence.rb +49 -0
  56. data/lib/steep/subtyping/variable_variance.rb +69 -0
  57. data/lib/steep/type_construction.rb +1630 -524
  58. data/lib/steep/type_inference/block_params.rb +100 -0
  59. data/lib/steep/type_inference/constant_env.rb +55 -0
  60. data/lib/steep/type_inference/send_args.rb +222 -0
  61. data/lib/steep/type_inference/type_env.rb +226 -0
  62. data/lib/steep/type_name.rb +27 -7
  63. data/lib/steep/typing.rb +4 -0
  64. data/lib/steep/version.rb +1 -1
  65. data/lib/steep.rb +71 -16
  66. data/smoke/and/a.rb +4 -2
  67. data/smoke/array/a.rb +4 -5
  68. data/smoke/array/b.rb +4 -4
  69. data/smoke/block/a.rb +2 -2
  70. data/smoke/block/a.rbi +2 -0
  71. data/smoke/block/b.rb +15 -0
  72. data/smoke/case/a.rb +3 -3
  73. data/smoke/class/a.rb +3 -3
  74. data/smoke/class/b.rb +0 -2
  75. data/smoke/class/d.rb +2 -2
  76. data/smoke/class/e.rb +1 -1
  77. data/smoke/class/f.rb +2 -2
  78. data/smoke/class/g.rb +8 -0
  79. data/smoke/const/a.rb +3 -3
  80. data/smoke/dstr/a.rb +1 -1
  81. data/smoke/ensure/a.rb +22 -0
  82. data/smoke/enumerator/a.rb +6 -6
  83. data/smoke/enumerator/b.rb +22 -0
  84. data/smoke/extension/a.rb +2 -2
  85. data/smoke/extension/b.rb +3 -3
  86. data/smoke/extension/c.rb +1 -1
  87. data/smoke/hello/hello.rb +2 -2
  88. data/smoke/if/a.rb +4 -2
  89. data/smoke/kwbegin/a.rb +8 -0
  90. data/smoke/literal/a.rb +5 -5
  91. data/smoke/method/a.rb +5 -5
  92. data/smoke/method/a.rbi +4 -0
  93. data/smoke/method/b.rb +29 -0
  94. data/smoke/module/a.rb +3 -3
  95. data/smoke/module/a.rbi +9 -0
  96. data/smoke/module/b.rb +2 -2
  97. data/smoke/module/c.rb +1 -1
  98. data/smoke/module/d.rb +5 -0
  99. data/smoke/module/e.rb +13 -0
  100. data/smoke/module/f.rb +13 -0
  101. data/smoke/rescue/a.rb +62 -0
  102. data/smoke/super/a.rb +2 -2
  103. data/smoke/type_case/a.rb +35 -0
  104. data/smoke/yield/a.rb +2 -2
  105. data/stdlib/builtin.rbi +463 -24
  106. data/steep.gemspec +3 -2
  107. metadata +91 -29
  108. data/lib/steep/annotation.rb +0 -223
  109. data/lib/steep/signature/class.rb +0 -450
  110. data/lib/steep/signature/extension.rb +0 -51
  111. data/lib/steep/signature/interface.rb +0 -49
  112. data/lib/steep/types/any.rb +0 -31
  113. data/lib/steep/types/class.rb +0 -27
  114. data/lib/steep/types/instance.rb +0 -27
  115. data/lib/steep/types/merge.rb +0 -32
  116. data/lib/steep/types/name.rb +0 -57
  117. data/lib/steep/types/union.rb +0 -42
  118. data/lib/steep/types/var.rb +0 -38
  119. data/lib/steep/types.rb +0 -4
@@ -13,6 +13,8 @@ module Steep
13
13
 
14
14
  attr_reader :labeling
15
15
 
16
+ include Utils::EachSignature
17
+
16
18
  def initialize(source_paths:, signature_dirs:, stdout:, stderr:)
17
19
  @source_paths = source_paths
18
20
  @signature_dirs = signature_dirs
@@ -28,45 +30,67 @@ module Steep
28
30
  end
29
31
 
30
32
  def run
31
- assignability = TypeAssignability.new do |a|
32
- each_interface do |signature|
33
- a.add_signature(signature)
34
- end
33
+ Steep.logger.level = Logger::DEBUG if verbose
34
+
35
+ env = AST::Signature::Env.new
36
+
37
+ each_signature(signature_dirs, verbose) do |signature|
38
+ env.add signature
39
+ end
40
+
41
+ builder = Interface::Builder.new(signatures: env)
42
+ check = Subtyping::Check.new(builder: builder)
43
+
44
+ validator = Utils::Validator.new(stdout: stdout, stderr: stderr, verbose: verbose)
45
+
46
+ validated = validator.run(env: env, builder: builder, check: check) do |sig|
47
+ stderr.puts "Validating #{sig.name} (#{sig.location.name}:#{sig.location.start_line})..." if verbose
35
48
  end
36
49
 
37
- assignability.errors.each do |error|
38
- error.puts(stdout)
50
+ unless validated
51
+ return 1
39
52
  end
40
53
 
41
54
  sources = []
42
- each_ruby_source do |source|
55
+ each_ruby_source(source_paths, verbose) do |source|
43
56
  sources << source
44
57
  end
45
58
 
46
59
  typing = Typing.new
47
60
 
48
61
  sources.each do |source|
49
- stdout.puts "Typechecking #{source.path}..." if verbose
50
- annotations = source.annotations(block: source.node) || []
51
-
52
- p annotations if verbose
53
-
54
- construction = TypeConstruction.new(
55
- assignability: assignability,
56
- annotations: annotations,
57
- source: source,
58
- var_types: {},
59
- self_type: nil,
60
- block_context: nil,
61
- module_context: TypeConstruction::ModuleContext.new(
62
- instance_type: nil,
63
- module_type: nil,
64
- const_types: annotations.const_types
65
- ),
66
- method_context: nil,
67
- typing: typing,
68
- )
69
- construction.synthesize(source.node)
62
+ Steep.logger.tagged source.path do
63
+ Steep.logger.debug "Typechecking..."
64
+ annotations = source.annotations(block: source.node) || []
65
+
66
+ pp annotations if verbose
67
+
68
+ const_env = TypeInference::ConstantEnv.new(builder: check.builder, current_namespace: nil)
69
+ type_env = TypeInference::TypeEnv.build(annotations: annotations,
70
+ subtyping: check,
71
+ const_env: const_env,
72
+ signatures: check.builder.signatures)
73
+
74
+ construction = TypeConstruction.new(
75
+ checker: check,
76
+ annotations: annotations,
77
+ source: source,
78
+ self_type: AST::Types::Name.new_instance(name: "::Object"),
79
+ block_context: nil,
80
+ module_context: TypeConstruction::ModuleContext.new(
81
+ instance_type: nil,
82
+ module_type: nil,
83
+ implement_name: nil,
84
+ current_namespace: nil,
85
+ const_env: const_env
86
+ ),
87
+ method_context: nil,
88
+ typing: typing,
89
+ break_context: nil,
90
+ type_env: type_env
91
+ )
92
+ construction.synthesize(source.node)
93
+ end
70
94
  end
71
95
 
72
96
  if dump_all_types
@@ -89,55 +113,7 @@ module Steep
89
113
 
90
114
  typing.errors.each do |error|
91
115
  next if error.is_a?(Errors::FallbackAny) && !fallback_any_is_error
92
- stdout.puts error.to_s
93
- end
94
- end
95
-
96
- def each_interface
97
- signature_dirs.each do |path|
98
- if path.file?
99
- stdout.puts "Loading signature #{path}..." if verbose
100
- Parser.parse_signature(path.read).each do |interface|
101
- yield interface
102
- end
103
- end
104
-
105
- if path.directory?
106
- each_file_in_dir(".rbi", path) do |file|
107
- stdout.puts "Loading signature #{file}..." if verbose
108
- Parser.parse_signature(file.read).each do |interface|
109
- yield interface
110
- end
111
- end
112
- end
113
- end
114
- end
115
-
116
- def each_ruby_source
117
- source_paths.each do |path|
118
- if path.file?
119
- stdout.puts "Loading Ruby program #{path}..." if verbose
120
- yield Source.parse(path.read, path: path.to_s, labeling: labeling)
121
- end
122
-
123
- if path.directory?
124
- each_file_in_dir(".rb", path) do |file|
125
- stdout.puts "Loading Ruby program #{file}..." if verbose
126
- yield Source.parse(file.read, path: file.to_s, labeling: labeling)
127
- end
128
- end
129
- end
130
- end
131
-
132
- def each_file_in_dir(suffix, path, &block)
133
- path.children.each do |child|
134
- if child.directory?
135
- each_file_in_dir(suffix, child, &block)
136
- end
137
-
138
- if child.file? && suffix == child.extname
139
- yield child
140
- end
116
+ error.print_to stdout
141
117
  end
142
118
  end
143
119
  end
@@ -0,0 +1,303 @@
1
+ module Steep
2
+ module Drivers
3
+ class Scaffold
4
+ attr_reader :source_paths
5
+ attr_reader :stdout
6
+ attr_reader :stderr
7
+ attr_reader :labeling
8
+
9
+ include Utils::EachSignature
10
+
11
+ def initialize(source_paths:, stdout:, stderr:)
12
+ @source_paths = source_paths
13
+ @stdout = stdout
14
+ @stderr = stderr
15
+
16
+ @labeling = ASTUtils::Labeling.new
17
+ end
18
+
19
+ def run
20
+ each_ruby_source(source_paths, false) do |source|
21
+ Generator.new(source: source, stderr: stderr).write(io: stdout)
22
+ end
23
+ end
24
+
25
+ class Generator
26
+ class Module
27
+ attr_reader :name
28
+ attr_reader :methods
29
+ attr_reader :singleton_methods
30
+ attr_reader :ivars
31
+ attr_reader :kind
32
+
33
+ def initialize(name:, kind:)
34
+ @kind = kind
35
+ @name = name
36
+ @ivars = {}
37
+ @methods = {}
38
+ @singleton_methods = {}
39
+ end
40
+
41
+ def class?
42
+ kind == :class
43
+ end
44
+
45
+ def module?
46
+ kind == :module
47
+ end
48
+ end
49
+
50
+ attr_reader :source
51
+ attr_reader :modules
52
+ attr_reader :constants
53
+ attr_reader :stderr
54
+
55
+ def initialize(source:, stderr:)
56
+ @source = source
57
+ @stderr = stderr
58
+ @modules = []
59
+ @constants = {}
60
+ end
61
+
62
+ def write(io:)
63
+ generate(source.node, current_path: [])
64
+
65
+ modules.each do |mod|
66
+ unless mod.methods.empty? && mod.singleton_methods.empty?
67
+ io.puts "#{mod.kind} #{mod.name}"
68
+
69
+ mod.ivars.each do |name, type|
70
+ io.puts " #{name}: #{type}"
71
+ end
72
+
73
+ mod.methods.each do |name, type|
74
+ io.puts " def #{name}: #{type}"
75
+ end
76
+
77
+ mod.singleton_methods.each do |name, type|
78
+ io.puts " def self.#{name}: #{type}"
79
+ end
80
+
81
+ io.puts "end"
82
+ io.puts
83
+ end
84
+ end
85
+
86
+ constants.each do |name, ty|
87
+ io.puts "#{name}: #{ty}"
88
+ end
89
+ end
90
+
91
+ def module_name(name)
92
+ if name.type == :const
93
+ prefix = name.children[0]
94
+ if prefix
95
+ "#{module_name(prefix)}::#{name.children[1]}"
96
+ else
97
+ name.children[1].to_s
98
+ end
99
+ else
100
+ stderr.puts "Unexpected node for class name: #{name}"
101
+ return "____"
102
+ end
103
+ end
104
+
105
+ def full_name(current_path, name)
106
+ (current_path + [name]).join("::")
107
+ end
108
+
109
+ def generate(node, current_path:, current_module: nil, is_instance_method: false)
110
+ case node.type
111
+ when :module
112
+ name = module_name(node.children[0])
113
+ mod = Module.new(name: full_name(current_path, name), kind: :module)
114
+ modules << mod
115
+
116
+ if node.children[1]
117
+ generate(node.children[1],
118
+ current_path: current_path + [name],
119
+ current_module: mod)
120
+ end
121
+
122
+ when :class
123
+ name = module_name(node.children[0])
124
+ klass = Module.new(name: full_name(current_path, name), kind: :class)
125
+ modules << klass
126
+
127
+ if node.children[2]
128
+ generate(node.children[2],
129
+ current_path: current_path + [name],
130
+ current_module: klass)
131
+ end
132
+
133
+ when :def
134
+ name, args, body = node.children
135
+
136
+ if current_module
137
+ current_module.methods[name] = "(#{arg_types(args)}) -> #{guess_type(body)}"
138
+ end
139
+
140
+ if body
141
+ generate(body,
142
+ current_path: current_path,
143
+ current_module: current_module,
144
+ is_instance_method: true)
145
+ end
146
+
147
+ when :ivar, :ivasgn
148
+ name = node.children[0]
149
+
150
+ if current_module && is_instance_method
151
+ current_module.ivars[name] = guess_type(node.children[1])
152
+ end
153
+
154
+ each_child_node(node) do |child|
155
+ generate(child,
156
+ current_path: current_path,
157
+ current_module: current_module,
158
+ is_instance_method: is_instance_method)
159
+ end
160
+
161
+ when :defs
162
+ if node.children[0].type == :self
163
+ _, name, args, body = node.children
164
+
165
+ if current_module
166
+ current_module.singleton_methods[name] = "(#{arg_types(args)}) -> #{guess_type(body)}"
167
+ end
168
+
169
+ if body
170
+ generate(body,
171
+ current_path: current_path,
172
+ current_module: current_module,
173
+ is_instance_method: false)
174
+ end
175
+ end
176
+
177
+ when :casgn
178
+ if node.children[0]
179
+ stderr.puts "Unexpected casgn: #{node}, #{node.loc.line}"
180
+ end
181
+
182
+ constants[full_name(current_path, node.children[1])] = guess_type(node.children[2])
183
+
184
+ if node.children[2]
185
+ generate(node.children[2],
186
+ current_path: current_path,
187
+ current_module: current_module,
188
+ is_instance_method: is_instance_method)
189
+ end
190
+
191
+ else
192
+ each_child_node(node) do |child|
193
+ generate(child, current_path: current_path, current_module: current_module, is_instance_method: is_instance_method)
194
+ end
195
+ end
196
+ end
197
+
198
+ def guess_type(node)
199
+ return "any" unless node
200
+ case node.type
201
+ when :false, :true
202
+ "_Boolean"
203
+ when :int
204
+ "Integer"
205
+ when :float
206
+ "Float"
207
+ when :complex
208
+ "Complex"
209
+ when :rational
210
+ "Rational"
211
+ when :str, :dstr, :xstr
212
+ "String"
213
+ when :sym, :dsym
214
+ "Symbol"
215
+ when :regexp
216
+ "Regexp"
217
+ when :array
218
+ "Array<any>"
219
+ when :hash
220
+ "Hash<any, any>"
221
+ when :irange, :erange
222
+ "Range<any>"
223
+ when :lvasgn, :ivasgn, :cvasgn, :gvasgn, :casgn
224
+ guess_type(node.children.last)
225
+ when :send
226
+ if node.children[1] == :[]=
227
+ guess_type(node.children.last)
228
+ else
229
+ "any"
230
+ end
231
+ when :begin
232
+ # should support shortcut return?
233
+ guess_type(node.children.last)
234
+ when :return
235
+ children = node.children
236
+ if children.size == 1
237
+ guess_type(node.children.last)
238
+ else
239
+ "Array<any>" # or Tuple or any?
240
+ end
241
+ when :if
242
+ children = node.children
243
+ if children[2]
244
+ ty1 = guess_type(children[1])
245
+ ty2 = guess_type(children[2])
246
+ if ty1 == ty2
247
+ ty1
248
+ else
249
+ "any"
250
+ end
251
+ else
252
+ "void" # assuming no-else if statement implies void
253
+ end
254
+ when :while, :until, :while_post, :until_post, :for
255
+ "void"
256
+ when :case
257
+ children = node.children
258
+ if children.last
259
+ ty = guess_type(children.last)
260
+ children[1..-2].each do |child|
261
+ return "any" if ty != guess_type(child.children.last)
262
+ end
263
+ ty
264
+ else
265
+ "any"
266
+ end
267
+ when :masgn
268
+ "void" # assuming masgn implies void
269
+ else
270
+ "any"
271
+ end
272
+ end
273
+
274
+ def each_child_node(node, &block)
275
+ node.children.each do |child|
276
+ if child.is_a?(::AST::Node)
277
+ yield child
278
+ end
279
+ end
280
+ end
281
+
282
+ def arg_types(args)
283
+ args.children.map do |arg|
284
+ case arg.type
285
+ when :arg
286
+ "any"
287
+ when :optarg
288
+ "?#{guess_type(arg.children[1])}"
289
+ when :restarg
290
+ "*any"
291
+ when :kwarg
292
+ "#{arg.children.first.name}: any"
293
+ when :kwoptarg
294
+ "?#{arg.children.first.name}: #{guess_type(arg.children[1])}"
295
+ when :kwrestarg
296
+ "**any"
297
+ end
298
+ end.compact.join(", ")
299
+ end
300
+ end
301
+ end
302
+ end
303
+ end
@@ -0,0 +1,66 @@
1
+ module Steep
2
+ module Drivers
3
+ module Utils
4
+ module EachSignature
5
+ def each_signature(signature_dirs, verbose)
6
+ signature_dirs.each do |path|
7
+ if path.file?
8
+ stderr.puts "Loading signature #{path}..." if verbose
9
+ Parser.parse_signature(path.read, name: path).each do |signature|
10
+ yield signature
11
+ end
12
+ end
13
+
14
+ if path.directory?
15
+ each_file_in_dir(".rbi", path) do |file|
16
+ stderr.puts "Loading signature #{file}..." if verbose
17
+ Parser.parse_signature(file.read, name: file).each do |signature|
18
+ yield signature
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ def each_ruby_source(source_paths, verbose)
26
+ each_ruby_file source_paths do |file|
27
+ begin
28
+ stdout.puts "Loading Ruby program #{file}..." if verbose
29
+ if (source = Source.parse(file.read, path: file.to_s, labeling: labeling))
30
+ yield source
31
+ end
32
+ rescue => exn
33
+ Steep.logger.error "Error occured on parsing #{file}: #{exn.inspect}"
34
+ end
35
+ end
36
+ end
37
+
38
+ def each_ruby_file(source_paths)
39
+ source_paths.each do |path|
40
+ if path.file?
41
+ yield path
42
+ end
43
+
44
+ if path.directory?
45
+ each_file_in_dir(".rb", path) do |file|
46
+ yield file
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ def each_file_in_dir(suffix, path, &block)
53
+ path.children.each do |child|
54
+ if child.directory?
55
+ each_file_in_dir(suffix, child, &block)
56
+ end
57
+
58
+ if child.file? && suffix == child.extname
59
+ yield child
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,115 @@
1
+ module Steep
2
+ module Drivers
3
+ module Utils
4
+ class Validator
5
+ attr_reader :stdout
6
+ attr_reader :stderr
7
+ attr_reader :verbose
8
+
9
+ def initialize(stdout:, stderr:, verbose:)
10
+ @stdout = stdout
11
+ @stderr = stderr
12
+ @verbose = verbose
13
+ end
14
+
15
+ def run(env:, builder:, check:)
16
+ result = true
17
+
18
+ env.each do |sig|
19
+ yield sig if block_given?
20
+
21
+ case sig
22
+ when AST::Signature::Interface
23
+ yield_self do
24
+ instance_name = TypeName::Interface.new(name: sig.name)
25
+ instance_interface = builder.build(instance_name)
26
+
27
+ args = instance_interface.params.map {|var| AST::Types::Var.fresh(var) }
28
+ instance_type = AST::Types::Name.new_interface(name: sig.name, args: args)
29
+
30
+ instance_interface.instantiate(type: instance_type,
31
+ args: args,
32
+ instance_type: instance_type,
33
+ module_type: nil).validate(check)
34
+ end
35
+
36
+ when AST::Signature::Module
37
+ yield_self do
38
+ instance_name = TypeName::Instance.new(name: sig.name)
39
+ instance_interface = builder.build(instance_name)
40
+ instance_args = instance_interface.params.map {|var| AST::Types::Var.fresh(var) }
41
+
42
+ module_name = TypeName::Module.new(name: sig.name)
43
+ module_interface = builder.build(module_name)
44
+ module_args = module_interface.params.map {|var| AST::Types::Var.fresh(var) }
45
+
46
+ instance_type = AST::Types::Name.new_instance(name: sig.name, args: instance_args)
47
+ module_type = AST::Types::Name.new_module(name: sig.name, args: module_args)
48
+
49
+ stdout.puts "👀 Validating instance methods..." if verbose
50
+ instance_interface.instantiate(type: instance_type,
51
+ args: instance_args,
52
+ instance_type: instance_type,
53
+ module_type: module_type).validate(check)
54
+
55
+ stdout.puts "👀 Validating class methods..." if verbose
56
+ module_interface.instantiate(type: module_type,
57
+ args: module_args,
58
+ instance_type: instance_type,
59
+ module_type: module_type).validate(check)
60
+ end
61
+
62
+ when AST::Signature::Class
63
+ yield_self do
64
+ instance_name = TypeName::Instance.new(name: sig.name)
65
+ instance_interface = builder.build(instance_name)
66
+ instance_args = instance_interface.params.map {|var| AST::Types::Var.fresh(var) }
67
+
68
+ module_name = TypeName::Class.new(name: sig.name, constructor: nil)
69
+ module_interface = builder.build(module_name)
70
+ module_args = module_interface.params.map {|var| AST::Types::Var.fresh(var) }
71
+
72
+ instance_type = AST::Types::Name.new_instance(name: sig.name, args: instance_args)
73
+ module_type = AST::Types::Name.new_class(name: sig.name, args: module_args, constructor: nil)
74
+
75
+ stdout.puts "👀 Validating instance methods..." if verbose
76
+ instance_interface.instantiate(type: instance_type,
77
+ args: instance_args,
78
+ instance_type: instance_type,
79
+ module_type: module_type).validate(check)
80
+
81
+ stdout.puts "👀 Validating class methods..." if verbose
82
+ module_interface.instantiate(type: module_type,
83
+ args: module_args,
84
+ instance_type: instance_type,
85
+ module_type: module_type).validate(check)
86
+ end
87
+ end
88
+
89
+ rescue Interface::Instantiated::InvalidMethodOverrideError => exn
90
+ result = false
91
+ stdout.puts "😱 #{exn.message}"
92
+ exn.result.trace.each do |s, t|
93
+ case s
94
+ when Interface::Method
95
+ stdout.puts " #{s.name}(#{s.type_name}) <: #{t.name}(#{t.type_name})"
96
+ when Interface::MethodType
97
+ stdout.puts " #{s.location.source} <: #{t.location.source} (#{s.location.name}:#{s.location.start_line})"
98
+ else
99
+ stdout.puts " #{s} <: #{t}"
100
+ end
101
+ end
102
+ stdout.puts " 🚨 #{exn.result.error.message}"
103
+
104
+ rescue Interface::Instantiated::InvalidIvarOverrideError => exn
105
+ result = false
106
+ stdout.puts "😱 #{exn.message}"
107
+
108
+ end
109
+
110
+ result
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,39 @@
1
+ module Steep
2
+ module Drivers
3
+ class Validate
4
+ attr_reader :signature_dirs
5
+ attr_reader :stdout
6
+ attr_reader :stderr
7
+ attr_accessor :verbose
8
+
9
+ def initialize(signature_dirs:, stdout:, stderr:)
10
+ @signature_dirs = signature_dirs
11
+ @stdout = stdout
12
+ @stderr = stderr
13
+
14
+ self.verbose = false
15
+ end
16
+
17
+ include Utils::EachSignature
18
+
19
+ def run
20
+ Steep.logger.level = Logger::DEBUG if verbose
21
+
22
+ env = AST::Signature::Env.new
23
+
24
+ each_signature(signature_dirs, verbose) do |signature|
25
+ env.add signature
26
+ end
27
+
28
+ builder = Interface::Builder.new(signatures: env)
29
+ check = Subtyping::Check.new(builder: builder)
30
+
31
+ validator = Utils::Validator.new(stdout: stdout, stderr: stderr, verbose: verbose)
32
+
33
+ validator.run(env: env, builder: builder, check: check) do |sig|
34
+ stderr.puts "Validating #{sig.name} (#{sig.location.name}:#{sig.location.start_line})..." if verbose
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end