Skip to content

Commit 4f34121

Browse files
authored
Parse quoted constructors as constructors, not methods (microsoft#31949)
* Parse quoted constructors as constructors, not methods * Update baselines * Fix disambiguation between quoted constructor and property named constructor * Clean up parsing a bit * Support escapes in constructor name * Update baselines
1 parent 37f2e59 commit 4f34121

File tree

10 files changed

+186
-98
lines changed

10 files changed

+186
-98
lines changed

src/compiler/checker.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33017,9 +33017,6 @@ namespace ts {
3301733017
return grammarErrorAtPos(node, node.end - 1, ";".length, Diagnostics._0_expected, "{");
3301833018
}
3301933019
}
33020-
else if (isClassLike(node.parent) && isStringLiteral(node.name) && node.name.text === "constructor" && (!compilerOptions.target || compilerOptions.target < ScriptTarget.ES5)) {
33021-
return grammarErrorOnNode(node.name, Diagnostics.Quoted_constructors_have_previously_been_interpreted_as_methods_which_is_incorrect_In_TypeScript_3_6_they_will_be_correctly_parsed_as_constructors_In_the_meantime_consider_using_constructor_to_write_a_constructor_or_constructor_to_write_a_method);
33022-
}
3302333020
if (checkGrammarForGenerator(node)) {
3302433021
return true;
3302533022
}

src/compiler/diagnosticMessages.json

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5113,10 +5113,6 @@
51135113
"category": "Error",
51145114
"code": 18004
51155115
},
5116-
"Quoted constructors have previously been interpreted as methods, which is incorrect. In TypeScript 3.6, they will be correctly parsed as constructors. In the meantime, consider using 'constructor()' to write a constructor, or '[\"constructor\"]()' to write a method.": {
5117-
"category": "Error",
5118-
"code": 18005
5119-
},
51205116
"Classes may not have a field named 'constructor'.": {
51215117
"category": "Error",
51225118
"code": 18006

src/compiler/parser.ts

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5656,12 +5656,27 @@ namespace ts {
56565656
return finishNode(node);
56575657
}
56585658

5659-
function parseConstructorDeclaration(node: ConstructorDeclaration): ConstructorDeclaration {
5660-
node.kind = SyntaxKind.Constructor;
5661-
parseExpected(SyntaxKind.ConstructorKeyword);
5662-
fillSignature(SyntaxKind.ColonToken, SignatureFlags.None, node);
5663-
node.body = parseFunctionBlockOrSemicolon(SignatureFlags.None, Diagnostics.or_expected);
5664-
return finishNode(node);
5659+
function parseConstructorName() {
5660+
if (token() === SyntaxKind.ConstructorKeyword) {
5661+
return parseExpected(SyntaxKind.ConstructorKeyword);
5662+
}
5663+
if (token() === SyntaxKind.StringLiteral && lookAhead(nextToken) === SyntaxKind.OpenParenToken) {
5664+
return tryParse(() => {
5665+
const literalNode = parseLiteralNode();
5666+
return literalNode.text === "constructor" ? literalNode : undefined;
5667+
});
5668+
}
5669+
}
5670+
5671+
function tryParseConstructorDeclaration(node: ConstructorDeclaration): ConstructorDeclaration | undefined {
5672+
return tryParse(() => {
5673+
if (parseConstructorName()) {
5674+
node.kind = SyntaxKind.Constructor;
5675+
fillSignature(SyntaxKind.ColonToken, SignatureFlags.None, node);
5676+
node.body = parseFunctionBlockOrSemicolon(SignatureFlags.None, Diagnostics.or_expected);
5677+
return finishNode(node);
5678+
}
5679+
});
56655680
}
56665681

56675682
function parseMethodDeclaration(node: MethodDeclaration, asteriskToken: AsteriskToken, diagnosticMessage?: DiagnosticMessage): MethodDeclaration {
@@ -5867,8 +5882,11 @@ namespace ts {
58675882
return parseAccessorDeclaration(<AccessorDeclaration>node, SyntaxKind.SetAccessor);
58685883
}
58695884

5870-
if (token() === SyntaxKind.ConstructorKeyword) {
5871-
return parseConstructorDeclaration(<ConstructorDeclaration>node);
5885+
if (token() === SyntaxKind.ConstructorKeyword || token() === SyntaxKind.StringLiteral) {
5886+
const constructorDeclaration = tryParseConstructorDeclaration(<ConstructorDeclaration>node);
5887+
if (constructorDeclaration) {
5888+
return constructorDeclaration;
5889+
}
58725890
}
58735891

58745892
if (isIndexSignature()) {

src/compiler/utilities.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3150,6 +3150,23 @@ namespace ts {
31503150
return s.replace(escapedCharsRegExp, getReplacement);
31513151
}
31523152

3153+
/**
3154+
* Strip off existed single quotes or double quotes from a given string
3155+
*
3156+
* @return non-quoted string
3157+
*/
3158+
export function stripQuotes(name: string) {
3159+
const length = name.length;
3160+
if (length >= 2 && name.charCodeAt(0) === name.charCodeAt(length - 1) && startsWithQuote(name)) {
3161+
return name.substring(1, length - 1);
3162+
}
3163+
return name;
3164+
}
3165+
3166+
export function startsWithQuote(name: string): boolean {
3167+
return isSingleOrDoubleQuote(name.charCodeAt(0));
3168+
}
3169+
31533170
function getReplacement(c: string, offset: number, input: string) {
31543171
if (c.charCodeAt(0) === CharacterCodes.nullCharacter) {
31553172
const lookAhead = input.charCodeAt(offset + c.length);

src/services/utilities.ts

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1636,23 +1636,6 @@ namespace ts {
16361636
return !!location.parent && isImportOrExportSpecifier(location.parent) && location.parent.propertyName === location;
16371637
}
16381638

1639-
/**
1640-
* Strip off existed single quotes or double quotes from a given string
1641-
*
1642-
* @return non-quoted string
1643-
*/
1644-
export function stripQuotes(name: string) {
1645-
const length = name.length;
1646-
if (length >= 2 && name.charCodeAt(0) === name.charCodeAt(length - 1) && startsWithQuote(name)) {
1647-
return name.substring(1, length - 1);
1648-
}
1649-
return name;
1650-
}
1651-
1652-
export function startsWithQuote(name: string): boolean {
1653-
return isSingleOrDoubleQuote(name.charCodeAt(0));
1654-
}
1655-
16561639
export function scriptKindIs(fileName: string, host: LanguageServiceHost, ...scriptKinds: ScriptKind[]): boolean {
16571640
const scriptKind = getScriptKind(fileName, host);
16581641
return some(scriptKinds, k => k === scriptKind);

tests/baselines/reference/quotedConstructors.errors.txt

Lines changed: 0 additions & 30 deletions
This file was deleted.
Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,68 @@
11
//// [quotedConstructors.ts]
22
class C {
3-
"constructor"() {} // Error in 3.5
3+
"constructor"() {
4+
console.log(this);
5+
}
46
}
57

68
class D {
7-
'constructor'() {} // Error in 3.5
9+
'constructor'() {
10+
console.log(this);
11+
}
812
}
913

1014
class E {
11-
['constructor']() {}
15+
['constructor']() {
16+
console.log(this);
17+
}
1218
}
1319

1420
new class {
15-
"constructor"() {} // Error in 3.5
21+
"constructor"() {
22+
console.log(this);
23+
}
1624
};
1725

1826
var o = { "constructor"() {} };
27+
28+
class F {
29+
"\x63onstructor"() {
30+
console.log(this);
31+
}
32+
}
1933

2034

2135
//// [quotedConstructors.js]
2236
var C = /** @class */ (function () {
2337
function C() {
38+
console.log(this);
2439
}
25-
C.prototype["constructor"] = function () { }; // Error in 3.5
2640
return C;
2741
}());
2842
var D = /** @class */ (function () {
2943
function D() {
44+
console.log(this);
3045
}
31-
D.prototype['constructor'] = function () { }; // Error in 3.5
3246
return D;
3347
}());
3448
var E = /** @class */ (function () {
3549
function E() {
3650
}
37-
E.prototype['constructor'] = function () { };
51+
E.prototype['constructor'] = function () {
52+
console.log(this);
53+
};
3854
return E;
3955
}());
4056
new /** @class */ (function () {
4157
function class_1() {
58+
console.log(this);
4259
}
43-
class_1.prototype["constructor"] = function () { }; // Error in 3.5
4460
return class_1;
4561
}());
4662
var o = { "constructor": function () { } };
63+
var F = /** @class */ (function () {
64+
function F() {
65+
console.log(this);
66+
}
67+
return F;
68+
}());

tests/baselines/reference/quotedConstructors.symbols

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,65 @@
22
class C {
33
>C : Symbol(C, Decl(quotedConstructors.ts, 0, 0))
44

5-
"constructor"() {} // Error in 3.5
6-
>"constructor" : Symbol(C["constructor"], Decl(quotedConstructors.ts, 0, 9))
5+
"constructor"() {
6+
console.log(this);
7+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
8+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
9+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
10+
>this : Symbol(C, Decl(quotedConstructors.ts, 0, 0))
11+
}
712
}
813

914
class D {
10-
>D : Symbol(D, Decl(quotedConstructors.ts, 2, 1))
15+
>D : Symbol(D, Decl(quotedConstructors.ts, 4, 1))
1116

12-
'constructor'() {} // Error in 3.5
13-
>'constructor' : Symbol(D['constructor'], Decl(quotedConstructors.ts, 4, 9))
17+
'constructor'() {
18+
console.log(this);
19+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
20+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
21+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
22+
>this : Symbol(D, Decl(quotedConstructors.ts, 4, 1))
23+
}
1424
}
1525

1626
class E {
17-
>E : Symbol(E, Decl(quotedConstructors.ts, 6, 1))
27+
>E : Symbol(E, Decl(quotedConstructors.ts, 10, 1))
1828

19-
['constructor']() {}
20-
>['constructor'] : Symbol(E['constructor'], Decl(quotedConstructors.ts, 8, 9))
21-
>'constructor' : Symbol(E['constructor'], Decl(quotedConstructors.ts, 8, 9))
29+
['constructor']() {
30+
>['constructor'] : Symbol(E['constructor'], Decl(quotedConstructors.ts, 12, 9))
31+
>'constructor' : Symbol(E['constructor'], Decl(quotedConstructors.ts, 12, 9))
32+
33+
console.log(this);
34+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
35+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
36+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
37+
>this : Symbol(E, Decl(quotedConstructors.ts, 10, 1))
38+
}
2239
}
2340

2441
new class {
25-
"constructor"() {} // Error in 3.5
26-
>"constructor" : Symbol((Anonymous class)["constructor"], Decl(quotedConstructors.ts, 12, 11))
27-
42+
"constructor"() {
43+
console.log(this);
44+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
45+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
46+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
47+
>this : Symbol((Anonymous class), Decl(quotedConstructors.ts, 18, 3))
48+
}
2849
};
2950

3051
var o = { "constructor"() {} };
31-
>o : Symbol(o, Decl(quotedConstructors.ts, 16, 3))
32-
>"constructor" : Symbol("constructor", Decl(quotedConstructors.ts, 16, 9))
52+
>o : Symbol(o, Decl(quotedConstructors.ts, 24, 3))
53+
>"constructor" : Symbol("constructor", Decl(quotedConstructors.ts, 24, 9))
54+
55+
class F {
56+
>F : Symbol(F, Decl(quotedConstructors.ts, 24, 31))
57+
58+
"\x63onstructor"() {
59+
console.log(this);
60+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
61+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
62+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
63+
>this : Symbol(F, Decl(quotedConstructors.ts, 24, 31))
64+
}
65+
}
3366

tests/baselines/reference/quotedConstructors.types

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,74 @@
22
class C {
33
>C : C
44

5-
"constructor"() {} // Error in 3.5
6-
>"constructor" : () => void
5+
"constructor"() {
6+
console.log(this);
7+
>console.log(this) : void
8+
>console.log : (message?: any, ...optionalParams: any[]) => void
9+
>console : Console
10+
>log : (message?: any, ...optionalParams: any[]) => void
11+
>this : this
12+
}
713
}
814

915
class D {
1016
>D : D
1117

12-
'constructor'() {} // Error in 3.5
13-
>'constructor' : () => void
18+
'constructor'() {
19+
console.log(this);
20+
>console.log(this) : void
21+
>console.log : (message?: any, ...optionalParams: any[]) => void
22+
>console : Console
23+
>log : (message?: any, ...optionalParams: any[]) => void
24+
>this : this
25+
}
1426
}
1527

1628
class E {
1729
>E : E
1830

19-
['constructor']() {}
31+
['constructor']() {
2032
>['constructor'] : () => void
2133
>'constructor' : "constructor"
34+
35+
console.log(this);
36+
>console.log(this) : void
37+
>console.log : (message?: any, ...optionalParams: any[]) => void
38+
>console : Console
39+
>log : (message?: any, ...optionalParams: any[]) => void
40+
>this : this
41+
}
2242
}
2343

2444
new class {
25-
>new class { "constructor"() {} // Error in 3.5} : (Anonymous class)
26-
>class { "constructor"() {} // Error in 3.5} : typeof (Anonymous class)
27-
28-
"constructor"() {} // Error in 3.5
29-
>"constructor" : () => void
45+
>new class { "constructor"() { console.log(this); }} : (Anonymous class)
46+
>class { "constructor"() { console.log(this); }} : typeof (Anonymous class)
3047

48+
"constructor"() {
49+
console.log(this);
50+
>console.log(this) : void
51+
>console.log : (message?: any, ...optionalParams: any[]) => void
52+
>console : Console
53+
>log : (message?: any, ...optionalParams: any[]) => void
54+
>this : this
55+
}
3156
};
3257

3358
var o = { "constructor"() {} };
3459
>o : { "constructor"(): void; }
3560
>{ "constructor"() {} } : { "constructor"(): void; }
3661
>"constructor" : () => void
3762

63+
class F {
64+
>F : F
65+
66+
"\x63onstructor"() {
67+
console.log(this);
68+
>console.log(this) : void
69+
>console.log : (message?: any, ...optionalParams: any[]) => void
70+
>console : Console
71+
>log : (message?: any, ...optionalParams: any[]) => void
72+
>this : this
73+
}
74+
}
75+

0 commit comments

Comments
 (0)