tapioca 0.16.10 → 0.17.0

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 (123) hide show
  1. checksums.yaml +4 -4
  2. data/lib/ruby_lsp/tapioca/addon.rb +24 -20
  3. data/lib/ruby_lsp/tapioca/run_gem_rbi_check.rb +20 -20
  4. data/lib/tapioca/bundler_ext/auto_require_hook.rb +5 -10
  5. data/lib/tapioca/commands/abstract_dsl.rb +36 -62
  6. data/lib/tapioca/commands/abstract_gem.rb +23 -43
  7. data/lib/tapioca/commands/annotations.rb +27 -33
  8. data/lib/tapioca/commands/check_shims.rb +4 -13
  9. data/lib/tapioca/commands/command.rb +8 -20
  10. data/lib/tapioca/commands/command_without_tracker.rb +1 -1
  11. data/lib/tapioca/commands/configure.rb +11 -16
  12. data/lib/tapioca/commands/dsl_compiler_list.rb +2 -1
  13. data/lib/tapioca/commands/dsl_generate.rb +2 -1
  14. data/lib/tapioca/commands/dsl_verify.rb +2 -1
  15. data/lib/tapioca/commands/gem_generate.rb +4 -8
  16. data/lib/tapioca/commands/gem_sync.rb +2 -1
  17. data/lib/tapioca/commands/gem_verify.rb +3 -2
  18. data/lib/tapioca/commands/require.rb +3 -7
  19. data/lib/tapioca/commands/todo.rb +6 -10
  20. data/lib/tapioca/dsl/compiler.rb +28 -53
  21. data/lib/tapioca/dsl/compilers/aasm.rb +31 -41
  22. data/lib/tapioca/dsl/compilers/action_controller_helpers.rb +7 -5
  23. data/lib/tapioca/dsl/compilers/action_mailer.rb +5 -3
  24. data/lib/tapioca/dsl/compilers/action_text.rb +5 -3
  25. data/lib/tapioca/dsl/compilers/active_job.rb +5 -8
  26. data/lib/tapioca/dsl/compilers/active_model_attributes.rb +9 -7
  27. data/lib/tapioca/dsl/compilers/active_model_secure_password.rb +4 -2
  28. data/lib/tapioca/dsl/compilers/active_model_validations_confirmation.rb +4 -2
  29. data/lib/tapioca/dsl/compilers/active_record_associations.rb +16 -42
  30. data/lib/tapioca/dsl/compilers/active_record_columns.rb +19 -24
  31. data/lib/tapioca/dsl/compilers/active_record_delegated_types.rb +7 -5
  32. data/lib/tapioca/dsl/compilers/active_record_enum.rb +6 -4
  33. data/lib/tapioca/dsl/compilers/active_record_fixtures.rb +53 -61
  34. data/lib/tapioca/dsl/compilers/active_record_relations.rb +86 -119
  35. data/lib/tapioca/dsl/compilers/active_record_scope.rb +7 -11
  36. data/lib/tapioca/dsl/compilers/active_record_secure_token.rb +4 -2
  37. data/lib/tapioca/dsl/compilers/active_record_store.rb +4 -2
  38. data/lib/tapioca/dsl/compilers/active_record_typed_store.rb +18 -26
  39. data/lib/tapioca/dsl/compilers/active_resource.rb +18 -19
  40. data/lib/tapioca/dsl/compilers/active_storage.rb +5 -5
  41. data/lib/tapioca/dsl/compilers/active_support_concern.rb +8 -6
  42. data/lib/tapioca/dsl/compilers/active_support_current_attributes.rb +7 -5
  43. data/lib/tapioca/dsl/compilers/active_support_time_ext.rb +4 -2
  44. data/lib/tapioca/dsl/compilers/config.rb +4 -2
  45. data/lib/tapioca/dsl/compilers/frozen_record.rb +6 -9
  46. data/lib/tapioca/dsl/compilers/graphql_input_object.rb +8 -8
  47. data/lib/tapioca/dsl/compilers/graphql_mutation.rb +5 -8
  48. data/lib/tapioca/dsl/compilers/identity_cache.rb +10 -37
  49. data/lib/tapioca/dsl/compilers/json_api_client_resource.rb +8 -16
  50. data/lib/tapioca/dsl/compilers/kredis.rb +6 -4
  51. data/lib/tapioca/dsl/compilers/mixed_in_class_attributes.rb +4 -2
  52. data/lib/tapioca/dsl/compilers/protobuf.rb +12 -24
  53. data/lib/tapioca/dsl/compilers/rails_generators.rb +8 -9
  54. data/lib/tapioca/dsl/compilers/sidekiq_worker.rb +22 -11
  55. data/lib/tapioca/dsl/compilers/smart_properties.rb +11 -20
  56. data/lib/tapioca/dsl/compilers/state_machines.rb +14 -24
  57. data/lib/tapioca/dsl/compilers/url_helpers.rb +9 -7
  58. data/lib/tapioca/dsl/compilers.rb +4 -7
  59. data/lib/tapioca/dsl/helpers/active_model_type_helper.rb +13 -16
  60. data/lib/tapioca/dsl/helpers/active_record_column_type_helper.rb +13 -28
  61. data/lib/tapioca/dsl/helpers/active_record_constants_helper.rb +19 -15
  62. data/lib/tapioca/dsl/helpers/graphql_type_helper.rb +5 -24
  63. data/lib/tapioca/dsl/pipeline.rb +30 -58
  64. data/lib/tapioca/executor.rb +6 -12
  65. data/lib/tapioca/gem/events.rb +22 -28
  66. data/lib/tapioca/gem/listeners/base.rb +6 -6
  67. data/lib/tapioca/gem/listeners/dynamic_mixins.rb +4 -2
  68. data/lib/tapioca/gem/listeners/foreign_constants.rb +5 -7
  69. data/lib/tapioca/gem/listeners/methods.rb +15 -34
  70. data/lib/tapioca/gem/listeners/mixins.rb +6 -18
  71. data/lib/tapioca/gem/listeners/remove_empty_payload_scopes.rb +4 -2
  72. data/lib/tapioca/gem/listeners/sorbet_enums.rb +4 -2
  73. data/lib/tapioca/gem/listeners/sorbet_helpers.rb +4 -2
  74. data/lib/tapioca/gem/listeners/sorbet_props.rb +4 -2
  75. data/lib/tapioca/gem/listeners/sorbet_required_ancestors.rb +4 -2
  76. data/lib/tapioca/gem/listeners/sorbet_signatures.rb +7 -5
  77. data/lib/tapioca/gem/listeners/sorbet_type_variables.rb +6 -4
  78. data/lib/tapioca/gem/listeners/source_location.rb +7 -4
  79. data/lib/tapioca/gem/listeners/subconstants.rb +5 -4
  80. data/lib/tapioca/gem/listeners/yard_doc.rb +23 -22
  81. data/lib/tapioca/gem/pipeline.rb +57 -72
  82. data/lib/tapioca/gem_info.rb +1 -1
  83. data/lib/tapioca/gemfile.rb +64 -73
  84. data/lib/tapioca/helpers/cli_helper.rb +3 -3
  85. data/lib/tapioca/helpers/config_helper.rb +15 -24
  86. data/lib/tapioca/helpers/env_helper.rb +1 -1
  87. data/lib/tapioca/helpers/gem_helper.rb +5 -5
  88. data/lib/tapioca/helpers/git_attributes.rb +3 -3
  89. data/lib/tapioca/helpers/rbi_files_helper.rb +73 -67
  90. data/lib/tapioca/helpers/rbi_helper.rb +14 -22
  91. data/lib/tapioca/helpers/sorbet_helper.rb +9 -18
  92. data/lib/tapioca/helpers/source_uri.rb +15 -25
  93. data/lib/tapioca/helpers/test/content.rb +6 -6
  94. data/lib/tapioca/helpers/test/dsl_compiler.rb +19 -29
  95. data/lib/tapioca/helpers/test/isolation.rb +4 -4
  96. data/lib/tapioca/helpers/test/template.rb +5 -7
  97. data/lib/tapioca/internal.rb +5 -1
  98. data/lib/tapioca/loaders/dsl.rb +11 -19
  99. data/lib/tapioca/loaders/gem.rb +6 -21
  100. data/lib/tapioca/loaders/loader.rb +15 -27
  101. data/lib/tapioca/rbi_ext/model.rb +12 -37
  102. data/lib/tapioca/rbi_formatter.rb +10 -19
  103. data/lib/tapioca/rbs/rewriter.rb +55 -0
  104. data/lib/tapioca/repo_index.rb +7 -7
  105. data/lib/tapioca/runtime/attached_class_of_32.rb +1 -1
  106. data/lib/tapioca/runtime/attached_class_of_legacy.rb +1 -1
  107. data/lib/tapioca/runtime/dynamic_mixin_compiler.rb +23 -23
  108. data/lib/tapioca/runtime/generic_type_registry.rb +13 -23
  109. data/lib/tapioca/runtime/reflection.rb +48 -56
  110. data/lib/tapioca/runtime/trackers/autoload.rb +4 -8
  111. data/lib/tapioca/runtime/trackers/mixin.rb +6 -10
  112. data/lib/tapioca/runtime/trackers/required_ancestor.rb +3 -3
  113. data/lib/tapioca/runtime/trackers/tracker.rb +2 -2
  114. data/lib/tapioca/runtime/trackers.rb +4 -8
  115. data/lib/tapioca/sorbet_ext/generic_name_patch.rb +9 -15
  116. data/lib/tapioca/sorbet_ext/name_patch.rb +1 -1
  117. data/lib/tapioca/sorbet_ext/proc_bind_patch.rb +1 -1
  118. data/lib/tapioca/static/requires_compiler.rb +6 -6
  119. data/lib/tapioca/static/symbol_loader.rb +14 -16
  120. data/lib/tapioca/static/symbol_table_parser.rb +8 -8
  121. data/lib/tapioca/version.rb +1 -1
  122. data/lib/tapioca.rb +22 -29
  123. metadata +25 -10
@@ -9,7 +9,7 @@ module Tapioca
9
9
  requires_ancestor { Thor::Shell }
10
10
  requires_ancestor { SorbetHelper }
11
11
 
12
- sig { params(index: RBI::Index, kind: String, file: String).void }
12
+ #: (RBI::Index index, String kind, String file) -> void
13
13
  def index_rbi(index, kind, file)
14
14
  return unless File.exist?(file)
15
15
 
@@ -21,7 +21,7 @@ module Tapioca
21
21
  say("(#{time.round(2)}s)")
22
22
  end
23
23
 
24
- sig { params(index: RBI::Index, kind: String, dir: String, number_of_workers: T.nilable(Integer)).void }
24
+ #: (RBI::Index index, String kind, String dir, number_of_workers: Integer?) -> void
25
25
  def index_rbis(index, kind, dir, number_of_workers:)
26
26
  return unless Dir.exist?(dir) && !Dir.empty?(dir)
27
27
 
@@ -38,13 +38,7 @@ module Tapioca
38
38
  say("(#{time.round(2)}s)")
39
39
  end
40
40
 
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
41
+ #: (RBI::Index index, shim_rbi_dir: String, todo_rbi_file: String) -> Hash[String, Array[RBI::Node]]
48
42
  def duplicated_nodes_from_index(index, shim_rbi_dir:, todo_rbi_file:)
49
43
  duplicates = {}
50
44
  say("Looking for duplicates... ")
@@ -61,7 +55,7 @@ module Tapioca
61
55
  duplicates
62
56
  end
63
57
 
64
- sig { params(loc: RBI::Loc, path_prefix: T.nilable(String)).returns(String) }
58
+ #: (RBI::Loc loc, path_prefix: String?) -> String
65
59
  def location_to_payload_url(loc, path_prefix:)
66
60
  return loc.to_s unless path_prefix
67
61
 
@@ -73,16 +67,7 @@ module Tapioca
73
67
  url
74
68
  end
75
69
 
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
70
+ #: (command: String, gem_dir: String, dsl_dir: String, auto_strictness: bool, ?gems: Array[Gemfile::GemSpec], ?compilers: T::Enumerable[singleton(Dsl::Compiler)]) -> void
86
71
  def validate_rbi_files(command:, gem_dir:, dsl_dir:, auto_strictness:, gems: [], compilers: [])
87
72
  error_url_base = Spoom::Sorbet::Errors::DEFAULT_ERROR_URL_BASE
88
73
 
@@ -151,7 +136,7 @@ module Tapioca
151
136
 
152
137
  private
153
138
 
154
- sig { params(index: RBI::Index, files: T::Array[String], number_of_workers: T.nilable(Integer)).void }
139
+ #: (RBI::Index index, Array[String] files, number_of_workers: Integer?) -> void
155
140
  def parse_and_index_files(index, files, number_of_workers:)
156
141
  executor = Executor.new(files, number_of_workers: number_of_workers)
157
142
 
@@ -167,64 +152,100 @@ module Tapioca
167
152
  index.visit_all(trees)
168
153
  end
169
154
 
170
- sig { params(nodes: T::Array[RBI::Node], shim_rbi_dir: String, todo_rbi_file: String).returns(T::Boolean) }
155
+ # Do the list of `nodes` sharing the same name have duplicates?
156
+ #: (Array[RBI::Node] nodes, shim_rbi_dir: String, todo_rbi_file: String) -> bool
171
157
  def shims_or_todos_have_duplicates?(nodes, shim_rbi_dir:, todo_rbi_file:)
158
+ # If there is only one node, there are no duplicates
172
159
  return false if nodes.size == 1
173
160
 
161
+ # Extract the nodes from the sorbet/rbi/shims/ directory and the todo.rbi file
174
162
  shims_or_todos = extract_shims_and_todos(nodes, shim_rbi_dir: shim_rbi_dir, todo_rbi_file: todo_rbi_file)
175
163
  return false if shims_or_todos.empty?
176
164
 
177
- not_shims_or_todos = nodes - shims_or_todos
178
- shims_or_todos_empty_scopes = extract_empty_scopes(shims_or_todos)
165
+ # First let's look into scopes (classes, modules, sclass) for duplicates
166
+ has_duplicated_scopes?(nodes, shims_or_todos) ||
167
+ # Then let's look into mixins
168
+ has_duplicated_mixins?(shims_or_todos) ||
169
+ # Finally, let's compare the methods and attributes with the same name
170
+ has_duplicated_methods_and_attrs?(nodes, shims_or_todos)
171
+ end
172
+
173
+ #: (Array[RBI::Node], Array[RBI::Node]) -> bool
174
+ def has_duplicated_scopes?(all_nodes, shims_or_todos)
175
+ shims_or_todos_scopes = shims_or_todos.grep(RBI::Scope)
176
+ return false if shims_or_todos_scopes.empty?
177
+
178
+ # Extract the empty scopes from the shims or todos
179
+ # We do not care about non-empty scopes because they hold definitions that we will check against Tapioca's
180
+ # generated RBI files in another iteration.
181
+ shims_or_todos_empty_scopes = shims_or_todos_scopes.select(&:empty?)
182
+
183
+ # Extract the nodes that are not shims or todos (basically the nodes from the RBI files generated by Tapioca)
184
+ not_shims_or_todos = all_nodes - shims_or_todos
179
185
 
180
- # We need to discard classes that are redefining the parent of a class
181
- shims_or_todos_empty_scopes.select! do |scope|
186
+ shims_or_todos_empty_scopes.any? do |scope|
182
187
  # Empty modules are always duplicates
183
- next true unless scope.is_a?(RBI::Class)
188
+ break true unless scope.is_a?(RBI::Class)
184
189
 
185
190
  # Empty classes without parents are also duplicates
186
191
  parent_name = scope.superclass_name
187
- next true unless parent_name
192
+ break true unless parent_name
188
193
 
189
194
  # 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 }
195
+ break true if not_shims_or_todos.any? do |node|
196
+ node.is_a?(RBI::Class) && node.superclass_name == parent_name
197
+ end
191
198
  end
192
- return true unless shims_or_todos_empty_scopes.empty?
199
+ end
200
+
201
+ #: (Array[RBI::Node] shims_or_todos) -> bool
202
+ def has_duplicated_mixins?(shims_or_todos)
203
+ # Don't forget `shims_or_todos` is a list of nodes with the same qualified name, so if we find two mixins of the
204
+ # same name, they _are_ about the same thing, like two `include(A)` or two `requires_ancestor(A)` so this is a
205
+ # duplicate
206
+ shims_or_todos.any? { |node| node.is_a?(RBI::Mixin) || node.is_a?(RBI::RequiresAncestor) }
207
+ end
193
208
 
194
- mixins = extract_mixins(shims_or_todos)
195
- return true unless mixins.empty?
209
+ #: (Array[RBI::Node] nodes, Array[RBI::Node] shims_or_todos) -> bool
210
+ def has_duplicated_methods_and_attrs?(nodes, shims_or_todos)
211
+ shims_or_todos_props = extract_methods_and_attrs(shims_or_todos)
212
+ if shims_or_todos_props.any?
213
+ shims_or_todos_props.each do |shim_or_todo_prop|
214
+ other_nodes = extract_methods_and_attrs(nodes) - [shim_or_todo_prop]
196
215
 
197
- props = extract_methods_and_attrs(shims_or_todos)
198
- return false if props.empty?
216
+ if shim_or_todo_prop.sigs.empty?
217
+ # If the node doesn't have a signature and is an attribute accessor, we have a duplicate
218
+ return true if shim_or_todo_prop.is_a?(RBI::Attr)
199
219
 
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
220
+ # Now we know it's a method
203
221
 
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
222
+ # If the node has no parameters and we compare it against an attribute of the same name, it's a duplicate
223
+ return true if shim_or_todo_prop.params.empty? && other_nodes.grep(RBI::Attr).any?
224
+
225
+ # If the node has parameters, we compare them against all the other methods
226
+ # If at least one of them has the same parameters, it's a duplicate
227
+ return true if other_nodes.grep(RBI::Method).any? { |other| shim_or_todo_prop.params == other.params }
228
+ end
208
229
 
209
- return false
230
+ # We compare the shim or todo prop with all the other props of the same name
231
+ other_nodes.each do |node|
232
+ # Another prop has the same sig, we have a duplicate
233
+ return true if shim_or_todo_prop.sigs.any? { |sig| node.sigs.include?(sig) }
234
+ end
235
+ end
210
236
  end
211
237
 
212
- true
238
+ false
213
239
  end
214
240
 
215
- sig { params(nodes: T::Array[RBI::Node], shim_rbi_dir: String, todo_rbi_file: String).returns(T::Array[RBI::Node]) }
241
+ #: (Array[RBI::Node] nodes, shim_rbi_dir: String, todo_rbi_file: String) -> Array[RBI::Node]
216
242
  def extract_shims_and_todos(nodes, shim_rbi_dir:, todo_rbi_file:)
217
243
  nodes.select do |node|
218
244
  node.loc&.file&.start_with?(shim_rbi_dir) || node.loc&.file == todo_rbi_file
219
245
  end
220
246
  end
221
247
 
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)]) }
248
+ #: (Array[RBI::Node] nodes) -> Array[(RBI::Method | RBI::Attr)]
228
249
  def extract_methods_and_attrs(nodes)
229
250
  T.cast(
230
251
  nodes.select do |node|
@@ -234,22 +255,7 @@ module Tapioca
234
255
  )
235
256
  end
236
257
 
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 }
258
+ #: (Array[Spoom::Sorbet::Errors::Error] errors, String gem_dir) -> void
253
259
  def update_gem_rbis_strictnesses(errors, gem_dir)
254
260
  files = []
255
261
 
@@ -276,7 +282,7 @@ module Tapioca
276
282
  say("\n")
277
283
  end
278
284
 
279
- sig { params(path: String).returns(String) }
285
+ #: (String path) -> String
280
286
  def gem_name_from_rbi_path(path)
281
287
  T.must(File.basename(path, ".rbi").split("@").first)
282
288
  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
@@ -10,32 +10,32 @@ module Tapioca
10
10
 
11
11
  requires_ancestor { Kernel }
12
12
 
13
- sig { void }
13
+ #: -> void
14
14
  def teardown
15
15
  super
16
16
  remove_tmp_path
17
17
  end
18
18
 
19
- sig { params(args: String).returns(String) }
19
+ #: (*String args) -> String
20
20
  def tmp_path(*args)
21
- @tmp_path = T.let(@tmp_path, T.nilable(String))
21
+ @tmp_path = @tmp_path #: String?
22
22
  @tmp_path ||= Dir.mktmpdir
23
23
  T.unsafe(File).join(@tmp_path, *args)
24
24
  end
25
25
 
26
- sig { void }
26
+ #: -> void
27
27
  def remove_tmp_path
28
28
  FileUtils.rm_rf(tmp_path)
29
29
  end
30
30
 
31
- sig { params(name: String, content: String, require_file: T::Boolean).returns(String) }
31
+ #: (String name, String content, ?require_file: bool) -> String
32
32
  def add_ruby_file(name, content, require_file: true)
33
33
  add_content_file(name, content).tap do |file_name|
34
34
  Tapioca.silence_warnings { require(file_name) } if require_file
35
35
  end
36
36
  end
37
37
 
38
- sig { params(name: String, content: String).returns(String) }
38
+ #: (String name, String content) -> String
39
39
  def add_content_file(name, content)
40
40
  file_name = tmp_path("lib/#{name}")
41
41
  raise ArgumentError, "a file named '#{name}' was already added; cannot overwrite." if File.exist?(file_name)
@@ -20,37 +20,32 @@ module Tapioca
20
20
 
21
21
  requires_ancestor { Kernel }
22
22
 
23
- sig { params(compiler_class: T.class_of(Tapioca::Dsl::Compiler)).void }
23
+ #: (singleton(Tapioca::Dsl::Compiler) compiler_class) -> void
24
24
  def use_dsl_compiler(compiler_class)
25
- @context = T.let(CompilerContext.new(compiler_class), T.nilable(CompilerContext))
25
+ @context = CompilerContext.new(compiler_class) #: CompilerContext?
26
26
  end
27
27
 
28
- sig { params(compiler_classes: T.class_of(Tapioca::Dsl::Compiler)).void }
28
+ #: (*singleton(Tapioca::Dsl::Compiler) compiler_classes) -> void
29
29
  def activate_other_dsl_compilers(*compiler_classes)
30
30
  context.activate_other_dsl_compilers(compiler_classes)
31
31
  end
32
32
 
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
33
+ #: ((Symbol | String) constant_name, ?compiler_options: Hash[Symbol, untyped]) -> String
39
34
  def rbi_for(constant_name, compiler_options: {})
40
35
  context.rbi_for(constant_name, compiler_options: compiler_options)
41
36
  end
42
37
 
43
- sig { returns(T::Array[String]) }
38
+ #: -> Array[String]
44
39
  def gathered_constants
45
40
  context.gathered_constants
46
41
  end
47
42
 
48
- sig { returns(T::Array[String]) }
43
+ #: -> Array[String]
49
44
  def generated_errors
50
45
  context.errors
51
46
  end
52
47
 
53
- sig { returns(CompilerContext) }
48
+ #: -> CompilerContext
54
49
  def context
55
50
  raise "Please call `use_dsl_compiler` before" unless @context
56
51
 
@@ -62,41 +57,36 @@ module Tapioca
62
57
 
63
58
  include SorbetHelper
64
59
 
65
- sig { returns(T.class_of(Tapioca::Dsl::Compiler)) }
60
+ #: singleton(Tapioca::Dsl::Compiler)
66
61
  attr_reader :compiler_class
67
62
 
68
- sig { returns(T::Array[T.class_of(Tapioca::Dsl::Compiler)]) }
63
+ #: Array[singleton(Tapioca::Dsl::Compiler)]
69
64
  attr_reader :other_compiler_classes
70
65
 
71
- sig { params(compiler_class: T.class_of(Tapioca::Dsl::Compiler)).void }
66
+ #: (singleton(Tapioca::Dsl::Compiler) compiler_class) -> void
72
67
  def initialize(compiler_class)
73
68
  @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])
69
+ @other_compiler_classes = [] #: Array[singleton(Tapioca::Dsl::Compiler)]
70
+ @pipeline = nil #: Tapioca::Dsl::Pipeline?
71
+ @errors = [] #: Array[String]
77
72
  end
78
73
 
79
- sig { params(compiler_classes: T::Array[T.class_of(Tapioca::Dsl::Compiler)]).void }
74
+ #: (Array[singleton(Tapioca::Dsl::Compiler)] compiler_classes) -> void
80
75
  def activate_other_dsl_compilers(compiler_classes)
81
76
  @other_compiler_classes = compiler_classes
82
77
  end
83
78
 
84
- sig { returns(T::Array[T.class_of(Tapioca::Dsl::Compiler)]) }
79
+ #: -> Array[singleton(Tapioca::Dsl::Compiler)]
85
80
  def activated_compiler_classes
86
81
  [compiler_class, *other_compiler_classes]
87
82
  end
88
83
 
89
- sig { returns(T::Array[String]) }
84
+ #: -> Array[String]
90
85
  def gathered_constants
91
86
  compiler_class.processable_constants.filter_map(&:name).sort
92
87
  end
93
88
 
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
89
+ #: ((Symbol | String) constant_name, ?compiler_options: Hash[Symbol, untyped]) -> String
100
90
  def rbi_for(constant_name, compiler_options: {})
101
91
  # Make sure this is a constant that we can handle.
102
92
  unless gathered_constants.include?(constant_name.to_s)
@@ -131,14 +121,14 @@ module Tapioca
131
121
  rbi
132
122
  end
133
123
 
134
- sig { returns(T::Array[String]) }
124
+ #: -> Array[String]
135
125
  def errors
136
126
  pipeline.errors
137
127
  end
138
128
 
139
129
  private
140
130
 
141
- sig { returns(Tapioca::Dsl::Pipeline) }
131
+ #: -> Tapioca::Dsl::Pipeline
142
132
  def pipeline
143
133
  @pipeline ||= Tapioca::Dsl::Pipeline.new(
144
134
  requested_constants: [],
@@ -13,7 +13,7 @@ module Tapioca
13
13
  class << self
14
14
  extend T::Sig
15
15
 
16
- sig { returns(T::Boolean) }
16
+ #: -> bool
17
17
  def forking_env?
18
18
  !ENV["NO_FORK"] && Process.respond_to?(:fork)
19
19
  end
@@ -33,7 +33,7 @@ module Tapioca
33
33
 
34
34
  requires_ancestor { Kernel }
35
35
 
36
- sig { params(_blk: T.untyped).returns(String) }
36
+ #: ?{ (?) -> untyped } -> String
37
37
  def run_in_isolation(&_blk)
38
38
  read, write = IO.pipe
39
39
  read.binmode
@@ -78,11 +78,11 @@ module Tapioca
78
78
 
79
79
  requires_ancestor { Kernel }
80
80
 
81
- ORIG_ARGV = T.let(ARGV.dup, T::Array[T.untyped]) unless defined?(ORIG_ARGV)
81
+ ORIG_ARGV = ARGV.dup unless defined?(ORIG_ARGV) #: as !nil
82
82
 
83
83
  # Crazy H4X to get this working in windows / jruby with
84
84
  # no forking.
85
- sig { params(_blk: T.untyped).returns(String) }
85
+ #: ?{ (?) -> untyped } -> String
86
86
  def run_in_isolation(&_blk)
87
87
  this = T.cast(self, Minitest::Test)
88
88
  require "tempfile"