fix(python): suppress weak same-name CALLS for get/run/execute - #1386
fix(python): suppress weak same-name CALLS for get/run/execute#1386Joseph-MingEn wants to merge 1 commit into
Conversation
Mirror TS/JS weak-method guards for Python attribute calls and bare generic callees so suffix_match cannot bind prior_cp.get / run() onto unrelated Methods (G2 / Yui WP-B C2). Co-authored-by: Cursor <cursoragent@cursor.com>
|
Thanks for opening this — it has been seen, and it is queued. This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence. Current review status: working through a backlog. What that means for this PR, concretely:
Things that will genuinely speed it up whenever review does happen:
If this fixes a bug, a reproduction we can run is worth more than a description of the symptom. Thanks for contributing, and sorry in advance for the wait. |
|
Thank you for the focused Python same-name fallback suppression and the tests around generic method names. This is now routed as a high-priority parsing fix in |
|
The false-positive class is real (prior_cp.get resolving to an unrelated _SessionRegistry.get is exactly the #1276 family) and mirroring the TS/JS guard is the right instinct. Three things before this can merge: (1) mechanical — the branch conflicts, DCO and lint are red, and all three pr-smoke legs fail; rebase + sign-off + lint first. (2) The blanket is_method=true for every Python attribute call in extract_calls.c changes semantics for every other is_method consumer — narrow it or make the case. (3) The hardcoded {get,run,execute} blacklist plus blanket weak-member suppression is a recall cut on a priority language: our #1000 lesson is that graph-shape changes need corpus-scale evidence, so please include Django-scale before/after edge counts (we can help run them once the branch is green). Related work just landed you should build on: #1647 now suppresses cross-language suffix_match CALLS. |
|
Closing this one, and I want to be precise about why, because you identified a real defect class that nothing else in the codebase catches — and half of your diagnosis is being built now with credit to you. What happened to each halfThe member half is now covered. #1903 landed on main ( The difference is exemptions. Your version flags every Python attribute call with none, which also drops The bare-call half is genuinely yours, uncovered by anything, and is being implemented. After #1903: def _run_with_heavy_slot(run):
return run() # still suffix_matches onto SatoriLive.run#1903 structurally cannot catch this — there is no receiver, so the guard never fires. I checked whether anything else does: #1647's cross-language suffix guard only fires when caller and target languages differ, and both are Python here. So this class survives on main today, and you are the only person who found it. The one change to your mechanismThe implementation keys on local binding rather than the The reasoning against the list is not that it is wrong today — it is that it is a claim about spellings rather than about whether the resolver knew anything, and it would age invisibly. Nothing fails when the generic-name distribution shifts; the graph just quietly loses different edges. Local binding covers Why this PR itself cannot be the vehiclePractical, not a judgement on the work: it does not compile against current main (#1903 renamed Credit where it is dueYou have two PRs here and both found real things. On #1371 you flagged the recall risk on your own PR, unprompted — that is rare and it is why the before/after numbers got run and it merged. Here you found a false-edge class that survives every guard currently on main. The diagnosis is yours even though the mechanism changed. Thank you, genuinely. If you would rather carry the bare-call implementation yourself, say so and I will hand it straight back. |
A Python `foo()` whose callee identifier is bound as a parameter of an
enclosing scope cannot be the module-level `foo` -- the parameter shadows it
for the whole body -- so resolving the call to a project Function/Method by a
weak short-name strategy fabricates the edge by construction:
def _run_with_heavy_slot(run):
return run() # bound an unrelated SatoriLive.run
The receiver-aware weak-member guard (DeusData#1276) cannot see this class at all: a
bare call has no receiver, so is_method is false and the guard never fires.
This is the bare-call counterpart of python_receiver_is_exempt.
Keyed on the SCOPE FACT, not on the callee's spelling. A list of
generic-looking names (get / run / execute) asserts that certain spellings are
usually noise, which is a claim about corpus fashion rather than about what the
resolver knew, and it ages invisibly: nothing fails when the distribution
shifts, the graph just quietly loses different edges. A parameter binding is
decidable from this file's AST outright.
Parameters only, deliberately. A parameter is in scope for the entire body
regardless of position and Python forbids `global` on one, so no flow analysis
is needed. Local assignments are flow- and binding-form-sensitive (`for`,
`with as`, `except as`, `:=`, unpacking, plus global/nonlocal overrides); a
partial body scan would suppress the wrong edges invisibly -- the same failure
mode that rules out the name list. Enclosing scopes are walked to the file root
so a closure over an outer parameter counts.
Wired at both pass_calls.c and pass_parallel.c with an identical language gate:
a guard on one resolver only diverges the sequential and parallel paths. The
weak-strategy drop-list is now a single shared predicate used by both the member
guard and this one, so they cannot disagree about what "weak" means; a unit test
pins that agreement.
Tests pin both directions. The pipeline positive control is a cross-file bare
call with NO import, so it resolves by a weak strategy this guard could have
killed -- asserting a same_module edge would prove nothing, since no guard
touches same_module for any input. Verified by breaking the guard in both
directions: under-suppressing fails the negatives and the extraction flag;
over-suppressing fails the positives, so they are not vacuous. Disabling only
the pass_parallel.c gate fails only the >=50-file test and nothing else.
805 passed / 0 failed across pipeline registry parallel extraction complexity
lsp_resolution_probe.
The defect class was identified by Joseph-MingEn in DeusData#1386, which proposed a
name-keyed shape; the diagnosis is theirs.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Co-authored-by: Joseph-MingEn <125283161+Joseph-MingEn@users.noreply.github.com>
Summary
get/run/execute), mirroring the existing TS/JS weak-method guard.attributecalls asis_methodduring extraction soprior_cp.get(...)cannotsuffix_matchonto unrelated Methods (e.g._SessionRegistry.get).run()Callable-parameter calls from binding to unrelated Methods (e.g.SatoriLive.run).Motivation (Yui WP-B C2 / G2)
On a large Python repo,
trace_pathhop-1 showed fabricated CALLS:router.submit_task→_SessionRegistry.getviastrategy=suffix_match/callee=prior_cp.get/ conf0.28gate._run_with_heavy_slot→SatoriLive.runviasuffix_match/ barerun()/ conf0.28Strong strategies (
import_map,same_module,lsp_*) are kept.Test plan
./build/c/test-runner registry(includes new python suppress unit tests)./build/c/test-runner pipeline(includespipeline_python_suppresses_weak_generic_edges)Made with Cursor