Skip to content

Fix panic in the optional-chain transform when a chain ends in a tagged template - #63968

Open
bigboateng wants to merge 3 commits into
microsoft:mainfrom
bigboateng:fix-63965
Open

Fix panic in the optional-chain transform when a chain ends in a tagged template#63968
bigboateng wants to merge 3 commits into
microsoft:mainfrom
bigboateng:fix-63965

Conversation

@bigboateng

Copy link
Copy Markdown

Fixes #63965

flattenChain in tsc/internal/transformers/estransforms/optionalchain.go walks inward until the chain head is a tagged template or carries a ?. token, but then unconditionally calls chain.Expression(). TaggedTemplateExpression has a Tag rather than an expression, so Node.Expression() panics with an unhandled-kind error.

// any target that lowers optional chaining, e.g. es2017
e?.``(
panic: Unhandled case in Node.Expression: KindTaggedTemplateExpression
  ...estransforms.flattenChain
      tsc/internal/transformers/estransforms/optionalchain.go:143

The input is malformed (TS1358 makes tagged templates in optional chains a grammar error), but the compiler should report the diagnostics rather than crash — the transform runs regardless because the error is recoverable.

  • Use the tagged template's tag as the flattened chain base, and re-tag the template onto the nullish-checked value when rebuilding the chain (a tagged template can only be the head link, since flattenChain stops walking at it). The repro now emits e === null || e === void 0 ? void 0 : e``();.
  • Add a regression test covering the minimized repro and sibling shapes (a?.bc``, a tagged template followed by a call, and property access on a tagged-template head).

This fix was developed with AI assistance; I reviewed, tested, and take responsibility for every line.

flattenChain stops walking when the chain head is a tagged template, but
then unconditionally read chain.Expression(), which is unhandled for
TaggedTemplateExpression. Use the template's tag as the chain base and
re-tag the template onto the checked value when rebuilding the chain.
@typescript-automation typescript-automation Bot added the For Uncommitted Bug PR for untriaged, rejected, closed or missing bug label Aug 23, 2026
@bigboateng

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

@bigboateng
bigboateng marked this pull request as ready for review August 23, 2026 14:51
Copilot AI balanced review requested due to automatic review settings August 23, 2026 14:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Prevents optional-chain lowering from panicking when encountering malformed chains containing tagged templates.

Changes:

  • Handles tagged templates when flattening and rebuilding optional chains.
  • Adds regression cases and diagnostic, emit, type, and symbol baselines.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tsc/internal/transformers/estransforms/optionalchain.go Supports tagged-template chain segments.
tsc/testdata/tests/cases/compiler/optionalChainTaggedTemplateNoCrash.ts Adds regression scenarios.
tsc/testdata/baselines/reference/compiler/optionalChainTaggedTemplateNoCrash.errors.txt Captures expected diagnostics.
tsc/testdata/baselines/reference/compiler/optionalChainTaggedTemplateNoCrash.js Captures downlevel emit.
tsc/testdata/baselines/reference/compiler/optionalChainTaggedTemplateNoCrash.symbols Captures symbol information.
tsc/testdata/baselines/reference/compiler/optionalChainTaggedTemplateNoCrash.types Captures inferred types.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: can we rename this to something that doesn't say "no crash"?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to optionalChainTaggedTemplate.ts.

Comment on lines +1 to +4
// @target: es2017
// @filename: repro.ts
e?.``(
// @filename: siblings.ts

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should ensure that we do the right thing when we don't transform optional chaining away by adding a second target:

Suggested change
// @target: es2017
// @filename: repro.ts
e?.``(
// @filename: siblings.ts
// @target: es2017, es2024
// @filename: repro.ts
e?.``(
// @filename: siblings.ts

Not sure what others think.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, applied as suggested. The es2024 baselines show the chain reaching emit untransformed. The printer keeps ?. on the property access ((a?.b) c), but it drops `?.` on the tagged template itself (`e ();`). There is no legal syntax for a tagged template in an optional chain, which is why TS1358 exists. The output matches what 5.9 emits for the same input, so this is existing behavior, now pinned by the test.

Address review feedback: drop the NoCrash suffix from the test name and
add es2024 as a second target so the emit is also covered when optional
chaining is not lowered.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

For Uncommitted Bug PR for untriaged, rejected, closed or missing bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Crash: optional chain + tagged template panics the optional-chain transform

3 participants