steep 0.9.0 → 0.10.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 05a765d5cdc7792406c32f581e041ee9e47b41c1b70dfb0d43e8db5400fbe172
4
- data.tar.gz: 71e35e1516a261d82082e1278774ff0a8d36c5ffecdd8511ba8de312b93a16f8
3
+ metadata.gz: 63ee9143e226c595a96dd859109327d66f97ab2c428601c30f771a12a21a1603
4
+ data.tar.gz: ec8d8d3cdcde5c0e37c73fdb70faeb0887b5b36ae1590d57dbab1feea95f86ad
5
5
  SHA512:
6
- metadata.gz: b721b3ac5bb01d3296071025ef72a7e2ab76f4900bdfb0909ee8a715642d371812185c4adc74585344a156c43af742e0e35782b8dc9f89966da6d558e7f87f83
7
- data.tar.gz: 3849077a1ea0d7109eb3b148be3dc2e8260f9cc63df17cb1864235fa2137faa984947a64d95343524c1b34d75fcf4ff9ada215488e15977bcc657359da3cac4c
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.8.3 (2018-11-11)
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)
@@ -50,7 +50,7 @@ module Steep
50
50
  end
51
51
 
52
52
  def with_location(new_location)
53
- self.class.new(location: new_location)
53
+ self.class.new(location: new_location, params: params, return_type: return_type)
54
54
  end
55
55
 
56
56
  def map_type(&block)
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
- each_ruby_source(source_paths, false) do |source|
21
- source.each_annotation.sort_by {|node, _| [node.loc.expression.begin_pos, node.loc.expression.end_pos] }.each do |node, annotations|
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 "#{source.path}:#{loc.line}:#{loc.column}:#{node.type}:\t#{node.loc.expression.source.lines.first}"
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
@@ -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
- @labeling = ASTUtils::Labeling.new
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
- env = AST::Signature::Env.new
42
+ project = Project.new(Project::SyntaxErrorRaisingListener.new)
38
43
 
39
- each_signature(signature_dirs, verbose) do |signature|
40
- env.add signature
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
- builder = Interface::Builder.new(signatures: env)
44
- check = Subtyping::Check.new(builder: builder)
45
-
46
- validator = Utils::Validator.new(stdout: stdout, stderr: stderr, verbose: verbose)
47
-
48
- validated = validator.run(env: env, builder: builder, check: check) do |sig|
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
- unless validated
53
- return 1
54
- end
60
+ project.type_check
55
61
 
56
- sources = []
57
- each_ruby_source(source_paths, verbose) do |source|
58
- sources << source
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
- typing = Typing.new
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
- lines = []
98
-
99
- typing.nodes.each_value do |node|
100
- begin
101
- type = typing.type_of(node: node)
102
- lines << [node.loc.expression.source_buffer.name, [node.loc.last_line,node.loc.last_column], [node.loc.first_line, node.loc.column], node, type]
103
- rescue
104
- lines << [node.loc.expression.source_buffer.name, [node.loc.last_line,node.loc.last_column], [node.loc.first_line, node.loc.column], node, nil]
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
- lines.sort {|x,y| y <=> x }.reverse_each do |line|
109
- source = line[3].loc.expression.source
110
- stdout.puts "#{line[0]}:(#{line[2].join(",")}):(#{line[1].join(",")}):\t#{line[3].type}:\t#{line[4]}\t(#{source.split(/\n/).first})"
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
- errors = typing.errors.select do |error|
115
- case
116
- when error.is_a?(Errors::FallbackAny) && !fallback_any_is_error
117
- false
118
- when error.is_a?(Errors::MethodDefinitionMissing) && allow_missing_definitions
119
- false
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
- true
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
- env = AST::Signature::Env.new
23
-
24
- each_signature(signature_dirs, false) do |signature|
25
- env.add signature
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
- begin
29
- builder = Interface::Builder.new(signatures: env)
30
- check = Subtyping::Check.new(builder: builder)
31
+ project.reload_signature
31
32
 
32
- interface = check.resolve(type)
33
+ case sig = project.signature
34
+ when Project::SignatureLoaded
35
+ begin
36
+ check = sig.check
37
+ interface = check.resolve(type)
33
38
 
34
- stdout.puts "#{type}"
35
- stdout.puts "- Instance variables:"
36
- interface.ivars.each do |name, type|
37
- puts " - #{name}: #{type}"
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
- stdout.puts "- Methods:"
40
- interface.methods.each do |name, method|
41
- puts " - #{Rainbow(name).blue}:"
42
- method.types.each do |method_type|
43
- loc = if method_type.location
44
- "#{method_type.location.buffer.name}:#{method_type.location.to_s}"
45
- else
46
- "no location"
47
- end
48
- puts " - #{Rainbow(method_type.to_s).red} (#{loc})"
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
- 0
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
- each_ruby_source(source_paths, false) do |source|
21
- Generator.new(source: source, stderr: stderr).write(io: stdout)
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 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
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
- if path.directory?
45
- each_file_in_dir(".rb", path) do |file|
46
- yield file
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
- env = AST::Signature::Env.new
22
+ project = Project.new
23
23
 
24
- each_signature(signature_dirs, verbose) do |signature|
25
- env.add signature
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
- builder = Interface::Builder.new(signatures: env)
29
- check = Subtyping::Check.new(builder: builder)
32
+ project.type_check
30
33
 
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 ? 0 : 1
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