/** Slice 7 — `?` and `!` propagation operators. * * Walks every `fn` / `method` node whose handler body uses a postfix `?` * or `!` after a call to a Result/Option-producing function, and rewrites * the body with statement-level hoisting: * * const u = parseUser(raw)?; → const __k_t1 = parseUser(raw); * if (__k_t1.kind === 'err') return __k_t1; * const u = __k_t1.value; * * const u = parseUser(raw)!; → const __k_t1 = parseUser(raw); * if (__k_t1.kind === 'err') throw new KernUnwrapError(__k_t1); * const u = __k_t1.value; * * IIFE wrappers were tried in v0 and rejected: their `return` exits the * IIFE, not the enclosing handler, so propagation fell through silently. * * Recognition is **name-anchored** at call expressions and accepts a tiny * statement grammar: * 1. `(const|let|var) (:)? = ;` * 2. `return ;` * 3. `;` * Everything else (mid-expression, `await call()?`, ternary surroundings, * for-headers, JSX attributes, template `${…}`) is rejected with a clear * diagnostic. Bare `obj.prop!` (TypeScript non-null assertion) is preserved * verbatim because pass B skips member-access call sites. * * The failure discriminator (`'err'` vs `'none'`) comes from the CALLEE's * kind, not the enclosing function's return type — `Option.some(x)?` always * branches on `'none'` regardless of whether the enclosing fn returns * Result or Option. Mixed cases (`?` on Option callee inside a Result fn) * are rejected. * * Diagnostics: * - INVALID_PROPAGATION — `?` outside a Result/Option fn, * mismatched callee/container kind, * closure-nested, mid-expression, or * `await` in front of the call * - UNSAFE_UNWRAP_IN_RESULT_FN — soft warning when `!` lives inside * a Result/Option-returning fn (`?` * keeps the rich error shape) * - NESTED_PROPAGATION — `expr??` chains rejected; bind to * a let between steps */ import type { ParseState } from './parser-diagnostics.js'; import type { IRNode } from './types.js'; interface PropagationContext { /** Identifiers known to return Result<…> in this module. */ resultFns: Set; /** Identifiers known to return Option<…> in this module. */ optionFns: Set; /** Slice 7 v2.1 — identifiers known to return Promise>. */ asyncResultFns?: Set; /** Slice 7 v2.1 — identifiers known to return Promise>. */ asyncOptionFns?: Set; } /** Exported symbol metadata for a KERN module. This starts small on purpose: * import/codegen only needs the source symbol's semantic kind to bridge * target naming conventions such as KERN `parseUser` -> Python `parse_user`. * More target-specific names can be added later without changing the * `use/from` source syntax. */ export interface ModuleExportSymbol { /** Public KERN symbol name visible to importers of this module. */ name: string; /** Original KERN source symbol before barrel aliases, when known. */ sourceName?: string; kind: string; /** Public emitted names by target. Example: direct `fn parseUser` has * `{ python: "parse_user", ts: "parseUser" }`; a barrel alias * `parseUser as parseUserPublic` has public Python/TS name * `parseUserPublic`. */ targetNames?: Record; } /** Slice 7 v2 — exported fn signatures of a single KERN module, narrowed * to the names whose `returns` is `Result<…>` / `Option<…>` (sync) or * `Promise>` / `Promise>` (async). */ export interface ModuleExports { /** All exported source symbols by KERN name. Used to enrich `from` bindings * with `kind=` metadata after the resolver proves the path is a KERN * module. Optional for older callers that only provide propagation sets. */ symbols?: Map; /** Names of fns / methods exported by the module that return `Result<…>`. */ resultFns: Set; /** Names of fns / methods exported by the module that return `Option<…>`. */ optionFns: Set; /** Slice 7 v2.1 — async fns that return `Promise>` (or any * fn marked `async=true` with a `Result<…>` return). Recognised at * `await call()?` propagation sites. */ asyncResultFns?: Set; /** Slice 7 v2.1 — async fns that return `Promise>`. */ asyncOptionFns?: Set; } /** Slice 7 v2 — caller-supplied resolver mapping a `use path="…"` value to * the imported KERN module's exported fn signatures. Returning `null` * means "this import does not resolve to a KERN module" (bare npm import, * unresolved path, or non-KERN file) — those are skipped silently. The * CLI builds and supplies the resolver after a project-wide pre-pass. * Pure-parse callers (browser playground, tests) can omit it; cross- * module recognition is then disabled. */ export type ImportResolver = (path: string) => ModuleExports | null; /** Containing-fn return-type classification for the current handler. * `result`/`option` cover the slice 7 v1 sync shapes; `asyncResult` / * `asyncOption` cover slice 7 v2.1 — async fns whose declared return is * `Promise>` / `Promise>` (or marked `async=true` * with the inner Result/Option return). */ type FnReturn = 'result' | 'option' | 'asyncResult' | 'asyncOption' | 'other'; export interface PropagationRewriteResult { code: string; usedUnwrap: boolean; } type DiagCode = 'INVALID_PROPAGATION' | 'NESTED_PROPAGATION' | 'UNSAFE_UNWRAP_IN_RESULT_FN'; type Emit = (code: DiagCode, message: string) => void; /** Rewrite a single handler body. Public entry exported for tests. */ export declare function rewritePropagationInBody(code: string, fnReturn: FnReturn, ctx: PropagationContext, emit: Emit): PropagationRewriteResult; /** Walk the IR and rewrite every fn/method handler body in place. Returns * the set of nodes whose handlers used `!` so the codegen can decide * whether to add `KernUnwrapError` to the auto-emitted preamble. * * Slice 7 v2 — when `resolveImport` is supplied, fn names imported via * `use path="…"` get merged into the recognised set so cross-module * `parseUser(raw)?` calls propagate. The CLI builds the resolver from a * project-wide pre-pass; pure-parse callers omit it and cross-module * recognition is disabled. */ export declare function validateAndRewritePropagation(state: ParseState, root: IRNode, resolveImport?: ImportResolver): { unwrapUsedAnywhere: boolean; }; export {};