steep 0.1.0.pre2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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