steep 0.37.0 → 0.42.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 +34 -0
- data/Rakefile +5 -2
- data/bin/output_rebaseline.rb +34 -0
- data/bin/output_test.rb +53 -0
- data/lib/steep.rb +95 -14
- data/lib/steep/ast/types/bot.rb +1 -1
- data/lib/steep/ast/types/class.rb +4 -0
- data/lib/steep/ast/types/factory.rb +10 -0
- data/lib/steep/ast/types/logic.rb +16 -3
- data/lib/steep/ast/types/top.rb +1 -1
- data/lib/steep/cli.rb +31 -7
- data/lib/steep/diagnostic/helper.rb +17 -0
- data/lib/steep/diagnostic/lsp_formatter.rb +16 -0
- data/lib/steep/diagnostic/ruby.rb +619 -0
- data/lib/steep/diagnostic/signature.rb +357 -0
- data/lib/steep/drivers/annotations.rb +19 -28
- data/lib/steep/drivers/check.rb +182 -60
- data/lib/steep/drivers/diagnostic_printer.rb +99 -0
- data/lib/steep/drivers/langserver.rb +3 -8
- data/lib/steep/drivers/print_project.rb +10 -9
- data/lib/steep/drivers/stats.rb +124 -32
- data/lib/steep/drivers/trace_printer.rb +5 -1
- data/lib/steep/drivers/utils/jobs_count.rb +9 -0
- data/lib/steep/drivers/validate.rb +31 -13
- data/lib/steep/drivers/watch.rb +69 -48
- data/lib/steep/drivers/worker.rb +16 -8
- data/lib/steep/expectations.rb +159 -0
- data/lib/steep/index/rbs_index.rb +334 -0
- data/lib/steep/index/signature_symbol_provider.rb +162 -0
- data/lib/steep/index/source_index.rb +100 -0
- data/lib/steep/project.rb +0 -30
- data/lib/steep/project/dsl.rb +5 -3
- data/lib/steep/project/options.rb +4 -4
- data/lib/steep/project/pattern.rb +56 -0
- data/lib/steep/project/target.rb +9 -214
- data/lib/steep/range_extension.rb +29 -0
- data/lib/steep/server/base_worker.rb +43 -7
- data/lib/steep/server/change_buffer.rb +63 -0
- data/lib/steep/server/interaction_worker.rb +73 -56
- data/lib/steep/server/master.rb +245 -109
- data/lib/steep/server/type_check_worker.rb +122 -0
- data/lib/steep/server/worker_process.rb +17 -15
- data/lib/steep/{project → services}/completion_provider.rb +3 -3
- data/lib/steep/services/content_change.rb +61 -0
- data/lib/steep/services/file_loader.rb +48 -0
- data/lib/steep/{project → services}/hover_content.rb +14 -16
- data/lib/steep/services/path_assignment.rb +29 -0
- data/lib/steep/services/signature_service.rb +369 -0
- data/lib/steep/services/stats_calculator.rb +69 -0
- data/lib/steep/services/type_check_service.rb +342 -0
- data/lib/steep/signature/validator.rb +174 -32
- data/lib/steep/subtyping/check.rb +248 -47
- data/lib/steep/subtyping/constraints.rb +2 -2
- data/lib/steep/type_construction.rb +565 -295
- data/lib/steep/type_inference/constant_env.rb +5 -1
- data/lib/steep/type_inference/local_variable_type_env.rb +26 -12
- data/lib/steep/type_inference/logic_type_interpreter.rb +99 -26
- data/lib/steep/type_inference/type_env.rb +43 -17
- data/lib/steep/typing.rb +8 -2
- 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_expectations.yml +96 -0
- data/smoke/and/a.rb +0 -3
- data/smoke/and/test_expectations.yml +31 -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_expectations.yml +103 -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_expectations.yml +125 -0
- data/smoke/case/a.rb +0 -3
- data/smoke/case/test_expectations.yml +47 -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_expectations.yml +120 -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_expectations.yml +139 -0
- data/smoke/diagnostics-rbs-duplicated/Steepfile +5 -0
- data/smoke/diagnostics-rbs-duplicated/a.rbs +5 -0
- data/smoke/diagnostics-rbs-duplicated/test_expectations.yml +13 -0
- data/smoke/diagnostics-rbs/Steepfile +8 -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_expectations.yml +231 -0
- data/smoke/diagnostics-rbs/unknown-method-alias.rbs +3 -0
- data/smoke/diagnostics-rbs/unknown-type-name-2.rbs +5 -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_expectations.yml +477 -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_expectations.yml +13 -0
- data/smoke/ensure/a.rb +0 -4
- data/smoke/ensure/test_expectations.yml +62 -0
- data/smoke/enumerator/a.rb +0 -6
- data/smoke/enumerator/b.rb +0 -3
- data/smoke/enumerator/test_expectations.yml +135 -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/f.rb +2 -0
- data/smoke/extension/f.rbs +3 -0
- data/smoke/extension/test_expectations.yml +73 -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_expectations.yml +81 -0
- data/smoke/hello/hello.rb +0 -2
- data/smoke/hello/test_expectations.yml +25 -0
- data/smoke/if/a.rb +0 -2
- data/smoke/if/test_expectations.yml +34 -0
- data/smoke/implements/a.rb +0 -2
- data/smoke/implements/test_expectations.yml +23 -0
- data/smoke/initialize/test_expectations.yml +1 -0
- data/smoke/integer/a.rb +0 -7
- data/smoke/integer/test_expectations.yml +101 -0
- data/smoke/interface/a.rb +0 -2
- data/smoke/interface/test_expectations.yml +23 -0
- data/smoke/kwbegin/a.rb +0 -1
- data/smoke/kwbegin/test_expectations.yml +17 -0
- data/smoke/lambda/a.rb +1 -4
- data/smoke/lambda/test_expectations.yml +39 -0
- data/smoke/literal/a.rb +0 -5
- data/smoke/literal/b.rb +0 -2
- data/smoke/literal/test_expectations.yml +106 -0
- data/smoke/map/test_expectations.yml +1 -0
- data/smoke/method/a.rb +0 -5
- data/smoke/method/b.rb +0 -1
- data/smoke/method/test_expectations.yml +90 -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_expectations.yml +75 -0
- data/smoke/regexp/a.rb +0 -38
- data/smoke/regexp/b.rb +0 -26
- data/smoke/regexp/test_expectations.yml +615 -0
- data/smoke/regression/set_divide.rb +0 -4
- data/smoke/regression/test_expectations.yml +43 -0
- data/smoke/rescue/a.rb +0 -5
- data/smoke/rescue/test_expectations.yml +79 -0
- data/smoke/self/a.rb +0 -2
- data/smoke/self/test_expectations.yml +23 -0
- data/smoke/skip/skip.rb +0 -2
- data/smoke/skip/test_expectations.yml +23 -0
- data/smoke/stdout/test_expectations.yml +1 -0
- data/smoke/super/a.rb +0 -4
- data/smoke/super/test_expectations.yml +79 -0
- data/smoke/toplevel/a.rb +0 -1
- data/smoke/toplevel/test_expectations.yml +15 -0
- data/smoke/tsort/Steepfile +2 -0
- data/smoke/tsort/a.rb +0 -3
- data/smoke/tsort/test_expectations.yml +63 -0
- data/smoke/type_case/a.rb +0 -4
- data/smoke/type_case/test_expectations.yml +48 -0
- data/smoke/unexpected/Steepfile +5 -0
- data/smoke/unexpected/test_expectations.yml +25 -0
- data/smoke/unexpected/unexpected.rb +1 -0
- data/smoke/unexpected/unexpected.rbs +3 -0
- data/smoke/yield/a.rb +0 -3
- data/smoke/yield/b.rb +6 -0
- data/smoke/yield/test_expectations.yml +68 -0
- data/steep.gemspec +4 -3
- metadata +144 -29
- data/bin/smoke_runner.rb +0 -139
- data/lib/steep/drivers/signature_error_printer.rb +0 -25
- data/lib/steep/errors.rb +0 -565
- data/lib/steep/project/file_loader.rb +0 -68
- data/lib/steep/project/signature_file.rb +0 -33
- data/lib/steep/project/source_file.rb +0 -129
- data/lib/steep/server/code_worker.rb +0 -137
- data/lib/steep/server/signature_worker.rb +0 -152
- data/lib/steep/server/utils.rb +0 -69
- data/lib/steep/signature/errors.rb +0 -82
- data/lib/steep/type_assignability.rb +0 -367
@@ -1,68 +0,0 @@
|
|
1
|
-
module Steep
|
2
|
-
class Project
|
3
|
-
class FileLoader
|
4
|
-
attr_reader :project
|
5
|
-
|
6
|
-
def initialize(project:)
|
7
|
-
@project = project
|
8
|
-
end
|
9
|
-
|
10
|
-
def each_path_in_patterns(patterns, ext)
|
11
|
-
patterns.each do |path|
|
12
|
-
absolute_path = project.base_dir + path
|
13
|
-
|
14
|
-
if absolute_path.file?
|
15
|
-
yield project.relative_path(absolute_path)
|
16
|
-
else
|
17
|
-
files = if absolute_path.directory?
|
18
|
-
Pathname.glob("#{absolute_path}/**/*#{ext}")
|
19
|
-
else
|
20
|
-
Pathname.glob(absolute_path)
|
21
|
-
end
|
22
|
-
|
23
|
-
files.sort.each do |source_path|
|
24
|
-
yield project.relative_path(source_path)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def load_sources(command_line_patterns)
|
31
|
-
loaded_paths = Set[]
|
32
|
-
|
33
|
-
project.targets.each do |target|
|
34
|
-
Steep.logger.tagged "target=#{target.name}" do
|
35
|
-
target_patterns = command_line_patterns.empty? ? target.source_patterns : command_line_patterns
|
36
|
-
|
37
|
-
each_path_in_patterns(target_patterns, ".rb") do |path|
|
38
|
-
if target.possible_source_file?(path)
|
39
|
-
if loaded_paths.include?(path)
|
40
|
-
Steep.logger.warn { "Skipping #{target} while loading #{path}... (Already loaded to another target.)" }
|
41
|
-
else
|
42
|
-
Steep.logger.info { "Adding source file: #{path}" }
|
43
|
-
target.add_source path, project.absolute_path(path).read
|
44
|
-
loaded_paths << path
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def load_signatures()
|
53
|
-
project.targets.each do |target|
|
54
|
-
Steep.logger.tagged "target=#{target.name}" do
|
55
|
-
each_path_in_patterns target.signature_patterns, ".rbs" do |path|
|
56
|
-
if target.possible_signature_file?(path)
|
57
|
-
unless target.signature_file?(path)
|
58
|
-
Steep.logger.info { "Adding signature file: #{path}" }
|
59
|
-
target.add_signature path, project.absolute_path(path).read
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
module Steep
|
2
|
-
class Project
|
3
|
-
class SignatureFile
|
4
|
-
attr_reader :path
|
5
|
-
attr_reader :content
|
6
|
-
attr_reader :content_updated_at
|
7
|
-
|
8
|
-
attr_reader :status
|
9
|
-
|
10
|
-
ParseErrorStatus = Struct.new(:error, :timestamp, keyword_init: true)
|
11
|
-
DeclarationsStatus = Struct.new(:declarations, :timestamp, keyword_init: true)
|
12
|
-
|
13
|
-
def initialize(path:)
|
14
|
-
@path = path
|
15
|
-
self.content = ""
|
16
|
-
end
|
17
|
-
|
18
|
-
def content=(content)
|
19
|
-
@content_updated_at = Time.now
|
20
|
-
@content = content
|
21
|
-
@status = nil
|
22
|
-
end
|
23
|
-
|
24
|
-
def load!
|
25
|
-
buffer = RBS::Buffer.new(name: path, content: content)
|
26
|
-
decls = RBS::Parser.parse_signature(buffer)
|
27
|
-
@status = DeclarationsStatus.new(declarations: decls, timestamp: Time.now)
|
28
|
-
rescue RBS::Parser::SyntaxError, RBS::Parser::SemanticsError => exn
|
29
|
-
@status = ParseErrorStatus.new(error: exn, timestamp: Time.now)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,129 +0,0 @@
|
|
1
|
-
module Steep
|
2
|
-
class Project
|
3
|
-
class SourceFile
|
4
|
-
attr_reader :path
|
5
|
-
attr_reader :content
|
6
|
-
attr_reader :content_updated_at
|
7
|
-
attr_reader :factory
|
8
|
-
|
9
|
-
attr_accessor :status
|
10
|
-
|
11
|
-
ParseErrorStatus = Struct.new(:error, :timestamp, keyword_init: true)
|
12
|
-
AnnotationSyntaxErrorStatus = Struct.new(:error, :location, :timestamp, keyword_init: true)
|
13
|
-
TypeCheckStatus = Struct.new(:typing, :source, :timestamp, keyword_init: true)
|
14
|
-
TypeCheckErrorStatus = Struct.new(:error, :timestamp, keyword_init: true)
|
15
|
-
|
16
|
-
def initialize(path:)
|
17
|
-
@path = path
|
18
|
-
@content = false
|
19
|
-
self.content = ""
|
20
|
-
end
|
21
|
-
|
22
|
-
def content=(content)
|
23
|
-
@content_updated_at = Time.now
|
24
|
-
@content = content
|
25
|
-
@status = nil
|
26
|
-
end
|
27
|
-
|
28
|
-
def errors
|
29
|
-
case status
|
30
|
-
when TypeCheckStatus
|
31
|
-
status.typing.errors
|
32
|
-
else
|
33
|
-
[]
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.parse(source_code, path:, factory:)
|
38
|
-
Source.parse(source_code, path: path.to_s, factory: factory, labeling: ASTUtils::Labeling.new)
|
39
|
-
end
|
40
|
-
|
41
|
-
def self.type_check(source, subtyping:)
|
42
|
-
annotations = source.annotations(block: source.node, factory: subtyping.factory, current_module: RBS::Namespace.root)
|
43
|
-
const_env = TypeInference::ConstantEnv.new(factory: subtyping.factory, context: [RBS::Namespace.root])
|
44
|
-
type_env = TypeInference::TypeEnv.build(annotations: annotations,
|
45
|
-
subtyping: subtyping,
|
46
|
-
const_env: const_env,
|
47
|
-
signatures: subtyping.factory.env)
|
48
|
-
lvar_env = TypeInference::LocalVariableTypeEnv.empty(
|
49
|
-
subtyping: subtyping,
|
50
|
-
self_type: AST::Builtin::Object.instance_type
|
51
|
-
).annotate(annotations)
|
52
|
-
|
53
|
-
context = TypeInference::Context.new(
|
54
|
-
block_context: nil,
|
55
|
-
module_context: TypeInference::Context::ModuleContext.new(
|
56
|
-
instance_type: AST::Builtin::Object.instance_type,
|
57
|
-
module_type: AST::Builtin::Object.module_type,
|
58
|
-
implement_name: nil,
|
59
|
-
current_namespace: RBS::Namespace.root,
|
60
|
-
const_env: const_env,
|
61
|
-
class_name: AST::Builtin::Object.module_name,
|
62
|
-
instance_definition: subtyping.factory.definition_builder.build_instance(AST::Builtin::Object.module_name),
|
63
|
-
module_definition: subtyping.factory.definition_builder.build_singleton(AST::Builtin::Object.module_name)
|
64
|
-
),
|
65
|
-
method_context: nil,
|
66
|
-
break_context: nil,
|
67
|
-
self_type: AST::Builtin::Object.instance_type,
|
68
|
-
type_env: type_env,
|
69
|
-
lvar_env: lvar_env,
|
70
|
-
call_context: TypeInference::MethodCall::TopLevelContext.new
|
71
|
-
)
|
72
|
-
|
73
|
-
typing = Typing.new(source: source, root_context: context)
|
74
|
-
|
75
|
-
construction = TypeConstruction.new(
|
76
|
-
checker: subtyping,
|
77
|
-
annotations: annotations,
|
78
|
-
source: source,
|
79
|
-
context: context,
|
80
|
-
typing: typing
|
81
|
-
)
|
82
|
-
|
83
|
-
construction.synthesize(source.node) if source.node
|
84
|
-
|
85
|
-
typing
|
86
|
-
end
|
87
|
-
|
88
|
-
def type_check(subtyping, env_updated_at)
|
89
|
-
# skip type check
|
90
|
-
return false if status && env_updated_at <= status.timestamp
|
91
|
-
|
92
|
-
now = Time.now
|
93
|
-
|
94
|
-
parse(subtyping.factory) do |source|
|
95
|
-
typing = self.class.type_check(source, subtyping: subtyping)
|
96
|
-
@status = TypeCheckStatus.new(typing: typing, source: source, timestamp: now)
|
97
|
-
rescue RBS::NoTypeFoundError,
|
98
|
-
RBS::NoMixinFoundError,
|
99
|
-
RBS::NoSuperclassFoundError,
|
100
|
-
RBS::DuplicatedMethodDefinitionError,
|
101
|
-
RBS::InvalidTypeApplicationError => exn
|
102
|
-
# Skip logging known signature errors (they are handled with load_signatures(validate: true))
|
103
|
-
@status = TypeCheckErrorStatus.new(error: exn, timestamp: now)
|
104
|
-
rescue => exn
|
105
|
-
Steep.log_error(exn)
|
106
|
-
@status = TypeCheckErrorStatus.new(error: exn, timestamp: now)
|
107
|
-
end
|
108
|
-
|
109
|
-
true
|
110
|
-
end
|
111
|
-
|
112
|
-
def parse(factory)
|
113
|
-
now = Time.now
|
114
|
-
|
115
|
-
if status.is_a?(TypeCheckStatus)
|
116
|
-
yield status.source
|
117
|
-
else
|
118
|
-
yield self.class.parse(content, path: path, factory: factory)
|
119
|
-
end
|
120
|
-
rescue AnnotationParser::SyntaxError => exn
|
121
|
-
Steep.logger.warn { "Annotation syntax error on #{path}: #{exn.inspect}" }
|
122
|
-
@status = AnnotationSyntaxErrorStatus.new(error: exn, location: exn.location, timestamp: now)
|
123
|
-
rescue ::Parser::SyntaxError, EncodingError => exn
|
124
|
-
Steep.logger.warn { "Source parsing error on #{path}: #{exn.inspect}" }
|
125
|
-
@status = ParseErrorStatus.new(error: exn, timestamp: now)
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
@@ -1,137 +0,0 @@
|
|
1
|
-
module Steep
|
2
|
-
module Server
|
3
|
-
class CodeWorker < BaseWorker
|
4
|
-
LSP = LanguageServer::Protocol
|
5
|
-
|
6
|
-
include Utils
|
7
|
-
|
8
|
-
attr_reader :typecheck_paths
|
9
|
-
attr_reader :queue
|
10
|
-
|
11
|
-
def initialize(project:, reader:, writer:, queue: Queue.new)
|
12
|
-
super(project: project, reader: reader, writer: writer)
|
13
|
-
|
14
|
-
@typecheck_paths = Set[]
|
15
|
-
@queue = queue
|
16
|
-
end
|
17
|
-
|
18
|
-
def enqueue_type_check(target:, path:)
|
19
|
-
Steep.logger.info "Enqueueing type check: #{target.name}::#{path}..."
|
20
|
-
queue << [target, path]
|
21
|
-
end
|
22
|
-
|
23
|
-
def typecheck_file(path, target)
|
24
|
-
Steep.logger.info "Starting type checking: #{target.name}::#{path}..."
|
25
|
-
|
26
|
-
source = target.source_files[path]
|
27
|
-
target.type_check(target_sources: [source], validate_signatures: false)
|
28
|
-
|
29
|
-
if target.status.is_a?(Project::Target::TypeCheckStatus) && target.status.type_check_sources.empty?
|
30
|
-
Steep.logger.debug "Skipped type checking: #{target.name}::#{path}"
|
31
|
-
else
|
32
|
-
Steep.logger.info "Finished type checking: #{target.name}::#{path}"
|
33
|
-
end
|
34
|
-
|
35
|
-
diagnostics = source_diagnostics(source, target.options)
|
36
|
-
|
37
|
-
writer.write(
|
38
|
-
method: :"textDocument/publishDiagnostics",
|
39
|
-
params: LSP::Interface::PublishDiagnosticsParams.new(
|
40
|
-
uri: URI.parse(project.absolute_path(path).to_s).tap {|uri| uri.scheme = "file"},
|
41
|
-
diagnostics: diagnostics
|
42
|
-
)
|
43
|
-
)
|
44
|
-
end
|
45
|
-
|
46
|
-
def source_diagnostics(source, options)
|
47
|
-
case status = source.status
|
48
|
-
when Project::SourceFile::ParseErrorStatus
|
49
|
-
[]
|
50
|
-
when Project::SourceFile::AnnotationSyntaxErrorStatus
|
51
|
-
[
|
52
|
-
LSP::Interface::Diagnostic.new(
|
53
|
-
message: "Annotation syntax error: #{status.error.cause.message}",
|
54
|
-
severity: LSP::Constant::DiagnosticSeverity::ERROR,
|
55
|
-
range: LSP::Interface::Range.new(
|
56
|
-
start: LSP::Interface::Position.new(
|
57
|
-
line: status.location.start_line - 1,
|
58
|
-
character: status.location.start_column
|
59
|
-
),
|
60
|
-
end: LSP::Interface::Position.new(
|
61
|
-
line: status.location.end_line - 1,
|
62
|
-
character: status.location.end_column
|
63
|
-
)
|
64
|
-
)
|
65
|
-
)
|
66
|
-
]
|
67
|
-
when Project::SourceFile::TypeCheckStatus
|
68
|
-
status.typing.errors.select {|error| options.error_to_report?(error) }.map do |error|
|
69
|
-
loc = error.location_to_str
|
70
|
-
|
71
|
-
LSP::Interface::Diagnostic.new(
|
72
|
-
message: StringIO.new.tap {|io| error.print_to(io) }.string.gsub(/\A#{Regexp.escape(loc)}: /, "").chomp,
|
73
|
-
severity: LSP::Constant::DiagnosticSeverity::ERROR,
|
74
|
-
range: LSP::Interface::Range.new(
|
75
|
-
start: LSP::Interface::Position.new(
|
76
|
-
line: error.node.loc.line - 1,
|
77
|
-
character: error.node.loc.column
|
78
|
-
),
|
79
|
-
end: LSP::Interface::Position.new(
|
80
|
-
line: error.node.loc.last_line - 1,
|
81
|
-
character: error.node.loc.last_column
|
82
|
-
)
|
83
|
-
)
|
84
|
-
)
|
85
|
-
end
|
86
|
-
when Project::SourceFile::TypeCheckErrorStatus
|
87
|
-
[]
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def handle_request(request)
|
92
|
-
case request[:method]
|
93
|
-
when "initialize"
|
94
|
-
# Don't respond to initialize request, but start type checking.
|
95
|
-
project.targets.each do |target|
|
96
|
-
target.source_files.each_key do |path|
|
97
|
-
if typecheck_paths.include?(path)
|
98
|
-
enqueue_type_check(target: target, path: path)
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
when "workspace/executeCommand"
|
104
|
-
if request[:params][:command] == "steep/registerSourceToWorker"
|
105
|
-
paths = request[:params][:arguments].map {|arg| source_path(URI.parse(arg)) }
|
106
|
-
typecheck_paths.merge(paths)
|
107
|
-
end
|
108
|
-
|
109
|
-
when "textDocument/didChange"
|
110
|
-
update_source(request) do |path, _|
|
111
|
-
source_target, signature_targets = project.targets_for_path(path)
|
112
|
-
|
113
|
-
if source_target
|
114
|
-
if typecheck_paths.include?(path)
|
115
|
-
enqueue_type_check(target: source_target, path: path)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
signature_targets.each do |target|
|
120
|
-
target.source_files.each_key do |source_path|
|
121
|
-
if typecheck_paths.include?(source_path)
|
122
|
-
enqueue_type_check(target: target, path: source_path)
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
def handle_job(job)
|
131
|
-
target, path = job
|
132
|
-
|
133
|
-
typecheck_file(path, target)
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
@@ -1,152 +0,0 @@
|
|
1
|
-
module Steep
|
2
|
-
module Server
|
3
|
-
class SignatureWorker < BaseWorker
|
4
|
-
attr_reader :queue
|
5
|
-
attr_reader :last_target_validated_at
|
6
|
-
|
7
|
-
def initialize(project:, reader:, writer:, queue: Queue.new)
|
8
|
-
super(project: project, reader: reader, writer: writer)
|
9
|
-
|
10
|
-
@queue = queue
|
11
|
-
@last_target_validated_at = {}
|
12
|
-
end
|
13
|
-
|
14
|
-
def validate_signature_if_required(request)
|
15
|
-
path = source_path(URI.parse(request[:params][:textDocument][:uri]))
|
16
|
-
|
17
|
-
project.targets.each do |target|
|
18
|
-
if target.signature_file?(path)
|
19
|
-
enqueue_target target: target, timestamp: Time.now
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def enqueue_target(target:, timestamp:)
|
25
|
-
Steep.logger.debug "queueing target #{target.name}@#{timestamp}"
|
26
|
-
last_target_validated_at[target] = timestamp
|
27
|
-
queue << [target, timestamp]
|
28
|
-
end
|
29
|
-
|
30
|
-
def handle_request(request)
|
31
|
-
case request[:method]
|
32
|
-
when "initialize"
|
33
|
-
# Don't respond to initialize request, but start type checking.
|
34
|
-
project.targets.each do |target|
|
35
|
-
enqueue_target(target: target, timestamp: Time.now)
|
36
|
-
end
|
37
|
-
when "textDocument/didChange"
|
38
|
-
update_source(request)
|
39
|
-
validate_signature_if_required(request)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def validate_signature(target, timestamp:)
|
44
|
-
Steep.logger.info "Starting signature validation: #{target.name} (#{timestamp})..."
|
45
|
-
|
46
|
-
target.type_check(target_sources: [], validate_signatures: true)
|
47
|
-
|
48
|
-
Steep.logger.info "Finished signature validation: #{target.name} (#{timestamp})"
|
49
|
-
|
50
|
-
diagnostics = case status = target.status
|
51
|
-
when Project::Target::SignatureSyntaxErrorStatus
|
52
|
-
target.signature_files.each.with_object({}) do |(path, file), hash|
|
53
|
-
if file.status.is_a?(Project::SignatureFile::ParseErrorStatus)
|
54
|
-
location = case error = file.status.error
|
55
|
-
when RBS::Parser::SyntaxError
|
56
|
-
if error.error_value.is_a?(String)
|
57
|
-
buf = RBS::Buffer.new(name: path, content: file.content)
|
58
|
-
RBS::Location.new(buffer: buf, start_pos: buf.content.size, end_pos: buf.content.size)
|
59
|
-
else
|
60
|
-
error.error_value.location
|
61
|
-
end
|
62
|
-
when RBS::Parser::SemanticsError
|
63
|
-
error.location
|
64
|
-
else
|
65
|
-
raise
|
66
|
-
end
|
67
|
-
|
68
|
-
hash[path] =
|
69
|
-
[
|
70
|
-
LSP::Interface::Diagnostic.new(
|
71
|
-
message: file.status.error.message,
|
72
|
-
severity: LSP::Constant::DiagnosticSeverity::ERROR,
|
73
|
-
range: LSP::Interface::Range.new(
|
74
|
-
start: LSP::Interface::Position.new(
|
75
|
-
line: location.start_line - 1,
|
76
|
-
character: location.start_column,
|
77
|
-
),
|
78
|
-
end: LSP::Interface::Position.new(
|
79
|
-
line: location.end_line - 1,
|
80
|
-
character: location.end_column
|
81
|
-
)
|
82
|
-
)
|
83
|
-
)
|
84
|
-
]
|
85
|
-
else
|
86
|
-
hash[path] = []
|
87
|
-
end
|
88
|
-
end
|
89
|
-
when Project::Target::SignatureValidationErrorStatus
|
90
|
-
error_hash = status.errors.group_by {|error| error.location.buffer.name }
|
91
|
-
|
92
|
-
target.signature_files.each_key.with_object({}) do |path, hash|
|
93
|
-
errors = error_hash[path] || []
|
94
|
-
hash[path] = errors.map do |error|
|
95
|
-
LSP::Interface::Diagnostic.new(
|
96
|
-
message: StringIO.new.tap {|io| error.puts(io) }.string.split(/\t/, 2).last,
|
97
|
-
severity: LSP::Constant::DiagnosticSeverity::ERROR,
|
98
|
-
range: LSP::Interface::Range.new(
|
99
|
-
start: LSP::Interface::Position.new(
|
100
|
-
line: error.location.start_line - 1,
|
101
|
-
character: error.location.start_column,
|
102
|
-
),
|
103
|
-
end: LSP::Interface::Position.new(
|
104
|
-
line: error.location.end_line - 1,
|
105
|
-
character: error.location.end_column
|
106
|
-
)
|
107
|
-
)
|
108
|
-
)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
when Project::Target::TypeCheckStatus
|
112
|
-
target.signature_files.each_key.with_object({}) do |path, hash|
|
113
|
-
hash[path] = []
|
114
|
-
end
|
115
|
-
when Project::Target::SignatureOtherErrorStatus
|
116
|
-
Steep.log_error status.error
|
117
|
-
{}
|
118
|
-
else
|
119
|
-
Steep.logger.info "Unexpected target status: #{status.class}"
|
120
|
-
{}
|
121
|
-
end
|
122
|
-
|
123
|
-
diagnostics.each do |path, diags|
|
124
|
-
writer.write(
|
125
|
-
method: :"textDocument/publishDiagnostics",
|
126
|
-
params: LSP::Interface::PublishDiagnosticsParams.new(
|
127
|
-
uri: URI.parse(project.absolute_path(path).to_s).tap {|uri| uri.scheme = "file"},
|
128
|
-
diagnostics: diags
|
129
|
-
)
|
130
|
-
)
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
def active_job?(target, timestamp)
|
135
|
-
if last_target_validated_at[target] == timestamp
|
136
|
-
sleep 0.1
|
137
|
-
last_target_validated_at[target] == timestamp
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
def handle_job(job)
|
142
|
-
target, timestamp = job
|
143
|
-
|
144
|
-
if active_job?(target, timestamp)
|
145
|
-
validate_signature(target, timestamp: timestamp)
|
146
|
-
else
|
147
|
-
Steep.logger.info "Skipping signature validation: #{target.name}, queued timestamp=#{timestamp}, latest timestamp=#{last_target_validated_at[target]}"
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
152
|
-
end
|