steep 1.10.0 → 2.0.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.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +84 -1
  3. data/CLAUDE.md +114 -0
  4. data/README.md +1 -1
  5. data/Rakefile +15 -3
  6. data/Steepfile +13 -13
  7. data/lib/steep/annotation_parser.rb +5 -1
  8. data/lib/steep/annotations_helper.rb +12 -2
  9. data/lib/steep/ast/node/type_application.rb +22 -16
  10. data/lib/steep/ast/node/type_assertion.rb +7 -4
  11. data/lib/steep/ast/types/factory.rb +3 -2
  12. data/lib/steep/cli.rb +246 -2
  13. data/lib/steep/daemon/configuration.rb +19 -0
  14. data/lib/steep/daemon/server.rb +476 -0
  15. data/lib/steep/daemon.rb +201 -0
  16. data/lib/steep/diagnostic/ruby.rb +50 -8
  17. data/lib/steep/diagnostic/signature.rb +31 -8
  18. data/lib/steep/drivers/check.rb +301 -140
  19. data/lib/steep/drivers/print_project.rb +9 -10
  20. data/lib/steep/drivers/query.rb +102 -0
  21. data/lib/steep/drivers/start_server.rb +19 -0
  22. data/lib/steep/drivers/stop_server.rb +20 -0
  23. data/lib/steep/drivers/watch.rb +2 -2
  24. data/lib/steep/index/rbs_index.rb +38 -13
  25. data/lib/steep/index/signature_symbol_provider.rb +24 -3
  26. data/lib/steep/interface/builder.rb +48 -15
  27. data/lib/steep/interface/shape.rb +13 -5
  28. data/lib/steep/locator.rb +377 -0
  29. data/lib/steep/project/dsl.rb +26 -5
  30. data/lib/steep/project/group.rb +8 -2
  31. data/lib/steep/project/target.rb +16 -2
  32. data/lib/steep/project.rb +21 -2
  33. data/lib/steep/server/base_worker.rb +2 -2
  34. data/lib/steep/server/change_buffer.rb +2 -1
  35. data/lib/steep/server/custom_methods.rb +12 -0
  36. data/lib/steep/server/inline_source_change_detector.rb +94 -0
  37. data/lib/steep/server/interaction_worker.rb +51 -74
  38. data/lib/steep/server/lsp_formatter.rb +48 -12
  39. data/lib/steep/server/master.rb +100 -18
  40. data/lib/steep/server/target_group_files.rb +124 -151
  41. data/lib/steep/server/type_check_controller.rb +276 -123
  42. data/lib/steep/server/type_check_worker.rb +104 -3
  43. data/lib/steep/services/completion_provider/rbs.rb +74 -0
  44. data/lib/steep/services/completion_provider/ruby.rb +652 -0
  45. data/lib/steep/services/completion_provider/type_name.rb +243 -0
  46. data/lib/steep/services/completion_provider.rb +39 -662
  47. data/lib/steep/services/content_change.rb +14 -1
  48. data/lib/steep/services/file_loader.rb +4 -2
  49. data/lib/steep/services/goto_service.rb +271 -68
  50. data/lib/steep/services/hover_provider/content.rb +67 -0
  51. data/lib/steep/services/hover_provider/rbs.rb +8 -9
  52. data/lib/steep/services/hover_provider/ruby.rb +123 -64
  53. data/lib/steep/services/hover_provider/singleton_methods.rb +4 -0
  54. data/lib/steep/services/signature_service.rb +129 -54
  55. data/lib/steep/services/type_check_service.rb +72 -27
  56. data/lib/steep/signature/validator.rb +30 -18
  57. data/lib/steep/source/ignore_ranges.rb +14 -4
  58. data/lib/steep/source.rb +16 -2
  59. data/lib/steep/tagged_logging.rb +39 -0
  60. data/lib/steep/type_construction.rb +94 -21
  61. data/lib/steep/type_inference/block_params.rb +7 -7
  62. data/lib/steep/type_inference/context.rb +4 -2
  63. data/lib/steep/type_inference/logic_type_interpreter.rb +21 -3
  64. data/lib/steep/type_inference/method_call.rb +4 -0
  65. data/lib/steep/type_inference/type_env.rb +1 -1
  66. data/lib/steep/typing.rb +0 -2
  67. data/lib/steep/version.rb +1 -1
  68. data/lib/steep.rb +42 -32
  69. data/manual/ruby-diagnostics.md +67 -0
  70. data/sample/Steepfile +1 -0
  71. data/sample/lib/conference.rb +1 -0
  72. data/sample/lib/deprecated.rb +6 -0
  73. data/sample/lib/inline.rb +43 -0
  74. data/sample/sig/generics.rbs +3 -0
  75. data/steep.gemspec +4 -5
  76. metadata +26 -26
  77. data/lib/steep/services/type_name_completion.rb +0 -236
@@ -1,57 +1,8 @@
1
1
  module Steep
2
2
  module Services
3
3
  module HoverProvider
4
- class Ruby
5
- TypeContent = _ = Struct.new(:node, :type, :location, keyword_init: true)
6
- VariableContent = _ = Struct.new(:node, :name, :type, :location, keyword_init: true)
7
- TypeAssertionContent = _ = Struct.new(:node, :original_type, :asserted_type, :location, keyword_init: true)
8
- MethodCallContent = _ = Struct.new(:node, :method_call, :location, keyword_init: true)
9
- DefinitionContent = _ = Struct.new(:node, :method_name, :method_type, :definition, :location, keyword_init: true)
10
- ConstantContent = _ = Struct.new(:location, :full_name, :type, :decl, keyword_init: true) do
11
- # @implements ConstantContent
12
-
13
- def comments
14
- case
15
- when decl = class_decl
16
- decl.decls.map {|d| d.decl.comment }
17
- when decl = class_alias
18
- [decl.decl.comment]
19
- when decl = constant_decl
20
- [decl.decl.comment]
21
- else
22
- raise
23
- end.compact
24
- end
25
-
26
- def class_decl
27
- case decl
28
- when ::RBS::Environment::ClassEntry, ::RBS::Environment::ModuleEntry
29
- decl
30
- end
31
- end
32
-
33
- def class_alias
34
- case decl
35
- when ::RBS::Environment::ClassAliasEntry, ::RBS::Environment::ModuleAliasEntry
36
- decl
37
- end
38
- end
39
-
40
- def constant_decl
41
- if decl.is_a?(::RBS::Environment::ConstantEntry)
42
- decl
43
- end
44
- end
45
-
46
- def constant?
47
- constant_decl ? true : false
48
- end
49
-
50
- def class_or_module?
51
- (class_decl || class_alias) ? true : false
52
- end
53
- end
54
4
 
5
+ class Ruby
55
6
  attr_reader :service
56
7
 
57
8
  def initialize(service:)
@@ -85,7 +36,10 @@ module Steep
85
36
  source = source.without_unrelated_defs(line: line, column: column)
86
37
  resolver = ::RBS::Resolver::ConstantResolver.new(builder: subtyping.factory.definition_builder)
87
38
  pos = source.buffer.loc_to_pos([line, column])
88
- Services::TypeCheckService.type_check(source: source, subtyping: subtyping, constant_resolver: resolver, cursor: pos)
39
+ [
40
+ Services::TypeCheckService.type_check(source: source, subtyping: subtyping, constant_resolver: resolver, cursor: pos),
41
+ subtyping
42
+ ]
89
43
  rescue
90
44
  nil
91
45
  end
@@ -112,10 +66,15 @@ module Steep
112
66
 
113
67
  def content_for(target:, path:, line:, column:)
114
68
  file = service.source_files[path] or return
115
- typing = typecheck(target, path: path, content: file.content, line: line, column: column) or return
116
- node, *parents = typing.source.find_nodes(line: line, column: column)
69
+ (typing, subtyping = typecheck(target, path: path, content: file.content, line: line, column: column)) or return
70
+ locator = Locator::Ruby.new(typing.source)
71
+ result = locator.find(line, column)
72
+
73
+ case result
74
+ when Locator::NodeResult
75
+ node = result.node
76
+ parents = result.parents
117
77
 
118
- if node && parents
119
78
  case node.type
120
79
  when :lvar
121
80
  var_name = node.children[0]
@@ -130,7 +89,7 @@ module Steep
130
89
  )
131
90
 
132
91
  when :lvasgn
133
- var_name, rhs = node.children
92
+ var_name, _rhs = node.children
134
93
  context = typing.cursor_context.context or raise
135
94
  type = context.type_env[var_name] || typing.type_of(node: node)
136
95
 
@@ -197,15 +156,6 @@ module Steep
197
156
  decl: entry
198
157
  )
199
158
  end
200
- when :assertion
201
- original_node, _ = node.children
202
-
203
- original_type = typing.type_of(node: original_node)
204
- asserted_type = typing.type_of(node: node)
205
-
206
- if original_type != asserted_type
207
- return TypeAssertionContent.new(node: node, original_type: original_type, asserted_type: asserted_type, location: node.location.expression)
208
- end
209
159
  end
210
160
 
211
161
  TypeContent.new(
@@ -213,8 +163,117 @@ module Steep
213
163
  type: typing.type_of(node: node),
214
164
  location: node.location.expression
215
165
  )
166
+
167
+ when Locator::TypeAssertionResult
168
+ context = typing.cursor_context.context or raise
169
+ pos = typing.source.buffer.loc_to_pos([line, column])
170
+
171
+ nesting = context.module_context.nesting
172
+ type_vars = context.variable_context.type_params.map { _1.name }
173
+
174
+ if (name, location = result.locate_type_name(pos, nesting, subtyping, type_vars))
175
+ if content = type_name_content(subtyping.factory.env, name, location)
176
+ return content
177
+ end
178
+ end
179
+
180
+ assertion_node = result.node.node
181
+ original_node = assertion_node.children[0] or raise
182
+
183
+ original_type = typing.type_of(node: original_node)
184
+ asserted_type = typing.type_of(node: assertion_node)
185
+
186
+ if original_type != asserted_type
187
+ TypeAssertionContent.new(
188
+ node: assertion_node,
189
+ original_type: original_type,
190
+ asserted_type: asserted_type,
191
+ location: assertion_node.location.expression
192
+ )
193
+ else
194
+ TypeContent.new(
195
+ node: assertion_node,
196
+ type: typing.type_of(node: assertion_node),
197
+ location: assertion_node.location.expression
198
+ )
199
+ end
200
+
201
+ when Locator::TypeApplicationResult
202
+ begin
203
+ context = typing.cursor_context.context or raise
204
+ pos = typing.source.buffer.loc_to_pos([line, column])
205
+
206
+ nesting = context.module_context.nesting
207
+ type_vars = context.variable_context.type_params.map { _1.name }
208
+
209
+ if (name, location = result.locate_type_name(pos, nesting, subtyping, type_vars))
210
+ if content = type_name_content(subtyping.factory.env, name, location)
211
+ return content
212
+ end
213
+ end
214
+
215
+ nil
216
+ rescue ::RBS::ParsingError
217
+ return nil
218
+ end
216
219
  end
217
220
  end
221
+
222
+ def type_name_content(environment, type_name, location)
223
+ case
224
+ when type_name.class?
225
+ if entry = environment.module_class_entry(type_name)
226
+ decl = case entry
227
+ when ::RBS::Environment::ModuleEntry, ::RBS::Environment::ClassEntry
228
+ entry.primary_decl
229
+ else
230
+ entry.decl
231
+ end
232
+
233
+ ClassTypeContent.new(
234
+ location: location,
235
+ decl: decl
236
+ )
237
+ end
238
+ when type_name.interface?
239
+ if entry = environment.interface_decls.fetch(type_name, nil)
240
+ InterfaceTypeContent.new(
241
+ location: location,
242
+ decl: entry.decl
243
+ )
244
+ end
245
+ when type_name.alias?
246
+ if entry = environment.type_alias_decls.fetch(type_name, nil)
247
+ TypeAliasContent.new(
248
+ location: location,
249
+ decl: entry.decl
250
+ )
251
+ end
252
+ end
253
+ end
254
+
255
+ def content_for_inline(target:, path:, line:, column:)
256
+ signature = service.signature_services.fetch(target.name)
257
+ source = signature.latest_env.sources.find do
258
+ if _1.is_a?(::RBS::Source::Ruby)
259
+ _1.buffer.name == path
260
+ end
261
+ end
262
+
263
+ return unless source.is_a?(::RBS::Source::Ruby)
264
+
265
+ locator = Locator::Inline.new(source)
266
+ result = locator.find(line, column)
267
+
268
+ case result
269
+ when Locator::InlineTypeNameResult
270
+ return type_name_content(signature.latest_env, result.type_name, result.location)
271
+ else
272
+ Steep.logger.fatal { { result: result.class }.inspect }
273
+ end
274
+
275
+ nil
276
+ end
218
277
  end
219
278
  end
220
279
  end
@@ -6,6 +6,10 @@ module Steep
6
6
  project = service.project
7
7
 
8
8
  case
9
+ when target = project.target_for_inline_source_path(path)
10
+ service = Ruby.new(service: service)
11
+ service.content_for_inline(target: target, path: path, line: line, column: column) ||
12
+ service.content_for(target: target, path: path, line: line, column: column)
9
13
  when target = project.target_for_source_path(path)
10
14
  Ruby.new(service: service).content_for(target: target, path: path, line: line, column: column)
11
15
  when target = project.target_for_signature_path(path)
@@ -76,19 +76,36 @@ module Steep
76
76
  end
77
77
  end
78
78
 
79
- FileStatus = _ = Struct.new(:path, :content, :signature, keyword_init: true)
79
+ RBSFileStatus = _ = Struct.new(:path, :content, :source, keyword_init: true)
80
80
 
81
81
  attr_reader :implicitly_returns_nil
82
82
 
83
- def initialize(env:, implicitly_returns_nil:)
84
- builder = RBS::DefinitionBuilder.new(env: env)
85
- @status = LoadedStatus.new(builder: builder, files: {}, implicitly_returns_nil: implicitly_returns_nil)
83
+ def initialize(status:, implicitly_returns_nil:)
84
+ @status = status
86
85
  @implicitly_returns_nil = implicitly_returns_nil
87
86
  end
88
87
 
89
88
  def self.load_from(loader, implicitly_returns_nil:)
90
89
  env = RBS::Environment.from_loader(loader).resolve_type_names
91
- new(env: env, implicitly_returns_nil: implicitly_returns_nil)
90
+ builder = RBS::DefinitionBuilder.new(env: env)
91
+ status = LoadedStatus.new(builder: builder, files: {}, implicitly_returns_nil: implicitly_returns_nil)
92
+ new(status: status, implicitly_returns_nil: implicitly_returns_nil)
93
+ rescue RBS::ParsingError => exn
94
+ # When library RBS contains syntax error, load only *core* libraries and set `SyntaxErrorStatus`.
95
+ core_loader = RBS::EnvironmentLoader.new(core_root: loader.core_root)
96
+ core_env = RBS::Environment.from_loader(core_loader).resolve_type_names
97
+ status = SyntaxErrorStatus.new(
98
+ files: {},
99
+ changed_paths: Set[],
100
+ diagnostics: [Diagnostic::Signature.from_rbs_error(exn, factory: _ = nil)],
101
+ last_builder: RBS::DefinitionBuilder.new(env: core_env)
102
+ )
103
+ service = new(status: status, implicitly_returns_nil: implicitly_returns_nil)
104
+ # Add the failed library path to env_rbs_paths so it's recognized as a library path by TypeCheckService
105
+ if exn.location
106
+ service.env_rbs_paths << Pathname(exn.location.buffer.name)
107
+ end
108
+ service
92
109
  end
93
110
 
94
111
  def env_rbs_paths
@@ -154,51 +171,101 @@ module Steep
154
171
  def apply_changes(files, changes)
155
172
  Steep.logger.tagged "#apply_changes" do
156
173
  Steep.measure2 "Applying change" do |sampler|
157
- changes.each.with_object({}) do |pair, update| # $ Hash[Pathname, FileStatus]
158
- path, cs = pair
174
+ changes.each.with_object({}) do |(path, cs), update| # $ Hash[Pathname, file_status]
159
175
  sampler.sample "#{path}" do
160
- old_text = files[path]&.content
161
- content = cs.inject(old_text || "") {|text, change| change.apply_to(text) }
162
-
163
- content ||= "" # It was not clear why `content` can be `nil`, but it happens with `master_test`.
164
- buffer = RBS::Buffer.new(name: path, content: content)
165
-
166
- update[path] =
167
- begin
168
- FileStatus.new(path: path, content: content, signature: RBS::Parser.parse_signature(buffer))
169
- rescue ArgumentError => exn
170
- error = Diagnostic::Signature::UnexpectedError.new(
171
- message: exn.message,
172
- location: RBS::Location.new(buffer: buffer, start_pos: 0, end_pos: content.size)
173
- )
174
- FileStatus.new(path: path, content: content, signature: error)
175
- rescue RBS::ParsingError => exn
176
- FileStatus.new(path: path, content: content, signature: exn)
176
+ old_file = files.fetch(path, nil)
177
+
178
+ case old_file
179
+ when RBSFileStatus
180
+ old_text = old_file.content
181
+ new_file = load_rbs_file(path, old_text, cs)
182
+ when RBS::Source::Ruby
183
+ old_text = old_file.buffer.content
184
+ new_file = load_ruby_file(path, old_text, cs)
185
+ when nil
186
+ # New file: Detect based on the file name
187
+ if path.extname == ".rbs"
188
+ # RBS File
189
+ new_file = load_rbs_file(path, "", cs)
190
+ else
191
+ # Ruby File
192
+ new_file = load_ruby_file(path, "", cs)
177
193
  end
194
+ end
195
+
196
+ update[path] = new_file
178
197
  end
179
198
  end
180
199
  end
181
200
  end
182
201
  end
183
202
 
203
+ def load_rbs_file(path, old_text, changes)
204
+ content = changes.reduce(old_text) do |text, change| # $ String
205
+ change.apply_to(text)
206
+ end
207
+
208
+ buffer = RBS::Buffer.new(name: path, content: content)
209
+ source =
210
+ begin
211
+ _, dirs, decls = RBS::Parser.parse_signature(buffer)
212
+ RBS::Source::RBS.new(buffer, dirs, decls)
213
+ rescue ArgumentError => exn
214
+ Diagnostic::Signature::UnexpectedError.new(
215
+ message: exn.message,
216
+ location: RBS::Location.new(buffer: buffer, start_pos: 0, end_pos: content.size)
217
+ )
218
+ rescue RBS::ParsingError => exn
219
+ exn
220
+ end
221
+
222
+ RBSFileStatus.new(path: path, content: content, source: source)
223
+ end
224
+
225
+ def load_ruby_file(path, old_text, changes)
226
+ content = changes.reduce(old_text) do |text, change| # $ String
227
+ change.apply_to(text)
228
+ end
229
+
230
+ buffer = RBS::Buffer.new(name: path, content: content)
231
+ prism = Prism.parse(buffer.content, filepath: path.to_s)
232
+ result = RBS::InlineParser.parse(buffer, prism)
233
+ RBS::Source::Ruby.new(buffer, prism, result.declarations, result.diagnostics)
234
+ end
235
+
236
+ def error_file?(file)
237
+ case file
238
+ when RBSFileStatus
239
+ !file.source.is_a?(RBS::Source::RBS)
240
+ when RBS::Source::Ruby
241
+ false
242
+ end
243
+ end
244
+
184
245
  def update(changes)
185
246
  Steep.logger.tagged "#update" do
186
247
  updates = apply_changes(files, changes)
187
248
  paths = Set.new(updates.each_key)
188
249
  paths.merge(pending_changed_paths)
189
250
 
190
- if updates.each_value.any? {|file| !file.signature.is_a?(Array) }
251
+ if updates.each_value.any? {|file| error_file?(file) }
191
252
  diagnostics = [] #: Array[Diagnostic::Signature::Base]
192
253
 
193
254
  updates.each_value do |file|
194
- unless file.signature.is_a?(Array)
195
- diagnostic = if file.signature.is_a?(Diagnostic::Signature::Base)
196
- file.signature
197
- else
198
- # factory is not used here because the error is a syntax error.
199
- Diagnostic::Signature.from_rbs_error(file.signature, factory: _ = nil)
200
- end
201
- diagnostics << diagnostic
255
+ if error_file?(file)
256
+ if file.is_a?(RBSFileStatus)
257
+ diagnostic =
258
+ case file.source
259
+ when Diagnostic::Signature::Base
260
+ file.source
261
+ when RBS::ParsingError
262
+ Diagnostic::Signature.from_rbs_error(file.source, factory: _ = nil)
263
+ else
264
+ raise "file (#{file.path}) must be an error"
265
+ end
266
+
267
+ diagnostics << diagnostic
268
+ end
202
269
  end
203
270
  end
204
271
 
@@ -235,30 +302,26 @@ module Steep
235
302
  def update_env(updated_files, paths:)
236
303
  Steep.logger.tagged "#update_env" do
237
304
  errors = [] #: Array[RBS::BaseError]
238
- new_decls = Set[].compare_by_identity #: Set[RBS::AST::Declarations::t]
305
+ new_decls = Set[].compare_by_identity #: Set[RBS::AST::Declarations::t | RBS::AST::Ruby::Declarations::t]
239
306
 
240
307
  env =
241
308
  Steep.measure "Deleting out of date decls" do
242
- bufs = latest_env.buffers.select {|buf| paths.include?(buf.name) }
243
- latest_env.unload(Set.new(bufs))
309
+ latest_env.unload(paths)
244
310
  end
245
311
 
246
312
  Steep.measure "Loading new decls" do
247
313
  updated_files.each_value do |content|
248
- case content.signature
249
- when RBS::ParsingError
250
- errors << content.signature
251
- when Diagnostic::Signature::UnexpectedError
252
- return [content.signature]
253
- else
254
- begin
255
- buffer, dirs, decls = content.signature
256
- env.add_signature(buffer: buffer, directives: dirs, decls: decls)
257
- new_decls.merge(decls)
258
- rescue RBS::LoadingError => exn
259
- errors << exn
260
- end
314
+ case content
315
+ when RBSFileStatus
316
+ (source = content.source).is_a?(RBS::Source::RBS) or raise "Cannot be an error"
317
+ env.add_source(source)
318
+ new_decls.merge(source.declarations)
319
+ when RBS::Source::Ruby
320
+ env.add_source(content)
321
+ new_decls.merge(content.declarations)
261
322
  end
323
+ rescue RBS::LoadingError => exn
324
+ errors << exn
262
325
  end
263
326
  end
264
327
 
@@ -339,13 +402,25 @@ module Steep
339
402
  end
340
403
 
341
404
  def type_names(paths:, env:)
342
- env.declarations.each.with_object(Set[]) do |decl, set|
343
- if decl.location
344
- if paths.include?(Pathname(decl.location.buffer.name))
345
- type_name_from_decl(decl, set: set)
346
- end
405
+
406
+ Hash.new {}
407
+ set = Set[] #: Set[RBS::TypeName]
408
+
409
+ env.each_rbs_source do |source|
410
+ next unless paths.include?(source.buffer.name)
411
+ source.each_type_name do |type_name|
412
+ set << type_name
347
413
  end
348
414
  end
415
+
416
+ env.each_ruby_source do |source|
417
+ next unless paths.include?(source.buffer.name)
418
+ source.each_type_name do |type_name|
419
+ set << type_name
420
+ end
421
+ end
422
+
423
+ set
349
424
  end
350
425
 
351
426
  def const_decls(paths:, env:)
@@ -53,17 +53,40 @@ module Steep
53
53
  errors
54
54
  when typing && ignores
55
55
  errors = [] #: Array[Diagnostic::Ruby::Base]
56
+ error_lines = [] #: Array[Integer]
56
57
 
57
- errors.concat(
58
- typing.errors.delete_if do |diagnostic|
59
- case diagnostic.location
60
- when ::Parser::Source::Range
61
- ignores.ignore?(diagnostic.location.first_line, diagnostic.location.last_line, diagnostic.diagnostic_code)
62
- when RBS::Location
63
- ignores.ignore?(diagnostic.location.start_line, diagnostic.location.end_line, diagnostic.diagnostic_code)
58
+ used_comments = Set[].compare_by_identity #: Set[Source::IgnoreRanges::ignore]
59
+
60
+ typing.errors.each do |diagnostic|
61
+ case diagnostic.location
62
+ when ::Parser::Source::Range
63
+ error_lines |= (diagnostic.location.first_line..diagnostic.location.last_line).to_a
64
+ if ignore = ignores.ignore?(diagnostic.location.first_line, diagnostic.location.last_line, diagnostic.diagnostic_code)
65
+ used_comments << ignore
66
+ next
67
+ end
68
+ when RBS::Location
69
+ if ignore = ignores.ignore?(diagnostic.location.start_line, diagnostic.location.end_line, diagnostic.diagnostic_code)
70
+ used_comments << ignore
71
+ next
64
72
  end
65
73
  end
66
- )
74
+
75
+ errors << diagnostic
76
+ end
77
+
78
+ ignores.each_ignore do |ignore|
79
+ next if used_comments.include?(ignore)
80
+
81
+ case ignore
82
+ when Array
83
+ location = RBS::Location.new(ignore[0].location.buffer, ignore[0].location.start_pos, ignore[1].location.end_pos)
84
+ else
85
+ location = ignore.location
86
+ end
87
+
88
+ errors << Diagnostic::Ruby::RedundantIgnoreComment.new(location: location)
89
+ end
67
90
 
68
91
  ignores.error_ignores.each do |ignore|
69
92
  errors << Diagnostic::Ruby::InvalidIgnoreComment.new(comment: ignore.comment)
@@ -119,10 +142,6 @@ module Steep
119
142
  signature_diagnostics
120
143
  end
121
144
 
122
- def has_diagnostics?
123
- each_diagnostics.count > 0
124
- end
125
-
126
145
  def diagnostics
127
146
  each_diagnostics.to_h
128
147
  end
@@ -156,7 +175,9 @@ module Steep
156
175
  Steep.measure "validation" do
157
176
  service = signature_services.fetch(target.name)
158
177
 
159
- raise "#{path} is not library nor signature of #{target.name}" unless target.possible_signature_file?(path) || service.env_rbs_paths.include?(path)
178
+ unless target.possible_signature_file?(path) || target.possible_inline_source_file?(path) || service.env_rbs_paths.include?(path)
179
+ raise "#{path} is not library nor signature of #{target.name}"
180
+ end
160
181
 
161
182
  case service.status
162
183
  when SignatureService::SyntaxErrorStatus
@@ -221,6 +242,14 @@ module Steep
221
242
  end
222
243
  end
223
244
 
245
+ source = service.status.files[path]
246
+ if source.is_a?(RBS::Source::Ruby)
247
+ source.diagnostics.each do |d|
248
+ diagnostic = Diagnostic::Signature::InlineDiagnostic.new(d)
249
+ diagnostics.push(diagnostic)
250
+ end
251
+ end
252
+
224
253
  signature_validation_diagnostics.fetch(target.name)[path] = diagnostics
225
254
  end
226
255
  end
@@ -240,6 +269,29 @@ module Steep
240
269
  source_files[path] = file
241
270
 
242
271
  file.diagnostics
272
+ else
273
+ # Signature loading failed. If the errors originate from library RBS files,
274
+ # they won't be reported by validate_signature (which filters by user file path).
275
+ # Report them on source files so the user knows type checking is broken. (#2176)
276
+ case signature_service.status
277
+ when SignatureService::SyntaxErrorStatus, SignatureService::AncestorErrorStatus
278
+ library_errors = signature_service.status.diagnostics.select do |diag|
279
+ diag_path = diag.location && Pathname(diag.location.buffer.name)
280
+ diag_path &&
281
+ signature_service.env_rbs_paths.include?(diag_path) &&
282
+ !signature_service.status.files.key?(diag_path)
283
+ end
284
+
285
+ unless library_errors.empty?
286
+ text = source_files.fetch(path).content
287
+ buffer = RBS::Buffer.new(name: path, content: text)
288
+ location = RBS::Location.new(buffer: buffer, start_pos: 0, end_pos: text.size)
289
+
290
+ library_errors.map do |error|
291
+ Diagnostic::Ruby::LibraryRBSError.new(error: error, location: location)
292
+ end
293
+ end
294
+ end
243
295
  end
244
296
  end
245
297
  end
@@ -249,8 +301,9 @@ module Steep
249
301
  Steep.logger.tagged "#update_signature" do
250
302
  signature_targets = {} #: Hash[Pathname, Project::Target]
251
303
  changes.each do |path, changes|
252
- target = project.targets.find { _1.possible_signature_file?(path) } or next
253
- signature_targets[path] = target
304
+ if target = project.target_for_signature_path(path) || project.target_for_inline_source_path(path)
305
+ signature_targets[path] = target
306
+ end
254
307
  end
255
308
 
256
309
  project.targets.each do |target|
@@ -368,7 +421,10 @@ module Steep
368
421
  end
369
422
 
370
423
  def source_file?(path)
371
- source_files.key?(path) || (project.target_for_source_path(path) ? true : false)
424
+ return true if source_files.key?(path)
425
+ return true if project.target_for_source_path(path)
426
+ return true if project.target_for_inline_source_path(path)
427
+ false
372
428
  end
373
429
 
374
430
  def signature_file?(path)
@@ -378,17 +434,6 @@ module Steep
378
434
  targets.keys
379
435
  end
380
436
  end
381
-
382
- def app_signature_file?(path)
383
- target_names = signature_services.select {|_, sig| sig.files.key?(path) }.keys
384
- unless target_names.empty?
385
- target_names
386
- end
387
- end
388
-
389
- def lib_signature_file?(path)
390
- signature_services.each_value.any? {|sig| sig.env_rbs_paths.include?(path) }
391
- end
392
437
  end
393
438
  end
394
439
  end