/** * Per-function exception extractor (change: add-error-propagation-graph). * * Deterministic, LLM-free static extraction of a single function's exception * facts — the throw sites it contains, the `try` regions that guard parts of its * body, and the call sites within it (each tagged with the guards that enclose * it) — for the languages whose throw/catch semantics are cleanly statically * extractable: TypeScript, JavaScript, Python, Java, and C#. Go uses the * separate value-flow extractor in this module. Every other language fails soft * (an `unsupported` record with no facts), never a guess. * * This is the substrate under the `analyze_error_propagation` conclusion tool. * The CFG overlay (`cfg.ts`) already models try/catch/finally/throw as control * flow; this module adds the *exception semantics* the CFG omits — which type is * thrown, which a handler catches, and whether a throw escapes its function — * reusing the same per-language throw/try node-type knowledge rather than a new * grammar. * * Containment is resolved by BYTE RANGE, not line number: a throw/call "inside" a * `try` body means its node lies within the body's byte span, so a throw in a * catch body (or after a one-line nested try sharing the same physical line) is * never mis-attributed as guarded. Handling walks ALL enclosing guards outward * (an inner typed/finally guard that does not match does not shadow an outer * catch-all). It deliberately does NOT descend into nested closures/functions * (consistent with the CFG overlay): a throw inside a nested function is * attributed to that nested function. Computed live (no persisted artifact). */ import type Parser from 'tree-sitter'; /** The languages whose exception flow is statically extractable here. This is the * single authoritative source the language-support registry derives the * `errorPropagation` capability from, so the matrix cannot over-claim. */ export declare const ERROR_PROPAGATION_LANGUAGES: ReadonlySet; /** A thrown value whose static type cannot be known (a bare re-raise, `throw e`, * `throw someValue`, a thrown call result). Surfaced, never dropped. */ export declare const DYNAMIC_TYPE = ""; /** One `try` region's guard: the body it covers and what its handler catches. */ export interface TryGuard { /** 1-based line span of the guarded `try` body (NOT the catch/finally). */ fromLine: number; toLine: number; /** Byte span of the guarded `try` body — the authoritative containment range. */ fromIndex: number; toIndex: number; /** True when the handler catches everything (every TS/JS `catch`; Python bare * `except` / `except Exception` / `except BaseException`). */ catchAll: boolean; /** Exact exception type names a typed Python `except` matches (empty for a * catch-all). Matching is by exact name only — no subclass hierarchy. */ caughtTypes: string[]; /** True when the handler re-throws/re-raises — it does not swallow. */ rethrows: boolean; /** Per-clause semantics. Aggregate fields above remain for compatibility. */ handlers?: CatchHandler[]; /** A definitely abrupt finally block suppresses completion of the try body. */ suppresses?: boolean; } export interface CatchHandler { catchAll: boolean; caughtTypes: string[]; rethrows: boolean; /** C# `when` filters are runtime predicates and cannot prove handling. */ filtered?: boolean; } /** One direct `throw`/`raise` site in a function body. */ export interface ThrowSite { /** The constructed exception type, or {@link DYNAMIC_TYPE}. */ type: string; /** 1-based line of the throw/raise statement. */ line: number; /** Byte offset of the throw/raise statement (for containment). */ index: number; /** True when an enclosing `try` in the SAME function catches it (so it does not * escape this function). */ locallyHandled: boolean; /** Java checked-exception contract declarations are escape sources, not throws. */ source?: 'throw' | 'throws_clause'; } /** How a call's callee is addressed. * - `self` : an intra-object call — TS/JS `this.x()` / `super.x()`, Python * `self.x()` / `cls.x()`. The callee is provably an in-project * method, so a MISSING call-graph edge for it is a true unresolved * in-project callee (not an external), to be disclosed — never * silently assumed exception-free. * - `self-field` : a CHAINED intra-object call — `this..x()` / * `self..x()` (change: shrink-receiver-resolution-boundary). The * receiver is a field of the enclosing object, so unlike `self` the callee is * NOT provably in-project: it is whatever the field's type is. The call graph * binds it when the per-file receiver registry types the field, and emits * nothing otherwise — deliberately, since an `external::` leaf would assert * the callee leaves the project. A missing edge here is therefore an UNKNOWN * callee, disclosed under its own boundary rather than folded into `self` * (which would overclaim it as in-project) or `other` (which would imply an * external leaf exists for it). * - `other` : a member call on some other receiver (`obj.x()`) — resolves to an * internal edge or an `external::obj.x` edge (already disclosable). * - `none` : a bare call (`x()`) — resolves to an internal or external edge. */ export type CallReceiver = 'self' | 'self-field' | 'other' | 'none' | 'constructor'; /** One call site within a function body, tagged with the guards that enclose it. */ export interface CallSite { /** The callee name as it appears in source (for joining to a call-graph edge). */ calleeName: string; /** 1-based line of the call. */ line: number; /** How the callee is addressed — see {@link CallReceiver}. Used to disclose an * intra-object (`this.`/`self.`) call site that the call graph failed to * resolve, the one call shape that otherwise gets NEITHER a resolved nor an * external edge and so would be silently assumed exception-free. */ receiver: CallReceiver; /** The `try` guards that enclose this call, innermost first. An exception * propagating from the callee is caught here iff one of these guards catches * its type. */ guards: TryGuard[]; } /** A function's static exception facts. */ export interface FunctionExceptionFacts { language: string; /** False outside the exception-shaped subset (and for Go, whose facts come * from {@link extractGoErrorFacts}): no facts, not a claim of error-freedom. */ supported: boolean; throwSites: ThrowSite[]; tryGuards: TryGuard[]; callSites: CallSite[]; /** Count of throw sites whose type is {@link DYNAMIC_TYPE} (re-raises / rethrows * / thrown values) — a disclosed honesty signal, not an error. */ dynamicThrowCount: number; /** Semantics intentionally left unmodeled rather than silently cleared. */ boundaries: string[]; } export interface GoErrorSite { value: string; kind: 'returned_error' | 'panic'; line: number; index: number; calleeName?: string; callLine?: number; callResultIndex?: number; /** Internal lexical identity used to avoid conflating shadowed names. */ bindingId?: number; } export interface GoHandledSite { value: string; kind: 'checked_error' | 'recovered_panic'; line: number; fromCallee?: string; callLine?: number; callResultIndex?: number; index?: number; /** Internal lexical identity used to avoid conflating shadowed names. */ bindingId?: number; } export interface GoCallSite { calleeName: string; line: number; index: number; } export interface GoDiscardedResult { calleeName: string; line: number; index: number; resultIndex: number; } /** Go uses returned values and panic/recover, deliberately separate from exception facts. */ export interface GoErrorFacts { language: 'Go'; supported: boolean; returnsError: boolean; escapes: GoErrorSite[]; handledInternally: GoHandledSite[]; checkedCandidates: GoHandledSite[]; discardedResults: GoDiscardedResult[]; callSites: GoCallSite[]; recoversPanics: boolean; recoveryDeferIndex?: number; errorResultIndices: number[]; boundaries: string[]; } /** A tree-sitter parser for a supported language, or null if unavailable. * `filePath` is optional and only used to select the JSX grammar. */ export declare function getExceptionParser(language: string, filePath?: string): Promise; type Node = Parser.SyntaxNode; /** Resolve a cached function span against a freshly parsed tree. Edits can make * byte ranges stale; callers must use the current node range rather than letting * a stale range widen extraction to the file root or a sibling function. */ export declare function resolveCurrentFunctionSpan(root: Node, startIndex: number, endIndex: number, language: string, expectedName: string): { startIndex: number; endIndex: number; } | null; /** Whether the per-tree identity/custom-type index could be built within bounds. */ export declare function rootAstIndexWithinBudget(root: Node): boolean; /** * Extract a single function's exception facts from a parsed tree, scoped to the * byte range `[startIndex, endIndex)`. The range identifies the function; throws, * tries, and calls inside nested closures within it are excluded (attributed to * those closures). Deterministic. */ export declare function extractExceptionFacts(root: Node, startIndex: number, endIndex: number, language: string): FunctionExceptionFacts; /** Convenience: parse `source` as a whole file and extract facts for all of it * (the function under test). Returns an unsupported record if the language is * not supported or the parser is unavailable. */ export declare function extractExceptionFactsFromSource(source: string, language: string, filePath?: string): Promise; /** Extract a conservative, value-shaped lower bound for one Go function. */ export declare function extractGoErrorFacts(root: Node, startIndex: number, endIndex: number): GoErrorFacts; export declare function extractGoErrorFactsFromSource(source: string): Promise; /** All `try` guards whose body byte-range encloses `index`, innermost (smallest * span) first. Byte containment is exact — unlike line containment it never * conflates a throw/call sharing a physical line with a try-body boundary. */ export declare function enclosingGuards(guards: TryGuard[], index: number): TryGuard[]; /** The innermost (smallest byte-span) `try` guard whose body encloses `index`, or * null. Kept for callers that want a single guard; resolution prefers * {@link enclosingGuards} so an outer catch-all is not shadowed. */ export declare function innermostGuard(guards: TryGuard[], index: number): TryGuard | null; /** Does a guard catch an exception of `type`? A catch-all catches anything * (including {@link DYNAMIC_TYPE}); a typed guard catches only its exact named * types (never {@link DYNAMIC_TYPE}, which it cannot be proven to match). A * re-throwing handler does not swallow. */ export declare function guardCatches(guard: TryGuard, type: string): boolean; /** Is an exception of `type` caught by ANY of these enclosing guards? */ export declare function guardsCatch(guards: TryGuard[], type: string): boolean; export {}; //# sourceMappingURL=exception-flow.d.ts.map