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
@@ -0,0 +1,224 @@
|
|
1
|
+
module Steep
|
2
|
+
module Diagnostic
|
3
|
+
module Signature
|
4
|
+
class Base
|
5
|
+
include Helper
|
6
|
+
|
7
|
+
attr_reader :location
|
8
|
+
|
9
|
+
def initialize(location:)
|
10
|
+
@location = location
|
11
|
+
end
|
12
|
+
|
13
|
+
def header_line
|
14
|
+
StringIO.new.tap do |io|
|
15
|
+
puts io
|
16
|
+
end.string
|
17
|
+
end
|
18
|
+
|
19
|
+
def detail_lines
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def diagnostic_code
|
24
|
+
"Ruby::#{error_name}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def path
|
28
|
+
location.buffer.name
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class SyntaxError < Base
|
33
|
+
attr_reader :exception
|
34
|
+
|
35
|
+
def initialize(exception, location:)
|
36
|
+
super(location: location)
|
37
|
+
@exception = exception
|
38
|
+
end
|
39
|
+
|
40
|
+
def header_line
|
41
|
+
"Syntax error: #{exception.message}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class DuplicatedDeclaration < Base
|
46
|
+
attr_reader :type_name
|
47
|
+
|
48
|
+
def initialize(type_name:, location:)
|
49
|
+
super(location: location)
|
50
|
+
@type_name = type_name
|
51
|
+
end
|
52
|
+
|
53
|
+
def header_line
|
54
|
+
"Declaration of `#{type_name}` is duplicated"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class UnknownTypeName < Base
|
59
|
+
attr_reader :name
|
60
|
+
|
61
|
+
def initialize(name:, location:)
|
62
|
+
super(location: location)
|
63
|
+
@name = name
|
64
|
+
end
|
65
|
+
|
66
|
+
def header_line
|
67
|
+
"Cannot find type `#{name}`"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class InvalidTypeApplication < Base
|
72
|
+
attr_reader :name
|
73
|
+
attr_reader :args
|
74
|
+
attr_reader :params
|
75
|
+
|
76
|
+
def initialize(name:, args:, params:, location:)
|
77
|
+
super(location: location)
|
78
|
+
@name = name
|
79
|
+
@args = args
|
80
|
+
@params = params
|
81
|
+
end
|
82
|
+
|
83
|
+
def header_line
|
84
|
+
case
|
85
|
+
when params.empty?
|
86
|
+
"Type `#{name}` is not generic but used as a generic type with #{args.size} arguments"
|
87
|
+
when args.empty?
|
88
|
+
"Type `#{name}` is generic but used as a non generic type"
|
89
|
+
else
|
90
|
+
"Type `#{name}` expects #{params.size} arguments, but #{args.size} arguments are given"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class InvalidMethodOverload < Base
|
96
|
+
attr_reader :class_name
|
97
|
+
attr_reader :method_name
|
98
|
+
|
99
|
+
def initialize(class_name:, method_name:, location:)
|
100
|
+
super(location: location)
|
101
|
+
@class_name = class_name
|
102
|
+
@method_name = method_name
|
103
|
+
end
|
104
|
+
|
105
|
+
def header_line
|
106
|
+
"Cannot find a non-overloading definition of `#{method_name}` in `#{class_name}`"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class UnknownMethodAlias < Base
|
111
|
+
attr_reader :class_name
|
112
|
+
attr_reader :method_name
|
113
|
+
|
114
|
+
def initialize(class_name:, method_name:, location:)
|
115
|
+
super(location: location)
|
116
|
+
@class_name = class_name
|
117
|
+
@method_name = method_name
|
118
|
+
end
|
119
|
+
|
120
|
+
def header_line
|
121
|
+
"Cannot find the original method `#{method_name}` in `#{class_name}`"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class DuplicatedMethodDefinition < Base
|
126
|
+
attr_reader :class_name
|
127
|
+
attr_reader :method_name
|
128
|
+
|
129
|
+
def initialize(class_name:, method_name:, location:)
|
130
|
+
super(location: location)
|
131
|
+
@class_name = class_name
|
132
|
+
@method_name = method_name
|
133
|
+
end
|
134
|
+
|
135
|
+
def header_line
|
136
|
+
"Non-overloading method definition of `#{method_name}` in `#{class_name}` cannot be duplicated"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
class RecursiveAlias < Base
|
141
|
+
attr_reader :class_name
|
142
|
+
attr_reader :names
|
143
|
+
attr_reader :location
|
144
|
+
|
145
|
+
def initialize(class_name:, names:, location:)
|
146
|
+
super(location: location)
|
147
|
+
@class_name = class_name
|
148
|
+
@names = names
|
149
|
+
end
|
150
|
+
|
151
|
+
def header_line
|
152
|
+
"Circular method alias is detected in `#{class_name}`: #{names.join(" -> ")}"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
class RecursiveAncestor < Base
|
157
|
+
attr_reader :ancestors
|
158
|
+
|
159
|
+
def initialize(ancestors:, location:)
|
160
|
+
super(location: location)
|
161
|
+
@ancestors = ancestors
|
162
|
+
end
|
163
|
+
|
164
|
+
def header_line
|
165
|
+
names = ancestors.map do |ancestor|
|
166
|
+
case ancestor
|
167
|
+
when RBS::Definition::Ancestor::Singleton
|
168
|
+
"singleton(#{ancestor.name})"
|
169
|
+
when RBS::Definition::Ancestor::Instance
|
170
|
+
if ancestor.args.empty?
|
171
|
+
ancestor.name.to_s
|
172
|
+
else
|
173
|
+
"#{ancestor.name}[#{ancestor.args.join(", ")}]"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
"Circular inheritance/mix-in is detected: #{names.join(" <: ")}"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
class SuperclassMismatch < Base
|
183
|
+
attr_reader :name
|
184
|
+
|
185
|
+
def initialize(name:, location:)
|
186
|
+
super(location: location)
|
187
|
+
@name = name
|
188
|
+
end
|
189
|
+
|
190
|
+
def header_line
|
191
|
+
"Different superclasses are specified for `#{name}`"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
class GenericParameterMismatch < Base
|
196
|
+
attr_reader :name
|
197
|
+
|
198
|
+
def initialize(name:, location:)
|
199
|
+
super(location: location)
|
200
|
+
@name = name
|
201
|
+
end
|
202
|
+
|
203
|
+
def header_line
|
204
|
+
"Different generic parameters are specified across definitions of `#{name}`"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
class InvalidVarianceAnnotation < Base
|
209
|
+
attr_reader :name
|
210
|
+
attr_reader :param
|
211
|
+
|
212
|
+
def initialize(name:, param:, location:)
|
213
|
+
super(location: location)
|
214
|
+
@name = name
|
215
|
+
@param = param
|
216
|
+
end
|
217
|
+
|
218
|
+
def header_line
|
219
|
+
"The variance of type parameter `#{param.name}` is #{param.variance}, but used in incompatible position here"
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
@@ -39,12 +39,19 @@ module Steep
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
42
|
-
when Project::Target::
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
42
|
+
when Project::Target::SignatureErrorStatus
|
43
|
+
formatter = Diagnostic::LSPFormatter.new
|
44
|
+
diagnostics = status.errors.group_by {|e| e.location.buffer }.transform_values do |errors|
|
45
|
+
errors.map {|error| formatter.format(error) }
|
46
|
+
end
|
47
|
+
|
48
|
+
diagnostics.each do |buffer, ds|
|
49
|
+
printer = DiagnosticPrinter.new(stdout: stdout, buffer: buffer)
|
50
|
+
ds.each do |d|
|
51
|
+
printer.print(d)
|
52
|
+
stdout.puts
|
53
|
+
end
|
54
|
+
end
|
48
55
|
end
|
49
56
|
end
|
50
57
|
end
|
data/lib/steep/drivers/check.rb
CHANGED
@@ -1,20 +1,18 @@
|
|
1
1
|
module Steep
|
2
2
|
module Drivers
|
3
3
|
class Check
|
4
|
+
LSP = LanguageServer::Protocol
|
5
|
+
|
4
6
|
attr_reader :stdout
|
5
7
|
attr_reader :stderr
|
6
8
|
attr_reader :command_line_patterns
|
7
9
|
|
8
|
-
attr_accessor :dump_all_types
|
9
|
-
|
10
10
|
include Utils::DriverHelper
|
11
11
|
|
12
12
|
def initialize(stdout:, stderr:)
|
13
13
|
@stdout = stdout
|
14
14
|
@stderr = stderr
|
15
15
|
@command_line_patterns = []
|
16
|
-
|
17
|
-
self.dump_all_types = false
|
18
16
|
end
|
19
17
|
|
20
18
|
def run
|
@@ -24,73 +22,98 @@ module Steep
|
|
24
22
|
loader.load_sources(command_line_patterns)
|
25
23
|
loader.load_signatures()
|
26
24
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
25
|
+
stdout.puts Rainbow("# Type checking files:").bold
|
26
|
+
stdout.puts
|
27
|
+
|
28
|
+
client_read, server_write = IO.pipe
|
29
|
+
server_read, client_write = IO.pipe
|
30
|
+
|
31
|
+
client_reader = LanguageServer::Protocol::Transport::Io::Reader.new(client_read)
|
32
|
+
client_writer = LanguageServer::Protocol::Transport::Io::Writer.new(client_write)
|
33
|
+
|
34
|
+
server_reader = LanguageServer::Protocol::Transport::Io::Reader.new(server_read)
|
35
|
+
server_writer = LanguageServer::Protocol::Transport::Io::Writer.new(server_write)
|
36
|
+
|
37
|
+
interaction_worker = Server::WorkerProcess.spawn_worker(:interaction, name: "interaction", steepfile: project.steepfile_path, delay_shutdown: true)
|
38
|
+
signature_worker = Server::WorkerProcess.spawn_worker(:signature, name: "signature", steepfile: project.steepfile_path, delay_shutdown: true)
|
39
|
+
code_workers = Server::WorkerProcess.spawn_code_workers(steepfile: project.steepfile_path, delay_shutdown: true)
|
40
|
+
|
41
|
+
master = Server::Master.new(
|
42
|
+
project: project,
|
43
|
+
reader: server_reader,
|
44
|
+
writer: server_writer,
|
45
|
+
interaction_worker: interaction_worker,
|
46
|
+
signature_worker: signature_worker,
|
47
|
+
code_workers: code_workers
|
48
|
+
)
|
49
|
+
|
50
|
+
main_thread = Thread.start do
|
51
|
+
master.start()
|
41
52
|
end
|
53
|
+
main_thread.abort_on_exception = true
|
42
54
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
source_file.errors.select {|error| target.options.error_to_report?(error) }.each do |error|
|
57
|
-
error.print_to stdout
|
58
|
-
end
|
59
|
-
when Project::SourceFile::TypeCheckErrorStatus
|
60
|
-
Steep.log_error source_file.status.error
|
61
|
-
end
|
62
|
-
end
|
63
|
-
when Project::Target::SignatureOtherErrorStatus
|
64
|
-
Steep.log_error status.error
|
55
|
+
client_writer.write({ method: :initialize, id: 0 })
|
56
|
+
|
57
|
+
shutdown_id = -1
|
58
|
+
client_writer.write({ method: :shutdown, id: shutdown_id })
|
59
|
+
|
60
|
+
responses = []
|
61
|
+
error_messages = []
|
62
|
+
client_reader.read do |response|
|
63
|
+
case
|
64
|
+
when response[:method] == "textDocument/publishDiagnostics"
|
65
|
+
ds = response[:params][:diagnostics]
|
66
|
+
if ds.empty?
|
67
|
+
stdout.print "."
|
65
68
|
else
|
66
|
-
|
69
|
+
stdout.print "F"
|
70
|
+
end
|
71
|
+
responses << response[:params]
|
72
|
+
stdout.flush
|
73
|
+
when response[:method] == "window/showMessage"
|
74
|
+
# Assuming ERROR message means unrecoverable error.
|
75
|
+
message = response[:params]
|
76
|
+
if message[:type] == LSP::Constant::MessageType::ERROR
|
77
|
+
error_messages << message[:message]
|
67
78
|
end
|
79
|
+
when response[:id] == shutdown_id
|
80
|
+
break
|
68
81
|
end
|
69
82
|
end
|
70
83
|
|
71
|
-
|
72
|
-
|
73
|
-
return 0
|
74
|
-
end
|
84
|
+
client_writer.write({ method: :exit })
|
85
|
+
client_writer.io.close()
|
75
86
|
|
76
|
-
|
77
|
-
end
|
87
|
+
main_thread.join()
|
78
88
|
|
79
|
-
|
80
|
-
|
89
|
+
stdout.puts
|
90
|
+
stdout.puts
|
81
91
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
92
|
+
case
|
93
|
+
when responses.all? {|res| res[:diagnostics].empty? } && error_messages.empty?
|
94
|
+
emoji = %w(🫖 🫖 🫖 🫖 🫖 🫖 🫖 🫖 🍵 🧋 🧉).sample
|
95
|
+
stdout.puts Rainbow("No type error detected. #{emoji}").green.bold
|
96
|
+
0
|
97
|
+
when !error_messages.empty?
|
98
|
+
stdout.puts Rainbow("Unexpected error reported. 🚨").red.bold
|
99
|
+
1
|
100
|
+
else
|
101
|
+
errors = responses.reject {|res| res[:diagnostics].empty? }
|
102
|
+
total = errors.sum {|res| res[:diagnostics].size }
|
103
|
+
stdout.puts Rainbow("Detected #{total} problems from #{errors.size} files").red.bold
|
104
|
+
stdout.puts
|
105
|
+
|
106
|
+
errors.each do |resp|
|
107
|
+
path = project.relative_path(Pathname(URI.parse(resp[:uri]).path))
|
108
|
+
buffer = RBS::Buffer.new(name: path, content: path.read)
|
109
|
+
printer = DiagnosticPrinter.new(buffer: buffer, stdout: stdout)
|
90
110
|
|
91
|
-
|
92
|
-
|
93
|
-
|
111
|
+
resp[:diagnostics].each do |diag|
|
112
|
+
printer.print(diag)
|
113
|
+
stdout.puts
|
114
|
+
end
|
115
|
+
end
|
116
|
+
1
|
94
117
|
end
|
95
118
|
end
|
96
119
|
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Steep
|
2
|
+
module Drivers
|
3
|
+
class DiagnosticPrinter
|
4
|
+
LSP = LanguageServer::Protocol
|
5
|
+
|
6
|
+
attr_reader :stdout
|
7
|
+
attr_reader :buffer
|
8
|
+
|
9
|
+
def initialize(stdout:, buffer:)
|
10
|
+
@stdout = stdout
|
11
|
+
@buffer = buffer
|
12
|
+
end
|
13
|
+
|
14
|
+
def path
|
15
|
+
buffer.name
|
16
|
+
end
|
17
|
+
|
18
|
+
def color_severity(string, severity:)
|
19
|
+
s = Rainbow(string)
|
20
|
+
|
21
|
+
case severity
|
22
|
+
when LSP::Constant::DiagnosticSeverity::ERROR
|
23
|
+
s.red
|
24
|
+
when LSP::Constant::DiagnosticSeverity::WARNING
|
25
|
+
s.yellow
|
26
|
+
when LSP::Constant::DiagnosticSeverity::INFORMATION
|
27
|
+
s.blue
|
28
|
+
else
|
29
|
+
s
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def severity_message(severity)
|
34
|
+
string = case severity
|
35
|
+
when LSP::Constant::DiagnosticSeverity::ERROR
|
36
|
+
"error"
|
37
|
+
when LSP::Constant::DiagnosticSeverity::WARNING
|
38
|
+
"warning"
|
39
|
+
when LSP::Constant::DiagnosticSeverity::INFORMATION
|
40
|
+
"information"
|
41
|
+
when LSP::Constant::DiagnosticSeverity::HINT
|
42
|
+
"hint"
|
43
|
+
end
|
44
|
+
|
45
|
+
color_severity(string, severity: severity)
|
46
|
+
end
|
47
|
+
|
48
|
+
def location(diagnostic)
|
49
|
+
start = diagnostic[:range][:start]
|
50
|
+
Rainbow("#{path}:#{start[:line]+1}:#{start[:character]}").magenta
|
51
|
+
end
|
52
|
+
|
53
|
+
def print(diagnostic)
|
54
|
+
header, *rest = diagnostic[:message].split(/\n/)
|
55
|
+
|
56
|
+
stdout.puts "#{location(diagnostic)}: [#{severity_message(diagnostic[:severity])}] #{Rainbow(header).underline}"
|
57
|
+
|
58
|
+
unless rest.empty?
|
59
|
+
rest.each do |message|
|
60
|
+
stdout.puts "│ #{message}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
if diagnostic[:code]
|
65
|
+
stdout.puts "│" unless rest.empty?
|
66
|
+
stdout.puts "│ Diagnostic ID: #{diagnostic[:code]}"
|
67
|
+
end
|
68
|
+
|
69
|
+
stdout.puts "│"
|
70
|
+
|
71
|
+
print_source_line(diagnostic)
|
72
|
+
end
|
73
|
+
|
74
|
+
def print_source_line(diagnostic)
|
75
|
+
start_pos = diagnostic[:range][:start]
|
76
|
+
end_pos = diagnostic[:range][:end]
|
77
|
+
|
78
|
+
line = buffer.lines[start_pos[:line]]
|
79
|
+
|
80
|
+
leading = line[0...start_pos[:character]]
|
81
|
+
if start_pos[:line] == end_pos[:line]
|
82
|
+
subject = line[start_pos[:character]...end_pos[:character]]
|
83
|
+
trailing = line[end_pos[:character]...].chomp
|
84
|
+
else
|
85
|
+
subject = line[start_pos[:character]...].chomp
|
86
|
+
trailing = ""
|
87
|
+
end
|
88
|
+
|
89
|
+
stdout.puts "└ #{leading}#{color_severity(subject, severity: diagnostic[:severity])}#{trailing}"
|
90
|
+
stdout.puts " #{" " * leading.size}#{"~" * subject.size}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|