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 +4 -4
- data/Gemfile +13 -14
- data/exe/tapioca +2 -0
- data/lib/tapioca/compilers/dsl/active_job.rb +79 -0
- data/lib/tapioca/compilers/dsl/protobuf.rb +132 -16
- data/lib/tapioca/compilers/requires_compiler.rb +1 -1
- data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +261 -362
- data/lib/tapioca/core_ext/string.rb +18 -0
- data/lib/tapioca/gemfile.rb +24 -4
- data/lib/tapioca/generator.rb +8 -11
- data/lib/tapioca/generic_type_registry.rb +22 -19
- data/lib/tapioca/internal.rb +8 -0
- data/lib/tapioca/rbi/model.rb +405 -0
- data/lib/tapioca/rbi/printer.rb +410 -0
- data/lib/tapioca/rbi/rewriters/group_nodes.rb +106 -0
- data/lib/tapioca/rbi/rewriters/nest_non_public_methods.rb +65 -0
- data/lib/tapioca/rbi/rewriters/nest_singleton_methods.rb +42 -0
- data/lib/tapioca/rbi/rewriters/sort_nodes.rb +86 -0
- data/lib/tapioca/rbi/visitor.rb +21 -0
- data/lib/tapioca/sorbet_ext/fixed_hash_patch.rb +20 -0
- data/lib/tapioca/version.rb +1 -1
- metadata +13 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3346249d6372983bf2f589dc19126ec052dc93701d360e30d5b3f6955ece4f03
|
4
|
+
data.tar.gz: 68857527a145259323b58089e08caf23144fa9bc987de1a40b03ee4d0afa4bcb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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(
|
30
|
-
ref:
|
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:
|
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
|
-
|
79
|
-
|
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
|
-
|
87
|
-
|
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
|
-
).
|
235
|
+
).returns(Field)
|
121
236
|
end
|
122
237
|
def create_descriptor_method(klass, desc)
|
123
|
-
|
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
|
@@ -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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
43
|
-
|
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).
|
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
|
91
|
-
|
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
|
111
|
-
|
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).
|
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
|
-
|
141
|
+
tree << RBI::Const.new(name, target)
|
141
142
|
end
|
142
143
|
|
143
|
-
sig
|
144
|
-
|
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
|
-
|
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 =
|
156
|
-
|
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).
|
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
|
-
|
174
|
+
scope =
|
165
175
|
if constant.is_a?(Class)
|
166
|
-
|
176
|
+
superclass = compile_superclass(constant)
|
177
|
+
RBI::Class.new(name, superclass_name: superclass)
|
167
178
|
else
|
168
|
-
|
179
|
+
RBI::Module.new(name)
|
169
180
|
end
|
170
181
|
|
171
|
-
|
182
|
+
compile_body(scope, name, constant)
|
172
183
|
|
173
|
-
return if symbol_ignored?(name) &&
|
184
|
+
return if symbol_ignored?(name) && scope.empty?
|
174
185
|
|
175
|
-
|
176
|
-
|
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).
|
184
|
-
def compile_body(name, constant)
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
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).
|
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
|
-
|
207
|
-
|
208
|
-
|
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).
|
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
|
-
|
226
|
-
|
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
|
-
|
221
|
+
RBI::TStructProp.new(name.to_s, type, default: default)
|
229
222
|
end
|
230
|
-
end
|
223
|
+
end
|
231
224
|
end
|
232
225
|
|
233
|
-
sig { params(constant: Module).
|
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
|
-
|
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).
|
251
|
-
def compile_subconstants(name, constant)
|
252
|
-
|
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
|
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).
|
270
|
-
def compile_type_variables(constant)
|
271
|
-
|
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
|
-
|
275
|
-
|
276
|
-
|
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).
|
286
|
-
def compile_type_variable_declarations(constant)
|
287
|
-
|
288
|
-
|
289
|
-
|
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
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
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 =
|
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
|
375
|
-
return
|
337
|
+
return if superclass == ::Object || superclass == ::Delegator
|
338
|
+
return if superclass.nil?
|
376
339
|
|
377
340
|
name = name_of(superclass)
|
378
|
-
return
|
341
|
+
return if name.nil? || name.empty?
|
342
|
+
|
343
|
+
add_to_symbol_queue(name)
|
379
344
|
|
380
|
-
"
|
345
|
+
"::#{name}"
|
381
346
|
end
|
382
347
|
|
383
|
-
sig { params(constant: Module).
|
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
|
-
|
358
|
+
Module != class_of(mod) || are_equal?(mod, singleton_class)
|
394
359
|
end
|
395
360
|
|
396
|
-
|
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
|
-
|
370
|
+
qname = qualified_name_of(mod)
|
371
|
+
tree << RBI::Include.new(T.must(qname))
|
405
372
|
end
|
406
373
|
|
407
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
421
|
-
end
|
388
|
+
add_to_symbol_queue(name_of(mod))
|
422
389
|
|
423
|
-
|
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).
|
427
|
-
def compile_mixes_in_class_methods(constant)
|
428
|
-
return
|
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
|
-
|
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
|
-
|
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
|
477
|
-
mod != constant && public_module?(mod)
|
478
|
-
end
|
447
|
+
dynamic_extends.find { |mod| mod != constant }
|
479
448
|
end
|
480
449
|
|
481
|
-
return
|
450
|
+
return if mixed_in_module.nil?
|
482
451
|
|
483
452
|
qualified_name = qualified_name_of(mixed_in_module)
|
484
|
-
return
|
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).
|
495
|
-
def compile_methods(name, constant)
|
496
|
-
|
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
|
-
|
503
|
-
|
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
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
583
|
+
rbi_method << RBI::OptParam.new(name, "T.unsafe(nil)")
|
620
584
|
when :rest
|
621
|
-
|
585
|
+
rbi_method << RBI::RestParam.new(name)
|
622
586
|
when :keyreq
|
623
|
-
|
587
|
+
rbi_method << RBI::KwParam.new(name)
|
624
588
|
when :key
|
625
|
-
|
589
|
+
rbi_method << RBI::KwOptParam.new(name, "T.unsafe(nil)")
|
626
590
|
when :keyrest
|
627
|
-
|
591
|
+
rbi_method << RBI::KwRestParam.new(name)
|
628
592
|
when :block
|
629
|
-
|
593
|
+
rbi_method << RBI::BlockParam.new(name)
|
630
594
|
end
|
631
|
-
end
|
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(
|
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
|
-
|
653
|
-
type = parameter_types[name.to_sym]
|
654
|
-
"#{name}: #{type}"
|
655
|
-
end.join(", ")
|
610
|
+
sig = RBI::Sig.new
|
656
611
|
|
657
|
-
|
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
|
-
|
660
|
-
|
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
|
-
|
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
|
-
|
628
|
+
sig.is_abstract = true
|
665
629
|
when "override"
|
666
|
-
|
630
|
+
sig.is_override = true
|
667
631
|
when "overridable_override"
|
668
|
-
|
632
|
+
sig.is_overridable = true
|
633
|
+
sig.is_override = true
|
669
634
|
when "overridable"
|
670
|
-
|
671
|
-
else
|
672
|
-
""
|
635
|
+
sig.is_overridable = true
|
673
636
|
end
|
674
637
|
|
675
|
-
|
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
|
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")
|