steep 1.10.0.dev.1 → 1.10.0.pre.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c16f09c4999f414317b991d4c36ed5a5b51a2bf785d4f865ab9d698890050205
4
- data.tar.gz: f0a34b2b9c27703a87375e19a33055bed47ae3cc1ac8dae4fc846a931e054223
3
+ metadata.gz: 8dd3b63cbd4f85f05eb3643505e5bff6c4a6d6a1384959c3a0660153030e9449
4
+ data.tar.gz: b4afe185ffc5f9e0907e97b2f6a60638b887dcecb8b8b57992d8a652b63d94f7
5
5
  SHA512:
6
- metadata.gz: 24d081be36d7b8bd33ffe5786db94ff5e8a1b505c7dfa72e4076b3c7612d78e6c5978bebba991d792e9fc4b97bb0da3f004db308eade2b6ed6e63075ce3f9d9f
7
- data.tar.gz: 68069586284621fac6acbc1f279a3b866b26dcba5436334f16abb6db3466c2917a549c0f2b48bfc205eab74ee3d865124ab1a87f7b02403bb2a8b39d2c67a938
6
+ metadata.gz: 2060d786b8adf7d3876f3168f3fea26e999f6f4445cf8c109032e73ddcb99f2e4483706a4aedd1a61a9eb480cb9f42bcaea941f41d9ea9f843b84bcfa7cef3da
7
+ data.tar.gz: 875d9870e9c6082b428636b9e70c9e794008feb35b25a040aa608893e70bac5021f5cd4f0d9b44064f07851c134b17a148377906df0e90c547cf8359b01e9972
data/CHANGELOG.md CHANGED
@@ -1,5 +1,55 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 3.10.0.pre.2 (2025-03-14)
4
+
5
+ ### Type checker core
6
+
7
+ * Fix `self` type handling at block-self-hint ([#1531](https://github.com/soutaro/steep/pull/1531))
8
+ * Delay type variable resolution on method call with blocks ([#1530](https://github.com/soutaro/steep/pull/1530))
9
+ * Fix type checking block calls on `untyped` receiver ([#1528](https://github.com/soutaro/steep/pull/1528))
10
+ * Catch the message of VariableDuplicationError ([#1521](https://github.com/soutaro/steep/pull/1521))
11
+
12
+ ## 1.10.0.pre.1 (2025-03-11)
13
+
14
+ ### Type checker core
15
+
16
+ * Skip type checking forwarded argument if the method is undeclared ([#1519](https://github.com/soutaro/steep/pull/1519))
17
+ * Dedup error message on ancestors check ([#1515](https://github.com/soutaro/steep/pull/1515))
18
+ * Add deprecation validation on RBS ([#1518](https://github.com/soutaro/steep/pull/1518))
19
+ * Fix union self type refinement based on method call ([#1517](https://github.com/soutaro/steep/pull/1517))
20
+ * Check deprecations in Ruby code ([#1513](https://github.com/soutaro/steep/pull/1513))
21
+ * Support `InvalidTypeApplication` on `#update_env` ([#1507](https://github.com/soutaro/steep/pull/1507))
22
+ * Fix Source.parse crashes with selector-less sendish node ([#1433](https://github.com/soutaro/steep/pull/1433))
23
+ * Fix steep:ignore does not work with CR/LF ([#1406](https://github.com/soutaro/steep/pull/1406))
24
+ * Fix self union type checking ([#1467](https://github.com/soutaro/steep/pull/1467))
25
+ * Type narrowing union types via return type of method call ([#1497](https://github.com/soutaro/steep/pull/1497))
26
+ * Fix `else` clause of `case-when` syntax typing ([#1475](https://github.com/soutaro/steep/pull/1475))
27
+ * Strict record and tuple subtyping ([#1460](https://github.com/soutaro/steep/pull/1460))
28
+ * Allow to annotate "self" on toplevel ([#1455](https://github.com/soutaro/steep/pull/1455))
29
+ * Let annotations in `when` clause without `cond` expression work ([#1459](https://github.com/soutaro/steep/pull/1459))
30
+ * Fix `RuntimeError` on `when` clause with assertion ([#1458](https://github.com/soutaro/steep/pull/1458))
31
+ * Add diagnostic for method definition without types ([#1457](https://github.com/soutaro/steep/pull/1457))
32
+
33
+ ### Commandline tool
34
+
35
+ * Implement GitHub formatter ([#1516](https://github.com/soutaro/steep/pull/1516))
36
+ * Refine `--help` messages ([#1463](https://github.com/soutaro/steep/pull/1463))
37
+
38
+ ### Language server
39
+
40
+ * Reforking steep ([#1492](https://github.com/soutaro/steep/pull/1492))
41
+ * Set up file watcher for groups ([#1485](https://github.com/soutaro/steep/pull/1485))
42
+
43
+ ### Miscellaneous
44
+
45
+ * Fix a runtime warning for ambiguous `/`; wrap regexp in parentheses or add a space after `/` operator ([#1438](https://github.com/soutaro/steep/pull/1438))
46
+ * Fix a runtime warning for ambiguous `*` has been interpreted as an argument prefix ([#1439](https://github.com/soutaro/steep/pull/1439))
47
+ * Fix a runtime warning for key :cursor is duplicated and overwritten on line 99 ([#1440](https://github.com/soutaro/steep/pull/1440))
48
+ * Use release version of RBS ([#1511](https://github.com/soutaro/steep/pull/1511))
49
+ * Prepare for rbs-3.9 ([#1510](https://github.com/soutaro/steep/pull/1510))
50
+ * Fix typos ([#1506](https://github.com/soutaro/steep/pull/1506))
51
+ * Fix CI ([#1486](https://github.com/soutaro/steep/pull/1486))
52
+
3
53
  ## 1.9.3 (2024-12-26)
4
54
 
5
55
  ### Miscellaneous
data/Rakefile CHANGED
@@ -224,3 +224,26 @@ NOTES
224
224
  end
225
225
  end
226
226
  end
227
+
228
+ namespace :rbs do
229
+ task :watch do
230
+ require "listen"
231
+ listener = Listen.to('test') do |modified, added, removed|
232
+ paths = (modified + added).map do
233
+ Pathname(_1).relative_path_from(Pathname.pwd)
234
+ end
235
+ sh "rbs-inline", "--opt-out", "--output=sig", *paths.map(&:to_s)
236
+ end
237
+ listener.start
238
+ begin
239
+ sleep
240
+ rescue Interrupt
241
+ listener.stop
242
+ end
243
+ end
244
+
245
+ task :generate do
246
+ sh "rbs-inline --opt-out --output=sig test"
247
+ sh "rbs-inline --opt-out --output=tmp/rbs-inline bin/generate-diagnostics-docs.rb"
248
+ end
249
+ end
@@ -14,5 +14,30 @@ module Steep
14
14
 
15
15
  nil
16
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
17
42
  end
18
43
  end
data/lib/steep/cli.rb CHANGED
@@ -187,6 +187,10 @@ BANNER
187
187
  end
188
188
  end
189
189
 
190
+ opts.on("--format=FORMATTER", ["code", "github"], "Output formatters (default: code, options: code,github)") do |formatter|
191
+ command.formatter = formatter
192
+ end
193
+
190
194
  handle_jobs_option command.jobs_option, opts
191
195
  handle_logging_options opts
192
196
  end.parse!(argv)
@@ -54,6 +54,9 @@ module Steep
54
54
  case diagnostic
55
55
  when Ruby::DeprecatedReference
56
56
  tags << LSP::Constant::DiagnosticTag::DEPRECATED
57
+ when Signature::DeprecatedTypeName
58
+ tags << LSP::Constant::DiagnosticTag::DEPRECATED
59
+ severity = LSP::Constant::DiagnosticSeverity::WARNING
57
60
  end
58
61
 
59
62
  json = {
@@ -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
@@ -0,0 +1,95 @@
1
+ module Steep
2
+ module Drivers
3
+ class DiagnosticPrinter
4
+ class CodeFormatter < BaseFormatter
5
+ def print(diagnostic, prefix: "", source: true)
6
+ header, *rest = diagnostic[:message].split(/\n/)
7
+
8
+ stdout.puts "#{prefix}#{location(diagnostic)}: [#{severity_message(diagnostic[:severity])}] #{Rainbow(header).underline}"
9
+
10
+ unless rest.empty?
11
+ rest.each do |message|
12
+ stdout.puts "#{prefix}│ #{message}"
13
+ end
14
+ end
15
+
16
+ if diagnostic[:code]
17
+ stdout.puts "#{prefix}│" unless rest.empty?
18
+ stdout.puts "#{prefix}│ Diagnostic ID: #{diagnostic[:code]}"
19
+ end
20
+
21
+ stdout.puts "#{prefix}│"
22
+
23
+ if source
24
+ print_source_line(diagnostic, prefix: prefix)
25
+ else
26
+ stdout.puts "#{prefix}└ (no source code available)"
27
+ stdout.puts "#{prefix}"
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def color_severity(string, severity:)
34
+ s = Rainbow(string)
35
+
36
+ case severity
37
+ when LSP::Constant::DiagnosticSeverity::ERROR
38
+ s.red
39
+ when LSP::Constant::DiagnosticSeverity::WARNING
40
+ s.yellow
41
+ when LSP::Constant::DiagnosticSeverity::INFORMATION
42
+ s.blue
43
+ else
44
+ s
45
+ end
46
+ end
47
+
48
+ def severity_message(severity)
49
+ string = case severity
50
+ when LSP::Constant::DiagnosticSeverity::ERROR
51
+ "error"
52
+ when LSP::Constant::DiagnosticSeverity::WARNING
53
+ "warning"
54
+ when LSP::Constant::DiagnosticSeverity::INFORMATION
55
+ "information"
56
+ when LSP::Constant::DiagnosticSeverity::HINT
57
+ "hint"
58
+ else
59
+ raise
60
+ end
61
+
62
+ color_severity(string, severity: severity)
63
+ end
64
+
65
+ def location(diagnostic)
66
+ start = diagnostic[:range][:start]
67
+ Rainbow("#{path}:#{start[:line]+1}:#{start[:character]}").magenta
68
+ end
69
+
70
+ def print_source_line(diagnostic, prefix: "")
71
+ start_pos = diagnostic[:range][:start]
72
+ end_pos = diagnostic[:range][:end]
73
+
74
+ line = buffer.lines.fetch(start_pos[:line])
75
+
76
+ leading = line[0...start_pos[:character]] || ""
77
+ if start_pos[:line] == end_pos[:line]
78
+ subject = line[start_pos[:character]...end_pos[:character]] || ""
79
+ trailing = (line[end_pos[:character]...] || "").chomp
80
+ else
81
+ subject = (line[start_pos[:character]...] || "").chomp
82
+ trailing = ""
83
+ end
84
+
85
+ unless subject.valid_encoding?
86
+ subject.scrub!
87
+ end
88
+
89
+ stdout.puts "#{prefix}└ #{leading}#{color_severity(subject, severity: diagnostic[:severity])}#{trailing}"
90
+ stdout.puts "#{prefix} #{" " * leading.size}#{"~" * subject.size}"
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,44 @@
1
+ module Steep
2
+ module Drivers
3
+ class DiagnosticPrinter
4
+ # https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions
5
+ class GitHubActionsFormatter < BaseFormatter
6
+ ESCAPE_MAP = { '%' => '%25', "\n" => '%0A', "\r" => '%0D' }.freeze
7
+
8
+ def print(diagnostic, prefix: "", source: true)
9
+ stdout.printf(
10
+ "::%<severity>s file=%<file>s,line=%<line>d,endLine=%<endLine>d,col=%<column>d,endColumn=%<endColumn>d::%<message>s",
11
+ severity: github_severity(diagnostic[:severity]),
12
+ file: path,
13
+ line: diagnostic[:range][:start][:line] + 1,
14
+ endLine: diagnostic[:range][:end][:line] + 1,
15
+ column: diagnostic[:range][:start][:character],
16
+ endColumn: diagnostic[:range][:end][:character],
17
+ message: github_escape("[#{diagnostic[:code]}] #{diagnostic[:message]}")
18
+ )
19
+ end
20
+
21
+ private
22
+
23
+ def github_severity(severity)
24
+ case severity
25
+ when LSP::Constant::DiagnosticSeverity::ERROR
26
+ "error"
27
+ when LSP::Constant::DiagnosticSeverity::WARNING
28
+ "warning"
29
+ when LSP::Constant::DiagnosticSeverity::INFORMATION
30
+ "notice"
31
+ when LSP::Constant::DiagnosticSeverity::HINT
32
+ "notice"
33
+ else
34
+ raise
35
+ end
36
+ end
37
+
38
+ def github_escape(string)
39
+ string.gsub(Regexp.union(ESCAPE_MAP.keys), ESCAPE_MAP)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,104 +1,27 @@
1
1
  module Steep
2
2
  module Drivers
3
3
  class DiagnosticPrinter
4
+
4
5
  LSP = LanguageServer::Protocol
5
6
 
6
7
  attr_reader :stdout
7
8
  attr_reader :buffer
8
9
 
9
- def initialize(stdout:, buffer:)
10
+ def initialize(stdout:, buffer:, formatter: 'code')
10
11
  @stdout = stdout
11
12
  @buffer = buffer
12
- end
13
-
14
- def path
15
- Pathname(buffer.name)
16
- end
17
-
18
- def color_severity(string, severity:)
19
- s = Rainbow(string)
20
-
21
- case severity
22
- when LSP::Constant::DiagnosticSeverity::ERROR
23
- s.red
24
- when LSP::Constant::DiagnosticSeverity::WARNING
25
- s.yellow
26
- when LSP::Constant::DiagnosticSeverity::INFORMATION
27
- s.blue
13
+ @formatter = case formatter
14
+ when 'code'
15
+ CodeFormatter.new(stdout: stdout, buffer: buffer)
16
+ when 'github'
17
+ GitHubActionsFormatter.new(stdout: stdout, buffer: buffer)
28
18
  else
29
- s
19
+ raise "Unknown formatter: #{formatter}"
30
20
  end
31
21
  end
32
22
 
33
- def severity_message(severity)
34
- string = case severity
35
- when LSP::Constant::DiagnosticSeverity::ERROR
36
- "error"
37
- when LSP::Constant::DiagnosticSeverity::WARNING
38
- "warning"
39
- when LSP::Constant::DiagnosticSeverity::INFORMATION
40
- "information"
41
- when LSP::Constant::DiagnosticSeverity::HINT
42
- "hint"
43
- else
44
- raise
45
- end
46
-
47
- color_severity(string, severity: severity)
48
- end
49
-
50
- def location(diagnostic)
51
- start = diagnostic[:range][:start]
52
- Rainbow("#{path}:#{start[:line]+1}:#{start[:character]}").magenta
53
- end
54
-
55
23
  def print(diagnostic, prefix: "", source: true)
56
- header, *rest = diagnostic[:message].split(/\n/)
57
-
58
- stdout.puts "#{prefix}#{location(diagnostic)}: [#{severity_message(diagnostic[:severity])}] #{Rainbow(header).underline}"
59
-
60
- unless rest.empty?
61
- rest.each do |message|
62
- stdout.puts "#{prefix}│ #{message}"
63
- end
64
- end
65
-
66
- if diagnostic[:code]
67
- stdout.puts "#{prefix}│" unless rest.empty?
68
- stdout.puts "#{prefix}│ Diagnostic ID: #{diagnostic[:code]}"
69
- end
70
-
71
- stdout.puts "#{prefix}│"
72
-
73
- if source
74
- print_source_line(diagnostic, prefix: prefix)
75
- else
76
- stdout.puts "#{prefix}└ (no source code available)"
77
- stdout.puts "#{prefix}"
78
- end
79
- end
80
-
81
- def print_source_line(diagnostic, prefix: "")
82
- start_pos = diagnostic[:range][:start]
83
- end_pos = diagnostic[:range][:end]
84
-
85
- line = buffer.lines.fetch(start_pos[:line])
86
-
87
- leading = line[0...start_pos[:character]] || ""
88
- if start_pos[:line] == end_pos[:line]
89
- subject = line[start_pos[:character]...end_pos[:character]] || ""
90
- trailing = (line[end_pos[:character]...] || "").chomp
91
- else
92
- subject = (line[start_pos[:character]...] || "").chomp
93
- trailing = ""
94
- end
95
-
96
- unless subject.valid_encoding?
97
- subject.scrub!
98
- end
99
-
100
- stdout.puts "#{prefix}└ #{leading}#{color_severity(subject, severity: diagnostic[:severity])}#{trailing}"
101
- stdout.puts "#{prefix} #{" " * leading.size}#{"~" * subject.size}"
24
+ @formatter.print(diagnostic, prefix: prefix, source: source)
102
25
  end
103
26
  end
104
27
  end
@@ -268,6 +268,11 @@ module Steep
268
268
 
269
269
  type_name = sig_service.latest_env.normalize_type_name(type_name)
270
270
 
271
+ tags = [] #: Array[LSP::Constant::CompletionItemTag::t]
272
+ if AnnotationsHelper.deprecated_type_name?(type_name, sig_service.latest_env)
273
+ tags << LSP::Constant::CompletionItemTag::DEPRECATED
274
+ end
275
+
271
276
  case type_name.kind
272
277
  when :class
273
278
  env = sig_service.latest_env
@@ -290,7 +295,8 @@ module Steep
290
295
  range: range,
291
296
  new_text: complete_text
292
297
  ),
293
- kind: LSP::Constant::CompletionItemKind::CLASS
298
+ kind: LSP::Constant::CompletionItemKind::CLASS,
299
+ tags: tags
294
300
  )
295
301
  when :alias
296
302
  alias_decl = sig_service.latest_env.type_alias_decls[type_name]&.decl or raise
@@ -303,7 +309,8 @@ module Steep
303
309
  new_text: complete_text
304
310
  ),
305
311
  documentation: LSPFormatter.markup_content { LSPFormatter.format_rbs_completion_docs(type_name, alias_decl, [alias_decl.comment].compact) },
306
- kind: LSP::Constant::CompletionItemKind::FIELD
312
+ kind: LSP::Constant::CompletionItemKind::FIELD,
313
+ tags: tags
307
314
  )
308
315
  when :interface
309
316
  interface_decl = sig_service.latest_env.interface_decls[type_name]&.decl or raise
@@ -316,7 +323,8 @@ module Steep
316
323
  new_text: complete_text
317
324
  ),
318
325
  documentation: LSPFormatter.markup_content { LSPFormatter.format_rbs_completion_docs(type_name, interface_decl, [interface_decl.comment].compact) },
319
- kind: LSP::Constant::CompletionItemKind::INTERFACE
326
+ kind: LSP::Constant::CompletionItemKind::INTERFACE,
327
+ tags: tags
320
328
  )
321
329
  else
322
330
  raise
@@ -435,6 +443,12 @@ module Steep
435
443
  when item.absolute_type_name.alias?
436
444
  LSP::Constant::CompletionItemKind::FIELD
437
445
  end
446
+
447
+ tags = [] #: Array[LSP::Constant::CompletionItemTag::t]
448
+ if AnnotationsHelper.deprecated_type_name?(item.absolute_type_name, item.env)
449
+ tags << LSP::Constant::CompletionItemTag::DEPRECATED
450
+ end
451
+
438
452
  LSP::Interface::CompletionItem.new(
439
453
  label: item.relative_type_name.to_s,
440
454
  kind: kind,
@@ -443,7 +457,8 @@ module Steep
443
457
  text_edit: LSP::Interface::TextEdit.new(
444
458
  range: range,
445
459
  new_text: item.relative_type_name.to_s
446
- )
460
+ ),
461
+ tags: tags
447
462
  )
448
463
  when Services::CompletionProvider::TextItem
449
464
  LSP::Interface::CompletionItem.new(
@@ -53,19 +53,7 @@ module Steep
53
53
  end
54
54
 
55
55
  def deprecated?
56
- annotations =
57
- case entry = env.constant_entry(full_name)
58
- when RBS::Environment::ConstantEntry
59
- entry.decl.annotations
60
- when RBS::Environment::ClassEntry, RBS::Environment::ModuleEntry
61
- entry.decls.flat_map { _1.decl.annotations }
62
- when RBS::Environment::ClassAliasEntry, RBS::Environment::ModuleAliasEntry
63
- entry.decl.annotations
64
- else
65
- raise
66
- end
67
-
68
- if AnnotationsHelper.deprecated_annotation?(annotations)
56
+ if AnnotationsHelper.deprecated_type_name?(full_name, env)
69
57
  true
70
58
  else
71
59
  false
@@ -294,7 +294,7 @@ module Steep
294
294
  end
295
295
 
296
296
  unless errors.empty?
297
- # Builder won't be used.
297
+ errors.uniq! { |e| [e.class, e.message] }
298
298
  factory = AST::Types::Factory.new(builder: RBS::DefinitionBuilder.new(env: env, ancestor_builder: builder))
299
299
  return errors.map {|error| Diagnostic::Signature.from_rbs_error(error, factory: factory) }
300
300
  end
@@ -144,17 +144,39 @@ module Steep
144
144
  validate_type_application_constraints(name, type_params, type_args, location: type.location)
145
145
  end
146
146
  end
147
-
148
- type.each_type do |child|
149
- validate_type_application(child)
150
- end
151
147
  end
152
148
 
153
149
  def validate_type(type)
154
150
  Steep.logger.debug { "#{Location.to_string type.location}: Validating #{type}..." }
155
151
 
156
152
  validator.validate_type(type, context: nil)
153
+ validate_type_0(type)
154
+ end
155
+
156
+ def validate_type_0(type)
157
157
  validate_type_application(type)
158
+
159
+ case type
160
+ when RBS::Types::ClassInstance, RBS::Types::Interface, RBS::Types::ClassSingleton, RBS::Types::Alias
161
+ type_name = type.name
162
+ if type.location
163
+ location = type.location[:name]
164
+ end
165
+ end
166
+
167
+ if type_name && location
168
+ validate_type_name_deprecation(type_name, location)
169
+ end
170
+
171
+ type.each_type do |child|
172
+ validate_type_0(child)
173
+ end
174
+ end
175
+
176
+ def validate_type_name_deprecation(type_name, location)
177
+ if (_, message = AnnotationsHelper.deprecated_type_name?(type_name, env))
178
+ @errors << Diagnostic::Signature::DeprecatedTypeName.new(type_name, message, location: location)
179
+ end
158
180
  end
159
181
 
160
182
  def ancestor_to_type(ancestor)
@@ -269,6 +291,9 @@ module Steep
269
291
  type_params.each do |type_param|
270
292
  param = checker.factory.type_param(type_param)
271
293
 
294
+ validate_type(type_param.upper_bound_type) if type_param.upper_bound_type
295
+ validate_type(type_param.default_type) if type_param.default_type
296
+
272
297
  default_type = param.default_type or next
273
298
  upper_bound = param.upper_bound or next
274
299
 
@@ -296,6 +321,16 @@ module Steep
296
321
  args: entry.type_params.map { AST::Types::Any.instance() }
297
322
  )
298
323
 
324
+ entry.decls.each do |decl|
325
+ ast = decl.decl
326
+
327
+ unless AnnotationsHelper.deprecated_annotation?(ast.annotations)
328
+ if location = ast.location
329
+ validate_type_name_deprecation(name, location[:name])
330
+ end
331
+ end
332
+ end
333
+
299
334
  Steep.logger.tagged "#{name}" do
300
335
  builder.build_instance(name).tap do |definition|
301
336
  upper_bounds = definition.type_params_decl.each.with_object({}) do |param, bounds| #$ Hash[Symbol, AST::Types::t?]
@@ -353,6 +388,20 @@ module Steep
353
388
  case ancestor
354
389
  when RBS::Definition::Ancestor::Instance
355
390
  validate_ancestor_application(name, ancestor)
391
+ location =
392
+ case ancestor.source
393
+ when :super
394
+ if (primary_decl = entry.primary.decl).is_a?(RBS::AST::Declarations::Class)
395
+ primary_decl.super_class&.location
396
+ end
397
+ when nil
398
+ # skip
399
+ else
400
+ ancestor.source.location
401
+ end
402
+ if location
403
+ validate_type_name_deprecation(ancestor.name, location)
404
+ end
356
405
  end
357
406
  end
358
407
 
@@ -632,6 +681,9 @@ module Steep
632
681
  rescue_validation_errors(name) do
633
682
  Steep.logger.debug "Validating class/module alias `#{name}`..."
634
683
  validator.validate_class_alias(entry: entry)
684
+ if location = entry.decl.location
685
+ validate_type_name_deprecation(entry.decl.old_name, location[:old_name])
686
+ end
635
687
  end
636
688
  end
637
689
 
@@ -3458,7 +3458,7 @@ module Steep
3458
3458
  block_annotations = source.annotations(block: node, factory: checker.factory, context: nesting)
3459
3459
  block_params or raise
3460
3460
 
3461
- constr = constr.synthesize_children(node.children[0])
3461
+ constr = constr.synthesize_children(node.children[0], skips: [receiver])
3462
3462
 
3463
3463
  constr.type_block_without_hint(
3464
3464
  node: node,
@@ -3864,26 +3864,40 @@ module Steep
3864
3864
  if forwarded_args
3865
3865
  method_name or raise "method_name cannot be nil if `forwarded_args` is given, because proc/block doesn't support `...` arg"
3866
3866
 
3867
- (params, block = context.method_context&.forward_arg_type) or raise
3867
+ method_context = context.method_context or raise
3868
+ forward_arg_type = method_context.forward_arg_type
3868
3869
 
3869
- checker.with_context(self_type: self_type, instance_type: context.module_context.instance_type, class_type: context.module_context.module_type, constraints: constraints) do
3870
- result = checker.check_method_params(
3871
- :"... (argument forwarding)",
3872
- Subtyping::Relation.new(
3873
- sub_type: forwarded_args.params,
3874
- super_type: params
3870
+ case forward_arg_type
3871
+ when nil
3872
+ if context.method_context.method_type
3873
+ raise "Method context must have `forwarded_arg_type` if `...` node appears in it"
3874
+ else
3875
+ # Skips type checking forwarded argument because the method type is not given
3876
+ end
3877
+ when true
3878
+ # Skip type checking forwarded argument because the method is untyped function
3879
+ else
3880
+ params, _block = forward_arg_type
3881
+
3882
+ checker.with_context(self_type: self_type, instance_type: context.module_context.instance_type, class_type: context.module_context.module_type, constraints: constraints) do
3883
+ result = checker.check_method_params(
3884
+ :"... (argument forwarding)",
3885
+ Subtyping::Relation.new(
3886
+ sub_type: forwarded_args.params,
3887
+ super_type: params
3888
+ )
3875
3889
  )
3876
- )
3877
3890
 
3878
- if result.failure?
3879
- errors.push(
3880
- Diagnostic::Ruby::IncompatibleArgumentForwarding.new(
3881
- method_name: method_name,
3882
- node: forwarded_args.node,
3883
- params_pair: [params, forwarded_args.params],
3884
- result: result
3891
+ if result.failure?
3892
+ errors.push(
3893
+ Diagnostic::Ruby::IncompatibleArgumentForwarding.new(
3894
+ method_name: method_name,
3895
+ node: forwarded_args.node,
3896
+ params_pair: [params, forwarded_args.params],
3897
+ result: result
3898
+ )
3885
3899
  )
3886
- )
3900
+ end
3887
3901
  end
3888
3902
  end
3889
3903
  end
@@ -4105,6 +4119,15 @@ module Steep
4105
4119
 
4106
4120
  fvs_.merge(method_type.type.params.free_variables) if method_type.type.params
4107
4121
  fvs_.merge(method_type.block.type.params.free_variables) if method_type.block.type.params
4122
+ (method_type.type.return_type.free_variables + method_type.block.type.return_type.free_variables).each do |var|
4123
+ if var.is_a?(Symbol)
4124
+ if constraints.unknown?(var)
4125
+ unless constraints.has_constraint?(var)
4126
+ fvs_.delete(var)
4127
+ end
4128
+ end
4129
+ end
4130
+ end
4108
4131
 
4109
4132
  constraints.solution(checker, variables: fvs_, context: ccontext)
4110
4133
  }
@@ -4116,7 +4139,7 @@ module Steep
4116
4139
  block_constr = block_constr.update_type_env {|env| env.subst(s) }
4117
4140
  block_constr = block_constr.update_context {|context|
4118
4141
  context.with(
4119
- self_type: method_type.block.self_type || context.self_type,
4142
+ self_type: context.self_type.subst(s),
4120
4143
  type_env: context.type_env.subst(s),
4121
4144
  block_context: context.block_context&.subst(s),
4122
4145
  break_context: context.break_context&.subst(s)
@@ -4237,18 +4260,29 @@ module Steep
4237
4260
 
4238
4261
  case
4239
4262
  when forwarded_args_node = args.forwarded_args_node
4240
- (_, block = method_context!.forward_arg_type) or raise
4263
+ case forward_arg_type = method_context!.forward_arg_type
4264
+ when nil
4265
+ if method_context!.method_type
4266
+ raise "Method context must have `forwarded_arg_type` if `...` node appears in it"
4267
+ else
4268
+ # Skips type checking forwarded argument because the method type is not given
4269
+ end
4270
+ when true
4271
+ # Skip type checking because it's untyped function
4272
+ else
4273
+ _, block = forward_arg_type
4241
4274
 
4242
- method_block_type = method_type.block&.to_proc_type || AST::Builtin.nil_type
4243
- forwarded_block_type = block&.to_proc_type || AST::Builtin.nil_type
4275
+ method_block_type = method_type.block&.to_proc_type || AST::Builtin.nil_type
4276
+ forwarded_block_type = block&.to_proc_type || AST::Builtin.nil_type
4244
4277
 
4245
- if result = constr.no_subtyping?(sub_type: forwarded_block_type, super_type: method_block_type)
4246
- errors << Diagnostic::Ruby::IncompatibleArgumentForwarding.new(
4247
- method_name: method_name,
4248
- node: forwarded_args_node,
4249
- block_pair: [block, method_type.block],
4250
- result: result
4251
- )
4278
+ if result = constr.no_subtyping?(sub_type: forwarded_block_type, super_type: method_block_type)
4279
+ errors << Diagnostic::Ruby::IncompatibleArgumentForwarding.new(
4280
+ method_name: method_name,
4281
+ node: forwarded_args_node,
4282
+ block_pair: [block, method_type.block],
4283
+ result: result
4284
+ )
4285
+ end
4252
4286
  end
4253
4287
 
4254
4288
  when arg.compatible?
@@ -4550,12 +4584,11 @@ module Steep
4550
4584
  next_type: block_context.body_type || AST::Builtin.any_type
4551
4585
  )
4552
4586
 
4553
- self_type = block_annotations.self_type || block_self_hint || self.self_type
4587
+ self_type = block_self_hint || self.self_type
4554
4588
  module_context = self.module_context
4555
4589
 
4556
4590
  if implements = block_annotations.implement_module_annotation
4557
4591
  module_context = default_module_context(implements.name, nesting: nesting)
4558
-
4559
4592
  self_type = module_context.module_type
4560
4593
  end
4561
4594
 
@@ -4563,6 +4596,11 @@ module Steep
4563
4596
  self_type = annotation_self_type
4564
4597
  end
4565
4598
 
4599
+ # self_type here means the top-level `self` type because of the `Interface::Builder` implementation
4600
+ if self_type
4601
+ self_type = expand_self(self_type)
4602
+ end
4603
+
4566
4604
  self.class.new(
4567
4605
  checker: checker,
4568
4606
  source: source,
@@ -155,6 +155,10 @@ module Steep
155
155
  @type_env = type_env
156
156
  @call_context = call_context
157
157
  @variable_context = variable_context
158
+
159
+ if self_type.free_variables.include?(AST::Types::Self.instance)
160
+ raise "Context#self_type cannot contain `self`"
161
+ end
158
162
  end
159
163
 
160
164
  def with(method_context: self.method_context,
@@ -173,29 +173,41 @@ module Steep
173
173
  receiver_type = typing.type_of(node: receiver) if receiver
174
174
 
175
175
  if env[receiver] && receiver_type.is_a?(AST::Types::Union)
176
- result = evaluate_union_method_call(node: node, env: env, receiver: receiver, receiver_type: receiver_type)
177
- return result if result
176
+ result = evaluate_union_method_call(node: node, type: type, env: env, receiver: receiver, receiver_type: receiver_type)
177
+ if result
178
+ truthy_result = result[0] unless result[0].unreachable
179
+ falsy_result = result[1] unless result[1].unreachable
180
+ end
178
181
  end
179
182
 
180
- if env[node]
181
- truthy_type, falsy_type = factory.partition_union(type)
183
+ truthy_result ||= Result.new(type: type, env: env, unreachable: false)
184
+ falsy_result ||= Result.new(type: type, env: env, unreachable: false)
182
185
 
183
- truthy_result =
184
- if truthy_type
185
- Result.new(type: truthy_type, env: env.refine_types(pure_call_types: { node => truthy_type }), unreachable: false)
186
- else
187
- Result.new(type: type, env: env, unreachable: true)
188
- end
186
+ truthy_type, falsy_type = factory.partition_union(type)
189
187
 
190
- falsy_result =
191
- if falsy_type
192
- Result.new(type: falsy_type, env: env.refine_types(pure_call_types: { node => falsy_type }), unreachable: false)
193
- else
194
- Result.new(type: type, env: env, unreachable: true)
195
- end
188
+ if truthy_type
189
+ truthy_result = truthy_result.update_type { truthy_type }
190
+ else
191
+ truthy_result = truthy_result.update_type { BOT }.unreachable!
192
+ end
196
193
 
197
- return [truthy_result, falsy_result]
194
+ if falsy_type
195
+ falsy_result = falsy_result.update_type { falsy_type }
196
+ else
197
+ falsy_result = falsy_result.update_type { BOT }.unreachable!
198
+ end
199
+
200
+ if truthy_result.env[node] && falsy_result.env[node]
201
+ if truthy_type
202
+ truthy_result = Result.new(type: truthy_type, env: truthy_result.env.refine_types(pure_call_types: { node => truthy_type }), unreachable: false)
203
+ end
204
+
205
+ if falsy_type
206
+ falsy_result = Result.new(type: falsy_type, env: falsy_result.env.refine_types(pure_call_types: { node => falsy_type }), unreachable: false)
207
+ end
198
208
  end
209
+
210
+ return [truthy_result, falsy_result]
199
211
  end
200
212
  end
201
213
 
@@ -416,7 +428,7 @@ module Steep
416
428
  end
417
429
  end
418
430
 
419
- def evaluate_union_method_call(node:, env:, receiver:, receiver_type:)
431
+ def evaluate_union_method_call(node:, type:, env:, receiver:, receiver_type:)
420
432
  call_type = typing.call_of(node: node) rescue nil
421
433
  return unless call_type.is_a?(Steep::TypeInference::MethodCall::Typed)
422
434
 
@@ -453,8 +465,8 @@ module Steep
453
465
  )
454
466
 
455
467
  return [
456
- Result.new(type: truthy_type, env: truthy_env, unreachable: truthy_type.nil?),
457
- Result.new(type: falsy_type, env: falsy_env, unreachable: falsy_type.nil?)
468
+ Result.new(type: type, env: truthy_env, unreachable: truthy_type.nil?),
469
+ Result.new(type: type, env: falsy_env, unreachable: falsy_type.nil?)
458
470
  ]
459
471
  end
460
472
 
data/lib/steep/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Steep
2
- VERSION = "1.10.0.dev.1"
2
+ VERSION = "1.10.0.pre.2"
3
3
  end
data/lib/steep.rb CHANGED
@@ -151,6 +151,9 @@ require "steep/drivers/init"
151
151
  require "steep/drivers/vendor"
152
152
  require "steep/drivers/worker"
153
153
  require "steep/drivers/diagnostic_printer"
154
+ require "steep/drivers/diagnostic_printer/base_formatter"
155
+ require "steep/drivers/diagnostic_printer/code_formatter"
156
+ require "steep/drivers/diagnostic_printer/github_actions_formatter"
154
157
 
155
158
  require "steep/annotations_helper"
156
159
 
@@ -2,3 +2,6 @@ Foo
2
2
 
3
3
  Bar.bar()
4
4
 
5
+ Bar.new.hogehoge("")
6
+
7
+ foo = nil #: Foo
@@ -1,4 +1,4 @@
1
- %a{deprecated} class Foo end
1
+ %a{deprecated: Use Bar instead} class Foo end
2
2
 
3
3
  class Bar
4
4
  # Original bar
@@ -6,6 +6,11 @@ class Bar
6
6
 
7
7
  # Overloading bar
8
8
  def self.bar: (String) -> String | ...
9
+
10
+ def hogehoge: (String) -> void
11
+ | %a{deprecated: Pass an positional argument} (string: String) -> void
9
12
  end
10
13
 
14
+ type t = Foo
15
+
11
16
  %a{deprecated} $test: untyped
data/steep.gemspec CHANGED
@@ -42,7 +42,7 @@ Gem::Specification.new do |spec|
42
42
  spec.add_runtime_dependency "rainbow", ">= 2.2.2", "< 4.0"
43
43
  spec.add_runtime_dependency "listen", "~> 3.0"
44
44
  spec.add_runtime_dependency "language_server-protocol", ">= 3.17.0.4", "< 4.0"
45
- spec.add_runtime_dependency "rbs", "~> 3.9.dev"
45
+ spec.add_runtime_dependency "rbs", "~> 3.9.pre"
46
46
  spec.add_runtime_dependency "concurrent-ruby", ">= 1.1.10"
47
47
  spec.add_runtime_dependency "terminal-table", ">= 2", "< 5"
48
48
  spec.add_runtime_dependency "securerandom", ">= 0.1"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: steep
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.10.0.dev.1
4
+ version: 1.10.0.pre.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Soutaro Matsumoto
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-03-07 00:00:00.000000000 Z
10
+ date: 2025-03-14 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: parser
@@ -97,14 +97,14 @@ dependencies:
97
97
  requirements:
98
98
  - - "~>"
99
99
  - !ruby/object:Gem::Version
100
- version: 3.9.dev
100
+ version: 3.9.pre
101
101
  type: :runtime
102
102
  prerelease: false
103
103
  version_requirements: !ruby/object:Gem::Requirement
104
104
  requirements:
105
105
  - - "~>"
106
106
  - !ruby/object:Gem::Version
107
- version: 3.9.dev
107
+ version: 3.9.pre
108
108
  - !ruby/object:Gem::Dependency
109
109
  name: concurrent-ruby
110
110
  requirement: !ruby/object:Gem::Requirement
@@ -328,6 +328,9 @@ files:
328
328
  - lib/steep/drivers/check.rb
329
329
  - lib/steep/drivers/checkfile.rb
330
330
  - lib/steep/drivers/diagnostic_printer.rb
331
+ - lib/steep/drivers/diagnostic_printer/base_formatter.rb
332
+ - lib/steep/drivers/diagnostic_printer/code_formatter.rb
333
+ - lib/steep/drivers/diagnostic_printer/github_actions_formatter.rb
331
334
  - lib/steep/drivers/init.rb
332
335
  - lib/steep/drivers/langserver.rb
333
336
  - lib/steep/drivers/print_project.rb