From fc229368d15fbc24d19e59b48ca9d64bb624e65a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 20 Feb 2015 18:56:42 -0800 Subject: [PATCH] Include the binary expression's operator in the AST. --- src/compiler/checker.ts | 32 +++++++++---------- src/compiler/emitter.ts | 12 +++---- src/compiler/parser.ts | 32 ++++++++----------- src/compiler/types.ts | 2 +- src/services/breakpoints.ts | 2 +- src/services/formatting/formattingScanner.ts | 26 ++++++++------- src/services/services.ts | 2 +- .../baselines/reference/APISample_compile.js | 2 +- .../reference/APISample_compile.types | 6 ++-- tests/baselines/reference/APISample_linter.js | 6 ++-- .../reference/APISample_linter.types | 14 ++++---- .../reference/APISample_transform.js | 2 +- .../reference/APISample_transform.types | 6 ++-- .../baselines/reference/APISample_watcher.js | 2 +- .../reference/APISample_watcher.types | 6 ++-- tests/cases/compiler/APISample_linter.ts | 2 +- tests/cases/unittests/incrementalParser.ts | 2 +- 17 files changed, 78 insertions(+), 78 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5f93fe82786..489f84cfcea 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3409,7 +3409,7 @@ module ts { return isContextSensitive((node).whenTrue) || isContextSensitive((node).whenFalse); case SyntaxKind.BinaryExpression: - return (node).operator === SyntaxKind.BarBarToken && + return (node).operatorToken.kind === SyntaxKind.BarBarToken && (isContextSensitive((node).left) || isContextSensitive((node).right)); case SyntaxKind.PropertyAssignment: return isContextSensitive((node).initializer); @@ -4591,7 +4591,7 @@ module ts { return links.assignmentChecks[symbol.id] = isAssignedIn(node); function isAssignedInBinaryExpression(node: BinaryExpression) { - if (node.operator >= SyntaxKind.FirstAssignment && node.operator <= SyntaxKind.LastAssignment) { + if (node.operatorToken.kind >= SyntaxKind.FirstAssignment && node.operatorToken.kind <= SyntaxKind.LastAssignment) { var n = node.left; while (n.kind === SyntaxKind.ParenthesizedExpression) { n = (n).expression; @@ -4724,10 +4724,10 @@ module ts { case SyntaxKind.BinaryExpression: // In the right operand of an && or ||, narrow based on left operand if (child === (node).right) { - if ((node).operator === SyntaxKind.AmpersandAmpersandToken) { + if ((node).operatorToken.kind === SyntaxKind.AmpersandAmpersandToken) { narrowedType = narrowType(type, (node).left, /*assumeTrue*/ true); } - else if ((node).operator === SyntaxKind.BarBarToken) { + else if ((node).operatorToken.kind === SyntaxKind.BarBarToken) { narrowedType = narrowType(type, (node).left, /*assumeTrue*/ false); } } @@ -4765,7 +4765,7 @@ module ts { return type; } var typeInfo = primitiveTypeInfo[right.text]; - if (expr.operator === SyntaxKind.ExclamationEqualsEqualsToken) { + if (expr.operatorToken.kind === SyntaxKind.ExclamationEqualsEqualsToken) { assumeTrue = !assumeTrue; } if (assumeTrue) { @@ -4855,7 +4855,7 @@ module ts { case SyntaxKind.ParenthesizedExpression: return narrowType(type, (expr).expression, assumeTrue); case SyntaxKind.BinaryExpression: - var operator = (expr).operator; + var operator = (expr).operatorToken.kind; if (operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) { return narrowTypeByEquality(type, expr, assumeTrue); } @@ -5202,7 +5202,7 @@ module ts { function getContextualTypeForBinaryOperand(node: Expression): Type { var binaryExpression = node.parent; - var operator = binaryExpression.operator; + var operator = binaryExpression.operatorToken.kind; if (operator >= SyntaxKind.FirstAssignment && operator <= SyntaxKind.LastAssignment) { // In an assignment expression, the right operand is contextually typed by the type of the left operand. if (node === binaryExpression.right) { @@ -5452,7 +5452,7 @@ module ts { // an assignment target. Examples include 'a = xxx', '{ p: a } = xxx', '[{ p: a}] = xxx'. function isAssignmentTarget(node: Node): boolean { var parent = node.parent; - if (parent.kind === SyntaxKind.BinaryExpression && (parent).operator === SyntaxKind.EqualsToken && (parent).left === node) { + if (parent.kind === SyntaxKind.BinaryExpression && (parent).operatorToken.kind === SyntaxKind.EqualsToken && (parent).left === node) { return true; } if (parent.kind === SyntaxKind.PropertyAssignment) { @@ -7108,7 +7108,7 @@ module ts { } function checkDestructuringAssignment(target: Expression, sourceType: Type, contextualMapper?: TypeMapper): Type { - if (target.kind === SyntaxKind.BinaryExpression && (target).operator === SyntaxKind.EqualsToken) { + if (target.kind === SyntaxKind.BinaryExpression && (target).operatorToken.kind === SyntaxKind.EqualsToken) { checkBinaryExpression(target, contextualMapper); target = (target).left; } @@ -7131,13 +7131,13 @@ module ts { function checkBinaryExpression(node: BinaryExpression, contextualMapper?: TypeMapper) { // Grammar checking - if (isLeftHandSideExpression(node.left) && isAssignmentOperator(node.operator)) { + if (isLeftHandSideExpression(node.left) && isAssignmentOperator(node.operatorToken.kind)) { // ECMA 262 (Annex C) The identifier eval or arguments may not appear as the LeftHandSideExpression of an // Assignment operator(11.13) or of a PostfixExpression(11.3) checkGrammarEvalOrArgumentsInStrictMode(node, node.left); } - var operator = node.operator; + var operator = node.operatorToken.kind; if (operator === SyntaxKind.EqualsToken && (node.left.kind === SyntaxKind.ObjectLiteralExpression || node.left.kind === SyntaxKind.ArrayLiteralExpression)) { return checkDestructuringAssignment(node.left, checkExpression(node.right, contextualMapper), contextualMapper); } @@ -7178,8 +7178,8 @@ module ts { // try and return them a helpful suggestion if ((leftType.flags & TypeFlags.Boolean) && (rightType.flags & TypeFlags.Boolean) && - (suggestedOperator = getSuggestedBooleanOperator(node.operator)) !== undefined) { - error(node, Diagnostics.The_0_operator_is_not_allowed_for_boolean_types_Consider_using_1_instead, tokenToString(node.operator), tokenToString(suggestedOperator)); + (suggestedOperator = getSuggestedBooleanOperator(node.operatorToken.kind)) !== undefined) { + error(node, Diagnostics.The_0_operator_is_not_allowed_for_boolean_types_Consider_using_1_instead, tokenToString(node.operatorToken.kind), tokenToString(suggestedOperator)); } else { // otherwise just check each operand separately and report errors as normal @@ -7312,7 +7312,7 @@ module ts { } function reportOperatorError() { - error(node, Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2, tokenToString(node.operator), typeToString(leftType), typeToString(rightType)); + error(node, Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2, tokenToString(node.operatorToken.kind), typeToString(leftType), typeToString(rightType)); } } @@ -9252,7 +9252,7 @@ module ts { if (right === undefined) { return undefined; } - switch ((e).operator) { + switch ((e).operatorToken.kind) { case SyntaxKind.BarToken: return left | right; case SyntaxKind.AmpersandToken: return left & right; case SyntaxKind.GreaterThanGreaterThanToken: return left >> right; @@ -10830,7 +10830,7 @@ module ts { if (languageVersion < ScriptTarget.ES6) { return grammarErrorOnNode(node, Diagnostics.Computed_property_names_are_only_available_when_targeting_ECMAScript_6_and_higher); } - else if (computedPropertyName.expression.kind === SyntaxKind.BinaryExpression && (computedPropertyName.expression).operator === SyntaxKind.CommaToken) { + else if (computedPropertyName.expression.kind === SyntaxKind.BinaryExpression && (computedPropertyName.expression).operatorToken.kind === SyntaxKind.CommaToken) { return grammarErrorOnNode(computedPropertyName.expression, Diagnostics.A_comma_expression_is_not_allowed_in_a_computed_property_name); } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 62910c184aa..3a7f6248389 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2252,7 +2252,7 @@ module ts { // spread ('...') unary operators that are anticipated for ES6. switch (expression.kind) { case SyntaxKind.BinaryExpression: - switch ((expression).operator) { + switch ((expression).operatorToken.kind) { case SyntaxKind.AsteriskToken: case SyntaxKind.SlashToken: case SyntaxKind.PercentToken: @@ -2773,14 +2773,14 @@ module ts { } function emitBinaryExpression(node: BinaryExpression) { - if (languageVersion < ScriptTarget.ES6 && node.operator === SyntaxKind.EqualsToken && + if (languageVersion < ScriptTarget.ES6 && node.operatorToken.kind === SyntaxKind.EqualsToken && (node.left.kind === SyntaxKind.ObjectLiteralExpression || node.left.kind === SyntaxKind.ArrayLiteralExpression)) { emitDestructuring(node); } else { emit(node.left); - if (node.operator !== SyntaxKind.CommaToken) write(" "); - write(tokenToString(node.operator)); + if (node.operatorToken.kind !== SyntaxKind.CommaToken) write(" "); + write(tokenToString(node.operatorToken.kind)); write(" "); emit(node.right); } @@ -3122,7 +3122,7 @@ module ts { // Return the expression 'value === void 0 ? defaultValue : value' var equals = createNode(SyntaxKind.BinaryExpression); equals.left = value; - equals.operator = SyntaxKind.EqualsEqualsEqualsToken; + equals.operatorToken = createNode(SyntaxKind.EqualsEqualsEqualsToken); equals.right = createVoidZero(); var cond = createNode(SyntaxKind.ConditionalExpression); cond.condition = equals; @@ -3205,7 +3205,7 @@ module ts { } function emitDestructuringAssignment(target: Expression, value: Expression) { - if (target.kind === SyntaxKind.BinaryExpression && (target).operator === SyntaxKind.EqualsToken) { + if (target.kind === SyntaxKind.BinaryExpression && (target).operatorToken.kind === SyntaxKind.EqualsToken) { value = createDefaultValueCheck(value,(target).right); target = (target).left; } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 95e1ea5de00..aaa19e4c801 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -152,6 +152,7 @@ module ts { return visitNode(cbNode, (node).operand); case SyntaxKind.BinaryExpression: return visitNode(cbNode, (node).left) || + visitNode(cbNode, (node).operatorToken) || visitNode(cbNode, (node).right); case SyntaxKind.ConditionalExpression: return visitNode(cbNode, (node).condition) || @@ -1311,6 +1312,12 @@ module ts { return undefined; } + function parseTokenNode(): T { + var node = createNode(token); + nextToken(); + return finishNode(node); + } + function canParseSemicolon() { // If there's a real semicolon, then we can always parse it out. if (token === SyntaxKind.SemicolonToken) { @@ -2084,14 +2091,6 @@ module ts { return allowIdentifierNames ? parseIdentifierName() : parseIdentifier(); } - - - function parseTokenNode(): T { - var node = createNode(token); - nextToken(); - return finishNode(node); - } - function parseTemplateExpression(): TemplateExpression { var template = createNode(SyntaxKind.TemplateExpression); @@ -2801,8 +2800,9 @@ module ts { // Expression[in] , AssignmentExpression[in] var expr = parseAssignmentExpressionOrHigher(); - while (parseOptional(SyntaxKind.CommaToken)) { - expr = makeBinaryExpression(expr, SyntaxKind.CommaToken, parseAssignmentExpressionOrHigher()); + var operatorToken: Node; + while ((operatorToken = parseOptionalToken(SyntaxKind.CommaToken))) { + expr = makeBinaryExpression(expr, operatorToken, parseAssignmentExpressionOrHigher()); } return expr; } @@ -2881,9 +2881,7 @@ module ts { // Note: we call reScanGreaterToken so that we get an appropriately merged token // for cases like > > = becoming >>= if (isLeftHandSideExpression(expr) && isAssignmentOperator(reScanGreaterToken())) { - var operator = token; - nextToken(); - return makeBinaryExpression(expr, operator, parseAssignmentExpressionOrHigher()); + return makeBinaryExpression(expr, parseTokenNode(), parseAssignmentExpressionOrHigher()); } // It wasn't an assignment or a lambda. This is a conditional expression: @@ -3187,9 +3185,7 @@ module ts { break; } - var operator = token; - nextToken(); - leftOperand = makeBinaryExpression(leftOperand, operator, parseBinaryExpressionOrHigher(newPrecedence)); + leftOperand = makeBinaryExpression(leftOperand, parseTokenNode(), parseBinaryExpressionOrHigher(newPrecedence)); } return leftOperand; @@ -3245,10 +3241,10 @@ module ts { return -1; } - function makeBinaryExpression(left: Expression, operator: SyntaxKind, right: Expression): BinaryExpression { + function makeBinaryExpression(left: Expression, operatorToken: Node, right: Expression): BinaryExpression { var node = createNode(SyntaxKind.BinaryExpression, left.pos); node.left = left; - node.operator = operator; + node.operatorToken = operatorToken; node.right = right; return finishNode(node); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0133c83c422..ccae04ae156 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -623,7 +623,7 @@ module ts { export interface BinaryExpression extends Expression { left: Expression; - operator: SyntaxKind; + operatorToken: Node; right: Expression; } diff --git a/src/services/breakpoints.ts b/src/services/breakpoints.ts index 86b72b711c1..07ab359f18d 100644 --- a/src/services/breakpoints.ts +++ b/src/services/breakpoints.ts @@ -69,7 +69,7 @@ module ts.BreakpointResolver { return textSpan(node); } - if (node.parent.kind === SyntaxKind.BinaryExpression && (node.parent).operator === SyntaxKind.CommaToken) { + if (node.parent.kind === SyntaxKind.BinaryExpression && (node.parent).operatorToken.kind === SyntaxKind.CommaToken) { // if this is comma expression, the breakpoint is possible in this expression return textSpan(node); } diff --git a/src/services/formatting/formattingScanner.ts b/src/services/formatting/formattingScanner.ts index e9485158aba..4ce463ec383 100644 --- a/src/services/formatting/formattingScanner.ts +++ b/src/services/formatting/formattingScanner.ts @@ -93,17 +93,19 @@ module ts.formatting { savedPos = scanner.getStartPos(); } - function shouldRescanGreaterThanToken(container: Node): boolean { - if (container.kind !== SyntaxKind.BinaryExpression) { - return false; - } - switch ((container).operator) { - case SyntaxKind.GreaterThanEqualsToken: - case SyntaxKind.GreaterThanGreaterThanEqualsToken: - case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: - case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: - case SyntaxKind.GreaterThanGreaterThanToken: - return true; + function shouldRescanGreaterThanToken(node: Node): boolean { + //if (container.kind !== SyntaxKind.BinaryExpression) { + // return false; + //} + if (node) { + switch (node.kind) { + case SyntaxKind.GreaterThanEqualsToken: + case SyntaxKind.GreaterThanGreaterThanEqualsToken: + case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: + case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: + case SyntaxKind.GreaterThanGreaterThanToken: + return true; + } } return false; @@ -164,7 +166,7 @@ module ts.formatting { if (expectedScanAction === ScanAction.RescanGreaterThanToken && currentToken === SyntaxKind.GreaterThanToken) { currentToken = scanner.reScanGreaterToken(); - Debug.assert((n).operator === currentToken); + Debug.assert(n.kind === currentToken); lastScanAction = ScanAction.RescanGreaterThanToken; } else if (expectedScanAction === ScanAction.RescanSlashToken && startsWithSlashToken(currentToken)) { diff --git a/src/services/services.ts b/src/services/services.ts index 7c77b8c5cd0..9b187f19848 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4648,7 +4648,7 @@ module ts { return true; } else if (parent.kind === SyntaxKind.BinaryExpression && (parent).left === node) { - var operator = (parent).operator; + var operator = (parent).operatorToken.kind; return SyntaxKind.FirstAssignment <= operator && operator <= SyntaxKind.LastAssignment; } } diff --git a/tests/baselines/reference/APISample_compile.js b/tests/baselines/reference/APISample_compile.js index 2d18228b12b..0e57a0db2c2 100644 --- a/tests/baselines/reference/APISample_compile.js +++ b/tests/baselines/reference/APISample_compile.js @@ -527,7 +527,7 @@ declare module "typescript" { } interface BinaryExpression extends Expression { left: Expression; - operator: SyntaxKind; + operatorToken: Node; right: Expression; } interface ConditionalExpression extends Expression { diff --git a/tests/baselines/reference/APISample_compile.types b/tests/baselines/reference/APISample_compile.types index e31c895ccf7..1f70294c918 100644 --- a/tests/baselines/reference/APISample_compile.types +++ b/tests/baselines/reference/APISample_compile.types @@ -1585,9 +1585,9 @@ declare module "typescript" { >left : Expression >Expression : Expression - operator: SyntaxKind; ->operator : SyntaxKind ->SyntaxKind : SyntaxKind + operatorToken: Node; +>operatorToken : Node +>Node : Node right: Expression; >right : Expression diff --git a/tests/baselines/reference/APISample_linter.js b/tests/baselines/reference/APISample_linter.js index 8bd19aaf26d..3b21fee8fdc 100644 --- a/tests/baselines/reference/APISample_linter.js +++ b/tests/baselines/reference/APISample_linter.js @@ -39,7 +39,7 @@ export function delint(sourceFile: ts.SourceFile) { break; case ts.SyntaxKind.BinaryExpression: - var op = (node).operator; + var op = (node).operatorToken.kind; if (op === ts.SyntaxKind.EqualsEqualsToken || op === ts.SyntaxKind.ExclamationEqualsToken) { report(node, "Use '===' and '!=='.") @@ -558,7 +558,7 @@ declare module "typescript" { } interface BinaryExpression extends Expression { left: Expression; - operator: SyntaxKind; + operatorToken: Node; right: Expression; } interface ConditionalExpression extends Expression { @@ -2003,7 +2003,7 @@ function delint(sourceFile) { } break; case 165 /* BinaryExpression */: - var op = node.operator; + var op = node.operatorToken.kind; if (op === 28 /* EqualsEqualsToken */ || op === 29 /* ExclamationEqualsToken */) { report(node, "Use '===' and '!=='."); } diff --git a/tests/baselines/reference/APISample_linter.types b/tests/baselines/reference/APISample_linter.types index 1b32e59e8cb..838a09e484a 100644 --- a/tests/baselines/reference/APISample_linter.types +++ b/tests/baselines/reference/APISample_linter.types @@ -173,15 +173,17 @@ export function delint(sourceFile: ts.SourceFile) { >SyntaxKind : typeof ts.SyntaxKind >BinaryExpression : ts.SyntaxKind - var op = (node).operator; + var op = (node).operatorToken.kind; >op : ts.SyntaxKind ->(node).operator : ts.SyntaxKind +>(node).operatorToken.kind : ts.SyntaxKind +>(node).operatorToken : ts.Node >(node) : ts.BinaryExpression >node : ts.BinaryExpression >ts : unknown >BinaryExpression : ts.BinaryExpression >node : ts.Node ->operator : ts.SyntaxKind +>operatorToken : ts.Node +>kind : ts.SyntaxKind if (op === ts.SyntaxKind.EqualsEqualsToken || op === ts.SyntaxKind.ExclamationEqualsToken) { >op === ts.SyntaxKind.EqualsEqualsToken || op === ts.SyntaxKind.ExclamationEqualsToken : boolean @@ -1729,9 +1731,9 @@ declare module "typescript" { >left : Expression >Expression : Expression - operator: SyntaxKind; ->operator : SyntaxKind ->SyntaxKind : SyntaxKind + operatorToken: Node; +>operatorToken : Node +>Node : Node right: Expression; >right : Expression diff --git a/tests/baselines/reference/APISample_transform.js b/tests/baselines/reference/APISample_transform.js index 1a3fe20fa9a..edb1fcff2cd 100644 --- a/tests/baselines/reference/APISample_transform.js +++ b/tests/baselines/reference/APISample_transform.js @@ -559,7 +559,7 @@ declare module "typescript" { } interface BinaryExpression extends Expression { left: Expression; - operator: SyntaxKind; + operatorToken: Node; right: Expression; } interface ConditionalExpression extends Expression { diff --git a/tests/baselines/reference/APISample_transform.types b/tests/baselines/reference/APISample_transform.types index 59ce6e8c2b7..2bede1d6047 100644 --- a/tests/baselines/reference/APISample_transform.types +++ b/tests/baselines/reference/APISample_transform.types @@ -1681,9 +1681,9 @@ declare module "typescript" { >left : Expression >Expression : Expression - operator: SyntaxKind; ->operator : SyntaxKind ->SyntaxKind : SyntaxKind + operatorToken: Node; +>operatorToken : Node +>Node : Node right: Expression; >right : Expression diff --git a/tests/baselines/reference/APISample_watcher.js b/tests/baselines/reference/APISample_watcher.js index ce86fbdeb52..89c8a4a5a6a 100644 --- a/tests/baselines/reference/APISample_watcher.js +++ b/tests/baselines/reference/APISample_watcher.js @@ -596,7 +596,7 @@ declare module "typescript" { } interface BinaryExpression extends Expression { left: Expression; - operator: SyntaxKind; + operatorToken: Node; right: Expression; } interface ConditionalExpression extends Expression { diff --git a/tests/baselines/reference/APISample_watcher.types b/tests/baselines/reference/APISample_watcher.types index 8348d7360c1..338e91b167d 100644 --- a/tests/baselines/reference/APISample_watcher.types +++ b/tests/baselines/reference/APISample_watcher.types @@ -1854,9 +1854,9 @@ declare module "typescript" { >left : Expression >Expression : Expression - operator: SyntaxKind; ->operator : SyntaxKind ->SyntaxKind : SyntaxKind + operatorToken: Node; +>operatorToken : Node +>Node : Node right: Expression; >right : Expression diff --git a/tests/cases/compiler/APISample_linter.ts b/tests/cases/compiler/APISample_linter.ts index c95d6273d4e..2c425a4cf23 100644 --- a/tests/cases/compiler/APISample_linter.ts +++ b/tests/cases/compiler/APISample_linter.ts @@ -39,7 +39,7 @@ export function delint(sourceFile: ts.SourceFile) { break; case ts.SyntaxKind.BinaryExpression: - var op = (node).operator; + var op = (node).operatorToken.kind; if (op === ts.SyntaxKind.EqualsEqualsToken || op === ts.SyntaxKind.ExclamationEqualsToken) { report(node, "Use '===' and '!=='.") diff --git a/tests/cases/unittests/incrementalParser.ts b/tests/cases/unittests/incrementalParser.ts index 7c4213e22c2..54adcaf345f 100644 --- a/tests/cases/unittests/incrementalParser.ts +++ b/tests/cases/unittests/incrementalParser.ts @@ -248,7 +248,7 @@ module ts { var oldText = ScriptSnapshot.fromString(source); var newTextAndChange = withChange(oldText, index, 2, "+"); - compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 21); + compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 24); }); it('Strict mode 1',() => {