steep 1.8.3 → 1.9.0.dev.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +0 -22
- data/Steepfile +35 -26
- data/bin/rbs-inline +19 -0
- data/lib/steep/cli.rb +38 -5
- data/lib/steep/diagnostic/ruby.rb +11 -58
- data/lib/steep/drivers/annotations.rb +1 -1
- data/lib/steep/drivers/check.rb +103 -1
- data/lib/steep/drivers/checkfile.rb +10 -8
- data/lib/steep/drivers/print_project.rb +83 -40
- data/lib/steep/drivers/utils/driver_helper.rb +39 -6
- data/lib/steep/drivers/watch.rb +24 -2
- data/lib/steep/index/signature_symbol_provider.rb +8 -8
- data/lib/steep/interface/builder.rb +14 -1
- data/lib/steep/interface/function.rb +2 -2
- data/lib/steep/path_helper.rb +4 -2
- data/lib/steep/project/dsl.rb +176 -151
- data/lib/steep/project/group.rb +31 -0
- data/lib/steep/project/pattern.rb +4 -0
- data/lib/steep/project/target.rb +32 -6
- data/lib/steep/project.rb +38 -10
- data/lib/steep/server/custom_methods.rb +16 -0
- data/lib/steep/server/delay_queue.rb +0 -3
- data/lib/steep/server/interaction_worker.rb +2 -11
- data/lib/steep/server/master.rb +129 -279
- data/lib/steep/server/target_group_files.rb +205 -0
- data/lib/steep/server/type_check_controller.rb +366 -0
- data/lib/steep/server/type_check_worker.rb +60 -86
- data/lib/steep/services/file_loader.rb +23 -0
- data/lib/steep/services/goto_service.rb +40 -31
- data/lib/steep/services/hover_provider/singleton_methods.rb +4 -4
- data/lib/steep/services/path_assignment.rb +23 -4
- data/lib/steep/services/type_check_service.rb +76 -159
- data/lib/steep/signature/validator.rb +4 -4
- data/lib/steep/subtyping/check.rb +2 -2
- data/lib/steep/thread_waiter.rb +24 -16
- data/lib/steep/type_construction.rb +12 -3
- data/lib/steep/type_inference/block_params.rb +1 -2
- data/lib/steep/type_inference/context.rb +1 -1
- data/lib/steep/type_inference/type_env.rb +4 -4
- data/lib/steep/type_inference/type_env_builder.rb +1 -1
- data/lib/steep/version.rb +1 -1
- data/lib/steep.rb +6 -4
- data/sample/Steepfile +6 -0
- data/sample/lib/conference.rb +1 -5
- data/steep.gemspec +7 -1
- metadata +9 -6
- data/lib/steep/drivers/validate.rb +0 -65
@@ -1,68 +1,111 @@
|
|
1
|
+
require "active_support/core_ext/hash/keys"
|
2
|
+
|
1
3
|
module Steep
|
2
4
|
module Drivers
|
3
5
|
class PrintProject
|
4
6
|
attr_reader :stdout
|
5
7
|
attr_reader :stderr
|
6
8
|
|
9
|
+
attr_accessor :print_files
|
10
|
+
attr_reader :files
|
11
|
+
|
7
12
|
include Utils::DriverHelper
|
8
13
|
|
9
14
|
def initialize(stdout:, stderr:)
|
10
15
|
@stdout = stdout
|
11
16
|
@stderr = stderr
|
17
|
+
@print_files = false
|
12
18
|
end
|
13
19
|
|
14
|
-
def
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
project.targets.each do |target|
|
20
|
-
source_changes = loader.load_changes(target.source_pattern, changes: {})
|
21
|
-
signature_changes = loader.load_changes(target.signature_pattern, changes: {})
|
22
|
-
|
23
|
-
stdout.puts "Target:"
|
24
|
-
stdout.puts " #{target.name}:"
|
25
|
-
stdout.puts " sources:"
|
26
|
-
stdout.puts " patterns:"
|
27
|
-
target.source_pattern.patterns.each do |pattern|
|
28
|
-
stdout.puts " - #{pattern}"
|
29
|
-
end
|
30
|
-
stdout.puts " ignores:"
|
31
|
-
target.source_pattern.ignores.each do |pattern|
|
32
|
-
stdout.puts " - #{pattern}"
|
33
|
-
end
|
34
|
-
stdout.puts " files:"
|
35
|
-
source_changes.each_key do |path|
|
36
|
-
stdout.puts " - #{path}"
|
20
|
+
def as_json(project)
|
21
|
+
{
|
22
|
+
steepfile: project.steepfile_path.to_s,
|
23
|
+
targets: project.targets.map do |target|
|
24
|
+
target_as_json(target)
|
37
25
|
end
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
stdout.puts " library dirs:"
|
52
|
-
target.new_env_loader(project: project).tap do |loader|
|
26
|
+
}.stringify_keys
|
27
|
+
end
|
28
|
+
|
29
|
+
def target_as_json(target)
|
30
|
+
json = {
|
31
|
+
"name" => target.name.to_s,
|
32
|
+
"source_pattern" => pattern_as_json(target.source_pattern),
|
33
|
+
"signature_pattern" => pattern_as_json(target.signature_pattern),
|
34
|
+
"groups" => target.groups.map do |group|
|
35
|
+
group_as_json(group)
|
36
|
+
end,
|
37
|
+
"libraries" => target.new_env_loader().yield_self do |loader|
|
38
|
+
libs = [] #: Array[library_json]
|
53
39
|
loader.each_dir do |lib, path|
|
54
40
|
case lib
|
55
41
|
when :core
|
56
|
-
|
42
|
+
libs << { "name" => "__core__", "path" => path.to_s }
|
57
43
|
when Pathname
|
58
44
|
raise "Unexpected pathname from loader: path=#{path}"
|
59
45
|
else
|
60
|
-
|
46
|
+
libs << { "name" => lib.name, "version" => lib.version, "path" => path.to_s }
|
61
47
|
end
|
62
48
|
end
|
49
|
+
libs
|
50
|
+
end,
|
51
|
+
"unreferenced" => target.unreferenced
|
52
|
+
} #: target_json
|
53
|
+
|
54
|
+
if files
|
55
|
+
files.each_group_signature_path(target, true) do |path|
|
56
|
+
(json["signature_paths"] ||= []) << path.to_s
|
57
|
+
end
|
58
|
+
files.each_group_source_path(target, true) do |path|
|
59
|
+
(json["source_paths"] ||= []) << path.to_s
|
63
60
|
end
|
64
61
|
end
|
65
62
|
|
63
|
+
json
|
64
|
+
end
|
65
|
+
|
66
|
+
def group_as_json(group)
|
67
|
+
json = {
|
68
|
+
"name" => group.name.to_s,
|
69
|
+
"source_pattern" => pattern_as_json(group.source_pattern),
|
70
|
+
"signature_pattern" => pattern_as_json(group.signature_pattern)
|
71
|
+
} #: group_json
|
72
|
+
|
73
|
+
if files
|
74
|
+
files.each_group_signature_path(group, true) do |path|
|
75
|
+
(json["signature_paths"] ||= []) << path.to_s
|
76
|
+
|
77
|
+
end
|
78
|
+
files.each_group_source_path(group, true) do |path|
|
79
|
+
(json["source_paths"] ||= []) << path.to_s
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
json
|
84
|
+
end
|
85
|
+
|
86
|
+
def pattern_as_json(pattern)
|
87
|
+
{
|
88
|
+
"pattern" => pattern.patterns,
|
89
|
+
"ignore" => pattern.ignores
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
def run
|
94
|
+
project = load_config()
|
95
|
+
if print_files
|
96
|
+
loader = Services::FileLoader.new(base_dir: project.base_dir)
|
97
|
+
@files = files = Server::TargetGroupFiles.new(project)
|
98
|
+
project.targets.each do |target|
|
99
|
+
loader.each_path_in_target(target) do |path|
|
100
|
+
files.add_path(path)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
else
|
104
|
+
@files = nil
|
105
|
+
end
|
106
|
+
|
107
|
+
stdout.puts YAML.dump(as_json(project))
|
108
|
+
|
66
109
|
0
|
67
110
|
end
|
68
111
|
end
|
@@ -3,6 +3,7 @@ module Steep
|
|
3
3
|
module Utils
|
4
4
|
module DriverHelper
|
5
5
|
attr_accessor :steepfile
|
6
|
+
attr_accessor :disable_install_collection
|
6
7
|
|
7
8
|
def load_config(path: steepfile || Pathname("Steepfile"))
|
8
9
|
if path.file?
|
@@ -13,9 +14,11 @@ module Steep
|
|
13
14
|
else
|
14
15
|
Steep.ui_logger.error { "Cannot find a configuration at #{path}: `steep init` to scaffold. Using current directory..." }
|
15
16
|
Project.new(steepfile_path: nil, base_dir: Pathname.pwd).tap do |project|
|
16
|
-
Project::DSL.
|
17
|
-
|
18
|
-
|
17
|
+
Project::DSL.eval(project) do
|
18
|
+
target :'.' do
|
19
|
+
check '.'
|
20
|
+
signature '.'
|
21
|
+
end
|
19
22
|
end
|
20
23
|
end
|
21
24
|
end.tap do |project|
|
@@ -23,17 +26,47 @@ module Steep
|
|
23
26
|
case result = target.options.load_collection_lock
|
24
27
|
when nil, RBS::Collection::Config::Lockfile
|
25
28
|
# ok
|
26
|
-
|
29
|
+
when Pathname
|
30
|
+
# File is missing
|
27
31
|
if result == target.options.collection_config_path
|
28
|
-
|
32
|
+
# Config file is missing
|
33
|
+
Steep.ui_logger.error { "rbs-collection configuration is missing: `#{result}`" }
|
34
|
+
else
|
35
|
+
# Lockfile is missing
|
36
|
+
Steep.ui_logger.error { "Run `rbs collection install` to generate missing lockfile: `#{result}`" }
|
37
|
+
end
|
38
|
+
when YAML::SyntaxError
|
39
|
+
# File is broken
|
40
|
+
Steep.ui_logger.error { "rbs-collection setup is broken:\nsyntax error #{result.inspect}" }
|
41
|
+
when RBS::Collection::Config::CollectionNotAvailable
|
42
|
+
unless disable_install_collection
|
43
|
+
install_collection(target, target.options.collection_config_path || raise)
|
29
44
|
else
|
30
|
-
Steep.ui_logger.error { "Run `rbs collection install` to
|
45
|
+
Steep.ui_logger.error { "Run `rbs collection install` to set up RBS files for gems" }
|
31
46
|
end
|
32
47
|
end
|
33
48
|
end
|
34
49
|
end
|
35
50
|
end
|
36
51
|
|
52
|
+
def install_collection(target, config_path)
|
53
|
+
Steep.ui_logger.info { "Installing RBS files for collection: #{config_path}" }
|
54
|
+
lockfile_path = RBS::Collection::Config.to_lockfile_path(config_path)
|
55
|
+
io = StringIO.new
|
56
|
+
begin
|
57
|
+
RBS::Collection::Installer.new(lockfile_path: lockfile_path, stdout: io).install_from_lockfile()
|
58
|
+
target.options.load_collection_lock(force: true)
|
59
|
+
Steep.ui_logger.debug { "Finished setting up RBS collection: " + io.string }
|
60
|
+
|
61
|
+
result = target.options.load_collection_lock(force: true)
|
62
|
+
unless result.is_a?(RBS::Collection::Config::Lockfile)
|
63
|
+
raise "Failed to set up RBS collection: #{result.inspect}"
|
64
|
+
end
|
65
|
+
rescue => exn
|
66
|
+
Steep.ui_logger.error { "Failed to set up RBS collection: #{exn.inspect}" }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
37
70
|
def request_id
|
38
71
|
SecureRandom.alphanumeric(10)
|
39
72
|
end
|
data/lib/steep/drivers/watch.rb
CHANGED
@@ -112,7 +112,19 @@ module Steep
|
|
112
112
|
end
|
113
113
|
end
|
114
114
|
|
115
|
-
|
115
|
+
params = { library_paths: [], signature_paths: [], code_paths: [] } #: Server::CustomMethods::TypeCheck::params
|
116
|
+
|
117
|
+
(modified + added).each do |path|
|
118
|
+
path = Pathname(path)
|
119
|
+
if target = project.target_for_source_path(path)
|
120
|
+
params[:code_paths] << [target.name.to_s, path.to_s]
|
121
|
+
end
|
122
|
+
if target = project.target_for_signature_path(path)
|
123
|
+
params[:signature_paths] << [target.name.to_s, path.to_s]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
client_writer.write(Server::CustomMethods::TypeCheck.request(SecureRandom.uuid, params))
|
116
128
|
|
117
129
|
stdout.puts Rainbow("done!").bold
|
118
130
|
end.tap(&:start)
|
@@ -120,7 +132,17 @@ module Steep
|
|
120
132
|
begin
|
121
133
|
stdout.puts Rainbow("👀 Watching directories, Ctrl-C to stop.").bold
|
122
134
|
|
123
|
-
|
135
|
+
params = { library_paths: [], signature_paths: [], code_paths: [] } #: Server::CustomMethods::TypeCheck::params
|
136
|
+
file_loader = Services::FileLoader.new(base_dir: project.base_dir)
|
137
|
+
project.targets.each do |target|
|
138
|
+
file_loader.each_path_in_patterns(target.source_pattern, dirs.map(&:to_s)) do |path|
|
139
|
+
params[:code_paths] << [target.name.to_s, project.absolute_path(path).to_s]
|
140
|
+
end
|
141
|
+
file_loader.each_path_in_patterns(target.signature_pattern, dirs.map(&:to_s)) do |path|
|
142
|
+
params[:signature_paths] << [target.name.to_s, project.absolute_path(path).to_s]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
client_writer.write(Server::CustomMethods::TypeCheck.request(SecureRandom.uuid, params))
|
124
146
|
|
125
147
|
client_reader.read do |response|
|
126
148
|
case response[:method]
|
@@ -11,7 +11,7 @@ module Steep
|
|
11
11
|
attr_reader :assignment
|
12
12
|
|
13
13
|
def initialize(project:, assignment:)
|
14
|
-
@indexes =
|
14
|
+
@indexes = {}
|
15
15
|
@project = project
|
16
16
|
@assignment = assignment
|
17
17
|
end
|
@@ -39,20 +39,20 @@ module Steep
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
def assigned?(path)
|
42
|
+
def assigned?(target, path)
|
43
43
|
if path.relative?
|
44
44
|
if project.targets.any? {|target| target.possible_signature_file?(path) }
|
45
45
|
path = project.absolute_path(path)
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
assignment =~ path
|
49
|
+
assignment =~ [target, path]
|
50
50
|
end
|
51
51
|
|
52
52
|
def query_symbol(query)
|
53
53
|
symbols = [] #: Array[SymbolInformation]
|
54
54
|
|
55
|
-
indexes.each do |index|
|
55
|
+
indexes.each do |target, index|
|
56
56
|
index.each_entry do |entry|
|
57
57
|
case entry
|
58
58
|
when RBSIndex::TypeEntry
|
@@ -63,7 +63,7 @@ module Steep
|
|
63
63
|
|
64
64
|
entry.declarations.each do |decl|
|
65
65
|
location = decl.location or next
|
66
|
-
next unless assigned?(Pathname(location.buffer.name))
|
66
|
+
next unless assigned?(target, Pathname(location.buffer.name))
|
67
67
|
|
68
68
|
case decl
|
69
69
|
when RBS::AST::Declarations::Class
|
@@ -111,7 +111,7 @@ module Steep
|
|
111
111
|
|
112
112
|
entry.declarations.each do |decl|
|
113
113
|
location = decl.location or next
|
114
|
-
next unless assigned?(Pathname(location.buffer.name))
|
114
|
+
next unless assigned?(target, Pathname(location.buffer.name))
|
115
115
|
|
116
116
|
case decl
|
117
117
|
when RBS::AST::Members::MethodDefinition
|
@@ -151,7 +151,7 @@ module Steep
|
|
151
151
|
|
152
152
|
entry.declarations.each do |decl|
|
153
153
|
next unless decl.location
|
154
|
-
next unless assigned?(Pathname(decl.location.buffer.name))
|
154
|
+
next unless assigned?(target, Pathname(decl.location.buffer.name))
|
155
155
|
|
156
156
|
symbols << SymbolInformation.new(
|
157
157
|
name: entry.const_name.name.to_s,
|
@@ -165,7 +165,7 @@ module Steep
|
|
165
165
|
|
166
166
|
entry.declarations.each do |decl|
|
167
167
|
next unless decl.location
|
168
|
-
next unless assigned?(Pathname(decl.location.buffer.name))
|
168
|
+
next unless assigned?(target, Pathname(decl.location.buffer.name))
|
169
169
|
|
170
170
|
symbols << SymbolInformation.new(
|
171
171
|
name: decl.name.to_s,
|
@@ -282,6 +282,7 @@ module Steep
|
|
282
282
|
method_type = factory.method_type(type_def.type)
|
283
283
|
method_type = replace_primitive_method(method_name, type_def, method_type)
|
284
284
|
method_type = replace_kernel_class(method_name, type_def, method_type) { AST::Builtin::Class.instance_type }
|
285
|
+
method_type = add_implicitly_returns_nil(type_def.annotations, method_type)
|
285
286
|
Shape::MethodOverload.new(method_type, [type_def])
|
286
287
|
end
|
287
288
|
|
@@ -315,6 +316,7 @@ module Steep
|
|
315
316
|
if type_name.class?
|
316
317
|
method_type = replace_kernel_class(method_name, type_def, method_type) { AST::Types::Name::Singleton.new(name: type_name) }
|
317
318
|
end
|
319
|
+
method_type = add_implicitly_returns_nil(type_def.annotations, method_type)
|
318
320
|
Shape::MethodOverload.new(method_type, [type_def])
|
319
321
|
end
|
320
322
|
|
@@ -586,7 +588,7 @@ module Steep
|
|
586
588
|
|
587
589
|
def record_shape(record)
|
588
590
|
all_key_type = AST::Types::Union.build(
|
589
|
-
types: record.elements.each_key.map {|value| AST::Types::Literal.new(value: value) }
|
591
|
+
types: record.elements.each_key.map {|value| AST::Types::Literal.new(value: value).back_type }
|
590
592
|
)
|
591
593
|
all_value_type = AST::Types::Union.build(types: record.elements.values)
|
592
594
|
hash_type = AST::Builtin::Hash.instance_type(all_key_type, all_value_type)
|
@@ -822,6 +824,17 @@ module Steep
|
|
822
824
|
|
823
825
|
method_type
|
824
826
|
end
|
827
|
+
|
828
|
+
def add_implicitly_returns_nil(annotations, method_type)
|
829
|
+
if annotations.find { _1.string == "implicitly-returns-nil" }
|
830
|
+
return_type = method_type.type.return_type
|
831
|
+
method_type = method_type.with(
|
832
|
+
type: method_type.type.with(return_type: AST::Types::Union.build(types: [return_type, AST::Builtin.nil_type]))
|
833
|
+
)
|
834
|
+
else
|
835
|
+
method_type
|
836
|
+
end
|
837
|
+
end
|
825
838
|
end
|
826
839
|
end
|
827
840
|
end
|
@@ -926,10 +926,10 @@ module Steep
|
|
926
926
|
def to_s
|
927
927
|
required = self.required.map {|ty| ty.to_s }
|
928
928
|
optional = self.optional.map {|ty| "?#{ty}" }
|
929
|
-
rest = self.rest ? ["*#{self.rest}"] : []
|
929
|
+
rest = self.rest ? ["*#{self.rest}"] : [] #: Array[String]
|
930
930
|
required_keywords = keyword_params.requireds.map {|name, type| "#{name}: #{type}" }
|
931
931
|
optional_keywords = keyword_params.optionals.map {|name, type| "?#{name}: #{type}"}
|
932
|
-
rest_keywords = keyword_params.rest ? ["**#{keyword_params.rest}"] : []
|
932
|
+
rest_keywords = keyword_params.rest ? ["**#{keyword_params.rest}"] : [] #: Array[String]
|
933
933
|
"(#{(required + optional + rest + required_keywords + optional_keywords + rest_keywords).join(", ")})"
|
934
934
|
end
|
935
935
|
|
data/lib/steep/path_helper.rb
CHANGED
@@ -2,12 +2,14 @@ module Steep
|
|
2
2
|
module PathHelper
|
3
3
|
module_function
|
4
4
|
|
5
|
+
URIParser = URI::RFC2396_Parser.new()
|
6
|
+
|
5
7
|
def to_pathname(uri, dosish: Gem.win_platform?)
|
6
8
|
uri = URI.parse(uri)
|
7
9
|
if uri.scheme == "file"
|
8
10
|
path = uri.path or raise
|
9
11
|
path.sub!(%r{^/([a-zA-Z])(:|%3A)//?}i, '\1:/') if dosish
|
10
|
-
path =
|
12
|
+
path = URIParser.unescape(path)
|
11
13
|
Pathname(path)
|
12
14
|
end
|
13
15
|
end
|
@@ -21,7 +23,7 @@ module Steep
|
|
21
23
|
if dosish
|
22
24
|
str_path.insert(0, "/") if str_path[0] != "/"
|
23
25
|
end
|
24
|
-
str_path =
|
26
|
+
str_path = URIParser.escape(str_path)
|
25
27
|
URI::File.build(path: str_path)
|
26
28
|
end
|
27
29
|
end
|