diff --git a/lib/tapioca/dsl/compiler.rb b/lib/tapioca/dsl/compiler.rb index fbe33fcea..5cac1d9a6 100644 --- a/lib/tapioca/dsl/compiler.rb +++ b/lib/tapioca/dsl/compiler.rb @@ -74,9 +74,21 @@ def all_modules @all_modules ||= if @@requested_constants.any? @@requested_constants.grep(Module) else - ObjectSpace.each_object(Module).to_a + ObjectSpace.each_object(Module).reject { |mod| deprecated_constant_proxy?(mod) }.to_a end.freeze #: Enumerable[Module[top]]? end + + # Rails 8.1+ wraps deprecated constants in DeprecatedConstantProxy, which + # undefines most instance methods and warns from method_missing. Inspecting + # those modules during DSL discovery (is_a?, singleton_class, etc.) emits + # deprecation warnings even though Tapioca is only enumerating ObjectSpace. + # class_of uses Kernel#class so we can recognize the proxy without warning. + #: (Module[top] mod) -> bool + def deprecated_constant_proxy?(mod) + proxy_class_name = name_of(class_of(mod)) + proxy_class_name == "ActiveSupport::Deprecation::DeprecatedConstantProxy" || + proxy_class_name == "ActiveSupport::DeprecatedConstantProxy" + end end #: ( diff --git a/spec/tapioca/dsl/compilers/active_support_concern_spec.rb b/spec/tapioca/dsl/compilers/active_support_concern_spec.rb index 2b46844e5..3e1c3b3dc 100644 --- a/spec/tapioca/dsl/compilers/active_support_concern_spec.rb +++ b/spec/tapioca/dsl/compilers/active_support_concern_spec.rb @@ -14,6 +14,29 @@ def before_setup end describe "gather_constants" do + it "does not gather ActiveSupport deprecation proxies and does not warn" do + warnings = [] + previous_behavior = ActiveSupport.deprecator.behavior + ActiveSupport.deprecator.behavior = ->(message, *) { warnings << message.to_s } + + begin + proxy = ActiveSupport::Deprecation::DeprecatedConstantProxy.new( + "FakeDeprecatedConstant", + "Module", + ActiveSupport.deprecator, + ) + # Name the proxy so discovery can trigger its warning. + ActiveSupportConcernSpec.const_set(:FakeDeprecatedConstant, proxy) + gathered_constants + all_modules = Tapioca::Dsl::Compilers::ActiveSupportConcern.send(:all_modules) + ensure + ActiveSupport.deprecator.behavior = previous_behavior + end + + refute(all_modules.any? { |mod| Tapioca::Runtime::Reflection.are_equal?(mod, proxy) }) + assert_empty(warnings.grep(/FakeDeprecatedConstant/)) + end + it "does not gather anonymous constants" do add_ruby_file("test_case.rb", <<~RUBY) module TestCase