tapioca 0.16.9 → 0.17.7
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/README.md +21 -0
- data/exe/tapioca +6 -1
- data/lib/ruby_lsp/tapioca/addon.rb +73 -43
- data/lib/ruby_lsp/tapioca/run_gem_rbi_check.rb +43 -43
- data/lib/ruby_lsp/tapioca/server_addon.rb +13 -10
- data/lib/tapioca/bundler_ext/auto_require_hook.rb +6 -14
- data/lib/tapioca/cli.rb +16 -8
- data/lib/tapioca/commands/abstract_dsl.rb +39 -66
- data/lib/tapioca/commands/abstract_gem.rb +25 -46
- data/lib/tapioca/commands/annotations.rb +28 -34
- data/lib/tapioca/commands/check_shims.rb +6 -15
- data/lib/tapioca/commands/command.rb +12 -26
- data/lib/tapioca/commands/command_without_tracker.rb +2 -5
- data/lib/tapioca/commands/configure.rb +11 -16
- data/lib/tapioca/commands/dsl_compiler_list.rb +2 -1
- data/lib/tapioca/commands/dsl_generate.rb +2 -1
- data/lib/tapioca/commands/dsl_verify.rb +2 -1
- data/lib/tapioca/commands/gem_generate.rb +5 -9
- data/lib/tapioca/commands/gem_sync.rb +2 -1
- data/lib/tapioca/commands/gem_verify.rb +3 -2
- data/lib/tapioca/commands/require.rb +3 -7
- data/lib/tapioca/commands/todo.rb +6 -10
- data/lib/tapioca/dsl/compiler.rb +36 -63
- data/lib/tapioca/dsl/compilers/aasm.rb +33 -44
- data/lib/tapioca/dsl/compilers/action_controller_helpers.rb +8 -7
- data/lib/tapioca/dsl/compilers/action_mailer.rb +6 -5
- data/lib/tapioca/dsl/compilers/action_text.rb +6 -5
- data/lib/tapioca/dsl/compilers/active_job.rb +6 -10
- data/lib/tapioca/dsl/compilers/active_model_attributes.rb +10 -11
- data/lib/tapioca/dsl/compilers/active_model_secure_password.rb +5 -6
- data/lib/tapioca/dsl/compilers/active_model_validations_confirmation.rb +5 -12
- data/lib/tapioca/dsl/compilers/active_record_associations.rb +17 -44
- data/lib/tapioca/dsl/compilers/active_record_columns.rb +20 -26
- data/lib/tapioca/dsl/compilers/active_record_delegated_types.rb +9 -8
- data/lib/tapioca/dsl/compilers/active_record_enum.rb +7 -6
- data/lib/tapioca/dsl/compilers/active_record_fixtures.rb +54 -62
- data/lib/tapioca/dsl/compilers/active_record_relations.rb +148 -209
- data/lib/tapioca/dsl/compilers/active_record_scope.rb +8 -13
- data/lib/tapioca/dsl/compilers/active_record_secure_token.rb +5 -4
- data/lib/tapioca/dsl/compilers/active_record_store.rb +5 -4
- data/lib/tapioca/dsl/compilers/active_record_typed_store.rb +19 -28
- data/lib/tapioca/dsl/compilers/active_resource.rb +19 -21
- data/lib/tapioca/dsl/compilers/active_storage.rb +6 -14
- data/lib/tapioca/dsl/compilers/active_support_concern.rb +9 -8
- data/lib/tapioca/dsl/compilers/active_support_current_attributes.rb +8 -7
- data/lib/tapioca/dsl/compilers/active_support_time_ext.rb +5 -4
- data/lib/tapioca/dsl/compilers/config.rb +5 -4
- data/lib/tapioca/dsl/compilers/frozen_record.rb +7 -11
- data/lib/tapioca/dsl/compilers/graphql_input_object.rb +9 -10
- data/lib/tapioca/dsl/compilers/graphql_mutation.rb +6 -10
- data/lib/tapioca/dsl/compilers/identity_cache.rb +11 -39
- data/lib/tapioca/dsl/compilers/json_api_client_resource.rb +9 -18
- data/lib/tapioca/dsl/compilers/kredis.rb +7 -8
- data/lib/tapioca/dsl/compilers/mixed_in_class_attributes.rb +5 -4
- data/lib/tapioca/dsl/compilers/protobuf.rb +13 -26
- data/lib/tapioca/dsl/compilers/rails_generators.rb +9 -11
- data/lib/tapioca/dsl/compilers/sidekiq_worker.rb +23 -13
- data/lib/tapioca/dsl/compilers/smart_properties.rb +32 -38
- data/lib/tapioca/dsl/compilers/state_machines.rb +15 -26
- data/lib/tapioca/dsl/compilers/url_helpers.rb +10 -9
- data/lib/tapioca/dsl/compilers.rb +4 -7
- data/lib/tapioca/dsl/helpers/active_model_type_helper.rb +13 -16
- data/lib/tapioca/dsl/helpers/active_record_column_type_helper.rb +13 -28
- data/lib/tapioca/dsl/helpers/active_record_constants_helper.rb +19 -15
- data/lib/tapioca/dsl/helpers/graphql_type_helper.rb +5 -24
- data/lib/tapioca/dsl/pipeline.rb +30 -58
- data/lib/tapioca/executor.rb +6 -12
- data/lib/tapioca/gem/events.rb +24 -34
- data/lib/tapioca/gem/listeners/base.rb +7 -10
- data/lib/tapioca/gem/listeners/dynamic_mixins.rb +4 -2
- data/lib/tapioca/gem/listeners/foreign_constants.rb +5 -7
- data/lib/tapioca/gem/listeners/methods.rb +36 -47
- data/lib/tapioca/gem/listeners/mixins.rb +6 -18
- data/lib/tapioca/gem/listeners/remove_empty_payload_scopes.rb +4 -2
- data/lib/tapioca/gem/listeners/sorbet_enums.rb +4 -2
- data/lib/tapioca/gem/listeners/sorbet_helpers.rb +4 -2
- data/lib/tapioca/gem/listeners/sorbet_props.rb +4 -2
- data/lib/tapioca/gem/listeners/sorbet_required_ancestors.rb +4 -2
- data/lib/tapioca/gem/listeners/sorbet_signatures.rb +7 -5
- data/lib/tapioca/gem/listeners/sorbet_type_variables.rb +6 -4
- data/lib/tapioca/gem/listeners/source_location.rb +15 -8
- data/lib/tapioca/gem/listeners/subconstants.rb +5 -4
- data/lib/tapioca/gem/listeners/yard_doc.rb +30 -23
- data/lib/tapioca/gem/pipeline.rb +107 -91
- data/lib/tapioca/gem_info.rb +1 -1
- data/lib/tapioca/gemfile.rb +64 -73
- data/lib/tapioca/helpers/cli_helper.rb +4 -7
- data/lib/tapioca/helpers/config_helper.rb +17 -29
- data/lib/tapioca/helpers/env_helper.rb +2 -5
- data/lib/tapioca/helpers/gem_helper.rb +5 -5
- data/lib/tapioca/helpers/git_attributes.rb +3 -3
- data/lib/tapioca/helpers/rbi_files_helper.rb +76 -73
- data/lib/tapioca/helpers/rbi_helper.rb +14 -22
- data/lib/tapioca/helpers/sorbet_helper.rb +9 -18
- data/lib/tapioca/helpers/source_uri.rb +15 -25
- data/lib/tapioca/helpers/test/content.rb +7 -10
- data/lib/tapioca/helpers/test/dsl_compiler.rb +20 -33
- data/lib/tapioca/helpers/test/isolation.rb +10 -14
- data/lib/tapioca/helpers/test/template.rb +6 -11
- data/lib/tapioca/internal.rb +18 -8
- data/lib/tapioca/loaders/dsl.rb +11 -19
- data/lib/tapioca/loaders/gem.rb +6 -21
- data/lib/tapioca/loaders/loader.rb +21 -39
- data/lib/tapioca/rbi_ext/model.rb +12 -37
- data/lib/tapioca/rbi_formatter.rb +10 -19
- data/lib/tapioca/rbs/rewriter.rb +55 -0
- data/lib/tapioca/repo_index.rb +7 -9
- data/lib/tapioca/runtime/attached_class_of_32.rb +1 -1
- data/lib/tapioca/runtime/attached_class_of_legacy.rb +2 -5
- data/lib/tapioca/runtime/dynamic_mixin_compiler.rb +23 -23
- data/lib/tapioca/runtime/generic_type_registry.rb +13 -23
- data/lib/tapioca/runtime/reflection.rb +81 -60
- data/lib/tapioca/runtime/source_location.rb +44 -0
- data/lib/tapioca/runtime/trackers/autoload.rb +7 -9
- data/lib/tapioca/runtime/trackers/constant_definition.rb +18 -14
- data/lib/tapioca/runtime/trackers/method_definition.rb +65 -0
- data/lib/tapioca/runtime/trackers/mixin.rb +8 -11
- data/lib/tapioca/runtime/trackers/required_ancestor.rb +3 -3
- data/lib/tapioca/runtime/trackers/tracker.rb +3 -6
- data/lib/tapioca/runtime/trackers.rb +5 -8
- data/lib/tapioca/sorbet_ext/generic_name_patch.rb +9 -15
- data/lib/tapioca/sorbet_ext/name_patch.rb +2 -2
- data/lib/tapioca/sorbet_ext/proc_bind_patch.rb +1 -1
- data/lib/tapioca/static/requires_compiler.rb +6 -6
- data/lib/tapioca/static/symbol_loader.rb +14 -16
- data/lib/tapioca/static/symbol_table_parser.rb +8 -8
- data/lib/tapioca/version.rb +1 -1
- data/lib/tapioca.rb +22 -29
- metadata +27 -10
|
@@ -2,14 +2,11 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
module Tapioca
|
|
5
|
+
# @requires_ancestor: Thor::Shell
|
|
6
|
+
# @requires_ancestor: SorbetHelper
|
|
5
7
|
module RBIFilesHelper
|
|
6
8
|
extend T::Sig
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
requires_ancestor { Thor::Shell }
|
|
10
|
-
requires_ancestor { SorbetHelper }
|
|
11
|
-
|
|
12
|
-
sig { params(index: RBI::Index, kind: String, file: String).void }
|
|
9
|
+
#: (RBI::Index index, String kind, String file) -> void
|
|
13
10
|
def index_rbi(index, kind, file)
|
|
14
11
|
return unless File.exist?(file)
|
|
15
12
|
|
|
@@ -21,7 +18,7 @@ module Tapioca
|
|
|
21
18
|
say("(#{time.round(2)}s)")
|
|
22
19
|
end
|
|
23
20
|
|
|
24
|
-
|
|
21
|
+
#: (RBI::Index index, String kind, String dir, number_of_workers: Integer?) -> void
|
|
25
22
|
def index_rbis(index, kind, dir, number_of_workers:)
|
|
26
23
|
return unless Dir.exist?(dir) && !Dir.empty?(dir)
|
|
27
24
|
|
|
@@ -38,13 +35,7 @@ module Tapioca
|
|
|
38
35
|
say("(#{time.round(2)}s)")
|
|
39
36
|
end
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
params(
|
|
43
|
-
index: RBI::Index,
|
|
44
|
-
shim_rbi_dir: String,
|
|
45
|
-
todo_rbi_file: String,
|
|
46
|
-
).returns(T::Hash[String, T::Array[RBI::Node]])
|
|
47
|
-
end
|
|
38
|
+
#: (RBI::Index index, shim_rbi_dir: String, todo_rbi_file: String) -> Hash[String, Array[RBI::Node]]
|
|
48
39
|
def duplicated_nodes_from_index(index, shim_rbi_dir:, todo_rbi_file:)
|
|
49
40
|
duplicates = {}
|
|
50
41
|
say("Looking for duplicates... ")
|
|
@@ -61,7 +52,7 @@ module Tapioca
|
|
|
61
52
|
duplicates
|
|
62
53
|
end
|
|
63
54
|
|
|
64
|
-
|
|
55
|
+
#: (RBI::Loc loc, path_prefix: String?) -> String
|
|
65
56
|
def location_to_payload_url(loc, path_prefix:)
|
|
66
57
|
return loc.to_s unless path_prefix
|
|
67
58
|
|
|
@@ -73,16 +64,7 @@ module Tapioca
|
|
|
73
64
|
url
|
|
74
65
|
end
|
|
75
66
|
|
|
76
|
-
|
|
77
|
-
params(
|
|
78
|
-
command: String,
|
|
79
|
-
gem_dir: String,
|
|
80
|
-
dsl_dir: String,
|
|
81
|
-
auto_strictness: T::Boolean,
|
|
82
|
-
gems: T::Array[Gemfile::GemSpec],
|
|
83
|
-
compilers: T::Enumerable[T.class_of(Dsl::Compiler)],
|
|
84
|
-
).void
|
|
85
|
-
end
|
|
67
|
+
#: (command: String, gem_dir: String, dsl_dir: String, auto_strictness: bool, ?gems: Array[Gemfile::GemSpec], ?compilers: T::Enumerable[singleton(Dsl::Compiler)]) -> void
|
|
86
68
|
def validate_rbi_files(command:, gem_dir:, dsl_dir:, auto_strictness:, gems: [], compilers: [])
|
|
87
69
|
error_url_base = Spoom::Sorbet::Errors::DEFAULT_ERROR_URL_BASE
|
|
88
70
|
|
|
@@ -146,12 +128,12 @@ module Tapioca
|
|
|
146
128
|
update_gem_rbis_strictnesses(redef_errors, gem_dir)
|
|
147
129
|
end
|
|
148
130
|
|
|
149
|
-
Kernel.raise
|
|
131
|
+
Kernel.raise Tapioca::Error, error_messages.join("\n") if parse_errors.any?
|
|
150
132
|
end
|
|
151
133
|
|
|
152
134
|
private
|
|
153
135
|
|
|
154
|
-
|
|
136
|
+
#: (RBI::Index index, Array[String] files, number_of_workers: Integer?) -> void
|
|
155
137
|
def parse_and_index_files(index, files, number_of_workers:)
|
|
156
138
|
executor = Executor.new(files, number_of_workers: number_of_workers)
|
|
157
139
|
|
|
@@ -167,64 +149,100 @@ module Tapioca
|
|
|
167
149
|
index.visit_all(trees)
|
|
168
150
|
end
|
|
169
151
|
|
|
170
|
-
|
|
152
|
+
# Do the list of `nodes` sharing the same name have duplicates?
|
|
153
|
+
#: (Array[RBI::Node] nodes, shim_rbi_dir: String, todo_rbi_file: String) -> bool
|
|
171
154
|
def shims_or_todos_have_duplicates?(nodes, shim_rbi_dir:, todo_rbi_file:)
|
|
155
|
+
# If there is only one node, there are no duplicates
|
|
172
156
|
return false if nodes.size == 1
|
|
173
157
|
|
|
158
|
+
# Extract the nodes from the sorbet/rbi/shims/ directory and the todo.rbi file
|
|
174
159
|
shims_or_todos = extract_shims_and_todos(nodes, shim_rbi_dir: shim_rbi_dir, todo_rbi_file: todo_rbi_file)
|
|
175
160
|
return false if shims_or_todos.empty?
|
|
176
161
|
|
|
177
|
-
|
|
178
|
-
|
|
162
|
+
# First let's look into scopes (classes, modules, sclass) for duplicates
|
|
163
|
+
has_duplicated_scopes?(nodes, shims_or_todos) ||
|
|
164
|
+
# Then let's look into mixins
|
|
165
|
+
has_duplicated_mixins?(shims_or_todos) ||
|
|
166
|
+
# Finally, let's compare the methods and attributes with the same name
|
|
167
|
+
has_duplicated_methods_and_attrs?(nodes, shims_or_todos)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
#: (Array[RBI::Node], Array[RBI::Node]) -> bool
|
|
171
|
+
def has_duplicated_scopes?(all_nodes, shims_or_todos)
|
|
172
|
+
shims_or_todos_scopes = shims_or_todos.grep(RBI::Scope)
|
|
173
|
+
return false if shims_or_todos_scopes.empty?
|
|
174
|
+
|
|
175
|
+
# Extract the empty scopes from the shims or todos
|
|
176
|
+
# We do not care about non-empty scopes because they hold definitions that we will check against Tapioca's
|
|
177
|
+
# generated RBI files in another iteration.
|
|
178
|
+
shims_or_todos_empty_scopes = shims_or_todos_scopes.select(&:empty?)
|
|
179
179
|
|
|
180
|
-
#
|
|
181
|
-
|
|
180
|
+
# Extract the nodes that are not shims or todos (basically the nodes from the RBI files generated by Tapioca)
|
|
181
|
+
not_shims_or_todos = all_nodes - shims_or_todos
|
|
182
|
+
|
|
183
|
+
shims_or_todos_empty_scopes.any? do |scope|
|
|
182
184
|
# Empty modules are always duplicates
|
|
183
|
-
|
|
185
|
+
break true unless scope.is_a?(RBI::Class)
|
|
184
186
|
|
|
185
187
|
# Empty classes without parents are also duplicates
|
|
186
188
|
parent_name = scope.superclass_name
|
|
187
|
-
|
|
189
|
+
break true unless parent_name
|
|
188
190
|
|
|
189
191
|
# Empty classes that are not redefining the parent are also duplicates
|
|
190
|
-
not_shims_or_todos.any?
|
|
192
|
+
break true if not_shims_or_todos.any? do |node|
|
|
193
|
+
node.is_a?(RBI::Class) && node.superclass_name == parent_name
|
|
194
|
+
end
|
|
191
195
|
end
|
|
192
|
-
|
|
196
|
+
end
|
|
193
197
|
|
|
194
|
-
|
|
195
|
-
|
|
198
|
+
#: (Array[RBI::Node] shims_or_todos) -> bool
|
|
199
|
+
def has_duplicated_mixins?(shims_or_todos)
|
|
200
|
+
# Don't forget `shims_or_todos` is a list of nodes with the same qualified name, so if we find two mixins of the
|
|
201
|
+
# same name, they _are_ about the same thing, like two `include(A)` or two `requires_ancestor(A)` so this is a
|
|
202
|
+
# duplicate
|
|
203
|
+
shims_or_todos.any? { |node| node.is_a?(RBI::Mixin) || node.is_a?(RBI::RequiresAncestor) }
|
|
204
|
+
end
|
|
196
205
|
|
|
197
|
-
|
|
198
|
-
|
|
206
|
+
#: (Array[RBI::Node] nodes, Array[RBI::Node] shims_or_todos) -> bool
|
|
207
|
+
def has_duplicated_methods_and_attrs?(nodes, shims_or_todos)
|
|
208
|
+
shims_or_todos_props = extract_methods_and_attrs(shims_or_todos)
|
|
209
|
+
if shims_or_todos_props.any?
|
|
210
|
+
shims_or_todos_props.each do |shim_or_todo_prop|
|
|
211
|
+
other_nodes = extract_methods_and_attrs(nodes) - [shim_or_todo_prop]
|
|
199
212
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
213
|
+
if shim_or_todo_prop.sigs.empty?
|
|
214
|
+
# If the node doesn't have a signature and is an attribute accessor, we have a duplicate
|
|
215
|
+
return true if shim_or_todo_prop.is_a?(RBI::Attr)
|
|
203
216
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
217
|
+
# Now we know it's a method
|
|
218
|
+
|
|
219
|
+
# If the node has no parameters and we compare it against an attribute of the same name, it's a duplicate
|
|
220
|
+
return true if shim_or_todo_prop.params.empty? && other_nodes.grep(RBI::Attr).any?
|
|
208
221
|
|
|
209
|
-
|
|
222
|
+
# If the node has parameters, we compare them against all the other methods
|
|
223
|
+
# If at least one of them has the same parameters, it's a duplicate
|
|
224
|
+
return true if other_nodes.grep(RBI::Method).any? { |other| shim_or_todo_prop.params == other.params }
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# We compare the shim or todo prop with all the other props of the same name
|
|
228
|
+
other_nodes.each do |node|
|
|
229
|
+
# Another prop has the same sig, we have a duplicate
|
|
230
|
+
return true if shim_or_todo_prop.sigs.any? { |sig| node.sigs.include?(sig) }
|
|
231
|
+
end
|
|
232
|
+
end
|
|
210
233
|
end
|
|
211
234
|
|
|
212
|
-
|
|
235
|
+
false
|
|
213
236
|
end
|
|
214
237
|
|
|
215
|
-
|
|
238
|
+
#: (Array[RBI::Node] nodes, shim_rbi_dir: String, todo_rbi_file: String) -> Array[RBI::Node]
|
|
216
239
|
def extract_shims_and_todos(nodes, shim_rbi_dir:, todo_rbi_file:)
|
|
217
240
|
nodes.select do |node|
|
|
218
241
|
node.loc&.file&.start_with?(shim_rbi_dir) || node.loc&.file == todo_rbi_file
|
|
219
242
|
end
|
|
220
243
|
end
|
|
221
244
|
|
|
222
|
-
|
|
223
|
-
def extract_empty_scopes(nodes)
|
|
224
|
-
T.cast(nodes.select { |node| node.is_a?(RBI::Scope) && node.empty? }, T::Array[RBI::Scope])
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
sig { params(nodes: T::Array[RBI::Node]).returns(T::Array[T.any(RBI::Method, RBI::Attr)]) }
|
|
245
|
+
#: (Array[RBI::Node] nodes) -> Array[(RBI::Method | RBI::Attr)]
|
|
228
246
|
def extract_methods_and_attrs(nodes)
|
|
229
247
|
T.cast(
|
|
230
248
|
nodes.select do |node|
|
|
@@ -234,22 +252,7 @@ module Tapioca
|
|
|
234
252
|
)
|
|
235
253
|
end
|
|
236
254
|
|
|
237
|
-
|
|
238
|
-
def extract_mixins(nodes)
|
|
239
|
-
T.cast(
|
|
240
|
-
nodes.select do |node|
|
|
241
|
-
node.is_a?(RBI::Mixin) || node.is_a?(RBI::RequiresAncestor)
|
|
242
|
-
end,
|
|
243
|
-
T::Array[T.all(RBI::Mixin, RBI::RequiresAncestor)],
|
|
244
|
-
)
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
sig { params(nodes: T::Array[T.any(RBI::Method, RBI::Attr)]).returns(T::Array[T.any(RBI::Method, RBI::Attr)]) }
|
|
248
|
-
def extract_nodes_with_sigs(nodes)
|
|
249
|
-
nodes.reject { |node| node.sigs.empty? }
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
sig { params(errors: T::Array[Spoom::Sorbet::Errors::Error], gem_dir: String).void }
|
|
255
|
+
#: (Array[Spoom::Sorbet::Errors::Error] errors, String gem_dir) -> void
|
|
253
256
|
def update_gem_rbis_strictnesses(errors, gem_dir)
|
|
254
257
|
files = []
|
|
255
258
|
|
|
@@ -276,7 +279,7 @@ module Tapioca
|
|
|
276
279
|
say("\n")
|
|
277
280
|
end
|
|
278
281
|
|
|
279
|
-
|
|
282
|
+
#: (String path) -> String
|
|
280
283
|
def gem_name_from_rbi_path(path)
|
|
281
284
|
T.must(File.basename(path, ".rbi").split("@").first)
|
|
282
285
|
end
|
|
@@ -11,15 +11,7 @@ module Tapioca
|
|
|
11
11
|
class << self
|
|
12
12
|
extend T::Sig
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
params(
|
|
16
|
-
type: String,
|
|
17
|
-
variance: Symbol,
|
|
18
|
-
fixed: T.nilable(String),
|
|
19
|
-
upper: T.nilable(String),
|
|
20
|
-
lower: T.nilable(String),
|
|
21
|
-
).returns(String)
|
|
22
|
-
end
|
|
14
|
+
#: (String type, Symbol variance, String? fixed, String? upper, String? lower) -> String
|
|
23
15
|
def serialize_type_variable(type, variance, fixed, upper, lower)
|
|
24
16
|
variance = nil if variance == :invariant
|
|
25
17
|
|
|
@@ -38,47 +30,47 @@ module Tapioca
|
|
|
38
30
|
end
|
|
39
31
|
end
|
|
40
32
|
|
|
41
|
-
|
|
33
|
+
#: (String name, type: String) -> RBI::TypedParam
|
|
42
34
|
def create_param(name, type:)
|
|
43
35
|
create_typed_param(RBI::ReqParam.new(name), type)
|
|
44
36
|
end
|
|
45
37
|
|
|
46
|
-
|
|
38
|
+
#: (String name, type: String, default: String) -> RBI::TypedParam
|
|
47
39
|
def create_opt_param(name, type:, default:)
|
|
48
40
|
create_typed_param(RBI::OptParam.new(name, default), type)
|
|
49
41
|
end
|
|
50
42
|
|
|
51
|
-
|
|
43
|
+
#: (String name, type: String) -> RBI::TypedParam
|
|
52
44
|
def create_rest_param(name, type:)
|
|
53
45
|
create_typed_param(RBI::RestParam.new(name), type)
|
|
54
46
|
end
|
|
55
47
|
|
|
56
|
-
|
|
48
|
+
#: (String name, type: String) -> RBI::TypedParam
|
|
57
49
|
def create_kw_param(name, type:)
|
|
58
50
|
create_typed_param(RBI::KwParam.new(name), type)
|
|
59
51
|
end
|
|
60
52
|
|
|
61
|
-
|
|
53
|
+
#: (String name, type: String, default: String) -> RBI::TypedParam
|
|
62
54
|
def create_kw_opt_param(name, type:, default:)
|
|
63
55
|
create_typed_param(RBI::KwOptParam.new(name, default), type)
|
|
64
56
|
end
|
|
65
57
|
|
|
66
|
-
|
|
58
|
+
#: (String name, type: String) -> RBI::TypedParam
|
|
67
59
|
def create_kw_rest_param(name, type:)
|
|
68
60
|
create_typed_param(RBI::KwRestParam.new(name), type)
|
|
69
61
|
end
|
|
70
62
|
|
|
71
|
-
|
|
63
|
+
#: (String name, type: String) -> RBI::TypedParam
|
|
72
64
|
def create_block_param(name, type:)
|
|
73
65
|
create_typed_param(RBI::BlockParam.new(name), type)
|
|
74
66
|
end
|
|
75
67
|
|
|
76
|
-
|
|
68
|
+
#: (RBI::Param param, String type) -> RBI::TypedParam
|
|
77
69
|
def create_typed_param(param, type)
|
|
78
70
|
RBI::TypedParam.new(param: param, type: sanitize_signature_types(type))
|
|
79
71
|
end
|
|
80
72
|
|
|
81
|
-
|
|
73
|
+
#: (String sig_string) -> String
|
|
82
74
|
def sanitize_signature_types(sig_string)
|
|
83
75
|
sig_string
|
|
84
76
|
.gsub(".returns(<VOID>)", ".void")
|
|
@@ -87,7 +79,7 @@ module Tapioca
|
|
|
87
79
|
.gsub(".params()", "")
|
|
88
80
|
end
|
|
89
81
|
|
|
90
|
-
|
|
82
|
+
#: (String type) -> String
|
|
91
83
|
def as_nilable_type(type)
|
|
92
84
|
if type.start_with?("T.nilable(", "::T.nilable(") || type == "T.untyped" || type == "::T.untyped"
|
|
93
85
|
type
|
|
@@ -96,7 +88,7 @@ module Tapioca
|
|
|
96
88
|
end
|
|
97
89
|
end
|
|
98
90
|
|
|
99
|
-
|
|
91
|
+
#: (String type) -> String
|
|
100
92
|
def as_non_nilable_type(type)
|
|
101
93
|
if type.match(/\A(?:::)?T.nilable\((.+)\)\z/)
|
|
102
94
|
T.must(::Regexp.last_match(1))
|
|
@@ -105,12 +97,12 @@ module Tapioca
|
|
|
105
97
|
end
|
|
106
98
|
end
|
|
107
99
|
|
|
108
|
-
|
|
100
|
+
#: (String name) -> bool
|
|
109
101
|
def valid_method_name?(name)
|
|
110
102
|
Prism.parse_success?("def self.#{name}(a); end")
|
|
111
103
|
end
|
|
112
104
|
|
|
113
|
-
|
|
105
|
+
#: (String name) -> bool
|
|
114
106
|
def valid_parameter_name?(name)
|
|
115
107
|
Prism.parse_success?("def sentinel_method_name(#{name}:); end")
|
|
116
108
|
end
|
|
@@ -5,42 +5,33 @@ module Tapioca
|
|
|
5
5
|
module SorbetHelper
|
|
6
6
|
extend T::Sig
|
|
7
7
|
|
|
8
|
-
SORBET_GEM_SPEC =
|
|
9
|
-
::Gem::Specification.find_by_name("sorbet-static"),
|
|
10
|
-
::Gem::Specification,
|
|
11
|
-
)
|
|
8
|
+
SORBET_GEM_SPEC = ::Gem::Specification.find_by_name("sorbet-static") #: ::Gem::Specification
|
|
12
9
|
|
|
13
|
-
SORBET_BIN =
|
|
14
|
-
Pathname.new(SORBET_GEM_SPEC.full_gem_path) / "libexec" / "sorbet",
|
|
15
|
-
Pathname,
|
|
16
|
-
)
|
|
10
|
+
SORBET_BIN = Pathname.new(SORBET_GEM_SPEC.full_gem_path) / "libexec" / "sorbet" #: Pathname
|
|
17
11
|
|
|
18
12
|
SORBET_EXE_PATH_ENV_VAR = "TAPIOCA_SORBET_EXE"
|
|
19
13
|
|
|
20
14
|
SORBET_PAYLOAD_URL = "https://github.com/sorbet/sorbet/tree/master/rbi"
|
|
21
15
|
|
|
22
|
-
SPOOM_CONTEXT =
|
|
16
|
+
SPOOM_CONTEXT = Spoom::Context.new(".") #: Spoom::Context
|
|
23
17
|
|
|
24
|
-
FEATURE_REQUIREMENTS =
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}.freeze,
|
|
28
|
-
T::Hash[Symbol, ::Gem::Requirement],
|
|
29
|
-
)
|
|
18
|
+
FEATURE_REQUIREMENTS = {
|
|
19
|
+
# feature_name: ::Gem::Requirement.new(">= ___"), # https://github.com/sorbet/sorbet/pull/___
|
|
20
|
+
}.freeze #: Hash[Symbol, ::Gem::Requirement]
|
|
30
21
|
|
|
31
|
-
|
|
22
|
+
#: (*String sorbet_args) -> Spoom::ExecResult
|
|
32
23
|
def sorbet(*sorbet_args)
|
|
33
24
|
SPOOM_CONTEXT.srb(sorbet_args.join(" "), sorbet_bin: sorbet_path)
|
|
34
25
|
end
|
|
35
26
|
|
|
36
|
-
|
|
27
|
+
#: -> String
|
|
37
28
|
def sorbet_path
|
|
38
29
|
sorbet_path = ENV.fetch(SORBET_EXE_PATH_ENV_VAR, SORBET_BIN)
|
|
39
30
|
sorbet_path = SORBET_BIN if sorbet_path.empty?
|
|
40
31
|
sorbet_path.to_s.shellescape
|
|
41
32
|
end
|
|
42
33
|
|
|
43
|
-
|
|
34
|
+
#: (Symbol feature, ?version: ::Gem::Version?) -> bool
|
|
44
35
|
def sorbet_supports?(feature, version: nil)
|
|
45
36
|
version = SORBET_GEM_SPEC.version unless version
|
|
46
37
|
requirement = FEATURE_REQUIREMENTS[feature]
|
|
@@ -7,38 +7,28 @@ module Tapioca
|
|
|
7
7
|
class SourceURI < URI::File
|
|
8
8
|
extend T::Sig
|
|
9
9
|
|
|
10
|
-
COMPONENT =
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
].freeze,
|
|
18
|
-
T::Array[Symbol],
|
|
19
|
-
)
|
|
10
|
+
COMPONENT = [
|
|
11
|
+
:scheme,
|
|
12
|
+
:gem_name,
|
|
13
|
+
:gem_version,
|
|
14
|
+
:path,
|
|
15
|
+
:line_number,
|
|
16
|
+
].freeze #: Array[Symbol]
|
|
20
17
|
|
|
21
18
|
# `uri` for Ruby 3.4 switched the default parser from RFC2396 to RFC3986. The new parser emits a deprecation
|
|
22
19
|
# warning on a few methods and delegates them to RFC2396, namely `extract`/`make_regexp`/`escape`/`unescape`.
|
|
23
20
|
# On earlier versions of the uri gem, the RFC2396_PARSER constant doesn't exist, so it needs some special
|
|
24
21
|
# handling to select a parser that doesn't emit deprecations. While it was backported to Ruby 3.1, users may
|
|
25
22
|
# have the uri gem in their own bundle and thus not use a compatible version.
|
|
26
|
-
PARSER =
|
|
23
|
+
PARSER = const_defined?(:RFC2396_PARSER) ? RFC2396_PARSER : DEFAULT_PARSER #: RFC2396_Parser
|
|
27
24
|
|
|
28
|
-
|
|
25
|
+
#: String?
|
|
29
26
|
attr_reader :gem_version
|
|
30
27
|
|
|
31
28
|
class << self
|
|
32
29
|
extend T::Sig
|
|
33
30
|
|
|
34
|
-
|
|
35
|
-
params(
|
|
36
|
-
gem_name: String,
|
|
37
|
-
gem_version: T.nilable(String),
|
|
38
|
-
path: String,
|
|
39
|
-
line_number: T.nilable(String),
|
|
40
|
-
).returns(T.attached_class)
|
|
41
|
-
end
|
|
31
|
+
#: (gem_name: String, gem_version: String?, path: String, line_number: String?) -> instance
|
|
42
32
|
def build(gem_name:, gem_version:, path:, line_number:)
|
|
43
33
|
super(
|
|
44
34
|
{
|
|
@@ -51,24 +41,24 @@ module Tapioca
|
|
|
51
41
|
end
|
|
52
42
|
end
|
|
53
43
|
|
|
54
|
-
|
|
44
|
+
#: -> String?
|
|
55
45
|
def gem_name
|
|
56
46
|
host
|
|
57
47
|
end
|
|
58
48
|
|
|
59
|
-
|
|
49
|
+
#: -> String?
|
|
60
50
|
def line_number
|
|
61
51
|
fragment
|
|
62
52
|
end
|
|
63
53
|
|
|
64
|
-
|
|
54
|
+
#: (String? v) -> void
|
|
65
55
|
def set_path(v) # rubocop:disable Naming/AccessorMethodName
|
|
66
56
|
return if v.nil?
|
|
67
57
|
|
|
68
58
|
@gem_version, @path = v.split("/", 2)
|
|
69
59
|
end
|
|
70
60
|
|
|
71
|
-
|
|
61
|
+
#: (String? v) -> bool
|
|
72
62
|
def check_host(v)
|
|
73
63
|
return true unless v
|
|
74
64
|
|
|
@@ -80,7 +70,7 @@ module Tapioca
|
|
|
80
70
|
true
|
|
81
71
|
end
|
|
82
72
|
|
|
83
|
-
|
|
73
|
+
#: -> String
|
|
84
74
|
def to_s
|
|
85
75
|
"source://#{gem_name}/#{gem_version}#{path}##{line_number}"
|
|
86
76
|
end
|
|
@@ -4,38 +4,35 @@
|
|
|
4
4
|
module Tapioca
|
|
5
5
|
module Helpers
|
|
6
6
|
module Test
|
|
7
|
+
# @requires_ancestor: Kernel
|
|
7
8
|
module Content
|
|
8
9
|
extend T::Sig
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
requires_ancestor { Kernel }
|
|
12
|
-
|
|
13
|
-
sig { void }
|
|
10
|
+
#: -> void
|
|
14
11
|
def teardown
|
|
15
12
|
super
|
|
16
13
|
remove_tmp_path
|
|
17
14
|
end
|
|
18
15
|
|
|
19
|
-
|
|
16
|
+
#: (*String args) -> String
|
|
20
17
|
def tmp_path(*args)
|
|
21
|
-
@tmp_path =
|
|
18
|
+
@tmp_path = @tmp_path #: String?
|
|
22
19
|
@tmp_path ||= Dir.mktmpdir
|
|
23
20
|
T.unsafe(File).join(@tmp_path, *args)
|
|
24
21
|
end
|
|
25
22
|
|
|
26
|
-
|
|
23
|
+
#: -> void
|
|
27
24
|
def remove_tmp_path
|
|
28
25
|
FileUtils.rm_rf(tmp_path)
|
|
29
26
|
end
|
|
30
27
|
|
|
31
|
-
|
|
28
|
+
#: (String name, String content, ?require_file: bool) -> String
|
|
32
29
|
def add_ruby_file(name, content, require_file: true)
|
|
33
30
|
add_content_file(name, content).tap do |file_name|
|
|
34
31
|
Tapioca.silence_warnings { require(file_name) } if require_file
|
|
35
32
|
end
|
|
36
33
|
end
|
|
37
34
|
|
|
38
|
-
|
|
35
|
+
#: (String name, String content) -> String
|
|
39
36
|
def add_content_file(name, content)
|
|
40
37
|
file_name = tmp_path("lib/#{name}")
|
|
41
38
|
raise ArgumentError, "a file named '#{name}' was already added; cannot overwrite." if File.exist?(file_name)
|
|
@@ -10,47 +10,39 @@ require "tapioca/helpers/sorbet_helper"
|
|
|
10
10
|
module Tapioca
|
|
11
11
|
module Helpers
|
|
12
12
|
module Test
|
|
13
|
+
# @requires_ancestor: Kernel
|
|
13
14
|
module DslCompiler
|
|
14
15
|
extend T::Sig
|
|
15
|
-
extend T::Helpers
|
|
16
|
-
|
|
17
16
|
include Isolation
|
|
18
17
|
include Content
|
|
19
18
|
include Template
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
sig { params(compiler_class: T.class_of(Tapioca::Dsl::Compiler)).void }
|
|
20
|
+
#: (singleton(Tapioca::Dsl::Compiler) compiler_class) -> void
|
|
24
21
|
def use_dsl_compiler(compiler_class)
|
|
25
|
-
@context =
|
|
22
|
+
@context = CompilerContext.new(compiler_class) #: CompilerContext?
|
|
26
23
|
end
|
|
27
24
|
|
|
28
|
-
|
|
25
|
+
#: (*singleton(Tapioca::Dsl::Compiler) compiler_classes) -> void
|
|
29
26
|
def activate_other_dsl_compilers(*compiler_classes)
|
|
30
27
|
context.activate_other_dsl_compilers(compiler_classes)
|
|
31
28
|
end
|
|
32
29
|
|
|
33
|
-
|
|
34
|
-
params(
|
|
35
|
-
constant_name: T.any(Symbol, String),
|
|
36
|
-
compiler_options: T::Hash[Symbol, T.untyped],
|
|
37
|
-
).returns(String)
|
|
38
|
-
end
|
|
30
|
+
#: ((Symbol | String) constant_name, ?compiler_options: Hash[Symbol, untyped]) -> String
|
|
39
31
|
def rbi_for(constant_name, compiler_options: {})
|
|
40
32
|
context.rbi_for(constant_name, compiler_options: compiler_options)
|
|
41
33
|
end
|
|
42
34
|
|
|
43
|
-
|
|
35
|
+
#: -> Array[String]
|
|
44
36
|
def gathered_constants
|
|
45
37
|
context.gathered_constants
|
|
46
38
|
end
|
|
47
39
|
|
|
48
|
-
|
|
40
|
+
#: -> Array[String]
|
|
49
41
|
def generated_errors
|
|
50
42
|
context.errors
|
|
51
43
|
end
|
|
52
44
|
|
|
53
|
-
|
|
45
|
+
#: -> CompilerContext
|
|
54
46
|
def context
|
|
55
47
|
raise "Please call `use_dsl_compiler` before" unless @context
|
|
56
48
|
|
|
@@ -62,41 +54,36 @@ module Tapioca
|
|
|
62
54
|
|
|
63
55
|
include SorbetHelper
|
|
64
56
|
|
|
65
|
-
|
|
57
|
+
#: singleton(Tapioca::Dsl::Compiler)
|
|
66
58
|
attr_reader :compiler_class
|
|
67
59
|
|
|
68
|
-
|
|
60
|
+
#: Array[singleton(Tapioca::Dsl::Compiler)]
|
|
69
61
|
attr_reader :other_compiler_classes
|
|
70
62
|
|
|
71
|
-
|
|
63
|
+
#: (singleton(Tapioca::Dsl::Compiler) compiler_class) -> void
|
|
72
64
|
def initialize(compiler_class)
|
|
73
65
|
@compiler_class = compiler_class
|
|
74
|
-
@other_compiler_classes =
|
|
75
|
-
@pipeline =
|
|
76
|
-
@errors =
|
|
66
|
+
@other_compiler_classes = [] #: Array[singleton(Tapioca::Dsl::Compiler)]
|
|
67
|
+
@pipeline = nil #: Tapioca::Dsl::Pipeline?
|
|
68
|
+
@errors = [] #: Array[String]
|
|
77
69
|
end
|
|
78
70
|
|
|
79
|
-
|
|
71
|
+
#: (Array[singleton(Tapioca::Dsl::Compiler)] compiler_classes) -> void
|
|
80
72
|
def activate_other_dsl_compilers(compiler_classes)
|
|
81
73
|
@other_compiler_classes = compiler_classes
|
|
82
74
|
end
|
|
83
75
|
|
|
84
|
-
|
|
76
|
+
#: -> Array[singleton(Tapioca::Dsl::Compiler)]
|
|
85
77
|
def activated_compiler_classes
|
|
86
78
|
[compiler_class, *other_compiler_classes]
|
|
87
79
|
end
|
|
88
80
|
|
|
89
|
-
|
|
81
|
+
#: -> Array[String]
|
|
90
82
|
def gathered_constants
|
|
91
83
|
compiler_class.processable_constants.filter_map(&:name).sort
|
|
92
84
|
end
|
|
93
85
|
|
|
94
|
-
|
|
95
|
-
params(
|
|
96
|
-
constant_name: T.any(Symbol, String),
|
|
97
|
-
compiler_options: T::Hash[Symbol, T.untyped],
|
|
98
|
-
).returns(String)
|
|
99
|
-
end
|
|
86
|
+
#: ((Symbol | String) constant_name, ?compiler_options: Hash[Symbol, untyped]) -> String
|
|
100
87
|
def rbi_for(constant_name, compiler_options: {})
|
|
101
88
|
# Make sure this is a constant that we can handle.
|
|
102
89
|
unless gathered_constants.include?(constant_name.to_s)
|
|
@@ -131,14 +118,14 @@ module Tapioca
|
|
|
131
118
|
rbi
|
|
132
119
|
end
|
|
133
120
|
|
|
134
|
-
|
|
121
|
+
#: -> Array[String]
|
|
135
122
|
def errors
|
|
136
123
|
pipeline.errors
|
|
137
124
|
end
|
|
138
125
|
|
|
139
126
|
private
|
|
140
127
|
|
|
141
|
-
|
|
128
|
+
#: -> Tapioca::Dsl::Pipeline
|
|
142
129
|
def pipeline
|
|
143
130
|
@pipeline ||= Tapioca::Dsl::Pipeline.new(
|
|
144
131
|
requested_constants: [],
|