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

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: 2233f51ac985ee697a7f3617582dc034f32b1cd3b0732a87b8184210f2c1d7b0
4
+ data.tar.gz: 1298685cafa59f5074afa950df4c1f14cff9a8b6be9b99b914902b37e537aad3
5
5
  SHA512:
6
- metadata.gz: 24d081be36d7b8bd33ffe5786db94ff5e8a1b505c7dfa72e4076b3c7612d78e6c5978bebba991d792e9fc4b97bb0da3f004db308eade2b6ed6e63075ce3f9d9f
7
- data.tar.gz: 68069586284621fac6acbc1f279a3b866b26dcba5436334f16abb6db3466c2917a549c0f2b48bfc205eab74ee3d865124ab1a87f7b02403bb2a8b39d2c67a938
6
+ metadata.gz: 1cb518e2f5743330e58b0c6032ed99d22f29254b91d9c8e33e3bafc3cb1a9f46247f181ee5b9b078dba13768f675f03b1c2e8fb24b53609902d6534092625282
7
+ data.tar.gz: e26fde370f8d793b13138d71dc6295ffde59e781ed2d0a39cf73e639c8440517d7fa13586d871c9eab6880ae8864c31a711beb45c49280152416d0111935f3da
data/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 1.10.0.pre.1 (2025-03-11)
4
+
5
+ ### Type checker core
6
+
7
+ * Skip type checking forwarded argument if the method is undeclared ([#1519](https://github.com/soutaro/steep/pull/1519))
8
+ * Dedup error message on ancestors check ([#1515](https://github.com/soutaro/steep/pull/1515))
9
+ * Add deprecation validation on RBS ([#1518](https://github.com/soutaro/steep/pull/1518))
10
+ * Fix union self type refinement based on method call ([#1517](https://github.com/soutaro/steep/pull/1517))
11
+ * Check deprecations in Ruby code ([#1513](https://github.com/soutaro/steep/pull/1513))
12
+ * Support `InvalidTypeApplication` on `#update_env` ([#1507](https://github.com/soutaro/steep/pull/1507))
13
+ * Fix Source.parse crashes with selector-less sendish node ([#1433](https://github.com/soutaro/steep/pull/1433))
14
+ * Fix steep:ignore does not work with CR/LF ([#1406](https://github.com/soutaro/steep/pull/1406))
15
+ * Fix self union type checking ([#1467](https://github.com/soutaro/steep/pull/1467))
16
+ * Type narrowing union types via return type of method call ([#1497](https://github.com/soutaro/steep/pull/1497))
17
+ * Fix `else` clause of `case-when` syntax typing ([#1475](https://github.com/soutaro/steep/pull/1475))
18
+ * Strict record and tuple subtyping ([#1460](https://github.com/soutaro/steep/pull/1460))
19
+ * Allow to annotate "self" on toplevel ([#1455](https://github.com/soutaro/steep/pull/1455))
20
+ * Let annotations in `when` clause without `cond` expression work ([#1459](https://github.com/soutaro/steep/pull/1459))
21
+ * Fix `RuntimeError` on `when` clause with assertion ([#1458](https://github.com/soutaro/steep/pull/1458))
22
+ * Add diagnostic for method definition without types ([#1457](https://github.com/soutaro/steep/pull/1457))
23
+
24
+ ### Commandline tool
25
+
26
+ * Implement GitHub formatter ([#1516](https://github.com/soutaro/steep/pull/1516))
27
+ * Refine `--help` messages ([#1463](https://github.com/soutaro/steep/pull/1463))
28
+
29
+ ### Language server
30
+
31
+ * Reforking steep ([#1492](https://github.com/soutaro/steep/pull/1492))
32
+ * Set up file watcher for groups ([#1485](https://github.com/soutaro/steep/pull/1485))
33
+
34
+ ### Miscellaneous
35
+
36
+ * Fix a runtime warning for ambiguous `/`; wrap regexp in parentheses or add a space after `/` operator ([#1438](https://github.com/soutaro/steep/pull/1438))
37
+ * Fix a runtime warning for ambiguous `*` has been interpreted as an argument prefix ([#1439](https://github.com/soutaro/steep/pull/1439))
38
+ * Fix a runtime warning for key :cursor is duplicated and overwritten on line 99 ([#1440](https://github.com/soutaro/steep/pull/1440))
39
+ * Use release version of RBS ([#1511](https://github.com/soutaro/steep/pull/1511))
40
+ * Prepare for rbs-3.9 ([#1510](https://github.com/soutaro/steep/pull/1510))
41
+ * Fix typos ([#1506](https://github.com/soutaro/steep/pull/1506))
42
+ * Fix CI ([#1486](https://github.com/soutaro/steep/pull/1486))
43
+
3
44
  ## 1.9.3 (2024-12-26)
4
45
 
5
46
  ### Miscellaneous
@@ -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 = {
@@ -460,6 +460,26 @@ module Steep
460
460
  end
461
461
  end
462
462
 
463
+ class DeprecatedTypeName < Base
464
+ attr_reader :type_name
465
+ attr_reader :message
466
+
467
+ def initialize(type_name, message, location:)
468
+ super(location: location)
469
+ @type_name = type_name
470
+ @message = message
471
+ end
472
+
473
+ def header_line
474
+ buffer = "Type `#{type_name}` is deprecated"
475
+ if message
476
+ buffer = +buffer
477
+ buffer << ": " << message
478
+ end
479
+ buffer
480
+ end
481
+ end
482
+
463
483
 
464
484
  def self.from_rbs_error(error, factory:)
465
485
  case error
@@ -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
 
@@ -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
3868
-
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
3875
- )
3876
- )
3867
+ method_context = context.method_context or raise
3868
+ forward_arg_type = method_context.forward_arg_type
3877
3869
 
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
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
3885
3888
  )
3886
3889
  )
3890
+
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
+ )
3899
+ )
3900
+ end
3887
3901
  end
3888
3902
  end
3889
3903
  end
@@ -4237,18 +4251,29 @@ module Steep
4237
4251
 
4238
4252
  case
4239
4253
  when forwarded_args_node = args.forwarded_args_node
4240
- (_, block = method_context!.forward_arg_type) or raise
4254
+ case forward_arg_type = method_context!.forward_arg_type
4255
+ when nil
4256
+ if method_context!.method_type
4257
+ raise "Method context must have `forwarded_arg_type` if `...` node appears in it"
4258
+ else
4259
+ # Skips type checking forwarded argument because the method type is not given
4260
+ end
4261
+ when true
4262
+ # Skip type checking because it's untyped function
4263
+ else
4264
+ _, block = forward_arg_type
4241
4265
 
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
4266
+ method_block_type = method_type.block&.to_proc_type || AST::Builtin.nil_type
4267
+ forwarded_block_type = block&.to_proc_type || AST::Builtin.nil_type
4244
4268
 
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
- )
4269
+ if result = constr.no_subtyping?(sub_type: forwarded_block_type, super_type: method_block_type)
4270
+ errors << Diagnostic::Ruby::IncompatibleArgumentForwarding.new(
4271
+ method_name: method_name,
4272
+ node: forwarded_args_node,
4273
+ block_pair: [block, method_type.block],
4274
+ result: result
4275
+ )
4276
+ end
4252
4277
  end
4253
4278
 
4254
4279
  when arg.compatible?
@@ -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.1"
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.1
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-11 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