From e21a444e1cef72b33b80fc07cdb796a95c1a6bb0 Mon Sep 17 00:00:00 2001 From: Sampo Kuokkanen Date: Fri, 22 May 2026 12:04:36 +0900 Subject: [PATCH] Add specs for defined? on protected methods (MRI Bug #22076) Covers protected methods accessed via subclass receiver, base-class receiver, and a shared included module. The module case is guarded by ruby_bug "#22076" until 4.1. MRI's defined? returned nil when the call would have succeeded; fix is in master with backports REQUIRED for 3.3, 3.4, 4.0. --- language/defined_spec.rb | 16 ++++++++++++++++ language/fixtures/defined.rb | 27 +++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/language/defined_spec.rb b/language/defined_spec.rb index 3fd611d09e..6846179a7c 100644 --- a/language/defined_spec.rb +++ b/language/defined_spec.rb @@ -211,6 +211,22 @@ }.should complain(/warning: possibly useless use of defined\? in void context/, verbose: true) end end + + describe "for a protected method" do + it "returns 'method' when the receiver is a subclass instance" do + DefinedSpecs::ProtectedBase.new.defined_on(DefinedSpecs::ProtectedSubclass.new).should == "method" + end + + it "returns 'method' when the receiver is the base class instance" do + DefinedSpecs::ProtectedSubclass.new.defined_on(DefinedSpecs::ProtectedBase.new).should == "method" + end + + ruby_bug "#22076", ""..."4.1" do + it "returns 'method' when the receiver is a sibling class instance via a shared included module" do + DefinedSpecs::ProtectedIncluderA.new.defined_on(DefinedSpecs::ProtectedIncluderB.new).should == "method" + end + end + end end describe "The defined? keyword for an expression" do diff --git a/language/fixtures/defined.rb b/language/fixtures/defined.rb index 3761cfa5bd..15bd7c50cf 100644 --- a/language/fixtures/defined.rb +++ b/language/fixtures/defined.rb @@ -299,6 +299,33 @@ def method_no_args super end end + + class ProtectedBase + def m; end + protected :m + def defined_on(o) + defined?(o.m) + end + end + + class ProtectedSubclass < ProtectedBase + end + + module ProtectedInModule + def m; end + protected :m + def defined_on(o) + defined?(o.m) + end + end + + class ProtectedIncluderA + include ProtectedInModule + end + + class ProtectedIncluderB + include ProtectedInModule + end end class Object