Input
def format_fields(values):
# nested local helper, only reachable inside format_fields
def join(vals):
return "-".join(sorted(vals))
return join(values)
def report_missing(unresolved):
# str.join on a literal: no relation to the local join above
missing_list = ", ".join(sorted(unresolved))
return f"Could not resolve: {missing_list}"
Output
$ codegraph callees report_missing
Callees of "report_missing" (1):
function join
repro.py:3
$ codegraph callers join
Callers of "join" (2):
function format_fields
repro.py:1
function report_missing
repro.py:9
report_missing calls str.join, a builtin method on a string literal. The graph resolves it
by bare name to the nested join at line 3. Both edges are fabricated: report_missing has zero
project callees, and join has exactly one caller (format_fields).
- Two distinct failures stacked: attribute calls on non-project receivers
(", ".join(...))
shouldn't resolve to project symbols at all, and a function nested inside format_fields is
lexically unreachable from report_missing, so scope alone rules the edge out.
- Impact scales with name commonality.
join here, but same mechanism fires on get, run,
close, update across modules: impact/callers (blast radius before editing) then reports
dependents that don't exist, and affected can select wrong test files.
I believe this can be fixed in all languages that have built-ins by using a literalReceiverTypes in all of the src/extraction/languages/*.ts files. But a bit hacky and drift prone.
OR: don't patch extraction but resolver instead: a nested local is not reachable from outside its container and should not be part of the graph attribution; exclude candidates whose containing symbol is a function other than the caller (or the caller's ancestor/children).
Input
Output
report_missingcallsstr.join, a builtin method on a string literal. The graph resolves itby bare name to the nested join at line 3. Both edges are fabricated:
report_missinghas zeroproject callees, and join has exactly one caller (
format_fields).(", ".join(...))shouldn't resolve to project symbols at all, and a function nested inside
format_fieldsislexically unreachable from
report_missing, so scope alone rules the edge out.joinhere, but same mechanism fires onget,run,close,updateacross modules: impact/callers (blast radius before editing) then reportsdependents that don't exist, and affected can select wrong test files.
I believe this can be fixed in all languages that have built-ins by using a
literalReceiverTypesin all of thesrc/extraction/languages/*.tsfiles. But a bit hacky and drift prone.OR: don't patch extraction but resolver instead: a nested local is not reachable from outside its container and should not be part of the graph attribution; exclude candidates whose containing symbol is a function other than the caller (or the caller's ancestor/children).