Skip to content

python: a method handed over as a value reaches its target (#1832) - #1968

Open
swapnilpaliwal-sd wants to merge 1 commit into
0.2.1from
fix/py-method-reference
Open

swapnilpaliwal-sd wants to merge 1 commit into
0.2.1from
fix/py-method-reference

Conversation

@swapnilpaliwal-sd

@swapnilpaliwal-sd swapnilpaliwal-sd commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Closes #1978

Part of #1832.

A bound or unbound method or a function named as a VALUE and not called
(executor.submit(self._work, x), Thread(target=svc.run), sorted(xs,
key=self.rank), [h.on_event], partial(svc.cancel, oid), view_func=
views.index, return self._handle) had no caller in the Python graph, so
impact OrderService.cancel did not name the function that queued it and
path said the two were independent. The engine already resolved what each
of these expressions denotes; it dropped the answer because no call
wrapped it.

call-edge-generation/method-reference.dl emits it as a call_chain_edge
with kind METHOD_REFERENCE and tier callback_registered, the tier the
other front ends use for a function handed over, so answers say "handed
over as a value" and never "calls it". Only what is resolved: a typed
receiver through the MRO, a class's unbound method, a scoped name, a
module member. Refused: an untyped receiver (stays as unresolved as
thing.cancel() is), a flowed value (a parameter, a name or attribute
assigned from an expression), a property read, a decorator application,
the argument of a one-dunder builtin (str(f)), and a comparison operand.

Measured on three open-source Python projects used for tuning: 3-4% more
resolved caller->callee pairs, and 20 of 20 sampled new edges right after the
guards (17 of 20 before the flowed-value guard). On three held-out projects,
graded blind by a reviewer who did not tune it: 39 of 40 sampled new edges
right (Wilson 95% interval 0.87-1.00). The one miss is a function handed to an
unregistration call; the only repeated wrong shape left is a function passed
to inspect.signature, about 1% of the new edges. The engine goldens gain only reference rows; the
torture oracle scorecard is unchanged.

Tests:

  • tests/cases/python/method-reference-reaches-its-target, 17 checks: every shape above as path/impact, and --tests reaching the target through a test that drives the executor. Controls: an untyped receiver, a same-named method on an unrelated class, the method's name in a string, __name__, str(f), and f is g do not link. On the base 3 of 17 pass (the controls), now 17 of 17.
  • Python engine suite (graph/test/python/run-tests.sh): 43/43. The goldens gain only METHOD_REFERENCE rows, all reviewed.
  • tests/fastpath.py --lang python 8/8. tests/run.py --lang python 462/462.

Follow-up, not in this change: treat inspect.signature/getfullargspec arguments and disconnect(...) arguments as reads, not handovers.

@swapnilpaliwal-sd swapnilpaliwal-sd added bug Something isn't working engine Resolution / call-graph engine rules python Python labels Oct 15, 2026
@swapnilpaliwal-sd swapnilpaliwal-sd added bug Something isn't working engine Resolution / call-graph engine rules python Python labels Oct 15, 2026
A bound or unbound method or a function named as a VALUE and not called
(executor.submit(self._work, x), Thread(target=svc.run), sorted(xs,
key=self.rank), [h.on_event], partial(svc.cancel, oid), view_func=
views.index, return self._handle) had no caller in the Python graph, so
impact OrderService.cancel did not name the function that queued it and
path said the two were independent. The engine already resolved what each
of these expressions denotes; it dropped the answer because no call
wrapped it.

call-edge-generation/method-reference.dl emits it as a call_chain_edge
with kind METHOD_REFERENCE and tier callback_registered, the tier the
other front ends use for a function handed over, so answers say "handed
over as a value" and never "calls it". Only what is resolved: a typed
receiver through the MRO, a class's unbound method, a scoped name, a
module member. Refused: an untyped receiver (stays as unresolved as
thing.cancel() is), a flowed value (a parameter, a name or attribute
assigned from an expression), a property read, a decorator application,
the argument of a one-dunder builtin (str(f)), and a comparison operand.

Measured on three open-source Python projects used for tuning: 3-4% more
resolved caller->callee pairs, and 20 of 20 sampled new edges right after the
guards (17 of 20 before the flowed-value guard). On three held-out projects,
graded blind by a reviewer who did not tune it: 39 of 40 sampled new edges
right (Wilson 95% interval 0.87-1.00). The one miss is a function handed to an
unregistration call; the only repeated wrong shape left is a function passed
to inspect.signature, about 1% of the new edges. The engine goldens gain only reference rows; the
torture oracle scorecard is unchanged.

Refs #1832
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working engine Resolution / call-graph engine rules python Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant