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.
This commit is contained in:
Cyrus Najmabadi
2014-12-10 18:52:56 -08:00
parent 3699a4079f
commit fc27f72324
4 changed files with 57 additions and 8 deletions

View File

@@ -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." },

View File

@@ -568,6 +568,10 @@
"category": "Error",
"code": 1183
},
"Modifiers cannot appear here.": {
"category": "Error",
"code": 1184
},
"Duplicate identifier '{0}'.": {
"category": "Error",

View File

@@ -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)) {

View File

@@ -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.
}