Repository navigation
fix(ruby): a call follows a class's real ancestors, in Ruby's order - #3169
Merged
Merged
Conversation
Since #1935 a receiver named after a class took what its supertypes declare, read by bare names: `class Human < ActiveRecord::Base` asked every class called `Base`, so rails' `human.name` reached Journey's `Node#name`, and `Topic.create` Digestor's `Node.create`. A value of a known class walked `extends` edges the same way, so `company.has_attribute?` took the `has_attribute?` of a `ClassMethods` module. And `class Mapper; include HttpHelpers` lost `mapper.get`, which 1.6.2 reached by a substring of the qualified name. The ancestors are now read as Ruby reads them: superclass and mixins found by constant lookup, a class's own body only, the module mixed in last asked first, and for an instance no module it `extend`s. - A receiver named after a class takes the class constant lookup finds, and past that class's own methods a word picks no namesake. - A call on a class reaches a superclass's `def self.`, what it `extend`s and the `ClassMethods` its concerns bring, never a mixin's instance method or a mixin's own `def self.`. - A bare name that is a method of the class around the call, taking no arguments and handing back what one class makes (`@jar ||= Jar.build(…)`), types the call on it, so `cookies.signed` reaches `ChainedCookieJars`. - A `?` or `!` method of Ruby's core classes needs a receiver named after the owner itself: sinatra's `bad_cookies.empty?` is no `Cookies::Jar#empty?`. Validation, base 4499d00 against this change (numbers in the PR): - rails-src 793d744c: 171,738 to 179,247 edges, 2,159 moved, 8,179 added, 670 lost. Added: Concern class methods (`Topic.find`), inherited instance methods (`topic.id`), helpers on bare calls (`concat`). Moved: to the first method in Ruby's order, off `Digestor::Node.create`. 86 sampled entries read: 0 false added. Lost: 442 `Node::name`-style false edges of the 670; about 30 were true by luck. - sinatra cb22afd7: 2 lost, both `bad_cookies.empty?`. mastodon (Ruby only) b0555670: 1 moved, 34 added, 1 lost. jekyll 97c3bdc8: same edges, 9 guesses now typed. Controls gin and flask byte-identical. - REG: rails-src 3 lost back; `company.has_attribute?` 14 of 14 on the instance method; rails-src/moved/24 (`table.foreign_key`, right by a substring) stays: it needs call-site flow, and the substring rule took 429 `error.message`. - Index CPU on rails-src, median of 5 interleaved: 74.9 to 73.2 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
colbymchenry
force-pushed
the
claude/fx-ruby-ancestors
branch
from
November 1, 2026 05:01
e8c76e4 to
7889010
Compare
bompus
added a commit
to bompus/codegraph
that referenced
this pull request
Nov 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Regression items 3, 4 and 6 of REG (Ruby): a call now follows a class's real ancestors, in Ruby's order.
company.has_attribute?reaches the instance method,mapper.getandcookies.signedcome back, and sinatra'sbad_cookies.empty?no longer linksCookies::Jar.Root cause
class Human < ActiveRecord::Baseasked every class calledBase, so 442 calls reached Journey'sNode#name-style methods (human.name,column.type), andTopic.createreachedDigestor::Node.create.Mapperlostinclude HttpHelpers, which 1.6.2 reached through a substring.extendsedges by simple name, socompany.has_attribute?took aClassMethodsmodule's.!/?methods and calls on what.newmade link their method #3049'sx.y?kept the lone-method guess, where a word of any module around the owner counted:bad_cookiessharesCookieswithSinatra::Cookies::Jar.Fix
extends.def self., what itextends and its concerns'ClassMethods, never a mixin's instance method or a mixin's owndef self..@cookie_jar ||= Cookies::CookieJar.build(…), the factory read too), types the call on it, unless the calling method binds that name.RUBY_STD_METHODS: a?or!method of Ruby's core classes needs a receiver named after the owner itself.record.new_record?and the rest of fix(ruby): calls to!/?methods and calls on what.newmade link their method #3049's gain stay.Validation
Red to green: 7 of 11 tests in
ruby-receiver-class-ancestry.test.tsfail on main; the rest guard the rules beside them.A/B against 4499d00, all CLI steps rc 0:
Classes, 86 sampled entries read against source, 0 false added:
Topic.find,Topic.create!,HelloJob.set), 2,034 inherited instance calls (topic.id,topic.reload), 1,715 bare helper calls in view tests (concat,content_tag), now in scope.savetoSuppressor#save, which callssuper), offFakeRecord::Base, offDigestor::Node.create.Node-style false edges, the falseClassMethodsandTimestamp#typepicks,Core::seton aConcurrent::Event. About 30 were true by luck, like 20xml.first, whoseString#firstcame throughActiveModel::Type::Stringread as coreString.REG's PUB entries: lost/20, lost/24, lost/25 and sinatra/added/3 hold.
company.has_attribute?14 of 14 on the instance method.rails-src/moved/24can't be fixed without harm:tableinadd_to(table)is aTableDefinitiononly because its one caller passesself. 1.6.2 got it from a substring of the qualified name, the rule that also sent 429error.messagetoWrongEncodingErrorand 144response.bodytoBuffer. Reaching it needs call-site flow, which nothing here has.Index CPU on rails-src, median of 5 interleaved: 74.9 to 73.2 s. No extraction change, so no kernel parity.
Suite on the top of this stack, 6e22039, on main 3bd9949: 857 files passed, 6 skipped; 10,662 tests passed, 0 failed, 87 skipped (kernel built, 4 workers). A rebuild after the rebase gave the same graphs as the A/B build on sinatra, serilog, eShopOnWeb, drupal-core and bookstack.
Stacked on the PHP PR.
🤖 Generated with Claude Code