tapioca 0.4.23 → 0.5.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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +14 -14
  3. data/README.md +2 -2
  4. data/Rakefile +5 -7
  5. data/exe/tapioca +2 -2
  6. data/lib/tapioca/cli.rb +256 -2
  7. data/lib/tapioca/compilers/dsl/aasm.rb +122 -0
  8. data/lib/tapioca/compilers/dsl/action_controller_helpers.rb +52 -12
  9. data/lib/tapioca/compilers/dsl/action_mailer.rb +6 -9
  10. data/lib/tapioca/compilers/dsl/active_job.rb +8 -12
  11. data/lib/tapioca/compilers/dsl/active_model_attributes.rb +131 -0
  12. data/lib/tapioca/compilers/dsl/active_record_associations.rb +33 -54
  13. data/lib/tapioca/compilers/dsl/active_record_columns.rb +10 -105
  14. data/lib/tapioca/compilers/dsl/active_record_enum.rb +8 -10
  15. data/lib/tapioca/compilers/dsl/active_record_scope.rb +7 -10
  16. data/lib/tapioca/compilers/dsl/active_record_typed_store.rb +5 -8
  17. data/lib/tapioca/compilers/dsl/active_resource.rb +9 -37
  18. data/lib/tapioca/compilers/dsl/active_storage.rb +98 -0
  19. data/lib/tapioca/compilers/dsl/active_support_concern.rb +108 -0
  20. data/lib/tapioca/compilers/dsl/active_support_current_attributes.rb +13 -8
  21. data/lib/tapioca/compilers/dsl/base.rb +96 -82
  22. data/lib/tapioca/compilers/dsl/config.rb +111 -0
  23. data/lib/tapioca/compilers/dsl/frozen_record.rb +5 -7
  24. data/lib/tapioca/compilers/dsl/identity_cache.rb +66 -29
  25. data/lib/tapioca/compilers/dsl/protobuf.rb +19 -69
  26. data/lib/tapioca/compilers/dsl/sidekiq_worker.rb +25 -12
  27. data/lib/tapioca/compilers/dsl/smart_properties.rb +19 -31
  28. data/lib/tapioca/compilers/dsl/state_machines.rb +56 -78
  29. data/lib/tapioca/compilers/dsl/url_helpers.rb +7 -10
  30. data/lib/tapioca/compilers/dsl_compiler.rb +22 -38
  31. data/lib/tapioca/compilers/requires_compiler.rb +2 -2
  32. data/lib/tapioca/compilers/sorbet.rb +26 -5
  33. data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +139 -154
  34. data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +4 -4
  35. data/lib/tapioca/compilers/symbol_table_compiler.rb +1 -1
  36. data/lib/tapioca/compilers/todos_compiler.rb +1 -1
  37. data/lib/tapioca/config.rb +2 -0
  38. data/lib/tapioca/config_builder.rb +4 -2
  39. data/lib/tapioca/constant_locator.rb +6 -8
  40. data/lib/tapioca/gemfile.rb +26 -19
  41. data/lib/tapioca/generator.rb +127 -43
  42. data/lib/tapioca/generic_type_registry.rb +25 -98
  43. data/lib/tapioca/helpers/active_record_column_type_helper.rb +98 -0
  44. data/lib/tapioca/internal.rb +1 -9
  45. data/lib/tapioca/loader.rb +14 -48
  46. data/lib/tapioca/rbi_ext/model.rb +122 -0
  47. data/lib/tapioca/reflection.rb +131 -0
  48. data/lib/tapioca/sorbet_ext/fixed_hash_patch.rb +1 -1
  49. data/lib/tapioca/sorbet_ext/generic_name_patch.rb +72 -4
  50. data/lib/tapioca/sorbet_ext/name_patch.rb +1 -1
  51. data/lib/tapioca/version.rb +1 -1
  52. data/lib/tapioca.rb +2 -0
  53. metadata +35 -23
  54. data/lib/tapioca/cli/main.rb +0 -146
  55. data/lib/tapioca/core_ext/class.rb +0 -28
  56. data/lib/tapioca/core_ext/string.rb +0 -18
  57. data/lib/tapioca/rbi/model.rb +0 -405
  58. data/lib/tapioca/rbi/printer.rb +0 -410
  59. data/lib/tapioca/rbi/rewriters/group_nodes.rb +0 -106
  60. data/lib/tapioca/rbi/rewriters/nest_non_public_methods.rb +0 -65
  61. data/lib/tapioca/rbi/rewriters/nest_singleton_methods.rb +0 -42
  62. data/lib/tapioca/rbi/rewriters/sort_nodes.rb +0 -82
  63. data/lib/tapioca/rbi/visitor.rb +0 -21
@@ -20,13 +20,14 @@ module Tapioca
20
20
  sig do
21
21
  params(
22
22
  requested_constants: T::Array[Module],
23
- requested_generators: T::Array[String],
23
+ requested_generators: T::Array[T.class_of(Dsl::Base)],
24
+ excluded_generators: T::Array[T.class_of(Dsl::Base)],
24
25
  error_handler: T.nilable(T.proc.params(error: String).void)
25
26
  ).void
26
27
  end
27
- def initialize(requested_constants:, requested_generators: [], error_handler: nil)
28
+ def initialize(requested_constants:, requested_generators: [], excluded_generators: [], error_handler: nil)
28
29
  @generators = T.let(
29
- gather_generators(requested_generators),
30
+ gather_generators(requested_generators, excluded_generators),
30
31
  T::Enumerable[Dsl::Base]
31
32
  )
32
33
  @requested_constants = requested_constants
@@ -54,23 +55,19 @@ module Tapioca
54
55
 
55
56
  private
56
57
 
57
- sig { params(requested_generators: T::Array[String]).returns(T.proc.params(klass: Class).returns(T::Boolean)) }
58
- def generator_filter(requested_generators)
59
- return ->(_klass) { true } if requested_generators.empty?
60
-
61
- generators = requested_generators.map(&:downcase)
62
-
63
- proc do |klass|
64
- generator = klass.name&.sub(/^Tapioca::Compilers::Dsl::/, '')&.downcase
65
- generators.include?(generator)
66
- end
58
+ sig do
59
+ params(
60
+ requested_generators: T::Array[T.class_of(Dsl::Base)],
61
+ excluded_generators: T::Array[T.class_of(Dsl::Base)]
62
+ ).returns(T::Enumerable[Dsl::Base])
67
63
  end
64
+ def gather_generators(requested_generators, excluded_generators)
65
+ generator_klasses = ::Tapioca::Reflection.descendants_of(Dsl::Base).select do |klass|
66
+ (requested_generators.empty? || requested_generators.include?(klass)) &&
67
+ !excluded_generators.include?(klass)
68
+ end.sort_by { |klass| T.must(klass.name) }
68
69
 
69
- sig { params(requested_generators: T::Array[String]).returns(T::Enumerable[Dsl::Base]) }
70
- def gather_generators(requested_generators)
71
- generator_filter = generator_filter(requested_generators)
72
-
73
- T.cast(Dsl::Base.descendants.select(&generator_filter).map(&:new), T::Enumerable[Dsl::Base])
70
+ generator_klasses.map(&:new)
74
71
  end
75
72
 
76
73
  sig { params(requested_constants: T::Array[Module]).returns(T::Set[Module]) }
@@ -82,32 +79,19 @@ module Tapioca
82
79
 
83
80
  sig { params(constant: Module).returns(T.nilable(String)) }
84
81
  def rbi_for_constant(constant)
85
- parlour = Parlour::RbiGenerator.new(sort_namespaces: true)
82
+ file = RBI::File.new(strictness: "true")
86
83
 
87
84
  generators.each do |generator|
88
85
  next unless generator.handles?(constant)
89
- generator.decorate(parlour.root, constant)
86
+ generator.decorate(file.root, constant)
90
87
  end
91
88
 
92
- return if parlour.root.children.empty?
93
-
94
- resolve_conflicts(parlour)
89
+ return if file.root.empty?
95
90
 
96
- parlour.rbi("true").strip
97
- end
98
-
99
- sig { params(parlour: Parlour::RbiGenerator).void }
100
- def resolve_conflicts(parlour)
101
- Parlour::ConflictResolver.new.resolve_conflicts(parlour.root) do |msg, candidates|
102
- error = StringIO.new
103
- error.puts "=== Error ==="
104
- error.puts msg
105
- error.puts "# Candidates"
106
- candidates.each_with_index do |candidate, index|
107
- error.puts " #{index}. #{candidate.describe}"
108
- end
109
- report_error(error.string)
110
- end
91
+ file.root.nest_non_public_methods!
92
+ file.root.group_nodes!
93
+ file.root.sort_nodes!
94
+ file.string
111
95
  end
112
96
 
113
97
  sig { params(error: String).returns(T.noreturn) }
@@ -1,7 +1,7 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'spoom'
4
+ require "spoom"
5
5
 
6
6
  module Tapioca
7
7
  module Compilers
@@ -87,7 +87,7 @@ module Tapioca
87
87
  sig { params(files: T::Enumerable[String], name: String).returns(T::Boolean) }
88
88
  def name_in_project?(files, name)
89
89
  files.any? do |file|
90
- File.basename(file, '.rb') == name
90
+ File.basename(file, ".rb") == name
91
91
  end
92
92
  end
93
93
  end
@@ -1,15 +1,26 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'pathname'
5
- require 'shellwords'
4
+ require "pathname"
5
+ require "shellwords"
6
6
 
7
7
  module Tapioca
8
8
  module Compilers
9
9
  module Sorbet
10
- SORBET = Pathname.new(Gem::Specification.find_by_name("sorbet-static").full_gem_path) / "libexec" / "sorbet"
10
+ SORBET_GEM_SPEC = T.let(
11
+ Gem::Specification.find_by_name("sorbet-static"),
12
+ Gem::Specification
13
+ )
14
+ SORBET = T.let(
15
+ Pathname.new(SORBET_GEM_SPEC.full_gem_path) / "libexec" / "sorbet",
16
+ Pathname
17
+ )
11
18
  EXE_PATH_ENV_VAR = "TAPIOCA_SORBET_EXE"
12
19
 
20
+ FEATURE_REQUIREMENTS = T.let({
21
+ mixes_in_class_methods_multiple_args: Gem::Requirement.new("> 0.5.6200"),
22
+ }.freeze, T::Hash[Symbol, Gem::Requirement])
23
+
13
24
  class << self
14
25
  extend(T::Sig)
15
26
 
@@ -20,7 +31,7 @@ module Tapioca
20
31
  sorbet_path,
21
32
  "--quiet",
22
33
  *args,
23
- ].join(' '),
34
+ ].join(" "),
24
35
  err: "/dev/null"
25
36
  ).read
26
37
  end
@@ -31,6 +42,16 @@ module Tapioca
31
42
  sorbet_path = SORBET if sorbet_path.empty?
32
43
  sorbet_path.to_s.shellescape
33
44
  end
45
+
46
+ sig { params(feature: Symbol, version: T.nilable(Gem::Version)).returns(T::Boolean) }
47
+ def supports?(feature, version: nil)
48
+ version = SORBET_GEM_SPEC.version unless version
49
+ requirement = FEATURE_REQUIREMENTS[feature]
50
+
51
+ raise "Invalid Sorbet feature #{feature}" unless requirement
52
+
53
+ requirement.satisfied_by?(version)
54
+ end
34
55
  end
35
56
  end
36
57
  end
@@ -1,23 +1,20 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'pathname'
4
+ require "pathname"
5
5
 
6
6
  module Tapioca
7
7
  module Compilers
8
8
  module SymbolTable
9
9
  class SymbolGenerator
10
10
  extend(T::Sig)
11
+ include(Reflection)
11
12
 
12
- IGNORED_SYMBOLS = %w{
13
- YAML
14
- MiniTest
15
- Mutex
16
- }
13
+ IGNORED_SYMBOLS = ["YAML", "MiniTest", "Mutex"]
17
14
 
18
15
  attr_reader(:gem, :indent)
19
16
 
20
- sig { params(gem: Gemfile::Gem, indent: Integer).void }
17
+ sig { params(gem: Gemfile::GemSpec, indent: Integer).void }
21
18
  def initialize(gem, indent = 0)
22
19
  @gem = gem
23
20
  @indent = indent
@@ -57,8 +54,8 @@ module Tapioca
57
54
  def engine_symbols(symbols)
58
55
  return Set.new unless Object.const_defined?("Rails::Engine")
59
56
 
60
- engine = Object.const_get("Rails::Engine")
61
- .descendants.reject(&:abstract_railtie?)
57
+ engine = descendants_of(Object.const_get("Rails::Engine"))
58
+ .reject(&:abstract_railtie?)
62
59
  .find do |klass|
63
60
  name = name_of(klass)
64
61
  !name.nil? && symbols.include?(name)
@@ -102,7 +99,7 @@ module Tapioca
102
99
  return unless constant
103
100
  return unless name
104
101
  return if name.strip.empty?
105
- return if name.start_with?('#<')
102
+ return if name.start_with?("#<")
106
103
  return if name.downcase == name
107
104
  return if alias_namespaced?(name)
108
105
  return if seen?(name)
@@ -144,7 +141,9 @@ module Tapioca
144
141
  sig { params(tree: RBI::Tree, name: String, value: BasicObject).void.checked(:never) }
145
142
  def compile_object(tree, name, value)
146
143
  return if symbol_ignored?(name)
144
+
147
145
  klass = class_of(value)
146
+ return if klass == TypeMember || klass == TypeTemplate
148
147
 
149
148
  klass_name = if klass == ObjectSpace::WeakMap
150
149
  # WeakMap is an implicit generic with one type variable
@@ -189,9 +188,10 @@ module Tapioca
189
188
 
190
189
  sig { params(tree: RBI::Tree, name: String, constant: Module).void }
191
190
  def compile_body(tree, name, constant)
191
+ # Compiling type variables must happen first to populate generic names
192
+ compile_type_variables(tree, constant)
192
193
  compile_methods(tree, name, constant)
193
194
  compile_module_helpers(tree, constant)
194
- compile_type_variables(tree, constant)
195
195
  compile_mixins(tree, constant)
196
196
  compile_mixes_in_class_methods(tree, constant)
197
197
  compile_props(tree, constant)
@@ -267,12 +267,10 @@ module Tapioca
267
267
  # Create a map of subconstants (via their object ids) to their names.
268
268
  # We need this later when we want to lookup the name of the registered type
269
269
  # variable via the value of the type variable constant.
270
- subconstant_to_name_lookup = constants_of(constant).map do |constant_name|
271
- [
272
- object_id_of(resolve_constant(constant_name.to_s, namespace: constant)),
273
- constant_name,
274
- ]
275
- end.to_h
270
+ subconstant_to_name_lookup = constants_of(constant)
271
+ .each_with_object({}.compare_by_identity) do |constant_name, table|
272
+ table[resolve_constant(constant_name.to_s, namespace: constant)] = constant_name.to_s
273
+ end
276
274
 
277
275
  # Map each type variable to its string representation.
278
276
  #
@@ -283,12 +281,13 @@ module Tapioca
283
281
  # By looping over these entries and then getting the actual constant name
284
282
  # from the `subconstant_to_name_lookup` we defined above, gives us all the
285
283
  # information we need to serialize type variable definitions.
286
- type_variable_declarations = type_variables.map do |type_variable_id, serialized_type_variable|
287
- constant_name = subconstant_to_name_lookup[type_variable_id]
284
+ type_variable_declarations = type_variables.map do |type_variable, serialized_type_variable|
285
+ constant_name = subconstant_to_name_lookup[type_variable]
286
+ type_variable.name = constant_name
288
287
  # Here, we know that constant_value will be an instance of
289
288
  # T::Types::CustomTypeVariable, which knows how to serialize
290
289
  # itself to a type_member/type_template
291
- tree << RBI::TypeMember.new(constant_name.to_s, serialized_type_variable)
290
+ tree << RBI::TypeMember.new(constant_name, serialized_type_variable)
292
291
  end
293
292
 
294
293
  return if type_variable_declarations.empty?
@@ -392,49 +391,80 @@ module Tapioca
392
391
  end
393
392
  end
394
393
 
395
- sig { params(tree: RBI::Tree, constant: Module).void }
396
- def compile_mixes_in_class_methods(tree, constant)
397
- return if constant.is_a?(Class)
398
-
399
- mixins_from_modules = {}
394
+ sig { params(constant: Module).returns([T::Array[Module], T::Array[Module]]) }
395
+ def collect_dynamic_mixins_of(constant)
396
+ mixins_from_modules = {}.compare_by_identity
400
397
 
401
398
  Class.new do
402
- # rubocop:disable Style/MethodMissingSuper, Style/MissingRespondToMissing
403
- def method_missing(symbol, *args)
399
+ # Override the `self.include` method
400
+ define_singleton_method(:include) do |mod|
401
+ # Take a snapshot of the list of singleton class ancestors
402
+ # before the actual include
403
+ before = singleton_class.ancestors
404
+ # Call the actual `include` method with the supplied module
405
+ include_result = super(mod)
406
+ # Take a snapshot of the list of singleton class ancestors
407
+ # after the actual include
408
+ after = singleton_class.ancestors
409
+ # The difference is the modules that are added to the list
410
+ # of ancestors of the singleton class. Those are all the
411
+ # modules that were `extend`ed due to the `include` call.
412
+ #
413
+ # We record those modules on our lookup table keyed by
414
+ # the included module with the values being all the modules
415
+ # that that module pulls into the singleton class.
416
+ #
417
+ # We need to reverse the order, since the extend order should
418
+ # be the inverse of the ancestor order. That is, earlier
419
+ # extended modules would be later in the ancestor chain.
420
+ mixins_from_modules[mod] = (after - before).reverse!
421
+
422
+ include_result
423
+ rescue Exception # rubocop:disable Lint/RescueException
424
+ # this is a best effort, bail if we can't perform this
404
425
  end
405
426
 
406
- define_singleton_method(:include) do |mod|
407
- begin
408
- before = singleton_class.ancestors
409
- super(mod).tap do
410
- mixins_from_modules[mod] = singleton_class.ancestors - before
411
- end
412
- rescue Exception # rubocop:disable Lint/RescueException
413
- # this is a best effort, bail if we can't perform this
414
- end
427
+ # rubocop:disable Style/MissingRespondToMissing
428
+ def method_missing(symbol, *args)
429
+ # We need this here so that we can handle any random instance
430
+ # method calls on the fake including class that may be done by
431
+ # the included module during the `self.included` hook.
415
432
  end
416
433
 
417
434
  class << self
418
435
  def method_missing(symbol, *args)
436
+ # Similarly, we need this here so that we can handle any
437
+ # random class method calls on the fake including class
438
+ # that may be done by the included module during the
439
+ # `self.included` hook.
419
440
  end
420
441
  end
421
- # rubocop:enable Style/MethodMissingSuper, Style/MissingRespondToMissing
442
+ # rubocop:enable Style/MissingRespondToMissing
422
443
  end.include(constant)
423
444
 
424
- all_dynamic_extends = mixins_from_modules.delete(constant)
425
- all_dynamic_includes = mixins_from_modules.keys
426
- dynamic_extends_from_dynamic_includes = mixins_from_modules.values.flatten
427
- dynamic_extends = all_dynamic_extends - dynamic_extends_from_dynamic_includes
428
-
429
- all_dynamic_includes
430
- .select { |mod| (name = name_of(mod)) && !name.start_with?("T::") }
431
- .map do |mod|
432
- add_to_symbol_queue(name_of(mod))
433
-
434
- qname = qualified_name_of(mod)
435
- tree << RBI::Include.new(T.must(qname))
436
- end.join("\n")
445
+ [
446
+ # The value that corresponds to the original included constant
447
+ # is the list of all dynamically extended modules because of that
448
+ # constant. We grab that value by deleting the key for the original
449
+ # constant.
450
+ T.must(mixins_from_modules.delete(constant)),
451
+ # Since we deleted the original constant from the list of keys, all
452
+ # the keys that remain are the ones that are dynamically included modules
453
+ # during the include of the original constant.
454
+ mixins_from_modules.keys,
455
+ ]
456
+ end
457
+
458
+ sig { params(constant: Module, dynamic_extends: T::Array[Module]).returns(T::Array[Module]) }
459
+ def collect_mixed_in_class_methods(constant, dynamic_extends)
460
+ if Tapioca::Compilers::Sorbet.supports?(:mixes_in_class_methods_multiple_args)
461
+ # If we can generate multiple mixes_in_class_methods, then
462
+ # we want to use all dynamic extends that are not the constant itself
463
+ return dynamic_extends.select { |mod| mod != constant }
464
+ end
437
465
 
466
+ # For older Sorbet version, we do an explicit check for an AS::Concern
467
+ # related ClassMethods module.
438
468
  ancestors = singleton_class_of(constant).ancestors
439
469
  extends_as_concern = ancestors.any? do |mod|
440
470
  qualified_name_of(mod) == "::ActiveSupport::Concern"
@@ -442,17 +472,45 @@ module Tapioca
442
472
  class_methods_module = resolve_constant("#{name_of(constant)}::ClassMethods")
443
473
 
444
474
  mixed_in_module = if extends_as_concern && Module === class_methods_module
475
+ # If this module is a concern and the ClassMethods module exists
476
+ # then, we prefer to generate a mixes_in_class_methods call for
477
+ # that module only, since we only have a single shot.
445
478
  class_methods_module
446
479
  else
480
+ # Otherwise, we use the first dynamic extend module that is not
481
+ # the constant itself. We don't have a better heuristic in the
482
+ # absence of being able to supply multiple arguments.
447
483
  dynamic_extends.find { |mod| mod != constant }
448
484
  end
449
485
 
450
- return if mixed_in_module.nil?
486
+ Array(mixed_in_module)
487
+ end
488
+
489
+ sig { params(tree: RBI::Tree, constant: Module).void }
490
+ def compile_mixes_in_class_methods(tree, constant)
491
+ return if constant.is_a?(Class)
492
+
493
+ dynamic_extends, dynamic_includes = collect_dynamic_mixins_of(constant)
494
+
495
+ dynamic_includes
496
+ .select { |mod| (name = name_of(mod)) && !name.start_with?("T::") }
497
+ .map do |mod|
498
+ add_to_symbol_queue(name_of(mod))
451
499
 
452
- qualified_name = qualified_name_of(mixed_in_module)
453
- return if qualified_name.nil? || qualified_name == ""
500
+ qname = qualified_name_of(mod)
501
+ tree << RBI::Include.new(T.must(qname))
502
+ end
454
503
 
455
- tree << RBI::MixesInClassMethods.new(qualified_name)
504
+ mixed_in_class_methods = collect_mixed_in_class_methods(constant, dynamic_extends)
505
+ return if mixed_in_class_methods.empty?
506
+
507
+ mixed_in_class_methods.each do |mod|
508
+ add_to_symbol_queue(name_of(mod))
509
+
510
+ qualified_name = qualified_name_of(mod)
511
+ next if qualified_name.nil? || qualified_name.empty?
512
+ tree << RBI::MixesInClassMethods.new(qualified_name)
513
+ end
456
514
  rescue
457
515
  nil # silence errors
458
516
  end
@@ -486,11 +544,11 @@ module Tapioca
486
544
  next if name == :initialize
487
545
  vis = case visibility
488
546
  when :protected
489
- RBI::Visibility::Protected
547
+ RBI::Protected.new
490
548
  when :private
491
- RBI::Visibility::Private
549
+ RBI::Private.new
492
550
  else
493
- RBI::Visibility::Public
551
+ RBI::Public.new
494
552
  end
495
553
  compile_method(tree, module_name, mod, mod.instance_method(name), vis)
496
554
  end
@@ -500,9 +558,9 @@ module Tapioca
500
558
  sig { params(mod: Module).returns(T::Hash[Symbol, T::Array[Symbol]]) }
501
559
  def method_names_by_visibility(mod)
502
560
  {
503
- public: Module.instance_method(:public_instance_methods).bind(mod).call,
504
- protected: Module.instance_method(:protected_instance_methods).bind(mod).call,
505
- private: Module.instance_method(:private_instance_methods).bind(mod).call,
561
+ public: public_instance_methods_of(mod),
562
+ protected: protected_instance_methods_of(mod),
563
+ private: private_instance_methods_of(mod),
506
564
  }
507
565
  end
508
566
 
@@ -513,7 +571,7 @@ module Tapioca
513
571
  constant
514
572
  .props
515
573
  .keys
516
- .include?(method_name.gsub(/=$/, '').to_sym)
574
+ .include?(method_name.gsub(/=$/, "").to_sym)
517
575
  end
518
576
 
519
577
  sig do
@@ -525,7 +583,7 @@ module Tapioca
525
583
  visibility: RBI::Visibility
526
584
  ).void
527
585
  end
528
- def compile_method(tree, symbol_name, constant, method, visibility = RBI::Visibility::Public)
586
+ def compile_method(tree, symbol_name, constant, method, visibility = RBI::Public.new)
529
587
  return unless method
530
588
  return unless method.owner == constant
531
589
  return if symbol_ignored?(symbol_name) && !method_in_gem?(method)
@@ -615,7 +673,7 @@ module Tapioca
615
673
  sig << RBI::SigParam.new(name, type)
616
674
  end
617
675
 
618
- return_type = type_of(signature.return_type)
676
+ return_type = name_of_type(signature.return_type)
619
677
  sig.return_type = sanitize_signature_types(return_type)
620
678
  add_to_symbol_queue(sig.return_type)
621
679
 
@@ -638,12 +696,22 @@ module Tapioca
638
696
  sig
639
697
  end
640
698
 
699
+ sig { params(sig_string: String).returns(String) }
700
+ def sanitize_signature_types(sig_string)
701
+ sig_string
702
+ .gsub(".returns(<VOID>)", ".void")
703
+ .gsub("<VOID>", "void")
704
+ .gsub("<NOT-TYPED>", "T.untyped")
705
+ .gsub(".params()", "")
706
+ end
707
+
641
708
  sig { params(symbol_name: String).returns(T::Boolean) }
642
709
  def symbol_ignored?(symbol_name)
643
710
  SymbolLoader.ignore_symbol?(symbol_name)
644
711
  end
645
712
 
646
- SPECIAL_METHOD_NAMES = %w[! ~ +@ ** -@ * / % + - << >> & | ^ < <= => > >= == === != =~ !~ <=> [] []= `]
713
+ SPECIAL_METHOD_NAMES = ["!", "~", "+@", "**", "-@", "*", "/", "%", "+", "-", "<<", ">>", "&", "|", "^", "<",
714
+ "<=", "=>", ">", ">=", "==", "===", "!=", "=~", "!~", "<=>", "[]", "[]=", "`"]
647
715
 
648
716
  sig { params(name: String).returns(T::Boolean) }
649
717
  def valid_method_name?(name)
@@ -708,40 +776,6 @@ module Tapioca
708
776
  nil
709
777
  end
710
778
 
711
- sig { params(constant: BasicObject).returns(Class).checked(:never) }
712
- def class_of(constant)
713
- Kernel.instance_method(:class).bind(constant).call
714
- end
715
-
716
- sig { params(constant: Module).returns(T::Array[Symbol]) }
717
- def constants_of(constant)
718
- Module.instance_method(:constants).bind(constant).call(false)
719
- end
720
-
721
- sig { params(constant: Module).returns(T.nilable(String)) }
722
- def raw_name_of(constant)
723
- Module.instance_method(:name).bind(constant).call
724
- end
725
-
726
- sig { params(constant: Module).returns(Class) }
727
- def singleton_class_of(constant)
728
- Object.instance_method(:singleton_class).bind(constant).call
729
- end
730
-
731
- sig { params(constant: Module).returns(T::Array[Module]) }
732
- def ancestors_of(constant)
733
- Module.instance_method(:ancestors).bind(constant).call
734
- end
735
-
736
- sig { params(constant: Module).returns(T::Array[Module]) }
737
- def inherited_ancestors_of(constant)
738
- if Class === constant
739
- ancestors_of(superclass_of(constant) || Object)
740
- else
741
- Module.ancestors
742
- end
743
- end
744
-
745
779
  sig { params(constant: Module).returns(T::Array[Module]) }
746
780
  def interesting_ancestors_of(constant)
747
781
  inherited_ancestors_ids = Set.new(
@@ -771,9 +805,9 @@ module Tapioca
771
805
 
772
806
  sig { params(constant: Module).returns(T.nilable(String)) }
773
807
  def name_of(constant)
774
- name = name_of_proxy_target(constant)
808
+ name = name_of_proxy_target(constant, super(class_of(constant)))
775
809
  return name if name
776
- name = raw_name_of(constant)
810
+ name = super(constant)
777
811
  return if name.nil?
778
812
  return unless are_equal?(constant, resolve_constant(name, inherit: true))
779
813
  name = "Struct" if name =~ /^(::)?Struct::[^:]+$/
@@ -793,67 +827,18 @@ module Tapioca
793
827
  "#{type_name}[#{type_variable_names}]"
794
828
  end
795
829
 
796
- sig { params(constant: Module).returns(T.nilable(String)) }
797
- def name_of_proxy_target(constant)
798
- klass = class_of(constant)
799
- return unless raw_name_of(klass) == "ActiveSupport::Deprecation::DeprecatedConstantProxy"
830
+ sig { params(constant: Module, class_name: T.nilable(String)).returns(T.nilable(String)) }
831
+ def name_of_proxy_target(constant, class_name)
832
+ return unless class_name == "ActiveSupport::Deprecation::DeprecatedConstantProxy"
800
833
  # We are dealing with a ActiveSupport::Deprecation::DeprecatedConstantProxy
801
834
  # so try to get the name of the target class
802
835
  begin
803
- target = Kernel.instance_method(:send).bind(constant).call(:target)
836
+ target = constant.__send__(:target)
804
837
  rescue NoMethodError
805
838
  return
806
839
  end
807
840
 
808
- raw_name_of(target)
809
- end
810
-
811
- sig { params(constant: Module).returns(T.nilable(String)) }
812
- def qualified_name_of(constant)
813
- name = name_of(constant)
814
- return if name.nil?
815
-
816
- if name.start_with?("::")
817
- name
818
- else
819
- "::#{name}"
820
- end
821
- end
822
-
823
- sig { params(constant: Class).returns(T.nilable(Class)) }
824
- def superclass_of(constant)
825
- Class.instance_method(:superclass).bind(constant).call
826
- end
827
-
828
- sig { params(method: T.any(UnboundMethod, Method)).returns(T.untyped) }
829
- def signature_of(method)
830
- T::Private::Methods.signature_for_method(method)
831
- rescue LoadError, StandardError
832
- nil
833
- end
834
-
835
- sig { params(sig_string: String).returns(String) }
836
- def sanitize_signature_types(sig_string)
837
- sig_string
838
- .gsub(".returns(<VOID>)", ".void")
839
- .gsub("<VOID>", "void")
840
- .gsub("<NOT-TYPED>", "T.untyped")
841
- .gsub(".params()", "")
842
- end
843
-
844
- sig { params(constant: T::Types::Base).returns(String) }
845
- def type_of(constant)
846
- constant.to_s.gsub(/\bAttachedClass\b/, "T.attached_class")
847
- end
848
-
849
- sig { params(object: BasicObject).returns(Integer).checked(:never) }
850
- def object_id_of(object)
851
- Object.instance_method(:object_id).bind(object).call
852
- end
853
-
854
- sig { params(constant: Module, other: BasicObject).returns(T::Boolean).checked(:never) }
855
- def are_equal?(constant, other)
856
- BasicObject.instance_method(:equal?).bind(constant).call(other)
841
+ name_of(target)
857
842
  end
858
843
  end
859
844
  end