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.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +21 -0
  3. data/exe/tapioca +6 -1
  4. data/lib/ruby_lsp/tapioca/addon.rb +73 -43
  5. data/lib/ruby_lsp/tapioca/run_gem_rbi_check.rb +43 -43
  6. data/lib/ruby_lsp/tapioca/server_addon.rb +13 -10
  7. data/lib/tapioca/bundler_ext/auto_require_hook.rb +6 -14
  8. data/lib/tapioca/cli.rb +16 -8
  9. data/lib/tapioca/commands/abstract_dsl.rb +39 -66
  10. data/lib/tapioca/commands/abstract_gem.rb +25 -46
  11. data/lib/tapioca/commands/annotations.rb +28 -34
  12. data/lib/tapioca/commands/check_shims.rb +6 -15
  13. data/lib/tapioca/commands/command.rb +12 -26
  14. data/lib/tapioca/commands/command_without_tracker.rb +2 -5
  15. data/lib/tapioca/commands/configure.rb +11 -16
  16. data/lib/tapioca/commands/dsl_compiler_list.rb +2 -1
  17. data/lib/tapioca/commands/dsl_generate.rb +2 -1
  18. data/lib/tapioca/commands/dsl_verify.rb +2 -1
  19. data/lib/tapioca/commands/gem_generate.rb +5 -9
  20. data/lib/tapioca/commands/gem_sync.rb +2 -1
  21. data/lib/tapioca/commands/gem_verify.rb +3 -2
  22. data/lib/tapioca/commands/require.rb +3 -7
  23. data/lib/tapioca/commands/todo.rb +6 -10
  24. data/lib/tapioca/dsl/compiler.rb +36 -63
  25. data/lib/tapioca/dsl/compilers/aasm.rb +33 -44
  26. data/lib/tapioca/dsl/compilers/action_controller_helpers.rb +8 -7
  27. data/lib/tapioca/dsl/compilers/action_mailer.rb +6 -5
  28. data/lib/tapioca/dsl/compilers/action_text.rb +6 -5
  29. data/lib/tapioca/dsl/compilers/active_job.rb +6 -10
  30. data/lib/tapioca/dsl/compilers/active_model_attributes.rb +10 -11
  31. data/lib/tapioca/dsl/compilers/active_model_secure_password.rb +5 -6
  32. data/lib/tapioca/dsl/compilers/active_model_validations_confirmation.rb +5 -12
  33. data/lib/tapioca/dsl/compilers/active_record_associations.rb +17 -44
  34. data/lib/tapioca/dsl/compilers/active_record_columns.rb +20 -26
  35. data/lib/tapioca/dsl/compilers/active_record_delegated_types.rb +9 -8
  36. data/lib/tapioca/dsl/compilers/active_record_enum.rb +7 -6
  37. data/lib/tapioca/dsl/compilers/active_record_fixtures.rb +54 -62
  38. data/lib/tapioca/dsl/compilers/active_record_relations.rb +148 -209
  39. data/lib/tapioca/dsl/compilers/active_record_scope.rb +8 -13
  40. data/lib/tapioca/dsl/compilers/active_record_secure_token.rb +5 -4
  41. data/lib/tapioca/dsl/compilers/active_record_store.rb +5 -4
  42. data/lib/tapioca/dsl/compilers/active_record_typed_store.rb +19 -28
  43. data/lib/tapioca/dsl/compilers/active_resource.rb +19 -21
  44. data/lib/tapioca/dsl/compilers/active_storage.rb +6 -14
  45. data/lib/tapioca/dsl/compilers/active_support_concern.rb +9 -8
  46. data/lib/tapioca/dsl/compilers/active_support_current_attributes.rb +8 -7
  47. data/lib/tapioca/dsl/compilers/active_support_time_ext.rb +5 -4
  48. data/lib/tapioca/dsl/compilers/config.rb +5 -4
  49. data/lib/tapioca/dsl/compilers/frozen_record.rb +7 -11
  50. data/lib/tapioca/dsl/compilers/graphql_input_object.rb +9 -10
  51. data/lib/tapioca/dsl/compilers/graphql_mutation.rb +6 -10
  52. data/lib/tapioca/dsl/compilers/identity_cache.rb +11 -39
  53. data/lib/tapioca/dsl/compilers/json_api_client_resource.rb +9 -18
  54. data/lib/tapioca/dsl/compilers/kredis.rb +7 -8
  55. data/lib/tapioca/dsl/compilers/mixed_in_class_attributes.rb +5 -4
  56. data/lib/tapioca/dsl/compilers/protobuf.rb +13 -26
  57. data/lib/tapioca/dsl/compilers/rails_generators.rb +9 -11
  58. data/lib/tapioca/dsl/compilers/sidekiq_worker.rb +23 -13
  59. data/lib/tapioca/dsl/compilers/smart_properties.rb +32 -38
  60. data/lib/tapioca/dsl/compilers/state_machines.rb +15 -26
  61. data/lib/tapioca/dsl/compilers/url_helpers.rb +10 -9
  62. data/lib/tapioca/dsl/compilers.rb +4 -7
  63. data/lib/tapioca/dsl/helpers/active_model_type_helper.rb +13 -16
  64. data/lib/tapioca/dsl/helpers/active_record_column_type_helper.rb +13 -28
  65. data/lib/tapioca/dsl/helpers/active_record_constants_helper.rb +19 -15
  66. data/lib/tapioca/dsl/helpers/graphql_type_helper.rb +5 -24
  67. data/lib/tapioca/dsl/pipeline.rb +30 -58
  68. data/lib/tapioca/executor.rb +6 -12
  69. data/lib/tapioca/gem/events.rb +24 -34
  70. data/lib/tapioca/gem/listeners/base.rb +7 -10
  71. data/lib/tapioca/gem/listeners/dynamic_mixins.rb +4 -2
  72. data/lib/tapioca/gem/listeners/foreign_constants.rb +5 -7
  73. data/lib/tapioca/gem/listeners/methods.rb +36 -47
  74. data/lib/tapioca/gem/listeners/mixins.rb +6 -18
  75. data/lib/tapioca/gem/listeners/remove_empty_payload_scopes.rb +4 -2
  76. data/lib/tapioca/gem/listeners/sorbet_enums.rb +4 -2
  77. data/lib/tapioca/gem/listeners/sorbet_helpers.rb +4 -2
  78. data/lib/tapioca/gem/listeners/sorbet_props.rb +4 -2
  79. data/lib/tapioca/gem/listeners/sorbet_required_ancestors.rb +4 -2
  80. data/lib/tapioca/gem/listeners/sorbet_signatures.rb +7 -5
  81. data/lib/tapioca/gem/listeners/sorbet_type_variables.rb +6 -4
  82. data/lib/tapioca/gem/listeners/source_location.rb +15 -8
  83. data/lib/tapioca/gem/listeners/subconstants.rb +5 -4
  84. data/lib/tapioca/gem/listeners/yard_doc.rb +30 -23
  85. data/lib/tapioca/gem/pipeline.rb +107 -91
  86. data/lib/tapioca/gem_info.rb +1 -1
  87. data/lib/tapioca/gemfile.rb +64 -73
  88. data/lib/tapioca/helpers/cli_helper.rb +4 -7
  89. data/lib/tapioca/helpers/config_helper.rb +17 -29
  90. data/lib/tapioca/helpers/env_helper.rb +2 -5
  91. data/lib/tapioca/helpers/gem_helper.rb +5 -5
  92. data/lib/tapioca/helpers/git_attributes.rb +3 -3
  93. data/lib/tapioca/helpers/rbi_files_helper.rb +76 -73
  94. data/lib/tapioca/helpers/rbi_helper.rb +14 -22
  95. data/lib/tapioca/helpers/sorbet_helper.rb +9 -18
  96. data/lib/tapioca/helpers/source_uri.rb +15 -25
  97. data/lib/tapioca/helpers/test/content.rb +7 -10
  98. data/lib/tapioca/helpers/test/dsl_compiler.rb +20 -33
  99. data/lib/tapioca/helpers/test/isolation.rb +10 -14
  100. data/lib/tapioca/helpers/test/template.rb +6 -11
  101. data/lib/tapioca/internal.rb +18 -8
  102. data/lib/tapioca/loaders/dsl.rb +11 -19
  103. data/lib/tapioca/loaders/gem.rb +6 -21
  104. data/lib/tapioca/loaders/loader.rb +21 -39
  105. data/lib/tapioca/rbi_ext/model.rb +12 -37
  106. data/lib/tapioca/rbi_formatter.rb +10 -19
  107. data/lib/tapioca/rbs/rewriter.rb +55 -0
  108. data/lib/tapioca/repo_index.rb +7 -9
  109. data/lib/tapioca/runtime/attached_class_of_32.rb +1 -1
  110. data/lib/tapioca/runtime/attached_class_of_legacy.rb +2 -5
  111. data/lib/tapioca/runtime/dynamic_mixin_compiler.rb +23 -23
  112. data/lib/tapioca/runtime/generic_type_registry.rb +13 -23
  113. data/lib/tapioca/runtime/reflection.rb +81 -60
  114. data/lib/tapioca/runtime/source_location.rb +44 -0
  115. data/lib/tapioca/runtime/trackers/autoload.rb +7 -9
  116. data/lib/tapioca/runtime/trackers/constant_definition.rb +18 -14
  117. data/lib/tapioca/runtime/trackers/method_definition.rb +65 -0
  118. data/lib/tapioca/runtime/trackers/mixin.rb +8 -11
  119. data/lib/tapioca/runtime/trackers/required_ancestor.rb +3 -3
  120. data/lib/tapioca/runtime/trackers/tracker.rb +3 -6
  121. data/lib/tapioca/runtime/trackers.rb +5 -8
  122. data/lib/tapioca/sorbet_ext/generic_name_patch.rb +9 -15
  123. data/lib/tapioca/sorbet_ext/name_patch.rb +2 -2
  124. data/lib/tapioca/sorbet_ext/proc_bind_patch.rb +1 -1
  125. data/lib/tapioca/static/requires_compiler.rb +6 -6
  126. data/lib/tapioca/static/symbol_loader.rb +14 -16
  127. data/lib/tapioca/static/symbol_table_parser.rb +8 -8
  128. data/lib/tapioca/version.rb +1 -1
  129. data/lib/tapioca.rb +22 -29
  130. 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
- extend T::Helpers
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
- sig { params(index: RBI::Index, kind: String, dir: String, number_of_workers: T.nilable(Integer)).void }
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
- sig do
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
- sig { params(loc: RBI::Loc, path_prefix: T.nilable(String)).returns(String) }
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
- sig do
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 Thor::Error, error_messages.join("\n") if parse_errors.any?
131
+ Kernel.raise Tapioca::Error, error_messages.join("\n") if parse_errors.any?
150
132
  end
151
133
 
152
134
  private
153
135
 
154
- sig { params(index: RBI::Index, files: T::Array[String], number_of_workers: T.nilable(Integer)).void }
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
- sig { params(nodes: T::Array[RBI::Node], shim_rbi_dir: String, todo_rbi_file: String).returns(T::Boolean) }
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
- not_shims_or_todos = nodes - shims_or_todos
178
- shims_or_todos_empty_scopes = extract_empty_scopes(shims_or_todos)
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
- # We need to discard classes that are redefining the parent of a class
181
- shims_or_todos_empty_scopes.select! do |scope|
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
- next true unless scope.is_a?(RBI::Class)
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
- next true unless parent_name
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? { |node| node.is_a?(RBI::Class) && node.superclass_name == parent_name }
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
- return true unless shims_or_todos_empty_scopes.empty?
196
+ end
193
197
 
194
- mixins = extract_mixins(shims_or_todos)
195
- return true unless mixins.empty?
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
- props = extract_methods_and_attrs(shims_or_todos)
198
- return false if props.empty?
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
- shims_or_todos_with_sigs = extract_nodes_with_sigs(props)
201
- shims_or_todos_with_sigs.each do |shim_or_todo|
202
- shims_or_todos_sigs = shim_or_todo.sigs
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
- extract_methods_and_attrs(nodes).each do |node|
205
- next if node == shim_or_todo
206
- return true if shims_or_todos_sigs.all? { |sig| node.sigs.include?(sig) }
207
- end
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
- return false
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
- true
235
+ false
213
236
  end
214
237
 
215
- sig { params(nodes: T::Array[RBI::Node], shim_rbi_dir: String, todo_rbi_file: String).returns(T::Array[RBI::Node]) }
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
- sig { params(nodes: T::Array[RBI::Node]).returns(T::Array[RBI::Scope]) }
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
- sig { params(nodes: T::Array[RBI::Node]).returns(T::Array[T.any(RBI::Mixin, RBI::RequiresAncestor)]) }
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
- sig { params(path: String).returns(String) }
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
- sig do
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
- sig { params(name: String, type: String).returns(RBI::TypedParam) }
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
- sig { params(name: String, type: String, default: String).returns(RBI::TypedParam) }
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
- sig { params(name: String, type: String).returns(RBI::TypedParam) }
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
- sig { params(name: String, type: String).returns(RBI::TypedParam) }
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
- sig { params(name: String, type: String, default: String).returns(RBI::TypedParam) }
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
- sig { params(name: String, type: String).returns(RBI::TypedParam) }
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
- sig { params(name: String, type: String).returns(RBI::TypedParam) }
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
- sig { params(param: RBI::Param, type: String).returns(RBI::TypedParam) }
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
- sig { params(sig_string: String).returns(String) }
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
- sig { params(type: String).returns(String) }
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
- sig { params(type: String).returns(String) }
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
- sig { params(name: String).returns(T::Boolean) }
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
- sig { params(name: String).returns(T::Boolean) }
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 = T.let(
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 = T.let(
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 = T.let(Spoom::Context.new("."), Spoom::Context)
16
+ SPOOM_CONTEXT = Spoom::Context.new(".") #: Spoom::Context
23
17
 
24
- FEATURE_REQUIREMENTS = T.let(
25
- {
26
- # feature_name: ::Gem::Requirement.new(">= ___"), # https://github.com/sorbet/sorbet/pull/___
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
- sig { params(sorbet_args: String).returns(Spoom::ExecResult) }
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
- sig { returns(String) }
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
- sig { params(feature: Symbol, version: T.nilable(::Gem::Version)).returns(T::Boolean) }
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 = T.let(
11
- [
12
- :scheme,
13
- :gem_name,
14
- :gem_version,
15
- :path,
16
- :line_number,
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 = T.let(const_defined?(:RFC2396_PARSER) ? RFC2396_PARSER : DEFAULT_PARSER, RFC2396_Parser)
23
+ PARSER = const_defined?(:RFC2396_PARSER) ? RFC2396_PARSER : DEFAULT_PARSER #: RFC2396_Parser
27
24
 
28
- sig { returns(T.nilable(String)) }
25
+ #: String?
29
26
  attr_reader :gem_version
30
27
 
31
28
  class << self
32
29
  extend T::Sig
33
30
 
34
- sig do
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
- sig { returns(T.nilable(String)) }
44
+ #: -> String?
55
45
  def gem_name
56
46
  host
57
47
  end
58
48
 
59
- sig { returns(T.nilable(String)) }
49
+ #: -> String?
60
50
  def line_number
61
51
  fragment
62
52
  end
63
53
 
64
- sig { params(v: T.nilable(String)).void }
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
- sig { params(v: T.nilable(String)).returns(T::Boolean) }
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
- sig { returns(String) }
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
- extend T::Helpers
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
- sig { params(args: String).returns(String) }
16
+ #: (*String args) -> String
20
17
  def tmp_path(*args)
21
- @tmp_path = T.let(@tmp_path, T.nilable(String))
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
- sig { void }
23
+ #: -> void
27
24
  def remove_tmp_path
28
25
  FileUtils.rm_rf(tmp_path)
29
26
  end
30
27
 
31
- sig { params(name: String, content: String, require_file: T::Boolean).returns(String) }
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
- sig { params(name: String, content: String).returns(String) }
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
- requires_ancestor { Kernel }
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 = T.let(CompilerContext.new(compiler_class), T.nilable(CompilerContext))
22
+ @context = CompilerContext.new(compiler_class) #: CompilerContext?
26
23
  end
27
24
 
28
- sig { params(compiler_classes: T.class_of(Tapioca::Dsl::Compiler)).void }
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
- sig do
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
- sig { returns(T::Array[String]) }
35
+ #: -> Array[String]
44
36
  def gathered_constants
45
37
  context.gathered_constants
46
38
  end
47
39
 
48
- sig { returns(T::Array[String]) }
40
+ #: -> Array[String]
49
41
  def generated_errors
50
42
  context.errors
51
43
  end
52
44
 
53
- sig { returns(CompilerContext) }
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
- sig { returns(T.class_of(Tapioca::Dsl::Compiler)) }
57
+ #: singleton(Tapioca::Dsl::Compiler)
66
58
  attr_reader :compiler_class
67
59
 
68
- sig { returns(T::Array[T.class_of(Tapioca::Dsl::Compiler)]) }
60
+ #: Array[singleton(Tapioca::Dsl::Compiler)]
69
61
  attr_reader :other_compiler_classes
70
62
 
71
- sig { params(compiler_class: T.class_of(Tapioca::Dsl::Compiler)).void }
63
+ #: (singleton(Tapioca::Dsl::Compiler) compiler_class) -> void
72
64
  def initialize(compiler_class)
73
65
  @compiler_class = compiler_class
74
- @other_compiler_classes = T.let([], T::Array[T.class_of(Tapioca::Dsl::Compiler)])
75
- @pipeline = T.let(nil, T.nilable(Tapioca::Dsl::Pipeline))
76
- @errors = T.let([], T::Array[String])
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
- sig { params(compiler_classes: T::Array[T.class_of(Tapioca::Dsl::Compiler)]).void }
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
- sig { returns(T::Array[T.class_of(Tapioca::Dsl::Compiler)]) }
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
- sig { returns(T::Array[String]) }
81
+ #: -> Array[String]
90
82
  def gathered_constants
91
83
  compiler_class.processable_constants.filter_map(&:name).sort
92
84
  end
93
85
 
94
- sig do
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
- sig { returns(T::Array[String]) }
121
+ #: -> Array[String]
135
122
  def errors
136
123
  pipeline.errors
137
124
  end
138
125
 
139
126
  private
140
127
 
141
- sig { returns(Tapioca::Dsl::Pipeline) }
128
+ #: -> Tapioca::Dsl::Pipeline
142
129
  def pipeline
143
130
  @pipeline ||= Tapioca::Dsl::Pipeline.new(
144
131
  requested_constants: [],