steep 0.39.0 → 0.40.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/workflows/ruby.yml +1 -1
- data/CHANGELOG.md +6 -0
- data/Rakefile +5 -2
- data/bin/output_rebaseline.rb +49 -0
- data/bin/output_test.rb +93 -0
- data/lib/steep.rb +8 -3
- data/lib/steep/cli.rb +1 -1
- data/lib/steep/diagnostic/helper.rb +17 -0
- data/lib/steep/diagnostic/lsp_formatter.rb +16 -0
- data/lib/steep/diagnostic/ruby.rb +623 -0
- data/lib/steep/diagnostic/signature.rb +224 -0
- data/lib/steep/drivers/annotations.rb +13 -6
- data/lib/steep/drivers/check.rb +83 -60
- data/lib/steep/drivers/diagnostic_printer.rb +94 -0
- data/lib/steep/drivers/stats.rb +125 -29
- data/lib/steep/drivers/trace_printer.rb +5 -1
- data/lib/steep/drivers/validate.rb +13 -6
- data/lib/steep/drivers/watch.rb +26 -9
- data/lib/steep/drivers/worker.rb +5 -0
- data/lib/steep/project/options.rb +4 -4
- data/lib/steep/project/signature_file.rb +8 -2
- data/lib/steep/project/stats_calculator.rb +80 -0
- data/lib/steep/project/target.rb +64 -53
- data/lib/steep/range_extension.rb +29 -0
- data/lib/steep/server/base_worker.rb +42 -4
- data/lib/steep/server/code_worker.rb +37 -24
- data/lib/steep/server/interaction_worker.rb +1 -0
- data/lib/steep/server/master.rb +268 -82
- data/lib/steep/server/signature_worker.rb +7 -59
- data/lib/steep/server/worker_process.rb +9 -9
- data/lib/steep/signature/validator.rb +33 -9
- data/lib/steep/type_construction.rb +276 -194
- data/lib/steep/version.rb +1 -1
- data/smoke/alias/a.rb +0 -3
- data/smoke/alias/b.rb +0 -1
- data/smoke/alias/c.rb +0 -2
- data/smoke/alias/test.yaml +73 -0
- data/smoke/and/a.rb +0 -3
- data/smoke/and/test.yaml +24 -0
- data/smoke/array/a.rb +0 -3
- data/smoke/array/b.rb +0 -2
- data/smoke/array/c.rb +0 -1
- data/smoke/array/test.yaml +80 -0
- data/smoke/block/a.rb +0 -2
- data/smoke/block/b.rb +0 -2
- data/smoke/block/d.rb +0 -4
- data/smoke/block/test.yaml +96 -0
- data/smoke/broken/Steepfile +5 -0
- data/smoke/broken/broken.rb +0 -0
- data/smoke/broken/broken.rbs +0 -0
- data/smoke/broken/test.yaml +6 -0
- data/smoke/case/a.rb +0 -3
- data/smoke/case/test.yaml +36 -0
- data/smoke/class/a.rb +0 -3
- data/smoke/class/c.rb +0 -1
- data/smoke/class/f.rb +0 -1
- data/smoke/class/g.rb +0 -2
- data/smoke/class/i.rb +0 -2
- data/smoke/class/test.yaml +89 -0
- data/smoke/const/a.rb +0 -3
- data/smoke/const/b.rb +7 -0
- data/smoke/const/b.rbs +5 -0
- data/smoke/const/test.yaml +96 -0
- data/smoke/diagnostics-rbs-duplicated/Steepfile +5 -0
- data/smoke/diagnostics-rbs-duplicated/a.rbs +5 -0
- data/smoke/diagnostics-rbs-duplicated/test.yaml +10 -0
- data/smoke/diagnostics-rbs/Steepfile +5 -0
- data/smoke/diagnostics-rbs/duplicated-method-definition.rbs +20 -0
- data/smoke/diagnostics-rbs/generic-parameter-mismatch.rbs +7 -0
- data/smoke/diagnostics-rbs/invalid-method-overload.rbs +3 -0
- data/smoke/diagnostics-rbs/invalid-type-application.rbs +7 -0
- data/smoke/diagnostics-rbs/invalid_variance_annotation.rbs +3 -0
- data/smoke/diagnostics-rbs/recursive-alias.rbs +5 -0
- data/smoke/diagnostics-rbs/recursive-class.rbs +8 -0
- data/smoke/diagnostics-rbs/superclass-mismatch.rbs +7 -0
- data/smoke/diagnostics-rbs/test.yaml +142 -0
- data/smoke/diagnostics-rbs/unknown-method-alias.rbs +3 -0
- data/smoke/diagnostics-rbs/unknown-type-name.rbs +13 -0
- data/smoke/diagnostics/Steepfile +5 -0
- data/smoke/diagnostics/a.rbs +26 -0
- data/smoke/diagnostics/argument_type_mismatch.rb +1 -0
- data/smoke/diagnostics/block_body_type_mismatch.rb +1 -0
- data/smoke/diagnostics/block_type_mismatch.rb +3 -0
- data/smoke/diagnostics/break_type_mismatch.rb +1 -0
- data/smoke/diagnostics/else_on_exhaustive_case.rb +12 -0
- data/smoke/diagnostics/incompatible_annotation.rb +6 -0
- data/smoke/diagnostics/incompatible_argument.rb +1 -0
- data/smoke/diagnostics/incompatible_assignment.rb +8 -0
- data/smoke/diagnostics/method_arity_mismatch.rb +11 -0
- data/smoke/diagnostics/method_body_type_mismatch.rb +6 -0
- data/smoke/diagnostics/method_definition_missing.rb +2 -0
- data/smoke/diagnostics/method_return_type_annotation_mismatch.rb +7 -0
- data/smoke/diagnostics/missing_keyword.rb +1 -0
- data/smoke/diagnostics/no_method.rb +1 -0
- data/smoke/diagnostics/required_block_missing.rb +1 -0
- data/smoke/diagnostics/return_type_mismatch.rb +6 -0
- data/smoke/diagnostics/test.yaml +333 -0
- data/smoke/diagnostics/unexpected_block_given.rb +1 -0
- data/smoke/diagnostics/unexpected_dynamic_method.rb +3 -0
- data/smoke/diagnostics/unexpected_jump.rb +4 -0
- data/smoke/diagnostics/unexpected_jump_value.rb +3 -0
- data/smoke/diagnostics/unexpected_keyword.rb +1 -0
- data/smoke/diagnostics/unexpected_splat.rb +1 -0
- data/smoke/diagnostics/unexpected_yield.rb +6 -0
- data/smoke/diagnostics/unknown_constant_assigned.rb +7 -0
- data/smoke/diagnostics/unresolved_overloading.rb +1 -0
- data/smoke/diagnostics/unsatisfiable_constraint.rb +7 -0
- data/smoke/diagnostics/unsupported_syntax.rb +2 -0
- data/smoke/dstr/a.rb +0 -1
- data/smoke/dstr/test.yaml +10 -0
- data/smoke/ensure/a.rb +0 -4
- data/smoke/ensure/test.yaml +47 -0
- data/smoke/enumerator/a.rb +0 -6
- data/smoke/enumerator/b.rb +0 -3
- data/smoke/enumerator/test.yaml +100 -0
- data/smoke/extension/a.rb +0 -1
- data/smoke/extension/b.rb +0 -2
- data/smoke/extension/c.rb +0 -1
- data/smoke/extension/test.yaml +50 -0
- data/smoke/hash/b.rb +0 -1
- data/smoke/hash/c.rb +0 -3
- data/smoke/hash/d.rb +0 -1
- data/smoke/hash/e.rb +0 -1
- data/smoke/hash/test.yaml +62 -0
- data/smoke/hello/hello.rb +0 -2
- data/smoke/hello/test.yaml +18 -0
- data/smoke/if/a.rb +0 -2
- data/smoke/if/test.yaml +27 -0
- data/smoke/implements/a.rb +0 -2
- data/smoke/implements/test.yaml +16 -0
- data/smoke/initialize/test.yaml +4 -0
- data/smoke/integer/a.rb +0 -7
- data/smoke/integer/test.yaml +66 -0
- data/smoke/interface/a.rb +0 -2
- data/smoke/interface/test.yaml +16 -0
- data/smoke/kwbegin/a.rb +0 -1
- data/smoke/kwbegin/test.yaml +14 -0
- data/smoke/lambda/a.rb +1 -4
- data/smoke/lambda/test.yaml +28 -0
- data/smoke/literal/a.rb +0 -5
- data/smoke/literal/b.rb +0 -2
- data/smoke/literal/test.yaml +79 -0
- data/smoke/map/test.yaml +4 -0
- data/smoke/method/a.rb +0 -5
- data/smoke/method/b.rb +0 -1
- data/smoke/method/test.yaml +71 -0
- data/smoke/module/a.rb +0 -2
- data/smoke/module/b.rb +0 -2
- data/smoke/module/c.rb +0 -1
- data/smoke/module/d.rb +0 -1
- data/smoke/module/f.rb +0 -2
- data/smoke/module/test.yaml +51 -0
- data/smoke/regexp/a.rb +0 -38
- data/smoke/regexp/b.rb +0 -26
- data/smoke/regexp/test.yaml +372 -0
- data/smoke/regression/set_divide.rb +0 -4
- data/smoke/regression/test.yaml +38 -0
- data/smoke/rescue/a.rb +0 -5
- data/smoke/rescue/test.yaml +60 -0
- data/smoke/self/a.rb +0 -2
- data/smoke/self/test.yaml +16 -0
- data/smoke/skip/skip.rb +0 -2
- data/smoke/skip/test.yaml +16 -0
- data/smoke/stdout/test.yaml +4 -0
- data/smoke/super/a.rb +0 -4
- data/smoke/super/test.yaml +52 -0
- data/smoke/toplevel/a.rb +0 -1
- data/smoke/toplevel/test.yaml +12 -0
- data/smoke/tsort/a.rb +0 -3
- data/smoke/tsort/test.yaml +32 -0
- data/smoke/type_case/a.rb +0 -4
- data/smoke/type_case/test.yaml +33 -0
- data/smoke/yield/a.rb +0 -3
- data/smoke/yield/b.rb +6 -0
- data/smoke/yield/test.yaml +49 -0
- data/steep.gemspec +3 -3
- metadata +108 -17
- data/bin/smoke_runner.rb +0 -139
- data/lib/steep/drivers/signature_error_printer.rb +0 -25
- data/lib/steep/errors.rb +0 -594
- data/lib/steep/signature/errors.rb +0 -128
- data/lib/steep/type_assignability.rb +0 -367
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 725a6432404ca94fa643ba89ed2028d7b3923ece4031512ebea88ebbdc4fd01e
|
4
|
+
data.tar.gz: 957a95acbda38474f1a796f829f8cf098b69484a8ddf42bd39e2c2be935a961e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 363a04610a81d4652541c9e5856705f7317a8b4e92a2074dc460b5f34e19f8eb5bfd080826c3a61a41414668d22d0352e4b300d1ce09c659243ef8c06492fc22
|
7
|
+
data.tar.gz: 33b8d47d5290acea68f1f07ee74a6129ad0a2568fff2ddc783b7910f2ed55ae2d6d6b6f0b1b90b8dea773bfab0c9cc7ccc70723ab5aee75db3e3a0f997b93fe9
|
data/.github/workflows/ruby.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 0.40.0 (2021-01-31)
|
6
|
+
|
7
|
+
* Report progress with dots ([#287](https://github.com/soutaro/steep/pull/287))
|
8
|
+
* Diagnostics message improvements ([#297](https://github.com/soutaro/steep/pull/297), [#301](https://github.com/soutaro/steep/pull/301))
|
9
|
+
* Fix error on implicit `to_proc` syntax when `untyped` is yielded ([#291](https://github.com/soutaro/steep/pull/291))
|
10
|
+
|
5
11
|
## 0.39.0 (2020-12-25)
|
6
12
|
|
7
13
|
* Update RBS to 1.0.0 ([#282](https://github.com/soutaro/steep/pull/282))
|
data/Rakefile
CHANGED
@@ -0,0 +1,49 @@
|
|
1
|
+
require "pathname"
|
2
|
+
require "yaml"
|
3
|
+
require "open3"
|
4
|
+
require "tempfile"
|
5
|
+
|
6
|
+
if ARGV.empty?
|
7
|
+
test_dirs = (Pathname(__dir__) + "../smoke").children
|
8
|
+
else
|
9
|
+
test_dirs = ARGV.map {|p| Pathname.pwd + p }
|
10
|
+
end
|
11
|
+
|
12
|
+
test_dirs.each do |dir|
|
13
|
+
test = dir + "test.yaml"
|
14
|
+
|
15
|
+
if test.file?
|
16
|
+
content = YAML.load_file(test)
|
17
|
+
else
|
18
|
+
content = { "test" => {} }
|
19
|
+
end
|
20
|
+
|
21
|
+
puts "Rebaselining #{dir}..."
|
22
|
+
|
23
|
+
command = content["command"] || "steep check"
|
24
|
+
puts " command: #{command}"
|
25
|
+
|
26
|
+
output, _ = Open3.capture2(command, chdir: dir.to_s)
|
27
|
+
|
28
|
+
diagnostics = output.split(/\n\n/).each.with_object({}) do |message, hash|
|
29
|
+
if message =~ /\A([^:]+):\d+:\d+:/
|
30
|
+
path = $1
|
31
|
+
hash[path] ||= { "diagnostics" => [] }
|
32
|
+
hash[path]["diagnostics"] << message.chomp + "\n"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
content["test"].each_key do |path|
|
37
|
+
unless diagnostics.key?(path)
|
38
|
+
diagnostics[path] = { "diagnostics" => [] }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
content["test"] = diagnostics.keys.sort.each.with_object({}) do |key, hash|
|
43
|
+
hash[key] = diagnostics[key]
|
44
|
+
end
|
45
|
+
|
46
|
+
test.open("w") do |io|
|
47
|
+
YAML.dump(content, io, header: false)
|
48
|
+
end
|
49
|
+
end
|
data/bin/output_test.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
require "pathname"
|
2
|
+
require "yaml"
|
3
|
+
require "open3"
|
4
|
+
require "tempfile"
|
5
|
+
require "optparse"
|
6
|
+
|
7
|
+
OptionParser.new do |opts|
|
8
|
+
opts.on("--verbose", "-v") { @verbose = true }
|
9
|
+
end.parse!(ARGV)
|
10
|
+
|
11
|
+
if ARGV.empty?
|
12
|
+
test_dirs = (Pathname(__dir__) + "../smoke").children
|
13
|
+
else
|
14
|
+
test_dirs = ARGV.map {|p| Pathname.pwd + p }
|
15
|
+
end
|
16
|
+
|
17
|
+
success = true
|
18
|
+
|
19
|
+
test_dirs.each do |dir|
|
20
|
+
test = dir + "test.yaml"
|
21
|
+
|
22
|
+
next unless test.file?
|
23
|
+
|
24
|
+
puts "Running test #{dir}..."
|
25
|
+
|
26
|
+
content = YAML.load_file(test)
|
27
|
+
|
28
|
+
command = content["command"] || "steep check"
|
29
|
+
puts " command: #{command}"
|
30
|
+
|
31
|
+
output, _ = Open3.capture2(command, chdir: dir.to_s)
|
32
|
+
|
33
|
+
if @verbose
|
34
|
+
puts " Raw output:"
|
35
|
+
output.split(/\n/).each do |line|
|
36
|
+
puts " > #{line.chomp}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
diagnostics = output.split(/\n\n/).each.with_object({}) do |d, hash|
|
41
|
+
if d =~ /\A([^:]+):\d+:\d+:/
|
42
|
+
path = $1
|
43
|
+
hash[path] ||= []
|
44
|
+
hash[path] << (d.chomp + "\n")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
content["test"].each do |path, test|
|
49
|
+
puts " Checking: #{path}..."
|
50
|
+
|
51
|
+
fail_expected = test["fail"] || false
|
52
|
+
|
53
|
+
expected_diagnostics = test["diagnostics"]
|
54
|
+
reported_diagnostics = (diagnostics[path] || [])
|
55
|
+
|
56
|
+
puts " # of expected: #{expected_diagnostics.size}, # of reported: #{reported_diagnostics.size}"
|
57
|
+
|
58
|
+
unexpected_diagnostics = reported_diagnostics.reject {|d| expected_diagnostics.include?(d) }
|
59
|
+
missing_diagnostics = expected_diagnostics.reject {|d| reported_diagnostics.include?(d) }
|
60
|
+
|
61
|
+
unexpected_diagnostics.each do |d|
|
62
|
+
puts " Unexpected diagnostics:"
|
63
|
+
d.split(/\n/).each do |line|
|
64
|
+
puts " + #{line.chomp}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
missing_diagnostics.each do |d|
|
69
|
+
puts " Missing diagnostics:"
|
70
|
+
d.split(/\n/).each do |line|
|
71
|
+
puts " - #{line.chomp}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
if unexpected_diagnostics.empty? && missing_diagnostics.empty?
|
76
|
+
puts " 👍"
|
77
|
+
else
|
78
|
+
if fail_expected
|
79
|
+
puts " 🚨 (expected failure)"
|
80
|
+
else
|
81
|
+
puts " 🚨"
|
82
|
+
success = false
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
if success
|
89
|
+
puts "All tests ok! 👏"
|
90
|
+
else
|
91
|
+
puts "Errors detected! 🤮"
|
92
|
+
exit 1
|
93
|
+
end
|
data/lib/steep.rb
CHANGED
@@ -42,6 +42,8 @@ require "steep/ast/annotation/collection"
|
|
42
42
|
require "steep/ast/builtin"
|
43
43
|
require "steep/ast/types/factory"
|
44
44
|
|
45
|
+
require "steep/range_extension"
|
46
|
+
|
45
47
|
require "steep/interface/function"
|
46
48
|
require "steep/interface/block"
|
47
49
|
require "steep/interface/method_type"
|
@@ -56,12 +58,14 @@ require "steep/subtyping/constraints"
|
|
56
58
|
require "steep/subtyping/variable_variance"
|
57
59
|
require "steep/subtyping/variable_occurrence"
|
58
60
|
|
59
|
-
require "steep/
|
61
|
+
require "steep/diagnostic/helper"
|
62
|
+
require "steep/diagnostic/ruby"
|
63
|
+
require "steep/diagnostic/signature"
|
64
|
+
require "steep/diagnostic/lsp_formatter"
|
60
65
|
require "steep/signature/validator"
|
61
66
|
require "steep/source"
|
62
67
|
require "steep/annotation_parser"
|
63
68
|
require "steep/typing"
|
64
|
-
require "steep/errors"
|
65
69
|
require "steep/module_helper"
|
66
70
|
require "steep/type_construction"
|
67
71
|
require "steep/type_inference/context"
|
@@ -97,6 +101,7 @@ require "steep/project/dsl"
|
|
97
101
|
require "steep/project/file_loader"
|
98
102
|
require "steep/project/hover_content"
|
99
103
|
require "steep/project/completion_provider"
|
104
|
+
require "steep/project/stats_calculator"
|
100
105
|
require "steep/drivers/utils/driver_helper"
|
101
106
|
require "steep/drivers/check"
|
102
107
|
require "steep/drivers/stats"
|
@@ -104,12 +109,12 @@ require "steep/drivers/validate"
|
|
104
109
|
require "steep/drivers/annotations"
|
105
110
|
require "steep/drivers/watch"
|
106
111
|
require "steep/drivers/langserver"
|
107
|
-
require "steep/drivers/signature_error_printer"
|
108
112
|
require "steep/drivers/trace_printer"
|
109
113
|
require "steep/drivers/print_project"
|
110
114
|
require "steep/drivers/init"
|
111
115
|
require "steep/drivers/vendor"
|
112
116
|
require "steep/drivers/worker"
|
117
|
+
require "steep/drivers/diagnostic_printer"
|
113
118
|
|
114
119
|
if ENV["NO_COLOR"]
|
115
120
|
Rainbow.enabled = false
|
data/lib/steep/cli.rb
CHANGED
@@ -83,7 +83,6 @@ module Steep
|
|
83
83
|
opts.banner = "Usage: steep check [options] [sources]"
|
84
84
|
|
85
85
|
opts.on("--steepfile=PATH") {|path| check.steepfile = Pathname(path) }
|
86
|
-
opts.on("--dump-all-types") { check.dump_all_types = true }
|
87
86
|
handle_logging_options opts
|
88
87
|
end.parse!(argv)
|
89
88
|
|
@@ -184,6 +183,7 @@ module Steep
|
|
184
183
|
opts.on("--signature") { command.worker_type = :signature }
|
185
184
|
opts.on("--steepfile=PATH") {|path| command.steepfile = Pathname(path) }
|
186
185
|
opts.on("--name=NAME") {|name| command.worker_name = name }
|
186
|
+
opts.on("--delay-shutdown") { command.delay_shutdown = true }
|
187
187
|
end.parse!(argv)
|
188
188
|
end.run
|
189
189
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Steep
|
2
|
+
module Diagnostic
|
3
|
+
module Helper
|
4
|
+
def error_name
|
5
|
+
self.class.name.split(/::/).last
|
6
|
+
end
|
7
|
+
|
8
|
+
def full_message
|
9
|
+
if detail = detail_lines
|
10
|
+
"#{header_line}\n#{detail}"
|
11
|
+
else
|
12
|
+
"#{header_line}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Steep
|
2
|
+
module Diagnostic
|
3
|
+
class LSPFormatter
|
4
|
+
LSP = LanguageServer::Protocol
|
5
|
+
|
6
|
+
def format(diagnostic)
|
7
|
+
LSP::Interface::Diagnostic.new(
|
8
|
+
message: diagnostic.full_message,
|
9
|
+
code: diagnostic.diagnostic_code,
|
10
|
+
severity: LSP::Constant::DiagnosticSeverity::ERROR,
|
11
|
+
range: diagnostic.location.as_lsp_range
|
12
|
+
).to_hash
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,623 @@
|
|
1
|
+
module Steep
|
2
|
+
module Diagnostic
|
3
|
+
module Ruby
|
4
|
+
class Base
|
5
|
+
include Helper
|
6
|
+
|
7
|
+
attr_reader :node
|
8
|
+
attr_reader :location
|
9
|
+
|
10
|
+
def initialize(node:, location: node.location.expression)
|
11
|
+
@node = node
|
12
|
+
@location = location
|
13
|
+
end
|
14
|
+
|
15
|
+
def header_line
|
16
|
+
error_name
|
17
|
+
end
|
18
|
+
|
19
|
+
def detail_lines
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def diagnostic_code
|
24
|
+
"Ruby::#{error_name}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module ResultPrinter
|
29
|
+
def print_result_to(io, level: 1)
|
30
|
+
printer = Drivers::TracePrinter.new(io)
|
31
|
+
printer.print result.trace, level: level
|
32
|
+
end
|
33
|
+
|
34
|
+
def trace_lines
|
35
|
+
StringIO.new.tap do |io|
|
36
|
+
print_result_to(io)
|
37
|
+
end.string.chomp
|
38
|
+
end
|
39
|
+
|
40
|
+
def detail_lines
|
41
|
+
StringIO.new.tap do |io|
|
42
|
+
print_result_to(io)
|
43
|
+
end.string.chomp
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class IncompatibleAssignment < Base
|
48
|
+
attr_reader :lhs_type
|
49
|
+
attr_reader :rhs_type
|
50
|
+
attr_reader :result
|
51
|
+
|
52
|
+
include ResultPrinter
|
53
|
+
|
54
|
+
def initialize(node:, lhs_type:, rhs_type:, result:)
|
55
|
+
super(node: node)
|
56
|
+
@lhs_type = lhs_type
|
57
|
+
@rhs_type = rhs_type
|
58
|
+
@result = result
|
59
|
+
end
|
60
|
+
|
61
|
+
def header_line
|
62
|
+
element = case node.type
|
63
|
+
when :ivasgn, :lvasgn, :gvasgn, :cvasgn
|
64
|
+
"a variable"
|
65
|
+
when :casgn
|
66
|
+
"a constant"
|
67
|
+
else
|
68
|
+
"an expression"
|
69
|
+
end
|
70
|
+
"Cannot assign a value of type `#{rhs_type}` to #{element} of type `#{lhs_type}`"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class IncompatibleArguments < Base
|
75
|
+
attr_reader :node
|
76
|
+
attr_reader :method_name
|
77
|
+
attr_reader :receiver_type
|
78
|
+
attr_reader :method_types
|
79
|
+
|
80
|
+
def initialize(node:, method_name:, receiver_type:, method_types:)
|
81
|
+
location = case node.type
|
82
|
+
when :send
|
83
|
+
node.loc.selector
|
84
|
+
when :block
|
85
|
+
node.children[0].yield_self do |node|
|
86
|
+
node.loc.selector
|
87
|
+
end
|
88
|
+
when :super
|
89
|
+
node.loc.expression
|
90
|
+
else
|
91
|
+
Steep.logger.error { "Unexpected node given: #{node.type} (IncompatibleArguments#initialize)"}
|
92
|
+
node.loc.expression
|
93
|
+
end
|
94
|
+
super(node: node, location: location)
|
95
|
+
@receiver_type = receiver_type
|
96
|
+
@method_types = method_types
|
97
|
+
@method_name = method_name
|
98
|
+
end
|
99
|
+
|
100
|
+
def header_line
|
101
|
+
"Cannot find method `#{method_name}` of type `#{receiver_type}` with compatible arity"
|
102
|
+
end
|
103
|
+
|
104
|
+
def detail_lines
|
105
|
+
StringIO.new.tap do |io|
|
106
|
+
io.puts "Method types:"
|
107
|
+
first_type, *rest_types = method_types
|
108
|
+
defn = " def #{method_name}"
|
109
|
+
io.puts "#{defn}: #{first_type}"
|
110
|
+
rest_types.each do |method_type|
|
111
|
+
io.puts "#{" " * defn.size}| #{method_type}"
|
112
|
+
end
|
113
|
+
end.string.chomp
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
class UnresolvedOverloading < Base
|
118
|
+
attr_reader :node
|
119
|
+
attr_reader :receiver_type
|
120
|
+
attr_reader :method_name
|
121
|
+
attr_reader :method_types
|
122
|
+
|
123
|
+
def initialize(node:, receiver_type:, method_name:, method_types:)
|
124
|
+
super node: node
|
125
|
+
@receiver_type = receiver_type
|
126
|
+
@method_name = method_name
|
127
|
+
@method_types = method_types
|
128
|
+
end
|
129
|
+
|
130
|
+
def header_line
|
131
|
+
"Cannot find compatible overloading of method `#{method_name}` of type `#{receiver_type}`"
|
132
|
+
end
|
133
|
+
|
134
|
+
def detail_lines
|
135
|
+
StringIO.new.tap do |io|
|
136
|
+
io.puts "Method types:"
|
137
|
+
first_type, *rest_types = method_types
|
138
|
+
defn = " def #{method_name}"
|
139
|
+
io.puts "#{defn}: #{first_type}"
|
140
|
+
rest_types.each do |method_type|
|
141
|
+
io.puts "#{" " * defn.size}| #{method_type}"
|
142
|
+
end
|
143
|
+
end.string.chomp
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
class ArgumentTypeMismatch < Base
|
148
|
+
attr_reader :node
|
149
|
+
attr_reader :expected
|
150
|
+
attr_reader :actual
|
151
|
+
attr_reader :receiver_type
|
152
|
+
attr_reader :result
|
153
|
+
|
154
|
+
include ResultPrinter
|
155
|
+
|
156
|
+
def initialize(node:, receiver_type:, expected:, actual:, result:)
|
157
|
+
super(node: node)
|
158
|
+
@receiver_type = receiver_type
|
159
|
+
@expected = expected
|
160
|
+
@actual = actual
|
161
|
+
@result = result
|
162
|
+
end
|
163
|
+
|
164
|
+
def header_line
|
165
|
+
"Cannot pass a value of type `#{actual}` as an argument of type `#{expected}`"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
class NoMethod < Base
|
170
|
+
attr_reader :type
|
171
|
+
attr_reader :method
|
172
|
+
|
173
|
+
def initialize(node:, type:, method:)
|
174
|
+
loc = case node.type
|
175
|
+
when :send
|
176
|
+
node.loc.selector
|
177
|
+
when :block
|
178
|
+
node.children[0].loc.selector
|
179
|
+
else
|
180
|
+
node.loc.expression
|
181
|
+
end
|
182
|
+
super(node: node, location: loc)
|
183
|
+
@type = type
|
184
|
+
@method = method
|
185
|
+
end
|
186
|
+
|
187
|
+
def header_line
|
188
|
+
"Type `#{type}` does not have method `#{method}`"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
class ReturnTypeMismatch < Base
|
193
|
+
attr_reader :expected
|
194
|
+
attr_reader :actual
|
195
|
+
attr_reader :result
|
196
|
+
|
197
|
+
include ResultPrinter
|
198
|
+
|
199
|
+
def initialize(node:, expected:, actual:, result:)
|
200
|
+
super(node: node)
|
201
|
+
@expected = expected
|
202
|
+
@actual = actual
|
203
|
+
@result = result
|
204
|
+
end
|
205
|
+
|
206
|
+
def header_line
|
207
|
+
"The method cannot return a value of type `#{actual}` because declared as type `#{expected}`"
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
class UnexpectedBlockGiven < Base
|
212
|
+
attr_reader :method_type
|
213
|
+
|
214
|
+
def initialize(node:, method_type:)
|
215
|
+
loc = node.loc.begin.join(node.loc.end)
|
216
|
+
super(node: node, location: loc)
|
217
|
+
@method_type = method_type
|
218
|
+
end
|
219
|
+
|
220
|
+
def header_line
|
221
|
+
"The method cannot be called with a block"
|
222
|
+
end
|
223
|
+
|
224
|
+
def to_s
|
225
|
+
format_message "method_type=#{method_type}"
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
class RequiredBlockMissing < Base
|
230
|
+
attr_reader :method_type
|
231
|
+
|
232
|
+
def initialize(node:, method_type:)
|
233
|
+
super(node: node, location: node.loc.selector)
|
234
|
+
@method_type = method_type
|
235
|
+
end
|
236
|
+
|
237
|
+
def header_line
|
238
|
+
"The method cannot be called without a block"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
class BlockTypeMismatch < Base
|
243
|
+
attr_reader :expected
|
244
|
+
attr_reader :actual
|
245
|
+
attr_reader :result
|
246
|
+
|
247
|
+
include ResultPrinter
|
248
|
+
|
249
|
+
def initialize(node:, expected:, actual:, result:)
|
250
|
+
super(node: node)
|
251
|
+
@expected = expected
|
252
|
+
@actual = actual
|
253
|
+
@result = result
|
254
|
+
end
|
255
|
+
|
256
|
+
def header_line
|
257
|
+
"Cannot pass a value of type `#{actual}` as a block-pass-argument of type `#{expected}`"
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
class BlockBodyTypeMismatch < Base
|
262
|
+
attr_reader :expected
|
263
|
+
attr_reader :actual
|
264
|
+
attr_reader :result
|
265
|
+
|
266
|
+
include ResultPrinter
|
267
|
+
|
268
|
+
def initialize(node:, expected:, actual:, result:)
|
269
|
+
super(node: node, location: node.loc.begin.join(node.loc.end))
|
270
|
+
@expected = expected
|
271
|
+
@actual = actual
|
272
|
+
@result = result
|
273
|
+
end
|
274
|
+
|
275
|
+
def header_line
|
276
|
+
"Cannot allow block body have type `#{actual}` because declared as type `#{expected}`"
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
class BreakTypeMismatch < Base
|
281
|
+
attr_reader :expected
|
282
|
+
attr_reader :actual
|
283
|
+
attr_reader :result
|
284
|
+
|
285
|
+
include ResultPrinter
|
286
|
+
|
287
|
+
def initialize(node:, expected:, actual:, result:)
|
288
|
+
super(node: node)
|
289
|
+
@expected = expected
|
290
|
+
@actual = actual
|
291
|
+
@result = result
|
292
|
+
end
|
293
|
+
|
294
|
+
def header_line
|
295
|
+
"Cannot break with a value of type `#{actual}` because type `#{expected}` is assumed"
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
class UnexpectedJump < Base
|
300
|
+
def header_line
|
301
|
+
"Cannot jump from here"
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
class UnexpectedJumpValue < Base
|
306
|
+
def header_line
|
307
|
+
"The value given to #{node.type} will be ignored"
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
class MethodArityMismatch < Base
|
312
|
+
attr_reader :method_type
|
313
|
+
|
314
|
+
def initialize(node:, method_type:)
|
315
|
+
args = case node.type
|
316
|
+
when :def
|
317
|
+
node.children[1]
|
318
|
+
when :defs
|
319
|
+
node.children[2]
|
320
|
+
end
|
321
|
+
super(node: node, location: args&.loc&.expression || node.loc.name)
|
322
|
+
@method_type = method_type
|
323
|
+
end
|
324
|
+
|
325
|
+
def header_line
|
326
|
+
"Method parameters are incompatible with declaration `#{method_type}`"
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
class IncompatibleMethodTypeAnnotation < Base
|
331
|
+
attr_reader :interface_method
|
332
|
+
attr_reader :annotation_method
|
333
|
+
attr_reader :result
|
334
|
+
|
335
|
+
include ResultPrinter
|
336
|
+
|
337
|
+
def initialize(node:, interface_method:, annotation_method:, result:)
|
338
|
+
raise
|
339
|
+
super(node: node)
|
340
|
+
@interface_method = interface_method
|
341
|
+
@annotation_method = annotation_method
|
342
|
+
@result = result
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
class MethodReturnTypeAnnotationMismatch < Base
|
347
|
+
attr_reader :method_type
|
348
|
+
attr_reader :annotation_type
|
349
|
+
attr_reader :result
|
350
|
+
|
351
|
+
include ResultPrinter
|
352
|
+
|
353
|
+
def initialize(node:, method_type:, annotation_type:, result:)
|
354
|
+
super(node: node)
|
355
|
+
@method_type = method_type
|
356
|
+
@annotation_type = annotation_type
|
357
|
+
@result = result
|
358
|
+
end
|
359
|
+
|
360
|
+
def header_line
|
361
|
+
"Annotation `@type return` specifies type `#{annotation_type}` where declared as type `#{method_type}`"
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
class MethodBodyTypeMismatch < Base
|
366
|
+
attr_reader :expected
|
367
|
+
attr_reader :actual
|
368
|
+
attr_reader :result
|
369
|
+
|
370
|
+
include ResultPrinter
|
371
|
+
|
372
|
+
def initialize(node:, expected:, actual:, result:)
|
373
|
+
super(node: node)
|
374
|
+
@expected = expected
|
375
|
+
@actual = actual
|
376
|
+
@result = result
|
377
|
+
end
|
378
|
+
|
379
|
+
def header_line
|
380
|
+
"Cannot allow method body have type `#{actual}` because declared as type `#{expected}`"
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
class UnexpectedYield < Base
|
385
|
+
def header_line
|
386
|
+
"No block given for `yield`"
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
class UnexpectedSuper < Base
|
391
|
+
attr_reader :method
|
392
|
+
|
393
|
+
def initialize(node:, method:)
|
394
|
+
super(node: node)
|
395
|
+
@method = method
|
396
|
+
end
|
397
|
+
|
398
|
+
def to_s
|
399
|
+
format_message "method=#{method}"
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
class MethodDefinitionMissing < Base
|
404
|
+
attr_reader :module_name
|
405
|
+
attr_reader :kind
|
406
|
+
attr_reader :missing_method
|
407
|
+
|
408
|
+
def initialize(node:, module_name:, kind:, missing_method:)
|
409
|
+
super(node: node, location: node.children[0].loc.expression)
|
410
|
+
@module_name = module_name
|
411
|
+
@kind = kind
|
412
|
+
@missing_method = missing_method
|
413
|
+
end
|
414
|
+
|
415
|
+
def header_line
|
416
|
+
method_name = case kind
|
417
|
+
when :module
|
418
|
+
".#{missing_method}"
|
419
|
+
when :instance
|
420
|
+
"##{missing_method}"
|
421
|
+
end
|
422
|
+
"Cannot find implementation of method `#{module_name}#{method_name}`"
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
class UnexpectedDynamicMethod < Base
|
427
|
+
attr_reader :module_name
|
428
|
+
attr_reader :method_name
|
429
|
+
|
430
|
+
def initialize(node:, module_name:, method_name:)
|
431
|
+
super(node: node, location: node.children[0].loc.expression)
|
432
|
+
@module_name = module_name
|
433
|
+
@method_name = method_name
|
434
|
+
end
|
435
|
+
|
436
|
+
def header_line
|
437
|
+
"@dynamic annotation contains unknown method name `#{method_name}`"
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
class UnknownConstantAssigned < Base
|
442
|
+
attr_reader :context
|
443
|
+
attr_reader :name
|
444
|
+
|
445
|
+
def initialize(node:, context:, name:)
|
446
|
+
const = node.children[0]
|
447
|
+
loc = if const
|
448
|
+
const.loc.expression.join(node.loc.name)
|
449
|
+
else
|
450
|
+
node.loc.name
|
451
|
+
end
|
452
|
+
super(node: node, location: loc)
|
453
|
+
@context = context
|
454
|
+
@name = name
|
455
|
+
end
|
456
|
+
|
457
|
+
def header_line
|
458
|
+
"Cannot find the declaration of constant `#{name}`"
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
class FallbackAny < Base
|
463
|
+
def initialize(node:)
|
464
|
+
super(node: node)
|
465
|
+
end
|
466
|
+
|
467
|
+
def header_line
|
468
|
+
"Cannot detect the type of the expression"
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
class UnsatisfiableConstraint < Base
|
473
|
+
attr_reader :method_type
|
474
|
+
attr_reader :var
|
475
|
+
attr_reader :sub_type
|
476
|
+
attr_reader :super_type
|
477
|
+
attr_reader :result
|
478
|
+
|
479
|
+
def initialize(node:, method_type:, var:, sub_type:, super_type:, result:)
|
480
|
+
super(node: node)
|
481
|
+
@method_type = method_type
|
482
|
+
@var = var
|
483
|
+
@sub_type = sub_type
|
484
|
+
@super_type = super_type
|
485
|
+
@result = result
|
486
|
+
end
|
487
|
+
|
488
|
+
include ResultPrinter
|
489
|
+
|
490
|
+
def header_line
|
491
|
+
"Unsatisfiable constraint `#{sub_type} <: #{var} <: #{super_type}` is generated through #{method_type}"
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
class IncompatibleAnnotation < Base
|
496
|
+
attr_reader :var_name
|
497
|
+
attr_reader :result
|
498
|
+
attr_reader :relation
|
499
|
+
|
500
|
+
def initialize(node:, var_name:, result:, relation:)
|
501
|
+
super(node: node)
|
502
|
+
@var_name = var_name
|
503
|
+
@result = result
|
504
|
+
@relation = relation
|
505
|
+
end
|
506
|
+
|
507
|
+
include ResultPrinter
|
508
|
+
|
509
|
+
def header_line
|
510
|
+
"Type annotation about `#{var_name}` is incompatible since #{relation} doesn't hold"
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
class IncompatibleTypeCase < Base
|
515
|
+
attr_reader :var_name
|
516
|
+
attr_reader :result
|
517
|
+
attr_reader :relation
|
518
|
+
|
519
|
+
def initialize(node:, var_name:, result:, relation:)
|
520
|
+
super(node: node)
|
521
|
+
@var_name = var_name
|
522
|
+
@result = result
|
523
|
+
@relation = relation
|
524
|
+
end
|
525
|
+
|
526
|
+
include ResultPrinter
|
527
|
+
|
528
|
+
def header_line
|
529
|
+
"Type annotation for branch about `#{var_name}` is incompatible since #{relation} doesn't hold"
|
530
|
+
end
|
531
|
+
end
|
532
|
+
|
533
|
+
class ElseOnExhaustiveCase < Base
|
534
|
+
attr_reader :type
|
535
|
+
|
536
|
+
def initialize(node:, type:)
|
537
|
+
super(node: node)
|
538
|
+
@type = type
|
539
|
+
end
|
540
|
+
|
541
|
+
def header_line
|
542
|
+
"The branch is unreachable because the condition is exhaustive"
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
class UnexpectedSplat < Base
|
547
|
+
attr_reader :type
|
548
|
+
|
549
|
+
def initialize(node:, type:)
|
550
|
+
super(node: node)
|
551
|
+
@type = type
|
552
|
+
end
|
553
|
+
|
554
|
+
def header_line
|
555
|
+
"Hash splat is given with object other than `Hash[X, Y]`"
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
559
|
+
class UnexpectedKeyword < Base
|
560
|
+
attr_reader :unexpected_keywords
|
561
|
+
|
562
|
+
def initialize(node:, unexpected_keywords:)
|
563
|
+
super(node: node)
|
564
|
+
@unexpected_keywords = unexpected_keywords
|
565
|
+
end
|
566
|
+
|
567
|
+
def header_line
|
568
|
+
keywords = unexpected_keywords.sort.map {|x| "`#{x}`" }
|
569
|
+
"Cannot specify unexpected keyword arguments: #{keywords.join(", ")}"
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
class MissingKeyword < Base
|
574
|
+
attr_reader :missing_keywords
|
575
|
+
|
576
|
+
def initialize(node:, missing_keywords:)
|
577
|
+
super(node: node)
|
578
|
+
@missing_keywords = missing_keywords
|
579
|
+
end
|
580
|
+
|
581
|
+
def header_line
|
582
|
+
keywords = missing_keywords.sort.map {|x| "`#{x}`" }
|
583
|
+
"Cannot omit required keywords: #{keywords.join(", ")}"
|
584
|
+
end
|
585
|
+
end
|
586
|
+
|
587
|
+
class UnsupportedSyntax < Base
|
588
|
+
attr_reader :message
|
589
|
+
|
590
|
+
def initialize(node:, message: nil)
|
591
|
+
super(node: node)
|
592
|
+
@message = message
|
593
|
+
end
|
594
|
+
|
595
|
+
def header_line
|
596
|
+
if message
|
597
|
+
message
|
598
|
+
else
|
599
|
+
"Syntax `#{node.type}` is not supported in Steep"
|
600
|
+
end
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
class UnexpectedError < Base
|
605
|
+
attr_reader :message
|
606
|
+
attr_reader :error
|
607
|
+
|
608
|
+
def initialize(node:, error:)
|
609
|
+
super(node: node)
|
610
|
+
@error = error
|
611
|
+
end
|
612
|
+
|
613
|
+
def header_line
|
614
|
+
"UnexpectedError: #{error.message}"
|
615
|
+
end
|
616
|
+
|
617
|
+
def detail_lines
|
618
|
+
error.backtrace.join("\n")
|
619
|
+
end
|
620
|
+
end
|
621
|
+
end
|
622
|
+
end
|
623
|
+
end
|