Skip to content

Commit ac23d2d

Browse files
prosdevclaude
andcommitted
fix(core): address code review — WASM cleanup, type coverage accuracy
- Fix Query object WASM leak: call q.delete() in finally after matches() - Fix type coverage ratio inflation: add arrow-total and function-total queries for accurate denominator (was counting only typed functions) - Fix try/catch-alone classification: fall through to regex instead of mapping to 'throw' style (try-catch is a mechanism, not a style) - Add comment explaining AST/regex import count asymmetry Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d082551 commit ac23d2d

3 files changed

Lines changed: 26 additions & 6 deletions

File tree

packages/core/src/pattern-matcher/rules.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,18 @@ export const TYPE_COVERAGE_QUERIES: PatternMatchRule[] = [
9090
category: 'type-coverage',
9191
query: '(function_declaration return_type: (type_annotation)) @match',
9292
},
93+
{
94+
// Count ALL arrow functions (typed or not) for accurate denominator
95+
id: 'arrow-total',
96+
category: 'type-coverage',
97+
query: '(arrow_function) @match',
98+
},
99+
{
100+
// Count ALL function declarations (typed or not) for accurate denominator
101+
id: 'function-total',
102+
category: 'type-coverage',
103+
query: '(function_declaration) @match',
104+
},
93105
];
94106

95107
// ============================================================================

packages/core/src/scanner/tree-sitter.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,13 +365,16 @@ export async function runQueries(
365365

366366
const QueryCls = QueryClass;
367367
for (const { id, query: queryString } of queries) {
368+
let q: InstanceType<typeof QueryCls> | null = null;
368369
try {
369-
const q = new QueryCls(lang, queryString);
370+
q = new QueryCls(lang, queryString);
370371
const matches = q.matches(tree.rootNode);
371372
results.set(id, matches.length);
372373
} catch {
373374
// Invalid query — skip, return 0 for this rule
374375
results.set(id, 0);
376+
} finally {
377+
q?.delete();
375378
}
376379
}
377380
} catch {

packages/core/src/services/pattern-analysis-service.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,8 @@ export async function extractErrorHandlingWithAst(
152152
}
153153
if (hasThrow) return { style: 'throw', examples: [] };
154154
if (hasResultRegex) return regex; // AST can't detect Result<T>, keep regex
155-
if (hasTryCatch || hasPromiseCatch) return { style: 'throw', examples: [] };
156-
155+
// try-catch or promise.catch alone is a mechanism, not a style —
156+
// fall through to regex which may have found throw/Result in content
157157
return regex;
158158
}
159159

@@ -174,7 +174,9 @@ export async function extractImportStyleWithAst(
174174
const reExports = ast.get('re-export') ?? 0;
175175
const requires = ast.get('require') ?? 0;
176176

177-
// Dynamic imports count as ESM
177+
// AST affects style classification but not importCount — importCount only adds
178+
// genuinely new detections (dynamic imports) that regex missed. AST require
179+
// matches overlap with regex require matches so they don't inflate the count.
178180
const esmCount =
179181
(regex.style === 'esm' || regex.style === 'mixed' ? regex.importCount : 0) +
180182
dynamicImports +
@@ -206,11 +208,14 @@ export async function extractTypeCoverageWithAst(
206208

207209
const arrowTyped = ast.get('arrow-return-type') ?? 0;
208210
const functionTyped = ast.get('function-return-type') ?? 0;
211+
const arrowTotal = ast.get('arrow-total') ?? 0;
212+
const functionTotal = ast.get('function-total') ?? 0;
209213
const astAnnotated = arrowTyped + functionTyped;
214+
const astTotal = arrowTotal + functionTotal;
210215

211-
// Merge: use the higher count (AST catches arrows that signatures miss)
216+
// Merge: use the higher of AST total vs regex total for accurate denominator
212217
const annotatedCount = Math.max(regex.annotatedCount, astAnnotated);
213-
const totalCount = Math.max(regex.totalCount, astAnnotated); // at least as many as annotated
218+
const totalCount = Math.max(regex.totalCount, astTotal);
214219

215220
if (totalCount === 0) return regex;
216221

0 commit comments

Comments
 (0)