From ec9e46186969dafa7b05760ce89160b77e56b54b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 15 Jun 2015 17:31:15 -0700 Subject: [PATCH 1/5] Remove 'strict mode' from parsing and checking, and make it purely something purely checked at bind time. --- src/compiler/binder.ts | 217 +++++++++++++++++- src/compiler/checker.ts | 95 +------- src/compiler/parser.ts | 97 +++----- src/compiler/types.ts | 6 +- .../VariableDeclaration11_es6.errors.txt | 11 +- .../reference/VariableDeclaration11_es6.js | 2 +- .../YieldExpression18_es6.errors.txt | 12 +- .../reference/YieldExpression18_es6.js | 2 +- ...deleteOperatorInvalidOperations.errors.txt | 5 +- .../reference/downlevelLetConst11.errors.txt | 11 +- .../reference/downlevelLetConst11.js | 2 +- .../reference/letAsIdentifier.errors.txt | 15 ++ tests/baselines/reference/letAsIdentifier.js | 4 +- .../reference/letAsIdentifier.symbols | 17 -- .../baselines/reference/letAsIdentifier.types | 21 -- .../letAsIdentifierInStrictMode.errors.txt | 12 +- .../reference/letAsIdentifierInStrictMode.js | 3 +- tests/cases/unittests/incrementalParser.ts | 39 +--- 18 files changed, 316 insertions(+), 255 deletions(-) create mode 100644 tests/baselines/reference/letAsIdentifier.errors.txt delete mode 100644 tests/baselines/reference/letAsIdentifier.symbols delete mode 100644 tests/baselines/reference/letAsIdentifier.types diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 42bcd8ac61..9fee2b6f64 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -88,6 +88,8 @@ namespace ts { let container: Node; let blockScopeContainer: Node; let lastContainer: Node; + let inStrictMode = false; + let symbolCount = 0; let Symbol = objectAllocator.getSymbolConstructor(); let classifiableNames: Map = {}; @@ -531,6 +533,48 @@ namespace ts { typeLiteralSymbol.members = { [symbol.name]: symbol }; } + function bindObjectLiteralExpression(node: ObjectLiteralExpression) { + if (inStrictMode) { + let seen: Map = {}; + const Property = 1; + const NonProperty = 2; + + for (let prop of node.properties) { + if (prop.name.kind !== SyntaxKind.Identifier) { + continue; + } + + let identifier = prop.name; + + // ECMA-262 11.1.5 Object Initialiser + // If previous is not undefined then throw a SyntaxError exception if any of the following conditions are true + // a.This production is contained in strict code and IsDataDescriptor(previous) is true and + // IsDataDescriptor(propId.descriptor) is true. + // b.IsDataDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true. + // c.IsAccessorDescriptor(previous) is true and IsDataDescriptor(propId.descriptor) is true. + // d.IsAccessorDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true + // and either both previous and propId.descriptor have[[Get]] fields or both previous and propId.descriptor have[[Set]] fields + let currentKind = prop.kind === SyntaxKind.PropertyAssignment || prop.kind === SyntaxKind.ShorthandPropertyAssignment || prop.kind === SyntaxKind.MethodDeclaration + ? Property + : NonProperty; + + let existingKind = seen[identifier.text]; + if (!existingKind) { + seen[identifier.text] = currentKind; + continue; + } + + if (currentKind === Property && existingKind === Property) { + let span = getErrorSpanForNode(file, identifier); + file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, + Diagnostics.An_object_literal_cannot_have_multiple_properties_with_the_same_name_in_strict_mode)); + } + } + } + + return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, "__object"); + } + function bindAnonymousDeclaration(node: Declaration, symbolFlags: SymbolFlags, name: string) { let symbol = createSymbol(symbolFlags, name); addDeclarationToSymbol(symbol, node, symbolFlags); @@ -563,10 +607,11 @@ namespace ts { // The binder visits every node in the syntax tree so it is a convenient place to perform a single localized // check for reserved words used as identifiers in strict mode code. function checkStrictModeIdentifier(node: Identifier) { - if (node.parserContextFlags & ParserContextFlags.StrictMode && + if (inStrictMode && node.originalKeywordKind >= SyntaxKind.FirstFutureReservedWord && node.originalKeywordKind <= SyntaxKind.LastFutureReservedWord && !isIdentifierName(node)) { + // Report error only if there are no parse errors in file if (!file.parseDiagnostics.length) { let message = getAncestor(node, SyntaxKind.ClassDeclaration) || getAncestor(node, SyntaxKind.ClassExpression) ? @@ -577,6 +622,96 @@ namespace ts { } } + function checkStrictModeBinaryExpression(node: BinaryExpression) { + if (inStrictMode && isLeftHandSideExpression(node.left) && isAssignmentOperator(node.operatorToken.kind)) { + // ECMA 262 (Annex C) The identifier eval or arguments may not appear as the LeftHandSideExpression of an + // Assignment operator(11.13) or of a PostfixExpression(11.3) + checkGrammarEvalOrArgumentsInStrictMode(node, node.left); + } + } + + function checkStrictModeCatchClause(node: CatchClause) { + // It is a SyntaxError if a TryStatement with a Catch occurs within strict code and the Identifier of the + // Catch production is eval or arguments + if (inStrictMode && node.variableDeclaration) { + checkGrammarEvalOrArgumentsInStrictMode(node, node.variableDeclaration.name); + } + } + + function checkStrictModeDeleteExpression(node: DeleteExpression) { + // Grammar checking + if (inStrictMode && node.expression.kind === SyntaxKind.Identifier) { + // When a delete operator occurs within strict mode code, a SyntaxError is thrown if its + // UnaryExpression is a direct reference to a variable, function argument, or function name + let span = getErrorSpanForNode(file, node.expression); + file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, Diagnostics.delete_cannot_be_called_on_an_identifier_in_strict_mode)); + } + } + + function isEvalOrArgumentsIdentifier(node: Node): boolean { + return node.kind === SyntaxKind.Identifier && + ((node).text === "eval" || (node).text === "arguments"); + } + + function checkGrammarEvalOrArgumentsInStrictMode(contextNode: Node, name: Node) { + if (name && name.kind === SyntaxKind.Identifier) { + let identifier = name; + if (isEvalOrArgumentsIdentifier(identifier)) { + // We check first if the name is inside class declaration or class expression; if so give explicit message + // otherwise report generic error message. + let span = getErrorSpanForNode(file, name); + let message = getAncestor(identifier, SyntaxKind.ClassDeclaration) || getAncestor(identifier, SyntaxKind.ClassExpression) ? + Diagnostics.Invalid_use_of_0_Class_definitions_are_automatically_in_strict_mode : + Diagnostics.Invalid_use_of_0_in_strict_mode; + file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, message, identifier.text)); + } + } + } + + function checkStrictModeFunctionName(node: FunctionLikeDeclaration) { + if (inStrictMode) { + // It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a strict mode FunctionDeclaration or FunctionExpression (13.1)) + checkGrammarEvalOrArgumentsInStrictMode(node, node.name); + } + } + + function checkStrictModeNumericLiteral(node: LiteralExpression) { + if (inStrictMode && node.flags & NodeFlags.OctalLiteral) { + file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Octal_literals_are_not_allowed_in_strict_mode)); + } + } + + function checkStrictModePostfixUnaryExpression(node: PostfixUnaryExpression) { + // Grammar checking + // The identifier eval or arguments may not appear as the LeftHandSideExpression of an + // Assignment operator(11.13) or of a PostfixExpression(11.3) or as the UnaryExpression + // operated upon by a Prefix Increment(11.4.4) or a Prefix Decrement(11.4.5) operator. + if (inStrictMode) { + checkGrammarEvalOrArgumentsInStrictMode(node, node.operand); + } + } + + function checkStrictModePrefixUnaryExpression(node: PrefixUnaryExpression) { + // Grammar checking + if (inStrictMode) { + if (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) { + checkGrammarEvalOrArgumentsInStrictMode(node, node.operand); + } + } + } + + function checkStrictModeWithStatement(node: WithStatement) { + // Grammar checking for withStatement + if (inStrictMode) { + grammarErrorOnFirstToken(node, Diagnostics.with_statements_are_not_allowed_in_strict_mode); + } + } + + function grammarErrorOnFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any) { + let span = getSpanOfTokenAtPosition(file, node.pos); + file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, message, arg0, arg1, arg2)); + } + function getDestructuringParameterName(node: Declaration) { return "__" + indexOf((node.parent).parameters, node); } @@ -584,6 +719,11 @@ namespace ts { function bind(node: Node) { node.parent = parent; + var savedInStrictMode = inStrictMode; + if (!savedInStrictMode) { + updateStrictMode(node); + } + // First we bind declaration nodes to a symbol if possible. We'll both create a symbol // and then potentially add the symbol to an appropriate symbol table. Possible // destination symbol tables are: @@ -601,19 +741,76 @@ namespace ts { // the current 'container' node when it changes. This helps us know which symbol table // a local should go into for example. bindChildren(node); + + inStrictMode = savedInStrictMode; + } + + function updateStrictMode(node: Node) { + switch (node.kind) { + case SyntaxKind.SourceFile: + updateStrictModeStatementList((node).statements); + return; + case SyntaxKind.Block: + if (isFunctionLike(node.parent)) { + updateStrictModeStatementList((node).statements); + } + return; + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ClassExpression: + inStrictMode = true; + return; + } + } + + function updateStrictModeStatementList(statements: NodeArray) { + for (let statement of statements) { + if (!isPrologueDirective(statement)) { + return; + } + + if (isUseStrictPrologueDirective(statement)) { + inStrictMode = true; + return; + } + } } + /// Should be called only on prologue directives (isPrologueDirective(node) should be true) + function isUseStrictPrologueDirective(node: Node): boolean { + Debug.assert(isPrologueDirective(node)); + let nodeText = getTextOfNodeFromSourceText(file.text, (node).expression); + + // Note: the node text must be exactly "use strict" or 'use strict'. It is not ok for the + // string to contain unicode escapes (as per ES5). + return nodeText === '"use strict"' || nodeText === "'use strict'"; + } + function bindWorker(node: Node) { switch (node.kind) { case SyntaxKind.Identifier: return checkStrictModeIdentifier(node); + case SyntaxKind.BinaryExpression: + return checkStrictModeBinaryExpression(node); + case SyntaxKind.CatchClause: + return checkStrictModeCatchClause(node); + case SyntaxKind.DeleteExpression: + return checkStrictModeDeleteExpression(node); + case SyntaxKind.NumericLiteral: + return checkStrictModeNumericLiteral(node); + case SyntaxKind.PostfixUnaryExpression: + return checkStrictModePostfixUnaryExpression(node); + case SyntaxKind.PrefixUnaryExpression: + return checkStrictModePrefixUnaryExpression(node); + case SyntaxKind.WithStatement: + return checkStrictModeWithStatement(node); + case SyntaxKind.TypeParameter: return declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes); case SyntaxKind.Parameter: return bindParameter(node); - case SyntaxKind.VariableDeclaration: case SyntaxKind.BindingElement: - return bindVariableDeclarationOrBindingElement(node); + case SyntaxKind.VariableDeclaration: + return bindVariableDeclarationOrBindingElement(node); case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property | ((node).questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes); @@ -635,6 +832,7 @@ namespace ts { return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Method | ((node).questionToken ? SymbolFlags.Optional : SymbolFlags.None), isObjectLiteralMethod(node) ? SymbolFlags.PropertyExcludes : SymbolFlags.MethodExcludes); case SyntaxKind.FunctionDeclaration: + checkStrictModeFunctionName(node); return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); case SyntaxKind.Constructor: return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Constructor, /*symbolExcludes:*/ SymbolFlags.None); @@ -648,9 +846,10 @@ namespace ts { case SyntaxKind.TypeLiteral: return bindAnonymousDeclaration(node, SymbolFlags.TypeLiteral, "__type"); case SyntaxKind.ObjectLiteralExpression: - return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, "__object"); + return bindObjectLiteralExpression(node); case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: + checkStrictModeFunctionName(node); return bindAnonymousDeclaration(node, SymbolFlags.Function, "__function"); case SyntaxKind.ClassExpression: case SyntaxKind.ClassDeclaration: @@ -756,6 +955,10 @@ namespace ts { } function bindVariableDeclarationOrBindingElement(node: VariableDeclaration | BindingElement) { + if (inStrictMode) { + checkGrammarEvalOrArgumentsInStrictMode(node, node.name) + } + if (!isBindingPattern(node.name)) { if (isBlockOrCatchScoped(node)) { bindBlockScopedVariableDeclaration(node); @@ -779,6 +982,12 @@ namespace ts { } function bindParameter(node: ParameterDeclaration) { + if (inStrictMode) { + // It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a + // strict mode FunctionLikeDeclaration or FunctionExpression(13.1) + checkGrammarEvalOrArgumentsInStrictMode(node, node.name); + } + if (isBindingPattern(node.name)) { bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, getDestructuringParameterName(node)); } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 86639b9c75..e27e69bb99 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7734,7 +7734,7 @@ namespace ts { // Grammar checking let hasGrammarError = checkGrammarFunctionLikeDeclaration(node); if (!hasGrammarError && node.kind === SyntaxKind.FunctionExpression) { - checkGrammarFunctionName(node.name) || checkGrammarForGenerator(node); + checkGrammarForGenerator(node); } // The identityMapper object is used to indicate that function expressions are wildcards @@ -7891,14 +7891,7 @@ namespace ts { } function checkDeleteExpression(node: DeleteExpression): Type { - // Grammar checking - if (node.parserContextFlags & ParserContextFlags.StrictMode && node.expression.kind === SyntaxKind.Identifier) { - // When a delete operator occurs within strict mode code, a SyntaxError is thrown if its - // UnaryExpression is a direct reference to a variable, function argument, or function name - grammarErrorOnNode(node.expression, Diagnostics.delete_cannot_be_called_on_an_identifier_in_strict_mode); - } - - let operandType = checkExpression(node.expression); + checkExpression(node.expression); return booleanType; } @@ -7913,14 +7906,6 @@ namespace ts { } function checkPrefixUnaryExpression(node: PrefixUnaryExpression): Type { - // Grammar checking - // The identifier eval or arguments may not appear as the LeftHandSideExpression of an - // Assignment operator(11.13) or of a PostfixExpression(11.3) or as the UnaryExpression - // operated upon by a Prefix Increment(11.4.4) or a Prefix Decrement(11.4.5) operator - if ((node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken)) { - checkGrammarEvalOrArgumentsInStrictMode(node, node.operand); - } - let operandType = checkExpression(node.operand); switch (node.operator) { case SyntaxKind.PlusToken: @@ -7947,12 +7932,6 @@ namespace ts { } function checkPostfixUnaryExpression(node: PostfixUnaryExpression): Type { - // Grammar checking - // The identifier eval or arguments may not appear as the LeftHandSideExpression of an - // Assignment operator(11.13) or of a PostfixExpression(11.3) or as the UnaryExpression - // operated upon by a Prefix Increment(11.4.4) or a Prefix Decrement(11.4.5) operator. - checkGrammarEvalOrArgumentsInStrictMode(node, node.operand); - let operandType = checkExpression(node.operand); let ok = checkArithmeticOperandType(node.operand, operandType, Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_or_an_enum_type); if (ok) { @@ -8132,13 +8111,6 @@ namespace ts { } function checkBinaryExpression(node: BinaryExpression, contextualMapper?: TypeMapper) { - // Grammar checking - if (isLeftHandSideExpression(node.left) && isAssignmentOperator(node.operatorToken.kind)) { - // ECMA 262 (Annex C) The identifier eval or arguments may not appear as the LeftHandSideExpression of an - // Assignment operator(11.13) or of a PostfixExpression(11.3) - checkGrammarEvalOrArgumentsInStrictMode(node, node.left); - } - let operator = node.operatorToken.kind; if (operator === SyntaxKind.EqualsToken && (node.left.kind === SyntaxKind.ObjectLiteralExpression || node.left.kind === SyntaxKind.ArrayLiteralExpression)) { return checkDestructuringAssignment(node.left, checkExpression(node.right, contextualMapper), contextualMapper); @@ -8580,11 +8552,9 @@ namespace ts { // It is a SyntaxError if the Identifier "eval" or the Identifier "arguments" occurs as the // Identifier in a PropertySetParameterList of a PropertyAssignment that is contained in strict code // or if its FunctionBody is strict code(11.1.5). - // It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a - // strict mode FunctionLikeDeclaration or FunctionExpression(13.1) // Grammar checking - checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarEvalOrArgumentsInStrictMode(node, node.name); + checkGrammarDecorators(node) || checkGrammarModifiers(node); checkVariableLikeDeclaration(node); let func = getContainingFunction(node); @@ -9476,9 +9446,7 @@ namespace ts { function checkFunctionDeclaration(node: FunctionDeclaration): void { if (produceDiagnostics) { - checkFunctionLikeDeclaration(node) || - checkGrammarFunctionName(node.name) || - checkGrammarForGenerator(node); + checkFunctionLikeDeclaration(node) || checkGrammarForGenerator(node); checkCollisionWithCapturedSuperVariable(node, node.name); checkCollisionWithCapturedThisVariable(node, node.name); @@ -10305,12 +10273,7 @@ namespace ts { } function checkWithStatement(node: WithStatement) { - // Grammar checking for withStatement - if (!checkGrammarStatementInAmbientContext(node)) { - if (node.parserContextFlags & ParserContextFlags.StrictMode) { - grammarErrorOnFirstToken(node, Diagnostics.with_statements_are_not_allowed_in_strict_mode); - } - } + checkGrammarStatementInAmbientContext(node); checkExpression(node.expression); error(node.expression, Diagnostics.All_symbols_within_a_with_block_will_be_resolved_to_any); @@ -10414,10 +10377,6 @@ namespace ts { grammarErrorOnNode(localSymbol.valueDeclaration, Diagnostics.Cannot_redeclare_identifier_0_in_catch_clause, identifierName); } } - - // It is a SyntaxError if a TryStatement with a Catch occurs within strict code and the Identifier of the - // Catch production is eval or arguments - checkGrammarEvalOrArgumentsInStrictMode(node, catchClause.variableDeclaration.name); } } @@ -13022,11 +12981,6 @@ namespace ts { } } - function checkGrammarFunctionName(name: Node) { - // It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a strict mode FunctionDeclaration or FunctionExpression (13.1)) - return checkGrammarEvalOrArgumentsInStrictMode(name, name); - } - function checkGrammarForInvalidQuestionMark(node: Declaration, questionToken: Node, message: DiagnosticMessage): boolean { if (questionToken) { return grammarErrorOnNode(questionToken, message); @@ -13039,7 +12993,6 @@ namespace ts { let GetAccessor = 2; let SetAccesor = 4; let GetOrSetAccessor = GetAccessor | SetAccesor; - let inStrictMode = (node.parserContextFlags & ParserContextFlags.StrictMode) !== 0; for (let prop of node.properties) { let name = prop.name; @@ -13086,9 +13039,7 @@ namespace ts { else { let existingKind = seen[(name).text]; if (currentKind === Property && existingKind === Property) { - if (inStrictMode) { - grammarErrorOnNode(name, Diagnostics.An_object_literal_cannot_have_multiple_properties_with_the_same_name_in_strict_mode); - } + continue; } else if ((currentKind & GetOrSetAccessor) && (existingKind & GetOrSetAccessor)) { if (existingKind !== GetOrSetAccessor && currentKind !== existingKind) { @@ -13311,9 +13262,6 @@ namespace ts { return grammarErrorAtPos(getSourceFileOfNode(node), node.initializer.pos - 1, 1, Diagnostics.A_rest_element_cannot_have_an_initializer); } } - // It is a SyntaxError if a VariableDeclaration or VariableDeclarationNoIn occurs within strict code - // and its Identifier is eval or arguments - return checkGrammarEvalOrArgumentsInStrictMode(node, node.name); } function checkGrammarVariableDeclaration(node: VariableDeclaration) { @@ -13345,8 +13293,7 @@ namespace ts { // It is a SyntaxError if a VariableDeclaration or VariableDeclarationNoIn occurs within strict code // and its Identifier is eval or arguments - return (checkLetConstNames && checkGrammarNameInLetOrConstDeclarations(node.name)) || - checkGrammarEvalOrArgumentsInStrictMode(node, node.name); + return checkLetConstNames && checkGrammarNameInLetOrConstDeclarations(node.name); } function checkGrammarNameInLetOrConstDeclarations(name: Identifier | BindingPattern): boolean { @@ -13485,25 +13432,6 @@ namespace ts { } } - function checkGrammarEvalOrArgumentsInStrictMode(contextNode: Node, name: Node): boolean { - if (name && name.kind === SyntaxKind.Identifier) { - let identifier = name; - if (contextNode && (contextNode.parserContextFlags & ParserContextFlags.StrictMode) && isEvalOrArgumentsIdentifier(identifier)) { - // We check first if the name is inside class declaration or class expression; if so give explicit message - // otherwise report generic error message. - let message = getAncestor(identifier, SyntaxKind.ClassDeclaration) || getAncestor(identifier, SyntaxKind.ClassExpression) ? - Diagnostics.Invalid_use_of_0_Class_definitions_are_automatically_in_strict_mode : - Diagnostics.Invalid_use_of_0_in_strict_mode; - return grammarErrorOnNode(identifier, message, declarationNameToString(identifier)); - } - } - } - - function isEvalOrArgumentsIdentifier(node: Node): boolean { - return node.kind === SyntaxKind.Identifier && - ((node).text === "eval" || (node).text === "arguments"); - } - function checkGrammarConstructorTypeParameters(node: ConstructorDeclaration) { if (node.typeParameters) { return grammarErrorAtPos(getSourceFileOfNode(node), node.typeParameters.pos, node.typeParameters.end - node.typeParameters.pos, Diagnostics.Type_parameters_cannot_appear_on_a_constructor_declaration); @@ -13613,13 +13541,8 @@ namespace ts { function checkGrammarNumericLiteral(node: Identifier): boolean { // Grammar checking - if (node.flags & NodeFlags.OctalLiteral) { - if (node.parserContextFlags & ParserContextFlags.StrictMode) { - return grammarErrorOnNode(node, Diagnostics.Octal_literals_are_not_allowed_in_strict_mode); - } - else if (languageVersion >= ScriptTarget.ES5) { - return grammarErrorOnNode(node, Diagnostics.Octal_literals_are_not_available_when_targeting_ECMAScript_5_and_higher); - } + if (node.flags & NodeFlags.OctalLiteral && languageVersion >= ScriptTarget.ES5) { + return grammarErrorOnNode(node, Diagnostics.Octal_literals_are_not_available_when_targeting_ECMAScript_5_and_higher); } } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index f964febaf5..a507125d4b 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -544,7 +544,7 @@ namespace ts { token = nextToken(); processReferenceComments(sourceFile); - sourceFile.statements = parseList(ParsingContext.SourceElements, /*checkForStrictMode*/ true, parseStatement); + sourceFile.statements = parseList(ParsingContext.SourceElements, parseStatement); Debug.assert(token === SyntaxKind.EndOfFileToken); sourceFile.endOfFileToken = parseTokenNode(); @@ -647,10 +647,6 @@ namespace ts { } } - function setStrictModeContext(val: boolean) { - setContextFlag(val, ParserContextFlags.StrictMode); - } - function setDisallowInContext(val: boolean) { setContextFlag(val, ParserContextFlags.DisallowIn); } @@ -744,10 +740,6 @@ namespace ts { return (contextFlags & ParserContextFlags.Yield) !== 0; } - function inStrictModeContext() { - return (contextFlags & ParserContextFlags.StrictMode) !== 0; - } - function inGeneratorParameterContext() { return (contextFlags & ParserContextFlags.GeneratorParameter) !== 0; } @@ -1323,31 +1315,17 @@ namespace ts { } // Parses a list of elements - function parseList(kind: ParsingContext, checkForStrictMode: boolean, parseElement: () => T): NodeArray { + function parseList(kind: ParsingContext, parseElement: () => T): NodeArray { let saveParsingContext = parsingContext; parsingContext |= 1 << kind; let result = >[]; result.pos = getNodePos(); - let savedStrictModeContext = inStrictModeContext(); while (!isListTerminator(kind)) { if (isListElement(kind, /* inErrorRecovery */ false)) { let element = parseListElement(kind, parseElement); result.push(element); - // test elements only if we are not already in strict mode - if (checkForStrictMode && !inStrictModeContext()) { - if (isPrologueDirective(element)) { - if (isUseStrictPrologueDirective(element)) { - setStrictModeContext(true); - checkForStrictMode = false; - } - } - else { - checkForStrictMode = false; - } - } - continue; } @@ -1356,22 +1334,11 @@ namespace ts { } } - setStrictModeContext(savedStrictModeContext); result.end = getNodeEnd(); parsingContext = saveParsingContext; return result; } - /// Should be called only on prologue directives (isPrologueDirective(node) should be true) - function isUseStrictPrologueDirective(node: Node): boolean { - Debug.assert(isPrologueDirective(node)); - let nodeText = getTextOfNodeFromSourceText(sourceText, (node).expression); - - // Note: the node text must be exactly "use strict" or 'use strict'. It is not ok for the - // string to contain unicode escapes (as per ES5). - return nodeText === '"use strict"' || nodeText === "'use strict'"; - } - function parseListElement(parsingContext: ParsingContext, parseElement: () => T): T { let node = currentNode(parsingContext); if (node) { @@ -2276,7 +2243,7 @@ namespace ts { function parseObjectTypeMembers(): NodeArray { let members: NodeArray; if (parseExpected(SyntaxKind.OpenBraceToken)) { - members = parseList(ParsingContext.TypeMembers, /*checkForStrictMode*/ false, parseTypeMember); + members = parseList(ParsingContext.TypeMembers, parseTypeMember); parseExpected(SyntaxKind.CloseBraceToken); } else { @@ -2645,12 +2612,6 @@ namespace ts { return true; } - if (inStrictModeContext()) { - // If we're in strict mode, then 'yield' is a keyword, could only ever start - // a yield expression. - return true; - } - // We're in a context where 'yield expr' is not allowed. However, if we can // definitely tell that the user was trying to parse a 'yield expr' and not // just a normal expr that start with a 'yield' identifier, then parse out @@ -2665,7 +2626,7 @@ namespace ts { // for now we just check if the next token is an identifier. More heuristics // can be added here later as necessary. We just need to make sure that we // don't accidently consume something legal. - return lookAhead(nextTokenIsIdentifierOnSameLine); + return lookAhead(nextTokenIsIdentifierOrKeywordOrNumberOnSameLine); } return false; @@ -3508,10 +3469,10 @@ namespace ts { } // STATEMENTS - function parseBlock(ignoreMissingOpenBrace: boolean, checkForStrictMode: boolean, diagnosticMessage?: DiagnosticMessage): Block { + function parseBlock(ignoreMissingOpenBrace: boolean, diagnosticMessage?: DiagnosticMessage): Block { let node = createNode(SyntaxKind.Block); if (parseExpected(SyntaxKind.OpenBraceToken, diagnosticMessage) || ignoreMissingOpenBrace) { - node.statements = parseList(ParsingContext.BlockStatements, checkForStrictMode, parseStatement); + node.statements = parseList(ParsingContext.BlockStatements, parseStatement); parseExpected(SyntaxKind.CloseBraceToken); } else { @@ -3531,7 +3492,7 @@ namespace ts { setDecoratorContext(false); } - let block = parseBlock(ignoreMissingOpenBrace, /*checkForStrictMode*/ true, diagnosticMessage); + let block = parseBlock(ignoreMissingOpenBrace, diagnosticMessage); if (saveDecoratorContext) { setDecoratorContext(true); @@ -3673,7 +3634,7 @@ namespace ts { parseExpected(SyntaxKind.CaseKeyword); node.expression = allowInAnd(parseExpression); parseExpected(SyntaxKind.ColonToken); - node.statements = parseList(ParsingContext.SwitchClauseStatements, /*checkForStrictMode*/ false, parseStatement); + node.statements = parseList(ParsingContext.SwitchClauseStatements, parseStatement); return finishNode(node); } @@ -3681,7 +3642,7 @@ namespace ts { let node = createNode(SyntaxKind.DefaultClause); parseExpected(SyntaxKind.DefaultKeyword); parseExpected(SyntaxKind.ColonToken); - node.statements = parseList(ParsingContext.SwitchClauseStatements, /*checkForStrictMode*/ false, parseStatement); + node.statements = parseList(ParsingContext.SwitchClauseStatements, parseStatement); return finishNode(node); } @@ -3697,7 +3658,7 @@ namespace ts { parseExpected(SyntaxKind.CloseParenToken); let caseBlock = createNode(SyntaxKind.CaseBlock, scanner.getStartPos()); parseExpected(SyntaxKind.OpenBraceToken); - caseBlock.clauses = parseList(ParsingContext.SwitchClauses, /*checkForStrictMode*/ false, parseCaseOrDefaultClause); + caseBlock.clauses = parseList(ParsingContext.SwitchClauses, parseCaseOrDefaultClause); parseExpected(SyntaxKind.CloseBraceToken); node.caseBlock = finishNode(caseBlock); return finishNode(node); @@ -3724,14 +3685,14 @@ namespace ts { let node = createNode(SyntaxKind.TryStatement); parseExpected(SyntaxKind.TryKeyword); - node.tryBlock = parseBlock(/*ignoreMissingOpenBrace*/ false, /*checkForStrictMode*/ false); + node.tryBlock = parseBlock(/*ignoreMissingOpenBrace*/ false); node.catchClause = token === SyntaxKind.CatchKeyword ? parseCatchClause() : undefined; // If we don't have a catch clause, then we must have a finally clause. Try to parse // one out no matter what. if (!node.catchClause || token === SyntaxKind.FinallyKeyword) { parseExpected(SyntaxKind.FinallyKeyword); - node.finallyBlock = parseBlock(/*ignoreMissingOpenBrace*/ false, /*checkForStrictMode*/ false); + node.finallyBlock = parseBlock(/*ignoreMissingOpenBrace*/ false); } return finishNode(node); @@ -3745,7 +3706,7 @@ namespace ts { } parseExpected(SyntaxKind.CloseParenToken); - result.block = parseBlock(/*ignoreMissingOpenBrace*/ false, /*checkForStrictMode*/ false); + result.block = parseBlock(/*ignoreMissingOpenBrace*/ false); return finishNode(result); } @@ -3786,6 +3747,11 @@ namespace ts { return isIdentifierOrKeyword() && !scanner.hasPrecedingLineBreak(); } + function nextTokenIsIdentifierOrKeywordOrNumberOnSameLine() { + nextToken(); + return (isIdentifierOrKeyword() || token === SyntaxKind.NumericLiteral) && !scanner.hasPrecedingLineBreak(); + } + function isDeclaration(): boolean { while (true) { switch (token) { @@ -3913,16 +3879,15 @@ namespace ts { } } - function nextTokenIsIdentifierOrStartOfDestructuringOnTheSameLine() { + function nextTokenIsIdentifierOrStartOfDestructuring() { nextToken(); - return !scanner.hasPrecedingLineBreak() && - (isIdentifier() || token === SyntaxKind.OpenBraceToken || token === SyntaxKind.OpenBracketToken); + return isIdentifier() || token === SyntaxKind.OpenBraceToken || token === SyntaxKind.OpenBracketToken; } function isLetDeclaration() { - // It is let declaration if in strict mode or next token is identifier\open bracket\open curly on same line. - // otherwise it needs to be treated like identifier - return inStrictModeContext() || lookAhead(nextTokenIsIdentifierOrStartOfDestructuringOnTheSameLine); + // In ES6 'let' always starts a lexical declaration if followed by an identifier or { + // or [. + return lookAhead(nextTokenIsIdentifierOrStartOfDestructuring); } function parseStatement(): Statement { @@ -3930,7 +3895,7 @@ namespace ts { case SyntaxKind.SemicolonToken: return parseEmptyStatement(); case SyntaxKind.OpenBraceToken: - return parseBlock(/*ignoreMissingOpenBrace*/ false, /*checkForStrictMode*/ false); + return parseBlock(/*ignoreMissingOpenBrace*/ false); case SyntaxKind.VarKeyword: return parseVariableStatement(scanner.getStartPos(), /*decorators*/ undefined, /*modifiers*/ undefined); case SyntaxKind.LetKeyword: @@ -4448,10 +4413,6 @@ namespace ts { } function parseClassDeclarationOrExpression(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray, kind: SyntaxKind): ClassLikeDeclaration { - // In ES6 specification, All parts of a ClassDeclaration or a ClassExpression are strict mode code - let savedStrictModeContext = inStrictModeContext(); - setStrictModeContext(true); - var node = createNode(kind, fullStart); node.decorators = decorators; setModifiers(node, modifiers); @@ -4474,9 +4435,7 @@ namespace ts { node.members = createMissingList(); } - var finishedNode = finishNode(node); - setStrictModeContext(savedStrictModeContext); - return finishedNode; + return finishNode(node); } function parseHeritageClauses(isClassHeritageClause: boolean): NodeArray { @@ -4494,7 +4453,7 @@ namespace ts { } function parseHeritageClausesWorker() { - return parseList(ParsingContext.HeritageClauses, /*checkForStrictMode*/ false, parseHeritageClause); + return parseList(ParsingContext.HeritageClauses, parseHeritageClause); } function parseHeritageClause() { @@ -4524,7 +4483,7 @@ namespace ts { } function parseClassMembers() { - return parseList(ParsingContext.ClassMembers, /*checkForStrictMode*/ false, parseClassElement); + return parseList(ParsingContext.ClassMembers, parseClassElement); } function parseInterfaceDeclaration(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray): InterfaceDeclaration { @@ -4582,7 +4541,7 @@ namespace ts { function parseModuleBlock(): ModuleBlock { let node = createNode(SyntaxKind.ModuleBlock, scanner.getStartPos()); if (parseExpected(SyntaxKind.OpenBraceToken)) { - node.statements = parseList(ParsingContext.BlockStatements, /*checkForStrictMode*/ false, parseStatement); + node.statements = parseList(ParsingContext.BlockStatements, parseStatement); parseExpected(SyntaxKind.CloseBraceToken); } else { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 9c0fd3d0b2..8e27210214 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -362,10 +362,6 @@ namespace ts { export const enum ParserContextFlags { None = 0, - // Set if this node was parsed in strict mode. Used for grammar error checks, as well as - // checking if the node can be reused in incremental settings. - StrictMode = 1 << 0, - // If this node was parsed in a context where 'in-expressions' are not allowed. DisallowIn = 1 << 1, @@ -388,7 +384,7 @@ namespace ts { JavaScriptFile = 1 << 6, // Context flags set directly by the parser. - ParserGeneratedFlags = StrictMode | DisallowIn | Yield | GeneratorParameter | Decorator | ThisNodeHasError, + ParserGeneratedFlags = DisallowIn | Yield | GeneratorParameter | Decorator | ThisNodeHasError, // Context flags computed by aggregating child flags upwards. diff --git a/tests/baselines/reference/VariableDeclaration11_es6.errors.txt b/tests/baselines/reference/VariableDeclaration11_es6.errors.txt index 8c0ef5f869..d1e6ab7c15 100644 --- a/tests/baselines/reference/VariableDeclaration11_es6.errors.txt +++ b/tests/baselines/reference/VariableDeclaration11_es6.errors.txt @@ -1,8 +1,11 @@ -tests/cases/conformance/es6/variableDeclarations/VariableDeclaration11_es6.ts(2,4): error TS1123: Variable declaration list cannot be empty. +tests/cases/conformance/es6/variableDeclarations/VariableDeclaration11_es6.ts(2,1): error TS1212: Identifier expected. 'let' is a reserved word in strict mode +tests/cases/conformance/es6/variableDeclarations/VariableDeclaration11_es6.ts(2,1): error TS2304: Cannot find name 'let'. -==== tests/cases/conformance/es6/variableDeclarations/VariableDeclaration11_es6.ts (1 errors) ==== +==== tests/cases/conformance/es6/variableDeclarations/VariableDeclaration11_es6.ts (2 errors) ==== "use strict"; let - -!!! error TS1123: Variable declaration list cannot be empty. \ No newline at end of file + ~~~ +!!! error TS1212: Identifier expected. 'let' is a reserved word in strict mode + ~~~ +!!! error TS2304: Cannot find name 'let'. \ No newline at end of file diff --git a/tests/baselines/reference/VariableDeclaration11_es6.js b/tests/baselines/reference/VariableDeclaration11_es6.js index 83eec6d9a1..cf4c14617b 100644 --- a/tests/baselines/reference/VariableDeclaration11_es6.js +++ b/tests/baselines/reference/VariableDeclaration11_es6.js @@ -4,4 +4,4 @@ let //// [VariableDeclaration11_es6.js] "use strict"; -let ; +let; diff --git a/tests/baselines/reference/YieldExpression18_es6.errors.txt b/tests/baselines/reference/YieldExpression18_es6.errors.txt index 056fc0e950..5dd2807716 100644 --- a/tests/baselines/reference/YieldExpression18_es6.errors.txt +++ b/tests/baselines/reference/YieldExpression18_es6.errors.txt @@ -1,8 +1,14 @@ -tests/cases/conformance/es6/yieldExpressions/YieldExpression18_es6.ts(2,1): error TS1163: A 'yield' expression is only allowed in a generator body. +tests/cases/conformance/es6/yieldExpressions/YieldExpression18_es6.ts(2,1): error TS1212: Identifier expected. 'yield' is a reserved word in strict mode +tests/cases/conformance/es6/yieldExpressions/YieldExpression18_es6.ts(2,1): error TS2304: Cannot find name 'yield'. +tests/cases/conformance/es6/yieldExpressions/YieldExpression18_es6.ts(2,7): error TS2304: Cannot find name 'foo'. -==== tests/cases/conformance/es6/yieldExpressions/YieldExpression18_es6.ts (1 errors) ==== +==== tests/cases/conformance/es6/yieldExpressions/YieldExpression18_es6.ts (3 errors) ==== "use strict"; yield(foo); ~~~~~ -!!! error TS1163: A 'yield' expression is only allowed in a generator body. \ No newline at end of file +!!! error TS1212: Identifier expected. 'yield' is a reserved word in strict mode + ~~~~~ +!!! error TS2304: Cannot find name 'yield'. + ~~~ +!!! error TS2304: Cannot find name 'foo'. \ No newline at end of file diff --git a/tests/baselines/reference/YieldExpression18_es6.js b/tests/baselines/reference/YieldExpression18_es6.js index b4ba37d3ac..36be3faabf 100644 --- a/tests/baselines/reference/YieldExpression18_es6.js +++ b/tests/baselines/reference/YieldExpression18_es6.js @@ -4,4 +4,4 @@ yield(foo); //// [YieldExpression18_es6.js] "use strict"; -yield (foo); +yield(foo); diff --git a/tests/baselines/reference/deleteOperatorInvalidOperations.errors.txt b/tests/baselines/reference/deleteOperatorInvalidOperations.errors.txt index 5f7c6d7045..d50be713d0 100644 --- a/tests/baselines/reference/deleteOperatorInvalidOperations.errors.txt +++ b/tests/baselines/reference/deleteOperatorInvalidOperations.errors.txt @@ -1,9 +1,10 @@ tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorInvalidOperations.ts(5,20): error TS1005: ',' expected. tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorInvalidOperations.ts(5,27): error TS1109: Expression expected. tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorInvalidOperations.ts(8,23): error TS1109: Expression expected. +tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorInvalidOperations.ts(13,16): error TS1102: 'delete' cannot be called on an identifier in strict mode. -==== tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorInvalidOperations.ts (3 errors) ==== +==== tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorInvalidOperations.ts (4 errors) ==== // Unary operator delete var ANY; @@ -23,5 +24,7 @@ tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperator class testADelx { constructor(public s: () => {}) { delete s; //expect error + ~ +!!! error TS1102: 'delete' cannot be called on an identifier in strict mode. } } \ No newline at end of file diff --git a/tests/baselines/reference/downlevelLetConst11.errors.txt b/tests/baselines/reference/downlevelLetConst11.errors.txt index 42449bd3c8..29932f55c1 100644 --- a/tests/baselines/reference/downlevelLetConst11.errors.txt +++ b/tests/baselines/reference/downlevelLetConst11.errors.txt @@ -1,8 +1,11 @@ -tests/cases/compiler/downlevelLetConst11.ts(2,4): error TS1123: Variable declaration list cannot be empty. +tests/cases/compiler/downlevelLetConst11.ts(2,1): error TS1212: Identifier expected. 'let' is a reserved word in strict mode +tests/cases/compiler/downlevelLetConst11.ts(2,1): error TS2304: Cannot find name 'let'. -==== tests/cases/compiler/downlevelLetConst11.ts (1 errors) ==== +==== tests/cases/compiler/downlevelLetConst11.ts (2 errors) ==== "use strict"; let - -!!! error TS1123: Variable declaration list cannot be empty. \ No newline at end of file + ~~~ +!!! error TS1212: Identifier expected. 'let' is a reserved word in strict mode + ~~~ +!!! error TS2304: Cannot find name 'let'. \ No newline at end of file diff --git a/tests/baselines/reference/downlevelLetConst11.js b/tests/baselines/reference/downlevelLetConst11.js index 377c7e6a9e..e9749923d0 100644 --- a/tests/baselines/reference/downlevelLetConst11.js +++ b/tests/baselines/reference/downlevelLetConst11.js @@ -4,4 +4,4 @@ let //// [downlevelLetConst11.js] "use strict"; -var ; +let; diff --git a/tests/baselines/reference/letAsIdentifier.errors.txt b/tests/baselines/reference/letAsIdentifier.errors.txt new file mode 100644 index 0000000000..b4e4db2285 --- /dev/null +++ b/tests/baselines/reference/letAsIdentifier.errors.txt @@ -0,0 +1,15 @@ +tests/cases/compiler/letAsIdentifier.ts(3,5): error TS2300: Duplicate identifier 'a'. +tests/cases/compiler/letAsIdentifier.ts(6,1): error TS2300: Duplicate identifier 'a'. + + +==== tests/cases/compiler/letAsIdentifier.ts (2 errors) ==== + + var let = 10; + var a = 10; + ~ +!!! error TS2300: Duplicate identifier 'a'. + let = 30; + let + a; + ~ +!!! error TS2300: Duplicate identifier 'a'. \ No newline at end of file diff --git a/tests/baselines/reference/letAsIdentifier.js b/tests/baselines/reference/letAsIdentifier.js index ae45b4b491..05811ce108 100644 --- a/tests/baselines/reference/letAsIdentifier.js +++ b/tests/baselines/reference/letAsIdentifier.js @@ -10,10 +10,10 @@ a; var let = 10; var a = 10; let = 30; -let; -a; +var a; //// [letAsIdentifier.d.ts] declare var let: number; declare var a: number; +declare let a: any; diff --git a/tests/baselines/reference/letAsIdentifier.symbols b/tests/baselines/reference/letAsIdentifier.symbols deleted file mode 100644 index be5066f5ac..0000000000 --- a/tests/baselines/reference/letAsIdentifier.symbols +++ /dev/null @@ -1,17 +0,0 @@ -=== tests/cases/compiler/letAsIdentifier.ts === - -var let = 10; ->let : Symbol(let, Decl(letAsIdentifier.ts, 1, 3)) - -var a = 10; ->a : Symbol(a, Decl(letAsIdentifier.ts, 2, 3)) - -let = 30; ->let : Symbol(let, Decl(letAsIdentifier.ts, 1, 3)) - -let ->let : Symbol(let, Decl(letAsIdentifier.ts, 1, 3)) - -a; ->a : Symbol(a, Decl(letAsIdentifier.ts, 2, 3)) - diff --git a/tests/baselines/reference/letAsIdentifier.types b/tests/baselines/reference/letAsIdentifier.types deleted file mode 100644 index 36c190a92e..0000000000 --- a/tests/baselines/reference/letAsIdentifier.types +++ /dev/null @@ -1,21 +0,0 @@ -=== tests/cases/compiler/letAsIdentifier.ts === - -var let = 10; ->let : number ->10 : number - -var a = 10; ->a : number ->10 : number - -let = 30; ->let = 30 : number ->let : number ->30 : number - -let ->let : number - -a; ->a : number - diff --git a/tests/baselines/reference/letAsIdentifierInStrictMode.errors.txt b/tests/baselines/reference/letAsIdentifierInStrictMode.errors.txt index b59daca008..42f71669c6 100644 --- a/tests/baselines/reference/letAsIdentifierInStrictMode.errors.txt +++ b/tests/baselines/reference/letAsIdentifierInStrictMode.errors.txt @@ -1,20 +1,20 @@ +tests/cases/compiler/letAsIdentifierInStrictMode.ts(2,5): error TS1212: Identifier expected. 'let' is a reserved word in strict mode tests/cases/compiler/letAsIdentifierInStrictMode.ts(3,5): error TS2300: Duplicate identifier 'a'. -tests/cases/compiler/letAsIdentifierInStrictMode.ts(4,5): error TS1134: Variable declaration expected. -tests/cases/compiler/letAsIdentifierInStrictMode.ts(4,7): error TS1134: Variable declaration expected. +tests/cases/compiler/letAsIdentifierInStrictMode.ts(4,1): error TS1212: Identifier expected. 'let' is a reserved word in strict mode tests/cases/compiler/letAsIdentifierInStrictMode.ts(6,1): error TS2300: Duplicate identifier 'a'. ==== tests/cases/compiler/letAsIdentifierInStrictMode.ts (4 errors) ==== "use strict"; var let = 10; + ~~~ +!!! error TS1212: Identifier expected. 'let' is a reserved word in strict mode var a = 10; ~ !!! error TS2300: Duplicate identifier 'a'. let = 30; - ~ -!!! error TS1134: Variable declaration expected. - ~~ -!!! error TS1134: Variable declaration expected. + ~~~ +!!! error TS1212: Identifier expected. 'let' is a reserved word in strict mode let a; ~ diff --git a/tests/baselines/reference/letAsIdentifierInStrictMode.js b/tests/baselines/reference/letAsIdentifierInStrictMode.js index eb840e1a64..cce0b13cf6 100644 --- a/tests/baselines/reference/letAsIdentifierInStrictMode.js +++ b/tests/baselines/reference/letAsIdentifierInStrictMode.js @@ -10,6 +10,5 @@ a; "use strict"; var let = 10; var a = 10; -var ; -30; +let = 30; var a; diff --git a/tests/cases/unittests/incrementalParser.ts b/tests/cases/unittests/incrementalParser.ts index 93d99615c9..a0b361a87f 100644 --- a/tests/cases/unittests/incrementalParser.ts +++ b/tests/cases/unittests/incrementalParser.ts @@ -252,11 +252,6 @@ module ts { }); it('Strict mode 1',() => { - // In non-strict mode 'package' means nothing and can be reused. In strict mode though - // we'll have to reparse the nodes (and generate an error for 'package();' - // - // Note: in this test we don't actually add 'use strict'. This is so we can compare - // reuse with/without a strict mode change. var source = "foo1();\r\nfoo1();\r\nfoo1();\r\package();"; var oldText = ScriptSnapshot.fromString(source); @@ -266,23 +261,15 @@ module ts { }); it('Strict mode 2',() => { - // In non-strict mode 'package' means nothing and can be reused. In strict mode though - // we'll have to reparse the nodes (and generate an error for 'package();' var source = "foo1();\r\nfoo1();\r\nfoo1();\r\package();"; var oldText = ScriptSnapshot.fromString(source); var newTextAndChange = withInsert(oldText, 0, "'use strict';\r\n"); - // Note the decreased reuse of nodes compared to 'Strict mode 1' - compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 0); + compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 9); }); it('Strict mode 3',() => { - // In non-strict mode 'package' means nothing and can be reused. In strict mode though - // we'll have to reparse the nodes (and generate an error for 'package();' - // - // Note: in this test we don't actually remove 'use strict'. This is so we can compare - // reuse with/without a strict mode change. var source = "'strict';\r\nfoo1();\r\nfoo1();\r\nfoo1();\r\npackage();"; var index = source.indexOf('f'); @@ -293,16 +280,13 @@ module ts { }); it('Strict mode 4',() => { - // In non-strict mode 'package' means nothing and can be reused. In strict mode though - // we'll have to reparse the nodes (and generate an error for 'package();' var source = "'use strict';\r\nfoo1();\r\nfoo1();\r\nfoo1();\r\npackage();"; var index = source.indexOf('f'); var oldText = ScriptSnapshot.fromString(source); var newTextAndChange = withDelete(oldText, 0, index); - // Note the decreased reuse of nodes compared to testStrictMode3 - compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 0); + compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 9); }); it('Strict mode 5',() => { @@ -312,7 +296,7 @@ module ts { var oldText = ScriptSnapshot.fromString(source); var newTextAndChange = withChange(oldText, index, 6, "strict"); - compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 0); + compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 27); }); it('Strict mode 6',() => { @@ -322,7 +306,7 @@ module ts { var oldText = ScriptSnapshot.fromString(source); var newTextAndChange = withChange(oldText, index, 6, "blahhh"); - compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 0); + compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 27); }); it('Strict mode 7',() => { @@ -492,7 +476,6 @@ module ts { var oldText = ScriptSnapshot.fromString(source); var newTextAndChange = withChange(oldText, index, ": Foo { @@ -555,7 +538,7 @@ module ts { var index = source.length; var newTextAndChange = withInsert(oldText, index, "var x;"); - compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 0); + compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 2); }); it('Delete parameter after comment',() => { @@ -595,7 +578,7 @@ constructor(name) { }\ var index = source.indexOf("100"); var newTextAndChange = withInsert(oldText, index, "'1', "); - compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 5); + compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 7); }); it('Insert declare modifier before module',() => { @@ -718,7 +701,7 @@ 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. + compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 4); }); it('Moving methods from object literal to class in strict mode', () => { @@ -736,7 +719,7 @@ 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. + compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 18); }); it('Moving index signatures from class to interface in strict mode', () => { @@ -754,7 +737,7 @@ 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. + compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 18); }); @@ -782,7 +765,7 @@ 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. + compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 4); }); From 417cfa5c2cdf1440033ae307527e62c49024ed06 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 15 Jun 2015 17:48:15 -0700 Subject: [PATCH 2/5] Check namespaces for strict mode. --- src/compiler/binder.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 9fee2b6f64..2f4c219437 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -748,7 +748,8 @@ namespace ts { function updateStrictMode(node: Node) { switch (node.kind) { case SyntaxKind.SourceFile: - updateStrictModeStatementList((node).statements); + case SyntaxKind.ModuleBlock: + updateStrictModeStatementList((node).statements); return; case SyntaxKind.Block: if (isFunctionLike(node.parent)) { @@ -757,6 +758,7 @@ namespace ts { return; case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: + // All classes are automatically in strict mode in ES6. inStrictMode = true; return; } From dcbfa6988a8da79558dbbcaac1a3428add22b26c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 15 Jun 2015 18:01:18 -0700 Subject: [PATCH 3/5] Modules are always in strict mode in ES6. --- src/compiler/binder.ts | 6 +++- ...xportNonInitializedVariablesAMD.errors.txt | 5 ++- ...NonInitializedVariablesCommonJS.errors.txt | 5 ++- ...xportNonInitializedVariablesES6.errors.txt | 5 ++- ...rtNonInitializedVariablesSystem.errors.txt | 5 ++- ...xportNonInitializedVariablesUMD.errors.txt | 5 ++- ...rtsWithContextualKeywordNames01.errors.txt | 23 +++++++++++++ ...mportsWithContextualKeywordNames01.symbols | 30 ----------------- ...dImportsWithContextualKeywordNames01.types | 32 ------------------- 9 files changed, 48 insertions(+), 68 deletions(-) create mode 100644 tests/baselines/reference/exportsAndImportsWithContextualKeywordNames01.errors.txt delete mode 100644 tests/baselines/reference/exportsAndImportsWithContextualKeywordNames01.symbols delete mode 100644 tests/baselines/reference/exportsAndImportsWithContextualKeywordNames01.types diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 2f4c219437..5437615978 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -88,7 +88,11 @@ namespace ts { let container: Node; let blockScopeContainer: Node; let lastContainer: Node; - let inStrictMode = false; + + // If this file is an external module, then it is automatically in strict-mode according to + // ES6. If it is not an external module, then we'll determine if it is in strict mode or + // not depending on if we see "use strict" in certain places. + let inStrictMode = !!file.externalModuleIndicator; let symbolCount = 0; let Symbol = objectAllocator.getSymbolConstructor(); diff --git a/tests/baselines/reference/exportNonInitializedVariablesAMD.errors.txt b/tests/baselines/reference/exportNonInitializedVariablesAMD.errors.txt index ae79675d8d..27679cf607 100644 --- a/tests/baselines/reference/exportNonInitializedVariablesAMD.errors.txt +++ b/tests/baselines/reference/exportNonInitializedVariablesAMD.errors.txt @@ -1,15 +1,18 @@ tests/cases/conformance/externalModules/exportNonInitializedVariablesAMD.ts(2,4): error TS1123: Variable declaration list cannot be empty. +tests/cases/conformance/externalModules/exportNonInitializedVariablesAMD.ts(3,1): error TS1212: Identifier expected. 'let' is a reserved word in strict mode tests/cases/conformance/externalModules/exportNonInitializedVariablesAMD.ts(3,1): error TS2304: Cannot find name 'let'. tests/cases/conformance/externalModules/exportNonInitializedVariablesAMD.ts(4,6): error TS1123: Variable declaration list cannot be empty. -==== tests/cases/conformance/externalModules/exportNonInitializedVariablesAMD.ts (3 errors) ==== +==== tests/cases/conformance/externalModules/exportNonInitializedVariablesAMD.ts (4 errors) ==== var; !!! error TS1123: Variable declaration list cannot be empty. let; ~~~ +!!! error TS1212: Identifier expected. 'let' is a reserved word in strict mode + ~~~ !!! error TS2304: Cannot find name 'let'. const; diff --git a/tests/baselines/reference/exportNonInitializedVariablesCommonJS.errors.txt b/tests/baselines/reference/exportNonInitializedVariablesCommonJS.errors.txt index 65a18649af..1b05c756d7 100644 --- a/tests/baselines/reference/exportNonInitializedVariablesCommonJS.errors.txt +++ b/tests/baselines/reference/exportNonInitializedVariablesCommonJS.errors.txt @@ -1,15 +1,18 @@ tests/cases/conformance/externalModules/exportNonInitializedVariablesCommonJS.ts(2,4): error TS1123: Variable declaration list cannot be empty. +tests/cases/conformance/externalModules/exportNonInitializedVariablesCommonJS.ts(3,1): error TS1212: Identifier expected. 'let' is a reserved word in strict mode tests/cases/conformance/externalModules/exportNonInitializedVariablesCommonJS.ts(3,1): error TS2304: Cannot find name 'let'. tests/cases/conformance/externalModules/exportNonInitializedVariablesCommonJS.ts(4,6): error TS1123: Variable declaration list cannot be empty. -==== tests/cases/conformance/externalModules/exportNonInitializedVariablesCommonJS.ts (3 errors) ==== +==== tests/cases/conformance/externalModules/exportNonInitializedVariablesCommonJS.ts (4 errors) ==== var; !!! error TS1123: Variable declaration list cannot be empty. let; ~~~ +!!! error TS1212: Identifier expected. 'let' is a reserved word in strict mode + ~~~ !!! error TS2304: Cannot find name 'let'. const; diff --git a/tests/baselines/reference/exportNonInitializedVariablesES6.errors.txt b/tests/baselines/reference/exportNonInitializedVariablesES6.errors.txt index 905d16b360..f0ae7a83a1 100644 --- a/tests/baselines/reference/exportNonInitializedVariablesES6.errors.txt +++ b/tests/baselines/reference/exportNonInitializedVariablesES6.errors.txt @@ -1,15 +1,18 @@ tests/cases/conformance/externalModules/exportNonInitializedVariablesES6.ts(2,4): error TS1123: Variable declaration list cannot be empty. +tests/cases/conformance/externalModules/exportNonInitializedVariablesES6.ts(3,1): error TS1212: Identifier expected. 'let' is a reserved word in strict mode tests/cases/conformance/externalModules/exportNonInitializedVariablesES6.ts(3,1): error TS2304: Cannot find name 'let'. tests/cases/conformance/externalModules/exportNonInitializedVariablesES6.ts(4,6): error TS1123: Variable declaration list cannot be empty. -==== tests/cases/conformance/externalModules/exportNonInitializedVariablesES6.ts (3 errors) ==== +==== tests/cases/conformance/externalModules/exportNonInitializedVariablesES6.ts (4 errors) ==== var; !!! error TS1123: Variable declaration list cannot be empty. let; ~~~ +!!! error TS1212: Identifier expected. 'let' is a reserved word in strict mode + ~~~ !!! error TS2304: Cannot find name 'let'. const; diff --git a/tests/baselines/reference/exportNonInitializedVariablesSystem.errors.txt b/tests/baselines/reference/exportNonInitializedVariablesSystem.errors.txt index d7aeb5d2ac..55249c36c7 100644 --- a/tests/baselines/reference/exportNonInitializedVariablesSystem.errors.txt +++ b/tests/baselines/reference/exportNonInitializedVariablesSystem.errors.txt @@ -1,15 +1,18 @@ tests/cases/conformance/externalModules/exportNonInitializedVariablesSystem.ts(2,4): error TS1123: Variable declaration list cannot be empty. +tests/cases/conformance/externalModules/exportNonInitializedVariablesSystem.ts(3,1): error TS1212: Identifier expected. 'let' is a reserved word in strict mode tests/cases/conformance/externalModules/exportNonInitializedVariablesSystem.ts(3,1): error TS2304: Cannot find name 'let'. tests/cases/conformance/externalModules/exportNonInitializedVariablesSystem.ts(4,6): error TS1123: Variable declaration list cannot be empty. -==== tests/cases/conformance/externalModules/exportNonInitializedVariablesSystem.ts (3 errors) ==== +==== tests/cases/conformance/externalModules/exportNonInitializedVariablesSystem.ts (4 errors) ==== var; !!! error TS1123: Variable declaration list cannot be empty. let; ~~~ +!!! error TS1212: Identifier expected. 'let' is a reserved word in strict mode + ~~~ !!! error TS2304: Cannot find name 'let'. const; diff --git a/tests/baselines/reference/exportNonInitializedVariablesUMD.errors.txt b/tests/baselines/reference/exportNonInitializedVariablesUMD.errors.txt index eaf68e6705..b21d9a8971 100644 --- a/tests/baselines/reference/exportNonInitializedVariablesUMD.errors.txt +++ b/tests/baselines/reference/exportNonInitializedVariablesUMD.errors.txt @@ -1,15 +1,18 @@ tests/cases/conformance/externalModules/exportNonInitializedVariablesUMD.ts(2,4): error TS1123: Variable declaration list cannot be empty. +tests/cases/conformance/externalModules/exportNonInitializedVariablesUMD.ts(3,1): error TS1212: Identifier expected. 'let' is a reserved word in strict mode tests/cases/conformance/externalModules/exportNonInitializedVariablesUMD.ts(3,1): error TS2304: Cannot find name 'let'. tests/cases/conformance/externalModules/exportNonInitializedVariablesUMD.ts(4,6): error TS1123: Variable declaration list cannot be empty. -==== tests/cases/conformance/externalModules/exportNonInitializedVariablesUMD.ts (3 errors) ==== +==== tests/cases/conformance/externalModules/exportNonInitializedVariablesUMD.ts (4 errors) ==== var; !!! error TS1123: Variable declaration list cannot be empty. let; ~~~ +!!! error TS1212: Identifier expected. 'let' is a reserved word in strict mode + ~~~ !!! error TS2304: Cannot find name 'let'. const; diff --git a/tests/baselines/reference/exportsAndImportsWithContextualKeywordNames01.errors.txt b/tests/baselines/reference/exportsAndImportsWithContextualKeywordNames01.errors.txt new file mode 100644 index 0000000000..94d216724a --- /dev/null +++ b/tests/baselines/reference/exportsAndImportsWithContextualKeywordNames01.errors.txt @@ -0,0 +1,23 @@ +tests/cases/conformance/es6/modules/t3.ts(1,17): error TS1212: Identifier expected. 'yield' is a reserved word in strict mode + + +==== tests/cases/conformance/es6/modules/t1.ts (0 errors) ==== + + let set = { + set foo(x: number) { + } + } + let get = 10; + + export { set, get }; + +==== tests/cases/conformance/es6/modules/t2.ts (0 errors) ==== + import * as set from "./t1"; + +==== tests/cases/conformance/es6/modules/t3.ts (1 errors) ==== + import { set as yield } from "./t1"; + ~~~~~ +!!! error TS1212: Identifier expected. 'yield' is a reserved word in strict mode + +==== tests/cases/conformance/es6/modules/t4.ts (0 errors) ==== + import { get } from "./t1"; \ No newline at end of file diff --git a/tests/baselines/reference/exportsAndImportsWithContextualKeywordNames01.symbols b/tests/baselines/reference/exportsAndImportsWithContextualKeywordNames01.symbols deleted file mode 100644 index f6695a9cf3..0000000000 --- a/tests/baselines/reference/exportsAndImportsWithContextualKeywordNames01.symbols +++ /dev/null @@ -1,30 +0,0 @@ -=== tests/cases/conformance/es6/modules/t1.ts === - -let set = { ->set : Symbol(set, Decl(t1.ts, 1, 3)) - - set foo(x: number) { ->foo : Symbol(foo, Decl(t1.ts, 1, 11)) ->x : Symbol(x, Decl(t1.ts, 2, 12)) - } -} -let get = 10; ->get : Symbol(get, Decl(t1.ts, 5, 3)) - -export { set, get }; ->set : Symbol(set, Decl(t1.ts, 7, 8)) ->get : Symbol(get, Decl(t1.ts, 7, 13)) - -=== tests/cases/conformance/es6/modules/t2.ts === -import * as set from "./t1"; ->set : Symbol(set, Decl(t2.ts, 0, 6)) - -=== tests/cases/conformance/es6/modules/t3.ts === -import { set as yield } from "./t1"; ->set : Symbol(yield, Decl(t3.ts, 0, 8)) ->yield : Symbol(yield, Decl(t3.ts, 0, 8)) - -=== tests/cases/conformance/es6/modules/t4.ts === -import { get } from "./t1"; ->get : Symbol(get, Decl(t4.ts, 0, 8)) - diff --git a/tests/baselines/reference/exportsAndImportsWithContextualKeywordNames01.types b/tests/baselines/reference/exportsAndImportsWithContextualKeywordNames01.types deleted file mode 100644 index a687787c46..0000000000 --- a/tests/baselines/reference/exportsAndImportsWithContextualKeywordNames01.types +++ /dev/null @@ -1,32 +0,0 @@ -=== tests/cases/conformance/es6/modules/t1.ts === - -let set = { ->set : { foo: number; } ->{ set foo(x: number) { }} : { foo: number; } - - set foo(x: number) { ->foo : number ->x : number - } -} -let get = 10; ->get : number ->10 : number - -export { set, get }; ->set : { foo: number; } ->get : number - -=== tests/cases/conformance/es6/modules/t2.ts === -import * as set from "./t1"; ->set : typeof set - -=== tests/cases/conformance/es6/modules/t3.ts === -import { set as yield } from "./t1"; ->set : { foo: number; } ->yield : { foo: number; } - -=== tests/cases/conformance/es6/modules/t4.ts === -import { get } from "./t1"; ->get : number - From d558e42d948b0de1722e7f2f76d20b3ef59bb41f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 15 Jun 2015 18:20:44 -0700 Subject: [PATCH 4/5] External modules are always in strict mode in ES6 --- src/compiler/binder.ts | 56 +++++++++++++------ .../diagnosticInformationMap.generated.ts | 2 + src/compiler/diagnosticMessages.json | 8 +++ ...xportNonInitializedVariablesAMD.errors.txt | 4 +- ...NonInitializedVariablesCommonJS.errors.txt | 4 +- ...xportNonInitializedVariablesES6.errors.txt | 4 +- ...rtNonInitializedVariablesSystem.errors.txt | 4 +- ...xportNonInitializedVariablesUMD.errors.txt | 4 +- ...rtsWithContextualKeywordNames01.errors.txt | 4 +- ...vedWordInImportEqualDeclaration.errors.txt | 4 +- ...rictModeWordInImportDeclaration.errors.txt | 12 ++-- 11 files changed, 70 insertions(+), 36 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 5437615978..30a7b9cbde 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -618,19 +618,31 @@ namespace ts { // Report error only if there are no parse errors in file if (!file.parseDiagnostics.length) { - let message = getAncestor(node, SyntaxKind.ClassDeclaration) || getAncestor(node, SyntaxKind.ClassExpression) ? - Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Class_definitions_are_automatically_in_strict_mode : - Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode; - file.bindDiagnostics.push(createDiagnosticForNode(node, message, declarationNameToString(node))); + file.bindDiagnostics.push(createDiagnosticForNode(node, + getStrictModeIdentifierMessage(node), declarationNameToString(node))); } } } + function getStrictModeIdentifierMessage(node: Node) { + // Provide specialized messages to help the user understand why we think they're in + // strict mode. + if (getAncestor(node, SyntaxKind.ClassDeclaration) || getAncestor(node, SyntaxKind.ClassExpression)) { + return Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Class_definitions_are_automatically_in_strict_mode; + } + + if (file.externalModuleIndicator) { + return Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Modules_are_automatically_in_strict_mode; + } + + return Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode; + } + function checkStrictModeBinaryExpression(node: BinaryExpression) { if (inStrictMode && isLeftHandSideExpression(node.left) && isAssignmentOperator(node.operatorToken.kind)) { // ECMA 262 (Annex C) The identifier eval or arguments may not appear as the LeftHandSideExpression of an // Assignment operator(11.13) or of a PostfixExpression(11.3) - checkGrammarEvalOrArgumentsInStrictMode(node, node.left); + checkStrictModeEvalOrArguments(node, node.left); } } @@ -638,7 +650,7 @@ namespace ts { // It is a SyntaxError if a TryStatement with a Catch occurs within strict code and the Identifier of the // Catch production is eval or arguments if (inStrictMode && node.variableDeclaration) { - checkGrammarEvalOrArgumentsInStrictMode(node, node.variableDeclaration.name); + checkStrictModeEvalOrArguments(node, node.variableDeclaration.name); } } @@ -657,25 +669,37 @@ namespace ts { ((node).text === "eval" || (node).text === "arguments"); } - function checkGrammarEvalOrArgumentsInStrictMode(contextNode: Node, name: Node) { + function checkStrictModeEvalOrArguments(contextNode: Node, name: Node) { if (name && name.kind === SyntaxKind.Identifier) { let identifier = name; if (isEvalOrArgumentsIdentifier(identifier)) { // We check first if the name is inside class declaration or class expression; if so give explicit message // otherwise report generic error message. let span = getErrorSpanForNode(file, name); - let message = getAncestor(identifier, SyntaxKind.ClassDeclaration) || getAncestor(identifier, SyntaxKind.ClassExpression) ? - Diagnostics.Invalid_use_of_0_Class_definitions_are_automatically_in_strict_mode : - Diagnostics.Invalid_use_of_0_in_strict_mode; - file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, message, identifier.text)); + file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, + getStrictModeEvalOrArgumentsMessage(contextNode), identifier.text)); } } } + function getStrictModeEvalOrArgumentsMessage(node: Node) { + // Provide specialized messages to help the user understand why we think they're in + // strict mode. + if (getAncestor(node, SyntaxKind.ClassDeclaration) || getAncestor(node, SyntaxKind.ClassExpression)) { + return Diagnostics.Invalid_use_of_0_Class_definitions_are_automatically_in_strict_mode; + } + + if (file.externalModuleIndicator) { + return Diagnostics.Invalid_use_of_0_Modules_are_automatically_in_strict_mode; + } + + return Diagnostics.Invalid_use_of_0_in_strict_mode; + } + function checkStrictModeFunctionName(node: FunctionLikeDeclaration) { if (inStrictMode) { // It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a strict mode FunctionDeclaration or FunctionExpression (13.1)) - checkGrammarEvalOrArgumentsInStrictMode(node, node.name); + checkStrictModeEvalOrArguments(node, node.name); } } @@ -691,7 +715,7 @@ namespace ts { // Assignment operator(11.13) or of a PostfixExpression(11.3) or as the UnaryExpression // operated upon by a Prefix Increment(11.4.4) or a Prefix Decrement(11.4.5) operator. if (inStrictMode) { - checkGrammarEvalOrArgumentsInStrictMode(node, node.operand); + checkStrictModeEvalOrArguments(node, node.operand); } } @@ -699,7 +723,7 @@ namespace ts { // Grammar checking if (inStrictMode) { if (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) { - checkGrammarEvalOrArgumentsInStrictMode(node, node.operand); + checkStrictModeEvalOrArguments(node, node.operand); } } } @@ -962,7 +986,7 @@ namespace ts { function bindVariableDeclarationOrBindingElement(node: VariableDeclaration | BindingElement) { if (inStrictMode) { - checkGrammarEvalOrArgumentsInStrictMode(node, node.name) + checkStrictModeEvalOrArguments(node, node.name) } if (!isBindingPattern(node.name)) { @@ -991,7 +1015,7 @@ namespace ts { if (inStrictMode) { // It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a // strict mode FunctionLikeDeclaration or FunctionExpression(13.1) - checkGrammarEvalOrArgumentsInStrictMode(node, node.name); + checkStrictModeEvalOrArguments(node, node.name); } if (isBindingPattern(node.name)) { diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index de5e0f552d..f6fd2a60f5 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -171,6 +171,8 @@ namespace ts { A_class_declaration_without_the_default_modifier_must_have_a_name: { code: 1211, category: DiagnosticCategory.Error, key: "A class declaration without the 'default' modifier must have a name" }, Identifier_expected_0_is_a_reserved_word_in_strict_mode: { code: 1212, category: DiagnosticCategory.Error, key: "Identifier expected. '{0}' is a reserved word in strict mode" }, Identifier_expected_0_is_a_reserved_word_in_strict_mode_Class_definitions_are_automatically_in_strict_mode: { code: 1213, category: DiagnosticCategory.Error, key: "Identifier expected. '{0}' is a reserved word in strict mode. Class definitions are automatically in strict mode." }, + Identifier_expected_0_is_a_reserved_word_in_strict_mode_Modules_are_automatically_in_strict_mode: { code: 1214, category: DiagnosticCategory.Error, key: "Identifier expected. '{0}' is a reserved word in strict mode. Modules are automatically in strict mode." }, + Invalid_use_of_0_Modules_are_automatically_in_strict_mode: { code: 1215, category: DiagnosticCategory.Error, key: "Invalid use of '{0}'. Modules are automatically in strict mode." }, Export_assignment_is_not_supported_when_module_flag_is_system: { code: 1218, category: DiagnosticCategory.Error, key: "Export assignment is not supported when '--module' flag is 'system'." }, Experimental_support_for_decorators_is_a_feature_that_is_subject_to_change_in_a_future_release_Specify_experimentalDecorators_to_remove_this_warning: { code: 1219, category: DiagnosticCategory.Error, key: "Experimental support for decorators is a feature that is subject to change in a future release. Specify '--experimentalDecorators' to remove this warning." }, Generators_are_only_available_when_targeting_ECMAScript_6_or_higher: { code: 1220, category: DiagnosticCategory.Error, key: "Generators are only available when targeting ECMAScript 6 or higher." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index c4a805413e..cf25e77bf6 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -671,6 +671,14 @@ "category": "Error", "code": 1213 }, + "Identifier expected. '{0}' is a reserved word in strict mode. Modules are automatically in strict mode.": { + "category": "Error", + "code": 1214 + }, + "Invalid use of '{0}'. Modules are automatically in strict mode.": { + "category": "Error", + "code": 1215 + }, "Export assignment is not supported when '--module' flag is 'system'.": { "category": "Error", "code": 1218 diff --git a/tests/baselines/reference/exportNonInitializedVariablesAMD.errors.txt b/tests/baselines/reference/exportNonInitializedVariablesAMD.errors.txt index 27679cf607..28d7253a70 100644 --- a/tests/baselines/reference/exportNonInitializedVariablesAMD.errors.txt +++ b/tests/baselines/reference/exportNonInitializedVariablesAMD.errors.txt @@ -1,5 +1,5 @@ tests/cases/conformance/externalModules/exportNonInitializedVariablesAMD.ts(2,4): error TS1123: Variable declaration list cannot be empty. -tests/cases/conformance/externalModules/exportNonInitializedVariablesAMD.ts(3,1): error TS1212: Identifier expected. 'let' is a reserved word in strict mode +tests/cases/conformance/externalModules/exportNonInitializedVariablesAMD.ts(3,1): error TS1214: Identifier expected. 'let' is a reserved word in strict mode. Modules are automatically in strict mode. tests/cases/conformance/externalModules/exportNonInitializedVariablesAMD.ts(3,1): error TS2304: Cannot find name 'let'. tests/cases/conformance/externalModules/exportNonInitializedVariablesAMD.ts(4,6): error TS1123: Variable declaration list cannot be empty. @@ -11,7 +11,7 @@ tests/cases/conformance/externalModules/exportNonInitializedVariablesAMD.ts(4,6) !!! error TS1123: Variable declaration list cannot be empty. let; ~~~ -!!! error TS1212: Identifier expected. 'let' is a reserved word in strict mode +!!! error TS1214: Identifier expected. 'let' is a reserved word in strict mode. Modules are automatically in strict mode. ~~~ !!! error TS2304: Cannot find name 'let'. const; diff --git a/tests/baselines/reference/exportNonInitializedVariablesCommonJS.errors.txt b/tests/baselines/reference/exportNonInitializedVariablesCommonJS.errors.txt index 1b05c756d7..84d4cab9c8 100644 --- a/tests/baselines/reference/exportNonInitializedVariablesCommonJS.errors.txt +++ b/tests/baselines/reference/exportNonInitializedVariablesCommonJS.errors.txt @@ -1,5 +1,5 @@ tests/cases/conformance/externalModules/exportNonInitializedVariablesCommonJS.ts(2,4): error TS1123: Variable declaration list cannot be empty. -tests/cases/conformance/externalModules/exportNonInitializedVariablesCommonJS.ts(3,1): error TS1212: Identifier expected. 'let' is a reserved word in strict mode +tests/cases/conformance/externalModules/exportNonInitializedVariablesCommonJS.ts(3,1): error TS1214: Identifier expected. 'let' is a reserved word in strict mode. Modules are automatically in strict mode. tests/cases/conformance/externalModules/exportNonInitializedVariablesCommonJS.ts(3,1): error TS2304: Cannot find name 'let'. tests/cases/conformance/externalModules/exportNonInitializedVariablesCommonJS.ts(4,6): error TS1123: Variable declaration list cannot be empty. @@ -11,7 +11,7 @@ tests/cases/conformance/externalModules/exportNonInitializedVariablesCommonJS.ts !!! error TS1123: Variable declaration list cannot be empty. let; ~~~ -!!! error TS1212: Identifier expected. 'let' is a reserved word in strict mode +!!! error TS1214: Identifier expected. 'let' is a reserved word in strict mode. Modules are automatically in strict mode. ~~~ !!! error TS2304: Cannot find name 'let'. const; diff --git a/tests/baselines/reference/exportNonInitializedVariablesES6.errors.txt b/tests/baselines/reference/exportNonInitializedVariablesES6.errors.txt index f0ae7a83a1..08e1d9588b 100644 --- a/tests/baselines/reference/exportNonInitializedVariablesES6.errors.txt +++ b/tests/baselines/reference/exportNonInitializedVariablesES6.errors.txt @@ -1,5 +1,5 @@ tests/cases/conformance/externalModules/exportNonInitializedVariablesES6.ts(2,4): error TS1123: Variable declaration list cannot be empty. -tests/cases/conformance/externalModules/exportNonInitializedVariablesES6.ts(3,1): error TS1212: Identifier expected. 'let' is a reserved word in strict mode +tests/cases/conformance/externalModules/exportNonInitializedVariablesES6.ts(3,1): error TS1214: Identifier expected. 'let' is a reserved word in strict mode. Modules are automatically in strict mode. tests/cases/conformance/externalModules/exportNonInitializedVariablesES6.ts(3,1): error TS2304: Cannot find name 'let'. tests/cases/conformance/externalModules/exportNonInitializedVariablesES6.ts(4,6): error TS1123: Variable declaration list cannot be empty. @@ -11,7 +11,7 @@ tests/cases/conformance/externalModules/exportNonInitializedVariablesES6.ts(4,6) !!! error TS1123: Variable declaration list cannot be empty. let; ~~~ -!!! error TS1212: Identifier expected. 'let' is a reserved word in strict mode +!!! error TS1214: Identifier expected. 'let' is a reserved word in strict mode. Modules are automatically in strict mode. ~~~ !!! error TS2304: Cannot find name 'let'. const; diff --git a/tests/baselines/reference/exportNonInitializedVariablesSystem.errors.txt b/tests/baselines/reference/exportNonInitializedVariablesSystem.errors.txt index 55249c36c7..1e461ea725 100644 --- a/tests/baselines/reference/exportNonInitializedVariablesSystem.errors.txt +++ b/tests/baselines/reference/exportNonInitializedVariablesSystem.errors.txt @@ -1,5 +1,5 @@ tests/cases/conformance/externalModules/exportNonInitializedVariablesSystem.ts(2,4): error TS1123: Variable declaration list cannot be empty. -tests/cases/conformance/externalModules/exportNonInitializedVariablesSystem.ts(3,1): error TS1212: Identifier expected. 'let' is a reserved word in strict mode +tests/cases/conformance/externalModules/exportNonInitializedVariablesSystem.ts(3,1): error TS1214: Identifier expected. 'let' is a reserved word in strict mode. Modules are automatically in strict mode. tests/cases/conformance/externalModules/exportNonInitializedVariablesSystem.ts(3,1): error TS2304: Cannot find name 'let'. tests/cases/conformance/externalModules/exportNonInitializedVariablesSystem.ts(4,6): error TS1123: Variable declaration list cannot be empty. @@ -11,7 +11,7 @@ tests/cases/conformance/externalModules/exportNonInitializedVariablesSystem.ts(4 !!! error TS1123: Variable declaration list cannot be empty. let; ~~~ -!!! error TS1212: Identifier expected. 'let' is a reserved word in strict mode +!!! error TS1214: Identifier expected. 'let' is a reserved word in strict mode. Modules are automatically in strict mode. ~~~ !!! error TS2304: Cannot find name 'let'. const; diff --git a/tests/baselines/reference/exportNonInitializedVariablesUMD.errors.txt b/tests/baselines/reference/exportNonInitializedVariablesUMD.errors.txt index b21d9a8971..b180c13850 100644 --- a/tests/baselines/reference/exportNonInitializedVariablesUMD.errors.txt +++ b/tests/baselines/reference/exportNonInitializedVariablesUMD.errors.txt @@ -1,5 +1,5 @@ tests/cases/conformance/externalModules/exportNonInitializedVariablesUMD.ts(2,4): error TS1123: Variable declaration list cannot be empty. -tests/cases/conformance/externalModules/exportNonInitializedVariablesUMD.ts(3,1): error TS1212: Identifier expected. 'let' is a reserved word in strict mode +tests/cases/conformance/externalModules/exportNonInitializedVariablesUMD.ts(3,1): error TS1214: Identifier expected. 'let' is a reserved word in strict mode. Modules are automatically in strict mode. tests/cases/conformance/externalModules/exportNonInitializedVariablesUMD.ts(3,1): error TS2304: Cannot find name 'let'. tests/cases/conformance/externalModules/exportNonInitializedVariablesUMD.ts(4,6): error TS1123: Variable declaration list cannot be empty. @@ -11,7 +11,7 @@ tests/cases/conformance/externalModules/exportNonInitializedVariablesUMD.ts(4,6) !!! error TS1123: Variable declaration list cannot be empty. let; ~~~ -!!! error TS1212: Identifier expected. 'let' is a reserved word in strict mode +!!! error TS1214: Identifier expected. 'let' is a reserved word in strict mode. Modules are automatically in strict mode. ~~~ !!! error TS2304: Cannot find name 'let'. const; diff --git a/tests/baselines/reference/exportsAndImportsWithContextualKeywordNames01.errors.txt b/tests/baselines/reference/exportsAndImportsWithContextualKeywordNames01.errors.txt index 94d216724a..6d443464d9 100644 --- a/tests/baselines/reference/exportsAndImportsWithContextualKeywordNames01.errors.txt +++ b/tests/baselines/reference/exportsAndImportsWithContextualKeywordNames01.errors.txt @@ -1,4 +1,4 @@ -tests/cases/conformance/es6/modules/t3.ts(1,17): error TS1212: Identifier expected. 'yield' is a reserved word in strict mode +tests/cases/conformance/es6/modules/t3.ts(1,17): error TS1214: Identifier expected. 'yield' is a reserved word in strict mode. Modules are automatically in strict mode. ==== tests/cases/conformance/es6/modules/t1.ts (0 errors) ==== @@ -17,7 +17,7 @@ tests/cases/conformance/es6/modules/t3.ts(1,17): error TS1212: Identifier expect ==== tests/cases/conformance/es6/modules/t3.ts (1 errors) ==== import { set as yield } from "./t1"; ~~~~~ -!!! error TS1212: Identifier expected. 'yield' is a reserved word in strict mode +!!! error TS1214: Identifier expected. 'yield' is a reserved word in strict mode. Modules are automatically in strict mode. ==== tests/cases/conformance/es6/modules/t4.ts (0 errors) ==== import { get } from "./t1"; \ No newline at end of file diff --git a/tests/baselines/reference/strictModeReservedWordInImportEqualDeclaration.errors.txt b/tests/baselines/reference/strictModeReservedWordInImportEqualDeclaration.errors.txt index 7511e88f53..3405db573a 100644 --- a/tests/baselines/reference/strictModeReservedWordInImportEqualDeclaration.errors.txt +++ b/tests/baselines/reference/strictModeReservedWordInImportEqualDeclaration.errors.txt @@ -1,4 +1,4 @@ -tests/cases/compiler/strictModeReservedWordInImportEqualDeclaration.ts(3,8): error TS1212: Identifier expected. 'public' is a reserved word in strict mode +tests/cases/compiler/strictModeReservedWordInImportEqualDeclaration.ts(3,8): error TS1214: Identifier expected. 'public' is a reserved word in strict mode. Modules are automatically in strict mode. tests/cases/compiler/strictModeReservedWordInImportEqualDeclaration.ts(3,25): error TS2307: Cannot find module '1'. @@ -7,6 +7,6 @@ tests/cases/compiler/strictModeReservedWordInImportEqualDeclaration.ts(3,25): er "use strict" import public = require("1"); ~~~~~~ -!!! error TS1212: Identifier expected. 'public' is a reserved word in strict mode +!!! error TS1214: Identifier expected. 'public' is a reserved word in strict mode. Modules are automatically in strict mode. ~~~ !!! error TS2307: Cannot find module '1'. \ No newline at end of file diff --git a/tests/baselines/reference/strictModeWordInImportDeclaration.errors.txt b/tests/baselines/reference/strictModeWordInImportDeclaration.errors.txt index aed48e1d3f..b6e20cfc18 100644 --- a/tests/baselines/reference/strictModeWordInImportDeclaration.errors.txt +++ b/tests/baselines/reference/strictModeWordInImportDeclaration.errors.txt @@ -1,8 +1,8 @@ -tests/cases/compiler/strictModeWordInImportDeclaration.ts(2,13): error TS1212: Identifier expected. 'package' is a reserved word in strict mode +tests/cases/compiler/strictModeWordInImportDeclaration.ts(2,13): error TS1214: Identifier expected. 'package' is a reserved word in strict mode. Modules are automatically in strict mode. tests/cases/compiler/strictModeWordInImportDeclaration.ts(2,26): error TS2307: Cannot find module './1'. -tests/cases/compiler/strictModeWordInImportDeclaration.ts(3,16): error TS1212: Identifier expected. 'private' is a reserved word in strict mode +tests/cases/compiler/strictModeWordInImportDeclaration.ts(3,16): error TS1214: Identifier expected. 'private' is a reserved word in strict mode. Modules are automatically in strict mode. tests/cases/compiler/strictModeWordInImportDeclaration.ts(3,30): error TS2307: Cannot find module './1'. -tests/cases/compiler/strictModeWordInImportDeclaration.ts(4,8): error TS1212: Identifier expected. 'public' is a reserved word in strict mode +tests/cases/compiler/strictModeWordInImportDeclaration.ts(4,8): error TS1214: Identifier expected. 'public' is a reserved word in strict mode. Modules are automatically in strict mode. tests/cases/compiler/strictModeWordInImportDeclaration.ts(4,20): error TS2307: Cannot find module './1'. @@ -10,16 +10,16 @@ tests/cases/compiler/strictModeWordInImportDeclaration.ts(4,20): error TS2307: C "use strict" import * as package from "./1" ~~~~~~~ -!!! error TS1212: Identifier expected. 'package' is a reserved word in strict mode +!!! error TS1214: Identifier expected. 'package' is a reserved word in strict mode. Modules are automatically in strict mode. ~~~~~ !!! error TS2307: Cannot find module './1'. import {foo as private} from "./1" ~~~~~~~ -!!! error TS1212: Identifier expected. 'private' is a reserved word in strict mode +!!! error TS1214: Identifier expected. 'private' is a reserved word in strict mode. Modules are automatically in strict mode. ~~~~~ !!! error TS2307: Cannot find module './1'. import public from "./1" ~~~~~~ -!!! error TS1212: Identifier expected. 'public' is a reserved word in strict mode +!!! error TS1214: Identifier expected. 'public' is a reserved word in strict mode. Modules are automatically in strict mode. ~~~~~ !!! error TS2307: Cannot find module './1'. \ No newline at end of file From 300089ce2dcf7847bfc6baf9f6d98d09f835ce3a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 15 Jun 2015 23:31:40 -0700 Subject: [PATCH 5/5] PR feedback. --- src/compiler/binder.ts | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 30a7b9cbde..eaad8593ee 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -91,7 +91,7 @@ namespace ts { // If this file is an external module, then it is automatically in strict-mode according to // ES6. If it is not an external module, then we'll determine if it is in strict mode or - // not depending on if we see "use strict" in certain places. + // not depending on if we see "use strict" in certain places (or if we hit a class/namespace). let inStrictMode = !!file.externalModuleIndicator; let symbolCount = 0; @@ -538,10 +538,13 @@ namespace ts { } function bindObjectLiteralExpression(node: ObjectLiteralExpression) { + const enum ElementKind { + Property = 1, + Accessor = 2 + } + if (inStrictMode) { - let seen: Map = {}; - const Property = 1; - const NonProperty = 2; + let seen: Map = {}; for (let prop of node.properties) { if (prop.name.kind !== SyntaxKind.Identifier) { @@ -559,8 +562,8 @@ namespace ts { // d.IsAccessorDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true // and either both previous and propId.descriptor have[[Get]] fields or both previous and propId.descriptor have[[Set]] fields let currentKind = prop.kind === SyntaxKind.PropertyAssignment || prop.kind === SyntaxKind.ShorthandPropertyAssignment || prop.kind === SyntaxKind.MethodDeclaration - ? Property - : NonProperty; + ? ElementKind.Property + : ElementKind.Accessor; let existingKind = seen[identifier.text]; if (!existingKind) { @@ -568,7 +571,7 @@ namespace ts { continue; } - if (currentKind === Property && existingKind === Property) { + if (currentKind === ElementKind.Property && existingKind === ElementKind.Property) { let span = getErrorSpanForNode(file, identifier); file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, Diagnostics.An_object_literal_cannot_have_multiple_properties_with_the_same_name_in_strict_mode)); @@ -798,7 +801,7 @@ namespace ts { return; } - if (isUseStrictPrologueDirective(statement)) { + if (isUseStrictPrologueDirective(statement)) { inStrictMode = true; return; } @@ -806,9 +809,8 @@ namespace ts { } /// Should be called only on prologue directives (isPrologueDirective(node) should be true) - function isUseStrictPrologueDirective(node: Node): boolean { - Debug.assert(isPrologueDirective(node)); - let nodeText = getTextOfNodeFromSourceText(file.text, (node).expression); + function isUseStrictPrologueDirective(node: ExpressionStatement): boolean { + let nodeText = getTextOfNodeFromSourceText(file.text, node.expression); // Note: the node text must be exactly "use strict" or 'use strict'. It is not ok for the // string to contain unicode escapes (as per ES5). @@ -838,9 +840,9 @@ namespace ts { return declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes); case SyntaxKind.Parameter: return bindParameter(node); - case SyntaxKind.BindingElement: case SyntaxKind.VariableDeclaration: - return bindVariableDeclarationOrBindingElement(node); + case SyntaxKind.BindingElement: + return bindVariableDeclarationOrBindingElement(node); case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property | ((node).questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes);