You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Python: module-scope dict member calls can resolve to unrelated project methods #1652
When a Python module declares a module-scope dictionary (or other built-in collection) and calls methods on it (such as dict.get), unresolved method calls can fall through to bare-name method matching and fabricate calls edges to unrelated project classes that happen to declare a same-named method (e.g. LRUCache.get).
read_setting in settings.py never imports or references LRUCache, but DEFAULTS.get() is incorrectly attributed to LRUCache.get.
Impact on Circular Dependency Analysis
GraphQueryManager.getFileDependencies walks resolved call/reference edges. When a false call edge is created across files, it introduces a false file dependency. On projects with a one-way import structure, this can report hundreds of phantom cycles (e.g. 136 reported cycles vs 1 genuine import cycle).
Scope & Relation
Discussed in #1566 (which addressed the TypeScript/JavaScript/TSX/ArkTS receiver resolution and conformance boundaries). This issue tracks extending similar receiver type inference and fallback boundaries to Python module-scope and instance member calls.
Tried to build a reproduction for this and could not on 1.6.0 (npm latest) — posting the attempt so it is not re-attempted blind, and so you can tell me what my fixture is missing.
# pkg/use.pyCONFIG= {"a": 1}
REG=dict()
defreads(k):
returnCONFIG.get(k) # module-scope dict member -> no edgedefwrites(d):
REG.update(d) # module-scope dict member -> no edgedefchained(m, k, v):
returnm.setdefault(k, {}).get(v) # -> calls pkg/api.py:get FABRICATED
Result on a fresh index: the two module-scope dict calls produce no edges at all (correct), while the third line fabricates exact-match -> api.get.
So with a plain {...} / dict() module-scope binding and a same-named top-level function present, I do not see the misresolution this issue describes. What I do see is that the receiver shape decides it: a plain identifier receiver is filtered correctly, a call-expression receiver is not.
It needs something my fixture lacks — the dict built by a comprehension or a function call, a TypedDict/annotation, or a call through an import rather than same-file.
If you can share the receiver expression and how the dict is bound, I am happy to re-run it. Details that would pin it: the exact call line, how the dict is created, and whether the wrongly-resolved target is a module-level function or a class method.
Environment: 1.6.0 (also checked 1.5.0), Linux, fresh codegraph init, verified via both the CLI and direct SQL against .codegraph/codegraph.db.
DEFAULTS.get(name) (and other module-scope dict/list/set/tuple/frozenset member calls) no longer fabricate calls edges onto unrelated project methods such as LRUCache.get.
Root cause: the Python builtin-method filter treated any symbol in knownNames as a “known class,” so the variable DEFAULTS itself escaped the filter; matchMethodCall Strategy 3 then single-survivor-bound the only get method in the project.
Fix: decline when the receiver is a same-file module-scope collection binding (signature gate), and require an actual Python class/struct/interface for the class escape. Imported project-module calls (ledger.append, #1681) stay resolvable.
Verified on Linux against the issue fixture (callers get → empty) and a 15-case regression suite (fail→pass vs main).
Activity
inth3shadows commentedon Sep 4, 2026
Tried to build a reproduction for this and could not on 1.6.0 (npm latest) — posting the attempt so it is not re-attempted blind, and so you can tell me what my fixture is missing.
Result on a fresh index: the two module-scope dict calls produce no edges at all (correct), while the third line fabricates
exact-match->api.get.So with a plain
{...}/dict()module-scope binding and a same-named top-level function present, I do not see the misresolution this issue describes. What I do see is that the receiver shape decides it: a plain identifier receiver is filtered correctly, a call-expression receiver is not.Two possibilities:
TypedDict/annotation, or a call through an import rather than same-file.If you can share the receiver expression and how the dict is bound, I am happy to re-run it. Details that would pin it: the exact call line, how the dict is created, and whether the wrongly-resolved target is a module-level function or a class method.
Environment: 1.6.0 (also checked 1.5.0), Linux, fresh
codegraph init, verified via both the CLI and direct SQL against.codegraph/codegraph.db.colbymchenry commentedon Sep 8, 2026
Fixed in #1776.
DEFAULTS.get(name)(and other module-scope dict/list/set/tuple/frozenset member calls) no longer fabricatecallsedges onto unrelated project methods such asLRUCache.get.Root cause: the Python builtin-method filter treated any symbol in
knownNamesas a “known class,” so the variableDEFAULTSitself escaped the filter;matchMethodCallStrategy 3 then single-survivor-bound the onlygetmethod in the project.Fix: decline when the receiver is a same-file module-scope collection binding (signature gate), and require an actual Python class/struct/interface for the class escape. Imported project-module calls (
ledger.append, #1681) stay resolvable.Verified on Linux against the issue fixture (
callers get→ empty) and a 15-case regression suite (fail→pass vs main).fix(resolution): reject Python module collection method guesses (#1652)…
Merge origin/main (8c04734) into fork/consolidated