steep 0.37.0 → 0.42.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (221) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +1 -1
  3. data/CHANGELOG.md +34 -0
  4. data/Rakefile +5 -2
  5. data/bin/output_rebaseline.rb +34 -0
  6. data/bin/output_test.rb +53 -0
  7. data/lib/steep.rb +95 -14
  8. data/lib/steep/ast/types/bot.rb +1 -1
  9. data/lib/steep/ast/types/class.rb +4 -0
  10. data/lib/steep/ast/types/factory.rb +10 -0
  11. data/lib/steep/ast/types/logic.rb +16 -3
  12. data/lib/steep/ast/types/top.rb +1 -1
  13. data/lib/steep/cli.rb +31 -7
  14. data/lib/steep/diagnostic/helper.rb +17 -0
  15. data/lib/steep/diagnostic/lsp_formatter.rb +16 -0
  16. data/lib/steep/diagnostic/ruby.rb +619 -0
  17. data/lib/steep/diagnostic/signature.rb +357 -0
  18. data/lib/steep/drivers/annotations.rb +19 -28
  19. data/lib/steep/drivers/check.rb +182 -60
  20. data/lib/steep/drivers/diagnostic_printer.rb +99 -0
  21. data/lib/steep/drivers/langserver.rb +3 -8
  22. data/lib/steep/drivers/print_project.rb +10 -9
  23. data/lib/steep/drivers/stats.rb +124 -32
  24. data/lib/steep/drivers/trace_printer.rb +5 -1
  25. data/lib/steep/drivers/utils/jobs_count.rb +9 -0
  26. data/lib/steep/drivers/validate.rb +31 -13
  27. data/lib/steep/drivers/watch.rb +69 -48
  28. data/lib/steep/drivers/worker.rb +16 -8
  29. data/lib/steep/expectations.rb +159 -0
  30. data/lib/steep/index/rbs_index.rb +334 -0
  31. data/lib/steep/index/signature_symbol_provider.rb +162 -0
  32. data/lib/steep/index/source_index.rb +100 -0
  33. data/lib/steep/project.rb +0 -30
  34. data/lib/steep/project/dsl.rb +5 -3
  35. data/lib/steep/project/options.rb +4 -4
  36. data/lib/steep/project/pattern.rb +56 -0
  37. data/lib/steep/project/target.rb +9 -214
  38. data/lib/steep/range_extension.rb +29 -0
  39. data/lib/steep/server/base_worker.rb +43 -7
  40. data/lib/steep/server/change_buffer.rb +63 -0
  41. data/lib/steep/server/interaction_worker.rb +73 -56
  42. data/lib/steep/server/master.rb +245 -109
  43. data/lib/steep/server/type_check_worker.rb +122 -0
  44. data/lib/steep/server/worker_process.rb +17 -15
  45. data/lib/steep/{project → services}/completion_provider.rb +3 -3
  46. data/lib/steep/services/content_change.rb +61 -0
  47. data/lib/steep/services/file_loader.rb +48 -0
  48. data/lib/steep/{project → services}/hover_content.rb +14 -16
  49. data/lib/steep/services/path_assignment.rb +29 -0
  50. data/lib/steep/services/signature_service.rb +369 -0
  51. data/lib/steep/services/stats_calculator.rb +69 -0
  52. data/lib/steep/services/type_check_service.rb +342 -0
  53. data/lib/steep/signature/validator.rb +174 -32
  54. data/lib/steep/subtyping/check.rb +248 -47
  55. data/lib/steep/subtyping/constraints.rb +2 -2
  56. data/lib/steep/type_construction.rb +565 -295
  57. data/lib/steep/type_inference/constant_env.rb +5 -1
  58. data/lib/steep/type_inference/local_variable_type_env.rb +26 -12
  59. data/lib/steep/type_inference/logic_type_interpreter.rb +99 -26
  60. data/lib/steep/type_inference/type_env.rb +43 -17
  61. data/lib/steep/typing.rb +8 -2
  62. data/lib/steep/version.rb +1 -1
  63. data/smoke/alias/a.rb +0 -3
  64. data/smoke/alias/b.rb +0 -1
  65. data/smoke/alias/c.rb +0 -2
  66. data/smoke/alias/test_expectations.yml +96 -0
  67. data/smoke/and/a.rb +0 -3
  68. data/smoke/and/test_expectations.yml +31 -0
  69. data/smoke/array/a.rb +0 -3
  70. data/smoke/array/b.rb +0 -2
  71. data/smoke/array/c.rb +0 -1
  72. data/smoke/array/test_expectations.yml +103 -0
  73. data/smoke/block/a.rb +0 -2
  74. data/smoke/block/b.rb +0 -2
  75. data/smoke/block/d.rb +0 -4
  76. data/smoke/block/test_expectations.yml +125 -0
  77. data/smoke/case/a.rb +0 -3
  78. data/smoke/case/test_expectations.yml +47 -0
  79. data/smoke/class/a.rb +0 -3
  80. data/smoke/class/c.rb +0 -1
  81. data/smoke/class/f.rb +0 -1
  82. data/smoke/class/g.rb +0 -2
  83. data/smoke/class/i.rb +0 -2
  84. data/smoke/class/test_expectations.yml +120 -0
  85. data/smoke/const/a.rb +0 -3
  86. data/smoke/const/b.rb +7 -0
  87. data/smoke/const/b.rbs +5 -0
  88. data/smoke/const/test_expectations.yml +139 -0
  89. data/smoke/diagnostics-rbs-duplicated/Steepfile +5 -0
  90. data/smoke/diagnostics-rbs-duplicated/a.rbs +5 -0
  91. data/smoke/diagnostics-rbs-duplicated/test_expectations.yml +13 -0
  92. data/smoke/diagnostics-rbs/Steepfile +8 -0
  93. data/smoke/diagnostics-rbs/duplicated-method-definition.rbs +20 -0
  94. data/smoke/diagnostics-rbs/generic-parameter-mismatch.rbs +7 -0
  95. data/smoke/diagnostics-rbs/invalid-method-overload.rbs +3 -0
  96. data/smoke/diagnostics-rbs/invalid-type-application.rbs +7 -0
  97. data/smoke/diagnostics-rbs/invalid_variance_annotation.rbs +3 -0
  98. data/smoke/diagnostics-rbs/recursive-alias.rbs +5 -0
  99. data/smoke/diagnostics-rbs/recursive-class.rbs +8 -0
  100. data/smoke/diagnostics-rbs/superclass-mismatch.rbs +7 -0
  101. data/smoke/diagnostics-rbs/test_expectations.yml +231 -0
  102. data/smoke/diagnostics-rbs/unknown-method-alias.rbs +3 -0
  103. data/smoke/diagnostics-rbs/unknown-type-name-2.rbs +5 -0
  104. data/smoke/diagnostics-rbs/unknown-type-name.rbs +13 -0
  105. data/smoke/diagnostics/Steepfile +5 -0
  106. data/smoke/diagnostics/a.rbs +26 -0
  107. data/smoke/diagnostics/argument_type_mismatch.rb +1 -0
  108. data/smoke/diagnostics/block_body_type_mismatch.rb +1 -0
  109. data/smoke/diagnostics/block_type_mismatch.rb +3 -0
  110. data/smoke/diagnostics/break_type_mismatch.rb +1 -0
  111. data/smoke/diagnostics/else_on_exhaustive_case.rb +12 -0
  112. data/smoke/diagnostics/incompatible_annotation.rb +6 -0
  113. data/smoke/diagnostics/incompatible_argument.rb +1 -0
  114. data/smoke/diagnostics/incompatible_assignment.rb +8 -0
  115. data/smoke/diagnostics/method_arity_mismatch.rb +11 -0
  116. data/smoke/diagnostics/method_body_type_mismatch.rb +6 -0
  117. data/smoke/diagnostics/method_definition_missing.rb +2 -0
  118. data/smoke/diagnostics/method_return_type_annotation_mismatch.rb +7 -0
  119. data/smoke/diagnostics/missing_keyword.rb +1 -0
  120. data/smoke/diagnostics/no_method.rb +1 -0
  121. data/smoke/diagnostics/required_block_missing.rb +1 -0
  122. data/smoke/diagnostics/return_type_mismatch.rb +6 -0
  123. data/smoke/diagnostics/test_expectations.yml +477 -0
  124. data/smoke/diagnostics/unexpected_block_given.rb +1 -0
  125. data/smoke/diagnostics/unexpected_dynamic_method.rb +3 -0
  126. data/smoke/diagnostics/unexpected_jump.rb +4 -0
  127. data/smoke/diagnostics/unexpected_jump_value.rb +3 -0
  128. data/smoke/diagnostics/unexpected_keyword.rb +1 -0
  129. data/smoke/diagnostics/unexpected_splat.rb +1 -0
  130. data/smoke/diagnostics/unexpected_yield.rb +6 -0
  131. data/smoke/diagnostics/unknown_constant_assigned.rb +7 -0
  132. data/smoke/diagnostics/unresolved_overloading.rb +1 -0
  133. data/smoke/diagnostics/unsatisfiable_constraint.rb +7 -0
  134. data/smoke/diagnostics/unsupported_syntax.rb +2 -0
  135. data/smoke/dstr/a.rb +0 -1
  136. data/smoke/dstr/test_expectations.yml +13 -0
  137. data/smoke/ensure/a.rb +0 -4
  138. data/smoke/ensure/test_expectations.yml +62 -0
  139. data/smoke/enumerator/a.rb +0 -6
  140. data/smoke/enumerator/b.rb +0 -3
  141. data/smoke/enumerator/test_expectations.yml +135 -0
  142. data/smoke/extension/a.rb +0 -1
  143. data/smoke/extension/b.rb +0 -2
  144. data/smoke/extension/c.rb +0 -1
  145. data/smoke/extension/f.rb +2 -0
  146. data/smoke/extension/f.rbs +3 -0
  147. data/smoke/extension/test_expectations.yml +73 -0
  148. data/smoke/hash/b.rb +0 -1
  149. data/smoke/hash/c.rb +0 -3
  150. data/smoke/hash/d.rb +0 -1
  151. data/smoke/hash/e.rb +0 -1
  152. data/smoke/hash/test_expectations.yml +81 -0
  153. data/smoke/hello/hello.rb +0 -2
  154. data/smoke/hello/test_expectations.yml +25 -0
  155. data/smoke/if/a.rb +0 -2
  156. data/smoke/if/test_expectations.yml +34 -0
  157. data/smoke/implements/a.rb +0 -2
  158. data/smoke/implements/test_expectations.yml +23 -0
  159. data/smoke/initialize/test_expectations.yml +1 -0
  160. data/smoke/integer/a.rb +0 -7
  161. data/smoke/integer/test_expectations.yml +101 -0
  162. data/smoke/interface/a.rb +0 -2
  163. data/smoke/interface/test_expectations.yml +23 -0
  164. data/smoke/kwbegin/a.rb +0 -1
  165. data/smoke/kwbegin/test_expectations.yml +17 -0
  166. data/smoke/lambda/a.rb +1 -4
  167. data/smoke/lambda/test_expectations.yml +39 -0
  168. data/smoke/literal/a.rb +0 -5
  169. data/smoke/literal/b.rb +0 -2
  170. data/smoke/literal/test_expectations.yml +106 -0
  171. data/smoke/map/test_expectations.yml +1 -0
  172. data/smoke/method/a.rb +0 -5
  173. data/smoke/method/b.rb +0 -1
  174. data/smoke/method/test_expectations.yml +90 -0
  175. data/smoke/module/a.rb +0 -2
  176. data/smoke/module/b.rb +0 -2
  177. data/smoke/module/c.rb +0 -1
  178. data/smoke/module/d.rb +0 -1
  179. data/smoke/module/f.rb +0 -2
  180. data/smoke/module/test_expectations.yml +75 -0
  181. data/smoke/regexp/a.rb +0 -38
  182. data/smoke/regexp/b.rb +0 -26
  183. data/smoke/regexp/test_expectations.yml +615 -0
  184. data/smoke/regression/set_divide.rb +0 -4
  185. data/smoke/regression/test_expectations.yml +43 -0
  186. data/smoke/rescue/a.rb +0 -5
  187. data/smoke/rescue/test_expectations.yml +79 -0
  188. data/smoke/self/a.rb +0 -2
  189. data/smoke/self/test_expectations.yml +23 -0
  190. data/smoke/skip/skip.rb +0 -2
  191. data/smoke/skip/test_expectations.yml +23 -0
  192. data/smoke/stdout/test_expectations.yml +1 -0
  193. data/smoke/super/a.rb +0 -4
  194. data/smoke/super/test_expectations.yml +79 -0
  195. data/smoke/toplevel/a.rb +0 -1
  196. data/smoke/toplevel/test_expectations.yml +15 -0
  197. data/smoke/tsort/Steepfile +2 -0
  198. data/smoke/tsort/a.rb +0 -3
  199. data/smoke/tsort/test_expectations.yml +63 -0
  200. data/smoke/type_case/a.rb +0 -4
  201. data/smoke/type_case/test_expectations.yml +48 -0
  202. data/smoke/unexpected/Steepfile +5 -0
  203. data/smoke/unexpected/test_expectations.yml +25 -0
  204. data/smoke/unexpected/unexpected.rb +1 -0
  205. data/smoke/unexpected/unexpected.rbs +3 -0
  206. data/smoke/yield/a.rb +0 -3
  207. data/smoke/yield/b.rb +6 -0
  208. data/smoke/yield/test_expectations.yml +68 -0
  209. data/steep.gemspec +4 -3
  210. metadata +144 -29
  211. data/bin/smoke_runner.rb +0 -139
  212. data/lib/steep/drivers/signature_error_printer.rb +0 -25
  213. data/lib/steep/errors.rb +0 -565
  214. data/lib/steep/project/file_loader.rb +0 -68
  215. data/lib/steep/project/signature_file.rb +0 -33
  216. data/lib/steep/project/source_file.rb +0 -129
  217. data/lib/steep/server/code_worker.rb +0 -137
  218. data/lib/steep/server/signature_worker.rb +0 -152
  219. data/lib/steep/server/utils.rb +0 -69
  220. data/lib/steep/signature/errors.rb +0 -82
  221. 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