diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 71c24e2793c..a7c3c47b62c 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1385,14 +1385,12 @@ module ts { } // Parses a comma-delimited list of elements - function parseDelimitedList(kind: ParsingContext, parseElement: () => T, allowTrailingComma: boolean): NodeArray { + function parseDelimitedList(kind: ParsingContext, parseElement: () => T): NodeArray { var saveParsingContext = parsingContext; parsingContext |= 1 << kind; var result = >[]; result.pos = getNodePos(); - // Keep track of how many errors we had before the list started. If we don't see any new - // errors resulting from the list being malformed, we are free to complain about a trailing comma. - var errorCountBeforeParsingList = file._parserDiagnostics.length; + var commaStart = -1; // Meaning the previous token was not a comma while (true) { if (isListElement(kind, /* inErrorRecovery */ false)) { @@ -1425,12 +1423,6 @@ module ts { // was a trailing comma. // Check if the last token was a comma. if (commaStart >= 0) { - if (!allowTrailingComma) { - if (file._parserDiagnostics.length === errorCountBeforeParsingList) { - // Report a grammar error so we don't affect lookahead - grammarErrorAtPos(commaStart, scanner.getStartPos() - commaStart, Diagnostics.Trailing_comma_not_allowed); - } - } // Always preserve a trailing comma by marking it on the NodeArray result.hasTrailingComma = true; } @@ -1457,7 +1449,7 @@ module ts { function parseBracketedList(kind: ParsingContext, parseElement: () => T, startToken: SyntaxKind, endToken: SyntaxKind): NodeArray { if (parseExpected(startToken)) { - var result = parseDelimitedList(kind, parseElement, /*allowTrailingComma*/ false); + var result = parseDelimitedList(kind, parseElement); parseExpected(endToken); return result; } @@ -2438,8 +2430,7 @@ module ts { else { parseExpected(SyntaxKind.OpenParenToken); } - callExpr.arguments = parseDelimitedList(ParsingContext.ArgumentExpressions, - parseArgumentExpression, /*allowTrailingComma*/ false); + callExpr.arguments = parseDelimitedList(ParsingContext.ArgumentExpressions, parseArgumentExpression); parseExpected(SyntaxKind.CloseParenToken); expr = finishNode(callExpr); continue; @@ -2564,8 +2555,7 @@ module ts { var node = createNode(SyntaxKind.ArrayLiteral); parseExpected(SyntaxKind.OpenBracketToken); if (scanner.hasPrecedingLineBreak()) node.flags |= NodeFlags.MultiLine; - node.elements = parseDelimitedList(ParsingContext.ArrayLiteralMembers, - parseArrayLiteralElement, /*allowTrailingComma*/ true); + node.elements = parseDelimitedList(ParsingContext.ArrayLiteralMembers, parseArrayLiteralElement); parseExpected(SyntaxKind.CloseBracketToken); return finishNode(node); } @@ -2626,7 +2616,7 @@ module ts { node.flags |= NodeFlags.MultiLine; } - node.properties = parseDelimitedList(ParsingContext.ObjectLiteralMembers, parseObjectLiteralMember, /*allowTrailingComma*/ true); + node.properties = parseDelimitedList(ParsingContext.ObjectLiteralMembers, parseObjectLiteralMember); parseExpected(SyntaxKind.CloseBraceToken); return finishNode(node); } @@ -2655,8 +2645,7 @@ module ts { parseExpected(SyntaxKind.NewKeyword); node.func = parseCallAndAccess(parsePrimaryExpression(), /* inNewExpression */ true); if (parseOptional(SyntaxKind.OpenParenToken) || token === SyntaxKind.LessThanToken && (node.typeArguments = tryParse(parseTypeArgumentsAndOpenParen))) { - node.arguments = parseDelimitedList(ParsingContext.ArgumentExpressions, - parseArgumentExpression, /*allowTrailingComma*/ false); + node.arguments = parseDelimitedList(ParsingContext.ArgumentExpressions, parseArgumentExpression); parseExpected(SyntaxKind.CloseParenToken); } return finishNode(node); @@ -2805,8 +2794,13 @@ module ts { } else { var forStatement = createNode(SyntaxKind.ForStatement, pos); - if (declarations) forStatement.declarations = declarations; - if (varOrInit) forStatement.initializer = varOrInit; + if (declarations) { + forStatement.declarations = declarations; + } + if (varOrInit) { + forStatement.initializer = varOrInit; + } + parseExpected(SyntaxKind.SemicolonToken); if (token !== SyntaxKind.SemicolonToken && token !== SyntaxKind.CloseParenToken) { forStatement.condition = parseExpression(); @@ -3231,8 +3225,7 @@ module ts { } function parseVariableDeclarationList(flags: NodeFlags, noIn?: boolean): NodeArray { - return parseDelimitedList(ParsingContext.VariableDeclarations, - () => parseVariableDeclaration(flags, noIn), /*allowTrailingComma*/ false); + return parseDelimitedList(ParsingContext.VariableDeclarations, () => parseVariableDeclaration(flags, noIn)); } function parseVariableStatement(pos?: number, flags?: NodeFlags): VariableStatement { @@ -3584,8 +3577,7 @@ module ts { var implementsKeywordLength: number; if (parseOptional(SyntaxKind.ImplementsKeyword)) { implementsKeywordLength = scanner.getStartPos() - implementsKeywordStart; - node.implementedTypes = parseDelimitedList(ParsingContext.BaseTypeReferences, - parseTypeReference, /*allowTrailingComma*/ false); + node.implementedTypes = parseDelimitedList(ParsingContext.BaseTypeReferences, parseTypeReference); } var errorCountBeforeClassBody = file._parserDiagnostics.length; if (parseExpected(SyntaxKind.OpenBraceToken)) { @@ -3613,8 +3605,7 @@ module ts { var extendsKeywordLength: number; if (parseOptional(SyntaxKind.ExtendsKeyword)) { extendsKeywordLength = scanner.getStartPos() - extendsKeywordStart; - node.baseTypes = parseDelimitedList(ParsingContext.BaseTypeReferences, - parseTypeReference, /*allowTrailingComma*/ false); + node.baseTypes = parseDelimitedList(ParsingContext.BaseTypeReferences, parseTypeReference); } var errorCountBeforeInterfaceBody = file._parserDiagnostics.length; node.members = parseTypeLiteral().members; @@ -3657,8 +3648,7 @@ module ts { parseExpected(SyntaxKind.EnumKeyword); node.name = parseIdentifier(); if (parseExpected(SyntaxKind.OpenBraceToken)) { - node.members = parseDelimitedList(ParsingContext.EnumMembers, - parseEnumMember, /*allowTrailingComma*/ true); + node.members = parseDelimitedList(ParsingContext.EnumMembers, parseEnumMember); parseExpected(SyntaxKind.CloseBraceToken); } else { @@ -4049,8 +4039,10 @@ module ts { switch (node.kind) { case SyntaxKind.ArrowFunction: return visitArrowFunction(node); case SyntaxKind.BinaryExpression: return visitBinaryExpression(node); + case SyntaxKind.CallExpression: return visitCallExpression(node); case SyntaxKind.CallSignature: return visitCallSignature(node); case SyntaxKind.CatchBlock: return visitCatchBlock(node); + case SyntaxKind.ClassDeclaration: return visitClassDeclaration(node); case SyntaxKind.Constructor: return visitConstructor(node); case SyntaxKind.ConstructorType: return visitConstructorType(node); case SyntaxKind.ConstructSignature: return visitConstructSignature(node); @@ -4060,14 +4052,17 @@ module ts { case SyntaxKind.FunctionType: return visitFunctionType(node); case SyntaxKind.GetAccessor: return visitGetAccessor(node); case SyntaxKind.IndexSignature: return visitIndexSignature(node); + case SyntaxKind.InterfaceDeclaration: return visitInterfaceDeclaration(node); case SyntaxKind.Method: return visitMethod(node); case SyntaxKind.ModuleDeclaration: return visitModuleDeclaration(node); + case SyntaxKind.NewExpression: return visitNewExpression(node); case SyntaxKind.ObjectLiteral: return visitObjectLiteral(node); case SyntaxKind.Parameter: return visitParameter(node); case SyntaxKind.PostfixOperator: return visitPostfixOperator(node); case SyntaxKind.PrefixOperator: return visitPrefixOperator(node); case SyntaxKind.SetAccessor: return visitSetAccessor(node); case SyntaxKind.TaggedTemplateExpression: return visitTaggedTemplateExpression(node); + case SyntaxKind.TupleType: return visitTupleType(node); case SyntaxKind.VariableDeclaration: return visitVariableDeclaration(node); case SyntaxKind.VariableStatement: return visitVariableStatement(node); } @@ -4110,6 +4105,19 @@ module ts { } } + function visitCallExpression(node: CallExpression) { + checkForTrailingComma(node.typeArguments) || + checkForTrailingComma(node.arguments); + } + + function checkForTrailingComma(list: NodeArray): boolean { + if (list && list.hasTrailingComma) { + var start = list.end - ",".length; + var end = list.end; + return grammarErrorAtPos(start, end - start, Diagnostics.Trailing_comma_not_allowed); + } + } + function visitCallSignature(node: ConstructorDeclaration) { checkTypeParameterList(node.typeParameters) || checkParameterList(node.parameters); @@ -4127,6 +4135,10 @@ module ts { } } + function visitClassDeclaration(node: ClassDeclaration) { + checkForTrailingComma(node.implementedTypes); + } + function visitConstructor(node: ConstructorDeclaration) { checkTypeParameterList(node.typeParameters) || checkParameterList(node.parameters) || @@ -4271,6 +4283,10 @@ module ts { } } + function visitInterfaceDeclaration(node: InterfaceDeclaration) { + checkForTrailingComma(node.baseTypes); + } + function visitMethod(node: MethodDeclaration) { checkTypeParameterList(node.typeParameters) || checkParameterList(node.parameters); @@ -4290,6 +4306,11 @@ module ts { } } + function visitNewExpression(node: NewExpression): void { + checkForTrailingComma(node.typeArguments) || + checkForTrailingComma(node.arguments); + } + function visitObjectLiteral(node: ObjectLiteral): void { var seen: Map = {}; var Property = 1; @@ -4371,6 +4392,10 @@ module ts { } function checkTypeParameterList(typeParameters: NodeArray): boolean { + if (checkForTrailingComma(typeParameters)) { + return true; + } + if (typeParameters && typeParameters.length === 0) { var start = typeParameters.pos - "<".length; var end = typeParameters.end + ">".length; @@ -4379,6 +4404,10 @@ module ts { } function checkParameterList(parameters: NodeArray): boolean { + if (checkForTrailingComma(parameters)) { + return true; + } + var seenOptionalParameter = false; var parameterCount = parameters.length; @@ -4488,6 +4517,10 @@ module ts { } } + function visitTupleType(node: TupleTypeNode) { + checkForTrailingComma(node.elementTypes); + } + function visitVariableDeclaration(node: VariableDeclaration) { if (inAmbientContext && node.initializer) { var equalsPos = node.type ? skipTrivia(sourceText, node.type.end) : skipTrivia(sourceText, node.name.end); @@ -4504,6 +4537,10 @@ module ts { } function visitVariableStatement(node: VariableStatement) { + if (checkForTrailingComma(node.declarations)) { + return; + } + if (!node.declarations.length) { grammarErrorOnNode(node, Diagnostics.Variable_declaration_list_cannot_be_empty); } diff --git a/tests/baselines/reference/parserErrorRecovery_ParameterList2.errors.txt b/tests/baselines/reference/parserErrorRecovery_ParameterList2.errors.txt index fd4c286f356..48cd83147d7 100644 --- a/tests/baselines/reference/parserErrorRecovery_ParameterList2.errors.txt +++ b/tests/baselines/reference/parserErrorRecovery_ParameterList2.errors.txt @@ -1,11 +1,8 @@ -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ParameterLists/parserErrorRecovery_ParameterList2.ts(1,13): error TS1009: Trailing comma not allowed. tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ParameterLists/parserErrorRecovery_ParameterList2.ts(1,15): error TS1005: ')' expected. -==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ParameterLists/parserErrorRecovery_ParameterList2.ts (2 errors) ==== +==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ParameterLists/parserErrorRecovery_ParameterList2.ts (1 errors) ==== function f(a, { - ~ -!!! error TS1009: Trailing comma not allowed. ~ !!! error TS1005: ')' expected. } \ No newline at end of file diff --git a/tests/baselines/reference/parserErrorRecovery_VariableList1.errors.txt b/tests/baselines/reference/parserErrorRecovery_VariableList1.errors.txt index b69cab688f0..a42ffcdd474 100644 --- a/tests/baselines/reference/parserErrorRecovery_VariableList1.errors.txt +++ b/tests/baselines/reference/parserErrorRecovery_VariableList1.errors.txt @@ -1,11 +1,8 @@ -tests/cases/conformance/parser/ecmascript5/ErrorRecovery/VariableLists/parserErrorRecovery_VariableList1.ts(1,6): error TS1009: Trailing comma not allowed. tests/cases/conformance/parser/ecmascript5/ErrorRecovery/VariableLists/parserErrorRecovery_VariableList1.ts(2,1): error TS1108: A 'return' statement can only be used within a function body. -==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/VariableLists/parserErrorRecovery_VariableList1.ts (2 errors) ==== +==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/VariableLists/parserErrorRecovery_VariableList1.ts (1 errors) ==== var a, - ~ -!!! error TS1009: Trailing comma not allowed. return; ~~~~~~ !!! error TS1108: A 'return' statement can only be used within a function body. \ No newline at end of file