Skip to content

Commit

Permalink
allow certain reserved words as identifiers (#356)
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Sep 1, 2020
1 parent 3a8ad53 commit c5404a3
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 44 deletions.
20 changes: 12 additions & 8 deletions internal/lexer/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,6 @@ const (
TImplements
TInterface
TLet
TPackage
TPrivate
TProtected
TPublic
TStatic
TYield
)
Expand Down Expand Up @@ -208,14 +204,22 @@ var Keywords = map[string]T{
"implements": TImplements,
"interface": TInterface,
"let": TLet,
"package": TPackage,
"private": TPrivate,
"protected": TProtected,
"public": TPublic,
"static": TStatic,
"yield": TYield,
}

var StrictModeReservedWords = []string{
"implements",
"interface",
"let",
"package",
"private",
"protected",
"public",
"static",
"yield",
}

type json struct {
parse bool
allowComments bool
Expand Down
4 changes: 0 additions & 4 deletions internal/lexer/lexer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -537,10 +537,6 @@ func TestTokens(t *testing.T) {
{"implements", TImplements},
{"interface", TInterface},
{"let", TLet},
{"package", TPackage},
{"private", TPrivate},
{"protected", TProtected},
{"public", TPublic},
{"static", TStatic},
{"yield", TYield},
}
Expand Down
4 changes: 0 additions & 4 deletions internal/lexer/tables.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,6 @@ var tokenToString = map[T]string{
TImplements: "\"implements\"",
TInterface: "\"interface\"",
TLet: "\"let\"",
TPackage: "\"package\"",
TPrivate: "\"private\"",
TProtected: "\"protected\"",
TPublic: "\"public\"",
TStatic: "\"static\"",
TYield: "\"yield\"",
}
Expand Down
49 changes: 22 additions & 27 deletions internal/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ type fnOpts struct {
allowYield bool
allowSuperCall bool
isTopLevel bool
isConstructor bool

// In TypeScript, forward declarations of functions have no bodies
allowMissingBodyForTypeScript bool
Expand Down Expand Up @@ -1336,6 +1337,7 @@ func (p *parser) parseProperty(
allowYield: opts.isGenerator,
allowSuperCall: opts.classHasExtends && isConstructor,
allowTSDecorators: opts.allowTSDecorators,
isConstructor: isConstructor,

// Only allow omitting the body if we're parsing TypeScript class
allowMissingBodyForTypeScript: p.TS.Parse && opts.isClass,
Expand Down Expand Up @@ -3799,38 +3801,31 @@ func (p *parser) parseFn(name *ast.LocRef, opts fnOpts) (fn ast.Fn, hadBody bool
fn.HasRestArg = true
}

// Potentially parse a TypeScript accessibility modifier
isTypeScriptField := false
if p.TS.Parse {
switch p.lexer.Token {
case lexer.TPrivate, lexer.TProtected, lexer.TPublic:
isTypeScriptField = true
p.lexer.Next()

// TypeScript requires an identifier binding
if p.lexer.Token != lexer.TIdentifier {
p.lexer.Expect(lexer.TIdentifier)
}
}
}

isTypeScriptCtorField := false
isIdentifier := p.lexer.Token == lexer.TIdentifier
identifierText := p.lexer.Identifier
text := p.lexer.Identifier
arg := p.parseBinding()

if p.TS.Parse {
// Skip over "readonly"
isBeforeBinding := p.lexer.Token == lexer.TIdentifier || p.lexer.Token == lexer.TOpenBrace || p.lexer.Token == lexer.TOpenBracket
if isBeforeBinding && isIdentifier && identifierText == "readonly" {
isTypeScriptField = true
// Skip over TypeScript accessibility modifiers, which turn this argument
// into a class field when used inside a class constructor. This is known
// as a "parameter property" in TypeScript.
if isIdentifier && opts.isConstructor {
for p.lexer.Token == lexer.TIdentifier || p.lexer.Token == lexer.TOpenBrace || p.lexer.Token == lexer.TOpenBracket {
if text != "public" && text != "private" && text != "protected" && text != "readonly" {
break
}
isTypeScriptCtorField = true

// TypeScript requires an identifier binding
if p.lexer.Token != lexer.TIdentifier {
p.lexer.Expect(lexer.TIdentifier)
}
// TypeScript requires an identifier binding
if p.lexer.Token != lexer.TIdentifier {
p.lexer.Expect(lexer.TIdentifier)
}
text = p.lexer.Identifier

// Re-parse the binding (the current binding is the "readonly" keyword)
arg = p.parseBinding()
// Re-parse the binding (the current binding is the TypeScript keyword)
arg = p.parseBinding()
}
}

// "function foo(a?) {}"
Expand Down Expand Up @@ -3861,7 +3856,7 @@ func (p *parser) parseFn(name *ast.LocRef, opts fnOpts) (fn ast.Fn, hadBody bool
Default: defaultValue,

// We need to track this because it affects code generation
IsTypeScriptCtorField: isTypeScriptField,
IsTypeScriptCtorField: isTypeScriptCtorField,
})

if p.lexer.Token != lexer.TComma {
Expand Down
4 changes: 4 additions & 0 deletions internal/parser/parser_ts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,10 @@ func TestTSClass(t *testing.T) {
expectPrintedTS(t, "class A<T extends number> implements B.C<D, E>, F.G<H, I> {}", "class A {\n}\n")
expectPrintedTS(t, "class A<T extends number> extends X implements B.C<D, E>, F.G<H, I> {}", "class A extends X {\n}\n")

expectPrintedTS(t, "class Foo { constructor(public) {} }", "class Foo {\n constructor(public) {\n }\n}\n")
expectPrintedTS(t, "class Foo { constructor(protected) {} }", "class Foo {\n constructor(protected) {\n }\n}\n")
expectPrintedTS(t, "class Foo { constructor(private) {} }", "class Foo {\n constructor(private) {\n }\n}\n")
expectPrintedTS(t, "class Foo { constructor(readonly) {} }", "class Foo {\n constructor(readonly) {\n }\n}\n")
expectPrintedTS(t, "class Foo { constructor(public x) {} }", "class Foo {\n constructor(x) {\n this.x = x;\n }\n}\n")
expectPrintedTS(t, "class Foo { constructor(protected x) {} }", "class Foo {\n constructor(x) {\n this.x = x;\n }\n}\n")
expectPrintedTS(t, "class Foo { constructor(private x) {} }", "class Foo {\n constructor(x) {\n this.x = x;\n }\n}\n")
Expand Down
5 changes: 4 additions & 1 deletion internal/renamer/renamer.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ import (
func ComputeReservedNames(moduleScopes []*ast.Scope, symbols ast.SymbolMap) map[string]uint32 {
names := make(map[string]uint32)

// All keywords are reserved names
// All keywords and strict mode reserved words are reserved names
for k := range lexer.Keywords {
names[k] = 1
}
for _, k := range lexer.StrictModeReservedWords {
names[k] = 1
}

// All unbound symbols must be reserved names
for _, scope := range moduleScopes {
Expand Down

0 comments on commit c5404a3

Please sign in to comment.