tapioca 0.4.27 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) 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 +138 -153
  34. data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +4 -4
  35. data/lib/tapioca/compilers/todos_compiler.rb +1 -1
  36. data/lib/tapioca/config.rb +2 -0
  37. data/lib/tapioca/config_builder.rb +4 -2
  38. data/lib/tapioca/constant_locator.rb +6 -8
  39. data/lib/tapioca/gemfile.rb +2 -4
  40. data/lib/tapioca/generator.rb +124 -40
  41. data/lib/tapioca/generic_type_registry.rb +25 -98
  42. data/lib/tapioca/helpers/active_record_column_type_helper.rb +98 -0
  43. data/lib/tapioca/internal.rb +2 -9
  44. data/lib/tapioca/loader.rb +13 -33
  45. data/lib/tapioca/rbi_ext/model.rb +122 -0
  46. data/lib/tapioca/reflection.rb +131 -0
  47. data/lib/tapioca/sorbet_ext/fixed_hash_patch.rb +1 -1
  48. data/lib/tapioca/sorbet_ext/generic_name_patch.rb +72 -4
  49. data/lib/tapioca/sorbet_ext/name_patch.rb +1 -1
  50. data/lib/tapioca/version.rb +1 -1
  51. data/lib/tapioca.rb +2 -1
  52. metadata +34 -22
  53. data/lib/tapioca/cli/main.rb +0 -146
  54. data/lib/tapioca/core_ext/class.rb +0 -28
  55. data/lib/tapioca/core_ext/string.rb +0 -18
  56. data/lib/tapioca/rbi/model.rb +0 -405
  57. data/lib/tapioca/rbi/printer.rb +0 -410
  58. data/lib/tapioca/rbi/rewriters/group_nodes.rb +0 -106
  59. data/lib/tapioca/rbi/rewriters/nest_non_public_methods.rb +0 -65
  60. data/lib/tapioca/rbi/rewriters/nest_singleton_methods.rb +0 -42
  61. data/lib/tapioca/rbi/rewriters/sort_nodes.rb +0 -86
  62. 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,19 +1,16 @@
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
 
@@ -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