diff --git a/gitnexus/bench/cfg/measure.mjs b/gitnexus/bench/cfg/measure.mjs index 4fe6bd267..efc0661a8 100644 --- a/gitnexus/bench/cfg/measure.mjs +++ b/gitnexus/bench/cfg/measure.mjs @@ -403,7 +403,12 @@ function canonicalizeCfg(cfg) { } function fingerprint(tk, scenario) { - const out = collectFunctionCfgs(tk.parse(scenario.gen(FP_SIZE)).rootNode, tk.visitor, 'fp', NO_CAP); + const out = collectFunctionCfgs( + tk.parse(scenario.gen(FP_SIZE)).rootNode, + tk.visitor, + 'fp', + NO_CAP, + ); const canon = out.cfgs.map(canonicalizeCfg).sort().join('\n====\n'); return { fingerprint: crypto.createHash('sha256').update(canon).digest('hex'), diff --git a/gitnexus/src/core/ingestion/cfg/synthetic-escape.ts b/gitnexus/src/core/ingestion/cfg/synthetic-escape.ts index 3f69e1a70..2934fec22 100644 --- a/gitnexus/src/core/ingestion/cfg/synthetic-escape.ts +++ b/gitnexus/src/core/ingestion/cfg/synthetic-escape.ts @@ -79,10 +79,7 @@ * is the documented, intentional trade-off: recover the common goto-cycle case; * surface anything with a genuine residual anomaly rather than guess. */ -import { - isExitReachableFromAllBlocks, - type PostDomTree, -} from './post-dominators.js'; +import { isExitReachableFromAllBlocks, type PostDomTree } from './post-dominators.js'; import type { CfgEdgeData, FunctionCfg } from './types.js'; /** @@ -285,8 +282,7 @@ export function augmentForPostDom(cfg: FunctionCfg): FunctionCfg { // acyclic block (ENTRY / the spine) that is exit-unreachable only because it // FEEDS a trap downstream; it gets its path to EXIT for free once the trap is // bridged, so it is never bridged on its own. - const isCycle = - comp.length > 1 || cfg.edges.some((e) => e.from === rep && e.to === rep); + const isCycle = comp.length > 1 || cfg.edges.some((e) => e.from === rep && e.to === rep); if (!isCycle) continue; // Controlling representative: the entry-reachable member with a branch diff --git a/gitnexus/src/core/ingestion/cfg/visitors/c-cpp.ts b/gitnexus/src/core/ingestion/cfg/visitors/c-cpp.ts index 0e22870d3..13918661e 100644 --- a/gitnexus/src/core/ingestion/cfg/visitors/c-cpp.ts +++ b/gitnexus/src/core/ingestion/cfg/visitors/c-cpp.ts @@ -193,7 +193,12 @@ class CCfgWalk { openSimple = idx; dangling = [idx]; } else { - this.builder.extendBlock(openSimple, endLineOf(stmt), stmt.text, this.harvest.facts(stmt)); + this.builder.extendBlock( + openSimple, + endLineOf(stmt), + stmt.text, + this.harvest.facts(stmt), + ); } } } @@ -561,7 +566,9 @@ class CCfgWalk { finishGotos(): void { for (const [label, froms] of this.pendingGotos) { // eslint-disable-next-line no-console - console.warn(`[cfg] unresolved goto label "${label}" routed to EXIT (${froms.length} site(s))`); + console.warn( + `[cfg] unresolved goto label "${label}" routed to EXIT (${froms.length} site(s))`, + ); for (const from of froms) this.builder.edge(from, this.builder.exitIndex, 'seq'); } this.pendingGotos.clear(); diff --git a/gitnexus/src/core/ingestion/cfg/visitors/csharp.ts b/gitnexus/src/core/ingestion/cfg/visitors/csharp.ts index e68117c6e..8a2ae0f50 100644 --- a/gitnexus/src/core/ingestion/cfg/visitors/csharp.ts +++ b/gitnexus/src/core/ingestion/cfg/visitors/csharp.ts @@ -183,7 +183,12 @@ class CsharpCfgWalk { openSimple = idx; dangling = [idx]; } else { - this.builder.extendBlock(openSimple, endLineOf(stmt), stmt.text, this.harvest.facts(stmt)); + this.builder.extendBlock( + openSimple, + endLineOf(stmt), + stmt.text, + this.harvest.facts(stmt), + ); } } } @@ -555,9 +560,7 @@ class CsharpCfgWalk { this.cfc.pushSwitch(switchExit); const body = stmt.childForFieldName('body'); - const sections = body - ? body.namedChildren.filter((c) => c.type === 'switch_section') - : []; + const sections = body ? body.namedChildren.filter((c) => c.type === 'switch_section') : []; // Each `switch_section`'s case-test patterns/`when` clauses evaluate before // the body runs — harvest their uses (and any pattern binding as a may-def) @@ -599,9 +602,7 @@ class CsharpCfgWalk { /** A switch_section's body statements (everything but its case labels/patterns). */ private sectionStatements(section: SyntaxNode): SyntaxNode[] { - return section.namedChildren.filter( - (c) => this.isStatementLike(c) && c.type !== 'comment', - ); + return section.namedChildren.filter((c) => this.isStatementLike(c) && c.type !== 'comment'); } /** The case-test expressions of a section (constant patterns + when clauses). */ @@ -655,13 +656,7 @@ class CsharpCfgWalk { // The resource (declaration or expression) is the named child before `body`. const resource = this.usingResource(stmt); const disposeFacts = resource ? this.harvest.facts(resource) : undefined; - return this.buildProtectedSynthetic( - bodyNode, - stmt, - 'dispose', - disposeFacts, - resource ?? stmt, - ); + return this.buildProtectedSynthetic(bodyNode, stmt, 'dispose', disposeFacts, resource ?? stmt); } /** @@ -704,9 +699,7 @@ class CsharpCfgWalk { for (const clause of catchClauses) { const clauseBody = clause.childForFieldName('body'); if (finallyRes) this.handlers.push(finallyRes.entry); - let res: SeqResult = clauseBody - ? this.visitSeq(this.statementsOf(clauseBody)) - : null; + let res: SeqResult = clauseBody ? this.visitSeq(this.statementsOf(clauseBody)) : null; if (finallyRes) this.handlers.pop(); if (res === null) { // Empty `catch {}` still catches — synthesize one block so exception @@ -851,8 +844,7 @@ function buildFunctionCfg(fnNode: SyntaxNode, filePath: string): FunctionCfg | u // The body is a `block` (field `body`) OR an `arrow_expression_clause` // (expression-bodied member) OR an expression (single-expression lambda). const body = - fnNode.childForFieldName('body') ?? - fnNode.namedChildren.find((c) => c.type === 'block'); + fnNode.childForFieldName('body') ?? fnNode.namedChildren.find((c) => c.type === 'block'); if (!body) return undefined; // abstract / partial / interface member — no body const builder = new CfgBuilder(filePath, startLine, endLine, startColumn); @@ -864,8 +856,7 @@ function buildFunctionCfg(fnNode: SyntaxNode, filePath: string): FunctionCfg | u if (body.type === 'arrow_expression_clause' || body.type !== 'block') { // Expression-bodied member / single-expression lambda: one block whose // value is returned. For an arrow clause the value is its inner expression. - const expr = - body.type === 'arrow_expression_clause' ? (body.namedChild(0) ?? body) : body; + const expr = body.type === 'arrow_expression_clause' ? (body.namedChild(0) ?? body) : body; const blk = builder.newBlock( startLineOf(expr), endLineOf(expr), diff --git a/gitnexus/src/core/ingestion/cfg/visitors/dart-harvest.ts b/gitnexus/src/core/ingestion/cfg/visitors/dart-harvest.ts index 381fe8dca..0abe244b0 100644 --- a/gitnexus/src/core/ingestion/cfg/visitors/dart-harvest.ts +++ b/gitnexus/src/core/ingestion/cfg/visitors/dart-harvest.ts @@ -159,7 +159,8 @@ export class DartHarvester { const names: SyntaxNode[] = []; for (const p of list.namedChildren) { if (p.type !== 'formal_parameter') continue; - const name = p.childForFieldName('name') ?? p.namedChildren.find((c) => c.type === 'identifier'); + const name = + p.childForFieldName('name') ?? p.namedChildren.find((c) => c.type === 'identifier'); if (name) names.push(name); } return names; @@ -427,7 +428,10 @@ export class DartHarvester { // `.name` / `(...args)` — a member-access suffix name is not a scalar // binding; walk the argument part for uses but skip the bare property id. for (const c of node.namedChildren) { - if (c.type === 'unconditional_assignable_selector' || c.type === 'conditional_assignable_selector') { + if ( + c.type === 'unconditional_assignable_selector' || + c.type === 'conditional_assignable_selector' + ) { continue; // `.name` — property name is not a use } this.walkValue(c, acc); diff --git a/gitnexus/src/core/ingestion/cfg/visitors/dart.ts b/gitnexus/src/core/ingestion/cfg/visitors/dart.ts index af3b16812..8d2247f71 100644 --- a/gitnexus/src/core/ingestion/cfg/visitors/dart.ts +++ b/gitnexus/src/core/ingestion/cfg/visitors/dart.ts @@ -152,13 +152,11 @@ const isComment = (n: SyntaxNode): boolean => COMMENT_TYPES.has(n.type); /** Whether an `expression_statement` is a bare `throw …;`. */ const isThrowStatement = (n: SyntaxNode): boolean => - n.type === 'expression_statement' && - n.namedChildren.some((c) => c.type === 'throw_expression'); + n.type === 'expression_statement' && n.namedChildren.some((c) => c.type === 'throw_expression'); /** Whether an `expression_statement` is a bare `rethrow;`. */ const isRethrowStatement = (n: SyntaxNode): boolean => - n.type === 'expression_statement' && - n.namedChildren.some((c) => c.type === 'rethrow_expression'); + n.type === 'expression_statement' && n.namedChildren.some((c) => c.type === 'rethrow_expression'); /** A statement sequence that produced no blocks (empty body) is "transparent". */ type SeqResult = TraversalResult | null; @@ -221,7 +219,12 @@ class DartCfgWalk { openSimple = idx; dangling = [idx]; } else { - this.builder.extendBlock(openSimple, endLineOf(stmt), stmt.text, this.harvest.facts(stmt)); + this.builder.extendBlock( + openSimple, + endLineOf(stmt), + stmt.text, + this.harvest.facts(stmt), + ); } } } @@ -630,7 +633,11 @@ class DartCfgWalk { const contLabel = this.caseContinueLabel(cases[i]); if (!res) { // Empty case — spill to the next case (or switchExit). - this.builder.edge(dispatch, i + 1 < cases.length ? entryOf[i + 1] : switchExit, 'fallthrough'); + this.builder.edge( + dispatch, + i + 1 < cases.length ? entryOf[i + 1] : switchExit, + 'fallthrough', + ); continue; } if (contLabel) { @@ -887,9 +894,7 @@ function buildFunctionCfg(fnNode: SyntaxNode, filePath: string): FunctionCfg | u } const walk = new DartCfgWalk(builder, harvest); - const res = block - ? walk.visitSeq(block.namedChildren.filter((c) => !isComment(c))) - : null; + const res = block ? walk.visitSeq(block.namedChildren.filter((c) => !isComment(c))) : null; if (!res) { builder.edge(builder.entryIndex, builder.exitIndex, 'seq'); // empty body diff --git a/gitnexus/src/core/ingestion/cfg/visitors/go-harvest.ts b/gitnexus/src/core/ingestion/cfg/visitors/go-harvest.ts index b409931aa..a5f17fec4 100644 --- a/gitnexus/src/core/ingestion/cfg/visitors/go-harvest.ts +++ b/gitnexus/src/core/ingestion/cfg/visitors/go-harvest.ts @@ -542,8 +542,7 @@ export class GoHarvester extends ScopeTreeHarvester { for (const target of targets) { // A `var_spec` carries its name(s) as children; an identifier resolves // directly. Skip the blank identifier (`_`), which binds nothing. - const names = - target.type === 'identifier' ? [target] : this.listIdentifiers(target); + const names = target.type === 'identifier' ? [target] : this.listIdentifiers(target); for (const n of names) { if (n.text === '_') continue; defs.push(this.resolve(n)); diff --git a/gitnexus/src/core/ingestion/cfg/visitors/go.ts b/gitnexus/src/core/ingestion/cfg/visitors/go.ts index 40a474d6d..dae764ea8 100644 --- a/gitnexus/src/core/ingestion/cfg/visitors/go.ts +++ b/gitnexus/src/core/ingestion/cfg/visitors/go.ts @@ -112,11 +112,7 @@ import type { CfgVisitor, FunctionCfg } from '../types.js'; import { GoHarvester } from './go-harvest.js'; /** Go node types that own a CFG-bearing function body. */ -const GO_FUNCTION_TYPES = new Set([ - 'function_declaration', - 'method_declaration', - 'func_literal', -]); +const GO_FUNCTION_TYPES = new Set(['function_declaration', 'method_declaration', 'func_literal']); /** Statement node types that break a basic block (everything else coalesces). */ const CONTROL_FLOW_TYPES = new Set([ @@ -212,7 +208,12 @@ class GoCfgWalk { openSimple = idx; dangling = [idx]; } else { - this.builder.extendBlock(openSimple, endLineOf(stmt), stmt.text, this.harvest.facts(stmt)); + this.builder.extendBlock( + openSimple, + endLineOf(stmt), + stmt.text, + this.harvest.facts(stmt), + ); } } } @@ -347,7 +348,8 @@ class GoCfgWalk { private visitLabeled(stmt: SyntaxNode): SeqResult { const labelNode = stmt.childForFieldName('label'); const label = labelNode?.text; - const body = stmt.namedChildren.find((c) => c.id !== labelNode?.id && c.type !== 'comment') ?? null; + const body = + stmt.namedChildren.find((c) => c.id !== labelNode?.id && c.type !== 'comment') ?? null; if (label !== undefined && body && this.isBreakableStatement(body)) { // Forward the label to the loop/switch/select frame this statement pushes. @@ -699,9 +701,7 @@ class GoCfgWalk { if (test) this.builder.attachFacts(dispatch, this.harvest.factsConditional(test)); } - const result = this.buildCases(dispatch, switchExit, cases, (c) => - this.exprCaseBody(c), - ); + const result = this.buildCases(dispatch, switchExit, cases, (c) => this.exprCaseBody(c)); this.cfc.pop(); return result; } @@ -721,20 +721,14 @@ class GoCfgWalk { const cases = stmt.namedChildren.filter( (c) => c.type === 'type_case' || c.type === 'default_case', ); - const result = this.buildCases(dispatch, switchExit, cases, (c) => - this.typeCaseBody(c), - ); + const result = this.buildCases(dispatch, switchExit, cases, (c) => this.typeCaseBody(c)); this.cfc.pop(); return result; } private visitSelect(stmt: SyntaxNode): TraversalResult { const labels = this.takeLabels(); - const dispatch = this.builder.newBlock( - startLineOf(stmt), - startLineOf(stmt), - 'select', - ); + const dispatch = this.builder.newBlock(startLineOf(stmt), startLineOf(stmt), 'select'); const selectExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), ''); this.cfc.pushSwitch(selectExit, labels); @@ -751,9 +745,7 @@ class GoCfgWalk { } const hasDefault = cases.some((c) => c.type === 'default_case'); - const result = this.buildCases(dispatch, selectExit, cases, (c) => - this.commCaseBody(c), - ); + const result = this.buildCases(dispatch, selectExit, cases, (c) => this.commCaseBody(c)); // A `select` with NO default BLOCKS until a case is ready — and a `select {}` // with no cases at all blocks forever. Either way EXIT must stay diff --git a/gitnexus/src/core/ingestion/cfg/visitors/java.ts b/gitnexus/src/core/ingestion/cfg/visitors/java.ts index 37f5bbf9e..aced4458d 100644 --- a/gitnexus/src/core/ingestion/cfg/visitors/java.ts +++ b/gitnexus/src/core/ingestion/cfg/visitors/java.ts @@ -220,7 +220,12 @@ class JavaCfgWalk { openSimple = idx; dangling = [idx]; } else { - this.builder.extendBlock(openSimple, endLineOf(stmt), stmt.text, this.harvest.facts(stmt)); + this.builder.extendBlock( + openSimple, + endLineOf(stmt), + stmt.text, + this.harvest.facts(stmt), + ); } } } diff --git a/gitnexus/src/core/ingestion/cfg/visitors/kotlin-harvest.ts b/gitnexus/src/core/ingestion/cfg/visitors/kotlin-harvest.ts index d9c04f82f..15c12b88f 100644 --- a/gitnexus/src/core/ingestion/cfg/visitors/kotlin-harvest.ts +++ b/gitnexus/src/core/ingestion/cfg/visitors/kotlin-harvest.ts @@ -151,7 +151,8 @@ export class KotlinHarvester { if (vd.type === 'variable_declaration') this.declareVariableDeclaration(vd, 'param'); else if (vd.type === 'multi_variable_declaration') { for (const inner of vd.namedChildren) { - if (inner.type === 'variable_declaration') this.declareVariableDeclaration(inner, 'param'); + if (inner.type === 'variable_declaration') + this.declareVariableDeclaration(inner, 'param'); } } } diff --git a/gitnexus/src/core/ingestion/cfg/visitors/kotlin.ts b/gitnexus/src/core/ingestion/cfg/visitors/kotlin.ts index 8cce7a5b6..d97335a94 100644 --- a/gitnexus/src/core/ingestion/cfg/visitors/kotlin.ts +++ b/gitnexus/src/core/ingestion/cfg/visitors/kotlin.ts @@ -224,7 +224,12 @@ class KotlinCfgWalk { openSimple = idx; dangling = [idx]; } else { - this.builder.extendBlock(openSimple, endLineOf(stmt), stmt.text, this.harvest.facts(stmt)); + this.builder.extendBlock( + openSimple, + endLineOf(stmt), + stmt.text, + this.harvest.facts(stmt), + ); } } } @@ -835,9 +840,7 @@ function buildFunctionCfg(fnNode: SyntaxNode, filePath: string): FunctionCfg | u } const walk = new KotlinCfgWalk(builder, harvest); - const res = body - ? walk.visitSeq(body.namedChildren.filter((c) => !isComment(c))) - : null; + const res = body ? walk.visitSeq(body.namedChildren.filter((c) => !isComment(c))) : null; if (!res) { builder.edge(builder.entryIndex, builder.exitIndex, 'seq'); // empty body diff --git a/gitnexus/src/core/ingestion/cfg/visitors/php-harvest.ts b/gitnexus/src/core/ingestion/cfg/visitors/php-harvest.ts index 40782f7ee..9bd680407 100644 --- a/gitnexus/src/core/ingestion/cfg/visitors/php-harvest.ts +++ b/gitnexus/src/core/ingestion/cfg/visitors/php-harvest.ts @@ -625,7 +625,10 @@ export class PhpHarvester { const accesses: string[] = []; let cur: SyntaxNode = this.unwrapParen(node); for (;;) { - if (cur.type === 'member_access_expression' || cur.type === 'nullsafe_member_access_expression') { + if ( + cur.type === 'member_access_expression' || + cur.type === 'nullsafe_member_access_expression' + ) { const field = cur.childForFieldName('name'); accesses.unshift(field?.text ?? ''); const obj = cur.childForFieldName('object'); diff --git a/gitnexus/src/core/ingestion/cfg/visitors/php.ts b/gitnexus/src/core/ingestion/cfg/visitors/php.ts index c506464e8..e88d114ea 100644 --- a/gitnexus/src/core/ingestion/cfg/visitors/php.ts +++ b/gitnexus/src/core/ingestion/cfg/visitors/php.ts @@ -211,7 +211,12 @@ class PhpCfgWalk { openSimple = idx; dangling = [idx]; } else { - this.builder.extendBlock(openSimple, endLineOf(stmt), stmt.text, this.harvest.facts(stmt)); + this.builder.extendBlock( + openSimple, + endLineOf(stmt), + stmt.text, + this.harvest.facts(stmt), + ); } } } diff --git a/gitnexus/src/core/ingestion/cfg/visitors/python-harvest.ts b/gitnexus/src/core/ingestion/cfg/visitors/python-harvest.ts index 6ea99c883..f5a4863c6 100644 --- a/gitnexus/src/core/ingestion/cfg/visitors/python-harvest.ts +++ b/gitnexus/src/core/ingestion/cfg/visitors/python-harvest.ts @@ -146,9 +146,7 @@ export class PythonHarvester { private declareParams(fnNode: SyntaxNode): void { const params = fnNode.childForFieldName('parameters') ?? - fnNode.namedChildren.find( - (c) => c.type === 'parameters' || c.type === 'lambda_parameters', - ); + fnNode.namedChildren.find((c) => c.type === 'parameters' || c.type === 'lambda_parameters'); if (!params) return; for (let i = 0; i < params.namedChildCount; i++) { const p = params.namedChild(i); diff --git a/gitnexus/src/core/ingestion/cfg/visitors/python.ts b/gitnexus/src/core/ingestion/cfg/visitors/python.ts index 24bb5cd9a..3b23cdbfd 100644 --- a/gitnexus/src/core/ingestion/cfg/visitors/python.ts +++ b/gitnexus/src/core/ingestion/cfg/visitors/python.ts @@ -101,7 +101,6 @@ import { drainFinalizerPending, wireJumpThroughFinalizers, } from '../control-flow-context.js'; -import type { FinalizerFrame } from '../control-flow-context.js'; import type { TraversalResult } from '../traversal-result.js'; import type { CfgVisitor, FunctionCfg } from '../types.js'; import { PythonHarvester } from './python-harvest.js'; @@ -191,7 +190,12 @@ class PythonCfgWalk { openSimple = idx; dangling = [idx]; } else { - this.builder.extendBlock(openSimple, endLineOf(stmt), stmt.text, this.harvest.facts(stmt)); + this.builder.extendBlock( + openSimple, + endLineOf(stmt), + stmt.text, + this.harvest.facts(stmt), + ); } } } @@ -461,7 +465,11 @@ class PythonCfgWalk { return alt?.type === 'else_clause' ? alt : undefined; } - private loopHeaderText(stmt: SyntaxNode, left: SyntaxNode | null, right: SyntaxNode | null): string { + private loopHeaderText( + stmt: SyntaxNode, + left: SyntaxNode | null, + right: SyntaxNode | null, + ): string { const l = left?.text ?? ''; const r = right?.text ?? ''; return l || r ? `for ${l} in ${r}` : stmt.text.split('\n')[0]; @@ -550,7 +558,8 @@ class PythonCfgWalk { // runs OUTSIDE this try's finalizer frame (a return inside finally threads // only OUTER finallys). const finallyBlock = finallyClause - ? this.bodyBlockOf(finallyClause) ?? finallyClause.namedChildren.find((c) => c.type === 'block') + ? (this.bodyBlockOf(finallyClause) ?? + finallyClause.namedChildren.find((c) => c.type === 'block')) : undefined; const finallyRes = finallyBlock ? this.visitSeq(this.statementsOf(finallyBlock)) : null; const finFrame = finallyRes ? this.cfc.pushFinalizer(finallyRes.entry) : null; @@ -565,7 +574,13 @@ class PythonCfgWalk { // The `except E as e:` header binds `e` — its own facts-only block in front // of the handler body (the binding happens once, on handler entry). const headFacts = this.harvest.exceptHeadFacts(clause); - const headBlock = this.builder.newBlock(startLineOf(clause), startLineOf(clause), '', 'normal', headFacts); + const headBlock = this.builder.newBlock( + startLineOf(clause), + startLineOf(clause), + '', + 'normal', + headFacts, + ); const bodyRes = handlerBlock ? this.visitSeq(this.statementsOf(handlerBlock)) : null; if (bodyRes) { this.builder.edge(headBlock, bodyRes.entry, 'seq'); @@ -650,7 +665,8 @@ class PythonCfgWalk { ); const matchExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), ''); - const body = stmt.childForFieldName('body') ?? stmt.namedChildren.find((c) => c.type === 'block'); + const body = + stmt.childForFieldName('body') ?? stmt.namedChildren.find((c) => c.type === 'block'); const cases = body ? body.namedChildren.filter((c) => c.type === 'case_clause') : []; // A case guard (`case P if g:`) evaluates conditionally — harvest its uses diff --git a/gitnexus/src/core/ingestion/cfg/visitors/ruby-harvest.ts b/gitnexus/src/core/ingestion/cfg/visitors/ruby-harvest.ts index ec1c5e4bd..0cececabb 100644 --- a/gitnexus/src/core/ingestion/cfg/visitors/ruby-harvest.ts +++ b/gitnexus/src/core/ingestion/cfg/visitors/ruby-harvest.ts @@ -167,7 +167,8 @@ export class RubyHarvester { return; } if (NAMED_PARAM_TYPES.has(p.type)) { - const name = p.childForFieldName('name') ?? p.namedChildren.find((c) => c.type === 'identifier'); + const name = + p.childForFieldName('name') ?? p.namedChildren.find((c) => c.type === 'identifier'); if (name) this.declare(name, 'param'); return; } @@ -319,7 +320,8 @@ export class RubyHarvester { return; } if (NAMED_PARAM_TYPES.has(p.type)) { - const name = p.childForFieldName('name') ?? p.namedChildren.find((c) => c.type === 'identifier'); + const name = + p.childForFieldName('name') ?? p.namedChildren.find((c) => c.type === 'identifier'); if (name) this.def(name, acc); return; } diff --git a/gitnexus/src/core/ingestion/cfg/visitors/ruby.ts b/gitnexus/src/core/ingestion/cfg/visitors/ruby.ts index 3048306e0..75363b224 100644 --- a/gitnexus/src/core/ingestion/cfg/visitors/ruby.ts +++ b/gitnexus/src/core/ingestion/cfg/visitors/ruby.ts @@ -108,19 +108,12 @@ import { drainFinalizerPending, wireJumpThroughFinalizers, } from '../control-flow-context.js'; -import type { FinalizerFrame } from '../control-flow-context.js'; import type { TraversalResult } from '../traversal-result.js'; import type { CfgVisitor, FunctionCfg } from '../types.js'; import { RubyHarvester } from './ruby-harvest.js'; /** Ruby node types that own a CFG-bearing function/closure body. */ -const RUBY_FUNCTION_TYPES = new Set([ - 'method', - 'singleton_method', - 'do_block', - 'block', - 'lambda', -]); +const RUBY_FUNCTION_TYPES = new Set(['method', 'singleton_method', 'do_block', 'block', 'lambda']); /** Statement node types that break a basic block (everything else coalesces). */ const CONTROL_FLOW_TYPES = new Set([ @@ -230,7 +223,12 @@ class RubyCfgWalk { openSimple = idx; dangling = [idx]; } else { - this.builder.extendBlock(openSimple, endLineOf(stmt), stmt.text, this.harvest.facts(stmt)); + this.builder.extendBlock( + openSimple, + endLineOf(stmt), + stmt.text, + this.harvest.facts(stmt), + ); } } } @@ -666,7 +664,8 @@ class RubyCfgWalk { const caseExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), ''); const clauses = stmt.namedChildren.filter((c) => c.type === 'in_clause'); - const elseClause = stmt.childForFieldName('else') ?? stmt.namedChildren.find((c) => c.type === 'else'); + const elseClause = + stmt.childForFieldName('else') ?? stmt.namedChildren.find((c) => c.type === 'else'); // An `in`-clause guard (`in P if g`) evaluates conditionally on the dispatch. for (const c of clauses) { diff --git a/gitnexus/src/core/ingestion/cfg/visitors/rust-harvest.ts b/gitnexus/src/core/ingestion/cfg/visitors/rust-harvest.ts index 7f2dd7034..bbc6756d3 100644 --- a/gitnexus/src/core/ingestion/cfg/visitors/rust-harvest.ts +++ b/gitnexus/src/core/ingestion/cfg/visitors/rust-harvest.ts @@ -137,9 +137,7 @@ export class RustHarvester { private declareParams(fnNode: SyntaxNode): void { const params = fnNode.childForFieldName('parameters') ?? - fnNode.namedChildren.find( - (c) => c.type === 'parameters' || c.type === 'closure_parameters', - ); + fnNode.namedChildren.find((c) => c.type === 'parameters' || c.type === 'closure_parameters'); if (!params) return; for (let i = 0; i < params.namedChildCount; i++) { const p = params.namedChild(i); @@ -292,7 +290,8 @@ export class RustHarvester { // Fallback: declare any identifier / shorthand leaf. for (let i = 0; i < field.namedChildCount; i++) { const c = field.namedChild(i); - if (c?.type === 'shorthand_field_identifier' || c?.type === 'identifier') this.declare(c, kind); + if (c?.type === 'shorthand_field_identifier' || c?.type === 'identifier') + this.declare(c, kind); } } diff --git a/gitnexus/src/core/ingestion/cfg/visitors/rust.ts b/gitnexus/src/core/ingestion/cfg/visitors/rust.ts index 1482982bf..f0c8ec77d 100644 --- a/gitnexus/src/core/ingestion/cfg/visitors/rust.ts +++ b/gitnexus/src/core/ingestion/cfg/visitors/rust.ts @@ -186,9 +186,7 @@ class RustCfgWalk { dangling = [...res.exits]; } else { const idx = - openSimple === undefined - ? this.openBlock(stmt) - : this.extendOpen(openSimple, stmt); + openSimple === undefined ? this.openBlock(stmt) : this.extendOpen(openSimple, stmt); if (openSimple === undefined) { if (entry === undefined) entry = idx; else this.builder.connect(dangling, idx, 'seq'); diff --git a/gitnexus/src/core/ingestion/cfg/visitors/swift-harvest.ts b/gitnexus/src/core/ingestion/cfg/visitors/swift-harvest.ts index f03f98918..d0fdf5480 100644 --- a/gitnexus/src/core/ingestion/cfg/visitors/swift-harvest.ts +++ b/gitnexus/src/core/ingestion/cfg/visitors/swift-harvest.ts @@ -115,7 +115,9 @@ export class SwiftHarvester { * `lambda_literal` carries the `statements` directly. */ private bodyOf(fnNode: SyntaxNode): SyntaxNode | undefined { - const fb = fnNode.childForFieldName('body') ?? fnNode.namedChildren.find((c) => c.type === 'function_body'); + const fb = + fnNode.childForFieldName('body') ?? + fnNode.namedChildren.find((c) => c.type === 'function_body'); if (fb && fb.type === 'function_body') { return fb.namedChildren.find((c) => c.type === 'statements') ?? fb; } @@ -156,7 +158,9 @@ export class SwiftHarvester { for (const id of params.namedChildren) { if (id.type === 'simple_identifier') this.declare(id, 'param'); else if (id.type === 'lambda_parameter') { - const name = id.childForFieldName('name') ?? id.namedChildren.find((c) => c.type === 'simple_identifier'); + const name = + id.childForFieldName('name') ?? + id.namedChildren.find((c) => c.type === 'simple_identifier'); if (name) this.declare(name, 'param'); } } @@ -319,7 +323,9 @@ export class SwiftHarvester { for (const id of params.namedChildren) { if (id.type === 'simple_identifier') this.def(id, acc); else if (id.type === 'lambda_parameter') { - const name = id.childForFieldName('name') ?? id.namedChildren.find((c) => c.type === 'simple_identifier'); + const name = + id.childForFieldName('name') ?? + id.namedChildren.find((c) => c.type === 'simple_identifier'); if (name) this.def(name, acc); } } @@ -447,10 +453,11 @@ export class SwiftHarvester { // `try expr` / `try? expr` / `try! expr` — the wrapped expression's uses. const expr = node.childForFieldName('expr'); if (expr) this.walkValue(expr, acc); - else for (let i = 0; i < node.namedChildCount; i++) { - const c = node.namedChild(i); - if (c && c.type !== 'try_operator') this.walkValue(c, acc); - } + else + for (let i = 0; i < node.namedChildCount; i++) { + const c = node.namedChild(i); + if (c && c.type !== 'try_operator') this.walkValue(c, acc); + } return; } case 'conjunction_expression': diff --git a/gitnexus/src/core/ingestion/cfg/visitors/swift.ts b/gitnexus/src/core/ingestion/cfg/visitors/swift.ts index 24cba3262..24a4652a2 100644 --- a/gitnexus/src/core/ingestion/cfg/visitors/swift.ts +++ b/gitnexus/src/core/ingestion/cfg/visitors/swift.ts @@ -162,7 +162,9 @@ class SwiftCfgWalk { /** Statements of a `statements` node, ignoring comments. */ private statementsOf(block: SyntaxNode): SyntaxNode[] { - return block.namedChildren.filter((c) => c.type !== 'comment' && c.type !== 'multiline_comment'); + return block.namedChildren.filter( + (c) => c.type !== 'comment' && c.type !== 'multiline_comment', + ); } /** The body `statements` of a loop/branch node (the LAST `statements` child). */ @@ -206,7 +208,12 @@ class SwiftCfgWalk { openSimple = idx; dangling = [idx]; } else { - this.builder.extendBlock(openSimple, endLineOf(stmt), stmt.text, this.harvest.facts(stmt)); + this.builder.extendBlock( + openSimple, + endLineOf(stmt), + stmt.text, + this.harvest.facts(stmt), + ); } // A straight-line statement may contain a `try` — it can error-return. this.wireTryExits(stmt, openSimple); @@ -661,8 +668,10 @@ class SwiftCfgWalk { } private entryIsDefault(entry: SyntaxNode): boolean { - return entry.namedChildren.some((c) => c.type === 'default_keyword') || - entry.children.some((c) => c.type === 'default_keyword'); + return ( + entry.namedChildren.some((c) => c.type === 'default_keyword') || + entry.children.some((c) => c.type === 'default_keyword') + ); } /** A `switch_entry` ends in an explicit `fallthrough` keyword child. */ @@ -701,7 +710,13 @@ class SwiftCfgWalk { } const errFacts = this.harvest.catchErrorFacts(clause); if (errFacts) { - const errBlock = this.builder.newBlock(startLineOf(clause), startLineOf(clause), '', 'normal', errFacts); + const errBlock = this.builder.newBlock( + startLineOf(clause), + startLineOf(clause), + '', + 'normal', + errFacts, + ); this.builder.edge(errBlock, res.entry, 'seq'); res = { entry: errBlock, exits: res.exits }; } @@ -896,7 +911,8 @@ function buildFunctionCfg(fnNode: SyntaxNode, filePath: string): FunctionCfg | u */ function bodyStatementsOf(fnNode: SyntaxNode): SyntaxNode | undefined { const fb = - fnNode.childForFieldName('body') ?? fnNode.namedChildren.find((c) => c.type === 'function_body'); + fnNode.childForFieldName('body') ?? + fnNode.namedChildren.find((c) => c.type === 'function_body'); if (fb && fb.type === 'function_body') { return fb.namedChildren.find((c) => c.type === 'statements'); } diff --git a/gitnexus/test/helpers/ts-cfg-harness.ts b/gitnexus/test/helpers/ts-cfg-harness.ts index 1d683ef82..3c5e32a81 100644 --- a/gitnexus/test/helpers/ts-cfg-harness.ts +++ b/gitnexus/test/helpers/ts-cfg-harness.ts @@ -17,11 +17,7 @@ import { emitTsScopeCaptures } from '../../src/core/ingestion/languages/typescri import { interpretTsImport } from '../../src/core/ingestion/languages/typescript/interpret.js'; import { makeCfgHarness } from './cfg-harness.js'; -const harness = makeCfgHarness( - TypeScript.typescript, - createTypeScriptCfgVisitor(), - 'fixture.ts', -); +const harness = makeCfgHarness(TypeScript.typescript, createTypeScriptCfgVisitor(), 'fixture.ts'); export const parse = harness.parse; export const collectFunctions = harness.collectFunctions; diff --git a/gitnexus/test/integration/cfg/pipeline-pdg.test.ts b/gitnexus/test/integration/cfg/pipeline-pdg.test.ts index 5b46947d4..00f4ed8f7 100644 --- a/gitnexus/test/integration/cfg/pipeline-pdg.test.ts +++ b/gitnexus/test/integration/cfg/pipeline-pdg.test.ts @@ -447,8 +447,7 @@ describe('U7 — C-family worker-mode --pdg pipeline', () => { // pdg:false ≡ pdg-absent — the dominant existing-user path is untouched. expect(offDigest).toEqual(defaultDigest); // None of the PDG node/rel types leak into the flag-off graph. - for (const t of ['BasicBlock'] as const) - expect(defaultDigest.byType[t]).toBeUndefined(); + for (const t of ['BasicBlock'] as const) expect(defaultDigest.byType[t]).toBeUndefined(); for (const t of ['CFG', 'REACHING_DEF', 'CDG', 'TAINTED', 'SANITIZES'] as const) expect(defaultDigest.byRelType[t]).toBeUndefined(); // Committed golden: the flag-off graph is pinned by snapshot so a future @@ -470,69 +469,77 @@ describe('U7 — remaining languages worker-mode --pdg pipeline (#2195 capstone) !vendored || isLanguageAvailable(SupportedLanguages[lang as keyof typeof SupportedLanguages]) ? it : it.skip; - testFn(`${lang}: --pdg on emits BasicBlock + CFG + REACHING_DEF + CDG (> 0) via the worker`, async () => { - const result = await runPipelineFromRepo(freshLangRepo(fixture), () => {}, WORKER_PDG); - // The CFG is built in the worker — a stale dist silently zeros this. - expect(result.usedWorkerPool, `${lang} used the worker pool`).toBe(true); - const { basicBlocks, cfgEdges, reachingDefs, cdg } = counts(result); - expect(basicBlocks, `${lang} BasicBlock count`).toBeGreaterThan(0); - expect(cfgEdges, `${lang} CFG edge count`).toBeGreaterThan(0); - // def/use harvest populated the data-dependence layer. - expect(reachingDefs, `${lang} REACHING_DEF count`).toBeGreaterThan(0); - // CDG > 0 proves the post-dom/CDG pass ran for SOME function in the fixture. - expect(cdg, `${lang} CDG count`).toBeGreaterThan(0); - // The load-bearing claim (#2197 U3): ≥1 CDG edge is sourced INSIDE the - // non-terminating-loop function itself — so the post-dom/CDG pass was NOT - // skipped for the function whose loop traps EXIT, i.e. EXIT stays - // reverse-reachable end-to-end through the worker even with that loop. The - // whole-fixture aggregate above would pass on any branching function; this - // pins it to the hazard. - if (hazard !== undefined) { - expect( - cdgSourcedInHazardFunction(result, hazard), - `${lang} CDG edge sourced inside the non-terminating-loop function (marker ${JSON.stringify(hazard)})`, - ).toBe(true); - } - - // Both CFG and CDG endpoints are persisted BasicBlocks; CDG carries a T/F. - const blockIds = new Set(); - result.graph.forEachNode((n) => { - if (n.label === 'BasicBlock') blockIds.add(n.id); - }); - for (const rel of result.graph.iterRelationships()) { - if (rel.type === 'CFG' || rel.type === 'REACHING_DEF' || rel.type === 'CDG') { - expect(blockIds.has(rel.sourceId), `${lang} ${rel.type} source is a BasicBlock`).toBe( - true, - ); - expect(blockIds.has(rel.targetId), `${lang} ${rel.type} target is a BasicBlock`).toBe( - true, - ); + testFn( + `${lang}: --pdg on emits BasicBlock + CFG + REACHING_DEF + CDG (> 0) via the worker`, + async () => { + const result = await runPipelineFromRepo(freshLangRepo(fixture), () => {}, WORKER_PDG); + // The CFG is built in the worker — a stale dist silently zeros this. + expect(result.usedWorkerPool, `${lang} used the worker pool`).toBe(true); + const { basicBlocks, cfgEdges, reachingDefs, cdg } = counts(result); + expect(basicBlocks, `${lang} BasicBlock count`).toBeGreaterThan(0); + expect(cfgEdges, `${lang} CFG edge count`).toBeGreaterThan(0); + // def/use harvest populated the data-dependence layer. + expect(reachingDefs, `${lang} REACHING_DEF count`).toBeGreaterThan(0); + // CDG > 0 proves the post-dom/CDG pass ran for SOME function in the fixture. + expect(cdg, `${lang} CDG count`).toBeGreaterThan(0); + // The load-bearing claim (#2197 U3): ≥1 CDG edge is sourced INSIDE the + // non-terminating-loop function itself — so the post-dom/CDG pass was NOT + // skipped for the function whose loop traps EXIT, i.e. EXIT stays + // reverse-reachable end-to-end through the worker even with that loop. The + // whole-fixture aggregate above would pass on any branching function; this + // pins it to the hazard. + if (hazard !== undefined) { + expect( + cdgSourcedInHazardFunction(result, hazard), + `${lang} CDG edge sourced inside the non-terminating-loop function (marker ${JSON.stringify(hazard)})`, + ).toBe(true); } - if (rel.type === 'CDG') expect(['T', 'F']).toContain(rel.reason); - } - }, 60000); - testFn(`${lang}: --pdg off emits zero PDG nodes/edges (the R3 flag-off gate)`, async () => { - // Default (no pdg flag) and explicit pdg:false must both produce a graph - // with NO PDG layer — the existing-user path stays untouched. - const defaultRun = await runPipelineFromRepo(freshLangRepo(fixture), () => {}, WORKER_OFF); - const offRun = await runPipelineFromRepo(freshLangRepo(fixture), () => {}, { - ...WORKER_OFF, - pdg: false, - }); + // Both CFG and CDG endpoints are persisted BasicBlocks; CDG carries a T/F. + const blockIds = new Set(); + result.graph.forEachNode((n) => { + if (n.label === 'BasicBlock') blockIds.add(n.id); + }); + for (const rel of result.graph.iterRelationships()) { + if (rel.type === 'CFG' || rel.type === 'REACHING_DEF' || rel.type === 'CDG') { + expect(blockIds.has(rel.sourceId), `${lang} ${rel.type} source is a BasicBlock`).toBe( + true, + ); + expect(blockIds.has(rel.targetId), `${lang} ${rel.type} target is a BasicBlock`).toBe( + true, + ); + } + if (rel.type === 'CDG') expect(['T', 'F']).toContain(rel.reason); + } + }, + 60000, + ); - for (const r of [defaultRun, offRun]) { - const { basicBlocks, cfgEdges, reachingDefs, tainted, sanitizes, cdg } = counts(r); - expect(basicBlocks).toBe(0); - expect(cfgEdges).toBe(0); - expect(reachingDefs).toBe(0); - expect(tainted).toBe(0); - expect(sanitizes).toBe(0); - expect(cdg).toBe(0); - } + testFn( + `${lang}: --pdg off emits zero PDG nodes/edges (the R3 flag-off gate)`, + async () => { + // Default (no pdg flag) and explicit pdg:false must both produce a graph + // with NO PDG layer — the existing-user path stays untouched. + const defaultRun = await runPipelineFromRepo(freshLangRepo(fixture), () => {}, WORKER_OFF); + const offRun = await runPipelineFromRepo(freshLangRepo(fixture), () => {}, { + ...WORKER_OFF, + pdg: false, + }); - // pdg:false ≡ pdg-absent — the symbolic graph digest is identical. - expect(graphDigest(offRun)).toEqual(graphDigest(defaultRun)); - }, 90000); + for (const r of [defaultRun, offRun]) { + const { basicBlocks, cfgEdges, reachingDefs, tainted, sanitizes, cdg } = counts(r); + expect(basicBlocks).toBe(0); + expect(cfgEdges).toBe(0); + expect(reachingDefs).toBe(0); + expect(tainted).toBe(0); + expect(sanitizes).toBe(0); + expect(cdg).toBe(0); + } + + // pdg:false ≡ pdg-absent — the symbolic graph digest is identical. + expect(graphDigest(offRun)).toEqual(graphDigest(defaultRun)); + }, + 90000, + ); } }); diff --git a/gitnexus/test/unit/cfg/c-cpp-visitor.test.ts b/gitnexus/test/unit/cfg/c-cpp-visitor.test.ts index 510df9da3..2ec67c919 100644 --- a/gitnexus/test/unit/cfg/c-cpp-visitor.test.ts +++ b/gitnexus/test/unit/cfg/c-cpp-visitor.test.ts @@ -5,7 +5,6 @@ import { createCCfgVisitor, createCppCfgVisitor, } from '../../../src/core/ingestion/cfg/visitors/c-cpp.js'; -import type { FunctionCfg, SiteRecord } from '../../../src/core/ingestion/cfg/types.js'; import { makeCfgHarness, type CfgHarness, @@ -158,7 +157,9 @@ describe('C CfgVisitor — switch (C-style fallthrough)', () => { describe('C CfgVisitor — goto / labels', () => { it('backward goto wires to an already-seen label block', () => { - const cfg = c.cfgOf(`void f() { int i = 0; loop: work(); i++; if (i < 10) goto loop; done(); }`); + const cfg = c.cfgOf( + `void f() { int i = 0; loop: work(); i++; if (i < 10) goto loop; done(); }`, + ); const gotoB = block(cfg, 'goto loop;'); const label = block(cfg, 'work();'); expect(reaches(cfg, gotoB, label)).toBe(true); diff --git a/gitnexus/test/unit/cfg/csharp-visitor.test.ts b/gitnexus/test/unit/cfg/csharp-visitor.test.ts index 0851e8e87..dcc74f831 100644 --- a/gitnexus/test/unit/cfg/csharp-visitor.test.ts +++ b/gitnexus/test/unit/cfg/csharp-visitor.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { createRequire } from 'node:module'; import { createCsharpCfgVisitor } from '../../../src/core/ingestion/cfg/visitors/csharp.js'; -import type { FunctionCfg, SiteRecord } from '../../../src/core/ingestion/cfg/types.js'; +import type { FunctionCfg } from '../../../src/core/ingestion/cfg/types.js'; import { makeCfgHarness, type CfgHarness, @@ -61,7 +61,9 @@ describe('C# CfgVisitor — structure', () => { }); it('constructor and local function are CFG-bearing functions', () => { - const cfgs = cs.cfgsOf(`class C { C(int a) { x = a; } void Outer() { int L(int z) { return z; } L(1); } }`); + const cfgs = cs.cfgsOf( + `class C { C(int a) { x = a; } void Outer() { int L(int z) { return z; } L(1); } }`, + ); // constructor C, Outer, and the local function L = 3 CFGs. expect(cfgs.length).toBeGreaterThanOrEqual(3); for (const cfg of cfgs) expect(reaches(cfg, cfg.entryIndex, cfg.exitIndex)).toBe(true); @@ -121,7 +123,9 @@ describe('C# CfgVisitor — loops', () => { }); it('C-style for: init once, condition header, back-edge through update', () => { - const cfg = cs.cfgOf(`class C { void M(int n) { for (int i = 0; i < n; i++) { step(); } done(); } }`); + const cfg = cs.cfgOf( + `class C { void M(int n) { for (int i = 0; i < n; i++) { step(); } done(); } }`, + ); const init = block(cfg, 'int i = 0'); const header = block(cfg, 'i < n'); const incr = block(cfg, 'i++'); @@ -133,7 +137,9 @@ describe('C# CfgVisitor — loops', () => { }); it('foreach: header + body + loop-back + exit; loop var is a def, source a use', () => { - const cfg = cs.cfgOf(`class C { void M(int[] xs) { foreach (var x in xs) { use(x); } done(); } }`); + const cfg = cs.cfgOf( + `class C { void M(int[] xs) { foreach (var x in xs) { use(x); } done(); } }`, + ); const body = block(cfg, 'use(x);'); expect(edgeKinds(cfg).has('cond-true')).toBe(true); expect(edgeKinds(cfg).has('loop-back')).toBe(true); @@ -191,7 +197,9 @@ describe('C# CfgVisitor — switch', () => { }); it('switch_expression arms each dispatch as a guarded branch (switch-case)', () => { - const cfg = cs.cfgOf(`class C { int M(int x) { return x switch { 1 => a(), 2 => b(), _ => c() }; } }`); + const cfg = cs.cfgOf( + `class C { int M(int x) { return x switch { 1 => a(), 2 => b(), _ => c() }; } }`, + ); // The switch-expression lives inside the return block — it does not break a // basic block, but the function still has a well-formed single-exit CFG. expect(reaches(cfg, cfg.entryIndex, cfg.exitIndex)).toBe(true); @@ -275,7 +283,9 @@ describe('C# CfgVisitor — try/catch/finally completion edges', () => { describe('C# CfgVisitor — goto / labels', () => { it('backward goto wires to an already-seen label block', () => { - const cfg = cs.cfgOf(`class C { void M() { int i = 0; top: work(); i++; if (i < 10) goto top; done(); } }`); + const cfg = cs.cfgOf( + `class C { void M() { int i = 0; top: work(); i++; if (i < 10) goto top; done(); } }`, + ); const gotoB = block(cfg, 'goto top;'); const label = block(cfg, 'work();'); expect(reaches(cfg, gotoB, label)).toBe(true); @@ -382,7 +392,8 @@ describe('C# CfgVisitor — does not throw on exotic shapes', () => { // is registered, so these sites produce zero TAINTED edges; they only give the // deferred per-language source/sink model something to match against. describe('C# CfgVisitor — call-site sites[] substrate', () => { - const cfgOf = (body: string): FunctionCfg => cs.cfgOf(`class C { void f(int cmd, int x, int a) { ${body} } }`); + const cfgOf = (body: string): FunctionCfg => + cs.cfgOf(`class C { void f(int cmd, int x, int a) { ${body} } }`); it('a bare invocation records a `call` site with callee name + arg occurrence', () => { const cfg = cfgOf(`Exec(cmd);`); diff --git a/gitnexus/test/unit/cfg/go-visitor.test.ts b/gitnexus/test/unit/cfg/go-visitor.test.ts index 5f62e2900..b05f23aa1 100644 --- a/gitnexus/test/unit/cfg/go-visitor.test.ts +++ b/gitnexus/test/unit/cfg/go-visitor.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { createRequire } from 'node:module'; import { createGoCfgVisitor } from '../../../src/core/ingestion/cfg/visitors/go.js'; -import type { FunctionCfg, SiteRecord } from '../../../src/core/ingestion/cfg/types.js'; +import type { FunctionCfg } from '../../../src/core/ingestion/cfg/types.js'; import { makeCfgHarness, type CfgHarness, @@ -73,9 +73,7 @@ describe('Go CfgVisitor — structure', () => { describe('Go CfgVisitor — if', () => { it('if with initializer: init+cond on the header, both arms reach the join', () => { - const cfg = go.cfgOf( - pkg(`func f() { if v := compute(); v > 0 { a() } else { b() }; c() }`), - ); + const cfg = go.cfgOf(pkg(`func f() { if v := compute(); v > 0 { a() } else { b() }; c() }`)); const kinds = edgeKinds(cfg); expect(kinds.has('cond-true')).toBe(true); expect(kinds.has('cond-false')).toBe(true); @@ -254,7 +252,9 @@ describe('Go CfgVisitor — defer (LIFO completion legs)', () => { }); it('defer runs on a return path too (return threads through the defer)', () => { - const cfg = go.cfgOf(pkg(`func f(x bool) int { defer cleanup(); if x { return 1 }; return 2 }`)); + const cfg = go.cfgOf( + pkg(`func f(x bool) int { defer cleanup(); if x { return 1 }; return 2 }`), + ); const cleanup = block(cfg, 'defer cleanup()'); expect(reaches(cfg, block(cfg, 'return 1'), cleanup)).toBe(true); expect(reaches(cfg, block(cfg, 'return 2'), cleanup)).toBe(true); diff --git a/gitnexus/test/unit/cfg/harness.test.ts b/gitnexus/test/unit/cfg/harness.test.ts index 00dea42b3..8e64e3a29 100644 --- a/gitnexus/test/unit/cfg/harness.test.ts +++ b/gitnexus/test/unit/cfg/harness.test.ts @@ -44,9 +44,11 @@ describe('makeCfgHarness — language-parameterized CFG harness (#2195 U1)', () }; }, }; - const code = ['function fd() { return 1; }', 'const a = () => 2;', 'const b = (x) => x + 1;'].join( - '\n', - ); + const code = [ + 'function fd() { return 1; }', + 'const a = () => 2;', + 'const b = (x) => x + 1;', + ].join('\n'); const cfgs = makeCfgHarness(TypeScript.typescript, stub, 'stub.ts').cfgsOf(code); expect(cfgs).toHaveLength(2); // the two arrows; the function_declaration is ignored expect(built).toEqual(['arrow_function', 'arrow_function']); diff --git a/gitnexus/test/unit/cfg/java-visitor.test.ts b/gitnexus/test/unit/cfg/java-visitor.test.ts index b250f05a1..7070a9ee9 100644 --- a/gitnexus/test/unit/cfg/java-visitor.test.ts +++ b/gitnexus/test/unit/cfg/java-visitor.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { createRequire } from 'node:module'; import { createJavaCfgVisitor } from '../../../src/core/ingestion/cfg/visitors/java.js'; -import type { FunctionCfg, SiteRecord } from '../../../src/core/ingestion/cfg/types.js'; +import type { FunctionCfg } from '../../../src/core/ingestion/cfg/types.js'; import { makeCfgHarness, type CfgHarness, @@ -143,9 +143,7 @@ describe('Java CfgVisitor — loops', () => { }); it('enhanced for (for-each): header + body + loop-back + exit; loop var is a def', () => { - const cfg = java.cfgOf( - `class C { void m(int[] xs) { for (int v : xs) { use(v); } done(); } }`, - ); + const cfg = java.cfgOf(`class C { void m(int[] xs) { for (int v : xs) { use(v); } done(); } }`); const body = block(cfg, 'use(v);'); expect(edgeKinds(cfg).has('cond-true')).toBe(true); expect(edgeKinds(cfg).has('loop-back')).toBe(true); @@ -417,7 +415,9 @@ describe('Java CfgVisitor — synchronized (deterministic finalizer)', () => { }); it('a return inside synchronized crosses the release (finally-return)', () => { - const cfg = java.cfgOf(`class C { int m(Object lock) { synchronized (lock) { return get(); } } }`); + const cfg = java.cfgOf( + `class C { int m(Object lock) { synchronized (lock) { return get(); } } }`, + ); expect(edgeKinds(cfg).has('finally-return')).toBe(true); }); }); @@ -439,13 +439,17 @@ describe('Java CfgVisitor — def/use harvest', () => { }); it('a && (x = g()) records x as a may-def inside the short-circuit', () => { - const cfg = java.cfgOf(`class C { void m(boolean a) { int x = 0; if (a && (x = g()) > 0) h(x); } }`); + const cfg = java.cfgOf( + `class C { void m(boolean a) { int x = 0; if (a && (x = g()) > 0) h(x); } }`, + ); const x = bindingIdx(cfg, 'x'); expect(hasMayDef(cfg, x)).toBe(true); }); it('ternary arms record their writes as may-defs', () => { - const cfg = java.cfgOf(`class C { void m(int a) { int x = 0; int y = a > 0 ? (x = 1) : (x = 2); use(y); } }`); + const cfg = java.cfgOf( + `class C { void m(int a) { int x = 0; int y = a > 0 ? (x = 1) : (x = 2); use(y); } }`, + ); const x = bindingIdx(cfg, 'x'); expect(hasMayDef(cfg, x)).toBe(true); }); diff --git a/gitnexus/test/unit/cfg/php-visitor.test.ts b/gitnexus/test/unit/cfg/php-visitor.test.ts index 802440322..8afae6a82 100644 --- a/gitnexus/test/unit/cfg/php-visitor.test.ts +++ b/gitnexus/test/unit/cfg/php-visitor.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect } from 'vitest'; import { createRequire } from 'node:module'; import { createPhpCfgVisitor } from '../../../src/core/ingestion/cfg/visitors/php.js'; -import type { FunctionCfg, SiteRecord } from '../../../src/core/ingestion/cfg/types.js'; +import type { FunctionCfg } from '../../../src/core/ingestion/cfg/types.js'; import { makeCfgHarness, type CfgHarness, @@ -169,7 +169,9 @@ describe('PHP CfgVisitor — loops', () => { describe('PHP CfgVisitor — switch / match', () => { it('switch: C-style FALLTHROUGH (a case with no break flows to the next)', () => { const cfg = php.cfgOf( - wrap(`switch ($x) { case 1: a(); break; case 2: b(); case 3: c(); break; default: d(); } e();`), + wrap( + `switch ($x) { case 1: a(); break; case 2: b(); case 3: c(); break; default: d(); } e();`, + ), ); const kinds = edgeKinds(cfg); expect(kinds.has('switch-case')).toBe(true); @@ -334,7 +336,9 @@ describe('PHP CfgVisitor — def/use harvest', () => { }); it('closure use ($b, &$c) captures bind in the closure body', () => { - const cfgs = php.cfgsOf(` (c.bindings ?? []).some((bd) => bd.name === '$a')); expect(closure).toBeDefined(); expect((closure!.bindings ?? []).some((bd) => bd.name === '$b')).toBe(true); diff --git a/gitnexus/test/unit/cfg/python-visitor.test.ts b/gitnexus/test/unit/cfg/python-visitor.test.ts index b1d14eff7..473cb6d4a 100644 --- a/gitnexus/test/unit/cfg/python-visitor.test.ts +++ b/gitnexus/test/unit/cfg/python-visitor.test.ts @@ -160,7 +160,9 @@ describe('Python CfgVisitor — for / while with the loop else', () => { }); it('continue re-tests the loop header', () => { - const cfg = py.cfgOf(`def f(xs):\n for i in xs:\n if i:\n continue\n use(i)\n`); + const cfg = py.cfgOf( + `def f(xs):\n for i in xs:\n if i:\n continue\n use(i)\n`, + ); const header = cfg.blocks.find((b) => b.text.startsWith('for '))!.index; expect(edgeKinds(cfg).has('continue')).toBe(true); expect(reaches(cfg, block(cfg, 'continue'), header)).toBe(true); diff --git a/gitnexus/test/unit/cfg/ruby-visitor.test.ts b/gitnexus/test/unit/cfg/ruby-visitor.test.ts index 005ec411d..29da04e7c 100644 --- a/gitnexus/test/unit/cfg/ruby-visitor.test.ts +++ b/gitnexus/test/unit/cfg/ruby-visitor.test.ts @@ -246,9 +246,7 @@ describe('Ruby CfgVisitor — begin / rescue / else / ensure', () => { }); it('method-level implicit begin: a bare def + rescue/ensure threads correctly', () => { - const cfg = rb.cfgOf( - `def f\n risky()\nrescue => e\n handle()\nensure\n done()\nend\n`, - ); + const cfg = rb.cfgOf(`def f\n risky()\nrescue => e\n handle()\nensure\n done()\nend\n`); const ensureB = block(cfg, 'done()'); expect(reaches(cfg, block(cfg, 'risky()'), block(cfg, 'handle()'))).toBe(true); expect(reaches(cfg, block(cfg, 'handle()'), ensureB)).toBe(true); @@ -275,9 +273,7 @@ describe('Ruby CfgVisitor — begin / rescue / else / ensure', () => { }); it('retry re-enters the begin protected body (loop-back)', () => { - const cfg = rb.cfgOf( - `def f\n begin\n risky()\n rescue\n retry\n end\nend\n`, - ); + const cfg = rb.cfgOf(`def f\n begin\n risky()\n rescue\n retry\n end\nend\n`); expect(edgeKinds(cfg).has('loop-back')).toBe(true); const retryB = block(cfg, 'retry'); const bodyB = block(cfg, 'risky()'); diff --git a/gitnexus/test/unit/cfg/rust-visitor.test.ts b/gitnexus/test/unit/cfg/rust-visitor.test.ts index 3f9ac635b..b29143cac 100644 --- a/gitnexus/test/unit/cfg/rust-visitor.test.ts +++ b/gitnexus/test/unit/cfg/rust-visitor.test.ts @@ -104,9 +104,7 @@ describe('Rust CfgVisitor — if / else / if let', () => { }); it('if let with a let-chain (let PAT = e && cond) still binds + branches', () => { - const cfg = rust.cfgOf( - `fn f(opt: Option) { if let Some(n) = opt && n > 0 { both(); } }`, - ); + const cfg = rust.cfgOf(`fn f(opt: Option) { if let Some(n) = opt && n > 0 { both(); } }`); expect(edgeKinds(cfg).has('cond-true')).toBe(true); expect(hasDef(cfg, bindingIdx(cfg, 'n'))).toBe(true); expect(reaches(cfg, block(cfg, 'both()'), cfg.exitIndex)).toBe(true); @@ -221,9 +219,7 @@ describe('Rust CfgVisitor — match (no fallthrough) + guards', () => { }); it('a match with NO `_` arm keeps a no-match path to the exit', () => { - const cfg = rust.cfgOf( - `fn f(x: i32) { match x { 1 => a(), 2 => b(), } after(); }`, - ); + const cfg = rust.cfgOf(`fn f(x: i32) { match x { 1 => a(), 2 => b(), } after(); }`); expect(reaches(cfg, cfg.entryIndex, block(cfg, 'after()'))).toBe(true); expect(isExitReachableFromAllBlocks(cfg)).toBe(true); }); @@ -262,7 +258,7 @@ describe('Rust CfgVisitor — labeled break / continue', () => { expect(reaches(cfg, block(cfg, "continue 'outer"), outerHeader)).toBe(true); }); - it("a plain (unlabeled) break exits the INNER loop only", () => { + it('a plain (unlabeled) break exits the INNER loop only', () => { const cfg = rust.cfgOf( `fn f() { for i in 0..10 { @@ -281,9 +277,7 @@ describe('Rust CfgVisitor — labeled break / continue', () => { describe('Rust CfgVisitor — return and the ? operator', () => { it('return Some(n) flows directly to EXIT (return edge)', () => { - const cfg = rust.cfgOf( - `fn f(n: i32) -> Option { if n > 0 { return Some(n); } Some(0) }`, - ); + const cfg = rust.cfgOf(`fn f(n: i32) -> Option { if n > 0 { return Some(n); } Some(0) }`); expect(edgeKinds(cfg).has('return')).toBe(true); expect(reaches(cfg, block(cfg, 'return Some(n)'), cfg.exitIndex)).toBe(true); }); @@ -324,7 +318,9 @@ describe('Rust CfgVisitor — def/use harvest (patterns)', () => { }); it('let Some(n) = opt() in a let_else defines n (the tuple-struct inner binds, not the path)', () => { - const cfg = rust.cfgOf(`fn f(opt: Option) { let Some(n) = opt() else { return; }; use_n(n); }`); + const cfg = rust.cfgOf( + `fn f(opt: Option) { let Some(n) = opt() else { return; }; use_n(n); }`, + ); expect(hasDef(cfg, bindingIdx(cfg, 'n'))).toBe(true); expect(hasUse(cfg, bindingIdx(cfg, 'n'))).toBe(true); // the let_else `else { return; }` jumps to EXIT. @@ -339,7 +335,9 @@ describe('Rust CfgVisitor — def/use harvest (patterns)', () => { }); it('plain assignment (x = p) defines x; a field write (obj.f = …) is NOT a scalar def', () => { - const cfg = rust.cfgOf(`fn f(p: i32, obj: T) { let mut x = 0; x = p; obj.field = 1; use_x(x); }`); + const cfg = rust.cfgOf( + `fn f(p: i32, obj: T) { let mut x = 0; x = p; obj.field = 1; use_x(x); }`, + ); expect(hasDef(cfg, bindingIdx(cfg, 'x'))).toBe(true); // `obj` is used (its field is written), never a scalar def for `field`. expect(hasUse(cfg, bindingIdx(cfg, 'obj'))).toBe(true); @@ -351,7 +349,9 @@ describe('Rust CfgVisitor — def/use harvest (patterns)', () => { }); it('a closure body is opaque to the enclosing function harvest (own CFG)', () => { - const cfgs = rust.cfgsOf(`fn f() { let cb = || { let inner = 1; use_inner(inner); }; call(cb); }`); + const cfgs = rust.cfgsOf( + `fn f() { let cb = || { let inner = 1; use_inner(inner); }; call(cb); }`, + ); // The OUTER fn has a `cb` binding but NOT `inner` (the closure body is opaque). const outer = cfgs.find((c) => (c.bindings ?? []).some((b) => b.name === 'cb')); expect(outer).toBeDefined(); diff --git a/gitnexus/test/unit/cfg/swift-visitor.test.ts b/gitnexus/test/unit/cfg/swift-visitor.test.ts index 36b1ef40e..a553eddc7 100644 --- a/gitnexus/test/unit/cfg/swift-visitor.test.ts +++ b/gitnexus/test/unit/cfg/swift-visitor.test.ts @@ -1,7 +1,6 @@ import { describe, it, expect } from 'vitest'; import { requireVendoredGrammar } from '../../../src/core/tree-sitter/vendored-grammars.js'; import { createSwiftCfgVisitor } from '../../../src/core/ingestion/cfg/visitors/swift.js'; -import type { FunctionCfg } from '../../../src/core/ingestion/cfg/types.js'; import { makeCfgHarness, type CfgHarness, @@ -74,7 +73,9 @@ describe('Swift CfgVisitor — branching', () => { }); it('else-if chain branches each condition', () => { - const cfg = swift.cfgOf(`func f(x: Int) { if x == 1 { a() } else if x == 2 { b() } else { c() } ; d() }`); + const cfg = swift.cfgOf( + `func f(x: Int) { if x == 1 { a() } else if x == 2 { b() } else { c() } ; d() }`, + ); const join = block(cfg, 'd()'); expect(reaches(cfg, block(cfg, 'a()'), join)).toBe(true); expect(reaches(cfg, block(cfg, 'b()'), join)).toBe(true); @@ -95,7 +96,9 @@ describe('Swift CfgVisitor — branching', () => { describe('Swift CfgVisitor — guard', () => { it('guard let ... else { return }: the else DIVERGES, the body CONTINUES', () => { - const cfg = swift.cfgOf(`func f(opt: Int?) -> Int { guard let y = opt else { return 0 } ; use(y) ; return y }`); + const cfg = swift.cfgOf( + `func f(opt: Int?) -> Int { guard let y = opt else { return 0 } ; use(y) ; return y }`, + ); const header = block(cfg, 'guard'); const elseBlk = block(cfg, 'return 0'); // The else is the cond-false (diverging) arm and returns.