steep 0.44.0 → 0.47.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 +4 -4
- data/.github/dependabot.yml +8 -0
- data/.github/workflows/ruby.yml +3 -2
- data/.gitignore +0 -1
- data/CHANGELOG.md +42 -0
- data/Gemfile +0 -3
- data/Gemfile.lock +75 -0
- data/README.md +2 -1
- data/lib/steep/annotation_parser.rb +1 -1
- data/lib/steep/ast/builtin.rb +7 -1
- data/lib/steep/ast/types/factory.rb +19 -25
- data/lib/steep/cli.rb +7 -1
- data/lib/steep/diagnostic/lsp_formatter.rb +59 -6
- data/lib/steep/diagnostic/ruby.rb +188 -60
- data/lib/steep/diagnostic/signature.rb +38 -15
- data/lib/steep/drivers/check.rb +3 -0
- data/lib/steep/drivers/init.rb +10 -3
- data/lib/steep/drivers/utils/driver_helper.rb +15 -0
- data/lib/steep/drivers/validate.rb +1 -1
- data/lib/steep/drivers/watch.rb +3 -0
- data/lib/steep/equatable.rb +21 -0
- data/lib/steep/interface/function.rb +798 -579
- data/lib/steep/project/dsl.rb +135 -36
- data/lib/steep/project/options.rb +13 -53
- data/lib/steep/project/target.rb +22 -8
- data/lib/steep/server/interaction_worker.rb +245 -26
- data/lib/steep/server/master.rb +2 -2
- data/lib/steep/server/type_check_worker.rb +6 -9
- data/lib/steep/services/file_loader.rb +26 -19
- data/lib/steep/services/goto_service.rb +1 -0
- data/lib/steep/services/hover_content.rb +135 -80
- data/lib/steep/source.rb +12 -11
- data/lib/steep/type_construction.rb +435 -502
- data/lib/steep/type_inference/block_params.rb +3 -6
- data/lib/steep/type_inference/method_params.rb +483 -0
- data/lib/steep/type_inference/send_args.rb +599 -128
- data/lib/steep/typing.rb +46 -21
- data/lib/steep/version.rb +1 -1
- data/lib/steep.rb +4 -2
- data/sample/Steepfile +10 -3
- data/smoke/alias/Steepfile +2 -1
- data/smoke/and/Steepfile +2 -1
- data/smoke/array/Steepfile +2 -1
- data/smoke/array/test_expectations.yml +3 -3
- data/smoke/block/Steepfile +2 -2
- data/smoke/block/c.rb +0 -1
- data/smoke/case/Steepfile +2 -1
- data/smoke/class/Steepfile +2 -1
- data/smoke/class/test_expectations.yml +12 -15
- data/smoke/const/Steepfile +2 -1
- data/smoke/diagnostics/Steepfile +2 -1
- data/smoke/diagnostics/different_method_parameter_kind.rb +9 -0
- data/smoke/diagnostics/method_arity_mismatch.rb +2 -2
- data/smoke/diagnostics/method_parameter_mismatch.rb +10 -0
- data/smoke/diagnostics/test_expectations.yml +108 -31
- data/smoke/diagnostics-rbs/Steepfile +1 -1
- data/smoke/diagnostics-rbs/mixin-class-error.rbs +6 -0
- data/smoke/diagnostics-rbs/test_expectations.yml +12 -0
- data/smoke/diagnostics-rbs-duplicated/Steepfile +2 -1
- data/smoke/diagnostics-ruby-unsat/Steepfile +2 -1
- data/smoke/dstr/Steepfile +2 -1
- data/smoke/ensure/Steepfile +2 -1
- data/smoke/ensure/test_expectations.yml +3 -3
- data/smoke/enumerator/Steepfile +2 -1
- data/smoke/enumerator/test_expectations.yml +1 -1
- data/smoke/extension/Steepfile +2 -1
- data/smoke/extension/e.rbs +1 -1
- data/smoke/hash/Steepfile +2 -1
- data/smoke/hello/Steepfile +2 -1
- data/smoke/if/Steepfile +2 -1
- data/smoke/implements/Steepfile +2 -1
- data/smoke/initialize/Steepfile +2 -1
- data/smoke/integer/Steepfile +2 -1
- data/smoke/interface/Steepfile +2 -1
- data/smoke/kwbegin/Steepfile +2 -1
- data/smoke/lambda/Steepfile +2 -1
- data/smoke/literal/Steepfile +2 -1
- data/smoke/literal/test_expectations.yml +2 -2
- data/smoke/map/Steepfile +2 -1
- data/smoke/method/Steepfile +2 -1
- data/smoke/method/test_expectations.yml +11 -10
- data/smoke/module/Steepfile +2 -1
- data/smoke/regexp/Steepfile +2 -1
- data/smoke/regression/Steepfile +2 -1
- data/smoke/rescue/Steepfile +2 -1
- data/smoke/rescue/test_expectations.yml +3 -3
- data/smoke/self/Steepfile +2 -1
- data/smoke/skip/Steepfile +2 -1
- data/smoke/stdout/Steepfile +2 -1
- data/smoke/super/Steepfile +2 -1
- data/smoke/toplevel/Steepfile +2 -1
- data/smoke/toplevel/test_expectations.yml +3 -3
- data/smoke/tsort/Steepfile +4 -5
- data/smoke/tsort/test_expectations.yml +2 -2
- data/smoke/type_case/Steepfile +2 -1
- data/smoke/unexpected/Steepfile +2 -1
- data/smoke/yield/Steepfile +2 -1
- data/steep.gemspec +2 -2
- metadata +16 -10
- data/sig/project.rbi +0 -109
data/lib/steep/project/dsl.rb
CHANGED
@@ -7,21 +7,34 @@ module Steep
|
|
7
7
|
attr_reader :libraries
|
8
8
|
attr_reader :signatures
|
9
9
|
attr_reader :ignored_sources
|
10
|
-
attr_reader :
|
11
|
-
attr_reader :
|
12
|
-
attr_reader :typing_option_hash
|
10
|
+
attr_reader :stdlib_root
|
11
|
+
attr_reader :core_root
|
13
12
|
attr_reader :repo_paths
|
13
|
+
attr_reader :code_diagnostics_config
|
14
|
+
attr_reader :project
|
15
|
+
attr_reader :collection_config_path
|
14
16
|
|
15
|
-
|
17
|
+
NONE = Object.new.freeze
|
18
|
+
|
19
|
+
def initialize(name, sources: [], libraries: [], signatures: [], ignored_sources: [], repo_paths: [], code_diagnostics_config: {}, project: nil, collection_config_path: NONE)
|
16
20
|
@name = name
|
17
21
|
@sources = sources
|
18
22
|
@libraries = libraries
|
19
23
|
@signatures = signatures
|
20
24
|
@ignored_sources = ignored_sources
|
21
|
-
@
|
22
|
-
@
|
23
|
-
@typing_option_hash = {}
|
25
|
+
@core_root = nil
|
26
|
+
@stdlib_root = nil
|
24
27
|
@repo_paths = []
|
28
|
+
@code_diagnostics_config = code_diagnostics_config
|
29
|
+
@project = project
|
30
|
+
@collection_config_path =
|
31
|
+
case collection_config_path
|
32
|
+
when NONE
|
33
|
+
path = project&.absolute_path(RBS::Collection::Config::PATH)
|
34
|
+
path&.exist? ? path : nil
|
35
|
+
else
|
36
|
+
collection_config_path
|
37
|
+
end
|
25
38
|
end
|
26
39
|
|
27
40
|
def initialize_copy(other)
|
@@ -30,10 +43,12 @@ module Steep
|
|
30
43
|
@libraries = other.libraries.dup
|
31
44
|
@signatures = other.signatures.dup
|
32
45
|
@ignored_sources = other.ignored_sources.dup
|
33
|
-
@vendor_dir = other.vendor_dir
|
34
|
-
@strictness_level = other.strictness_level
|
35
|
-
@typing_option_hash = other.typing_option_hash
|
36
46
|
@repo_paths = other.repo_paths.dup
|
47
|
+
@core_root = other.core_root
|
48
|
+
@stdlib_root = other.stdlib_root
|
49
|
+
@code_diagnostics_config = other.code_diagnostics_config.dup
|
50
|
+
@project = other.project
|
51
|
+
@collection_config_path = other.collection_config_path
|
37
52
|
end
|
38
53
|
|
39
54
|
def check(*args)
|
@@ -48,40 +63,122 @@ module Steep
|
|
48
63
|
libraries.push(*args)
|
49
64
|
end
|
50
65
|
|
51
|
-
def typing_options(level =
|
52
|
-
|
53
|
-
|
66
|
+
def typing_options(level = nil, **hash)
|
67
|
+
Steep.logger.error "#typing_options is deprecated and has no effect as of version 0.46.0. Update your Steepfile as follows for (almost) equivalent setting:"
|
68
|
+
|
69
|
+
messages = []
|
70
|
+
|
71
|
+
messages << "# D = Steep::Diagnostic # Define a constant to shorten namespace"
|
72
|
+
|
73
|
+
case level
|
74
|
+
when :strict
|
75
|
+
messages << "configure_code_diagnostics(D::Ruby.strict) # :strict"
|
76
|
+
when :default
|
77
|
+
messages << "configure_code_diagnostics(D::Ruby.default) # :default"
|
78
|
+
when :lenient
|
79
|
+
messages << "configure_code_diagnostics(D::Ruby.lenient) # :lenient"
|
80
|
+
end
|
81
|
+
|
82
|
+
messages.each do |msg|
|
83
|
+
Steep.logger.error " #{msg}"
|
84
|
+
end
|
85
|
+
|
86
|
+
config = []
|
87
|
+
|
88
|
+
if hash[:allow_missing_definitions]
|
89
|
+
config << "hash[D::Ruby::MethodDefinitionMissing] = nil # allow_missing_definitions"
|
90
|
+
end
|
91
|
+
|
92
|
+
if hash[:allow_fallback_any]
|
93
|
+
config << "hash[D::Ruby::FallbackAny] = nil # allow_fallback_any"
|
94
|
+
end
|
95
|
+
|
96
|
+
if hash[:allow_unknown_constant_assignment]
|
97
|
+
config << "hash[D::Ruby::UnknownConstantAssigned] = nil # allow_unknown_constant_assignment"
|
98
|
+
end
|
99
|
+
|
100
|
+
if hash[:allow_unknown_method_calls]
|
101
|
+
config << "hash[D::Ruby::NoMethod] = nil # allow_unknown_method_calls"
|
102
|
+
end
|
103
|
+
|
104
|
+
unless config.empty?
|
105
|
+
Steep.logger.error " configure_code_diagnostics do |hash|"
|
106
|
+
config.each do |c|
|
107
|
+
Steep.logger.error " #{c}"
|
108
|
+
end
|
109
|
+
Steep.logger.error " end"
|
110
|
+
end
|
111
|
+
|
54
112
|
end
|
55
113
|
|
56
114
|
def signature(*args)
|
57
115
|
signatures.push(*args)
|
58
116
|
end
|
59
117
|
|
60
|
-
def update(name: self.name, sources: self.sources, libraries: self.libraries, ignored_sources: self.ignored_sources, signatures: self.signatures)
|
118
|
+
def update(name: self.name, sources: self.sources, libraries: self.libraries, ignored_sources: self.ignored_sources, signatures: self.signatures, project: self.project)
|
61
119
|
self.class.new(
|
62
120
|
name,
|
63
121
|
sources: sources,
|
64
122
|
libraries: libraries,
|
65
123
|
signatures: signatures,
|
66
|
-
ignored_sources: ignored_sources
|
124
|
+
ignored_sources: ignored_sources,
|
125
|
+
project: project,
|
67
126
|
)
|
68
127
|
end
|
69
128
|
|
70
129
|
def no_builtin!(value = true)
|
71
|
-
Steep.logger.error "
|
130
|
+
Steep.logger.error "`#no_builtin!` in Steepfile is deprecated and ignored. Use `#stdlib_path` instead."
|
72
131
|
end
|
73
132
|
|
74
133
|
def vendor(dir = "vendor/sigs", stdlib: nil, gems: nil)
|
75
|
-
|
76
|
-
|
77
|
-
end
|
134
|
+
Steep.logger.error "`#vendor` in Steepfile is deprecated and ignored. Use `#stdlib_path` instead."
|
135
|
+
end
|
78
136
|
|
79
|
-
|
137
|
+
def stdlib_path(core_root:, stdlib_root:)
|
138
|
+
@core_root = core_root ? Pathname(core_root) : core_root
|
139
|
+
@stdlib_root = stdlib_root ? Pathname(stdlib_root) : stdlib_root
|
80
140
|
end
|
81
141
|
|
82
142
|
def repo_path(*paths)
|
83
143
|
@repo_paths.push(*paths.map {|s| Pathname(s) })
|
84
144
|
end
|
145
|
+
|
146
|
+
# Configure the code diagnostics printing setup.
|
147
|
+
#
|
148
|
+
# Yields a hash, and the update the hash in the block.
|
149
|
+
#
|
150
|
+
# ```rb
|
151
|
+
# D = Steep::Diagnostic
|
152
|
+
#
|
153
|
+
# configure_code_diagnostics do |hash|
|
154
|
+
# # Assign one of :error, :warning, :information, :hint or :nil to error classes.
|
155
|
+
# hash[D::Ruby::UnexpectedPositionalArgument] = :error
|
156
|
+
# end
|
157
|
+
# ```
|
158
|
+
#
|
159
|
+
# Passing a hash is also allowed.
|
160
|
+
#
|
161
|
+
# ```rb
|
162
|
+
# D = Steep::Diagnostic
|
163
|
+
#
|
164
|
+
# configure_code_diagnostics(D::Ruby.lenient)
|
165
|
+
# ```
|
166
|
+
#
|
167
|
+
def configure_code_diagnostics(hash = nil)
|
168
|
+
if hash
|
169
|
+
code_diagnostics_config.merge!(hash)
|
170
|
+
end
|
171
|
+
|
172
|
+
yield code_diagnostics_config if block_given?
|
173
|
+
end
|
174
|
+
|
175
|
+
def collection_config(path)
|
176
|
+
@collection_config_path = project.absolute_path(path)
|
177
|
+
end
|
178
|
+
|
179
|
+
def disable_collection
|
180
|
+
@collection_config_path = nil
|
181
|
+
end
|
85
182
|
end
|
86
183
|
|
87
184
|
attr_reader :project
|
@@ -107,40 +204,42 @@ module Steep
|
|
107
204
|
end
|
108
205
|
|
109
206
|
def self.parse(project, code, filename: "Steepfile")
|
110
|
-
|
207
|
+
Steep.logger.tagged filename do
|
208
|
+
self.new(project: project).instance_eval(code, filename)
|
209
|
+
end
|
111
210
|
end
|
112
211
|
|
113
212
|
def target(name, template: nil, &block)
|
114
213
|
target = if template
|
115
|
-
self.class.templates[template]&.dup&.update(name: name) or
|
214
|
+
self.class.templates[template]&.dup&.update(name: name, project: project) or
|
116
215
|
raise "Unknown template: #{template}, available templates: #{@@templates.keys.join(", ")}"
|
117
216
|
else
|
118
|
-
TargetDSL.new(name)
|
217
|
+
TargetDSL.new(name, code_diagnostics_config: Diagnostic::Ruby.default.dup, project: project)
|
119
218
|
end
|
120
219
|
|
121
|
-
|
220
|
+
Steep.logger.tagged "target=#{name}" do
|
221
|
+
target.instance_eval(&block) if block_given?
|
222
|
+
end
|
122
223
|
|
123
224
|
source_pattern = Pattern.new(patterns: target.sources, ignores: target.ignored_sources, ext: ".rb")
|
124
225
|
signature_pattern = Pattern.new(patterns: target.signatures, ext: ".rbs")
|
125
226
|
|
227
|
+
collection_lock = target.collection_config_path&.then { |p| RBS::Collection::Config.lockfile_of(p) }
|
228
|
+
|
126
229
|
Project::Target.new(
|
127
230
|
name: target.name,
|
128
231
|
source_pattern: source_pattern,
|
129
232
|
signature_pattern: signature_pattern,
|
130
233
|
options: Options.new.tap do |options|
|
131
234
|
options.libraries.push(*target.libraries)
|
132
|
-
options.
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
end
|
141
|
-
|
142
|
-
options.merge!(target.typing_option_hash)
|
143
|
-
end
|
235
|
+
options.paths = Options::PathOptions.new(
|
236
|
+
core_root: target.core_root,
|
237
|
+
stdlib_root: target.stdlib_root,
|
238
|
+
repo_paths: target.repo_paths
|
239
|
+
)
|
240
|
+
options.collection_lock = collection_lock
|
241
|
+
end,
|
242
|
+
code_diagnostics_config: target.code_diagnostics_config
|
144
243
|
).tap do |target|
|
145
244
|
project.targets << target
|
146
245
|
end
|
@@ -1,63 +1,23 @@
|
|
1
1
|
module Steep
|
2
2
|
class Project
|
3
3
|
class Options
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
attr_accessor :vendor_path
|
9
|
-
attr_reader :libraries
|
10
|
-
attr_reader :repository_paths
|
11
|
-
|
12
|
-
def initialize
|
13
|
-
apply_default_typing_options!
|
14
|
-
self.vendor_path = nil
|
15
|
-
|
16
|
-
@libraries = []
|
17
|
-
@repository_paths = []
|
18
|
-
end
|
19
|
-
|
20
|
-
def apply_default_typing_options!
|
21
|
-
self.allow_fallback_any = true
|
22
|
-
self.allow_missing_definitions = true
|
23
|
-
self.allow_unknown_constant_assignment = false
|
24
|
-
self.allow_unknown_method_calls = false
|
25
|
-
end
|
26
|
-
|
27
|
-
def apply_strict_typing_options!
|
28
|
-
self.allow_fallback_any = false
|
29
|
-
self.allow_missing_definitions = false
|
30
|
-
self.allow_unknown_constant_assignment = false
|
31
|
-
self.allow_unknown_method_calls = false
|
32
|
-
end
|
33
|
-
|
34
|
-
def apply_lenient_typing_options!
|
35
|
-
self.allow_fallback_any = true
|
36
|
-
self.allow_missing_definitions = true
|
37
|
-
self.allow_unknown_constant_assignment = true
|
38
|
-
self.allow_unknown_method_calls = true
|
39
|
-
end
|
4
|
+
PathOptions = Struct.new(:core_root, :stdlib_root, :repo_paths, keyword_init: true) do
|
5
|
+
def customized_stdlib?
|
6
|
+
stdlib_root != nil
|
7
|
+
end
|
40
8
|
|
41
|
-
|
42
|
-
|
43
|
-
when error.is_a?(Diagnostic::Ruby::FallbackAny)
|
44
|
-
!allow_fallback_any
|
45
|
-
when error.is_a?(Diagnostic::Ruby::MethodDefinitionMissing)
|
46
|
-
!allow_missing_definitions
|
47
|
-
when error.is_a?(Diagnostic::Ruby::NoMethod)
|
48
|
-
!allow_unknown_method_calls
|
49
|
-
when error.is_a?(Diagnostic::Ruby::UnknownConstantAssigned)
|
50
|
-
!allow_unknown_constant_assignment
|
51
|
-
else
|
52
|
-
true
|
9
|
+
def customized_core?
|
10
|
+
core_root != nil
|
53
11
|
end
|
54
12
|
end
|
55
13
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
14
|
+
attr_reader :libraries
|
15
|
+
attr_accessor :paths
|
16
|
+
attr_accessor :collection_lock
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@paths = PathOptions.new(repo_paths: [])
|
20
|
+
@libraries = []
|
61
21
|
end
|
62
22
|
end
|
63
23
|
end
|
data/lib/steep/project/target.rb
CHANGED
@@ -6,12 +6,14 @@ module Steep
|
|
6
6
|
|
7
7
|
attr_reader :source_pattern
|
8
8
|
attr_reader :signature_pattern
|
9
|
+
attr_reader :code_diagnostics_config
|
9
10
|
|
10
|
-
def initialize(name:, options:, source_pattern:, signature_pattern:)
|
11
|
+
def initialize(name:, options:, source_pattern:, signature_pattern:, code_diagnostics_config:)
|
11
12
|
@name = name
|
12
13
|
@options = options
|
13
14
|
@source_pattern = source_pattern
|
14
15
|
@signature_pattern = signature_pattern
|
16
|
+
@code_diagnostics_config = code_diagnostics_config
|
15
17
|
|
16
18
|
@source_files = {}
|
17
19
|
@signature_files = {}
|
@@ -30,20 +32,32 @@ module Steep
|
|
30
32
|
end
|
31
33
|
|
32
34
|
def self.construct_env_loader(options:, project:)
|
33
|
-
repo = RBS::Repository.new(no_stdlib: options.
|
34
|
-
|
35
|
+
repo = RBS::Repository.new(no_stdlib: options.paths.customized_stdlib?)
|
36
|
+
|
37
|
+
if options.paths.stdlib_root
|
38
|
+
repo.add(project.absolute_path(options.paths.stdlib_root))
|
39
|
+
end
|
40
|
+
|
41
|
+
options.paths.repo_paths.each do |path|
|
35
42
|
repo.add(project.absolute_path(path))
|
36
43
|
end
|
37
44
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
45
|
+
core_root_path =
|
46
|
+
if options.paths.customized_core?
|
47
|
+
if options.paths.core_root
|
48
|
+
project.absolute_path(options.paths.core_root)
|
49
|
+
end
|
50
|
+
else
|
51
|
+
RBS::EnvironmentLoader::DEFAULT_CORE_ROOT
|
52
|
+
end
|
53
|
+
|
54
|
+
loader = RBS::EnvironmentLoader.new(core_root: core_root_path, repository: repo)
|
55
|
+
|
43
56
|
options.libraries.each do |lib|
|
44
57
|
name, version = lib.split(/:/, 2)
|
45
58
|
loader.add(library: name, version: version)
|
46
59
|
end
|
60
|
+
loader.add_collection(options.collection_lock) if options.collection_lock
|
47
61
|
|
48
62
|
loader
|
49
63
|
end
|