rbs 4.0.3 → 4.1.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +34 -6
  3. data/.github/workflows/rust.yml +0 -2
  4. data/.github/workflows/windows.yml +0 -3
  5. data/CHANGELOG.md +0 -8
  6. data/Rakefile +2 -2
  7. data/config.yml +24 -0
  8. data/core/builtin.rbs +1 -0
  9. data/core/class.rbs +5 -3
  10. data/core/kernel.rbs +26 -11
  11. data/core/ruby_vm.rbs +40 -0
  12. data/docs/inline.md +29 -1
  13. data/ext/rbs_extension/ast_translation.c +21 -0
  14. data/ext/rbs_extension/class_constants.c +2 -0
  15. data/ext/rbs_extension/class_constants.h +1 -0
  16. data/ext/rbs_extension/extconf.rb +1 -0
  17. data/include/rbs/ast.h +314 -297
  18. data/include/rbs/defines.h +13 -0
  19. data/include/rbs/lexer.h +1 -0
  20. data/lib/rbs/annotate/rdoc_annotator.rb +27 -31
  21. data/lib/rbs/ast/ruby/annotations.rb +42 -0
  22. data/lib/rbs/ast/ruby/declarations.rb +11 -1
  23. data/lib/rbs/ast/ruby/members.rb +28 -0
  24. data/lib/rbs/cli.rb +3 -5
  25. data/lib/rbs/collection/config/lockfile_generator.rb +14 -1
  26. data/lib/rbs/environment.rb +6 -0
  27. data/lib/rbs/inline_parser.rb +49 -25
  28. data/lib/rbs/rewriter.rb +70 -0
  29. data/lib/rbs/test/type_check.rb +6 -1
  30. data/lib/rbs/version.rb +1 -1
  31. data/lib/rbs.rb +1 -0
  32. data/sig/annotate/rdoc_annotater.rbs +12 -9
  33. data/sig/ast/ruby/annotations.rbs +49 -0
  34. data/sig/ast/ruby/members.rbs +15 -0
  35. data/sig/collection/config/lockfile_generator.rbs +2 -0
  36. data/sig/inline_parser.rbs +2 -0
  37. data/sig/manifest.yaml +0 -1
  38. data/sig/rewriter.rbs +45 -0
  39. data/src/ast.c +109 -85
  40. data/src/lexer.c +137 -114
  41. data/src/lexer.re +1 -0
  42. data/src/lexstate.c +1 -0
  43. data/src/parser.c +55 -5
  44. data/stdlib/openssl/0/openssl.rbs +2 -2
  45. metadata +3 -1
@@ -83,4 +83,17 @@
83
83
  #define RBS_ATTRIBUTE_UNUSED
84
84
  #endif
85
85
 
86
+ /**
87
+ * Nullability annotations for pointer types.
88
+ * Clang supports _Nullable and _Nonnull to indicate whether a pointer may be NULL.
89
+ * On other compilers, these expand to nothing.
90
+ */
91
+ #ifdef __clang__
92
+ #define RBS_NULLABLE _Nullable
93
+ #define RBS_NONNULL _Nonnull
94
+ #else
95
+ #define RBS_NULLABLE
96
+ #define RBS_NONNULL
97
+ #endif
98
+
86
99
  #endif
data/include/rbs/lexer.h CHANGED
@@ -70,6 +70,7 @@ enum RBSTokenType {
70
70
  kATRBS, /* @rbs */
71
71
  kSKIP, /* skip */
72
72
  kRETURN, /* return */
73
+ kMODULESELF, /* module-self */
73
74
 
74
75
  tLIDENT, /* Identifiers starting with lower case */
75
76
  tUIDENT, /* Identifiers starting with upper case */
@@ -13,25 +13,22 @@ module RBS
13
13
  @include_filename = true
14
14
  end
15
15
 
16
- def annotate_file(path, preserve:)
17
- content = path.read()
16
+ def annotate_file(path, preserve: true)
17
+ buffer, _, decls = Parser.parse_signature(path.read())
18
18
 
19
- _, _, decls = Parser.parse_signature(content)
19
+ rewriter = Rewriter.new(buffer)
20
+ annotate_decls(decls, rewriter)
20
21
 
21
- annotate_decls(decls)
22
-
23
- path.open("w") do |io|
24
- Writer.new(out: io).preserve!(preserve: preserve).write(decls)
25
- end
22
+ path.write(rewriter.string)
26
23
  end
27
24
 
28
- def annotate_decls(decls, outer: [])
25
+ def annotate_decls(decls, rewriter, outer: [])
29
26
  decls.each do |decl|
30
27
  case decl
31
28
  when AST::Declarations::Class, AST::Declarations::Module
32
- annotate_class(decl, outer: outer)
29
+ annotate_class(decl, rewriter, outer: outer)
33
30
  when AST::Declarations::Constant
34
- annotate_constant(decl, outer: outer)
31
+ annotate_constant(decl, rewriter, outer: outer)
35
32
  end
36
33
  end
37
34
  end
@@ -244,7 +241,7 @@ module RBS
244
241
  end
245
242
  end
246
243
 
247
- def annotate_class(decl, outer:)
244
+ def annotate_class(decl, rewriter, outer:)
248
245
  annots = annotations(decl)
249
246
 
250
247
  full_name = resolve_name(decl.name, outer: outer)
@@ -252,7 +249,7 @@ module RBS
252
249
  text = resolve_doc_source(annots.copy_annotation, tester: annots) { doc_for_class(full_name, tester: annots) }
253
250
  end
254
251
 
255
- replace_comment(decl, text)
252
+ replace_comment(decl, text, rewriter)
256
253
 
257
254
  unless annots.skip_all?
258
255
  outer_ = outer + [decl.name.to_namespace]
@@ -260,28 +257,28 @@ module RBS
260
257
  decl.each_member do |member|
261
258
  case member
262
259
  when AST::Members::MethodDefinition
263
- annotate_method(full_name, member)
260
+ annotate_method(full_name, member, rewriter)
264
261
  when AST::Members::Alias
265
- annotate_alias(full_name, member)
262
+ annotate_alias(full_name, member, rewriter)
266
263
  when AST::Members::AttrReader, AST::Members::AttrAccessor, AST::Members::AttrWriter
267
- annotate_attribute(full_name, member)
264
+ annotate_attribute(full_name, member, rewriter)
268
265
  end
269
266
  end
270
267
 
271
- annotate_decls(decl.each_decl.to_a, outer: outer_)
268
+ annotate_decls(decl.each_decl.to_a, rewriter, outer: outer_)
272
269
  end
273
270
  end
274
271
 
275
- def annotate_constant(const, outer:)
272
+ def annotate_constant(const, rewriter, outer:)
276
273
  annots = Annotations.new([])
277
274
 
278
275
  full_name = resolve_name(const.name, outer: outer)
279
276
  text = doc_for_constant(full_name, tester: annots)
280
277
 
281
- replace_comment(const, text)
278
+ replace_comment(const, text, rewriter)
282
279
  end
283
280
 
284
- def annotate_alias(typename, als)
281
+ def annotate_alias(typename, als, rewriter)
285
282
  annots = annotations(als)
286
283
 
287
284
  unless annots.skip?
@@ -295,7 +292,7 @@ module RBS
295
292
  end
296
293
  end
297
294
 
298
- replace_comment(als, text)
295
+ replace_comment(als, text, rewriter)
299
296
  end
300
297
 
301
298
  def join_docs(docs, separator: "----")
@@ -311,7 +308,7 @@ module RBS
311
308
  end
312
309
  end
313
310
 
314
- def annotate_method(typename, method)
311
+ def annotate_method(typename, method, rewriter)
315
312
  annots = annotations(method)
316
313
 
317
314
  unless annots.skip?
@@ -337,10 +334,10 @@ module RBS
337
334
  }
338
335
  end
339
336
 
340
- replace_comment(method, text)
337
+ replace_comment(method, text, rewriter)
341
338
  end
342
339
 
343
- def annotate_attribute(typename, attr)
340
+ def annotate_attribute(typename, attr, rewriter)
344
341
  annots = annotations(attr)
345
342
 
346
343
  unless annots.skip?
@@ -368,18 +365,17 @@ module RBS
368
365
  end
369
366
  end
370
367
 
371
- replace_comment(attr, text)
368
+ replace_comment(attr, text, rewriter)
372
369
  end
373
370
 
374
- def replace_comment(commented, string)
371
+ def replace_comment(commented, string, rewriter)
375
372
  if string
376
373
  if string.empty?
377
- commented.instance_variable_set(:@comment, nil)
374
+ rewriter.delete_comment(commented.comment) if commented.comment
375
+ elsif commented.comment
376
+ rewriter.replace_comment(commented.comment, content: string)
378
377
  else
379
- commented.instance_variable_set(
380
- :@comment,
381
- AST::Comment.new(location: nil, string: string)
382
- )
378
+ rewriter.add_comment(commented.location || raise, *commented.annotations.filter_map(&:location), content: string)
383
379
  end
384
380
  end
385
381
  end
@@ -353,6 +353,48 @@ module RBS
353
353
  end
354
354
  end
355
355
 
356
+ class ModuleSelfAnnotation < Base
357
+ attr_reader :keyword_location, :colon_location, :name, :args, :open_bracket_location, :close_bracket_location, :args_comma_locations, :comment_location
358
+
359
+ def initialize(location:, prefix_location:, keyword_location:, colon_location:, name:, args:, open_bracket_location:, close_bracket_location:, args_comma_locations:, comment_location:)
360
+ super(location, prefix_location)
361
+ @keyword_location = keyword_location
362
+ @colon_location = colon_location
363
+ @name = name
364
+ @args = args
365
+ @open_bracket_location = open_bracket_location
366
+ @close_bracket_location = close_bracket_location
367
+ @args_comma_locations = args_comma_locations
368
+ @comment_location = comment_location
369
+ end
370
+
371
+ def map_type_name
372
+ mapped_args = args.map { |type| type.map_type_name { yield _1 } }
373
+
374
+ self.class.new(
375
+ location:,
376
+ prefix_location:,
377
+ keyword_location:,
378
+ colon_location:,
379
+ name: yield(name),
380
+ args: mapped_args,
381
+ open_bracket_location:,
382
+ close_bracket_location:,
383
+ args_comma_locations:,
384
+ comment_location:
385
+ ) #: self
386
+ end
387
+
388
+ def type_fingerprint
389
+ [
390
+ "annots/module_self",
391
+ name.to_s,
392
+ args.map(&:to_s),
393
+ comment_location&.source
394
+ ]
395
+ end
396
+ end
397
+
356
398
  class BlockParamTypeAnnotation < Base
357
399
  attr_reader :ampersand_location, :name_location, :colon_location, :question_location, :type_location, :type, :comment_location
358
400
 
@@ -137,7 +137,17 @@ module RBS
137
137
 
138
138
  def type_params = []
139
139
 
140
- def self_types = []
140
+ def self_types
141
+ members.filter_map do |member|
142
+ if member.is_a?(Members::ModuleSelfMember)
143
+ AST::Declarations::Module::Self.new(
144
+ name: member.name,
145
+ args: member.args,
146
+ location: member.location
147
+ )
148
+ end
149
+ end
150
+ end
141
151
 
142
152
  def location
143
153
  rbs_location(node.location)
@@ -717,6 +717,34 @@ module RBS
717
717
  ]
718
718
  end
719
719
  end
720
+
721
+ class ModuleSelfMember < Base
722
+ attr_reader :annotation
723
+
724
+ def initialize(buffer, annotation)
725
+ super(buffer)
726
+ @annotation = annotation
727
+ end
728
+
729
+ def name
730
+ annotation.name
731
+ end
732
+
733
+ def args
734
+ annotation.args
735
+ end
736
+
737
+ def location
738
+ annotation.location
739
+ end
740
+
741
+ def type_fingerprint
742
+ [
743
+ "members/module_self",
744
+ annotation.type_fingerprint
745
+ ]
746
+ end
747
+ end
720
748
  end
721
749
  end
722
750
  end
data/lib/rbs/cli.rb CHANGED
@@ -962,8 +962,6 @@ Options:
962
962
  source = RBS::Annotate::RDocSource.new()
963
963
  annotator = RBS::Annotate::RDocAnnotator.new(source: source)
964
964
 
965
- preserve = true
966
-
967
965
  OptionParser.new do |opts|
968
966
  opts.banner = <<-EOB
969
967
  Usage: rbs annotate [options...] [files...]
@@ -984,7 +982,7 @@ Options:
984
982
  opts.on("-d", "--dir DIRNAME", "Load RDoc from DIRNAME") {|d| source.extra_dirs << Pathname(d) }
985
983
  opts.on("--[no-]arglists", "Generate arglists section (defaults to true)") {|b| annotator.include_arg_lists = b }
986
984
  opts.on("--[no-]filename", "Include source file name in the documentation (defaults to true)") {|b| annotator.include_filename = b }
987
- opts.on("--[no-]preserve", "Try preserve the format of the original file (defaults to true)") {|b| preserve = b }
985
+ opts.on("--[no-]preserve", "[Deprecated] It always preserves the format") { stdout.puts "The `--preserve` option is deprecated. The tool always preserves the format of RBS files." }
988
986
  end.parse!(args)
989
987
 
990
988
  source.load()
@@ -994,11 +992,11 @@ Options:
994
992
  if path.directory?
995
993
  Pathname.glob((path + "**/*.rbs").to_s).each do |path|
996
994
  stdout.puts "Processing #{path}..."
997
- annotator.annotate_file(path, preserve: preserve)
995
+ annotator.annotate_file(path)
998
996
  end
999
997
  else
1000
998
  stdout.puts "Processing #{path}..."
1001
- annotator.annotate_file(path, preserve: preserve)
999
+ annotator.annotate_file(path)
1002
1000
  end
1003
1001
  end
1004
1002
 
@@ -21,6 +21,17 @@ module RBS
21
21
  "pstore" => nil,
22
22
  }
23
23
 
24
+ NONGEM_STDLIBS = Set[
25
+ "cgi-escape",
26
+ "coverage",
27
+ "monitor",
28
+ "objspace",
29
+ "pathname",
30
+ "pty",
31
+ "ripper",
32
+ "socket",
33
+ ]
34
+
24
35
  class GemfileLockMismatchError < StandardError
25
36
  def initialize(expected:, actual:)
26
37
  @expected = expected
@@ -168,7 +179,9 @@ module RBS
168
179
  end
169
180
  end
170
181
  else
171
- RBS.logger.warn "Cannot find `#{name}` gem. Using incorrect Bundler context? (#{definition.lockfile})"
182
+ unless NONGEM_STDLIBS.include?(name)
183
+ RBS.logger.warn "Cannot find `#{name}` gem. Using incorrect Bundler context? (#{definition.lockfile})"
184
+ end
172
185
  end
173
186
  end
174
187
 
@@ -854,6 +854,12 @@ module RBS
854
854
  member.buffer,
855
855
  resolved_annotation
856
856
  )
857
+ when AST::Ruby::Members::ModuleSelfMember
858
+ resolved_annotation = member.annotation.map_type_name {|name| absolute_type_name(resolver, nil, name, context: context) }
859
+ AST::Ruby::Members::ModuleSelfMember.new(
860
+ member.buffer,
861
+ resolved_annotation
862
+ )
857
863
  else
858
864
  raise "Unknown member type: #{member.class}"
859
865
  end
@@ -119,7 +119,53 @@ module RBS
119
119
 
120
120
  class_decl = AST::Ruby::Declarations::ClassDecl.new(buffer, class_name, node, super_class)
121
121
  insert_declaration(class_decl)
122
- push_module_nesting(class_decl) do
122
+ visit_class_or_module_body(class_decl, node)
123
+ end
124
+
125
+ def visit_module_node(node)
126
+ return if skip_node?(node)
127
+
128
+ unless module_name = constant_as_type_name(node.constant_path)
129
+ diagnostics << Diagnostic::NonConstantModuleName.new(
130
+ rbs_location(node.constant_path.location),
131
+ "Module name must be a constant"
132
+ )
133
+ return
134
+ end
135
+
136
+ module_decl = AST::Ruby::Declarations::ModuleDecl.new(buffer, module_name, node)
137
+
138
+ if leading_ref = comments.leading_block(node)
139
+ unused_annotations = [] #: Array[AST::Ruby::CommentBlock::AnnotationSyntaxError | AST::Ruby::Annotations::leading_annotation]
140
+
141
+ leading_ref.block.each_paragraph([]) do |paragraph|
142
+ case paragraph
143
+ when AST::Ruby::Annotations::ModuleSelfAnnotation
144
+ module_decl.members << AST::Ruby::Members::ModuleSelfMember.new(buffer, paragraph)
145
+ when Location
146
+ # Skip
147
+ when AST::Ruby::CommentBlock::AnnotationSyntaxError
148
+ unused_annotations << paragraph
149
+ when AST::Ruby::Annotations::SkipAnnotation
150
+ # Already handled by skip_node?
151
+ else
152
+ unused_annotations << paragraph
153
+ end
154
+ end
155
+
156
+ unless unused_annotations.empty?
157
+ report_unused_annotation(*unused_annotations)
158
+ end
159
+
160
+ leading_ref.associate!
161
+ end
162
+
163
+ insert_declaration(module_decl)
164
+ visit_class_or_module_body(module_decl, node)
165
+ end
166
+
167
+ def visit_class_or_module_body(decl, node)
168
+ push_module_nesting(decl) do
123
169
  visit_child_nodes(node)
124
170
 
125
171
  node.child_nodes.each do |child_node|
@@ -137,7 +183,7 @@ module RBS
137
183
  block.each_paragraph([]) do |paragraph|
138
184
  case paragraph
139
185
  when AST::Ruby::Annotations::InstanceVariableAnnotation
140
- class_decl.members << AST::Ruby::Members::InstanceVariableMember.new(buffer, paragraph)
186
+ decl.members << AST::Ruby::Members::InstanceVariableMember.new(buffer, paragraph)
141
187
  when Location
142
188
  # Skip
143
189
  when AST::Ruby::CommentBlock::AnnotationSyntaxError
@@ -150,29 +196,7 @@ module RBS
150
196
  report_unused_annotation(*unused_annotations)
151
197
  end
152
198
 
153
- class_decl.members.sort_by! { _1.location.start_line }
154
- end
155
-
156
- def visit_module_node(node)
157
- return if skip_node?(node)
158
-
159
- unless module_name = constant_as_type_name(node.constant_path)
160
- diagnostics << Diagnostic::NonConstantModuleName.new(
161
- rbs_location(node.constant_path.location),
162
- "Module name must be a constant"
163
- )
164
- return
165
- end
166
-
167
- module_decl = AST::Ruby::Declarations::ModuleDecl.new(buffer, module_name, node)
168
- insert_declaration(module_decl)
169
- push_module_nesting(module_decl) do
170
- visit_child_nodes(node)
171
- end
172
-
173
- comments.each_enclosed_block(node) do |block|
174
- report_unused_block(block)
175
- end
199
+ decl.members.sort_by! { _1.location.start_line }
176
200
  end
177
201
 
178
202
  def visit_def_node(node)
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RBS
4
+ class Rewriter
5
+ attr_reader :buffer
6
+
7
+ def initialize(buffer)
8
+ raise "Rewriter only supports toplevel buffers" if buffer.parent
9
+
10
+ @buffer = buffer
11
+ @rewrites = []
12
+ end
13
+
14
+ def rewrite(location, string)
15
+ @rewrites.each do |existing_location, _|
16
+ if location.start_pos < existing_location.end_pos && existing_location.start_pos < location.end_pos
17
+ raise "Overlapping rewrites: #{existing_location} and #{location}"
18
+ end
19
+ end
20
+
21
+ @rewrites << [location, string]
22
+ self
23
+ end
24
+
25
+ def add_comment(*locations, content:)
26
+ earliest = locations.min_by(&:start_pos) or raise "At least one location is required"
27
+ insert_pos = earliest.start_pos
28
+ indent = " " * earliest.start_column
29
+
30
+ formatted = format_comment(content, indent)
31
+
32
+ loc = Location.new(buffer, insert_pos, insert_pos)
33
+ rewrite(loc, "#{formatted}\n#{indent}")
34
+ end
35
+
36
+ def replace_comment(comment, content:)
37
+ location = comment.location or raise "Comment must have a location"
38
+ indent = " " * location.start_column
39
+
40
+ rewrite(location, format_comment(content, indent))
41
+ end
42
+
43
+ def delete_comment(comment)
44
+ location = comment.location or raise "Comment must have a location"
45
+ line_start = location.start_pos - location.start_column
46
+ line_end = location.end_pos + 1
47
+ loc = Location.new(buffer, line_start, line_end)
48
+ rewrite(loc, "")
49
+ end
50
+
51
+ def string
52
+ result = buffer.content.dup
53
+
54
+ @rewrites.sort_by { |location, _| location.start_pos }.reverse_each do |location, replacement|
55
+ result[location.start_pos...location.end_pos] = replacement
56
+ end
57
+
58
+ result
59
+ end
60
+
61
+ private
62
+
63
+ def format_comment(content, indent)
64
+ content.lines.map do |line|
65
+ line = line.chomp
66
+ line.empty? ? "#" : "# #{line}"
67
+ end.join("\n#{indent}")
68
+ end
69
+ end
70
+ end
@@ -356,7 +356,12 @@ module RBS
356
356
  when Types::Variable
357
357
  true
358
358
  when Types::Literal
359
- type.literal == val
359
+ begin
360
+ type.literal == val
361
+ rescue NoMethodError
362
+ raise if defined?(val.==)
363
+ false
364
+ end
360
365
  when Types::Union
361
366
  type.types.any? {|type| value(val, type) }
362
367
  when Types::Intersection
data/lib/rbs/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RBS
4
- VERSION = "4.0.3"
4
+ VERSION = "4.1.0.pre.1"
5
5
  end
data/lib/rbs.rb CHANGED
@@ -51,6 +51,7 @@ require "rbs/resolver/constant_resolver"
51
51
  require "rbs/resolver/type_name_resolver"
52
52
  require "rbs/ast/comment"
53
53
  require "rbs/writer"
54
+ require "rbs/rewriter"
54
55
  require "rbs/prototype/helpers"
55
56
  require "rbs/prototype/rbi"
56
57
  require "rbs/prototype/rb"
@@ -8,9 +8,10 @@ module RBS
8
8
 
9
9
  def initialize: (source: RDocSource) -> void
10
10
 
11
- def annotate_file: (Pathname, preserve: bool) -> void
11
+ def annotate_file: (Pathname) -> void
12
+ | %a{deprecated: it preserves the format by default} (Pathname, preserve: bool) -> void
12
13
 
13
- def annotate_decls: (Array[AST::Declarations::t], ?outer: Array[Namespace]) -> void
14
+ def annotate_decls: (Array[AST::Declarations::t], Rewriter, ?outer: Array[Namespace]) -> void
14
15
 
15
16
  interface _PathTester
16
17
  def test_path: (String) -> bool
@@ -50,15 +51,15 @@ module RBS
50
51
 
51
52
  def resolve_doc_source: (Annotations::Copy?, tester: _PathTester) { () -> String? } -> String?
52
53
 
53
- def annotate_class: (AST::Declarations::Class | AST::Declarations::Module, outer: Array[Namespace]) -> void
54
+ def annotate_class: (AST::Declarations::Class | AST::Declarations::Module, Rewriter, outer: Array[Namespace]) -> void
54
55
 
55
- def annotate_constant: (AST::Declarations::Constant, outer: Array[Namespace]) -> void
56
+ def annotate_constant: (AST::Declarations::Constant, Rewriter, outer: Array[Namespace]) -> void
56
57
 
57
- def annotate_method: (TypeName, AST::Members::MethodDefinition) -> void
58
+ def annotate_method: (TypeName, AST::Members::MethodDefinition, Rewriter) -> void
58
59
 
59
- def annotate_alias: (TypeName, AST::Members::Alias) -> void
60
+ def annotate_alias: (TypeName, AST::Members::Alias, Rewriter) -> void
60
61
 
61
- def annotate_attribute: (TypeName, AST::Members::AttrReader | AST::Members::AttrWriter | AST::Members::AttrAccessor) -> void
62
+ def annotate_attribute: (TypeName, AST::Members::AttrReader | AST::Members::AttrWriter | AST::Members::AttrAccessor, Rewriter) -> void
62
63
 
63
64
  def annotations: (_Annotated) -> Annotations
64
65
 
@@ -70,10 +71,12 @@ module RBS
70
71
  # - If empty string is given as `comment`, it deletes the original comment.
71
72
  # - If `nil` is given as `comment`, it keeps the original comment.
72
73
  #
73
- def replace_comment: (Object & _Commented, String? comment) -> void
74
+ def replace_comment: (_Commented, String?, Rewriter) -> void
74
75
 
75
76
  interface _Commented
76
- def comment: () -> AST::Comment?
77
+ %a{pure} def comment: () -> AST::Comment?
78
+ %a{pure} def location: () -> Location[untyped, untyped]?
79
+ def annotations: () -> Array[AST::Annotation]
77
80
  end
78
81
 
79
82
  def resolve_name: (TypeName, outer: Array[Namespace]) -> TypeName
@@ -7,6 +7,7 @@ module RBS
7
7
  | SkipAnnotation
8
8
  | ReturnTypeAnnotation
9
9
  | InstanceVariableAnnotation
10
+ | ModuleSelfAnnotation
10
11
  | ParamTypeAnnotation
11
12
  | SplatParamTypeAnnotation
12
13
  | DoubleSplatParamTypeAnnotation
@@ -237,6 +238,54 @@ module RBS
237
238
  def type_fingerprint: () -> untyped
238
239
  end
239
240
 
241
+ # `@rbs module-self: NAME[ARGS] -- comment` annotation in leading comments
242
+ #
243
+ # ```
244
+ # @rbs module-self: _Each[String, Integer] -- comment
245
+ # ^^^^ -- prefix_location
246
+ # ^^^^^^^^^^^ -- keyword_location
247
+ # ^ -- colon_location
248
+ # ^ -- open_bracket_location
249
+ # ^ -- args_comma_locations[0]
250
+ # ^ -- close_bracket_location
251
+ # ^^^^^^^^^^ -- comment_location
252
+ # ```
253
+ #
254
+ class ModuleSelfAnnotation < Base
255
+ attr_reader keyword_location: Location
256
+
257
+ attr_reader colon_location: Location
258
+
259
+ attr_reader name: TypeName
260
+
261
+ attr_reader args: Array[Types::t]
262
+
263
+ attr_reader open_bracket_location: Location?
264
+
265
+ attr_reader close_bracket_location: Location?
266
+
267
+ attr_reader args_comma_locations: Array[Location]
268
+
269
+ attr_reader comment_location: Location?
270
+
271
+ def initialize: (
272
+ location: Location,
273
+ prefix_location: Location,
274
+ keyword_location: Location,
275
+ colon_location: Location,
276
+ name: TypeName,
277
+ args: Array[Types::t],
278
+ open_bracket_location: Location?,
279
+ close_bracket_location: Location?,
280
+ args_comma_locations: Array[Location],
281
+ comment_location: Location?,
282
+ ) -> void
283
+
284
+ def map_type_name: () { (TypeName) -> TypeName } -> self
285
+
286
+ def type_fingerprint: () -> untyped
287
+ end
288
+
240
289
  class ParamTypeAnnotation < Base
241
290
  attr_reader name_location: Location
242
291