Skip to content

fix(resolution): decline Python module-scope collection method guesses (#1652) - #1776

Merged
colbymchenry merged 1 commit into
mainfrom
forge/fix-1652-python-module-scope-dict-methods
Sep 8, 2026
Merged

colbymchenry merged 1 commit into
mainfrom
forge/fix-1652-python-module-scope-dict-methods

Conversation

@colbymchenry

Copy link
Copy Markdown
Owner

Summary

Fixes #1652.

Python calls like DEFAULTS.get(name) on a module-scope dict (or other builtin collection) were resolving to unrelated project methods (LRUCache.get) via instance-method single-survivor matching.

Root cause: isBuiltInOrExternal's "known class" escape used knownNames.has(capitalized), and knownNames includes variables. The variable DEFAULTS itself punched through the builtin-method filter; matchMethodCall Strategy 3 then bound the only Python method named get.

Fix (receiver-type gate, as preferred on the issue):

  1. Decline dotted builtin-collection methods when the receiver is a same-file module-scope variable/constant whose signature is a collection literal or constructor ({}/[]/dict()/list()/set()/tuple()/frozenset(), etc.).
  2. Require an actual Python class/struct/interface node for the class escape (not any known name).
  3. Keep the #1681 imported-project-module escape.

Tests

Verify (Linux)

  • Fail→pass: new suite 14 failed / 1 passed on main @ 8733c288; 15/15 with this change.
  • Rebuilt CLI on the issue fixture: codegraph callers get --json[]; read_setting no longer calls cache.py methods.

Related open PR #1704 is broader (attr-type / branch-guard / kernel); this PR is the focused #1652 slice only.

Use same-file module variable initializers to filter builtin collection calls before class-name heuristics, and require a Python type node for the class escape. Preserve imported project module calls.

Add callers/callees regressions for dict, list, set, tuple, and frozenset, with a real instance control and same-name bindings across files.

Validation on Linux with Node 22.19.0: the new suite had 14 failures and one passing control on main at 8733c28; all 216 tests in the new suite, call-receiver-no-fabrication, and resolution now pass. TypeScript and copy-assets pass. Re-indexed /tmp/cg-1652-repro: callers get is empty and read_setting no longer calls a cache.py method.
@colbymchenry
colbymchenry merged commit 195888d into main Sep 8, 2026
@colbymchenry
colbymchenry deleted the forge/fix-1652-python-module-scope-dict-methods branch September 8, 2026 15:29
bompus added a commit to bompus/codegraph that referenced this pull request Sep 8, 2026
Second reconcile of the day. Upstream landed colbymchenry#1776 and colbymchenry#1778 after this
morning's merge (82cf9f0), and colbymchenry#1778 rewrote the lua variable-pairing code
in both arms, where the fork hangs markdown references off the node it
creates. Three conflicts, two of them the same collision twice.

true merge, both sides needed:
  codegraph-kernel/src/lua.rs     upstream's `value.map(...)` replaces the
                              fork's `values.get(i).map(...)`; the fork's
                              `let row = self.create_node(` is kept because
                              the markdown-refs block below the conflict
                              binds `row` and merged cleanly, so upstream's
                              side alone does not compile.
  src/extraction/tree-sitter.ts   the same shape in the WASM arm: upstream's
                              `target.name` with the fork's `const varNode`
                              capture and extractMarkdownPathReferences call.

union, upstream only:
  CHANGELOG.md                the fork side is empty, so this is upstream's
                              colbymchenry#1652 and colbymchenry#1730/colbymchenry#1731 entries verbatim.
inth3shadows added a commit to inth3shadows/codegraph that referenced this pull request Sep 8, 2026
The maintainer asked on colbymchenry#1704 to drop the bits that were landing separately
once colbymchenry#1749 merged. Both named PRs are on main now (colbymchenry#1748 bb1d309, colbymchenry#1749
edcd36e), along with three more python-resolution changes the same morning,
so this reconciles against all of them rather than only the two.

Six conflicts, resolved by what each side is now the authority on:

- src/resolution/index.ts — take main in all three hunks. Its
  isPythonProjectModule is our own, byte-identical apart from one comment,
  so ours is deleted rather than merged; git had auto-merged both copies
  into a duplicate definition. Its isKnownClass is the stricter colbymchenry#1776
  version, and the collection-binding filter now short-circuits before the
  class escape.

- __tests__/resolution.test.ts — take main. Our +54 was subsumed by its
  +347; the delta of this branch against main for that file is now zero,
  which is exactly what was asked for.

- src/extraction/tree-sitter.ts and codegraph-kernel/src/python.rs — keep
  both arms, main's first. colbymchenry#1748 claims the call-chain receiver
  (`d.setdefault(k, []).append(v)`) and encodes it as `<inner>().<method>`;
  ours stays the catch-all beneath it for the shapes that arm does not
  match, the attribute chain (`self.data.append`) and the subscript
  (`d[k].append`). The comments on both arms said they covered the
  call-chain shape and no longer do, so they are corrected in place.

- __tests__/fixtures/kernel-parity/torture.py — union. bucket_chains keeps
  the call receivers; fabrication_shapes keeps a real attribute chain and a
  subscript, since its former call-chain line is now bucket_chains' job.

What this branch still carries that main does not: the python attribute-type
inference read from the AST, the chained-receiver gate, the kernel mirror for
non-call receivers, the branch-guard changes and the module-suffix index.

Verified: tsc --noEmit clean; npm test 4226 passed / 238 files. The three
remaining failures (object-literal-methods, two in ui-steps-api) reproduce on
a clean upstream/main worktree at ee83636 and are not from this merge; a
fourth, ui-server-api's "under 100 ms", is a wall-clock assertion that passed
and then failed at 113ms in the same command.

NOT verified: the Rust arm. There is no cargo on this machine, so every
kernel-*-parity suite describe.skipIf's itself and the python.rs change is
unexercised.
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: module-scope dict member calls can resolve to unrelated project methods

1 participant