/** * 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. 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; } /** 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; } /** 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. * - `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' | 'other' | 'none'; /** 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 for a language outside {@link ERROR_PROPAGATION_LANGUAGES}: no facts, * not a claim of exception-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; } /** A tree-sitter parser for a supported language, or null if unavailable. */ export declare function getExceptionParser(language: string): Promise; type Node = Parser.SyntaxNode; /** * 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): 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