Repository navigation
Ruby: namespaced-constant receivers (A::B.call) get no references edge, so the class has 0 callers #2503
Description
Summary
A Ruby call whose receiver is a namespaced constant (Billing::ChargeCard.call(...)) gets no references edge to the class, so the class shows 0 callers / 0 dependents even though it is called from many places. #1110 added the receiver references edge for a bare constant (ChargeCard.call), but the scope_resolution form is excluded.
This pattern is the norm in Rails apps that organize service objects or commands under namespaces (app/services/billing/charge_card.rb → Billing::ChargeCard.call(...)). In the Rails codebase we evaluated (v1.6.2), it accounts for roughly 1.6k call sites, and callers / impact / codegraph_explore returned nothing for any of the classes we checked.
Repro (v1.6.2)
# app/services/billing/charge_card.rb
module Billing
class ChargeCard
def self.call(**kw) = new(**kw).call
def initialize(amount:) = @amount = amount
def call = @amount
end
end
# app/services/checkout/complete.rb
module Checkout
class Complete
def call
Billing::ChargeCard.call(amount: 1) # namespaced receiver: no edge
end
end
end
# app/services/billing/refund.rb
module Billing
class Refund
def call
ChargeCard.call(amount: 2) # bare receiver: edge recorded
end
end
endActual output (v1.6.2, fresh codegraph init):
$ codegraph callers ChargeCard
Callers of "ChargeCard" (1):
class Billing::ChargeCard (ruby) — app/services/billing/charge_card.rb:2
method call [references]
app/services/billing/refund.rb:3 <- only the bare-receiver caller
$ codegraph callees call -f checkout/complete.rb
Callees of "call" (0):
method Checkout::Complete::call (ruby) — app/services/checkout/complete.rb:3
ℹ No callees found for "call"
$ codegraph callees call -f billing/refund.rb
Callees of "call" (2):
...
class ChargeCard [references]
app/services/billing/charge_card.rb:2
Expected: Checkout::Complete#call is listed as a caller of Billing::ChargeCard, the same as Billing::Refund#call.
Root cause
codegraph-kernel/src/ruby.rs emits the extra receiver references ref only when the receiver node kind is constant. The comment says so explicitly:
codegraph/codegraph-kernel/src/ruby.rs
Lines 823 to 833 in 5171a6a
| // Capitalized constant receiver (`Klass.static_call`, `RETRY_MAX.times`) | |
| // → an ADDITIONAL references ref at the RECEIVER's position. | |
| // (scope_resolution receivers get none — type ≠ constant.) | |
| if !skip && receiver.kind() == "constant" { | |
| self.push_ref_at( | |
| caller, | |
| &receiver_name.to_string(), | |
| edge_kind_index("references").unwrap(), | |
| receiver, | |
| ); | |
| } |
// Capitalized constant receiver (`Klass.static_call`, `RETRY_MAX.times`)
// → an ADDITIONAL references ref at the RECEIVER's position.
// (scope_resolution receivers get none — type ≠ constant.)
if !skip && receiver.kind() == "constant" {Suggested fix
Also emit the references ref when receiver.kind() == "scope_resolution" and its last segment is capitalized. Either:
- reference the full qualified name (
Billing::ChargeCard), resolving against the class node's qualified name, which is the precise option since last segments likeCreate/Updaterepeat across namespaces; or - reference the last segment, mirroring how
NS::Widget.new→instantiates Widgetis already handled a few lines above (L796-L811), and let the resolver disambiguate.
The first is preferable for Rails/Zeitwerk codebases, where the fully qualified constant maps one-to-one to a file.
Activity
fix(ruby): a call on a namespaced class links its caller to the class…
colbymchenry commentedon Oct 23, 2026
Thanks for the clear report and repro!
#2827 is merged to
main. A call on a namespaced class likeBilling::ChargeCard.call(...)or::Billing::ChargeCard.call(...)now links the caller to the class and to the class method it calls, socodegraph callers ChargeCardlistsCheckout::Complete#calltoo.The name is looked up the way Ruby does it, from the namespaces around the call outward. So
Users::CreateandBilling::Createaren't confused, and library constants likeActiveRecord::Basenever land on a project class of the same last name.Thanks also to @drakeo338, whose #2509 was helpful guidance. It ships in the next release; re-index your Ruby projects after upgrading.
fix(ruby): a call on a namespaced class links its caller to the class…
fix(rust): a ruby call on a namespaced class links to the class and i…
fix(resolve): port upstream's new Ruby, Pascal, CFML and JS/Rust reso…