steep 0.39.0 → 0.40.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|