Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion lib/tapioca/dsl/compiler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,19 @@ def all_modules
@@requested_constants.grep(Module)
else
ObjectSpace.each_object(Module).to_a
end.freeze #: Enumerable[Module[top]]?
end.reject { |mod| deprecated_constant_proxy?(mod) }.freeze #: Enumerable[Module[top]]?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to abide by the requested constants even if they result in warnings. Can you move this reject above?

ObjectSpace.each_object(Module).reject { |mod| deprecated_constant_proxy?(mod) }

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

Expand Down
17 changes: 17 additions & 0 deletions spec/tapioca/dsl/compilers/active_support_concern_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,23 @@ def before_setup
end

describe "gather_constants" do
it "does not gather ActiveSupport deprecation proxies and does not warn" do
require "active_support/concurrency/load_interlock_aware_monitor"

warnings = []
previous_behavior = ActiveSupport.deprecator.behavior
ActiveSupport.deprecator.behavior = ->(message, *) { warnings << message.to_s }

begin
constants = gathered_constants
ensure
ActiveSupport.deprecator.behavior = previous_behavior
end

refute_includes(constants, "ActiveSupport::Concurrency::LoadInterlockAwareMonitor")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't dive into it but agent suggested this diff instead and claimed that current refute_includes would pass even without this change because the name is delegated and becomes Monitor, it might be better to do the diff below. Also can you ensure the final test fails without your change?

- constants = gathered_constants
+ proxy = ActiveSupport::Concurrency.const_get(:LoadInterlockAwareMonitor, false)
+ gathered_constants
+ all_modules = Tapioca::Dsl::Compilers::ActiveSupportConcern.send(:all_modules)
...
- refute_includes(constants, "ActiveSupport::Concurrency::LoadInterlockAwareMonitor")
+ refute(all_modules.any? { |mod| Tapioca::Runtime::Reflection.are_equal?(mod, proxy) })
  assert_empty(warnings.grep(/LoadInterlockAwareMonitor/))

assert_empty(warnings.grep(/LoadInterlockAwareMonitor/))
end

it "does not gather anonymous constants" do
add_ruby_file("test_case.rb", <<~RUBY)
module TestCase
Expand Down
Loading