Skip to content

Panic "Diagnostic emitted without context" ts-go in declaration emit for export default arrow/function expression with non-portable inferred return type #63761

Description

@suyash-vyas

🔎 Search Terms

panic Diagnostic emitted without context, declaration emit crash, throwDiagnostic, handleSymbolAccessibilityError, transformExportAssignment, canProduceDiagnostics, export default arrow function, function expression, inferred return type cannot be named, symbol accessibility, TS4082, tsgo, native compiler

🕗 Version & Regression Information

⏯ Playground Link

No response

💻 Code

// @filename: helper.ts
declare const brand: unique symbol;

class Foo {
  private [brand]: number = 1;
}

export function makeFoo() {
  return new Foo();
}

// @filename: repro.ts
import { makeFoo } from './helper';

export default () => makeFoo();
// tsconfig.json
{ "compilerOptions": { "declaration": true, "emitDeclarationOnly": true, "outDir": "dist", "strict": true } }

Run tsc -p ..

Both ingredients are required: the default export must be an ExportAssignment whose expression is an
arrow function or function expression
(export default function () {} at statement level is a
function declaration a different AST node and errors correctly), and the inferred return type
must contain a symbol that cannot be named from the exporting module.

🙁 Actual behavior

The whole compile crashes with a Go panic (exit code 2) instead of reporting a diagnostic:

panic: Diagnostic emitted without context [recovered, repanicked]

github.com/microsoft/typescript-go/internal/transformers/declarations.throwDiagnostic(...)
        internal/transformers/declarations/transform.go:276
...(*SymbolTrackerImpl).handleSymbolAccessibilityError(...)  // Accessibility = CannotBeNamed, ErrorNode = nil
        internal/transformers/declarations/tracker.go:211
...(*NodeBuilderImpl).serializeReturnTypeForSignature
...(*DeclarationTransformer).ensureType                       transform.go:1647
...(*DeclarationTransformer).transformFunctionLikeToDeclaration
...(*DeclarationTransformer).transformExportAssignment        transform.go:1223

In a large monorepo this also fires under noEmit + incremental (declaration transforms run for
buildinfo state), where transient stale-cache states can create the cannot be named condition on
otherwise-clean code making the crash appear nondeterministic.

🙂 Expected behavior

A normal diagnostic, as TypeScript 6.0.3 emits for the identical input:

repro.ts(3,1): error TS4082: Default export of the module has or is using private name 'brand'.

tsgo itself handles every neighbouring form correctly — only the expression-form default export panics:

form result (tsgo 7.0.2 / nightly)
export default () => makeFoo(); panic
export default (function () { … }); panic
export default function () { … } (declaration) TS4058 ✓
export const make = () => makeFoo(); TS4023 + TS4094 ✓

Additional information about the issue

Root cause (verified at tag typescript/v7.0.2, commit 2bd066d8, and unchanged on current main):

  1. visitSourceFile installs throwDiagnostic literally panic("Diagnostic emitted without context") as the file-level default getSymbolAccessibilityDiagnostic
    (internal/transformers/declarations/transform.go:276, :289).
  2. transformExportAssignment's ast.IsFunctionLike(unwrapped) branch (transform.go ~1241 on main),
    which promotes export default <arrow/function expression> via
    transformFunctionLikeToDeclaration → ensureType(unwrapped), returns without installing a
    diagnostic context and without PushErrorFallbackNode unlike the non-function fallback
    branch just below it, which installs Default_export_of_the_module_has_or_is_using_private_name_0
    before serializing.
  3. ensureType installs a per-node handler only if canProduceDiagnostics(node) (transform.go ~1671
    on main), and canProduceDiagnostics (internal/transformers/declarations/util.go:25) does not
    include ast.IsArrowFunction / ast.IsFunctionExpression.

So a SymbolAccessibilityCannotBeNamed result raised while serializing the arrow's inferred return
type reaches the panic stub and takes down the compiler.

Suggested minimal fix: in the IsFunctionLike branch, install the same default-export diagnostic
context (and PushErrorFallbackNode(assignment) / Pop) that the sibling fallback branch already
uses, before calling transformFunctionLikeToDeclaration. This restores parity with TS 6.x (TS4082).

Prior occurrences of the same panic via other triggers: microsoft/typescript-go#2522 (CommonJS .cjs
re-export — fixed for that trigger only), microsoft/typescript-go#3378 (LSP, closed unverified).
Observed in the wild: supabase/supabase#48876 (worked around by deleting the triggering code).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions