steep 1.8.3 → 1.9.0.dev.2
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 +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
|