Skip to content

Commit c9c6357

Browse files
committed
fix #4248: #private ids in arrow fn body in ?:
1 parent 9b42f68 commit c9c6357

File tree

3 files changed

+31
-12
lines changed

3 files changed

+31
-12
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
* Fix another TypeScript parsing edge case ([#4248](https://github.com/evanw/esbuild/issues/4248))
6+
7+
This fixes a regression with a change in the previous release that tries to more accurately parse TypeScript arrow functions inside the `?:` operator. The regression specifically involves parsing an arrow function containing a `#private` identifier inside the middle of a `?:` ternary operator inside a class body. This was fixed by propagating private identifier state into the parser clone used to speculatively parse the arrow function body. Here is an example of some affected code:
8+
9+
```ts
10+
class CachedDict {
11+
#has = (a: string) => dict.has(a);
12+
has = window
13+
? (word: string): boolean => this.#has(word)
14+
: this.#has;
15+
}
16+
```
17+
318
## 0.25.7
419

520
* Parse and print JavaScript imports with an explicit phase ([#4238](https://github.com/evanw/esbuild/issues/4238))

internal/js_parser/ts_parser.go

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -976,18 +976,14 @@ func (originalParser *parser) isTypeScriptArrowReturnTypeAfterQuestionAndBeforeC
976976
// future) from accidentally depending on some parser state that isn't cloned
977977
// here. That could result in a parser panic when parsing a more complex
978978
// version of this edge case.
979-
p := parser{
980-
// Clone all state that the parser needs to parse this arrow function body
981-
options: originalParser.options,
982-
source: originalParser.source,
983-
lexer: originalParser.lexer,
984-
log: logger.NewDeferLog(logger.DeferLogNoVerboseOrDebug, nil),
985-
currentScope: &js_ast.Scope{
986-
Kind: js_ast.ScopeFunctionArgs,
987-
Members: make(map[string]js_ast.ScopeMember),
988-
},
989-
}
979+
p := newParser(logger.NewDeferLog(logger.DeferLogNoVerboseOrDebug, nil), originalParser.source, originalParser.lexer, &originalParser.options)
980+
981+
// Clone all state that the parser needs to parse this arrow function body
982+
p.allowPrivateIdentifiers = originalParser.allowPrivateIdentifiers
983+
p.allowIn = originalParser.allowIn
990984
p.lexer.IsLogDisabled = true
985+
p.pushScopeForParsePass(js_ast.ScopeEntry, logger.Loc{Start: 0})
986+
p.pushScopeForParsePass(js_ast.ScopeFunctionArgs, logger.Loc{Start: 1})
991987

992988
// Parse the return type
993989
p.lexer.Expect(js_lexer.TColon)

internal/js_parser/ts_parser_test.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2388,11 +2388,19 @@ func TestTSArrow(t *testing.T) {
23882388
expectPrintedTS(t, "x = a ? (b) : T => c : d", "x = a ? (b) => c : d;\n")
23892389
expectPrintedTS(t, "x = a ? b - (c) : d => e", "x = a ? b - c : (d) => e;\n")
23902390
expectPrintedTS(t, "x = a ? b = (c) : T => d : e", "x = a ? b = (c) => d : e;\n")
2391-
expectPrintedTS(t, "x = (\n a ? (b = c) : { d: e }\n)", "x = a ? b = c : { d: e };\n") // Note: Newlines are important (they trigger backtracking)
23922391
expectParseErrorTS(t, "x = a ? (b = c) : T => d : (e = f) : g", "<stdin>: ERROR: Expected \";\" but found \":\"\n")
23932392
expectParseErrorTS(t, "x = a ? b ? (c = d) : T => e : (f = g)", "<stdin>: ERROR: Expected \":\" but found end of file\n")
23942393
expectParseErrorTS(t, "x = a ? - (b) : c => d : e", "<stdin>: ERROR: Expected \";\" but found \":\"\n")
23952394
expectParseErrorTS(t, "x = a ? b - (c) : d => e : f", "<stdin>: ERROR: Expected \";\" but found \":\"\n")
2395+
2396+
// Note: Newlines are important (they trigger backtracking)
2397+
expectPrintedTS(t, "x = (\n a ? (b = c) : { d: e }\n)", "x = a ? b = c : { d: e };\n")
2398+
2399+
// Need to clone "#private" identifier state in the parser
2400+
expectPrintedTS(t, "x = class { #y; y = a ? (b : T) : T => this.#y : c }", "x = class {\n #y;\n y = a ? (b) => this.#y : c;\n};\n")
2401+
2402+
// Need to clone "in" operator state in the parser
2403+
expectPrintedTS(t, "for (x = a ? () : T => b in c : d; ; ) ;", "for (x = a ? () => b in c : d; ; ) ;\n")
23962404
}
23972405

23982406
func TestTSSuperCall(t *testing.T) {

0 commit comments

Comments
 (0)