diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 70fb70a9df7..d94e8a757d5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4141,8 +4141,12 @@ module ts { // illegal, and will cause a parse error. // Note: It may be worth keeping the upper bound check on arity, but removing // the lower bound check if there are omitted expressions. - if (!isCorrect && forEach(node.arguments, arg => arg.kind === SyntaxKind.OmittedExpression)) { - return true; + if (!isCorrect) { + // Technically this type assertion is not safe because args could be initialized to emptyArray + // above. + if ((>args).hasTrailingComma || forEach(args, arg => arg.kind === SyntaxKind.OmittedExpression)) { + return true; + } } return isCorrect; } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 2b4e70a000c..36f2a502dc6 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -824,11 +824,21 @@ module ts { } } + function emitTrailingCommaIfPresent(nodeList: NodeArray, isMultiline: boolean): void { + if (nodeList.hasTrailingComma) { + write(","); + if (isMultiline) { + writeLine(); + } + } + } + function emitArrayLiteral(node: ArrayLiteral) { if (node.flags & NodeFlags.MultiLine) { write("["); increaseIndent(); emitMultiLineList(node.elements); + emitTrailingCommaIfPresent(node.elements, /*isMultiline*/ true); decreaseIndent(); writeLine(); write("]"); @@ -836,6 +846,7 @@ module ts { else { write("["); emitCommaList(node.elements); + emitTrailingCommaIfPresent(node.elements, /*isMultiline*/ false); write("]"); } } @@ -848,6 +859,11 @@ module ts { write("{"); increaseIndent(); emitMultiLineList(node.properties); + + if (compilerOptions.target === ScriptTarget.ES5) { + emitTrailingCommaIfPresent(node.properties, /*isMultiline*/ true); + } + decreaseIndent(); writeLine(); write("}"); @@ -855,6 +871,11 @@ module ts { else { write("{ "); emitCommaList(node.properties); + + if (compilerOptions.target === ScriptTarget.ES5) { + emitTrailingCommaIfPresent(node.properties, /*isMultiline*/ false); + } + write(" }"); } } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index a6621e42d30..1d2a5a760a2 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1197,7 +1197,7 @@ module ts { } // Parses a comma-delimited list of elements - function parseDelimitedList(kind: ParsingContext, parseElement: () => T, allowTrailingComma: boolean, preserveTrailingComma: boolean): NodeArray { + function parseDelimitedList(kind: ParsingContext, parseElement: () => T, allowTrailingComma: boolean): NodeArray { var saveParsingContext = parsingContext; parsingContext |= 1 << kind; var result = >[]; @@ -1228,11 +1228,8 @@ module ts { grammarErrorAtPos(commaStart, scanner.getStartPos() - commaStart, Diagnostics.Trailing_comma_not_allowed); } } - // Even if we reported an error because of a disallowed trailing comma, we still may - // need to preserve it for the checker so that signature help can work well. - if (preserveTrailingComma) { - result.push(createNode(SyntaxKind.OmittedExpression)); - } + // Always preserve a trailing comma by marking it on the NodeArray + result.hasTrailingComma = true; } break; @@ -1267,7 +1264,7 @@ module ts { function parseBracketedList(kind: ParsingContext, parseElement: () => T, startToken: SyntaxKind, endToken: SyntaxKind): NodeArray { if (parseExpected(startToken)) { - var result = parseDelimitedList(kind, parseElement, /*allowTrailingComma*/ false, /*preserveTrailingComma*/ false); + var result = parseDelimitedList(kind, parseElement, /*allowTrailingComma*/ false); parseExpected(endToken); return result; } @@ -2307,7 +2304,7 @@ module ts { // needs evidence of a trailing comma in order to give good results for signature help. // That is why we do not allow a trailing comma, but we "preserve" a trailing comma. callExpr.arguments = parseDelimitedList(ParsingContext.ArgumentExpressions, - parseArgumentExpression, /*allowTrailingComma*/ false, /*preserveTrailingComma*/ true); + parseArgumentExpression, /*allowTrailingComma*/ false); parseExpected(SyntaxKind.CloseParenToken); expr = finishNode(callExpr); continue; @@ -2402,7 +2399,7 @@ module ts { parseExpected(SyntaxKind.OpenBracketToken); if (scanner.hasPrecedingLineBreak()) node.flags |= NodeFlags.MultiLine; node.elements = parseDelimitedList(ParsingContext.ArrayLiteralMembers, - parseArrayLiteralElement, /*allowTrailingComma*/ true, /*preserveTrailingComma*/ true); + parseArrayLiteralElement, /*allowTrailingComma*/ true); parseExpected(SyntaxKind.CloseBracketToken); return finishNode(node); } @@ -2444,10 +2441,7 @@ module ts { node.flags |= NodeFlags.MultiLine; } - // ES3 itself does not accept a trailing comma in an object literal, however, we'd like to preserve it in ES5. - var preserveTrailingComma = languageVersion !== ScriptTarget.ES3; - - node.properties = parseDelimitedList(ParsingContext.ObjectLiteralMembers, parseObjectLiteralMember, /*allowTrailingComma*/ true, preserveTrailingComma); + node.properties = parseDelimitedList(ParsingContext.ObjectLiteralMembers, parseObjectLiteralMember, /*allowTrailingComma*/ true); parseExpected(SyntaxKind.CloseBraceToken); var seen: Map = {}; @@ -2540,7 +2534,7 @@ module ts { // needs evidence of a trailing comma in order to give good results for signature help. // That is why we do not allow a trailing comma, but we "preserve" a trailing comma. node.arguments = parseDelimitedList(ParsingContext.ArgumentExpressions, - parseArgumentExpression, /*allowTrailingComma*/ false, /*preserveTrailingComma*/ true); + parseArgumentExpression, /*allowTrailingComma*/ false); parseExpected(SyntaxKind.CloseParenToken); } return finishNode(node); @@ -3110,7 +3104,7 @@ module ts { function parseVariableDeclarationList(flags: NodeFlags, noIn?: boolean): NodeArray { return parseDelimitedList(ParsingContext.VariableDeclarations, - () => parseVariableDeclaration(flags, noIn), /*allowTrailingComma*/ false, /*preserveTrailingComma*/ false); + () => parseVariableDeclaration(flags, noIn), /*allowTrailingComma*/ false); } function parseVariableStatement(pos?: number, flags?: NodeFlags): VariableStatement { @@ -3510,7 +3504,7 @@ module ts { if (parseOptional(SyntaxKind.ImplementsKeyword)) { implementsKeywordLength = scanner.getStartPos() - implementsKeywordStart; node.implementedTypes = parseDelimitedList(ParsingContext.BaseTypeReferences, - parseTypeReference, /*allowTrailingComma*/ false, /*preserveTrailingComma*/ false); + parseTypeReference, /*allowTrailingComma*/ false); } var errorCountBeforeClassBody = file.syntacticErrors.length; if (parseExpected(SyntaxKind.OpenBraceToken)) { @@ -3539,7 +3533,7 @@ module ts { if (parseOptional(SyntaxKind.ExtendsKeyword)) { extendsKeywordLength = scanner.getStartPos() - extendsKeywordStart; node.baseTypes = parseDelimitedList(ParsingContext.BaseTypeReferences, - parseTypeReference, /*allowTrailingComma*/ false, /*preserveTrailingComma*/ false); + parseTypeReference, /*allowTrailingComma*/ false); } var errorCountBeforeInterfaceBody = file.syntacticErrors.length; node.members = parseTypeLiteral().members; @@ -3604,7 +3598,7 @@ module ts { node.name = parseIdentifier(); if (parseExpected(SyntaxKind.OpenBraceToken)) { node.members = parseDelimitedList(ParsingContext.EnumMembers, - parseAndCheckEnumMember, /*allowTrailingComma*/ true, /*preserveTrailingComma*/ false); + parseAndCheckEnumMember, /*allowTrailingComma*/ true); parseExpected(SyntaxKind.CloseBraceToken); } else { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ce228f39e4b..b308cd521b8 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -259,7 +259,9 @@ module ts { localSymbol?: Symbol; // Local symbol declared by node (initialized by binding only for exported nodes) } - export interface NodeArray extends Array, TextRange { } + export interface NodeArray extends Array, TextRange { + hasTrailingComma?: boolean; + } export interface Identifier extends Node { text: string; // Text of identifier (with escapes converted to characters) diff --git a/tests/baselines/reference/castExpressionParentheses.js b/tests/baselines/reference/castExpressionParentheses.js index 2f62d6e9447..2b518226c60 100644 --- a/tests/baselines/reference/castExpressionParentheses.js +++ b/tests/baselines/reference/castExpressionParentheses.js @@ -43,7 +43,7 @@ new (A()); // parentheses should be omitted // literals { a: 0 }; -[1, 3, ]; +[1, 3,]; "string"; 23.0; /regexp/g; diff --git a/tests/baselines/reference/emptyExpr.js b/tests/baselines/reference/emptyExpr.js index 36fd5ccee19..de3bddeba1e 100644 --- a/tests/baselines/reference/emptyExpr.js +++ b/tests/baselines/reference/emptyExpr.js @@ -2,4 +2,4 @@ [{},] //// [emptyExpr.js] -[{}, ]; +[{},]; diff --git a/tests/baselines/reference/parserArrayLiteralExpression10.js b/tests/baselines/reference/parserArrayLiteralExpression10.js index 8cbbff49c9f..986d3045dc0 100644 --- a/tests/baselines/reference/parserArrayLiteralExpression10.js +++ b/tests/baselines/reference/parserArrayLiteralExpression10.js @@ -2,4 +2,4 @@ var v = [1,1,]; //// [parserArrayLiteralExpression10.js] -var v = [1, 1, ]; +var v = [1, 1,]; diff --git a/tests/baselines/reference/parserArrayLiteralExpression15.js b/tests/baselines/reference/parserArrayLiteralExpression15.js index f31617a998b..84ab9dac240 100644 --- a/tests/baselines/reference/parserArrayLiteralExpression15.js +++ b/tests/baselines/reference/parserArrayLiteralExpression15.js @@ -2,4 +2,4 @@ var v = [,,1,1,,1,,1,1,,1,]; //// [parserArrayLiteralExpression15.js] -var v = [, , 1, 1, , 1, , 1, 1, , 1, ]; +var v = [, , 1, 1, , 1, , 1, 1, , 1,]; diff --git a/tests/baselines/reference/parserArrayLiteralExpression2.js b/tests/baselines/reference/parserArrayLiteralExpression2.js index f6984016054..1fb26155eb5 100644 --- a/tests/baselines/reference/parserArrayLiteralExpression2.js +++ b/tests/baselines/reference/parserArrayLiteralExpression2.js @@ -2,4 +2,4 @@ var v = [,]; //// [parserArrayLiteralExpression2.js] -var v = [, ]; +var v = [,]; diff --git a/tests/baselines/reference/parserArrayLiteralExpression3.js b/tests/baselines/reference/parserArrayLiteralExpression3.js index 6d70ebdb37f..2d7d19fc2c3 100644 --- a/tests/baselines/reference/parserArrayLiteralExpression3.js +++ b/tests/baselines/reference/parserArrayLiteralExpression3.js @@ -2,4 +2,4 @@ var v = [,,]; //// [parserArrayLiteralExpression3.js] -var v = [, , ]; +var v = [, ,]; diff --git a/tests/baselines/reference/parserArrayLiteralExpression4.js b/tests/baselines/reference/parserArrayLiteralExpression4.js index dd75cb14fd6..2287897338e 100644 --- a/tests/baselines/reference/parserArrayLiteralExpression4.js +++ b/tests/baselines/reference/parserArrayLiteralExpression4.js @@ -2,4 +2,4 @@ var v = [,,,]; //// [parserArrayLiteralExpression4.js] -var v = [, , , ]; +var v = [, , ,]; diff --git a/tests/baselines/reference/parserArrayLiteralExpression7.js b/tests/baselines/reference/parserArrayLiteralExpression7.js index 2ea0b298b4e..b302e87e352 100644 --- a/tests/baselines/reference/parserArrayLiteralExpression7.js +++ b/tests/baselines/reference/parserArrayLiteralExpression7.js @@ -2,4 +2,4 @@ var v = [1,]; //// [parserArrayLiteralExpression7.js] -var v = [1, ]; +var v = [1,]; diff --git a/tests/baselines/reference/parserArrayLiteralExpression8.js b/tests/baselines/reference/parserArrayLiteralExpression8.js index 7ef65a8cc0d..223a86db229 100644 --- a/tests/baselines/reference/parserArrayLiteralExpression8.js +++ b/tests/baselines/reference/parserArrayLiteralExpression8.js @@ -2,4 +2,4 @@ var v = [,1,]; //// [parserArrayLiteralExpression8.js] -var v = [, 1, ]; +var v = [, 1,]; diff --git a/tests/baselines/reference/trailingCommaInHeterogenousArrayLiteral1.js b/tests/baselines/reference/trailingCommaInHeterogenousArrayLiteral1.js index 26dc61d189c..478c05f5e73 100644 --- a/tests/baselines/reference/trailingCommaInHeterogenousArrayLiteral1.js +++ b/tests/baselines/reference/trailingCommaInHeterogenousArrayLiteral1.js @@ -17,7 +17,7 @@ var arrTest = (function () { }; arrTest.prototype.callTest = function () { // these two should give the same error - this.test([1, 2, "hi", 5, ]); + this.test([1, 2, "hi", 5,]); this.test([1, 2, "hi", 5]); }; return arrTest; diff --git a/tests/baselines/reference/trailingCommasES3.js b/tests/baselines/reference/trailingCommasES3.js index 56e30fe5c17..554390a83ea 100644 --- a/tests/baselines/reference/trailingCommasES3.js +++ b/tests/baselines/reference/trailingCommasES3.js @@ -18,8 +18,8 @@ var o2 = { a: 1, b: 2 }; var o3 = { a: 1 }; var o4 = {}; var a1 = [1, 2]; -var a2 = [1, 2, ]; -var a3 = [1, ]; +var a2 = [1, 2,]; +var a3 = [1,]; var a4 = []; -var a5 = [1, , ]; -var a6 = [, , ]; +var a5 = [1, ,]; +var a6 = [, ,]; diff --git a/tests/baselines/reference/trailingCommasES5.js b/tests/baselines/reference/trailingCommasES5.js index f342d9ac2d7..e54e911189a 100644 --- a/tests/baselines/reference/trailingCommasES5.js +++ b/tests/baselines/reference/trailingCommasES5.js @@ -14,12 +14,12 @@ var a6 = [, , ]; //// [trailingCommasES5.js] var o1 = { a: 1, b: 2 }; -var o2 = { a: 1, b: 2, }; -var o3 = { a: 1, }; +var o2 = { a: 1, b: 2, }; +var o3 = { a: 1, }; var o4 = {}; var a1 = [1, 2]; -var a2 = [1, 2, ]; -var a3 = [1, ]; +var a2 = [1, 2,]; +var a3 = [1,]; var a4 = []; -var a5 = [1, , ]; -var a6 = [, , ]; +var a5 = [1, ,]; +var a6 = [, ,];