tapioca 0.4.20 → 0.4.24

Sign up to get free protection for your applications and to get access to all the features.
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")