From 0672923323b35711cd65bf233955d4e848e449ef Mon Sep 17 00:00:00 2001 From: Yui T Date: Thu, 12 Mar 2015 15:19:45 -0700 Subject: [PATCH] Parse classDeclaration in strict mode code for ES6 --- src/compiler/checker.ts | 6 ++- src/compiler/parser.ts | 22 ++++++++++- ...ationInStrictModeByDefaultInES6.errors.txt | 31 +++++++++++++++ ...ssDeclarationInStrictModeByDefaultInES6.js | 23 +++++++++++ ...ssDeclarationInStrictModeByDefaultInES6.ts | 9 +++++ tests/cases/unittests/incrementalParser.ts | 39 +++++++++++++++++++ 6 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/parseClassDeclarationInStrictModeByDefaultInES6.errors.txt create mode 100644 tests/baselines/reference/parseClassDeclarationInStrictModeByDefaultInES6.js create mode 100644 tests/cases/conformance/es6/classDeclaration/parseClassDeclarationInStrictModeByDefaultInES6.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 28b3b74b271..689f27af867 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11891,7 +11891,11 @@ module ts { var identifier = name; if (contextNode && (contextNode.parserContextFlags & ParserContextFlags.StrictMode) && isEvalOrArgumentsIdentifier(identifier)) { var nameText = declarationNameToString(identifier); - return grammarErrorOnNode(identifier, Diagnostics.Invalid_use_of_0_in_strict_mode, nameText); + + // Always report 'eval' and 'arguments' invalid usage in strict mode code regardless of parser diagnostics + var sourceFile = getSourceFileOfNode(identifier); + diagnostics.add(createDiagnosticForNode(identifier, Diagnostics.Invalid_use_of_0_in_strict_mode, nameText)); + return true; } } } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 0d6f002a3da..7658a49bc48 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4372,6 +4372,12 @@ module ts { function parsePropertyOrMethodDeclaration(fullStart: number, modifiers: ModifiersArray): ClassElement { var asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); + + // From ES6 Specification, "implements", "interface", "let", "package", "private", "protected", "public", "static", and "yield" are reserved words within strict mode code + if (inStrictModeContext() && (token > SyntaxKind.LastReservedWord)) { + parseErrorAtCurrentToken(Diagnostics.Invalid_use_of_0_in_strict_mode, tokenToString(token)); + } + var name = parsePropertyName(); // Note: this is not legal as per the grammar. But we allow it in the parser and @@ -4517,6 +4523,12 @@ module ts { } function parseClassDeclaration(fullStart: number, modifiers: ModifiersArray): ClassDeclaration { + // In ES6 specification, All parts of a ClassDeclaration or a ClassExpression are strict mode code + if (languageVersion >= ScriptTarget.ES6) { + var savedStrictModeContext = inStrictModeContext(); + setStrictModeContext(true); + } + var node = createNode(SyntaxKind.ClassDeclaration, fullStart); setModifiers(node, modifiers); parseExpected(SyntaxKind.ClassKeyword); @@ -4537,7 +4549,15 @@ module ts { else { node.members = createMissingList(); } - return finishNode(node); + + var finishedNode = finishNode(node); + if (languageVersion >= ScriptTarget.ES6) { + setStrictModeContext(savedStrictModeContext); + return finishedNode; + } + else { + return finishedNode; + } } function parseHeritageClauses(isClassHeritageClause: boolean): NodeArray { diff --git a/tests/baselines/reference/parseClassDeclarationInStrictModeByDefaultInES6.errors.txt b/tests/baselines/reference/parseClassDeclarationInStrictModeByDefaultInES6.errors.txt new file mode 100644 index 00000000000..04ac8b28a8b --- /dev/null +++ b/tests/baselines/reference/parseClassDeclarationInStrictModeByDefaultInES6.errors.txt @@ -0,0 +1,31 @@ +tests/cases/conformance/es6/classDeclaration/parseClassDeclarationInStrictModeByDefaultInES6.ts(2,5): error TS1100: Invalid use of 'interface' in strict mode. +tests/cases/conformance/es6/classDeclaration/parseClassDeclarationInStrictModeByDefaultInES6.ts(3,12): error TS1100: Invalid use of 'implements' in strict mode. +tests/cases/conformance/es6/classDeclaration/parseClassDeclarationInStrictModeByDefaultInES6.ts(4,16): error TS1100: Invalid use of 'arguments' in strict mode. +tests/cases/conformance/es6/classDeclaration/parseClassDeclarationInStrictModeByDefaultInES6.ts(5,17): error TS1100: Invalid use of 'eval' in strict mode. +tests/cases/conformance/es6/classDeclaration/parseClassDeclarationInStrictModeByDefaultInES6.ts(6,9): error TS1100: Invalid use of 'arguments' in strict mode. +tests/cases/conformance/es6/classDeclaration/parseClassDeclarationInStrictModeByDefaultInES6.ts(6,9): error TS2322: Type 'string' is not assignable to type 'IArguments'. + Property 'callee' is missing in type 'String'. + + +==== tests/cases/conformance/es6/classDeclaration/parseClassDeclarationInStrictModeByDefaultInES6.ts (6 errors) ==== + class C { + interface = 10; + ~~~~~~~~~ +!!! error TS1100: Invalid use of 'interface' in strict mode. + public implements() { } + ~~~~~~~~~~ +!!! error TS1100: Invalid use of 'implements' in strict mode. + public foo(arguments: any) { } + ~~~~~~~~~ +!!! error TS1100: Invalid use of 'arguments' in strict mode. + private bar(eval:any) { + ~~~~ +!!! error TS1100: Invalid use of 'eval' in strict mode. + arguments = "hello"; + ~~~~~~~~~ +!!! error TS1100: Invalid use of 'arguments' in strict mode. + ~~~~~~~~~ +!!! error TS2322: Type 'string' is not assignable to type 'IArguments'. +!!! error TS2322: Property 'callee' is missing in type 'String'. + } + } \ No newline at end of file diff --git a/tests/baselines/reference/parseClassDeclarationInStrictModeByDefaultInES6.js b/tests/baselines/reference/parseClassDeclarationInStrictModeByDefaultInES6.js new file mode 100644 index 00000000000..2a8d75dde60 --- /dev/null +++ b/tests/baselines/reference/parseClassDeclarationInStrictModeByDefaultInES6.js @@ -0,0 +1,23 @@ +//// [parseClassDeclarationInStrictModeByDefaultInES6.ts] +class C { + interface = 10; + public implements() { } + public foo(arguments: any) { } + private bar(eval:any) { + arguments = "hello"; + } +} + +//// [parseClassDeclarationInStrictModeByDefaultInES6.js] +class C { + constructor() { + this.interface = 10; + } + implements() { + } + foo(arguments) { + } + bar(eval) { + arguments = "hello"; + } +} diff --git a/tests/cases/conformance/es6/classDeclaration/parseClassDeclarationInStrictModeByDefaultInES6.ts b/tests/cases/conformance/es6/classDeclaration/parseClassDeclarationInStrictModeByDefaultInES6.ts new file mode 100644 index 00000000000..b2517d89657 --- /dev/null +++ b/tests/cases/conformance/es6/classDeclaration/parseClassDeclarationInStrictModeByDefaultInES6.ts @@ -0,0 +1,9 @@ +// @target: es6 +class C { + interface = 10; + public implements() { } + public foo(arguments: any) { } + private bar(eval:any) { + arguments = "hello"; + } +} \ No newline at end of file diff --git a/tests/cases/unittests/incrementalParser.ts b/tests/cases/unittests/incrementalParser.ts index e785f1cdf00..87c0c3701f3 100644 --- a/tests/cases/unittests/incrementalParser.ts +++ b/tests/cases/unittests/incrementalParser.ts @@ -719,6 +719,16 @@ module m3 { }\ var oldText = ScriptSnapshot.fromString(source); var newTextAndChange = withChange(oldText, 0, "var v =".length, "class C"); + compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 0); // As specified in ES6 specification, all parts of a ClassDeclaration or a ClassExpression are strict mode code. + }); + + it('Moving methods from object literal to class in strict mode', () => { + debugger; + var source = "\"use strict\"; var v = { public A() { } public B() { } public C() { } }" + + var oldText = ScriptSnapshot.fromString(source); + var newTextAndChange = withChange(oldText, 14, "var v =".length, "class C"); + compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 4); }); @@ -728,6 +738,15 @@ module m3 { }\ var oldText = ScriptSnapshot.fromString(source); var newTextAndChange = withChange(oldText, 0, "class".length, "interface"); + compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 0); // As specified in ES6 specification, all parts of a ClassDeclaration or a ClassExpression are strict mode code. + }); + + it('Moving index signatures from class to interface in strict mode', () => { + var source = "\"use strict\"; class C { public [a: number]: string; public [a: number]: string; public [a: number]: string }" + + var oldText = ScriptSnapshot.fromString(source); + var newTextAndChange = withChange(oldText, 14, "class".length, "interface"); + compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 18); }); @@ -737,6 +756,16 @@ module m3 { }\ var oldText = ScriptSnapshot.fromString(source); var newTextAndChange = withChange(oldText, 0, "interface".length, "class"); + compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 0); // As specified in ES6 specification, all parts of a ClassDeclaration or a ClassExpression are strict mode code. + }); + + + it('Moving index signatures from interface to class in strict mode', () => { + var source = "\"use strict\"; interface C { public [a: number]: string; public [a: number]: string; public [a: number]: string }" + + var oldText = ScriptSnapshot.fromString(source); + var newTextAndChange = withChange(oldText, 14, "interface".length, "class"); + compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 18); }); @@ -755,6 +784,16 @@ module m3 { }\ var oldText = ScriptSnapshot.fromString(source); var newTextAndChange = withChange(oldText, 0, "var v =".length, "class C"); + compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 0); // As specified in ES6 specification, all parts of a ClassDeclaration or a ClassExpression are strict mode code. + }); + + + it('Moving accessors from object literal to class in strict mode', () => { + var source = "\"use strict\"; var v = { public get A() { } public get B() { } public get C() { } }" + + var oldText = ScriptSnapshot.fromString(source); + var newTextAndChange = withChange(oldText, 14, "var v =".length, "class C"); + compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 4); });