perf(hunt): the caller search prepares each product file once per change

call_sites rescanned every line of every product file for declarations on each call; with one name per
candidate row that was the whole cost of the source producer. code_index prepares a file once (the names each
line declares), call_sites takes it, and callers_of keeps a cache of prepared files the producer context owns.
Same calls, tested against the unprepared scan. One Laravel change of 33 rows: 249 seconds before this pair of
commits, 20 now.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
Marius Ciepluch
2026-10-14 09:46:46 +02:00
co-authored by Claude Fable 5.1
parent 86d70831c8
commit 4cf9c85d22
3 changed files with 64 additions and 13 deletions
+4 -1
View File
@@ -56,6 +56,7 @@ class Context:
spans: dict[str, tuple[source.FunctionRange, ...]] = field(default_factory=dict)
range_errors: dict[str, str] = field(default_factory=dict)
irs: dict[str, FileIR] = field(default_factory=dict)
call_index: dict[str, tuple[source.CodeLine, ...]] = field(default_factory=dict)
diff_bytes: int = 0
@cached_property
@@ -211,7 +212,9 @@ def _source(root: Path, change: Change, path: str, line: int, ctx: Context) -> d
ctx.read(path)
if path in ctx.range_errors:
raise _Absent(ctx.range_errors[path])
facts = source.facts_for(root, path, line, entry_points=ctx.entry_points, files=ctx.product_files, texts=ctx.texts)
facts = source.facts_for(
root, path, line, entry_points=ctx.entry_points, files=ctx.product_files, texts=ctx.texts, index=ctx.call_index
)
# Select the occurrence at this line when several methods share a name.
spans = [r for r in ctx.ranges(path) if r.name == facts.function and r.start <= line <= r.end]
span = min(spans, key=lambda r: r.end - r.start) if spans else None