From d947ffdc1ba97fa99d96270b5b54fd8109dd1e49 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Sat, 7 Mar 2026 04:05:16 -0700 Subject: [PATCH 1/2] fix: build extToLang map unconditionally in CFG and dataflow passes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the native engine provides pre-computed CFG data for all functions, `needsFallback` is false and `extToLang` was never built. Since native- parsed symbols lack `_langId`, the langId lookup in buildCFGData fell through to null and skipped every file — leaving cfg_blocks empty. On each subsequent incremental build the empty-table guard triggered a full re-parse of all files (~300 ms), making native no-op rebuilds ~80× slower than WASM (319 ms vs 4 ms). Move the extToLang construction before the needsFallback check so it is always available. Apply the same fix to buildDataflowEdges defensively. Impact: 2 functions changed, 13 affected --- src/cfg.js | 16 +++++++++------- src/dataflow.js | 16 +++++++++------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/cfg.js b/src/cfg.js index 23e9aae..1ecd8eb 100644 --- a/src/cfg.js +++ b/src/cfg.js @@ -1046,9 +1046,17 @@ export function buildFunctionCFG(functionNode, langId) { export async function buildCFGData(db, fileSymbols, rootDir, _engineOpts) { // Lazily init WASM parsers if needed let parsers = null; - let extToLang = null; let needsFallback = false; + // Always build ext→langId map so native-only builds (where _langId is unset) + // can still derive the language from the file extension. + const extToLang = new Map(); + for (const entry of LANGUAGE_REGISTRY) { + for (const ext of entry.extensions) { + extToLang.set(ext, entry.id); + } + } + for (const [relPath, symbols] of fileSymbols) { if (!symbols._tree) { const ext = path.extname(relPath).toLowerCase(); @@ -1068,12 +1076,6 @@ export async function buildCFGData(db, fileSymbols, rootDir, _engineOpts) { if (needsFallback) { const { createParsers } = await import('./parser.js'); parsers = await createParsers(); - extToLang = new Map(); - for (const entry of LANGUAGE_REGISTRY) { - for (const ext of entry.extensions) { - extToLang.set(ext, entry.id); - } - } } let getParserFn = null; diff --git a/src/dataflow.js b/src/dataflow.js index 08b982f..0203bb2 100644 --- a/src/dataflow.js +++ b/src/dataflow.js @@ -1005,9 +1005,17 @@ function collectIdentifiers(node, out, rules) { export async function buildDataflowEdges(db, fileSymbols, rootDir, _engineOpts) { // Lazily init WASM parsers if needed let parsers = null; - let extToLang = null; let needsFallback = false; + // Always build ext→langId map so native-only builds (where _langId is unset) + // can still derive the language from the file extension. + const extToLang = new Map(); + for (const entry of LANGUAGE_REGISTRY) { + for (const ext of entry.extensions) { + extToLang.set(ext, entry.id); + } + } + for (const [relPath, symbols] of fileSymbols) { if (!symbols._tree && !symbols.dataflow) { const ext = path.extname(relPath).toLowerCase(); @@ -1021,12 +1029,6 @@ export async function buildDataflowEdges(db, fileSymbols, rootDir, _engineOpts) if (needsFallback) { const { createParsers } = await import('./parser.js'); parsers = await createParsers(); - extToLang = new Map(); - for (const entry of LANGUAGE_REGISTRY) { - for (const ext of entry.extensions) { - extToLang.set(ext, entry.id); - } - } } let getParserFn = null; From c34fe2aa88df8430f3e974b33d7542ed5d804bb0 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Sat, 7 Mar 2026 04:22:42 -0700 Subject: [PATCH 2/2] fix: remove dead extToLang guards now that the map is always defined Clean up four stale null-checks identified by Greptile review: - cfg.js:1120 and dataflow.js:1074: drop `!extToLang ||` from guards - cfg.js:1143 and dataflow.js:1097: simplify ternary to direct .get() Impact: 2 functions changed, 0 affected --- src/cfg.js | 4 ++-- src/dataflow.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cfg.js b/src/cfg.js index 1ecd8eb..d0f4d2e 100644 --- a/src/cfg.js +++ b/src/cfg.js @@ -1117,7 +1117,7 @@ export async function buildCFGData(db, fileSymbols, rootDir, _engineOpts) { // WASM fallback if no cached tree and not all native if (!tree && !allNative) { - if (!extToLang || !getParserFn) continue; + if (!getParserFn) continue; langId = extToLang.get(ext); if (!langId || !CFG_LANG_IDS.has(langId)) continue; @@ -1140,7 +1140,7 @@ export async function buildCFGData(db, fileSymbols, rootDir, _engineOpts) { } if (!langId) { - langId = extToLang ? extToLang.get(ext) : null; + langId = extToLang.get(ext); if (!langId) continue; } diff --git a/src/dataflow.js b/src/dataflow.js index 0203bb2..e5b926d 100644 --- a/src/dataflow.js +++ b/src/dataflow.js @@ -1071,7 +1071,7 @@ export async function buildDataflowEdges(db, fileSymbols, rootDir, _engineOpts) // WASM fallback if no cached tree if (!tree) { - if (!extToLang || !getParserFn) continue; + if (!getParserFn) continue; langId = extToLang.get(ext); if (!langId || !DATAFLOW_LANG_IDS.has(langId)) continue; @@ -1094,7 +1094,7 @@ export async function buildDataflowEdges(db, fileSymbols, rootDir, _engineOpts) } if (!langId) { - langId = extToLang ? extToLang.get(ext) : null; + langId = extToLang.get(ext); if (!langId) continue; }