Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse quoted constructors as constructors, not methods #31949

Merged
merged 6 commits into from Jul 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 0 additions & 3 deletions src/compiler/checker.ts
Expand Up @@ -32053,9 +32053,6 @@ namespace ts {
return grammarErrorAtPos(node, node.end - 1, ";".length, Diagnostics._0_expected, "{");
}
}
else if (isClassLike(node.parent) && isStringLiteral(node.name) && node.name.text === "constructor" && (!compilerOptions.target || compilerOptions.target < ScriptTarget.ES5)) {
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);
}
if (checkGrammarForGenerator(node)) {
return true;
}
Expand Down
4 changes: 0 additions & 4 deletions src/compiler/diagnosticMessages.json
Expand Up @@ -4995,10 +4995,6 @@
"category": "Error",
"code": 18004
},
"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.": {
"category": "Error",
"code": 18005
},
"Classes may not have a field named 'constructor'.": {
"category": "Error",
"code": 18006
Expand Down
34 changes: 26 additions & 8 deletions src/compiler/parser.ts
Expand Up @@ -5650,12 +5650,27 @@ namespace ts {
return finishNode(node);
}

function parseConstructorDeclaration(node: ConstructorDeclaration): ConstructorDeclaration {
node.kind = SyntaxKind.Constructor;
parseExpected(SyntaxKind.ConstructorKeyword);
fillSignature(SyntaxKind.ColonToken, SignatureFlags.None, node);
node.body = parseFunctionBlockOrSemicolon(SignatureFlags.None, Diagnostics.or_expected);
return finishNode(node);
function parseConstructorName() {
if (token() === SyntaxKind.ConstructorKeyword) {
return parseExpected(SyntaxKind.ConstructorKeyword);
}
if (token() === SyntaxKind.StringLiteral && lookAhead(nextToken) === SyntaxKind.OpenParenToken) {
return tryParse(() => {
const literalNode = parseLiteralNode();
return literalNode.text === "constructor" ? literalNode : undefined;
});
}
}

function tryParseConstructorDeclaration(node: ConstructorDeclaration): ConstructorDeclaration | undefined {
return tryParse(() => {
if (parseConstructorName()) {
node.kind = SyntaxKind.Constructor;
fillSignature(SyntaxKind.ColonToken, SignatureFlags.None, node);
node.body = parseFunctionBlockOrSemicolon(SignatureFlags.None, Diagnostics.or_expected);
return finishNode(node);
}
});
}

function parseMethodDeclaration(node: MethodDeclaration, asteriskToken: AsteriskToken, diagnosticMessage?: DiagnosticMessage): MethodDeclaration {
Expand Down Expand Up @@ -5861,8 +5876,11 @@ namespace ts {
return parseAccessorDeclaration(<AccessorDeclaration>node, SyntaxKind.SetAccessor);
}

if (token() === SyntaxKind.ConstructorKeyword) {
return parseConstructorDeclaration(<ConstructorDeclaration>node);
if (token() === SyntaxKind.ConstructorKeyword || token() === SyntaxKind.StringLiteral) {
const constructorDeclaration = tryParseConstructorDeclaration(<ConstructorDeclaration>node);
if (constructorDeclaration) {
return constructorDeclaration;
}
}

if (isIndexSignature()) {
Expand Down
17 changes: 17 additions & 0 deletions src/compiler/utilities.ts
Expand Up @@ -3148,6 +3148,23 @@ namespace ts {
return s.replace(escapedCharsRegExp, getReplacement);
}

/**
* Strip off existed single quotes or double quotes from a given string
*
* @return non-quoted string
*/
export function stripQuotes(name: string) {
const length = name.length;
if (length >= 2 && name.charCodeAt(0) === name.charCodeAt(length - 1) && startsWithQuote(name)) {
return name.substring(1, length - 1);
}
return name;
}

export function startsWithQuote(name: string): boolean {
return isSingleOrDoubleQuote(name.charCodeAt(0));
}

function getReplacement(c: string, offset: number, input: string) {
if (c.charCodeAt(0) === CharacterCodes.nullCharacter) {
const lookAhead = input.charCodeAt(offset + c.length);
Expand Down
17 changes: 0 additions & 17 deletions src/services/utilities.ts
Expand Up @@ -1636,23 +1636,6 @@ namespace ts {
return !!location.parent && isImportOrExportSpecifier(location.parent) && location.parent.propertyName === location;
}

/**
* Strip off existed single quotes or double quotes from a given string
*
* @return non-quoted string
*/
export function stripQuotes(name: string) {
const length = name.length;
if (length >= 2 && name.charCodeAt(0) === name.charCodeAt(length - 1) && startsWithQuote(name)) {
return name.substring(1, length - 1);
}
return name;
}

export function startsWithQuote(name: string): boolean {
return isSingleOrDoubleQuote(name.charCodeAt(0));
}

export function scriptKindIs(fileName: string, host: LanguageServiceHost, ...scriptKinds: ScriptKind[]): boolean {
const scriptKind = getScriptKind(fileName, host);
return some(scriptKinds, k => k === scriptKind);
Expand Down
30 changes: 0 additions & 30 deletions tests/baselines/reference/quotedConstructors.errors.txt

This file was deleted.

38 changes: 30 additions & 8 deletions tests/baselines/reference/quotedConstructors.js
@@ -1,46 +1,68 @@
//// [quotedConstructors.ts]
class C {
"constructor"() {} // Error in 3.5
"constructor"() {
console.log(this);
}
}

class D {
'constructor'() {} // Error in 3.5
'constructor'() {
console.log(this);
}
}

class E {
['constructor']() {}
['constructor']() {
console.log(this);
}
}

new class {
"constructor"() {} // Error in 3.5
"constructor"() {
console.log(this);
}
};

var o = { "constructor"() {} };

class F {
"\x63onstructor"() {
console.log(this);
}
}


//// [quotedConstructors.js]
var C = /** @class */ (function () {
function C() {
console.log(this);
}
C.prototype["constructor"] = function () { }; // Error in 3.5
return C;
}());
var D = /** @class */ (function () {
function D() {
console.log(this);
}
D.prototype['constructor'] = function () { }; // Error in 3.5
return D;
}());
var E = /** @class */ (function () {
function E() {
}
E.prototype['constructor'] = function () { };
E.prototype['constructor'] = function () {
console.log(this);
};
return E;
}());
new /** @class */ (function () {
function class_1() {
console.log(this);
}
class_1.prototype["constructor"] = function () { }; // Error in 3.5
return class_1;
}());
var o = { "constructor": function () { } };
var F = /** @class */ (function () {
function F() {
console.log(this);
}
return F;
}());
61 changes: 47 additions & 14 deletions tests/baselines/reference/quotedConstructors.symbols
Expand Up @@ -2,32 +2,65 @@
class C {
>C : Symbol(C, Decl(quotedConstructors.ts, 0, 0))

"constructor"() {} // Error in 3.5
>"constructor" : Symbol(C["constructor"], Decl(quotedConstructors.ts, 0, 9))
"constructor"() {
console.log(this);
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>this : Symbol(C, Decl(quotedConstructors.ts, 0, 0))
}
}

class D {
>D : Symbol(D, Decl(quotedConstructors.ts, 2, 1))
>D : Symbol(D, Decl(quotedConstructors.ts, 4, 1))

'constructor'() {} // Error in 3.5
>'constructor' : Symbol(D['constructor'], Decl(quotedConstructors.ts, 4, 9))
'constructor'() {
console.log(this);
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>this : Symbol(D, Decl(quotedConstructors.ts, 4, 1))
}
}

class E {
>E : Symbol(E, Decl(quotedConstructors.ts, 6, 1))
>E : Symbol(E, Decl(quotedConstructors.ts, 10, 1))

['constructor']() {}
>['constructor'] : Symbol(E['constructor'], Decl(quotedConstructors.ts, 8, 9))
>'constructor' : Symbol(E['constructor'], Decl(quotedConstructors.ts, 8, 9))
['constructor']() {
>['constructor'] : Symbol(E['constructor'], Decl(quotedConstructors.ts, 12, 9))
>'constructor' : Symbol(E['constructor'], Decl(quotedConstructors.ts, 12, 9))

console.log(this);
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>this : Symbol(E, Decl(quotedConstructors.ts, 10, 1))
}
}

new class {
"constructor"() {} // Error in 3.5
>"constructor" : Symbol((Anonymous class)["constructor"], Decl(quotedConstructors.ts, 12, 11))

"constructor"() {
console.log(this);
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>this : Symbol((Anonymous class), Decl(quotedConstructors.ts, 18, 3))
}
};

var o = { "constructor"() {} };
>o : Symbol(o, Decl(quotedConstructors.ts, 16, 3))
>"constructor" : Symbol("constructor", Decl(quotedConstructors.ts, 16, 9))
>o : Symbol(o, Decl(quotedConstructors.ts, 24, 3))
>"constructor" : Symbol("constructor", Decl(quotedConstructors.ts, 24, 9))

class F {
>F : Symbol(F, Decl(quotedConstructors.ts, 24, 31))

"\x63onstructor"() {
console.log(this);
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>this : Symbol(F, Decl(quotedConstructors.ts, 24, 31))
}
}

58 changes: 48 additions & 10 deletions tests/baselines/reference/quotedConstructors.types
Expand Up @@ -2,36 +2,74 @@
class C {
>C : C

"constructor"() {} // Error in 3.5
>"constructor" : () => void
"constructor"() {
console.log(this);
>console.log(this) : void
>console.log : (message?: any, ...optionalParams: any[]) => void
>console : Console
>log : (message?: any, ...optionalParams: any[]) => void
>this : this
}
}

class D {
>D : D

'constructor'() {} // Error in 3.5
>'constructor' : () => void
'constructor'() {
console.log(this);
>console.log(this) : void
>console.log : (message?: any, ...optionalParams: any[]) => void
>console : Console
>log : (message?: any, ...optionalParams: any[]) => void
>this : this
}
}

class E {
>E : E

['constructor']() {}
['constructor']() {
>['constructor'] : () => void
>'constructor' : "constructor"

console.log(this);
>console.log(this) : void
>console.log : (message?: any, ...optionalParams: any[]) => void
>console : Console
>log : (message?: any, ...optionalParams: any[]) => void
>this : this
}
}

new class {
>new class { "constructor"() {} // Error in 3.5} : (Anonymous class)
>class { "constructor"() {} // Error in 3.5} : typeof (Anonymous class)

"constructor"() {} // Error in 3.5
>"constructor" : () => void
>new class { "constructor"() { console.log(this); }} : (Anonymous class)
>class { "constructor"() { console.log(this); }} : typeof (Anonymous class)

"constructor"() {
console.log(this);
>console.log(this) : void
>console.log : (message?: any, ...optionalParams: any[]) => void
>console : Console
>log : (message?: any, ...optionalParams: any[]) => void
>this : this
}
};

var o = { "constructor"() {} };
>o : { "constructor"(): void; }
>{ "constructor"() {} } : { "constructor"(): void; }
>"constructor" : () => void

class F {
>F : F

"\x63onstructor"() {
console.log(this);
>console.log(this) : void
>console.log : (message?: any, ...optionalParams: any[]) => void
>console : Console
>log : (message?: any, ...optionalParams: any[]) => void
>this : this
}
}