tapioca 0.4.20 → 0.4.24

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: 567eff35f7ee24b52dafcd45ceb40ccaf042b6e0661c13cc5dcb87228c21c792
4
- data.tar.gz: 0c5143d8407d5583ccd80edbb047fb0b7500b2de8bba1717723a92f97994c045
3
+ metadata.gz: 3346249d6372983bf2f589dc19126ec052dc93701d360e30d5b3f6955ece4f03
4
+ data.tar.gz: 68857527a145259323b58089e08caf23144fa9bc987de1a40b03ee4d0afa4bcb
5
5
  SHA512:
6
- metadata.gz: d82321ab3b2516a4410825a6dfe4b253d7cccf49abc8c62db0b2a8cdbe13f064aa43df759108685ba06c36073c7a59cfda4ff361cdad1bfc736d7e557ae3ada6
7
- data.tar.gz: 0b9bd1ff91ca97cbbb1922e9915a8d046421ff40f3cd2774b4518c979f44635507b48fcce93a904770b1a65e7214a9897ce5444ce26ee6d532523a595e24ec96
6
+ metadata.gz: 5d1affdb8f87456291175700165c84a121834abb89dd2bee50fb746926d752f05f1001dda59dad7f3688b081c97d94971829ebef8ea622f05acc2d264cc69632
7
+ data.tar.gz: 7ba8a6d28322f9155dcd4351b1d68e698038ae322154d71d2fc8c26d06135f816259355584ded1f57b5b1aad96ea364b5e5fad64ddc32bcbe0561ea89b993189
data/Gemfile CHANGED
@@ -4,18 +4,18 @@ source("https://rubygems.org")
4
4
 
5
5
  gemspec
6
6
 
7
- gem 'rubocop-shopify', require: false
8
-
9
- group(:deployment, :development) do
10
- gem("rake")
11
- end
12
-
13
- gem("yard", "~> 0.9.25")
14
- gem("pry-byebug")
15
7
  gem("minitest")
16
8
  gem("minitest-hooks")
17
9
  gem("minitest-reporters")
10
+ gem("pry-byebug")
11
+ gem("rubocop-shopify", require: false)
12
+ gem("rubocop-sorbet", ">= 0.4.1")
18
13
  gem("sorbet")
14
+ gem("yard", "~> 0.9.25")
15
+
16
+ group(:deployment, :development) do
17
+ gem("rake")
18
+ end
19
19
 
20
20
  group(:development, :test) do
21
21
  gem("smart_properties", ">= 1.15.0", require: false)
@@ -26,14 +26,13 @@ group(:development, :test) do
26
26
  gem("activerecord-typedstore", "~> 1.3", require: false)
27
27
  gem("sqlite3")
28
28
  gem("identity_cache", "~> 1.0", require: false)
29
- gem('cityhash', git: 'https://github.com/csfrancis/cityhash.git',
30
- ref: '3cfc7d01f333c01811d5e834f1495eaa29f87c36', require: false)
29
+ gem("cityhash", git: "https://github.com/csfrancis/cityhash.git",
30
+ ref: "3cfc7d01f333c01811d5e834f1495eaa29f87c36", require: false)
31
31
  gem("activemodel-serializers-xml", "~> 1.0", require: false)
32
32
  gem("activeresource", "~> 5.1", require: false)
33
- gem("google-protobuf", "~>3.12.0", require: false)
33
+ gem("google-protobuf", "~> 3.12.0", require: false)
34
34
  # Fix version to 0.14.1 since it is the last version to support Ruby 2.4
35
35
  gem("shopify-money", "= 0.14.1", require: false)
36
- gem("sidekiq", "~>5.0", require: false) # Version 6 dropped support for Ruby 2.4
36
+ gem("sidekiq", "~> 5.0", require: false) # Version 6 dropped support for Ruby 2.4
37
+ gem("nokogiri", "1.10.10", require: false) # Lock to last supported for Ruby 2.4
37
38
  end
38
-
39
- gem "rubocop-sorbet", ">= 0.4.1"
data/exe/tapioca CHANGED
@@ -5,6 +5,8 @@ require 'sorbet-runtime'
5
5
 
6
6
  begin
7
7
  T::Configuration.default_checked_level = :never
8
+ # Suppresses call validation errors
9
+ T::Configuration.call_validation_error_handler = ->(*) {}
8
10
  # Suppresses errors caused by T.cast, T.let, T.must, etc.
9
11
  T::Configuration.inline_type_error_handler = ->(*) {}
10
12
  # Suppresses errors caused by incorrect parameter ordering
@@ -0,0 +1,79 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "parlour"
5
+
6
+ begin
7
+ require "active_job"
8
+ rescue LoadError
9
+ return
10
+ end
11
+
12
+ module Tapioca
13
+ module Compilers
14
+ module Dsl
15
+ # `Tapioca::Compilers::Dsl::ActiveJob` generates RBI files for subclasses of
16
+ # [`ActiveJob::Base`](https://api.rubyonrails.org/classes/ActiveJob/Base.html).
17
+ #
18
+ # For example, with the following `ActiveJob` subclass:
19
+ #
20
+ # ~~~rb
21
+ # class NotifyUserJob < ActiveJob::Base
22
+ # sig { params(user: User).returns(Mail) }
23
+ # def perform(user)
24
+ # # ...
25
+ # end
26
+ # end
27
+ # ~~~
28
+ #
29
+ # this generator will produce the RBI file `notify_user_job.rbi` with the following content:
30
+ #
31
+ # ~~~rbi
32
+ # # notify_user_job.rbi
33
+ # # typed: true
34
+ # class NotifyUserJob
35
+ # sig { params(user: User).returns(T.any(NotifyUserJob, FalseClass)) }
36
+ # def self.perform_later(user); end
37
+ #
38
+ # sig { params(user: User).returns(Mail) }
39
+ # def self.perform_now(user); end
40
+ # end
41
+ # ~~~
42
+ class ActiveJob < Base
43
+ extend T::Sig
44
+
45
+ sig { override.params(root: Parlour::RbiGenerator::Namespace, constant: T.class_of(::ActiveJob::Base)).void }
46
+ def decorate(root, constant)
47
+ root.path(constant) do |job|
48
+ next unless constant.instance_methods(false).include?(:perform)
49
+
50
+ method = constant.instance_method(:perform)
51
+ parameters = compile_method_parameters_to_parlour(method)
52
+ return_type = compile_method_return_type_to_parlour(method)
53
+
54
+ create_method(
55
+ job,
56
+ "perform_later",
57
+ parameters: parameters,
58
+ return_type: "T.any(#{constant.name}, FalseClass)",
59
+ class_method: true
60
+ )
61
+
62
+ create_method(
63
+ job,
64
+ "perform_now",
65
+ parameters: parameters,
66
+ return_type: return_type,
67
+ class_method: true
68
+ )
69
+ end
70
+ end
71
+
72
+ sig { override.returns(T::Enumerable[Module]) }
73
+ def gather_constants
74
+ ::ActiveJob::Base.descendants
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -62,33 +62,99 @@ module Tapioca
62
62
  # end
63
63
  # ~~~
64
64
  class Protobuf < Base
65
+ # Parlour doesn't support type members out of the box, so adding the
66
+ # ability to do that here. This should be upstreamed.
67
+ class TypeMember < Parlour::RbiGenerator::RbiObject
68
+ extend T::Sig
69
+
70
+ sig { params(other: Object).returns(T::Boolean) }
71
+ def ==(other)
72
+ TypeMember === other && name == other.name
73
+ end
74
+
75
+ sig do
76
+ override
77
+ .params(indent_level: Integer, options: Parlour::RbiGenerator::Options)
78
+ .returns(T::Array[String])
79
+ end
80
+ def generate_rbi(indent_level, options)
81
+ [options.indented(indent_level, "#{name} = type_member")]
82
+ end
83
+
84
+ sig do
85
+ override
86
+ .params(others: T::Array[Parlour::RbiGenerator::RbiObject])
87
+ .returns(T::Boolean)
88
+ end
89
+ def mergeable?(others)
90
+ others.all? { |other| self == other }
91
+ end
92
+
93
+ sig { override.params(others: T::Array[Parlour::RbiGenerator::RbiObject]).void }
94
+ def merge_into_self(others); end
95
+
96
+ sig { override.returns(String) }
97
+ def describe
98
+ "Type Member (#{name})"
99
+ end
100
+ end
101
+
102
+ class Field < T::Struct
103
+ prop :name, String
104
+ prop :type, String
105
+ prop :init_type, String
106
+ prop :default, String
107
+
108
+ extend T::Sig
109
+
110
+ sig { returns(Parlour::RbiGenerator::Parameter) }
111
+ def to_init
112
+ Parlour::RbiGenerator::Parameter.new("#{name}:", type: init_type, default: default)
113
+ end
114
+ end
115
+
65
116
  extend T::Sig
66
117
 
67
118
  sig do
68
119
  override.params(
69
120
  root: Parlour::RbiGenerator::Namespace,
70
- constant: T.class_of(Google::Protobuf::MessageExts)
121
+ constant: Module
71
122
  ).void
72
123
  end
73
124
  def decorate(root, constant)
74
- descriptor = T.let(T.unsafe(constant).descriptor, Google::Protobuf::Descriptor)
75
- return unless descriptor.any?
76
-
77
125
  root.path(constant) do |klass|
78
- descriptor.each do |desc|
79
- create_descriptor_method(klass, desc)
126
+ if constant == Google::Protobuf::RepeatedField
127
+ create_type_members(klass, "Elem")
128
+ elsif constant == Google::Protobuf::Map
129
+ create_type_members(klass, "Key", "Value")
130
+ else
131
+ descriptor = T.let(T.unsafe(constant).descriptor, Google::Protobuf::Descriptor)
132
+ fields = descriptor.map { |desc| create_descriptor_method(klass, desc) }
133
+ fields.sort_by!(&:name)
134
+
135
+ create_method(klass, "initialize", parameters: fields.map!(&:to_init))
80
136
  end
81
137
  end
82
138
  end
83
139
 
84
140
  sig { override.returns(T::Enumerable[Module]) }
85
141
  def gather_constants
86
- classes = T.cast(ObjectSpace.each_object(Class), T::Enumerable[Class])
87
- classes.select { |c| c < Google::Protobuf::MessageExts && !c.singleton_class? }
142
+ marker = Google::Protobuf::MessageExts::ClassMethods
143
+ results = T.cast(ObjectSpace.each_object(marker).to_a, T::Array[Module])
144
+ results.any? ? results + [Google::Protobuf::RepeatedField, Google::Protobuf::Map] : []
88
145
  end
89
146
 
90
147
  private
91
148
 
149
+ sig { params(klass: Parlour::RbiGenerator::Namespace, names: String).void }
150
+ def create_type_members(klass, *names)
151
+ klass.create_extend("T::Generic")
152
+
153
+ names.each do |name|
154
+ klass.children << TypeMember.new(klass.generator, name)
155
+ end
156
+ end
157
+
92
158
  sig do
93
159
  params(
94
160
  descriptor: Google::Protobuf::FieldDescriptor
@@ -113,30 +179,80 @@ module Tapioca
113
179
  end
114
180
  end
115
181
 
182
+ sig { params(descriptor: Google::Protobuf::FieldDescriptor).returns(Field) }
183
+ def field_of(descriptor)
184
+ if descriptor.label == :repeated
185
+ # Here we're going to check if the submsg_name is named according to
186
+ # how Google names map entries.
187
+ # https://github.com/protocolbuffers/protobuf/blob/f82e26/ruby/ext/google/protobuf_c/defs.c#L1963-L1966
188
+ if descriptor.submsg_name.to_s.end_with?("_MapEntry_#{descriptor.name}")
189
+ key = descriptor.subtype.lookup('key')
190
+ value = descriptor.subtype.lookup('value')
191
+
192
+ key_type = type_of(key)
193
+ value_type = type_of(value)
194
+ type = "Google::Protobuf::Map[#{key_type}, #{value_type}]"
195
+
196
+ default_args = [key.type.inspect, value.type.inspect]
197
+ default_args << value_type if %i[enum message].include?(value.type)
198
+
199
+ Field.new(
200
+ name: descriptor.name,
201
+ type: type,
202
+ init_type: "T.any(#{type}, T::Hash[#{key_type}, #{value_type}])",
203
+ default: "Google::Protobuf::Map.new(#{default_args.join(', ')})"
204
+ )
205
+ else
206
+ elem_type = type_of(descriptor)
207
+ type = "Google::Protobuf::RepeatedField[#{elem_type}]"
208
+
209
+ default_args = [descriptor.type.inspect]
210
+ default_args << elem_type if %i[enum message].include?(descriptor.type)
211
+
212
+ Field.new(
213
+ name: descriptor.name,
214
+ type: type,
215
+ init_type: "T.any(#{type}, T::Array[#{elem_type}])",
216
+ default: "Google::Protobuf::RepeatedField.new(#{default_args.join(', ')})"
217
+ )
218
+ end
219
+ else
220
+ type = type_of(descriptor)
221
+
222
+ Field.new(
223
+ name: descriptor.name,
224
+ type: type,
225
+ init_type: type,
226
+ default: "nil"
227
+ )
228
+ end
229
+ end
230
+
116
231
  sig do
117
232
  params(
118
233
  klass: Parlour::RbiGenerator::Namespace,
119
234
  desc: Google::Protobuf::FieldDescriptor,
120
- ).void
235
+ ).returns(Field)
121
236
  end
122
237
  def create_descriptor_method(klass, desc)
123
- name = desc.name
124
- type = type_of(desc)
238
+ field = field_of(desc)
125
239
 
126
240
  create_method(
127
241
  klass,
128
- name,
129
- return_type: type
242
+ field.name,
243
+ return_type: field.type
130
244
  )
131
245
 
132
246
  create_method(
133
247
  klass,
134
- "#{name}=",
248
+ "#{field.name}=",
135
249
  parameters: [
136
- Parlour::RbiGenerator::Parameter.new("value", type: type),
250
+ Parlour::RbiGenerator::Parameter.new("value", type: field.type),
137
251
  ],
138
- return_type: type
252
+ return_type: field.type
139
253
  )
254
+
255
+ field
140
256
  end
141
257
  end
142
258
  end
@@ -22,7 +22,7 @@ module Tapioca
22
22
  name_in_project?(files, req)
23
23
  end
24
24
  end.sort.uniq.map do |name|
25
- "require '#{name}'\n"
25
+ "require \"#{name}\"\n"
26
26
  end.join
27
27
  end
28
28
 
@@ -23,24 +23,34 @@ module Tapioca
23
23
  @indent = indent
24
24
  @seen = Set.new
25
25
  @alias_namespace ||= Set.new
26
+ @symbol_queue = T.let(symbols.sort.dup, T::Array[String])
26
27
  end
27
28
 
28
29
  sig { returns(String) }
29
30
  def generate
30
- symbols
31
- .sort
32
- .map { |symbol| generate_from_symbol(symbol) }
33
- .compact
34
- .join("\n\n")
35
- .concat("\n")
31
+ rbi = RBI::Tree.new
32
+
33
+ generate_from_symbol(rbi, T.must(@symbol_queue.shift)) until @symbol_queue.empty?
34
+
35
+ rbi.nest_singleton_methods!
36
+ rbi.nest_non_public_methods!
37
+ rbi.group_nodes!
38
+ rbi.sort_nodes!
39
+ rbi.string
36
40
  end
37
41
 
38
42
  private
39
43
 
44
+ def add_to_symbol_queue(name)
45
+ @symbol_queue << name unless symbols.include?(name) || symbol_ignored?(name)
46
+ end
47
+
40
48
  sig { returns(T::Set[String]) }
41
49
  def symbols
42
- symbols = Tapioca::Compilers::SymbolTable::SymbolLoader.list_from_paths(gem.files)
43
- symbols.union(engine_symbols(symbols))
50
+ @symbols ||= begin
51
+ symbols = Tapioca::Compilers::SymbolTable::SymbolLoader.list_from_paths(gem.files)
52
+ symbols.union(engine_symbols(symbols))
53
+ end
44
54
  end
45
55
 
46
56
  sig { params(symbols: T::Set[String]).returns(T::Set[String]) }
@@ -65,13 +75,13 @@ module Tapioca
65
75
  Set.new
66
76
  end
67
77
 
68
- sig { params(symbol: String).returns(T.nilable(String)) }
69
- def generate_from_symbol(symbol)
78
+ sig { params(tree: RBI::Tree, symbol: String).void }
79
+ def generate_from_symbol(tree, symbol)
70
80
  constant = resolve_constant(symbol)
71
81
 
72
82
  return unless constant
73
83
 
74
- compile(symbol, constant)
84
+ compile(tree, symbol, constant)
75
85
  end
76
86
 
77
87
  sig do
@@ -87,12 +97,8 @@ module Tapioca
87
97
  nil
88
98
  end
89
99
 
90
- sig do
91
- params(name: T.nilable(String), constant: BasicObject)
92
- .returns(T.nilable(String))
93
- .checked(:never)
94
- end
95
- def compile(name, constant)
100
+ sig { params(tree: RBI::Tree, name: T.nilable(String), constant: BasicObject).void.checked(:never) }
101
+ def compile(tree, name, constant)
96
102
  return unless constant
97
103
  return unless name
98
104
  return if name.strip.empty?
@@ -100,33 +106,28 @@ module Tapioca
100
106
  return if name.downcase == name
101
107
  return if alias_namespaced?(name)
102
108
  return if seen?(name)
103
- return unless parent_declares_constant?(name)
104
109
  return if T::Enum === constant # T::Enum instances are defined via `compile_enums`
105
110
 
106
111
  mark_seen(name)
107
- compile_constant(name, constant)
112
+ compile_constant(tree, name, constant)
108
113
  end
109
114
 
110
- sig do
111
- params(name: String, constant: BasicObject)
112
- .returns(T.nilable(String))
113
- .checked(:never)
114
- end
115
- def compile_constant(name, constant)
115
+ sig { params(tree: RBI::Tree, name: String, constant: BasicObject).void.checked(:never) }
116
+ def compile_constant(tree, name, constant)
116
117
  case constant
117
118
  when Module
118
119
  if name_of(constant) != name
119
- compile_alias(name, constant)
120
+ compile_alias(tree, name, constant)
120
121
  else
121
- compile_module(name, constant)
122
+ compile_module(tree, name, constant)
122
123
  end
123
124
  else
124
- compile_object(name, constant)
125
+ compile_object(tree, name, constant)
125
126
  end
126
127
  end
127
128
 
128
- sig { params(name: String, constant: Module).returns(T.nilable(String)) }
129
- def compile_alias(name, constant)
129
+ sig { params(tree: RBI::Tree, name: String, constant: Module).void }
130
+ def compile_alias(tree, name, constant)
130
131
  return if symbol_ignored?(name)
131
132
 
132
133
  target = name_of(constant)
@@ -137,119 +138,105 @@ module Tapioca
137
138
 
138
139
  return if IGNORED_SYMBOLS.include?(name)
139
140
 
140
- indented("#{name} = #{target}")
141
+ tree << RBI::Const.new(name, target)
141
142
  end
142
143
 
143
- sig do
144
- params(name: String, value: BasicObject)
145
- .returns(T.nilable(String))
146
- .checked(:never)
147
- end
148
- def compile_object(name, value)
144
+ sig { params(tree: RBI::Tree, name: String, value: BasicObject).void.checked(:never) }
145
+ def compile_object(tree, name, value)
149
146
  return if symbol_ignored?(name)
150
147
  klass = class_of(value)
151
- klass_name = name_of(klass)
148
+
149
+ klass_name = if klass == ObjectSpace::WeakMap
150
+ # WeakMap is an implicit generic with one type variable
151
+ "ObjectSpace::WeakMap[T.untyped]"
152
+ elsif T::Generic === klass
153
+ generic_name_of(klass)
154
+ else
155
+ name_of(klass)
156
+ end
157
+
158
+ if klass_name == "T::Private::Types::TypeAlias"
159
+ tree << RBI::Const.new(name, "T.type_alias { #{T.unsafe(value).aliased_type} }")
160
+ return
161
+ end
152
162
 
153
163
  return if klass_name&.start_with?("T::Types::", "T::Private::")
154
164
 
155
- type_name = public_module?(klass) && klass_name || "T.untyped"
156
- indented("#{name} = T.let(T.unsafe(nil), #{type_name})")
165
+ type_name = klass_name || "T.untyped"
166
+
167
+ tree << RBI::Const.new(name, "T.let(T.unsafe(nil), #{type_name})")
157
168
  end
158
169
 
159
- sig { params(name: String, constant: Module).returns(T.nilable(String)) }
160
- def compile_module(name, constant)
161
- return unless public_module?(constant)
170
+ sig { params(tree: RBI::Tree, name: String, constant: Module).void }
171
+ def compile_module(tree, name, constant)
162
172
  return unless defined_in_gem?(constant, strict: false)
163
173
 
164
- header =
174
+ scope =
165
175
  if constant.is_a?(Class)
166
- indented("class #{name}#{compile_superclass(constant)}")
176
+ superclass = compile_superclass(constant)
177
+ RBI::Class.new(name, superclass_name: superclass)
167
178
  else
168
- indented("module #{name}")
179
+ RBI::Module.new(name)
169
180
  end
170
181
 
171
- body = compile_body(name, constant)
182
+ compile_body(scope, name, constant)
172
183
 
173
- return if symbol_ignored?(name) && body.nil?
184
+ return if symbol_ignored?(name) && scope.empty?
174
185
 
175
- [
176
- header,
177
- body,
178
- indented("end"),
179
- compile_subconstants(name, constant),
180
- ].select { |b| !b.nil? && b.strip != "" }.join("\n")
186
+ tree << scope
187
+ compile_subconstants(tree, name, constant)
181
188
  end
182
189
 
183
- sig { params(name: String, constant: Module).returns(T.nilable(String)) }
184
- def compile_body(name, constant)
185
- with_indentation do
186
- methods = compile_methods(name, constant)
187
-
188
- return if symbol_ignored?(name) && methods.nil?
189
-
190
- [
191
- compile_module_helpers(constant),
192
- compile_type_variables(constant),
193
- compile_mixins(constant),
194
- compile_mixes_in_class_methods(constant),
195
- compile_props(constant),
196
- compile_enums(constant),
197
- methods,
198
- ].select { |b| b && !b.empty? }.join("\n\n")
199
- end
190
+ sig { params(tree: RBI::Tree, name: String, constant: Module).void }
191
+ def compile_body(tree, name, constant)
192
+ compile_methods(tree, name, constant)
193
+ compile_module_helpers(tree, constant)
194
+ compile_type_variables(tree, constant)
195
+ compile_mixins(tree, constant)
196
+ compile_mixes_in_class_methods(tree, constant)
197
+ compile_props(tree, constant)
198
+ compile_enums(tree, constant)
200
199
  end
201
200
 
202
- sig { params(constant: Module).returns(T.nilable(String)) }
203
- def compile_module_helpers(constant)
201
+ sig { params(tree: RBI::Tree, constant: Module).void }
202
+ def compile_module_helpers(tree, constant)
204
203
  abstract_type = T::Private::Abstract::Data.get(constant, :abstract_type)
205
204
 
206
- helpers = []
207
- helpers << indented("#{abstract_type}!") if abstract_type
208
- helpers << indented("final!") if T::Private::Final.final_module?(constant)
209
- helpers << indented("sealed!") if T::Private::Sealed.sealed_module?(constant)
210
-
211
- return if helpers.empty?
212
-
213
- helpers.join("\n")
205
+ tree << RBI::Helper.new(abstract_type.to_s) if abstract_type
206
+ tree << RBI::Helper.new("final") if T::Private::Final.final_module?(constant)
207
+ tree << RBI::Helper.new("sealed") if T::Private::Sealed.sealed_module?(constant)
214
208
  end
215
209
 
216
- sig { params(constant: Module).returns(T.nilable(String)) }
217
- def compile_props(constant)
210
+ sig { params(tree: RBI::Tree, constant: Module).void }
211
+ def compile_props(tree, constant)
218
212
  return unless T::Props::ClassMethods === constant
219
213
 
220
214
  constant.props.map do |name, prop|
221
- method = "prop"
222
- method = "const" if prop.fetch(:immutable, false)
223
215
  type = prop.fetch(:type_object, "T.untyped").to_s.gsub(".returns(<VOID>)", ".void")
224
216
 
225
- if prop.key?(:default)
226
- indented("#{method} :#{name}, #{type}, default: T.unsafe(nil)")
217
+ default = prop.key?(:default) ? "T.unsafe(nil)" : nil
218
+ tree << if prop.fetch(:immutable, false)
219
+ RBI::TStructConst.new(name.to_s, type, default: default)
227
220
  else
228
- indented("#{method} :#{name}, #{type}")
221
+ RBI::TStructProp.new(name.to_s, type, default: default)
229
222
  end
230
- end.join("\n")
223
+ end
231
224
  end
232
225
 
233
- sig { params(constant: Module).returns(T.nilable(String)) }
234
- def compile_enums(constant)
226
+ sig { params(tree: RBI::Tree, constant: Module).void }
227
+ def compile_enums(tree, constant)
235
228
  return unless T::Enum > constant
236
229
 
237
230
  enums = T.unsafe(constant).values.map do |enum_type|
238
231
  enum_type.instance_variable_get(:@const_name).to_s
239
232
  end
240
233
 
241
- content = [
242
- indented('enums do'),
243
- *enums.map { |e| indented(" #{e} = new") }.join("\n"),
244
- indented('end'),
245
- ]
246
-
247
- content.join("\n")
234
+ tree << RBI::TEnumBlock.new(enums)
248
235
  end
249
236
 
250
- sig { params(name: String, constant: Module).returns(T.nilable(String)) }
251
- def compile_subconstants(name, constant)
252
- output = constants_of(constant).sort.uniq.map do |constant_name|
237
+ sig { params(tree: RBI::Tree, name: String, constant: Module).void }
238
+ def compile_subconstants(tree, name, constant)
239
+ constants_of(constant).sort.uniq.map do |constant_name|
253
240
  symbol = (name == "Object" ? "" : name) + "::#{constant_name}"
254
241
  subconstant = resolve_constant(symbol)
255
242
 
@@ -258,75 +245,58 @@ module Tapioca
258
245
  next if (Object == constant || BasicObject == constant) && Module === subconstant
259
246
  next unless subconstant
260
247
 
261
- compile(symbol, subconstant)
262
- end.compact
263
-
264
- return if output.empty?
265
-
266
- "\n" + output.join("\n\n")
248
+ compile(tree, symbol, subconstant)
249
+ end
267
250
  end
268
251
 
269
- sig { params(constant: Module).returns(T.nilable(String)) }
270
- def compile_type_variables(constant)
271
- type_variables = compile_type_variable_declarations(constant)
272
- singleton_class_type_variables = compile_type_variable_declarations(singleton_class_of(constant))
252
+ sig { params(tree: RBI::Tree, constant: Module).void }
253
+ def compile_type_variables(tree, constant)
254
+ compile_type_variable_declarations(tree, constant)
273
255
 
274
- return if !type_variables && !singleton_class_type_variables
275
-
276
- type_variables += "\n" if type_variables
277
- singleton_class_type_variables += "\n" if singleton_class_type_variables
278
-
279
- [
280
- type_variables,
281
- singleton_class_type_variables,
282
- ].compact.join("\n").rstrip
256
+ sclass = RBI::SingletonClass.new
257
+ compile_type_variable_declarations(sclass, singleton_class_of(constant))
258
+ tree << sclass if sclass.nodes.length > 1
283
259
  end
284
260
 
285
- sig { params(constant: Module).returns(T.nilable(String)) }
286
- def compile_type_variable_declarations(constant)
287
- with_indentation_for_constant(constant) do
288
- # Try to find the type variables defined on this constant, bail if we can't
289
- type_variables = GenericTypeRegistry.lookup_type_variables(constant)
290
- return unless type_variables
291
-
292
- # Create a map of subconstants (via their object ids) to their names.
293
- # We need this later when we want to lookup the name of the registered type
294
- # variable via the value of the type variable constant.
295
- subconstant_to_name_lookup = constants_of(constant).map do |constant_name|
296
- [
297
- object_id_of(resolve_constant(constant_name.to_s, namespace: constant)),
298
- constant_name,
299
- ]
300
- end.to_h
301
-
302
- # Map each type variable to its string representation.
303
- #
304
- # Each entry of `type_variables` maps an object_id to a String,
305
- # and the order they are inserted into the hash is the order they should be
306
- # defined in the source code.
307
- #
308
- # By looping over these entries and then getting the actual constant name
309
- # from the `subconstant_to_name_lookup` we defined above, gives us all the
310
- # information we need to serialize type variable definitions.
311
- type_variable_declarations = type_variables.map do |type_variable_id, serialized_type_variable|
312
- constant_name = subconstant_to_name_lookup[type_variable_id]
313
- # Here, we know that constant_value will be an instance of
314
- # T::Types::CustomTypeVariable, which knows how to serialize
315
- # itself to a type_member/type_template
316
- indented("#{constant_name} = #{serialized_type_variable}")
317
- end.compact
318
-
319
- return if type_variable_declarations.empty?
261
+ sig { params(tree: RBI::Tree, constant: Module).void }
262
+ def compile_type_variable_declarations(tree, constant)
263
+ # Try to find the type variables defined on this constant, bail if we can't
264
+ type_variables = GenericTypeRegistry.lookup_type_variables(constant)
265
+ return unless type_variables
320
266
 
267
+ # Create a map of subconstants (via their object ids) to their names.
268
+ # We need this later when we want to lookup the name of the registered type
269
+ # variable via the value of the type variable constant.
270
+ subconstant_to_name_lookup = constants_of(constant).map do |constant_name|
321
271
  [
322
- indented("extend T::Generic"),
323
- "",
324
- *type_variable_declarations,
325
- ].compact.join("\n")
272
+ object_id_of(resolve_constant(constant_name.to_s, namespace: constant)),
273
+ constant_name,
274
+ ]
275
+ end.to_h
276
+
277
+ # Map each type variable to its string representation.
278
+ #
279
+ # Each entry of `type_variables` maps an object_id to a String,
280
+ # and the order they are inserted into the hash is the order they should be
281
+ # defined in the source code.
282
+ #
283
+ # By looping over these entries and then getting the actual constant name
284
+ # from the `subconstant_to_name_lookup` we defined above, gives us all the
285
+ # information we need to serialize type variable definitions.
286
+ type_variable_declarations = type_variables.map do |type_variable_id, serialized_type_variable|
287
+ constant_name = subconstant_to_name_lookup[type_variable_id]
288
+ # Here, we know that constant_value will be an instance of
289
+ # T::Types::CustomTypeVariable, which knows how to serialize
290
+ # itself to a type_member/type_template
291
+ tree << RBI::TypeMember.new(constant_name.to_s, serialized_type_variable)
326
292
  end
293
+
294
+ return if type_variable_declarations.empty?
295
+
296
+ tree << RBI::Extend.new("T::Generic")
327
297
  end
328
298
 
329
- sig { params(constant: Class).returns(String) }
299
+ sig { params(constant: Class).returns(T.nilable(String)) }
330
300
  def compile_superclass(constant)
331
301
  superclass = T.let(nil, T.nilable(Class)) # rubocop:disable Lint/UselessAssignment
332
302
 
@@ -334,15 +304,6 @@ module Tapioca
334
304
  constant_name = name_of(constant)
335
305
  constant = superclass
336
306
 
337
- # Some classes have superclasses that are private constants
338
- # so if we generate code with that superclass, the output
339
- # will not be compilable (since private constants are not
340
- # publicly visible).
341
- #
342
- # So we skip superclasses that are not public and walk up the
343
- # chain.
344
- next unless public_module?(superclass)
345
-
346
307
  # Some types have "themselves" as their superclass
347
308
  # which can happen via:
348
309
  #
@@ -362,7 +323,9 @@ module Tapioca
362
323
  # B = A
363
324
  # A.superclass.name #=> "B"
364
325
  # B #=> A
365
- superclass_name = T.must(name_of(superclass))
326
+ superclass_name = name_of(superclass)
327
+ next unless superclass_name
328
+
366
329
  resolved_superclass = resolve_constant(superclass_name)
367
330
  next unless Module === resolved_superclass
368
331
  next if name_of(resolved_superclass) == constant_name
@@ -371,17 +334,19 @@ module Tapioca
371
334
  break
372
335
  end
373
336
 
374
- return "" if superclass == ::Object || superclass == ::Delegator
375
- return "" if superclass.nil?
337
+ return if superclass == ::Object || superclass == ::Delegator
338
+ return if superclass.nil?
376
339
 
377
340
  name = name_of(superclass)
378
- return "" if name.nil? || name.empty?
341
+ return if name.nil? || name.empty?
342
+
343
+ add_to_symbol_queue(name)
379
344
 
380
- " < ::#{name}"
345
+ "::#{name}"
381
346
  end
382
347
 
383
- sig { params(constant: Module).returns(String) }
384
- def compile_mixins(constant)
348
+ sig { params(tree: RBI::Tree, constant: Module).void }
349
+ def compile_mixins(tree, constant)
385
350
  singleton_class = singleton_class_of(constant)
386
351
 
387
352
  interesting_ancestors = interesting_ancestors_of(constant)
@@ -390,42 +355,46 @@ module Tapioca
390
355
  prepend = interesting_ancestors.take_while { |c| !are_equal?(constant, c) }
391
356
  include = interesting_ancestors.drop(prepend.size + 1)
392
357
  extend = interesting_singleton_class_ancestors.reject do |mod|
393
- !public_module?(mod) || Module != class_of(mod) || are_equal?(mod, singleton_class)
358
+ Module != class_of(mod) || are_equal?(mod, singleton_class)
394
359
  end
395
360
 
396
- prepends = prepend
361
+ prepend
397
362
  .reverse
398
363
  .select { |mod| (name = name_of(mod)) && !name.start_with?("T::") }
399
- .select(&method(:public_module?))
400
364
  .map do |mod|
365
+ add_to_symbol_queue(name_of(mod))
366
+
401
367
  # TODO: Sorbet currently does not handle prepend
402
368
  # properly for method resolution, so we generate an
403
369
  # include statement instead
404
- indented("include(#{qualified_name_of(mod)})")
370
+ qname = qualified_name_of(mod)
371
+ tree << RBI::Include.new(T.must(qname))
405
372
  end
406
373
 
407
- includes = include
374
+ include
408
375
  .reverse
409
376
  .select { |mod| (name = name_of(mod)) && !name.start_with?("T::") }
410
- .select(&method(:public_module?))
411
377
  .map do |mod|
412
- indented("include(#{qualified_name_of(mod)})")
378
+ add_to_symbol_queue(name_of(mod))
379
+
380
+ qname = qualified_name_of(mod)
381
+ tree << RBI::Include.new(T.must(qname))
413
382
  end
414
383
 
415
- extends = extend
384
+ extend
416
385
  .reverse
417
386
  .select { |mod| (name = name_of(mod)) && !name.start_with?("T::") }
418
- .select(&method(:public_module?))
419
387
  .map do |mod|
420
- indented("extend(#{qualified_name_of(mod)})")
421
- end
388
+ add_to_symbol_queue(name_of(mod))
422
389
 
423
- (prepends + includes + extends).join("\n")
390
+ qname = qualified_name_of(mod)
391
+ tree << RBI::Extend.new(T.must(qname))
392
+ end
424
393
  end
425
394
 
426
- sig { params(constant: Module).returns(String) }
427
- def compile_mixes_in_class_methods(constant)
428
- return "" if constant.is_a?(Class)
395
+ sig { params(tree: RBI::Tree, constant: Module).void }
396
+ def compile_mixes_in_class_methods(tree, constant)
397
+ return if constant.is_a?(Class)
429
398
 
430
399
  mixins_from_modules = {}
431
400
 
@@ -457,11 +426,13 @@ module Tapioca
457
426
  dynamic_extends_from_dynamic_includes = mixins_from_modules.values.flatten
458
427
  dynamic_extends = all_dynamic_extends - dynamic_extends_from_dynamic_includes
459
428
 
460
- result = all_dynamic_includes
429
+ all_dynamic_includes
461
430
  .select { |mod| (name = name_of(mod)) && !name.start_with?("T::") }
462
- .select(&method(:public_module?))
463
431
  .map do |mod|
464
- indented("include(#{qualified_name_of(mod)})")
432
+ add_to_symbol_queue(name_of(mod))
433
+
434
+ qname = qualified_name_of(mod)
435
+ tree << RBI::Include.new(T.must(qname))
465
436
  end.join("\n")
466
437
 
467
438
  ancestors = singleton_class_of(constant).ancestors
@@ -473,69 +444,57 @@ module Tapioca
473
444
  mixed_in_module = if extends_as_concern && Module === class_methods_module
474
445
  class_methods_module
475
446
  else
476
- dynamic_extends.find do |mod|
477
- mod != constant && public_module?(mod)
478
- end
447
+ dynamic_extends.find { |mod| mod != constant }
479
448
  end
480
449
 
481
- return result if mixed_in_module.nil?
450
+ return if mixed_in_module.nil?
482
451
 
483
452
  qualified_name = qualified_name_of(mixed_in_module)
484
- return result if qualified_name == ""
453
+ return if qualified_name.nil? || qualified_name == ""
485
454
 
486
- [
487
- result,
488
- indented("mixes_in_class_methods(#{qualified_name})"),
489
- ].select { |b| b != "" }.join("\n\n")
455
+ tree << RBI::MixesInClassMethods.new(qualified_name)
490
456
  rescue
491
- ""
457
+ nil # silence errors
492
458
  end
493
459
 
494
- sig { params(name: String, constant: Module).returns(T.nilable(String)) }
495
- def compile_methods(name, constant)
496
- initialize_method = compile_method(
460
+ sig { params(tree: RBI::Tree, name: String, constant: Module).void }
461
+ def compile_methods(tree, name, constant)
462
+ compile_method(
463
+ tree,
497
464
  name,
498
465
  constant,
499
466
  initialize_method_for(constant)
500
467
  )
501
468
 
502
- instance_methods = compile_directly_owned_methods(name, constant)
503
- singleton_methods = compile_directly_owned_methods(name, singleton_class_of(constant))
504
-
505
- return if symbol_ignored?(name) && !instance_methods && !singleton_methods
506
-
507
- [
508
- initialize_method,
509
- instance_methods,
510
- singleton_methods,
511
- ].select { |b| b && !b.strip.empty? }.join("\n\n")
469
+ compile_directly_owned_methods(tree, name, constant)
470
+ compile_directly_owned_methods(tree, name, singleton_class_of(constant))
512
471
  end
513
472
 
514
- sig { params(module_name: String, mod: Module, for_visibility: T::Array[Symbol]).returns(T.nilable(String)) }
515
- def compile_directly_owned_methods(module_name, mod, for_visibility = [:public, :protected, :private])
516
- with_indentation_for_constant(mod) do
517
- methods = method_names_by_visibility(mod)
518
- .delete_if { |visibility, _method_list| !for_visibility.include?(visibility) }
519
- .flat_map do |visibility, method_list|
520
- compiled = method_list.sort!.map do |name|
521
- next if name == :initialize
522
- compile_method(module_name, mod, mod.instance_method(name))
523
- end
524
- compiled.compact!
525
-
526
- unless compiled.empty? || visibility == :public
527
- # add visibility badge
528
- compiled.unshift('', indented(visibility.to_s), '')
473
+ sig do
474
+ params(
475
+ tree: RBI::Tree,
476
+ module_name: String,
477
+ mod: Module,
478
+ for_visibility: T::Array[Symbol]
479
+ ).void
480
+ end
481
+ def compile_directly_owned_methods(tree, module_name, mod, for_visibility = [:public, :protected, :private])
482
+ method_names_by_visibility(mod)
483
+ .delete_if { |visibility, _method_list| !for_visibility.include?(visibility) }
484
+ .each do |visibility, method_list|
485
+ method_list.sort!.map do |name|
486
+ next if name == :initialize
487
+ vis = case visibility
488
+ when :protected
489
+ RBI::Visibility::Protected
490
+ when :private
491
+ RBI::Visibility::Private
492
+ else
493
+ RBI::Visibility::Public
529
494
  end
530
-
531
- compiled
495
+ compile_method(tree, module_name, mod, mod.instance_method(name), vis)
532
496
  end
533
- .compact
534
-
535
- return if methods.empty?
536
-
537
- methods.join("\n")
538
- end
497
+ end
539
498
  end
540
499
 
541
500
  sig { params(mod: Module).returns(T::Hash[Symbol, T::Array[Symbol]]) }
@@ -559,12 +518,14 @@ module Tapioca
559
518
 
560
519
  sig do
561
520
  params(
521
+ tree: RBI::Tree,
562
522
  symbol_name: String,
563
523
  constant: Module,
564
- method: T.nilable(UnboundMethod)
565
- ).returns(T.nilable(String))
524
+ method: T.nilable(UnboundMethod),
525
+ visibility: RBI::Visibility
526
+ ).void
566
527
  end
567
- def compile_method(symbol_name, constant, method)
528
+ def compile_method(tree, symbol_name, constant, method, visibility = RBI::Visibility::Public)
568
529
  return unless method
569
530
  return unless method.owner == constant
570
531
  return if symbol_ignored?(symbol_name) && !method_in_gem?(method)
@@ -611,37 +572,34 @@ module Tapioca
611
572
  [type, name]
612
573
  end
613
574
 
614
- parameter_list = sanitized_parameters.map do |type, name|
575
+ rbi_method = RBI::Method.new(method_name, is_singleton: constant.singleton_class?, visibility: visibility)
576
+ rbi_method.sigs << compile_signature(signature, sanitized_parameters) if signature
577
+
578
+ sanitized_parameters.each do |type, name|
615
579
  case type
616
580
  when :req
617
- name
581
+ rbi_method << RBI::Param.new(name)
618
582
  when :opt
619
- "#{name} = T.unsafe(nil)"
583
+ rbi_method << RBI::OptParam.new(name, "T.unsafe(nil)")
620
584
  when :rest
621
- "*#{name}"
585
+ rbi_method << RBI::RestParam.new(name)
622
586
  when :keyreq
623
- "#{name}:"
587
+ rbi_method << RBI::KwParam.new(name)
624
588
  when :key
625
- "#{name}: T.unsafe(nil)"
589
+ rbi_method << RBI::KwOptParam.new(name, "T.unsafe(nil)")
626
590
  when :keyrest
627
- "**#{name}"
591
+ rbi_method << RBI::KwRestParam.new(name)
628
592
  when :block
629
- "&#{name}"
593
+ rbi_method << RBI::BlockParam.new(name)
630
594
  end
631
- end.join(', ')
632
-
633
- parameter_list = "(#{parameter_list})" if parameter_list != ""
634
- signature_str = indented(compile_signature(signature, sanitized_parameters)) if signature
595
+ end
635
596
 
636
- [
637
- signature_str,
638
- indented("def #{method_name}#{parameter_list}; end"),
639
- ].compact.join("\n")
597
+ tree << rbi_method
640
598
  end
641
599
 
642
600
  TYPE_PARAMETER_MATCHER = /T\.type_parameter\(:?([[:word:]]+)\)/
643
601
 
644
- sig { params(signature: T.untyped, parameters: T::Array[[Symbol, String]]).returns(String) }
602
+ sig { params(signature: T.untyped, parameters: T::Array[[Symbol, String]]).returns(RBI::Sig) }
645
603
  def compile_signature(signature, parameters)
646
604
  parameter_types = T.let(signature.arg_types.to_h, T::Hash[Symbol, T::Types::Base])
647
605
  parameter_types.merge!(signature.kwarg_types)
@@ -649,41 +607,35 @@ module Tapioca
649
607
  parameter_types[signature.keyrest_name] = signature.keyrest_type if signature.has_keyrest
650
608
  parameter_types[signature.block_name] = signature.block_type if signature.block_name
651
609
 
652
- params = parameters.map do |_, name|
653
- type = parameter_types[name.to_sym]
654
- "#{name}: #{type}"
655
- end.join(", ")
610
+ sig = RBI::Sig.new
656
611
 
657
- returns = type_of(signature.return_type)
612
+ parameters.each do |_, name|
613
+ type = sanitize_signature_types(parameter_types[name.to_sym].to_s)
614
+ add_to_symbol_queue(type)
615
+ sig << RBI::SigParam.new(name, type)
616
+ end
658
617
 
659
- type_parameters = (params + returns).scan(TYPE_PARAMETER_MATCHER).flatten.uniq.map { |p| ":#{p}" }.join(", ")
660
- type_parameters = ".type_parameters(#{type_parameters})" unless type_parameters.empty?
618
+ return_type = type_of(signature.return_type)
619
+ sig.return_type = sanitize_signature_types(return_type)
620
+ add_to_symbol_queue(sig.return_type)
661
621
 
662
- mode = case signature.mode
622
+ parameter_types.values.join(", ").scan(TYPE_PARAMETER_MATCHER).flatten.uniq.each do |k, _|
623
+ sig.type_params << k
624
+ end
625
+
626
+ case signature.mode
663
627
  when "abstract"
664
- ".abstract"
628
+ sig.is_abstract = true
665
629
  when "override"
666
- ".override"
630
+ sig.is_override = true
667
631
  when "overridable_override"
668
- ".overridable.override"
632
+ sig.is_overridable = true
633
+ sig.is_override = true
669
634
  when "overridable"
670
- ".overridable"
671
- else
672
- ""
635
+ sig.is_overridable = true
673
636
  end
674
637
 
675
- signature_body = +""
676
- signature_body << mode
677
- signature_body << type_parameters
678
- signature_body << ".params(#{params})" unless params.empty?
679
- signature_body << ".returns(#{returns})"
680
- signature_body = signature_body
681
- .gsub(".returns(<VOID>)", ".void")
682
- .gsub("<NOT-TYPED>", "T.untyped")
683
- .gsub(".params()", "")
684
- .gsub(TYPE_PARAMETER_MATCHER, "T.type_parameter(:\\1)")[1..-1]
685
-
686
- "sig { #{signature_body} }"
638
+ sig
687
639
  end
688
640
 
689
641
  sig { params(symbol_name: String).returns(T::Boolean) }
@@ -699,54 +651,6 @@ module Tapioca
699
651
  !!name.match(/^[[:word:]]+[?!=]?$/)
700
652
  end
701
653
 
702
- sig do
703
- type_parameters(:U)
704
- .params(
705
- step: Integer,
706
- _blk: T.proc
707
- .returns(T.type_parameter(:U))
708
- )
709
- .returns(T.type_parameter(:U))
710
- end
711
- def with_indentation(step = 1, &_blk)
712
- @indent += 2 * step
713
- yield
714
- ensure
715
- @indent -= 2 * step
716
- end
717
-
718
- sig do
719
- params(
720
- constant: Module,
721
- blk: T.proc
722
- .returns(T.nilable(String))
723
- )
724
- .returns(T.nilable(String))
725
- end
726
- def with_indentation_for_constant(constant, &blk)
727
- step = if constant.singleton_class?
728
- 1
729
- else
730
- 0
731
- end
732
-
733
- result = with_indentation(step, &blk)
734
-
735
- return result unless result
736
- return result unless constant.singleton_class?
737
-
738
- [
739
- indented("class << self"),
740
- result,
741
- indented("end"),
742
- ].compact.join("\n")
743
- end
744
-
745
- sig { params(str: String).returns(String) }
746
- def indented(str)
747
- " " * @indent + str
748
- end
749
-
750
654
  sig { params(method: UnboundMethod).returns(T::Boolean) }
751
655
  def method_in_gem?(method)
752
656
  source_location = method.source_location&.first
@@ -804,33 +708,6 @@ module Tapioca
804
708
  nil
805
709
  end
806
710
 
807
- def parent_declares_constant?(name)
808
- name_parts = name.split("::")
809
-
810
- parent_name = name_parts[0...-1].join("::")
811
- parent_name = parent_name[2..-1] if parent_name.start_with?("::")
812
- parent_name = 'Object' if parent_name == ""
813
- parent = T.cast(resolve_constant(parent_name), T.nilable(Module))
814
-
815
- return false unless parent
816
-
817
- constants_of(parent).include?(name_parts.last.to_sym)
818
- end
819
-
820
- sig { params(constant: Module).returns(T::Boolean) }
821
- def public_module?(constant)
822
- constant_name = name_of(constant)
823
- return false unless constant_name
824
- return false if constant_name.start_with?('T::Private')
825
-
826
- begin
827
- # can't use !! here because the constant might override ! and mess with us
828
- Module === eval(constant_name) # rubocop:disable Security/Eval
829
- rescue NameError
830
- false
831
- end
832
- end
833
-
834
711
  sig { params(constant: BasicObject).returns(Class).checked(:never) }
835
712
  def class_of(constant)
836
713
  Kernel.instance_method(:class).bind(constant).call
@@ -903,6 +780,19 @@ module Tapioca
903
780
  name
904
781
  end
905
782
 
783
+ sig { params(constant: T.all(Module, T::Generic)).returns(String) }
784
+ def generic_name_of(constant)
785
+ type_name = T.must(constant.name)
786
+ return type_name if type_name =~ /\[.*\]$/
787
+
788
+ type_variables = Tapioca::GenericTypeRegistry.lookup_type_variables(constant)
789
+ return type_name unless type_variables
790
+
791
+ type_variable_names = type_variables.map { "T.untyped" }.join(", ")
792
+
793
+ "#{type_name}[#{type_variable_names}]"
794
+ end
795
+
906
796
  sig { params(constant: Module).returns(T.nilable(String)) }
907
797
  def name_of_proxy_target(constant)
908
798
  klass = class_of(constant)
@@ -912,7 +802,7 @@ module Tapioca
912
802
  begin
913
803
  target = Kernel.instance_method(:send).bind(constant).call(:target)
914
804
  rescue NoMethodError
915
- return nil
805
+ return
916
806
  end
917
807
 
918
808
  raw_name_of(target)
@@ -942,6 +832,15 @@ module Tapioca
942
832
  nil
943
833
  end
944
834
 
835
+ sig { params(sig_string: String).returns(String) }
836
+ def sanitize_signature_types(sig_string)
837
+ sig_string
838
+ .gsub(".returns(<VOID>)", ".void")
839
+ .gsub("<VOID>", "void")
840
+ .gsub("<NOT-TYPED>", "T.untyped")
841
+ .gsub(".params()", "")
842
+ end
843
+
945
844
  sig { params(constant: T::Types::Base).returns(String) }
946
845
  def type_of(constant)
947
846
  constant.to_s.gsub(/\bAttachedClass\b/, "T.attached_class")