Skip to content

fix(pipeline): suppress weak Python member calls - #1324

Draft
Enferlain wants to merge 1 commit into
DeusData:mainfrom
Enferlain:fix/1276-python-weak-member-calls
Draft

fix(pipeline): suppress weak Python member calls#1324
Enferlain wants to merge 1 commit into
DeusData:mainfrom
Enferlain:fix/1276-python-weak-member-calls

Conversation

@Enferlain

Copy link
Copy Markdown

What does this PR do?

Fixes #1276.

Python attribute calls were not consistently classified as member calls, allowing weak suffix_match, unique, and field_type_hint resolutions to create false-positive CALLS edges when no import-reachable target existed.

This change marks non-self/cls/super receiver calls as method calls and applies the existing weak-member suppression policy consistently in sequential and parallel resolution. Strong LSP, import, same-module, and service resolutions remain available. Regression coverage exercises extraction plus both pipeline modes.

Verification

  • make -f Makefile.cbm test-focused TEST_SUITES='cypher extraction registry pipeline mcp index_resilience'
  • Result: 884 passed, 2 skipped

Checklist

  • Every commit is signed off (git commit -s) and follows the Contributor License Agreement
  • Full test suite passes locally (make -f Makefile.cbm test) — focused sanitized suites passed
  • Lint passes (make -f Makefile.cbm lint-ci) — not run locally
  • New behavior is covered by regression tests that would fail without this fix

Signed-off-by: imi <hoshinoimi@gmail.com>
@DeusData

Copy link
Copy Markdown
Owner

Reviewed properly, and this needs changes — but I want to start with what is right, because the direction is correct and the diagnosis behind it is excellent.

Issue #1276 is one of the best bug reports this repo has received. Reproducible on public code, a quantified repo-wide invariant (55 production→test edges in a 12k-node repo), and a correct implementation-level diagnosis — unflagged is_method plus field_type_hint doing little more than capitalising the receiver token. The fabricated edges you documented are genuinely wrong: accelerator.print() landing on a test mock at 0.85 confidence, .backward() on an unrelated FlashAttentionFunction, .step() picking one of 84 candidates at 0.01. Under our "a wrong edge is worse than a missing one" rule, suppressing those is right, and you landed it on the established Perl and TS/JS precedent including the subtle explicit-drop-list rationale that protects the lsp_* strategies in the parallel path.

Your tests are exemplary too — both resolver paths exercised, the parallel one reconstructed with the field_type_hint trigger, and assertions on the final graph rather than intermediate confidences. That is exactly what #1276's acceptance criteria asked for.

Now the problem, and unlike most red PRs I have looked at tonight, this CI failure is genuinely yours.

Our smoke fixture is:

from pkg import helper
result = helper.compute(42)

and all three smoke legs fail identically with FAIL: trace_path found 0 callers for 'compute'. The suppression removed a true main → compute edge.

The reason is a real Python-versus-TypeScript semantic difference rather than a coding slip. In Python, module.function() is the dominant cross-file call shape, and its receiver is an imported module the indexer already knows — not an unresolvable object. But the blanket is_method flag cannot distinguish a module receiver from an unknown-object receiver, so whenever such a call resolves through a weak strategy the true edge dies. TS/JS never had this failure mode because ES-module imports are not attribute calls on a namespace in the same way. This is not an edge case; it is the canonical Python pattern.

The fix shape: exempt receivers that are known imports of the file. Extraction already has the import table, or the resolution side can consult the import map before applying the guard — flag is_method only when the receiver identifier is not bound by an import. That preserves everything #1276 wants suppressed (accelerator and trainer are parameters, not imports) while keeping helper.compute alive.

Please also add a positive test pinning module.function() survival — that coverage gap is precisely what let this through, and your own checklist honestly noted the smoke suite was not run locally.

One more genuine failure: clang-format violations in the new extract_calls.c block, lines 2224-2235. make -f Makefile.cbm lint-ci reproduces it. One caveat that will save you a wasted round trip — our formatter is the Homebrew LLVM build; a standalone clang-format-20 produces spurious whole-file drift that looks nothing like the real lines.

Two of your six failures are not yours: security / codeql-gate timed out while the actual analyze job passed — a nondeterministic gate we have seen race on other PRs tonight — and ci-ok is just its aggregator.

One thing worth the maintainer's eyes that I have flagged separately: the unique_name drop also costs true edges in small projects or where the LSP misses. That trade was accepted for TS/JS on the strength of an LSP backstop, and Python's hybrid LSP gives a similar one — but Python is the flagship language, so it is worth a deliberate nod rather than inheritance.

With the import-receiver exemption this lands as a flagship graph-quality improvement. The hard part — finding and proving the false-edge factory — is already done.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python unresolved member calls produce false-positive CALLS edges via suffix_match and field_type_hint

2 participants