diff --git a/lib/tapioca/dsl/compilers/active_support_concern.rb b/lib/tapioca/dsl/compilers/active_support_concern.rb index 2b7a723cf..9f9e65634 100644 --- a/lib/tapioca/dsl/compilers/active_support_concern.rb +++ b/lib/tapioca/dsl/compilers/active_support_concern.rb @@ -68,10 +68,14 @@ class << self #: -> T::Enumerable[Module] def gather_constants all_modules.select do |mod| - name_of(mod) && # i.e. not anonymous - !mod.singleton_class? && - ActiveSupport::Concern > mod.singleton_class && - has_dependencies?(mod) + begin + name_of(mod) && # i.e. not anonymous + !mod.singleton_class? && + ActiveSupport::Concern > mod.singleton_class && + has_dependencies?(mod) + rescue ActiveSupport::DeprecationException + false + end end end diff --git a/lib/tapioca/dsl/compilers/mixed_in_class_attributes.rb b/lib/tapioca/dsl/compilers/mixed_in_class_attributes.rb index cf607b269..7a0d113c5 100644 --- a/lib/tapioca/dsl/compilers/mixed_in_class_attributes.rb +++ b/lib/tapioca/dsl/compilers/mixed_in_class_attributes.rb @@ -67,7 +67,13 @@ class << self def gather_constants # Select all non-anonymous modules that have overridden Module.included all_modules.select do |mod| - !mod.is_a?(Class) && name_of(mod) && Runtime::Reflection.method_of(mod, :included).owner != Module + begin + !mod.is_a?(Class) && + name_of(mod) && + Runtime::Reflection.method_of(mod, :included).owner != Module + rescue ActiveSupport::DeprecationException + false + end end end end diff --git a/lib/tapioca/dsl/compilers/url_helpers.rb b/lib/tapioca/dsl/compilers/url_helpers.rb index a954046ae..effc810f0 100644 --- a/lib/tapioca/dsl/compilers/url_helpers.rb +++ b/lib/tapioca/dsl/compilers/url_helpers.rb @@ -114,18 +114,22 @@ def gather_constants Object.const_set(:GeneratedPathHelpersModule, path_helpers_module) constants = all_modules.select do |mod| - next unless name_of(mod) - - # Fast-path to quickly disqualify most cases - next false unless url_helpers_module > mod || # rubocop:disable Style/InvertibleUnlessCondition - path_helpers_module > mod || - url_helpers_module > mod.singleton_class || - path_helpers_module > mod.singleton_class - - includes_helper?(mod, url_helpers_module) || - includes_helper?(mod, path_helpers_module) || - includes_helper?(mod.singleton_class, url_helpers_module) || - includes_helper?(mod.singleton_class, path_helpers_module) + begin + next unless name_of(mod) + + # Fast-path to quickly disqualify most cases + next false unless url_helpers_module > mod || # rubocop:disable Style/InvertibleUnlessCondition + path_helpers_module > mod || + url_helpers_module > mod.singleton_class || + path_helpers_module > mod.singleton_class + + includes_helper?(mod, url_helpers_module) || + includes_helper?(mod, path_helpers_module) || + includes_helper?(mod.singleton_class, url_helpers_module) || + includes_helper?(mod.singleton_class, path_helpers_module) + rescue ActiveSupport::DeprecationException + next false + end end constants.concat(NON_DISCOVERABLE_INCLUDERS).push(GeneratedUrlHelpersModule, GeneratedPathHelpersModule) diff --git a/spec/tapioca/dsl/compilers/active_support_concern_spec.rb b/spec/tapioca/dsl/compilers/active_support_concern_spec.rb index 2b46844e5..906529940 100644 --- a/spec/tapioca/dsl/compilers/active_support_concern_spec.rb +++ b/spec/tapioca/dsl/compilers/active_support_concern_spec.rb @@ -14,6 +14,45 @@ def before_setup end describe "gather_constants" do + it "ignores modules that raise ActiveSupport::DeprecationException" do + add_ruby_file("deprecated_case.rb", <<~RUBY) + module TestCase + module Foo + extend ActiveSupport::Concern + end + + # This module will simulate a deprecated constant that raises when inspected + module DeprecatedMod + end + + module Bar + extend ActiveSupport::Concern + include Foo + end + end + RUBY + + # Stub name_of to raise for our specific deprecated module and verify no crash + compiler = Tapioca::Dsl::Compilers::ActiveSupportConcern + original = Tapioca::Dsl::Compiler.method(:name_of) + begin + def compiler.name_of(mod) + if mod == TestCase::DeprecatedMod + raise ActiveSupport::DeprecationException + else + Tapioca::Dsl::Compiler.method(:name_of).call(mod) + end + end + + # Presence of DeprecatedMod should not cause crashes; Bar is the only gatherable constant + assert_equal(["TestCase::Bar"], gathered_constants_in_namespace(:TestCase)) + ensure + # Restore original implementation + def compiler.name_of(mod) + original.call(mod) + end + end + end it "does not gather anonymous constants" do add_ruby_file("test_case.rb", <<~RUBY) module TestCase diff --git a/spec/tapioca/dsl/compilers/mixed_in_class_attributes_spec.rb b/spec/tapioca/dsl/compilers/mixed_in_class_attributes_spec.rb index 7b41c089a..27f5f607e 100644 --- a/spec/tapioca/dsl/compilers/mixed_in_class_attributes_spec.rb +++ b/spec/tapioca/dsl/compilers/mixed_in_class_attributes_spec.rb @@ -19,6 +19,40 @@ def before_setup end describe "gather_constants" do + it "ignores modules that raise ActiveSupport::DeprecationException" do + add_ruby_file("file.rb", <<~RUBY) + module ManualIncluded + def self.included(base); end + end + + module Concern + extend ActiveSupport::Concern + end + + module DeprecatedMod + end + RUBY + + compiler = Tapioca::Dsl::Compilers::MixedInClassAttributes + original = Tapioca::Dsl::Compiler.method(:name_of) + begin + def compiler.name_of(mod) + if mod == DeprecatedMod + raise ActiveSupport::DeprecationException + else + Tapioca::Dsl::Compiler.method(:name_of).call(mod) + end + end + + assert_includes(gathered_constants, "ManualIncluded") + assert_includes(gathered_constants, "Concern") + refute_includes(gathered_constants, "DeprecatedMod") + ensure + def compiler.name_of(mod) + original.call(mod) + end + end + end it "gathers modules that respond to class_attribute" do add_ruby_file("file.rb", <<~RUBY) module ManualIncluded diff --git a/spec/tapioca/dsl/compilers/url_helpers_spec.rb b/spec/tapioca/dsl/compilers/url_helpers_spec.rb index c060e4029..cd1d76dcc 100644 --- a/spec/tapioca/dsl/compilers/url_helpers_spec.rb +++ b/spec/tapioca/dsl/compilers/url_helpers_spec.rb @@ -14,6 +14,39 @@ def before_setup end describe "initialize" do + it "ignores modules that raise ActiveSupport::DeprecationException" do + add_ruby_file("content.rb", <<~RUBY) + class Application < Rails::Application + end + + module DeprecatedMod + end + RUBY + + compiler = Tapioca::Dsl::Compilers::UrlHelpers + original = Tapioca::Dsl::Compiler.method(:name_of) + begin + def compiler.name_of(mod) + if mod == DeprecatedMod + raise ActiveSupport::DeprecationException + else + Tapioca::Dsl::Compiler.method(:name_of).call(mod) + end + end + + assert_equal( + [ + "GeneratedPathHelpersModule", + "GeneratedUrlHelpersModule", + ], + gathered_constants, + ) + ensure + def compiler.name_of(mod) + original.call(mod) + end + end + end it "does not gather constants when url_helpers is not included" do add_ruby_file("content.rb", <<~RUBY) class Application < Rails::Application