steep-relaxed 1.9.4.3 → 1.10.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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -0
  3. data/Rakefile +23 -0
  4. data/Steepfile +5 -2
  5. data/lib/steep/annotations_helper.rb +43 -0
  6. data/lib/steep/ast/ignore.rb +3 -4
  7. data/lib/steep/cli.rb +102 -23
  8. data/lib/steep/diagnostic/lsp_formatter.rb +17 -3
  9. data/lib/steep/diagnostic/ruby.rb +33 -0
  10. data/lib/steep/diagnostic/signature.rb +48 -0
  11. data/lib/steep/drivers/check.rb +3 -1
  12. data/lib/steep/drivers/diagnostic_printer/base_formatter.rb +19 -0
  13. data/lib/steep/drivers/diagnostic_printer/code_formatter.rb +95 -0
  14. data/lib/steep/drivers/diagnostic_printer/github_actions_formatter.rb +44 -0
  15. data/lib/steep/drivers/diagnostic_printer.rb +9 -86
  16. data/lib/steep/drivers/langserver.rb +4 -1
  17. data/lib/steep/drivers/worker.rb +2 -0
  18. data/lib/steep/interface/builder.rb +2 -2
  19. data/lib/steep/server/custom_methods.rb +12 -0
  20. data/lib/steep/server/interaction_worker.rb +33 -6
  21. data/lib/steep/server/master.rb +103 -7
  22. data/lib/steep/server/type_check_worker.rb +48 -2
  23. data/lib/steep/server/worker_process.rb +31 -20
  24. data/lib/steep/services/completion_provider.rb +12 -2
  25. data/lib/steep/services/signature_service.rb +2 -2
  26. data/lib/steep/signature/validator.rb +56 -4
  27. data/lib/steep/source.rb +2 -2
  28. data/lib/steep/subtyping/check.rb +5 -6
  29. data/lib/steep/type_construction.rb +171 -38
  30. data/lib/steep/type_inference/context.rb +4 -0
  31. data/lib/steep/type_inference/logic_type_interpreter.rb +77 -15
  32. data/lib/steep/type_inference/method_call.rb +5 -3
  33. data/lib/steep/version.rb +1 -1
  34. data/lib/steep.rb +10 -0
  35. data/manual/ruby-diagnostics.md +46 -0
  36. data/sample/Steepfile +0 -2
  37. data/sample/lib/deprecated.rb +7 -0
  38. data/sample/sig/deprecated.rbs +16 -0
  39. data/steep-relaxed.gemspec +5 -3
  40. metadata +15 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a2375aaccd85c970192b3cdaec8db80d183c631673e6d6d9b12e77f1d8d9105
4
- data.tar.gz: 559f9c8e17a214295d9908c4d56e7e514dc4f37725afab3cbfc551d91455d68e
3
+ metadata.gz: 0dc09711ef505e121141424fa2ef9fd4c667ca78c4b3388ee1ec127f554b7d01
4
+ data.tar.gz: 269da170bef99f779ebcc6e9941e701edae2cea137fa2ba4be7ebe0ea67cfdc4
5
5
  SHA512:
6
- metadata.gz: 3fcace45e6c372a8e445440295db3bec8336ae71235237110c0f37b64aff8db8094410c7849e146fdd890841ffee54416dd7996f2d237abec29a5b5bea143dd5
7
- data.tar.gz: 7bb970fbaf54732f2bfdd64a6e495e86a07742001e9fb255c39f5386be9a2c0309c5c19f57cd6345487bacc905b85efea8ba0f411bbecf33ffcc95ef5b11b04c
6
+ metadata.gz: e8e473956dfbc61dee8b3c11eea08435172e6417415a0e95d15525b475f194f2d0eef3f0d5b874ad65f62b7412f048a3fc6a84f48cbc2ff4af339c45b802c9da
7
+ data.tar.gz: a593f998234e8bf4387dd6764dc715ac983803ce168eeb02b8f5b55c1872dc3600fa81a892f8e1673065be7dade8ef60ba4b95387096f9017ed698cc7a07c395
data/CHANGELOG.md CHANGED
@@ -1,5 +1,65 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 3.10.0 (2025-03-18)
4
+
5
+ Nothing changed since `1.10.0.pre.3`.
6
+
7
+ ## 1.10.0.pre.3 (2025-03-14)
8
+
9
+ ### Type checker core
10
+
11
+ * Fix block type checking ([#1534](https://github.com/soutaro/steep/pull/1534))
12
+
13
+ ## 1.10.0.pre.2 (2025-03-14)
14
+
15
+ ### Type checker core
16
+
17
+ * Fix `self` type handling at block-self-hint ([#1531](https://github.com/soutaro/steep/pull/1531))
18
+ * Delay type variable resolution on method call with blocks ([#1530](https://github.com/soutaro/steep/pull/1530))
19
+ * Fix type checking block calls on `untyped` receiver ([#1528](https://github.com/soutaro/steep/pull/1528))
20
+ * Catch the message of VariableDuplicationError ([#1521](https://github.com/soutaro/steep/pull/1521))
21
+
22
+ ## 1.10.0.pre.1 (2025-03-11)
23
+
24
+ ### Type checker core
25
+
26
+ * Skip type checking forwarded argument if the method is undeclared ([#1519](https://github.com/soutaro/steep/pull/1519))
27
+ * Dedup error message on ancestors check ([#1515](https://github.com/soutaro/steep/pull/1515))
28
+ * Add deprecation validation on RBS ([#1518](https://github.com/soutaro/steep/pull/1518))
29
+ * Fix union self type refinement based on method call ([#1517](https://github.com/soutaro/steep/pull/1517))
30
+ * Check deprecations in Ruby code ([#1513](https://github.com/soutaro/steep/pull/1513))
31
+ * Support `InvalidTypeApplication` on `#update_env` ([#1507](https://github.com/soutaro/steep/pull/1507))
32
+ * Fix Source.parse crashes with selector-less sendish node ([#1433](https://github.com/soutaro/steep/pull/1433))
33
+ * Fix steep:ignore does not work with CR/LF ([#1406](https://github.com/soutaro/steep/pull/1406))
34
+ * Fix self union type checking ([#1467](https://github.com/soutaro/steep/pull/1467))
35
+ * Type narrowing union types via return type of method call ([#1497](https://github.com/soutaro/steep/pull/1497))
36
+ * Fix `else` clause of `case-when` syntax typing ([#1475](https://github.com/soutaro/steep/pull/1475))
37
+ * Strict record and tuple subtyping ([#1460](https://github.com/soutaro/steep/pull/1460))
38
+ * Allow to annotate "self" on toplevel ([#1455](https://github.com/soutaro/steep/pull/1455))
39
+ * Let annotations in `when` clause without `cond` expression work ([#1459](https://github.com/soutaro/steep/pull/1459))
40
+ * Fix `RuntimeError` on `when` clause with assertion ([#1458](https://github.com/soutaro/steep/pull/1458))
41
+ * Add diagnostic for method definition without types ([#1457](https://github.com/soutaro/steep/pull/1457))
42
+
43
+ ### Commandline tool
44
+
45
+ * Implement GitHub formatter ([#1516](https://github.com/soutaro/steep/pull/1516))
46
+ * Refine `--help` messages ([#1463](https://github.com/soutaro/steep/pull/1463))
47
+
48
+ ### Language server
49
+
50
+ * Reforking steep ([#1492](https://github.com/soutaro/steep/pull/1492))
51
+ * Set up file watcher for groups ([#1485](https://github.com/soutaro/steep/pull/1485))
52
+
53
+ ### Miscellaneous
54
+
55
+ * Fix a runtime warning for ambiguous `/`; wrap regexp in parentheses or add a space after `/` operator ([#1438](https://github.com/soutaro/steep/pull/1438))
56
+ * Fix a runtime warning for ambiguous `*` has been interpreted as an argument prefix ([#1439](https://github.com/soutaro/steep/pull/1439))
57
+ * Fix a runtime warning for key :cursor is duplicated and overwritten on line 99 ([#1440](https://github.com/soutaro/steep/pull/1440))
58
+ * Use release version of RBS ([#1511](https://github.com/soutaro/steep/pull/1511))
59
+ * Prepare for rbs-3.9 ([#1510](https://github.com/soutaro/steep/pull/1510))
60
+ * Fix typos ([#1506](https://github.com/soutaro/steep/pull/1506))
61
+ * Fix CI ([#1486](https://github.com/soutaro/steep/pull/1486))
62
+
3
63
  ## 1.9.3 (2024-12-26)
4
64
 
5
65
  ### Miscellaneous
data/Rakefile CHANGED
@@ -225,3 +225,26 @@ NOTES
225
225
  end
226
226
  end
227
227
  end
228
+
229
+ namespace :rbs do
230
+ task :watch do
231
+ require "listen"
232
+ listener = Listen.to('test') do |modified, added, removed|
233
+ paths = (modified + added).map do
234
+ Pathname(_1).relative_path_from(Pathname.pwd)
235
+ end
236
+ sh "rbs-inline", "--opt-out", "--output=sig", *paths.map(&:to_s)
237
+ end
238
+ listener.start
239
+ begin
240
+ sleep
241
+ rescue Interrupt
242
+ listener.stop
243
+ end
244
+ end
245
+
246
+ task :generate do
247
+ sh "rbs-inline --opt-out --output=sig test"
248
+ sh "rbs-inline --opt-out --output=tmp/rbs-inline bin/generate-diagnostics-docs.rb"
249
+ end
250
+ end
data/Steepfile CHANGED
@@ -13,7 +13,6 @@ if (source = rbs_dep&.source).is_a?(Bundler::Source::Path)
13
13
  end
14
14
  else
15
15
  FileUtils.rm_f(tmp_rbs_dir)
16
- library "rbs"
17
16
  end
18
17
 
19
18
  target :app do
@@ -64,5 +63,9 @@ target :bin do
64
63
  check "bin/generate-diagnostics-docs.rb"
65
64
  signature "tmp/rbs-inline/bin"
66
65
 
67
- library "rbs"
66
+ if tmp_rbs_dir.directory?
67
+ signature tmp_rbs_dir.to_s
68
+ else
69
+ library "rbs"
70
+ end
68
71
  end
@@ -0,0 +1,43 @@
1
+ module Steep
2
+ module AnnotationsHelper
3
+ module_function
4
+
5
+ def deprecated_annotation?(annotations)
6
+ annotations.reverse_each do |annotation|
7
+ if match = annotation.string.match(/deprecated(:\s*(?<message>.+))?/)
8
+ return [annotation, match[:message]]
9
+ end
10
+ if match = annotation.string.match(/steep:deprecated(:\s*(?<message>.+))?/)
11
+ return [annotation, match[:message]]
12
+ end
13
+ end
14
+
15
+ nil
16
+ end
17
+
18
+ def deprecated_type_name?(type_name, env)
19
+ annotations =
20
+ case
21
+ when type_name.class?
22
+ case
23
+ when decl = env.class_decls.fetch(type_name, nil)
24
+ decl.decls.flat_map { _1.decl.annotations }
25
+ when decl = env.class_alias_decls.fetch(type_name, nil)
26
+ decl.decl.annotations
27
+ end
28
+ when type_name.interface?
29
+ if decl = env.interface_decls.fetch(type_name, nil)
30
+ decl.decl.annotations
31
+ end
32
+ when type_name.alias?
33
+ if decl = env.type_alias_decls.fetch(type_name, nil)
34
+ decl.decl.annotations
35
+ end
36
+ end
37
+
38
+ if annotations
39
+ deprecated_annotation?(annotations)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -93,15 +93,14 @@ module Steep
93
93
  def self.parse(comment, buffer)
94
94
  return unless comment.inline?
95
95
 
96
- comment_location = RBS::Location.new(buffer, comment.loc.expression.begin_pos, comment.loc.expression.end_pos)
96
+ begin_pos = buffer.loc_to_pos([comment.loc.line, comment.loc.column])
97
+ end_pos = buffer.loc_to_pos([comment.loc.last_line, comment.loc.last_column])
98
+ comment_location = RBS::Location.new(buffer, begin_pos, end_pos)
97
99
  scanner = BufferScanner.new(comment_location)
98
100
 
99
101
  scanner.scan(/#/)
100
102
  scanner.skip(/\s*/)
101
103
 
102
- begin_pos = comment.location.expression.begin_pos
103
- end_pos = comment.location.expression.end_pos
104
-
105
104
  case
106
105
  when loc = scanner.scan(/steep:ignore:start\b/)
107
106
  scanner.skip(/\s*/)
data/lib/steep/cli.rb CHANGED
@@ -24,12 +24,13 @@ module Steep
24
24
  opts.banner = <<~USAGE
25
25
  Usage: steep [options]
26
26
 
27
- available commands: #{CLI.available_commands.join(', ')}
27
+ Available commands:
28
+ #{CLI.available_commands.join(', ')}
28
29
 
29
30
  Options:
30
31
  USAGE
31
32
 
32
- opts.on("--version") do
33
+ opts.on("--version", "Print Steep version") do
33
34
  process_version
34
35
  exit 0
35
36
  end
@@ -60,6 +61,10 @@ module Steep
60
61
  __send__(:"process_#{command}")
61
62
  end
62
63
 
64
+ def handle_steepfile_option(opts, command)
65
+ opts.on("--steepfile=PATH", "Specify path to Steepfile") {|path| command.steepfile = Pathname(path) }
66
+ end
67
+
63
68
  def handle_logging_options(opts)
64
69
  opts.on("--log-level=LEVEL", "Specify log level: debug, info, warn, error, fatal") do |level|
65
70
  Steep.logger.level = level
@@ -101,10 +106,16 @@ module Steep
101
106
  def process_init
102
107
  Drivers::Init.new(stdout: stdout, stderr: stderr).tap do |command|
103
108
  OptionParser.new do |opts|
104
- opts.banner = "Usage: steep init [options]"
109
+ opts.banner = <<BANNER
110
+ Usage: steep init [options]
111
+
112
+ Description:
113
+ Generates a Steepfile at specified path.
105
114
 
106
- opts.on("--steepfile=PATH") {|path| command.steepfile = Pathname(path) }
107
- opts.on("--force") { command.force_write = true }
115
+ Options:
116
+ BANNER
117
+ handle_steepfile_option(opts, command)
118
+ opts.on("--force", "Overwrite the Steepfile if it already exists") { command.force_write = true }
108
119
 
109
120
  handle_logging_options opts
110
121
  end.parse!(argv)
@@ -114,9 +125,19 @@ module Steep
114
125
  def process_check
115
126
  Drivers::Check.new(stdout: stdout, stderr: stderr).tap do |command|
116
127
  OptionParser.new do |opts|
117
- opts.banner = "Usage: steep check [options] [sources]"
128
+ opts.banner = <<BANNER
129
+ Usage: steep check [options] [paths]
130
+
131
+ Description:
132
+ Type check the program.
118
133
 
119
- opts.on("--steepfile=PATH") {|path| command.steepfile = Pathname(path) }
134
+ If paths are specified, it type checks and validates the files at the given path.
135
+ Otherwise, it type checks and validates all files in the project or the groups if specified.
136
+
137
+ Options:
138
+ BANNER
139
+
140
+ handle_steepfile_option(opts, command)
120
141
  opts.on("--with-expectations[=PATH]", "Type check with expectations saved in PATH (or steep_expectations.yml)") do |path|
121
142
  command.with_expectations_path = Pathname(path || "steep_expectations.yml")
122
143
  end
@@ -166,22 +187,33 @@ module Steep
166
187
  end
167
188
  end
168
189
 
190
+ opts.on("--format=FORMATTER", ["code", "github"], "Output formatters (default: code, options: code,github)") do |formatter|
191
+ command.formatter = formatter
192
+ end
193
+
169
194
  handle_jobs_option command.jobs_option, opts
170
195
  handle_logging_options opts
171
196
  end.parse!(argv)
172
197
 
173
198
  setup_jobs_for_ci(command.jobs_option)
174
199
 
175
- command.command_line_patterns.push *argv
200
+ command.command_line_patterns.push(*argv)
176
201
  end.run
177
202
  end
178
203
 
179
204
  def process_checkfile
180
205
  Drivers::Checkfile.new(stdout: stdout, stderr: stderr).tap do |command|
181
206
  OptionParser.new do |opts|
182
- opts.banner = "Usage: steep checkfile [options] [files]"
207
+ opts.banner = <<BANNER
208
+ Usage: steep checkfile [options] [files]
209
+
210
+ Description:
211
+ Deprecated: Use `steep check` instead.
212
+
213
+ Options:
214
+ BANNER
183
215
 
184
- opts.on("--steepfile=PATH") {|path| command.steepfile = Pathname(path) }
216
+ handle_steepfile_option(opts, command)
185
217
  opts.on("--all-rbs", "Type check all RBS files") { command.all_rbs = true }
186
218
  opts.on("--all-ruby", "Type check all Ruby files") { command.all_ruby = true }
187
219
  opts.on("--stdin", "Read files to type check from stdin") do
@@ -197,16 +229,23 @@ module Steep
197
229
 
198
230
  setup_jobs_for_ci(command.jobs_option)
199
231
 
200
- command.command_line_args.push *argv
232
+ command.command_line_args.push(*argv)
201
233
  end.run
202
234
  end
203
235
 
204
236
  def process_stats
205
237
  Drivers::Stats.new(stdout: stdout, stderr: stderr).tap do |command|
206
238
  OptionParser.new do |opts|
207
- opts.banner = "Usage: steep stats [options] [sources]"
239
+ opts.banner = <<BANNER
240
+ Usage: steep stats [options] [sources]
208
241
 
209
- opts.on("--steepfile=PATH") {|path| command.steepfile = Pathname(path) }
242
+ Description:
243
+ Displays statistics about the typing of method calls.
244
+
245
+ Options:
246
+ BANNER
247
+
248
+ handle_steepfile_option(opts, command)
210
249
  opts.on("--format=FORMAT", "Specify output format: csv, table") {|format| command.format = format }
211
250
  handle_jobs_option command.jobs_option, opts
212
251
  handle_logging_options opts
@@ -214,7 +253,7 @@ module Steep
214
253
 
215
254
  setup_jobs_for_ci(command.jobs_option)
216
255
 
217
- command.command_line_patterns.push *argv
256
+ command.command_line_patterns.push(*argv)
218
257
  end.run
219
258
  end
220
259
 
@@ -226,19 +265,33 @@ module Steep
226
265
  def process_annotations
227
266
  Drivers::Annotations.new(stdout: stdout, stderr: stderr).tap do |command|
228
267
  OptionParser.new do |opts|
229
- opts.banner = "Usage: steep annotations [options] [sources]"
268
+ opts.banner = <<BANNER
269
+ Usage: steep annotations [options] [sources]
270
+
271
+ Description:
272
+ Prints the type annotations in the Ruby code.
273
+
274
+ Options:
275
+ BANNER
230
276
  handle_logging_options opts
231
277
  end.parse!(argv)
232
278
 
233
- command.command_line_patterns.push *argv
279
+ command.command_line_patterns.push(*argv)
234
280
  end.run
235
281
  end
236
282
 
237
283
  def process_project
238
284
  Drivers::PrintProject.new(stdout: stdout, stderr: stderr).tap do |command|
239
285
  OptionParser.new do |opts|
240
- opts.banner = "Usage: steep project [options]"
241
- opts.on("--steepfile=PATH") {|path| command.steepfile = Pathname(path) }
286
+ opts.banner = <<BANNER
287
+ Usage: steep project [options]
288
+
289
+ Description:
290
+ Prints the project configuration.
291
+
292
+ Options:
293
+ BANNER
294
+ handle_steepfile_option(opts, command)
242
295
  opts.on("--[no-]print-files", "Print files") {|v|
243
296
  command.print_files = v ? true : false
244
297
  }
@@ -250,7 +303,15 @@ module Steep
250
303
  def process_watch
251
304
  Drivers::Watch.new(stdout: stdout, stderr: stderr).tap do |command|
252
305
  OptionParser.new do |opts|
253
- opts.banner = "Usage: steep watch [options] [dirs]"
306
+ opts.banner = <<BANNER
307
+ Usage: steep watch [options] [dirs]
308
+
309
+ Description:
310
+ Monitors file changes and automatically type checks updated files.
311
+ Using LSP is recommended for better performance and more features.
312
+
313
+ Options:
314
+ BANNER
254
315
  opts.on("--severity-level=LEVEL", /^error|warning|information|hint$/, "Specify the minimum diagnostic severity to be recognized as an error (defaults: warning): error, warning, information, or hint") do |level|
255
316
  # @type var level: String
256
317
  command.severity_level = _ = level.to_sym
@@ -269,7 +330,16 @@ module Steep
269
330
  def process_langserver
270
331
  Drivers::Langserver.new(stdout: stdout, stderr: stderr, stdin: stdin).tap do |command|
271
332
  OptionParser.new do |opts|
272
- opts.on("--steepfile=PATH") {|path| command.steepfile = Pathname(path) }
333
+ opts.banner = <<BANNER
334
+ Usage: steep langserver [options]
335
+
336
+ Description:
337
+ Starts language server, which is assumed to be invoked from language client.
338
+
339
+ Options:
340
+ BANNER
341
+ handle_steepfile_option(opts, command)
342
+ opts.on("--refork") { command.refork = true }
273
343
  handle_jobs_option command.jobs_option, opts
274
344
  handle_logging_options opts
275
345
  end.parse!(argv)
@@ -300,8 +370,8 @@ module Steep
300
370
  opts.banner = <<BANNER
301
371
  Usage: steep binstub [options]
302
372
 
303
- Generate a binstub to execute Steep with setting up Bundler and rbenv/rvm.
304
- Use the executable for LSP integration setup.
373
+ Description:
374
+ Generate a binstub which set up ruby executables and bundlers.
305
375
 
306
376
  Options:
307
377
  BANNER
@@ -372,6 +442,15 @@ TEMPLATE
372
442
  end
373
443
 
374
444
  def process_version
445
+ OptionParser.new do |opts|
446
+ opts.banner = <<BANNER
447
+ Usage: steep version [options]
448
+
449
+ Description:
450
+ Prints Steep version.
451
+ BANNER
452
+ end.parse!(argv)
453
+
375
454
  stdout.puts Steep::VERSION
376
455
  0
377
456
  end
@@ -384,7 +463,7 @@ TEMPLATE
384
463
 
385
464
  opts.on("--interaction") { command.worker_type = :interaction }
386
465
  opts.on("--typecheck") { command.worker_type = :typecheck }
387
- opts.on("--steepfile=PATH") {|path| command.steepfile = Pathname(path) }
466
+ handle_steepfile_option(opts, command)
388
467
  opts.on("--name=NAME") {|name| command.worker_name = name }
389
468
  opts.on("--delay-shutdown") { command.delay_shutdown = true }
390
469
  opts.on("--max-index=COUNT") {|count| command.max_index = Integer(count) }
@@ -49,13 +49,27 @@ module Steep
49
49
  } #: LSP::Interface::CodeDescription::json
50
50
  end
51
51
 
52
- {
52
+ tags = [] #: Array[LSP::Constant::DiagnosticTag::t]
53
+
54
+ case diagnostic
55
+ when Ruby::DeprecatedReference
56
+ tags << LSP::Constant::DiagnosticTag::DEPRECATED
57
+ when Signature::DeprecatedTypeName
58
+ tags << LSP::Constant::DiagnosticTag::DEPRECATED
59
+ severity = LSP::Constant::DiagnosticSeverity::WARNING
60
+ end
61
+
62
+ json = {
53
63
  message: diagnostic.full_message,
54
64
  code: diagnostic.diagnostic_code,
55
65
  severity: severity,
56
66
  range: range,
57
- codeDescription: description
58
- }
67
+ codeDescription: description,
68
+ } #: LSP::Interface::Diagnostic::json
69
+
70
+ json[:tags] = tags unless tags.empty?
71
+
72
+ json
59
73
  end
60
74
  end
61
75
 
@@ -1013,6 +1013,36 @@ module Steep
1013
1013
  end
1014
1014
  end
1015
1015
 
1016
+ class DeprecatedReference < Base
1017
+ attr_reader :message
1018
+
1019
+ def initialize(node:, location:, message:)
1020
+ super(node: node, location: location)
1021
+ @message = message
1022
+ end
1023
+
1024
+ def header_line
1025
+ header =
1026
+ case node&.type
1027
+ when :send, :csend, :block, :numblock
1028
+ "The method is deprecated"
1029
+ when :const, :casgn
1030
+ "The constant is deprecated"
1031
+ when :gvar, :gvasgn
1032
+ "The global variable is deprecated"
1033
+ else
1034
+ raise "Unexpected node: #{node}"
1035
+ end
1036
+
1037
+ if message
1038
+ header = +header
1039
+ header.concat(": ", message)
1040
+ end
1041
+
1042
+ header
1043
+ end
1044
+ end
1045
+
1016
1046
  ALL = ObjectSpace.each_object(Class).with_object([]) do |klass, array|
1017
1047
  if klass < Base
1018
1048
  array << klass
@@ -1033,6 +1063,7 @@ module Steep
1033
1063
  BlockBodyTypeMismatch => :warning,
1034
1064
  BlockTypeMismatch => :warning,
1035
1065
  BreakTypeMismatch => :hint,
1066
+ DeprecatedReference => :warning,
1036
1067
  DifferentMethodParameterKind => :hint,
1037
1068
  FallbackAny => :hint,
1038
1069
  FalseAssertion => :hint,
@@ -1095,6 +1126,7 @@ module Steep
1095
1126
  BlockBodyTypeMismatch => :error,
1096
1127
  BlockTypeMismatch => :error,
1097
1128
  BreakTypeMismatch => :error,
1129
+ DeprecatedReference => :warning,
1098
1130
  DifferentMethodParameterKind => :error,
1099
1131
  FallbackAny => :warning,
1100
1132
  FalseAssertion => :error,
@@ -1157,6 +1189,7 @@ module Steep
1157
1189
  BlockBodyTypeMismatch => :information,
1158
1190
  BlockTypeMismatch => :information,
1159
1191
  BreakTypeMismatch => :hint,
1192
+ DeprecatedReference => :warning,
1160
1193
  DifferentMethodParameterKind => nil,
1161
1194
  FallbackAny => nil,
1162
1195
  FalseAssertion => nil,
@@ -273,6 +273,30 @@ module Steep
273
273
  end
274
274
  end
275
275
 
276
+ class VariableDuplicationError < Base
277
+ attr_reader :type_name
278
+ attr_reader :variable_name
279
+ attr_reader :location
280
+
281
+ def initialize(type_name:, variable_name:, location:)
282
+ @type_name = type_name
283
+ @variable_name = variable_name
284
+ @location = location
285
+ end
286
+ end
287
+
288
+ class InstanceVariableDuplicationError < VariableDuplicationError
289
+ def header_line
290
+ "Duplicated instance variable name `#{variable_name}` in `#{type_name}`"
291
+ end
292
+ end
293
+
294
+ class ClassInstanceVariableDuplicationError < VariableDuplicationError
295
+ def header_line
296
+ "Duplicated class instance variable name `#{variable_name}` in `#{type_name}`"
297
+ end
298
+ end
299
+
276
300
  class ClassVariableDuplicationError < Base
277
301
  attr_reader :class_name
278
302
  attr_reader :other_class_name
@@ -460,6 +484,26 @@ module Steep
460
484
  end
461
485
  end
462
486
 
487
+ class DeprecatedTypeName < Base
488
+ attr_reader :type_name
489
+ attr_reader :message
490
+
491
+ def initialize(type_name, message, location:)
492
+ super(location: location)
493
+ @type_name = type_name
494
+ @message = message
495
+ end
496
+
497
+ def header_line
498
+ buffer = "Type `#{type_name}` is deprecated"
499
+ if message
500
+ buffer = +buffer
501
+ buffer << ": " << message
502
+ end
503
+ buffer
504
+ end
505
+ end
506
+
463
507
 
464
508
  def self.from_rbs_error(error, factory:)
465
509
  case error
@@ -561,6 +605,10 @@ module Steep
561
605
  Diagnostic::Signature::CyclicClassAliasDefinitionError.new(decl: error.alias_entry.decl)
562
606
  when RBS::TypeParamDefaultReferenceError
563
607
  Diagnostic::Signature::TypeParamDefaultReferenceError.new(error.type_param, location: error.location)
608
+ when RBS::InstanceVariableDuplicationError
609
+ Diagnostic::Signature::InstanceVariableDuplicationError.new(type_name: error.type_name, variable_name: error.variable_name, location: error.location)
610
+ when RBS::ClassInstanceVariableDuplicationError
611
+ Diagnostic::Signature::ClassInstanceVariableDuplicationError.new(type_name: error.type_name, variable_name: error.variable_name, location: error.location)
564
612
  else
565
613
  raise error
566
614
  end
@@ -16,6 +16,7 @@ module Steep
16
16
  attr_accessor :validate_group_signatures
17
17
  attr_accessor :validate_project_signatures
18
18
  attr_accessor :validate_library_signatures
19
+ attr_accessor :formatter
19
20
 
20
21
  include Utils::DriverHelper
21
22
 
@@ -30,6 +31,7 @@ module Steep
30
31
  @validate_group_signatures = true
31
32
  @validate_project_signatures = false
32
33
  @validate_library_signatures = false
34
+ @formatter = 'code'
33
35
  end
34
36
 
35
37
  def active_group?(group)
@@ -322,7 +324,7 @@ module Steep
322
324
  errors.each do |notification|
323
325
  path = Steep::PathHelper.to_pathname(notification[:uri]) or raise
324
326
  buffer = RBS::Buffer.new(name: project.relative_path(path), content: path.read)
325
- printer = DiagnosticPrinter.new(buffer: buffer, stdout: stdout)
327
+ printer = DiagnosticPrinter.new(buffer: buffer, stdout: stdout, formatter: formatter)
326
328
 
327
329
  notification[:diagnostics].each do |diag|
328
330
  printer.print(diag)
@@ -0,0 +1,19 @@
1
+ module Steep
2
+ module Drivers
3
+ class DiagnosticPrinter
4
+ class BaseFormatter
5
+ attr_reader :stdout
6
+ attr_reader :buffer
7
+
8
+ def initialize(stdout:, buffer:)
9
+ @stdout = stdout
10
+ @buffer = buffer
11
+ end
12
+
13
+ def path
14
+ Pathname(buffer.name)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end