From fc27f72324f7bd38c797df528df25f382283be20 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 10 Dec 2014 18:52:56 -0800 Subject: [PATCH] Understand and handle modifiers on function declarations and variable statements within blocks. This ensures reusability for functions/variables that may have been outside a block, but end up inside one afterwards. It also ensure the same tree is produced when incremental parsing. i.e. if you have: declare function F() { } And you add a { above it, then we current have an incremental parsing bug. Namely we would see a FunctionDeclaration node and say 'yes, we can reuse that node while parsing the block'. This is currently broken because the normal parse would not have normally accepted such a node (because of the modifiers). This was an example of contextual parsing of the same kind of node. Something which we do not want to do if we want incremental parsing to work properly. --- .../diagnosticInformationMap.generated.ts | 1 + src/compiler/diagnosticMessages.json | 4 ++ src/compiler/parser.ts | 51 ++++++++++++++++++- ...rserModifierOnStatementInBlock2.errors.txt | 9 ++-- 4 files changed, 57 insertions(+), 8 deletions(-) diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 17dbae83bf2..4520aaffc02 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -144,6 +144,7 @@ module ts { Array_element_destructuring_pattern_expected: { code: 1181, category: DiagnosticCategory.Error, key: "Array element destructuring pattern expected." }, A_destructuring_declaration_must_have_an_initializer: { code: 1182, category: DiagnosticCategory.Error, key: "A destructuring declaration must have an initializer." }, Destructuring_declarations_are_not_allowed_in_ambient_contexts: { code: 1183, category: DiagnosticCategory.Error, key: "Destructuring declarations are not allowed in ambient contexts." }, + Modifiers_cannot_appear_here: { code: 1184, category: DiagnosticCategory.Error, key: "Modifiers cannot appear here." }, Duplicate_identifier_0: { code: 2300, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'." }, Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor: { code: 2301, category: DiagnosticCategory.Error, key: "Initializer of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor." }, Static_members_cannot_reference_class_type_parameters: { code: 2302, category: DiagnosticCategory.Error, key: "Static members cannot reference class type parameters." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index a06b71d460e..7aacd21cea4 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -568,6 +568,10 @@ "category": "Error", "code": 1183 }, + "Modifiers cannot appear here.": { + "category": "Error", + "code": 1184 + }, "Duplicate identifier '{0}'.": { "category": "Error", diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 3e8301ef7f7..9552e0f9cc9 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3866,12 +3866,45 @@ module ts { } // Else parse it like identifier - fall through default: + if (isModifier(token)) { + var result = tryParse(parseVariableStatementOrFunctionDeclarationWithModifiers); + if (result) { + return result; + } + } + return isLabel() ? parseLabeledStatement() : parseExpressionStatement(); } } + function parseVariableStatementOrFunctionDeclarationWithModifiers(): FunctionDeclaration | VariableStatement { + var start = scanner.getStartPos(); + var modifiers = parseModifiers(); + switch (token) { + case SyntaxKind.ConstKeyword: + var nextTokenIsEnum = lookAhead(nextTokenIsEnumKeyword) + if (nextTokenIsEnum) { + return undefined; + } + return parseVariableStatement(start, modifiers); + + case SyntaxKind.LetKeyword: + if (!isLetDeclaration()) { + return undefined; + } + return parseVariableStatement(start, modifiers); + + case SyntaxKind.VarKeyword: + return parseVariableStatement(start, modifiers); + case SyntaxKind.FunctionKeyword: + return parseFunctionDeclaration(start, modifiers); + } + + return undefined; + } + function parseFunctionBlockOrSemicolon(isGenerator: boolean): Block { if (token === SyntaxKind.OpenBraceToken) { return parseFunctionBlock(isGenerator, /*ignoreMissingOpenBrace:*/ false); @@ -4600,6 +4633,7 @@ module ts { // We're automatically in an ambient context if this is a .d.ts file. var inAmbientContext = fileExtensionIs(file.filename, ".d.ts"); var inFunctionBlock = false; + var inBlock = false; var parent: Node; visitNode(file); @@ -4614,6 +4648,10 @@ module ts { if (isFunctionBlock(node)) { inFunctionBlock = true; } + var savedInBlock = inBlock; + if (node.kind === SyntaxKind.Block || node.kind === SyntaxKind.TryBlock || node.kind === SyntaxKind.FinallyBlock) { + inBlock = true; + } var savedInAmbientContext = inAmbientContext if (node.flags & NodeFlags.Ambient) { @@ -4624,6 +4662,7 @@ module ts { inAmbientContext = savedInAmbientContext; inFunctionBlock = savedInFunctionBlock; + inBlock = savedInBlock; } parent = savedParent; @@ -5085,7 +5124,8 @@ module ts { } function checkFunctionDeclaration(node: FunctionLikeDeclaration) { - return checkAnySignatureDeclaration(node) || + return checkForDisallowedModifiersInBlock(node) || + checkAnySignatureDeclaration(node) || checkFunctionName(node.name) || checkForBodyInAmbientContext(node.body, /*isConstructor:*/ false) || checkForGenerator(node); @@ -5820,10 +5860,17 @@ module ts { } function checkVariableStatement(node: VariableStatement) { - return checkVariableDeclarations(node.declarations) || + return checkForDisallowedModifiersInBlock(node) || + checkVariableDeclarations(node.declarations) || checkForDisallowedLetOrConstStatement(node); } + function checkForDisallowedModifiersInBlock(node: Node) { + if (inBlock && node.modifiers) { + return grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here); + } + } + function checkForDisallowedLetOrConstStatement(node: VariableStatement) { if (!allowLetAndConstDeclarations(node.parent)) { if (isLet(node)) { diff --git a/tests/baselines/reference/parserModifierOnStatementInBlock2.errors.txt b/tests/baselines/reference/parserModifierOnStatementInBlock2.errors.txt index 7155d5cfe42..b07d7d9aad1 100644 --- a/tests/baselines/reference/parserModifierOnStatementInBlock2.errors.txt +++ b/tests/baselines/reference/parserModifierOnStatementInBlock2.errors.txt @@ -1,13 +1,10 @@ -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserModifierOnStatementInBlock2.ts(2,12): error TS1005: ';' expected. -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserModifierOnStatementInBlock2.ts(2,4): error TS2304: Cannot find name 'declare'. +tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserModifierOnStatementInBlock2.ts(2,4): error TS1184: Modifiers cannot appear here. -==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserModifierOnStatementInBlock2.ts (2 errors) ==== +==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserModifierOnStatementInBlock2.ts (1 errors) ==== { declare var x = this; - ~~~ -!!! error TS1005: ';' expected. ~~~~~~~ -!!! error TS2304: Cannot find name 'declare'. +!!! error TS1184: Modifiers cannot appear here. } \ No newline at end of file