#1668: Ruby `include`/`extend`/`prepend <Const>` in a class/module body now emits
a `mixes_in` edge to the module. The mixin is captured during the node walk and
resolved cross-file by resolve_ruby_member_calls (single-owner guard, reusing
the #1640 module nodes as targets). The shared call pass skips these markers so
they are not mislabeled as `calls`. `extend self` and non-constant args are
skipped; ambiguous/undefined modules produce no edge. Rails concern composition
is now visible to affected/explain.
#1669: affected <Class> seeds the reverse walk with the root's own member nodes
(one method/contains hop) so callers that bind at method granularity (e.g.
Service.call -> the def self.call node, #1634) are reachable from the class.
method/contains stay out of the general relation-filtered walk (no forward
noise), and the seeded member nodes are not reported as hits.
Full suite: 2924 passed, 3 skipped. Verified end-to-end (Rails-shaped repros)
plus edge cases: extend self / undefined / ambiguous mixins emit nothing, mixins
are not emitted as calls, member methods aren't reported, class-level callers
still resolve, and one-hop seeding does not pull in downstream classes' methods.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
#1640 (node extraction): the extractor only created nodes for `class Foo`, so
plain `module Foo`, `Foo = Struct.new(...) do ... end`, `Foo = Class.new(Super)`
and `Result = Data.define(...)` produced no container node — their methods hung
off the file via `contains` with dot-less labels and no edge could target them.
`module` is now a container type (methods attach via `method`, nested modules
included), and a constant assignment whose RHS is Struct.new/Class.new/Data.define
synthesizes a class node named after the constant, attaches block-defined methods
to it, and emits an `inherits` edge for `Class.new(Super)`. Plain constant
assignments (MAX = 100, X = Foo.new) are untouched.
#1634 (resolution): constant-receiver singleton calls (`Service.call`,
`Model.where`, `SomeJob.perform_async`) emitted no edge, so a Zeitwerk-autoloaded
Rails app (no requires) had near-zero cross-file edges. resolve_ruby_member_calls
now handles a capitalized receiver with any callee: bind to the class's owned
singleton/instance method (`def self.call`) when present, else to the class node
itself so inherited/dynamic class methods (ActiveRecord where/find_by) still give
blast-radius. Namespaced receivers resolve by bare class name. The
single-owning-class god-node guard is kept — ambiguous receivers resolve to
nothing, never a wrong edge.
The two compound: PaymentProcessor#process -> TaxCalculator.rate_for needs the
module node (#1640) AND the resolver (#1634); both now land.
Full suite: 2893 passed, 3 skipped. Adversarial smoke confirms no false class
nodes from plain/multiple assignments and no self-loops on self-class calls.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve Ruby `obj.method()` calls by the inferred type of the receiver
instead of by globally-unique method name. `p = Processor.new; p.run`
now emits a `calls` edge to `Processor#run` and survives name collisions
with unrelated `Worker#run` definitions, where the old name-based match
either resolved by luck or dropped the edge as ambiguous.
Introduces graphify/resolver_registry.py, a behavior-identical
formalization of the existing tail-of-extract() language resolution
passes (Swift #1356, Python #1446 become registered entries), and
graphify/ruby_resolution.py, its first new consumer. Receiver type is
inferred only from unambiguous local `var = ClassName.new` bindings;
ambiguous or unknown receivers resolve to nothing (no false positives).
Note: Ruby member calls are now excluded from name-based cross-file
resolution and resolved by inferred type only. This is an intentional
precision-over-recall change scoped to Ruby: a cross-file `var.method`
whose receiver type cannot be proven from a local `X.new` binding no
longer resolves by name-luck (it produces no edge rather than a possibly
wrong one), matching the project's confidence model.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>