From 138970f35fc3d410761d3fbe449c813b4acb1c3c Mon Sep 17 00:00:00 2001 From: jbondc Date: Tue, 16 Jun 2015 22:00:42 -0400 Subject: [PATCH] Fixes #2632 (invoking methods on numbers) --- .../diagnosticInformationMap.generated.ts | 1 + src/compiler/diagnosticMessages.json | 4 ++ src/compiler/parser.ts | 41 ++++++++++++++----- src/compiler/types.ts | 3 +- tests/baselines/reference/numLit.errors.txt | 27 ++++++++---- tests/baselines/reference/numLit.js | 28 +++++++++++-- tests/cases/compiler/numLit.ts | 15 ++++++- 7 files changed, 93 insertions(+), 26 deletions(-) diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 09b009711fe..46976813120 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -569,5 +569,6 @@ namespace ts { decorators_can_only_be_used_in_a_ts_file: { code: 8017, category: DiagnosticCategory.Error, key: "'decorators' can only be used in a .ts file." }, Only_identifiers_Slashqualified_names_with_optional_type_arguments_are_currently_supported_in_a_class_extends_clauses: { code: 9002, category: DiagnosticCategory.Error, key: "Only identifiers/qualified-names with optional type arguments are currently supported in a class 'extends' clauses." }, class_expressions_are_not_currently_supported: { code: 9003, category: DiagnosticCategory.Error, key: "'class' expressions are not currently supported." }, + Numeric_literal_0_cannot_be_followed_by_an_expression: { code: 9004, category: DiagnosticCategory.Error, key: "Numeric literal {0} cannot be followed by an expression." }, }; } \ No newline at end of file diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index fee46a84c1e..4b705396fe5 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2268,5 +2268,9 @@ "'class' expressions are not currently supported.": { "category": "Error", "code": 9003 + }, + "Numeric literal {0} cannot be followed by an expression.": { + "category": "Error", + "code": 9004 } } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index fbb3dad5037..55fdcd91d34 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -100,6 +100,8 @@ namespace ts { visitNode(cbNode, (node).type) || visitNode(cbNode, (node).equalsGreaterThanToken) || visitNode(cbNode, (node).body); + case SyntaxKind.NumericLiteral: + return visitNode(cbNode, (node).invalidDotExpression); case SyntaxKind.TypeReference: return visitNode(cbNode, (node).typeName) || visitNodes(cbNodes, (node).typeArguments); @@ -1830,20 +1832,37 @@ namespace ts { } let tokenPos = scanner.getTokenPos(); + let nextCharCode: number; + nextToken(); finishNode(node); - // Octal literals are not allowed in strict mode or ES5 - // Note that theoretically the following condition would hold true literals like 009, - // which is not octal.But because of how the scanner separates the tokens, we would - // never get a token like this. Instead, we would get 00 and 9 as two separate tokens. - // We also do not need to check for negatives because any prefix operator would be part of a - // parent unary expression. - if (node.kind === SyntaxKind.NumericLiteral - && sourceText.charCodeAt(tokenPos) === CharacterCodes._0 - && isOctalDigit(sourceText.charCodeAt(tokenPos + 1))) { - - node.flags |= NodeFlags.OctalLiteral; + if (node.kind === SyntaxKind.NumericLiteral) { + // Octal literals are not allowed in strict mode or ES5 + // Note that theoretically the following condition would hold true literals like 009, + // which is not octal.But because of how the scanner separates the tokens, we would + // never get a token like this. Instead, we would get 00 and 9 as two separate tokens. + // We also do not need to check for negatives because any prefix operator would be part of a + // parent unary expression. + if (sourceText.charCodeAt(tokenPos) === CharacterCodes._0 && isOctalDigit(sourceText.charCodeAt(tokenPos + 1))) { + node.flags |= NodeFlags.OctalLiteral; + } + else if (token === SyntaxKind.DotToken) { + // Issue #2632 Keep space for 3 .toString() + if (isWhiteSpace(sourceText.charCodeAt(node.end))) { + node.end++; + scanner.setTextPos(node.end + 1); + } + } + else if (token === SyntaxKind.Identifier && sourceText.charCodeAt(node.end-1) === CharacterCodes.dot) { + nextCharCode = sourceText.charCodeAt(node.end); + if (!isWhiteSpace(nextCharCode) && !isLineBreak(nextCharCode)) { + // Eat up an invalid expression following '1.' e.g. 1.something() + node.invalidDotExpression = parseLeftHandSideExpressionOrHigher(); + parseErrorAtPosition(node.end, node.invalidDotExpression.end - node.end, Diagnostics.Numeric_literal_0_cannot_be_followed_by_an_expression, "'" + sourceText.substring(tokenPos, node.end) + "'"); + node.end = node.invalidDotExpression.end; + } + } } return node; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f9a2fa3b7b4..187cead4842 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -737,6 +737,7 @@ namespace ts { text: string; isUnterminated?: boolean; hasExtendedUnicodeEscape?: boolean; + invalidDotExpression?: LeftHandSideExpression; // 1.toString() we attach the node but never emit it } export interface TemplateExpression extends PrimaryExpression { @@ -1881,7 +1882,7 @@ namespace ts { CarriageReturnLineFeed = 0, LineFeed = 1, } - + export interface LineAndCharacter { line: number; /* diff --git a/tests/baselines/reference/numLit.errors.txt b/tests/baselines/reference/numLit.errors.txt index 98b84791e35..66f11141c15 100644 --- a/tests/baselines/reference/numLit.errors.txt +++ b/tests/baselines/reference/numLit.errors.txt @@ -1,13 +1,24 @@ -tests/cases/compiler/numLit.ts(3,3): error TS1005: ';' expected. -tests/cases/compiler/numLit.ts(3,3): error TS2304: Cannot find name 'toString'. +tests/cases/compiler/numLit.ts(3,3): error TS9004: Numeric literal '1.' cannot be followed by an expression. +tests/cases/compiler/numLit.ts(9,15): error TS9004: Numeric literal '2.' cannot be followed by an expression. ==== tests/cases/compiler/numLit.ts (2 errors) ==== 1..toString(); 1.0.toString(); - 1.toString(); - ~~~~~~~~ -!!! error TS1005: ';' expected. - ~~~~~~~~ -!!! error TS2304: Cannot find name 'toString'. - 1.+2.0 + 3. ; \ No newline at end of file + 1.toString(); // error: Numeric literal '1.' cannot be followed by an expression. + ~~~~~~~~~~ +!!! error TS9004: Numeric literal '1.' cannot be followed by an expression. + 1.+2.0 + 3. ; + + // Preserve whitespace where important for JS compatibility + var i: number = 1; + var test1 = i.toString(); + var test2 = 2.toString(); // error: Numeric literal '2.' cannot be followed by an expression. + ~~~~~~~~~~ +!!! error TS9004: Numeric literal '2.' cannot be followed by an expression. + var test3 = 3 .toString(); // preserve whitepace + var test4 = 3.['toString'](); + var test5 = 3 + .toString(); // preserve whitepace + var test6 = new Number(4).toString(); + var test7 = 3. + 3. \ No newline at end of file diff --git a/tests/baselines/reference/numLit.js b/tests/baselines/reference/numLit.js index aaec91cde5d..a6e33a9a3c1 100644 --- a/tests/baselines/reference/numLit.js +++ b/tests/baselines/reference/numLit.js @@ -1,12 +1,32 @@ //// [numLit.ts] 1..toString(); 1.0.toString(); -1.toString(); -1.+2.0 + 3. ; +1.toString(); // error: Numeric literal '1.' cannot be followed by an expression. +1.+2.0 + 3. ; + +// Preserve whitespace where important for JS compatibility +var i: number = 1; +var test1 = i.toString(); +var test2 = 2.toString(); // error: Numeric literal '2.' cannot be followed by an expression. +var test3 = 3 .toString(); // preserve whitepace +var test4 = 3.['toString'](); +var test5 = 3 +.toString(); // preserve whitepace +var test6 = new Number(4).toString(); +var test7 = 3. + 3. //// [numLit.js] 1..toString(); 1.0.toString(); -1.; -toString(); +1.toString(); // error: Numeric literal '1.' cannot be followed by an expression. 1. + 2.0 + 3.; +// Preserve whitespace where important for JS compatibility +var i = 1; +var test1 = i.toString(); +var test2 = 2.toString(); // error: Numeric literal '2.' cannot be followed by an expression. +var test3 = 3 .toString(); // preserve whitepace +var test4 = 3.['toString'](); +var test5 = 3 + .toString(); // preserve whitepace +var test6 = new Number(4).toString(); +var test7 = 3. + 3.; diff --git a/tests/cases/compiler/numLit.ts b/tests/cases/compiler/numLit.ts index 01e19e7c8fb..d9d42f9018c 100644 --- a/tests/cases/compiler/numLit.ts +++ b/tests/cases/compiler/numLit.ts @@ -1,4 +1,15 @@ 1..toString(); 1.0.toString(); -1.toString(); -1.+2.0 + 3. ; \ No newline at end of file +1.toString(); // error: Numeric literal '1.' cannot be followed by an expression. +1.+2.0 + 3. ; + +// Preserve whitespace where important for JS compatibility +var i: number = 1; +var test1 = i.toString(); +var test2 = 2.toString(); // error: Numeric literal '2.' cannot be followed by an expression. +var test3 = 3 .toString(); // preserve whitepace +var test4 = 3.['toString'](); +var test5 = 3 +.toString(); // preserve whitepace +var test6 = new Number(4).toString(); +var test7 = 3. + 3. \ No newline at end of file