import type { InterruptEffect } from "../symbolTable.js"; import type { Capability } from "../host/host.js"; import type { TypeCheckerContext, ScopeInfo } from "./types.js"; import type { AgencyNode } from "../types.js"; import type { HandleBlock } from "../types/handleBlock.js"; import type { InterruptStatement } from "../types/interruptStatement.js"; export type TaggedHandler = { block: HandleBlock; file: string; }; /** Stable cross-file identity for a function/node, of the form * `${file}:${name}`. Used as the key in `InterruptCallGraph` and on * every call edge so that propagation works correctly when the same * top-level name is defined in two modules or when an import is * aliased locally (`import { foo as bar } …`). */ export type QualifiedKey = string; export declare function qualifyName(file: string, name: string): QualifiedKey; export type CallEdge = { /** Local name used at the call site, e.g. `bar` for * `import { foo as bar } …; bar()`. Preserved for diagnostics and * tests; the propagation graph uses `calleeKey`. */ calleeName: string; /** Resolved cross-file identity of the callee. For locally defined * symbols this is `${currentFile}:${name}`. For imports it's * `${originFile}:${originalName}`. For unresolved names (builtins, * unknown identifiers) it falls back to `${currentFile}:${name}`. */ calleeKey: QualifiedKey; enclosingHandlers: TaggedHandler[]; /** The callees whose block arguments enclose this call, outermost first. * See `CallGraphFunction.blockCallHandlers`. */ viaBlockOf: QualifiedKey[]; }; export type CallGraphFunction = { /** Local (unqualified) name of the function/node — what users wrote * in the source. Stable within a single file. The outer map's key is * the qualified `${file}:${name}` form. */ name: string; /** Absolute path to the .agency file this function/node is defined in. */ file: string; callEdges: CallEdge[]; interruptSites: { site: InterruptStatement; /** Same as the function's `file`. Carried explicitly so consumers don't * have to look it up. */ file: string; enclosingHandlers: TaggedHandler[]; /** The callees whose block arguments enclose this site, outermost first. */ viaBlockOf: QualifiedKey[]; }[]; /** The `handle` blocks around EVERY one of this function's own calls to * its block parameter. A block passed to this function runs inside them, * so a site or call written in that block gains them too * (lib/analysis/interrupts.ts, `withBlockCallHandlers`). A handler around * only some of the calls is left out: listing it would tell the reader a * raise is answered on a run where it is not. Empty for a function that * takes no block or never calls it. */ blockCallHandlers: TaggedHandler[]; }; /** Cross-file call graph keyed by `QualifiedKey`. Built per-file by * `buildInterruptCallGraph` and merged across files in * `lib/analysis/interrupts.ts` (where the qualified keys are what make * the merge collision-free). */ export type InterruptCallGraph = Record; /** * Declarative pipeline: collect per-scope profiles → propagate transitively → format. */ export declare function analyzeInterruptsFromScopes(scopes: ScopeInfo[], ctx: TypeCheckerContext): Record; /** * The same pipeline for what each callable needs from the host. An imported * function brings the list the symbol table propagated for it; a function of * this file starts from its own `@capabilities` tag and gains what it calls, * through the same call edges the effect walk follows, so the two cannot * disagree about who calls whom. See docs/dev/language/capabilities.md. */ export declare function analyzeCapabilitiesFromScopes(scopes: ScopeInfo[], ctx: TypeCheckerContext): Record; /** * Build a per-function call graph that, for each call edge and each * `interruptStatement`, records the list of `handle` blocks in the * enclosing function body that lexically wrap it. Each handler is * tagged with its file so the file survives propagation in the * downstream handler-set analyzer. * * Unlike `analyzeInterruptsFromScopes` this does NOT propagate kinds * transitively — it only records direct, per-function structural facts. * The propagation lives in `lib/analysis/interrupts.ts`. */ export declare function buildInterruptCallGraph(scopes: ScopeInfo[], ctx: TypeCheckerContext): InterruptCallGraph; /** * Emit warnings for function calls that may throw interrupts but aren't * inside a handler. * * Scoped to graph-node bodies only. `def` functions are designed to * propagate interrupts to the nearest enclosing handler in the caller — * that's the whole point of the interrupt model. Warning on `def` * bodies floods every library function that calls `read`/`glob`/etc. * with noise and trains users to ignore the diagnostic, defeating its * purpose at the `node` boundary where the prompt actually surfaces to * a human operator. */ export declare function checkUnhandledInterruptWarnings(scopes: ScopeInfo[], interruptEffectsByFunction: Record, ctx: TypeCheckerContext): void; /** * Every interrupt effect kind an (inline handler / handle) `body` can raise, * transitively — direct raises plus the propagated kinds of everything it calls * (via `collectFromBody` + `interruptEffectsByFunction`). The single source of * the "what can this body raise" computation, shared by handler-offender * detection and handler-param typing (H1). */ export declare function collectRaisableEffects(body: AgencyNode[], info: ScopeInfo, interruptEffectsByFunction: Record, ctx: TypeCheckerContext): string[]; /** * `interrupt` is not allowed inside any callback body. Callbacks fire as * side effects; their body cannot pause execution to ask the user a * question. Move the `interrupt` into the calling node/function instead, * or use a runtime guard if you wanted budget enforcement. * * After the `liftCallbacks` preprocessor runs, every * `callback(...) { ... }` block becomes * `callback("hookName", __cb_scope_N)` — a 2-arg call whose second * argument is a `variableName` referencing a lifted top-level * function. We look that function up in `interruptEffectsByFunction` * (transitively populated) and emit an error if it may interrupt. */ export declare function checkCallbackBodyInterrupts(scopes: ScopeInfo[], interruptEffectsByFunction: Record, ctx: TypeCheckerContext): void;