/** * Call Graph Analyzer * * Performs static analysis of function calls across source files using tree-sitter. * Supports TypeScript/JavaScript, Python, Go, Rust, Ruby, Java, Swift — no LLM, pure AST. * * Produces: * - FunctionNode[] — all identified functions/methods * - CallEdge[] — resolved function→function call relationships * - Hub functions — high-fanIn nodes (called by many others) * - Entry points — functions with no internal callers * - Layer violations — cross-layer calls in the wrong direction */ import type Parser from 'tree-sitter'; import type { ImportMap } from './import-resolver-bridge.js'; import type { CfgSpill } from './cfg-spill.js'; import { type FileStyleRaw } from './style-fingerprint.js'; import { type FileParseHealth } from './parse-health.js'; import { type FileDynamicBoundary } from './dynamic-boundary.js'; import { type ExtractionLaneOptions } from './extraction-pool.js'; import type { Pass1FactCache } from './pass1-fact-cache.js'; import type { FunctionNode, CallEdge, ClassNode, CallGraphResult, SerializedCallGraph, FileExtractResult, DynamicDispatchFacts } from './call-graph-types.js'; export type { EdgeConfidence, EdgeKind, CallType, FunctionNode, FunctionCallArity, ExternalKind, CallEdge, LayerViolation, ClassNode, InheritanceEdge, CallGraphResult, SerializedCallGraph, AmbiguousCallSite, AmbiguousStrategy, FileExtractResult, } from './call-graph-types.js'; export { CALL_DISTANCE_COSTS, callDistance, layerOf, classifyLayerEdge, AMBIGUOUS_CANDIDATE_CAP } from './call-graph-types.js'; export { computeCyclomaticComplexity } from './call-graph-complexity.js'; /** Test-only boundary counters: actual parser/query/inference calls, not orchestration intent. */ export declare function __getAnalyzerWorkCountersForTests(): Readonly<{ parses: number; nativeQueryCompiles: number; nativeQueryCompileCounts: number[]; typeInferences: number; }>; export declare function __resetAnalyzerWorkCountersForTests(clearQueries?: boolean): void; export type GrammarStatus = 'loaded' | 'unavailable' | 'untried'; /** Runtime status for the grammar backing one statically-supported language. */ export declare function grammarStatus(language: string): GrammarStatus; /** The definition {@link computeEnclosing} implements. Exported for its differential test only. */ export declare function _enclosingByBruteForceForTesting(nodes: FunctionNode[], n: FunctionNode): FunctionNode | undefined; /** Test-only access to the swept version, so the two can be diffed. */ export declare function _computeEnclosingForTesting(nodes: FunctionNode[]): Map; /** Test-only differential hook for the call-attribution interval index. */ export declare function _findEnclosingFunctionForTesting(nodes: FunctionNode[], callPos: number): FunctionNode | undefined; /** Deterministic work receipt for the interval-index complexity regression test. */ export declare function _findEnclosingFunctionStepsForTesting(nodes: FunctionNode[], callPos: number): number; /** * Did a grammar load attempt for this language already FAIL in this process? * * Every soft loader records `null` in the handle cache when a grammar cannot be loaded — an * optional dependency that was not installed, a native binding that will not build, a WASM file * that is absent. This reports that, so a caller can tell "the grammar is not here" apart from * "the grammar is here and produced nothing", which are the same empty result but very different * facts. `false` also means "not attempted yet"; drive an extraction first. * * Exists because the two are indistinguishable at the assertion site: when `tree-sitter-kotlin` * failed to install in CI, nine tests failed with messages like `expected [] to include 'main'`, * which reads exactly like a broken extractor and cost real time to diagnose as an install * problem. The grammars are `optionalDependencies` BY DESIGN (`loadGrammarSoft`, restricted * environments) — so a suite that cannot distinguish absence from breakage will keep going red * for reasons that are not defects. */ export declare function grammarLoadFailed(language: string): boolean; /** Reset loader caches — test-only hook for the graceful-degradation test. */ export declare function __resetGrammarCacheForTests(): void; /** Replace the native query constructor after parser warm-up — test-only grammar-drift hook. */ export declare function __setNativeQueryForTests(query: typeof Parser.Query): void; /** * Languages for which `CallGraphBuilder.build()` extracts function/method nodes and * call edges. The authoritative source for the `callGraph` capability flag in the * declarative language-support registry (change: add-declarative-language-support-registry). * * MUST stay in sync with the per-language dispatch in `build()`: the native extractors * (Python/TS/JS/Go/Rust/Ruby/Java/C++/Swift/Elixir/Dart) plus the data-driven * `QUERY_LANG_SPECS` languages. A behavioral test asserts a fixture in each member * yields ≥1 node, so this set cannot silently over-claim. */ export declare const CALLGRAPH_LANGUAGES: ReadonlySet; /** * Extract parent class / interface relationships from source files using * tree-sitter. Returns a map from `filePath::ClassName` → relationship info. * Uses safeQuery so any query that doesn't match a grammar version is silently * skipped rather than crashing. */ /** @deprecated Test/reference implementation for proving Pass-1 fact equivalence. */ export declare function _extractClassRelationshipsLegacyForTesting(files: Array<{ path: string; content: string; language: string; }>, /** * Collects files this pass abandoned at the parse budget (change: * fix-analyze-native-abort-and-file-cost-budget). A file can squeak under the budget in Pass 1 * and overrun HERE — the per-file `catch` below would then drop its inheritance data with no * record anywhere, which is the silent loss this change exists to prevent. Reported so the * builder can record it like any other exclusion. */ budgetExceeded?: Set): Promise>; /** Per-channel handler fan-out cap. Over-cap channels are DROPPED, never guessed. */ export declare const EVENT_CHANNEL_FANOUT_CAP = 8; /** Resolve a referenced simple name to a single internal function node, or undefined * when unknown or ambiguous (never guesses). Prefers a match in `preferFile`. */ type HandlerResolver = (name: string, preferFile: string) => FunctionNode | undefined; export declare function synthesizeDynamicDispatchEdges(files: Array<{ path: string; content: string; language: string; }>, allNodes: Map, resolveHandler: HandlerResolver, pass1Facts?: DynamicDispatchFacts[]): Promise; /** Construction-time options for {@link CallGraphBuilder}. */ export interface CallGraphBuilderOptions { /** * Controls for the Pass-1 extraction lane (change: optimize-parallel-extraction-pool). * Production passes nothing: the lane decides for itself from core count, file count, the * process-wide worker budget, and `OPENLORE_NO_WORKERS`. Tests use these to drive a stub * pool whose completion order and failure modes are deterministic. */ extraction?: ExtractionLaneOptions; /** * Memo of per-file Pass-1 facts (change: optimize-hash-keyed-analyze). When supplied, a file * whose content and extractor stamp match a stored row skips extraction entirely and its * cached facts are merged in its input position — the merge, and therefore every downstream * pass, cannot tell the two apart. Absent (the watcher's per-file rebuilds, tests, any * embedded caller) means today's behavior: extract everything. */ pass1Cache?: Pass1FactCache; /** * Off-heap destination for the CFG/def-use overlay (issue #304). When supplied, each file's * overlay is serialized into the spill as the file is merged and then DROPPED, so the overlay * never accumulates across the repository; `cfgs` comes back `undefined` and the caller drains * the spill into `cfg_overlay` instead. Absent (the watcher's per-file rebuilds, tests, any * embedded caller) means today's behavior: the overlay is returned in memory. */ cfgSpill?: CfgSpill; /** Test-only oracle: run the pre-optimization late parsers for byte-equivalence checks. */ legacyLatePassesForTesting?: boolean; } export declare class CallGraphBuilder { private readonly extractionOptions; private readonly pass1Cache; private readonly cfgSpill; private readonly legacyLatePassesForTesting; constructor(options?: CallGraphBuilderOptions); /** * Build a call graph from a list of source files. * * @param files Source files with path, content, and language * @param layers Optional layer map { layerName: [path prefix, ...] } * @param importMap Optional per-file import map (from ImportResolverBridge) * @param resolutionNodes Optional pre-existing nodes used only to seed the * call-resolution trie (not added to the returned nodes/edges). An * incremental subset rebuild passes the full set of known nodes so calls * into files outside the re-parsed subset resolve to their real node * instead of degrading to a synthetic `external::` leaf. */ build(files: Array<{ path: string; content: string; language: string; }>, layers?: Record, importMap?: ImportMap, resolutionNodes?: FunctionNode[], resolutionClasses?: ClassNode[]): Promise; private detectLayerViolations; } /** * Dispatch ONE file to its per-language extractor (Pass-1 only — nodes/edges/cfg/style/parseHealth, * no cross-file resolution). The single source of truth for the language→extractor mapping, shared * by the full build, the watcher's per-file refreshers, AND the extraction-pool worker * (change: optimize-parallel-extraction-pool) so the dispatch is never duplicated and the pooled * lane cannot drift from the serial one. Returns `undefined` for a language with no extractor. */ export declare function dispatchFileExtract(file: { path: string; content: string; language: string; }): Promise; /** * Tally ONE file's style fingerprint in isolation (change: add-codebase-style-fingerprint). * Reuses the same per-language extractor (and its single parse) the full build uses, returning * only the style counters. Used by the watcher to refresh a changed file's fingerprint without a * whole-graph rebuild. Fail-soft: an unsupported language or parse failure returns `undefined`. */ export declare function extractFileStyle(file: { path: string; content: string; language: string; }): Promise; /** * Record ONE file's parse health in isolation (change: add-parse-health-boundary-disclosure). Runs * the same per-language dispatch the full build uses (so it covers every callGraph language, not * just the style ones) over a single file — Pass 2 resolution over one file is trivial — and returns * only its parse-health record, or `undefined` for a clean file. Used by the watcher to keep * `parse-health.json` live for a changed file without a whole-graph rebuild. Fail-soft: a parse * failure is itself a parse-health signal, surfaced as `parseFailed`. */ export declare function extractFileParseHealth(file: { path: string; content: string; language: string; }): Promise; /** * Record ONE file's dynamic-boundary sites in isolation (change: disclose-dynamic-boundary-regions). * Runs the same per-language dispatch the full build uses over a single file, so the watcher can * keep `dynamic-boundary.json` live for a changed file without a whole-graph rebuild. * * The partition is decided against THIS FILE's own resolution, which is all a single-file re-derive * can see. That is a sound direction: a construct the whole-repository build would have retracted * can only appear here as a site — a disclosed boundary is never a false claim of absence, whereas * omitting one would be. Fail-soft: a parse failure yields no sites. */ export declare function extractFileDynamicBoundary(file: { path: string; content: string; language: string; }): Promise; export declare function serializeCallGraph(result: CallGraphResult): SerializedCallGraph; //# sourceMappingURL=call-graph.d.ts.map