rbs 3.2.2 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/comments.yml +1 -1
  3. data/.github/workflows/ruby.yml +7 -2
  4. data/.rubocop.yml +1 -1
  5. data/CHANGELOG.md +113 -0
  6. data/Gemfile +3 -0
  7. data/Gemfile.lock +21 -15
  8. data/README.md +11 -2
  9. data/Rakefile +10 -7
  10. data/Steepfile +7 -7
  11. data/core/basic_object.rbs +7 -7
  12. data/core/binding.rbs +3 -3
  13. data/core/builtin.rbs +171 -5
  14. data/core/constants.rbs +17 -17
  15. data/core/dir.rbs +3 -3
  16. data/core/encoding.rbs +434 -628
  17. data/core/enumerator.rbs +37 -0
  18. data/core/exception.rbs +11 -11
  19. data/core/false_class.rbs +5 -11
  20. data/core/fiber.rbs +3 -3
  21. data/core/file_test.rbs +28 -26
  22. data/core/kernel.rbs +900 -21
  23. data/core/marshal.rbs +24 -14
  24. data/core/match_data.rbs +8 -8
  25. data/core/math.rbs +57 -53
  26. data/core/method.rbs +3 -1
  27. data/core/module.rbs +38 -36
  28. data/core/nil_class.rbs +7 -13
  29. data/core/object.rbs +3 -966
  30. data/core/process.rbs +3 -3
  31. data/core/ractor.rbs +2 -2
  32. data/core/rb_config.rbs +64 -43
  33. data/core/regexp.rbs +3 -3
  34. data/core/set.rbs +3 -2
  35. data/core/signal.rbs +10 -4
  36. data/core/struct.rbs +1 -1
  37. data/core/thread.rbs +7 -7
  38. data/core/thread_group.rbs +9 -9
  39. data/core/true_class.rbs +5 -11
  40. data/core/unbound_method.rbs +56 -7
  41. data/core/warning.rbs +33 -0
  42. data/docs/collection.md +56 -6
  43. data/docs/data_and_struct.md +57 -0
  44. data/docs/stdlib.md +61 -2
  45. data/docs/syntax.md +123 -2
  46. data/ext/rbs_extension/lexer.c +624 -569
  47. data/ext/rbs_extension/lexer.h +1 -0
  48. data/ext/rbs_extension/lexer.re +1 -0
  49. data/ext/rbs_extension/lexstate.c +1 -0
  50. data/ext/rbs_extension/parser.c +6 -0
  51. data/goodcheck.yml +2 -2
  52. data/lib/rbs/annotate/formatter.rb +13 -3
  53. data/lib/rbs/annotate/rdoc_source.rb +10 -1
  54. data/lib/rbs/cli/colored_io.rb +48 -0
  55. data/lib/rbs/cli/diff.rb +80 -0
  56. data/lib/rbs/cli.rb +169 -17
  57. data/lib/rbs/collection/config/lockfile.rb +0 -25
  58. data/lib/rbs/collection/config/lockfile_generator.rb +0 -6
  59. data/lib/rbs/collection/installer.rb +1 -1
  60. data/lib/rbs/collection/sources/git.rb +6 -4
  61. data/lib/rbs/collection/sources/local.rb +7 -5
  62. data/lib/rbs/diff.rb +121 -0
  63. data/lib/rbs/environment.rb +1 -1
  64. data/lib/rbs/method_type.rb +23 -0
  65. data/lib/rbs/prototype/rb.rb +2 -9
  66. data/lib/rbs/prototype/rbi.rb +1 -1
  67. data/lib/rbs/prototype/runtime/helpers.rb +59 -0
  68. data/lib/rbs/prototype/runtime/reflection.rb +19 -0
  69. data/lib/rbs/prototype/runtime/value_object_generator.rb +275 -0
  70. data/lib/rbs/prototype/runtime.rb +233 -153
  71. data/lib/rbs/resolver/constant_resolver.rb +1 -1
  72. data/lib/rbs/sorter.rb +144 -117
  73. data/lib/rbs/test/guaranteed.rb +31 -0
  74. data/lib/rbs/test/type_check.rb +4 -4
  75. data/lib/rbs/test.rb +3 -0
  76. data/lib/rbs/types.rb +184 -3
  77. data/lib/rbs/version.rb +1 -1
  78. data/lib/rbs/writer.rb +4 -4
  79. data/lib/rbs.rb +1 -0
  80. data/rbs.gemspec +1 -0
  81. data/sig/annotate/formatter.rbs +2 -2
  82. data/sig/annotate/rdoc_annotater.rbs +1 -1
  83. data/sig/cli/colored_io.rbs +15 -0
  84. data/sig/cli/diff.rbs +21 -0
  85. data/sig/cli.rbs +2 -0
  86. data/sig/collection/config/lockfile.rbs +0 -6
  87. data/sig/diff.rbs +23 -0
  88. data/sig/errors.rbs +1 -5
  89. data/sig/method_types.rbs +6 -0
  90. data/sig/prototype/runtime.rbs +166 -0
  91. data/sig/rdoc/rbs.rbs +4 -0
  92. data/sig/shims/bundler.rbs +5 -0
  93. data/sig/sorter.rbs +23 -5
  94. data/sig/types.rbs +29 -0
  95. data/stdlib/benchmark/0/benchmark.rbs +1 -1
  96. data/stdlib/cgi/0/core.rbs +2 -2
  97. data/stdlib/did_you_mean/0/did_you_mean.rbs +2 -2
  98. data/stdlib/digest/0/digest.rbs +1 -1
  99. data/stdlib/fileutils/0/fileutils.rbs +1 -1
  100. data/stdlib/forwardable/0/forwardable.rbs +4 -4
  101. data/stdlib/io-console/0/io-console.rbs +1 -1
  102. data/stdlib/json/0/json.rbs +37 -0
  103. data/stdlib/logger/0/logger.rbs +2 -2
  104. data/stdlib/net-http/0/manifest.yaml +1 -1
  105. data/stdlib/net-http/0/net-http.rbs +16 -63
  106. data/stdlib/net-protocol/0/manifest.yaml +2 -0
  107. data/stdlib/net-protocol/0/net-protocol.rbs +56 -0
  108. data/stdlib/net-smtp/0/manifest.yaml +2 -0
  109. data/stdlib/net-smtp/0/net-smtp.rbs +55 -0
  110. data/stdlib/open-uri/0/manifest.yaml +3 -0
  111. data/stdlib/open-uri/0/open-uri.rbs +341 -0
  112. data/stdlib/openssl/0/openssl.rbs +1 -1
  113. data/stdlib/pp/0/manifest.yaml +2 -0
  114. data/stdlib/pp/0/pp.rbs +301 -0
  115. data/stdlib/{yaml → psych}/0/dbm.rbs +3 -3
  116. data/stdlib/psych/0/manifest.yaml +3 -0
  117. data/stdlib/psych/0/psych.rbs +391 -0
  118. data/stdlib/{yaml → psych}/0/store.rbs +2 -2
  119. data/stdlib/rdoc/0/code_object.rbs +55 -0
  120. data/stdlib/rdoc/0/comment.rbs +60 -0
  121. data/stdlib/rdoc/0/context.rbs +153 -0
  122. data/stdlib/rdoc/0/markup.rbs +119 -0
  123. data/stdlib/rdoc/0/parser.rbs +56 -0
  124. data/stdlib/rdoc/0/rdoc.rbs +0 -372
  125. data/stdlib/rdoc/0/ri.rbs +17 -0
  126. data/stdlib/rdoc/0/store.rbs +48 -0
  127. data/stdlib/rdoc/0/top_level.rbs +97 -0
  128. data/stdlib/socket/0/basic_socket.rbs +1 -1
  129. data/stdlib/socket/0/socket.rbs +1 -1
  130. data/stdlib/uri/0/common.rbs +1 -1
  131. data/stdlib/yaml/0/manifest.yaml +1 -2
  132. data/stdlib/yaml/0/yaml.rbs +1 -199
  133. metadata +49 -7
  134. data/sig/shims/pp.rbs +0 -3
  135. data/sig/shims.rbs +0 -47
@@ -7,7 +7,7 @@ module RBS
7
7
  include Base
8
8
 
9
9
  attr_reader :path, :full_path
10
-
10
+
11
11
  def initialize(path:, base_directory:)
12
12
  # TODO: resolve relative path from dir of rbs_collection.yaml
13
13
  @path = Pathname(path)
@@ -33,22 +33,24 @@ module RBS
33
33
  from = @full_path.join(name, version)
34
34
  gem_dir = dest.join(name, version)
35
35
 
36
+ colored_io = CLI::ColoredIO.new(stdout: stdout)
37
+
36
38
  case
37
39
  when gem_dir.symlink? && gem_dir.readlink == from
38
- stdout.puts "Using #{name}:#{version} (#{from})"
40
+ colored_io.puts "Using #{name}:#{version} (#{from})"
39
41
  when gem_dir.symlink?
40
42
  prev = gem_dir.readlink
41
43
  gem_dir.unlink
42
44
  _install(from, dest.join(name, version))
43
- stdout.puts "Updating #{name}:#{version} to #{from} from #{prev}"
45
+ colored_io.puts_green("Updating #{name}:#{version} to #{from} from #{prev}")
44
46
  when gem_dir.directory?
45
47
  # TODO: Show version of git source
46
48
  FileUtils.remove_entry_secure(gem_dir.to_s)
47
49
  _install(from, dest.join(name, version))
48
- stdout.puts "Updating #{name}:#{version} from git source"
50
+ colored_io.puts_green("Updating #{name}:#{version} from git source")
49
51
  when !gem_dir.exist?
50
52
  _install(from, dest.join(name, version))
51
- stdout.puts "Installing #{name}:#{version} (#{from})"
53
+ colored_io.puts_green("Installing #{name}:#{version} (#{from})")
52
54
  else
53
55
  raise
54
56
  end
data/lib/rbs/diff.rb ADDED
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RBS
4
+ class Diff
5
+ def initialize(type_name:, library_options:, after_path: [], before_path: [])
6
+ @type_name = type_name
7
+ @library_options = library_options
8
+ @after_path = after_path
9
+ @before_path = before_path
10
+ end
11
+
12
+ def each_diff(&block)
13
+ return to_enum(:each_diff) unless block
14
+
15
+ before_instance_methods, before_singleton_methods, before_constant_children = build_methods(@before_path)
16
+ after_instance_methods, after_singleton_methods, after_constant_children = build_methods(@after_path)
17
+
18
+ each_diff_methods(:instance, before_instance_methods, after_instance_methods, &block)
19
+ each_diff_methods(:singleton, before_singleton_methods, after_singleton_methods, &block)
20
+
21
+ each_diff_constants(before_constant_children, after_constant_children, &block)
22
+ end
23
+
24
+ private
25
+
26
+ def each_diff_methods(kind, before_methods, after_methods)
27
+ all_keys = before_methods.keys.to_set + after_methods.keys.to_set
28
+ all_keys.each do |key|
29
+ before = definition_method_to_s(key, kind, before_methods[key]) or next
30
+ after = definition_method_to_s(key, kind, after_methods[key]) or next
31
+ next if before == after
32
+
33
+ yield before, after
34
+ end
35
+ end
36
+
37
+ def each_diff_constants(before_constant_children, after_constant_children)
38
+ all_keys = before_constant_children.keys.to_set + after_constant_children.keys.to_set
39
+ all_keys.each do |key|
40
+ before = constant_to_s(before_constant_children[key]) or next
41
+ after = constant_to_s(after_constant_children[key]) or next
42
+ next if before == after
43
+
44
+ yield before, after
45
+ end
46
+ end
47
+
48
+ def build_methods(path)
49
+ env = build_env(path)
50
+ builder = build_builder(env)
51
+
52
+ instance_methods = begin
53
+ builder.build_instance(@type_name).methods
54
+ rescue => e
55
+ RBS.logger.warn("#{path}: (#{e.class}) #{e.message}")
56
+ {}
57
+ end
58
+ singleton_methods = begin
59
+ builder.build_singleton(@type_name).methods
60
+ rescue => e
61
+ RBS.logger.warn("#{path}: (#{e.class}) #{e.message}")
62
+ {}
63
+ end
64
+
65
+ constant_children = begin
66
+ constant_resolver = RBS::Resolver::ConstantResolver.new(builder: builder)
67
+ constant_resolver.children(@type_name)
68
+ rescue => e
69
+ RBS.logger.warn("#{path}: (#{e.class}) #{e.message}")
70
+ {}
71
+ end
72
+
73
+ [ instance_methods, singleton_methods, constant_children ]
74
+ end
75
+
76
+ def build_env(path)
77
+ loader = @library_options.loader()
78
+ path&.each do |dir|
79
+ dir_pathname = Pathname(dir)
80
+ loader.add(path: dir_pathname)
81
+
82
+ manifest_pathname = dir_pathname / 'manifest.yaml'
83
+ if manifest_pathname.exist?
84
+ manifest = YAML.safe_load(manifest_pathname.read)
85
+ if manifest['dependencies']
86
+ manifest['dependencies'].each do |dependency|
87
+ loader.add(library: dependency['name'], version: nil)
88
+ end
89
+ end
90
+ end
91
+ end
92
+ Environment.from_loader(loader)
93
+ end
94
+
95
+ def build_builder(env)
96
+ DefinitionBuilder.new(env: env.resolve_type_names)
97
+ end
98
+
99
+ def definition_method_to_s(key, kind, definition_method)
100
+ if definition_method
101
+ prefix = kind == :instance ? "" : "self."
102
+
103
+ if definition_method.alias_of
104
+ "alias #{prefix}#{key} #{prefix}#{definition_method.alias_of.defs.first.member.name}"
105
+ else
106
+ "def #{prefix}#{key}: #{definition_method.method_types.join(" | ")}"
107
+ end
108
+ else
109
+ +"-"
110
+ end
111
+ end
112
+
113
+ def constant_to_s(constant)
114
+ if constant
115
+ "#{constant.name.name}: #{constant.type}"
116
+ else
117
+ +"-"
118
+ end
119
+ end
120
+ end
121
+ end
@@ -441,7 +441,7 @@ module RBS
441
441
 
442
442
  when AST::Declarations::Global
443
443
  if entry = global_decls[decl.name]
444
- raise DuplicatedDeclarationError.new(name, decl, entry.decl)
444
+ raise DuplicatedDeclarationError.new(decl.name, decl, entry.decl)
445
445
  end
446
446
 
447
447
  global_decls[decl.name] = GlobalEntry.new(name: decl.name, decl: decl, outer: outer)
@@ -86,6 +86,9 @@ module RBS
86
86
  type.each_type(&block)
87
87
  self.block&.yield_self do |b|
88
88
  b.type.each_type(&block)
89
+ if b.self_type
90
+ yield b.self_type
91
+ end
89
92
  end
90
93
  else
91
94
  enum_for :each_type
@@ -114,5 +117,25 @@ module RBS
114
117
  def type_param_names
115
118
  type_params.map(&:name)
116
119
  end
120
+
121
+ def has_self_type?
122
+ each_type.any? {|type| type.has_self_type? }
123
+ end
124
+
125
+ def has_classish_type?
126
+ each_type.any? {|type| type.has_classish_type? }
127
+ end
128
+
129
+ def with_nonreturn_void?
130
+ if type.with_nonreturn_void?
131
+ true
132
+ else
133
+ if block = block()
134
+ block.type.with_nonreturn_void? || block.self_type&.with_nonreturn_void? || false
135
+ else
136
+ false
137
+ end
138
+ end
139
+ end
117
140
  end
118
141
  end
@@ -89,8 +89,7 @@ module RBS
89
89
  body = "\n" if body.empty?
90
90
 
91
91
  comment = AST::Comment.new(string: body, location: nil)
92
- if (prev_comment = hash[line - 1])
93
- hash[line - 1] = nil
92
+ if prev_comment = hash.delete(line - 1)
94
93
  hash[line] = AST::Comment.new(string: prev_comment.string + comment.string,
95
94
  location: nil)
96
95
  else
@@ -362,13 +361,7 @@ module RBS
362
361
  end
363
362
 
364
363
  when :ITER
365
- method_name = node.children.first.children.first
366
- case method_name
367
- when :refine
368
- # ignore
369
- else
370
- process_children(node, decls: decls, comments: comments, context: context)
371
- end
364
+ # ignore
372
365
 
373
366
  when :CDECL
374
367
  const_name = case
@@ -246,7 +246,7 @@ module RBS
246
246
  end
247
247
  end
248
248
  value_node = node.children.last
249
- type = if value_node.type == :CALL && value_node.children[1] == :let
249
+ type = if value_node && value_node.type == :CALL && value_node.children[1] == :let
250
250
  type_node = each_arg(value_node.children[2]).to_a[1]
251
251
  type_of type_node, variables: current_module&.type_params || []
252
252
  else
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RBS
4
+ module Prototype
5
+ class Runtime
6
+ module Helpers
7
+ private
8
+
9
+ # Returns the exact name & not compactly declared name
10
+ def only_name(mod)
11
+ # No nil check because this method is invoked after checking if the module exists
12
+ const_name!(mod).split(/::/).last or raise # (A::B::C) => C
13
+ end
14
+
15
+ def const_name!(const)
16
+ const_name(const) or raise
17
+ end
18
+
19
+ def const_name(const)
20
+ @module_name_method ||= Module.instance_method(:name)
21
+ name = @module_name_method.bind(const).call
22
+ return nil unless name
23
+
24
+ begin
25
+ deprecated, Warning[:deprecated] = Warning[:deprecated], false
26
+ Object.const_get(name)
27
+ rescue NameError
28
+ # Should generate const name if anonymous or internal module (e.g. NameError::message)
29
+ nil
30
+ else
31
+ name
32
+ ensure
33
+ Warning[:deprecated] = deprecated
34
+ end
35
+ end
36
+
37
+ def to_type_name(name, full_name: false)
38
+ *prefix, last = name.split(/::/)
39
+
40
+ last or raise
41
+
42
+ if full_name
43
+ if prefix.empty?
44
+ TypeName.new(name: last.to_sym, namespace: Namespace.empty)
45
+ else
46
+ TypeName.new(name: last.to_sym, namespace: Namespace.parse(prefix.join("::")))
47
+ end
48
+ else
49
+ TypeName.new(name: last.to_sym, namespace: Namespace.empty)
50
+ end
51
+ end
52
+
53
+ def untyped
54
+ @untyped ||= Types::Bases::Any.new(location: nil)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RBS
4
+ module Prototype
5
+ class Runtime
6
+ module Reflection
7
+ def self.object_class(value)
8
+ @object_class ||= Object.instance_method(:class)
9
+ @object_class.bind_call(value)
10
+ end
11
+
12
+ def self.constants_of(mod, inherit = true)
13
+ @constants_of ||= Module.instance_method(:constants)
14
+ @constants_of.bind_call(mod, inherit)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,275 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helpers'
4
+
5
+ module RBS
6
+ module Prototype
7
+ class Runtime
8
+ class ValueObjectBase
9
+ include Helpers
10
+
11
+ def initialize(target_class)
12
+ @target_class = target_class
13
+ end
14
+
15
+ def build_decl
16
+ decl = AST::Declarations::Class.new(
17
+ name: to_type_name(only_name(@target_class)),
18
+ type_params: [],
19
+ super_class: build_super_class,
20
+ members: [],
21
+ annotations: [],
22
+ location: nil,
23
+ comment: nil
24
+ )
25
+
26
+ add_decl_members(decl)
27
+
28
+ decl
29
+ end
30
+
31
+ private
32
+
33
+ # def self.members: () -> [ :foo, :bar ]
34
+ # def members: () -> [ :foo, :bar ]
35
+ def build_s_members
36
+ [:singleton, :instance].map do |kind|
37
+ AST::Members::MethodDefinition.new(
38
+ name: :members,
39
+ overloads: [
40
+ AST::Members::MethodDefinition::Overload.new(
41
+ annotations: [],
42
+ method_type: MethodType.new(
43
+ type: Types::Function.empty(
44
+ Types::Tuple.new(
45
+ types: @target_class.members.map do |member|
46
+ if member.to_s.ascii_only?
47
+ Types::Literal.new(literal: member, location: nil)
48
+ else
49
+ BuiltinNames::Symbol.instance_type
50
+ end
51
+ end,
52
+ location: nil
53
+ )
54
+ ),
55
+ type_params: [],
56
+ block: nil,
57
+ location: nil,
58
+ )
59
+ )
60
+ ],
61
+ kind: kind,
62
+ location: nil,
63
+ comment: nil,
64
+ annotations: [],
65
+ overloading: false,
66
+ visibility: nil
67
+ )
68
+ end
69
+ end
70
+
71
+ # attr_accessor foo: untyped
72
+ def build_member_accessors(ast_members_class)
73
+ @target_class.members.map do |member|
74
+ ast_members_class.new(
75
+ name: member,
76
+ ivar_name: nil,
77
+ type: untyped,
78
+ kind: :instance,
79
+ location: nil,
80
+ comment: nil,
81
+ annotations: []
82
+ )
83
+ end
84
+ end
85
+ end
86
+
87
+ class StructGenerator < ValueObjectBase
88
+ def self.generatable?(target)
89
+ return false unless target < Struct
90
+ # Avoid direct inherited class like `class Option < Struct`
91
+ return false unless target.respond_to?(:members)
92
+
93
+ true
94
+ end
95
+
96
+ private
97
+
98
+ CAN_CALL_KEYWORD_INIT_P = Struct.new(:tmp).respond_to?(:keyword_init?)
99
+
100
+ def build_super_class
101
+ AST::Declarations::Class::Super.new(name: TypeName("::Struct"), args: [untyped], location: nil)
102
+ end
103
+
104
+ def add_decl_members(decl)
105
+ decl.members.concat build_s_new
106
+ decl.members.concat build_s_keyword_init_p
107
+ decl.members.concat build_s_members
108
+ decl.members.concat build_member_accessors(AST::Members::AttrAccessor)
109
+ end
110
+
111
+ # def self.new: (?untyped foo, ?untyped bar) -> instance
112
+ # | (?foo: untyped, ?bar: untyped) -> instance
113
+ def build_s_new
114
+ [:new, :[]].map do |name|
115
+ new_overloads = []
116
+
117
+ if CAN_CALL_KEYWORD_INIT_P
118
+ case @target_class.keyword_init?
119
+ when false
120
+ new_overloads << build_overload_for_positional_arguments
121
+ when true
122
+ new_overloads << build_overload_for_keyword_arguments
123
+ when nil
124
+ new_overloads << build_overload_for_positional_arguments
125
+ new_overloads << build_overload_for_keyword_arguments
126
+ else
127
+ raise
128
+ end
129
+ else
130
+ new_overloads << build_overload_for_positional_arguments
131
+ new_overloads << build_overload_for_keyword_arguments
132
+ end
133
+
134
+ AST::Members::MethodDefinition.new(
135
+ name: name,
136
+ overloads: new_overloads,
137
+ kind: :singleton,
138
+ location: nil,
139
+ comment: nil,
140
+ annotations: [],
141
+ overloading: false,
142
+ visibility: nil
143
+ )
144
+ end
145
+ end
146
+
147
+ def build_overload_for_positional_arguments
148
+ AST::Members::MethodDefinition::Overload.new(
149
+ annotations: [],
150
+ method_type: MethodType.new(
151
+ type: Types::Function.empty(Types::Bases::Instance.new(location: nil)).update(
152
+ optional_positionals: @target_class.members.map { |m| Types::Function::Param.new(name: m, type: untyped) },
153
+ ),
154
+ type_params: [],
155
+ block: nil,
156
+ location: nil,
157
+ )
158
+ )
159
+ end
160
+
161
+ def build_overload_for_keyword_arguments
162
+ AST::Members::MethodDefinition::Overload.new(
163
+ annotations: [],
164
+ method_type: MethodType.new(
165
+ type: Types::Function.empty(Types::Bases::Instance.new(location: nil)).update(
166
+ optional_keywords: @target_class.members.to_h { |m| [m, Types::Function::Param.new(name: nil, type: untyped)] },
167
+ ),
168
+ type_params: [],
169
+ block: nil,
170
+ location: nil,
171
+ )
172
+ )
173
+ end
174
+
175
+ # def self.keyword_init?: () -> bool?
176
+ def build_s_keyword_init_p
177
+ return [] unless CAN_CALL_KEYWORD_INIT_P
178
+
179
+ return_type = @target_class.keyword_init?.nil? \
180
+ ? Types::Bases::Nil.new(location: nil)
181
+ : Types::Literal.new(literal: @target_class.keyword_init?, location: nil)
182
+ type = Types::Function.empty(return_type)
183
+
184
+ [
185
+ AST::Members::MethodDefinition.new(
186
+ name: :keyword_init?,
187
+ overloads: [
188
+ AST::Members::MethodDefinition::Overload.new(
189
+ annotations: [],
190
+ method_type: MethodType.new(
191
+ type: type,
192
+ type_params: [],
193
+ block: nil,
194
+ location: nil,
195
+ )
196
+ )
197
+ ],
198
+ kind: :singleton,
199
+ location: nil,
200
+ comment: nil,
201
+ annotations: [],
202
+ overloading: false,
203
+ visibility: nil
204
+ )
205
+ ]
206
+ end
207
+ end
208
+
209
+ class DataGenerator < ValueObjectBase
210
+ def self.generatable?(target)
211
+ return false unless RUBY_VERSION >= '3.2'
212
+ return false unless target < Data
213
+ # Avoid direct inherited class like `class Option < Data`
214
+ return false unless target.respond_to?(:members)
215
+
216
+ true
217
+ end
218
+
219
+ private
220
+
221
+ def build_super_class
222
+ AST::Declarations::Class::Super.new(name: TypeName("::Data"), args: [], location: nil)
223
+ end
224
+
225
+ def add_decl_members(decl)
226
+ decl.members.concat build_s_new
227
+ decl.members.concat build_s_members
228
+ decl.members.concat build_member_accessors(AST::Members::AttrReader)
229
+ end
230
+
231
+ # def self.new: (untyped foo, untyped bar) -> instance
232
+ # | (foo: untyped, bar: untyped) -> instance
233
+ def build_s_new
234
+ [:new, :[]].map do |name|
235
+ new_overloads = []
236
+
237
+ new_overloads << AST::Members::MethodDefinition::Overload.new(
238
+ annotations: [],
239
+ method_type: MethodType.new(
240
+ type: Types::Function.empty(Types::Bases::Instance.new(location: nil)).update(
241
+ required_positionals: @target_class.members.map { |m| Types::Function::Param.new(name: m, type: untyped) },
242
+ ),
243
+ type_params: [],
244
+ block: nil,
245
+ location: nil,
246
+ )
247
+ )
248
+ new_overloads << AST::Members::MethodDefinition::Overload.new(
249
+ annotations: [],
250
+ method_type: MethodType.new(
251
+ type: Types::Function.empty(Types::Bases::Instance.new(location: nil)).update(
252
+ required_keywords: @target_class.members.to_h { |m| [m, Types::Function::Param.new(name: nil, type: untyped)] },
253
+ ),
254
+ type_params: [],
255
+ block: nil,
256
+ location: nil,
257
+ )
258
+ )
259
+
260
+ AST::Members::MethodDefinition.new(
261
+ name: name,
262
+ overloads: new_overloads,
263
+ kind: :singleton,
264
+ location: nil,
265
+ comment: nil,
266
+ annotations: [],
267
+ overloading: false,
268
+ visibility: nil
269
+ )
270
+ end
271
+ end
272
+ end
273
+ end
274
+ end
275
+ end