Skip to content

Commit 9eca464

Browse files
committed
fix ASI after get/set before * in class
1 parent d34e79e commit 9eca464

File tree

4 files changed

+61
-13
lines changed

4 files changed

+61
-13
lines changed

CHANGELOG.md

+17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
* Allow automatic semicolon insertion after `get`/`set`
6+
7+
This change fixes a grammar bug in the parser that incorrectly treated the following code as a syntax error:
8+
9+
```ts
10+
class Foo {
11+
get
12+
*x() {}
13+
set
14+
*y() {}
15+
}
16+
```
17+
18+
The above code will be considered valid starting with this release. This change to esbuild follows a [similar change to TypeScript](https://github.com/microsoft/TypeScript/pull/60225) which will allow this syntax starting with TypeScript 5.7.
19+
320
## 0.24.0
421

522
**_This release deliberately contains backwards-incompatible changes._** To avoid automatically picking up releases like this, you should either be pinning the exact version of `esbuild` in your `package.json` file (recommended) or be using a version range syntax that only accepts patch upgrades such as `^0.23.0` or `~0.23.0`. See npm's documentation about [semver](https://docs.npmjs.com/cli/v6/using-npm/semver/) for more information.

internal/js_parser/js_parser.go

+14-11
Original file line numberDiff line numberDiff line change
@@ -2136,47 +2136,50 @@ func (p *parser) parseProperty(startLoc logger.Loc, kind js_ast.PropertyKind, op
21362136
couldBeModifierKeyword := p.lexer.IsIdentifierOrKeyword()
21372137
if !couldBeModifierKeyword {
21382138
switch p.lexer.Token {
2139-
case js_lexer.TOpenBracket, js_lexer.TNumericLiteral, js_lexer.TStringLiteral,
2140-
js_lexer.TAsterisk, js_lexer.TPrivateIdentifier:
2139+
case js_lexer.TOpenBracket, js_lexer.TNumericLiteral, js_lexer.TStringLiteral, js_lexer.TPrivateIdentifier:
21412140
couldBeModifierKeyword = true
2141+
case js_lexer.TAsterisk:
2142+
if opts.isAsync || (raw != "get" && raw != "set") {
2143+
couldBeModifierKeyword = true
2144+
}
21422145
}
21432146
}
21442147

21452148
// If so, check for a modifier keyword
21462149
if couldBeModifierKeyword {
2147-
switch name.String {
2150+
switch raw {
21482151
case "get":
2149-
if !opts.isAsync && raw == name.String {
2152+
if !opts.isAsync {
21502153
p.markSyntaxFeature(compat.ObjectAccessors, nameRange)
21512154
return p.parseProperty(startLoc, js_ast.PropertyGetter, opts, nil)
21522155
}
21532156

21542157
case "set":
2155-
if !opts.isAsync && raw == name.String {
2158+
if !opts.isAsync {
21562159
p.markSyntaxFeature(compat.ObjectAccessors, nameRange)
21572160
return p.parseProperty(startLoc, js_ast.PropertySetter, opts, nil)
21582161
}
21592162

21602163
case "accessor":
2161-
if !p.lexer.HasNewlineBefore && !opts.isAsync && opts.isClass && raw == name.String {
2164+
if !p.lexer.HasNewlineBefore && !opts.isAsync && opts.isClass {
21622165
return p.parseProperty(startLoc, js_ast.PropertyAutoAccessor, opts, nil)
21632166
}
21642167

21652168
case "async":
2166-
if !p.lexer.HasNewlineBefore && !opts.isAsync && raw == name.String {
2169+
if !p.lexer.HasNewlineBefore && !opts.isAsync {
21672170
opts.isAsync = true
21682171
opts.asyncRange = nameRange
21692172
return p.parseProperty(startLoc, js_ast.PropertyMethod, opts, nil)
21702173
}
21712174

21722175
case "static":
2173-
if !opts.isStatic && !opts.isAsync && opts.isClass && raw == name.String {
2176+
if !opts.isStatic && !opts.isAsync && opts.isClass {
21742177
opts.isStatic = true
21752178
return p.parseProperty(startLoc, kind, opts, nil)
21762179
}
21772180

21782181
case "declare":
2179-
if !p.lexer.HasNewlineBefore && opts.isClass && p.options.ts.Parse && opts.tsDeclareRange.Len == 0 && raw == name.String {
2182+
if !p.lexer.HasNewlineBefore && opts.isClass && p.options.ts.Parse && opts.tsDeclareRange.Len == 0 {
21802183
opts.tsDeclareRange = nameRange
21812184
scopeIndex := len(p.scopesInOrder)
21822185

@@ -2213,7 +2216,7 @@ func (p *parser) parseProperty(startLoc logger.Loc, kind js_ast.PropertyKind, op
22132216
}
22142217

22152218
case "abstract":
2216-
if !p.lexer.HasNewlineBefore && opts.isClass && p.options.ts.Parse && !opts.isTSAbstract && raw == name.String {
2219+
if !p.lexer.HasNewlineBefore && opts.isClass && p.options.ts.Parse && !opts.isTSAbstract {
22172220
opts.isTSAbstract = true
22182221
scopeIndex := len(p.scopesInOrder)
22192222

@@ -2249,7 +2252,7 @@ func (p *parser) parseProperty(startLoc logger.Loc, kind js_ast.PropertyKind, op
22492252

22502253
case "private", "protected", "public", "readonly", "override":
22512254
// Skip over TypeScript keywords
2252-
if opts.isClass && p.options.ts.Parse && raw == name.String {
2255+
if opts.isClass && p.options.ts.Parse {
22532256
return p.parseProperty(startLoc, kind, opts, nil)
22542257
}
22552258
}

internal/js_parser/js_parser_test.go

+20-2
Original file line numberDiff line numberDiff line change
@@ -1372,6 +1372,14 @@ func TestObject(t *testing.T) {
13721372
expectPrintedMangle(t, "x = { '2147483648': y }", "x = { \"2147483648\": y };\n")
13731373
expectPrintedMangle(t, "x = { '-2147483648': y }", "x = { \"-2147483648\": y };\n")
13741374
expectPrintedMangle(t, "x = { '-2147483649': y }", "x = { \"-2147483649\": y };\n")
1375+
1376+
// See: https://github.com/microsoft/TypeScript/pull/60225
1377+
expectPrinted(t, "x = { get \n x() {} }", "x = { get x() {\n} };\n")
1378+
expectPrinted(t, "x = { set \n x(_) {} }", "x = { set x(_) {\n} };\n")
1379+
expectParseError(t, "x = { get \n *x() {} }", "<stdin>: ERROR: Expected \"}\" but found \"*\"\n")
1380+
expectParseError(t, "x = { set \n *x(_) {} }", "<stdin>: ERROR: Expected \"}\" but found \"*\"\n")
1381+
expectParseError(t, "x = { get \n async x() {} }", "<stdin>: ERROR: Expected \"(\" but found \"x\"\n")
1382+
expectParseError(t, "x = { set \n async x(_) {} }", "<stdin>: ERROR: Expected \"(\" but found \"x\"\n")
13751383
}
13761384

13771385
func TestComputedProperty(t *testing.T) {
@@ -1797,6 +1805,16 @@ func TestClass(t *testing.T) {
17971805

17981806
// Make sure direct "eval" doesn't cause the class name to change
17991807
expectPrinted(t, "class Foo { foo = [Foo, eval(bar)] }", "class Foo {\n foo = [Foo, eval(bar)];\n}\n")
1808+
1809+
// See: https://github.com/microsoft/TypeScript/pull/60225
1810+
expectPrinted(t, "class A { get \n x() {} }", "class A {\n get x() {\n }\n}\n")
1811+
expectPrinted(t, "class A { set \n x(_) {} }", "class A {\n set x(_) {\n }\n}\n")
1812+
expectPrinted(t, "class A { get \n *x() {} }", "class A {\n get;\n *x() {\n }\n}\n")
1813+
expectPrinted(t, "class A { set \n *x(_) {} }", "class A {\n set;\n *x(_) {\n }\n}\n")
1814+
expectParseError(t, "class A { get \n async x() {} }", "<stdin>: ERROR: Expected \"(\" but found \"x\"\n")
1815+
expectParseError(t, "class A { set \n async x(_) {} }", "<stdin>: ERROR: Expected \"(\" but found \"x\"\n")
1816+
expectParseError(t, "class A { async get \n *x() {} }", "<stdin>: ERROR: Expected \"(\" but found \"*\"\n")
1817+
expectParseError(t, "class A { async set \n *x(_) {} }", "<stdin>: ERROR: Expected \"(\" but found \"*\"\n")
18001818
}
18011819

18021820
func TestSuperCall(t *testing.T) {
@@ -2185,8 +2203,8 @@ __privateAdd(Foo, _x, __runInitializers(_init, 8, Foo)), __runInitializers(_init
21852203
func TestGenerator(t *testing.T) {
21862204
expectParseError(t, "(class { * foo })", "<stdin>: ERROR: Expected \"(\" but found \"}\"\n")
21872205
expectParseError(t, "(class { * *foo() {} })", "<stdin>: ERROR: Unexpected \"*\"\n")
2188-
expectParseError(t, "(class { get*foo() {} })", "<stdin>: ERROR: Unexpected \"*\"\n")
2189-
expectParseError(t, "(class { set*foo() {} })", "<stdin>: ERROR: Unexpected \"*\"\n")
2206+
expectParseError(t, "(class { get*foo() {} })", "<stdin>: ERROR: Expected \";\" but found \"*\"\n")
2207+
expectParseError(t, "(class { set*foo() {} })", "<stdin>: ERROR: Expected \";\" but found \"*\"\n")
21902208
expectParseError(t, "(class { *get foo() {} })", "<stdin>: ERROR: Expected \"(\" but found \"foo\"\n")
21912209
expectParseError(t, "(class { *set foo() {} })", "<stdin>: ERROR: Expected \"(\" but found \"foo\"\n")
21922210
expectParseError(t, "(class { *static foo() {} })", "<stdin>: ERROR: Expected \"(\" but found \"foo\"\n")

internal/js_parser/ts_parser_test.go

+10
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,16 @@ func TestTSClass(t *testing.T) {
820820
expectParseErrorTS(t, "class Foo { [foo]<T> }", "<stdin>: ERROR: Expected \"(\" but found \"}\"\n")
821821
expectParseErrorTS(t, "class Foo { [foo]?<T> }", "<stdin>: ERROR: Expected \"(\" but found \"}\"\n")
822822
expectParseErrorTS(t, "class Foo { [foo]!<T>() {} }", "<stdin>: ERROR: Expected \";\" but found \"<\"\n")
823+
824+
// See: https://github.com/microsoft/TypeScript/pull/60225
825+
expectPrintedTS(t, "class A { get \n x() {} }", "class A {\n get x() {\n }\n}\n")
826+
expectPrintedTS(t, "class A { set \n x(_) {} }", "class A {\n set x(_) {\n }\n}\n")
827+
expectPrintedTS(t, "class A { get \n *x() {} }", "class A {\n get;\n *x() {\n }\n}\n")
828+
expectPrintedTS(t, "class A { set \n *x(_) {} }", "class A {\n set;\n *x(_) {\n }\n}\n")
829+
expectParseErrorTS(t, "class A { get \n async x() {} }", "<stdin>: ERROR: Expected \"(\" but found \"x\"\n")
830+
expectParseErrorTS(t, "class A { set \n async x(_) {} }", "<stdin>: ERROR: Expected \"(\" but found \"x\"\n")
831+
expectParseErrorTS(t, "class A { async get \n *x() {} }", "<stdin>: ERROR: Expected \"(\" but found \"*\"\n")
832+
expectParseErrorTS(t, "class A { async set \n *x(_) {} }", "<stdin>: ERROR: Expected \"(\" but found \"*\"\n")
823833
}
824834

825835
func TestTSAutoAccessors(t *testing.T) {

0 commit comments

Comments
 (0)