steep 0.9.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -1
- data/lib/steep/ast/types/proc.rb +1 -1
- data/lib/steep/cli.rb +25 -4
- data/lib/steep/drivers/annotations.rb +14 -3
- data/lib/steep/drivers/check.rb +71 -75
- data/lib/steep/drivers/print_interface.rb +56 -25
- data/lib/steep/drivers/scaffold.rb +7 -4
- data/lib/steep/drivers/utils/each_signature.rb +6 -41
- data/lib/steep/drivers/validate.rb +35 -10
- data/lib/steep/drivers/watch.rb +188 -0
- data/lib/steep/errors.rb +2 -2
- data/lib/steep/project/file.rb +119 -0
- data/lib/steep/project/listener.rb +53 -0
- data/lib/steep/project/options.rb +13 -0
- data/lib/steep/project.rb +231 -0
- data/lib/steep/type_construction.rb +56 -29
- data/lib/steep/version.rb +1 -1
- data/lib/steep.rb +7 -1
- data/sig/project.rbi +106 -0
- data/smoke/hash/f.rb +11 -0
- data/stdlib/builtin.rbi +3 -0
- data/steep.gemspec +3 -1
- metadata +39 -7
- data/lib/steep/drivers/utils/validator.rb +0 -110
- data/sig/signature.rbi +0 -146
- data/sig/types.rbi +0 -43
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 63ee9143e226c595a96dd859109327d66f97ab2c428601c30f771a12a21a1603
|
4
|
+
data.tar.gz: ec8d8d3cdcde5c0e37c73fdb70faeb0887b5b36ae1590d57dbab1feea95f86ad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5297a41597af66524e848e02f2c6f6b33d53d9fc52aac03280ec438845414e306dc3ce6a86b643608d34e89e1cabacfc75262013f7f78970661af88d4679f624
|
7
|
+
data.tar.gz: 610747408983f5e98ee292b5a3795e7b61c8dd74b6d255e3768f5c3658676f63c7dd8157cf7cbba492c98d36806e9c08482dcf5d58474e7ffaaaf78ea93c1157
|
data/CHANGELOG.md
CHANGED
@@ -2,7 +2,14 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
-
## 0.
|
5
|
+
## 0.10.0 (2019-03-05)
|
6
|
+
|
7
|
+
* Add `watch` subcommand (#77)
|
8
|
+
* Automatically `@implements` super class if the class definition inherits
|
9
|
+
* Fix tuple typing
|
10
|
+
* Fix `or` typing
|
11
|
+
|
12
|
+
## 0.9.0 (2018-11-11)
|
6
13
|
|
7
14
|
* Private methods (#72)
|
8
15
|
* `__skip__` to skip type checking (#73)
|
data/lib/steep/ast/types/proc.rb
CHANGED
data/lib/steep/cli.rb
CHANGED
@@ -89,7 +89,7 @@ module Steep
|
|
89
89
|
end
|
90
90
|
|
91
91
|
def paths
|
92
|
-
options = if @options.none? {|option| option.is_a?(Pathname) }
|
92
|
+
options = if @options.none? {|option| option.is_a?(Pathname) } && Pathname("sig").directory?
|
93
93
|
@options + [Pathname("sig")]
|
94
94
|
else
|
95
95
|
@options
|
@@ -132,7 +132,7 @@ module Steep
|
|
132
132
|
end
|
133
133
|
|
134
134
|
def self.available_commands
|
135
|
-
[:check, :validate, :annotations, :scaffold, :interface, :version, :paths]
|
135
|
+
[:check, :validate, :annotations, :scaffold, :interface, :version, :paths, :watch]
|
136
136
|
end
|
137
137
|
|
138
138
|
def process_global_options
|
@@ -228,13 +228,11 @@ module Steep
|
|
228
228
|
def process_annotations
|
229
229
|
source_paths = argv.map {|file| Pathname(file) }
|
230
230
|
Drivers::Annotations.new(source_paths: source_paths, stdout: stdout, stderr: stderr).run
|
231
|
-
0
|
232
231
|
end
|
233
232
|
|
234
233
|
def process_scaffold
|
235
234
|
source_paths = argv.map {|file| Pathname(file) }
|
236
235
|
Drivers::Scaffold.new(source_paths: source_paths, stdout: stdout, stderr: stderr).run
|
237
|
-
0
|
238
236
|
end
|
239
237
|
|
240
238
|
def process_interface
|
@@ -244,6 +242,29 @@ module Steep
|
|
244
242
|
end.parse!(argv)
|
245
243
|
|
246
244
|
Drivers::PrintInterface.new(type_name: argv.first, signature_dirs: signature_options.paths, stdout: stdout, stderr: stderr).run
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def process_watch
|
249
|
+
with_signature_options do |signature_options|
|
250
|
+
strict = false
|
251
|
+
fallback_any_is_error = false
|
252
|
+
|
253
|
+
OptionParser.new do |opts|
|
254
|
+
handle_dir_options opts, signature_options
|
255
|
+
opts.on("--strict") { strict = true }
|
256
|
+
opts.on("--fallback-any-is-error") { fallback_any_is_error = true }
|
257
|
+
end.parse!(argv)
|
258
|
+
|
259
|
+
source_dirs = argv.map {|path| Pathname(path) }
|
260
|
+
if source_dirs.empty?
|
261
|
+
source_dirs << Pathname(".")
|
262
|
+
end
|
263
|
+
|
264
|
+
Drivers::Watch.new(source_dirs: source_dirs, signature_dirs: signature_options.paths, stdout: stdout, stderr: stderr).tap do |driver|
|
265
|
+
driver.options.fallback_any_is_error = fallback_any_is_error || strict
|
266
|
+
driver.options.allow_missing_definitions = false if strict
|
267
|
+
end.run
|
247
268
|
|
248
269
|
0
|
249
270
|
end
|
@@ -17,10 +17,21 @@ module Steep
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def run
|
20
|
-
|
21
|
-
|
20
|
+
project = Project.new()
|
21
|
+
|
22
|
+
source_paths.each do |path|
|
23
|
+
each_file_in_path(".rb", path) do |file_path|
|
24
|
+
file = Project::SourceFile.new(path: file_path, options: Project::Options.new)
|
25
|
+
file.content = file_path.read
|
26
|
+
project.source_files[file_path] = file
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
project.source_files.each_value do |file|
|
31
|
+
file.parse
|
32
|
+
file.source.each_annotation.sort_by {|node, _| [node.loc.expression.begin_pos, node.loc.expression.end_pos] }.each do |node, annotations|
|
22
33
|
loc = node.loc
|
23
|
-
stdout.puts "#{
|
34
|
+
stdout.puts "#{file.path}:#{loc.line}:#{loc.column}:#{node.type}:\t#{node.loc.expression.source.lines.first}"
|
24
35
|
annotations.each do |annotation|
|
25
36
|
stdout.puts " #{annotation.location.source}"
|
26
37
|
end
|
data/lib/steep/drivers/check.rb
CHANGED
@@ -27,106 +27,102 @@ module Steep
|
|
27
27
|
self.dump_all_types = false
|
28
28
|
self.fallback_any_is_error = false
|
29
29
|
self.allow_missing_definitions = true
|
30
|
+
end
|
30
31
|
|
31
|
-
|
32
|
+
def options
|
33
|
+
Project::Options.new.tap do |opt|
|
34
|
+
opt.allow_missing_definitions = allow_missing_definitions
|
35
|
+
opt.fallback_any_is_error = fallback_any_is_error
|
36
|
+
end
|
32
37
|
end
|
33
38
|
|
34
39
|
def run
|
35
40
|
Steep.logger.level = Logger::DEBUG if verbose
|
36
41
|
|
37
|
-
|
42
|
+
project = Project.new(Project::SyntaxErrorRaisingListener.new)
|
38
43
|
|
39
|
-
|
40
|
-
|
44
|
+
source_paths.each do |path|
|
45
|
+
each_file_in_path(".rb", path) do |file_path|
|
46
|
+
file = Project::SourceFile.new(path: file_path, options: options)
|
47
|
+
file.content = file_path.read
|
48
|
+
project.source_files[file_path] = file
|
49
|
+
end
|
41
50
|
end
|
42
51
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
stderr.puts "Validating #{sig.name} (#{sig.location.name}:#{sig.location.start_line})..." if verbose
|
52
|
+
signature_dirs.each do |path|
|
53
|
+
each_file_in_path(".rbi", path) do |file_path|
|
54
|
+
file = Project::SignatureFile.new(path: file_path)
|
55
|
+
file.content = file_path.read
|
56
|
+
project.signature_files[file_path] = file
|
57
|
+
end
|
50
58
|
end
|
51
59
|
|
52
|
-
|
53
|
-
return 1
|
54
|
-
end
|
60
|
+
project.type_check
|
55
61
|
|
56
|
-
|
57
|
-
|
58
|
-
|
62
|
+
case signature = project.signature
|
63
|
+
when Project::SignatureLoaded
|
64
|
+
output_type_check_result(project)
|
65
|
+
project.has_type_error? ? 1 : 0
|
66
|
+
when Project::SignatureHasError
|
67
|
+
output_signature_errors(project)
|
68
|
+
1
|
59
69
|
end
|
70
|
+
end
|
60
71
|
|
61
|
-
|
62
|
-
|
63
|
-
sources.each do |source|
|
64
|
-
Steep.logger.tagged source.path do
|
65
|
-
Steep.logger.debug "Typechecking..."
|
66
|
-
annotations = source.annotations(block: source.node, builder: check.builder, current_module: AST::Namespace.root)
|
67
|
-
|
68
|
-
const_env = TypeInference::ConstantEnv.new(builder: check.builder, context: 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::Builtin::Object.instance_type,
|
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: AST::Namespace.root,
|
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
|
94
|
-
end
|
72
|
+
def output_type_check_result(project)
|
73
|
+
# @type var project: Project
|
95
74
|
|
96
75
|
if dump_all_types
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
76
|
+
project.source_files.each_value do |file|
|
77
|
+
lines = []
|
78
|
+
|
79
|
+
if typing = file.typing
|
80
|
+
typing.nodes.each_value do |node|
|
81
|
+
begin
|
82
|
+
type = typing.type_of(node: node)
|
83
|
+
lines << [node.loc.expression.source_buffer.name, [node.loc.last_line,node.loc.last_column], [node.loc.first_line, node.loc.column], node, type]
|
84
|
+
rescue
|
85
|
+
lines << [node.loc.expression.source_buffer.name, [node.loc.last_line,node.loc.last_column], [node.loc.first_line, node.loc.column], node, nil]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
lines.sort {|x,y| y <=> x }.reverse_each do |line|
|
90
|
+
source = line[3].loc.expression.source
|
91
|
+
stdout.puts "#{line[0]}:(#{line[2].join(",")}):(#{line[1].join(",")}):\t#{line[3].type}:\t#{line[4]}\t(#{source.split(/\n/).first})"
|
92
|
+
end
|
105
93
|
end
|
106
94
|
end
|
95
|
+
end
|
107
96
|
|
108
|
-
|
109
|
-
|
110
|
-
|
97
|
+
project.source_files.each_value do |file|
|
98
|
+
file.errors&.each do |error|
|
99
|
+
error.print_to stdout
|
111
100
|
end
|
112
101
|
end
|
102
|
+
end
|
113
103
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
104
|
+
def output_signature_errors(project)
|
105
|
+
project.signature.errors.each do |error|
|
106
|
+
case error
|
107
|
+
when Interface::Instantiated::InvalidMethodOverrideError
|
108
|
+
stdout.puts "😱 #{error.message}"
|
109
|
+
error.result.trace.each do |s, t|
|
110
|
+
case s
|
111
|
+
when Interface::Method
|
112
|
+
stdout.puts " #{s.name}(#{s.type_name}) <: #{t.name}(#{t.type_name})"
|
113
|
+
when Interface::MethodType
|
114
|
+
stdout.puts " #{s} <: #{t} (#{s.location&.name||"?"}:#{s.location&.start_line||"?"})"
|
115
|
+
else
|
116
|
+
stdout.puts " #{s} <: #{t}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
stdout.puts " 🚨 #{error.result.error.message}"
|
120
|
+
when Interface::Instantiated::InvalidIvarOverrideError
|
121
|
+
stdout.puts "😱 #{error.message}"
|
120
122
|
else
|
121
|
-
|
123
|
+
stdout.puts "😱 #{error.inspect}"
|
122
124
|
end
|
123
125
|
end
|
124
|
-
|
125
|
-
errors.each do |error|
|
126
|
-
error.print_to stdout
|
127
|
-
end
|
128
|
-
|
129
|
-
errors.empty? ? 0 : 1
|
130
126
|
end
|
131
127
|
end
|
132
128
|
end
|
@@ -18,39 +18,70 @@ module Steep
|
|
18
18
|
def run
|
19
19
|
if type_name
|
20
20
|
type = Parser.parse_type(type_name)
|
21
|
+
project = Project.new()
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
signature_dirs.each do |path|
|
24
|
+
each_file_in_path(".rbi", path) do |file_path|
|
25
|
+
file = Project::SignatureFile.new(path: file_path)
|
26
|
+
file.content = file_path.read
|
27
|
+
project.signature_files[file_path] = file
|
28
|
+
end
|
26
29
|
end
|
27
30
|
|
28
|
-
|
29
|
-
builder = Interface::Builder.new(signatures: env)
|
30
|
-
check = Subtyping::Check.new(builder: builder)
|
31
|
+
project.reload_signature
|
31
32
|
|
32
|
-
|
33
|
+
case sig = project.signature
|
34
|
+
when Project::SignatureLoaded
|
35
|
+
begin
|
36
|
+
check = sig.check
|
37
|
+
interface = check.resolve(type)
|
33
38
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
39
|
+
stdout.puts "#{type}"
|
40
|
+
stdout.puts "- Instance variables:"
|
41
|
+
interface.ivars.each do |name, type|
|
42
|
+
puts " - #{name}: #{type}"
|
43
|
+
end
|
44
|
+
stdout.puts "- Methods:"
|
45
|
+
interface.methods.each do |name, method|
|
46
|
+
puts " - #{Rainbow(name).blue}:"
|
47
|
+
method.types.each do |method_type|
|
48
|
+
loc = if method_type.location
|
49
|
+
"#{method_type.location.buffer.name}:#{method_type.location.to_s}"
|
50
|
+
else
|
51
|
+
"no location"
|
52
|
+
end
|
53
|
+
puts " - #{Rainbow(method_type.to_s).red} (#{loc})"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
0
|
57
|
+
rescue Steep::Subtyping::Check::CannotResolveError
|
58
|
+
stderr.puts "🤔 #{Rainbow(type.to_s).red} cannot be resolved to interface"
|
59
|
+
1
|
38
60
|
end
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
61
|
+
|
62
|
+
when Project::SignatureHasError
|
63
|
+
sig.errors.each do |error|
|
64
|
+
case error
|
65
|
+
when Interface::Instantiated::InvalidMethodOverrideError
|
66
|
+
stdout.puts "😱 #{error.message}"
|
67
|
+
error.result.trace.each do |s, t|
|
68
|
+
case s
|
69
|
+
when Interface::Method
|
70
|
+
stdout.puts " #{s.name}(#{s.type_name}) <: #{t.name}(#{t.type_name})"
|
71
|
+
when Interface::MethodType
|
72
|
+
stdout.puts " #{s} <: #{t} (#{s.location&.name||"?"}:#{s.location&.start_line||"?"})"
|
73
|
+
else
|
74
|
+
stdout.puts " #{s} <: #{t}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
stdout.puts " 🚨 #{error.result.error.message}"
|
78
|
+
when Interface::Instantiated::InvalidIvarOverrideError
|
79
|
+
stdout.puts "😱 #{error.message}"
|
80
|
+
else
|
81
|
+
stdout.puts "😱 #{error.inspect}"
|
49
82
|
end
|
50
83
|
end
|
51
|
-
|
52
|
-
rescue Steep::Subtyping::Check::CannotResolveError
|
53
|
-
stderr.puts "🤔 #{Rainbow(type.to_s).red} cannot be resolved to interface"
|
84
|
+
|
54
85
|
1
|
55
86
|
end
|
56
87
|
else
|
@@ -12,13 +12,16 @@ module Steep
|
|
12
12
|
@source_paths = source_paths
|
13
13
|
@stdout = stdout
|
14
14
|
@stderr = stderr
|
15
|
-
|
16
|
-
@labeling = ASTUtils::Labeling.new
|
17
15
|
end
|
18
16
|
|
19
17
|
def run
|
20
|
-
|
21
|
-
|
18
|
+
source_paths.each do |path|
|
19
|
+
each_file_in_path(".rb", path) do |file_path|
|
20
|
+
file = Project::SourceFile.new(path: file_path, options: Project::Options.new)
|
21
|
+
file.content = file_path.read
|
22
|
+
file.parse
|
23
|
+
Generator.new(source: file.source, stderr: stderr).write(io: stdout)
|
24
|
+
end
|
22
25
|
end
|
23
26
|
0
|
24
27
|
end
|
@@ -2,49 +2,14 @@ module Steep
|
|
2
2
|
module Drivers
|
3
3
|
module Utils
|
4
4
|
module EachSignature
|
5
|
-
def
|
6
|
-
|
7
|
-
|
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
|
5
|
+
def each_file_in_path(suffix, path)
|
6
|
+
if path.file?
|
7
|
+
yield path
|
35
8
|
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
9
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
10
|
+
if path.directory?
|
11
|
+
each_file_in_dir(suffix, path) do |file|
|
12
|
+
yield file
|
48
13
|
end
|
49
14
|
end
|
50
15
|
end
|
@@ -19,20 +19,45 @@ module Steep
|
|
19
19
|
def run
|
20
20
|
Steep.logger.level = Logger::DEBUG if verbose
|
21
21
|
|
22
|
-
|
22
|
+
project = Project.new
|
23
23
|
|
24
|
-
|
25
|
-
|
24
|
+
signature_dirs.each do |path|
|
25
|
+
each_file_in_path(".rbi", path) do |file_path|
|
26
|
+
file = Project::SignatureFile.new(path: file_path)
|
27
|
+
file.content = file_path.read
|
28
|
+
project.signature_files[file_path] = file
|
29
|
+
end
|
26
30
|
end
|
27
31
|
|
28
|
-
|
29
|
-
check = Subtyping::Check.new(builder: builder)
|
32
|
+
project.type_check
|
30
33
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
34
|
+
case project.signature
|
35
|
+
when Project::SignatureHasError
|
36
|
+
project.signature.errors.each do |error|
|
37
|
+
case error
|
38
|
+
when Interface::Instantiated::InvalidMethodOverrideError
|
39
|
+
stdout.puts "😱 #{error.message}"
|
40
|
+
error.result.trace.each do |s, t|
|
41
|
+
case s
|
42
|
+
when Interface::Method
|
43
|
+
stdout.puts " #{s.name}(#{s.type_name}) <: #{t.name}(#{t.type_name})"
|
44
|
+
when Interface::MethodType
|
45
|
+
stdout.puts " #{s} <: #{t} (#{s.location&.name||"?"}:#{s.location&.start_line||"?"})"
|
46
|
+
else
|
47
|
+
stdout.puts " #{s} <: #{t}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
stdout.puts " 🚨 #{error.result.error.message}"
|
51
|
+
when Interface::Instantiated::InvalidIvarOverrideError
|
52
|
+
stdout.puts "😱 #{error.message}"
|
53
|
+
else
|
54
|
+
stdout.puts "😱 #{error.inspect}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
1
|
58
|
+
else
|
59
|
+
0
|
60
|
+
end
|
36
61
|
end
|
37
62
|
end
|
38
63
|
end
|