fix: stop silent fuzzy symbol substitution (#1473) (#1809)

Adapt @uvmplus's PR #1481 (3ecf7479) to the shared symbol lookup and
named-symbol flow resolver on current main. Missing names return not found
with suggestions, and exact matches with no callers stay empty.

Preserve #1512 definition grouping and --file narrowing, #173 qualified
misses, and codegraph_node's intentional fuzzy file lookup. Port the
upstream regression suite and cover the moved shared lookup paths.

Validation on Linux with Node 22: project build, 74 requested tests,
47 related flow tests, and 16 same-fixture CLI/MCP checks pass. Baseline
captured 12 failing tests and 13 failing fixture checks.

Fixes #1473.
Supersedes #1481.

Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>
This commit is contained in:
Colby Mchenry
2026-09-08 19:40:48 -05:00
committed by GitHub
co-authored by Colby McHenry
parent d3f9ef9bef
commit aed046e5c6
8 changed files with 306 additions and 33 deletions
+31
View File
@@ -157,6 +157,26 @@ describe.skipIf(!HAS_SQLITE)('matchesSymbol — module-qualified lookups (#173)'
expect(matches.length).toBe(0);
});
it('findAllSymbols rejects a fuzzy-only bare prefix with a suggestion (#1473)', () => {
expect(cg.getNodesByName('run_due')).toEqual([]);
expect(cg.searchNodes('run_due').length).toBeGreaterThan(0);
const all = findAllSymbols(cg, 'run_due');
expect(all.nodes).toEqual([]);
expect(all.note).toMatch(/Did you mean:.*run_due_tasks/);
});
it('findAllSymbols rejects an unknown qualifier even when the bare tail exists (#173)', () => {
expect(cg.getNodesByName('run').length).toBeGreaterThan(0);
expect(findAllSymbols(cg, 'missing::run').nodes).toEqual([]);
});
it('preserves codegraph_node file-basename lookup (#1473)', () => {
expect(cg.getNodesByName('stage_apply')).toEqual([]);
const matches = findSymbolMatches(cg, 'stage_apply');
expect(matches.length).toBeGreaterThan(0);
expect(matches[0]!.filePath).toMatch(/configurator\/stage_apply\.rs$/);
});
it('codegraph_node with a `file` hint pins an overloaded name to that file', async () => {
// `run` is defined in BOTH stage_apply.rs and stage_detect.rs. A bare lookup
// returns both; the `file` hint narrows to the one the caller saw in a trail.
@@ -391,4 +411,15 @@ describe.skipIf(!HAS_SQLITE)('lookupSymbolNodes — the shared path used by call
const { nodes } = lookupSymbolNodes(cg, 'chart.nonexistent_fn');
expect(nodes.length).toBe(0);
});
it.each(['grou', 'Group'])('rejects fuzzy-only bare name "%s" (#1473)', (symbol) => {
expect(cg.getNodesByName(symbol)).toEqual([]);
expect(cg.searchNodes(symbol).length).toBeGreaterThan(0);
expect(lookupSymbolNodes(cg, symbol)).toEqual({ nodes: [], ambiguous: false });
});
it('rejects an unknown qualifier even when the bare tail exists (#173)', () => {
expect(cg.getNodesByName('group').length).toBeGreaterThan(0);
expect(lookupSymbolNodes(cg, 'missing.group')).toEqual({ nodes: [], ambiguous: false });
});
});