Skip to content

Commit ec4f102

Browse files
authored
fix(audit): distinguish "file not found" from "file has zero functions" (#2142)
* fix(audit): distinguish "file not found" from "file has zero functions" codegraph audit <file> printed the same "No file matching" message for a file that genuinely isn't in the graph and for a real, tracked file that just has zero own function/method/class definitions (e.g. a pure re-export barrel) — misleadingly implying the graph doesn't know about a file it actually parsed correctly. Adds AuditResult.found, set to false only when explainData finds zero matching file/symbol entries at all; presentation/audit.ts now renders a distinct message when found !== false but functions is still empty. Closes #2135 Impact: 3 functions changed, 3 affected * fix: base found flag on pre-filter results to avoid false negatives (#2142) auditData set found: false using the post `--file`/`--kind` filtered results array, so a function that genuinely exists in the graph but is filtered out entirely by those options was misreported as "not found" -- the exact bug this PR set out to fix, reachable via a different input shape. Check explained.results (pre-filter) instead. Impact: 1 functions changed, 3 affected
1 parent 08644a1 commit ec4f102

4 files changed

Lines changed: 52 additions & 2 deletions

File tree

src/features/audit.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,18 @@ export function auditData(
162162
}
163163

164164
if (results.length === 0) {
165-
return { target, kind: explained.kind as 'function' | 'file', functions: [] };
165+
// `found` reflects whether the target exists in the graph at all -- check
166+
// the pre-filter `explained.results`, not the post `--file`/`--kind` filtered
167+
// `results`. Otherwise a real, tracked function filtered out entirely by
168+
// e.g. `--file src/other.js` would be misreported as "not found" (the same
169+
// bug this PR fixes for barrel files, reachable via a different input shape).
170+
const foundInGraph = explained.results.length > 0;
171+
return {
172+
target,
173+
kind: explained.kind as 'function' | 'file',
174+
functions: [],
175+
found: foundInGraph ? undefined : false,
176+
};
166177
}
167178

168179
// 2. Open DB for enrichment

src/presentation/audit.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,11 @@ export function audit(
9797
if (outputResult(data, null, opts)) return;
9898

9999
if (data.functions.length === 0) {
100-
console.log(`No ${data.kind === 'file' ? 'file' : 'function/symbol'} matching "${target}"`);
100+
if (data.found === false) {
101+
console.log(`No ${data.kind === 'file' ? 'file' : 'function/symbol'} matching "${target}"`);
102+
} else {
103+
console.log(`No functions to audit in "${target}" (0 own function/method/class definitions)`);
104+
}
101105
return;
102106
}
103107

src/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1731,6 +1731,11 @@ export interface AuditResult {
17311731
target: string;
17321732
kind: 'function' | 'file';
17331733
functions: AuditFunctionEntry[];
1734+
/** False only when target isn't tracked in the graph at all — distinguishes
1735+
* "no such file/symbol" from "file exists but has zero function-kind
1736+
* definitions" (e.g. a pure re-export barrel), which also yields an
1737+
* empty `functions` array. */
1738+
found?: boolean;
17341739
}
17351740

17361741
/** A single manifesto threshold breach reported against an audited function. */

tests/integration/audit.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ beforeAll(() => {
6363
insertNode(db, 'src/resolve.js', 'file', 'src/resolve.js', 0);
6464
insertNode(db, 'src/utils.js', 'file', 'src/utils.js', 0);
6565
insertNode(db, 'tests/builder.test.js', 'file', 'tests/builder.test.js', 0);
66+
// Barrel/re-export-only file — tracked in the graph but has zero own
67+
// function/method/class definitions.
68+
insertNode(db, 'src/barrel.js', 'file', 'src/barrel.js', 0);
6669

6770
// ── Function/class nodes ──
6871
const fnBuild = insertNode(
@@ -258,6 +261,33 @@ describe('auditData — edge cases', () => {
258261
expect(data.functions).toEqual([]);
259262
});
260263

264+
test('nonexistent function sets found: false', () => {
265+
const data = auditData('nonExistentFunction', dbPath);
266+
expect(data.found).toBe(false);
267+
});
268+
269+
test('nonexistent file sets found: false', () => {
270+
const data = auditData('src/does/not/exist.js', dbPath);
271+
expect(data.functions).toEqual([]);
272+
expect(data.found).toBe(false);
273+
});
274+
275+
test('barrel file with zero own functions does not set found: false', () => {
276+
const data = auditData('src/barrel.js', dbPath);
277+
expect(data.kind).toBe('file');
278+
expect(data.functions).toEqual([]);
279+
expect(data.found).not.toBe(false);
280+
});
281+
282+
test('--file filter removing every match does not set found: false', () => {
283+
// buildGraph exists in src/builder.js, not src/other.js -- the --file
284+
// filter legitimately empties the result set, but the symbol IS in the
285+
// graph, so this must not be reported as "not found".
286+
const data = auditData('buildGraph', dbPath, { file: 'src/other.js' });
287+
expect(data.functions).toEqual([]);
288+
expect(data.found).not.toBe(false);
289+
});
290+
261291
test('function with no complexity row has null health values', () => {
262292
const data = auditData('formatOutput', dbPath);
263293
const fn = data.functions.find((f) => f.name === 'formatOutput');

0 commit comments

Comments
 (0)