fix(printer): clamp out-of-range pos in getLineAndCharacter for source map emit#3939
Closed
Zelys-DFKH wants to merge 1 commit into
Closed
fix(printer): clamp out-of-range pos in getLineAndCharacter for source map emit#3939Zelys-DFKH wants to merge 1 commit into
Zelys-DFKH wants to merge 1 commit into
Conversation
…e map emit Synthetic tokens (e.g. the auto-inserted '}' for an unclosed block) can carry positions beyond the end of the source text. Go's slice operator panics on out-of-bounds indices, but JavaScript's String.prototype.slice silently clamps the end index to string.length. Add a pre-clamp to match JS semantics and prevent the panic when emitting source maps for files with parse errors that generate synthetic closing tokens. Fixes microsoft#3900 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes a panic in source map emission when the parser produces synthetic tokens positioned past end-of-file (e.g. an auto-inserted } for an unclosed block). The Go port's getLineAndCharacter sliced c.text[cachedPos:pos] directly, which panics on out-of-bounds, unlike JS's String.prototype.slice which clamps. The fix clamps pos to len(c.text).
Changes:
- Clamp
postolen(c.text)at the top oflineCharacterCache.getLineAndCharacter. - Add a new compiler test
sourceMapEmitUnclosedBlock.tsplus accepted baselines.
Reviewed changes
Copilot reviewed 6 out of 8 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| internal/printer/utilities.go | Adds bounds clamp for out-of-range positions in getLineAndCharacter. |
| testdata/tests/cases/compiler/sourceMapEmitUnclosedBlock.ts | New minimal repro test enabling --sourceMap on an unclosed block. |
| testdata/baselines/reference/compiler/sourceMapEmitUnclosedBlock.* | Accepted baselines (js, js.map, sourcemap.txt, errors.txt, types, symbols) for the new test. |
Files not reviewed (1)
- testdata/baselines/reference/compiler/sourceMapEmitUnclosedBlock.js: Language not supported
Member
|
We already have #3923. |
Author
|
Closing — #3923 already fixes this. Thanks for the quick catch, @jakebailey. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #3900
Analysis
tsgo --sourceMapon any file that triggers a parse error producing a synthetic closing token will panic:The crash is in
lineCharacterCache.getLineAndCharacter(internal/printer/utilities.go). It computes source map character offsets by slicing the source text:c.text[cachedPos:pos]. When the parser encounters an unclosed{, it inserts a synthetic}at a position past the end of the file (position 3 for a 2-byte file,{\n). Go panics on the out-of-bounds slice.TypeScript's original implementation doesn't have this problem because
String.prototype.slicesilently clamps its end index tostring.length. The Go port replicates the algorithm but not that implicit clamp.Fix
One guard at the top of
getLineAndCharacter: ifpos > len(c.text), clamp it tolen(c.text). Positions within bounds pass through unchanged, so this only affects synthetic tokens at or past end-of-file. The clamped position maps them to EOF in the source map, matching what TypeScript's JS implementation produces.Copilot Checklist
I successfully ran these commands at the end of my session, and they completed without error: