From 6a4927cef7348bb415c5e25b8577c973052a414f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 29 Nov 2014 13:43:30 -0800 Subject: [PATCH] Provide unique nodes for void/typeof/delete expressions. --- src/compiler/checker.ts | 52 ++++++++++++++++++-------- src/compiler/emitter.ts | 37 ++++++++++++++++-- src/compiler/parser.ts | 64 +++++++++++++++++++++++--------- src/compiler/types.ts | 17 ++++++++- src/harness/typeWriter.ts | 5 ++- src/services/formatting/rules.ts | 2 +- src/services/services.ts | 4 +- 7 files changed, 140 insertions(+), 41 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6afbbcdd510..ded46d3f19b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4333,7 +4333,10 @@ module ts { case SyntaxKind.NewExpression: case SyntaxKind.TypeAssertion: case SyntaxKind.ParenExpression: - case SyntaxKind.PrefixOperator: + case SyntaxKind.PrefixUnaryExpression: + case SyntaxKind.DeleteExpression: + case SyntaxKind.TypeOfExpression: + case SyntaxKind.VoidExpression: case SyntaxKind.PostfixOperator: case SyntaxKind.ConditionalExpression: case SyntaxKind.Block: @@ -4416,12 +4419,12 @@ module ts { return type; function narrowTypeByEquality(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { - var left = expr.left; + var left = expr.left; var right = expr.right; // Check that we have 'typeof ' on the left and string literal on the right - if (left.kind !== SyntaxKind.PrefixOperator || left.operator !== SyntaxKind.TypeOfKeyword || - left.operand.kind !== SyntaxKind.Identifier || right.kind !== SyntaxKind.StringLiteral || - getResolvedSymbol(left.operand) !== symbol) { + if (left.kind !== SyntaxKind.TypeOfExpression || + left.expression.kind !== SyntaxKind.Identifier || right.kind !== SyntaxKind.StringLiteral || + getResolvedSymbol(left.expression) !== symbol) { return type; } var t = right.text; @@ -4510,7 +4513,7 @@ module ts { return narrowTypeByInstanceof(type, expr, assumeTrue); } break; - case SyntaxKind.PrefixOperator: + case SyntaxKind.PrefixUnaryExpression: if ((expr).operator === SyntaxKind.ExclamationToken) { return narrowType(type, (expr).operand, !assumeTrue); } @@ -6211,6 +6214,21 @@ module ts { return true; } + function checkDeleteExpression(node: DeleteExpression): Type { + var operandType = checkExpression(node.expression); + return booleanType; + } + + function checkTypeOfExpression(node: TypeOfExpression): Type { + var operandType = checkExpression(node.expression); + return stringType; + } + + function checkVoidExpression(node: VoidExpression): Type { + var operandType = checkExpression(node.expression); + return undefinedType; + } + function checkPrefixExpression(node: UnaryExpression): Type { var operandType = checkExpression(node.operand); switch (node.operator) { @@ -6219,12 +6237,7 @@ module ts { case SyntaxKind.TildeToken: return numberType; case SyntaxKind.ExclamationToken: - case SyntaxKind.DeleteKeyword: return booleanType; - case SyntaxKind.TypeOfKeyword: - return stringType; - case SyntaxKind.VoidKeyword: - return undefinedType; case SyntaxKind.PlusPlusToken: case SyntaxKind.MinusMinusToken: var ok = checkArithmeticOperandType(node.operand, operandType, Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_or_an_enum_type); @@ -6566,7 +6579,13 @@ module ts { case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: return checkFunctionExpression(node, contextualMapper); - case SyntaxKind.PrefixOperator: + case SyntaxKind.TypeOfExpression: + return checkTypeOfExpression(node); + case SyntaxKind.DeleteExpression: + return checkDeleteExpression(node); + case SyntaxKind.VoidExpression: + return checkVoidExpression(node); + case SyntaxKind.PrefixUnaryExpression: return checkPrefixExpression(node); case SyntaxKind.PostfixOperator: return checkPostfixExpression(node); @@ -8077,7 +8096,7 @@ module ts { function evalConstant(e: Node): number { switch (e.kind) { - case SyntaxKind.PrefixOperator: + case SyntaxKind.PrefixUnaryExpression: var value = evalConstant((e).operand); if (value === undefined) { return undefined; @@ -8478,7 +8497,10 @@ module ts { case SyntaxKind.TaggedTemplateExpression: case SyntaxKind.TypeAssertion: case SyntaxKind.ParenExpression: - case SyntaxKind.PrefixOperator: + case SyntaxKind.TypeOfExpression: + case SyntaxKind.VoidExpression: + case SyntaxKind.DeleteExpression: + case SyntaxKind.PrefixUnaryExpression: case SyntaxKind.PostfixOperator: case SyntaxKind.BinaryExpression: case SyntaxKind.ConditionalExpression: @@ -8705,7 +8727,7 @@ module ts { case SyntaxKind.BooleanKeyword: return true; case SyntaxKind.VoidKeyword: - return node.parent.kind !== SyntaxKind.PrefixOperator; + return node.parent.kind !== SyntaxKind.VoidExpression; case SyntaxKind.StringLiteral: // Specialized signatures can have string literals as their parameters' type names return node.parent.kind === SyntaxKind.Parameter; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 2f573e57af7..a3315bf35c5 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2339,7 +2339,12 @@ module ts { // (typeof A).toString() should be emitted as (typeof A).toString() and not typeof A.toString() // new (A()) should be emitted as new (A()) and not new A() // (function foo() { })() should be emitted as an IIF (function foo(){})() and not declaration function foo(){} () - if (operand.kind !== SyntaxKind.PrefixOperator && operand.kind !== SyntaxKind.PostfixOperator && operand.kind !== SyntaxKind.NewExpression && + if (operand.kind !== SyntaxKind.PrefixUnaryExpression && + operand.kind !== SyntaxKind.VoidExpression && + operand.kind !== SyntaxKind.TypeOfExpression && + operand.kind !== SyntaxKind.DeleteExpression && + operand.kind !== SyntaxKind.PostfixOperator && + operand.kind !== SyntaxKind.NewExpression && !(operand.kind === SyntaxKind.CallExpression && node.parent.kind === SyntaxKind.NewExpression) && !(operand.kind === SyntaxKind.FunctionExpression && node.parent.kind === SyntaxKind.CallExpression)) { emit(operand); @@ -2351,8 +2356,26 @@ module ts { write(")"); } + function emitDeleteExpression(node: DeleteExpression) { + write(tokenToString(SyntaxKind.DeleteKeyword)); + write(" "); + emit(node.expression); + } + + function emitVoidExpression(node: VoidExpression) { + write(tokenToString(SyntaxKind.VoidKeyword)); + write(" "); + emit(node.expression); + } + + function emitTypeOfExpression(node: TypeOfExpression) { + write(tokenToString(SyntaxKind.TypeOfKeyword)); + write(" "); + emit(node.expression); + } + function emitUnaryExpression(node: UnaryExpression) { - if (node.kind === SyntaxKind.PrefixOperator) { + if (node.kind === SyntaxKind.PrefixUnaryExpression) { write(tokenToString(node.operator)); } // In some cases, we need to emit a space between the operator and the operand. One obvious case @@ -2370,7 +2393,7 @@ module ts { if (node.operator >= SyntaxKind.Identifier) { write(" "); } - else if (node.kind === SyntaxKind.PrefixOperator && node.operand.kind === SyntaxKind.PrefixOperator) { + else if (node.kind === SyntaxKind.PrefixUnaryExpression && node.operand.kind === SyntaxKind.PrefixUnaryExpression) { var operand = node.operand; if (node.operator === SyntaxKind.PlusToken && (operand.operator === SyntaxKind.PlusToken || operand.operator === SyntaxKind.PlusPlusToken)) { write(" "); @@ -3500,7 +3523,13 @@ module ts { case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: return emitFunctionDeclaration(node); - case SyntaxKind.PrefixOperator: + case SyntaxKind.DeleteExpression: + return emitDeleteExpression(node); + case SyntaxKind.TypeOfExpression: + return emitTypeOfExpression(node); + case SyntaxKind.VoidExpression: + return emitVoidExpression(node); + case SyntaxKind.PrefixUnaryExpression: case SyntaxKind.PostfixOperator: return emitUnaryExpression(node); case SyntaxKind.BinaryExpression: diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 7b30b9884e4..6651044be02 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -278,7 +278,13 @@ module ts { child((node).operand); case SyntaxKind.ParenExpression: return child((node).expression); - case SyntaxKind.PrefixOperator: + case SyntaxKind.DeleteExpression: + return child((node).expression); + case SyntaxKind.TypeOfExpression: + return child((node).expression); + case SyntaxKind.VoidExpression: + return child((node).expression); + case SyntaxKind.PrefixUnaryExpression: case SyntaxKind.PostfixOperator: return child((node).operand); case SyntaxKind.BinaryExpression: @@ -518,7 +524,10 @@ module ts { case SyntaxKind.ParenExpression: case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: - case SyntaxKind.PrefixOperator: + case SyntaxKind.VoidExpression: + case SyntaxKind.DeleteExpression: + case SyntaxKind.TypeOfExpression: + case SyntaxKind.PrefixUnaryExpression: case SyntaxKind.PostfixOperator: case SyntaxKind.BinaryExpression: case SyntaxKind.ConditionalExpression: @@ -1659,7 +1668,7 @@ module ts { // // // We do *not* want to consume the > as we're consuming the expression for "". - node.expression = parseUnaryExpression(); + node.expression = parseUnaryExpressionOrHigher(); } } @@ -2532,7 +2541,7 @@ module ts { } function parseBinaryExpressionOrHigher(precedence: number): Expression { - var leftOperand = parseUnaryExpression(); + var leftOperand = parseUnaryExpressionOrHigher(); return parseBinaryExpressionRest(precedence, leftOperand); } @@ -2611,21 +2620,33 @@ module ts { return finishNode(node); } - function parseUnaryExpression(): Expression { + function parseUnaryExpressionOrHigher(): Expression { var pos = getNodePos(); switch (token) { case SyntaxKind.PlusToken: case SyntaxKind.MinusToken: case SyntaxKind.TildeToken: case SyntaxKind.ExclamationToken: - case SyntaxKind.DeleteKeyword: - case SyntaxKind.TypeOfKeyword: - case SyntaxKind.VoidKeyword: case SyntaxKind.PlusPlusToken: case SyntaxKind.MinusMinusToken: var operator = token; nextToken(); - return makeUnaryExpression(SyntaxKind.PrefixOperator, pos, operator, parseUnaryExpression()); + return makeUnaryExpression(SyntaxKind.PrefixUnaryExpression, pos, operator, parseUnaryExpressionOrHigher()); + case SyntaxKind.DeleteKeyword: + var node = createNode(SyntaxKind.DeleteExpression); + nextToken(); + node.expression = parseUnaryExpressionOrHigher(); + return finishNode(node); + case SyntaxKind.TypeOfKeyword: + var node = createNode(SyntaxKind.TypeOfExpression); + nextToken(); + node.expression = parseUnaryExpressionOrHigher(); + return finishNode(node); + case SyntaxKind.VoidKeyword: + var node = createNode(SyntaxKind.VoidExpression); + nextToken(); + node.expression = parseUnaryExpressionOrHigher(); + return finishNode(node); case SyntaxKind.LessThanToken: return parseTypeAssertion(); } @@ -2659,7 +2680,7 @@ module ts { parseExpected(SyntaxKind.LessThanToken); node.type = parseType(); parseExpected(SyntaxKind.GreaterThanToken); - node.operand = parseUnaryExpression(); + node.operand = parseUnaryExpressionOrHigher(); return finishNode(node); } @@ -4073,6 +4094,7 @@ module ts { case SyntaxKind.ClassDeclaration: return checkClassDeclaration(node); case SyntaxKind.ComputedPropertyName: return checkComputedPropertyName(node); case SyntaxKind.Constructor: return checkConstructor(node); + case SyntaxKind.DeleteExpression: return checkDeleteExpression( node); case SyntaxKind.ExportAssignment: return checkExportAssignment(node); case SyntaxKind.ForInStatement: return checkForInStatement(node); case SyntaxKind.ForStatement: return checkForStatement(node); @@ -4089,7 +4111,7 @@ module ts { case SyntaxKind.NumericLiteral: return checkNumericLiteral(node); case SyntaxKind.Parameter: return checkParameter(node); case SyntaxKind.PostfixOperator: return checkPostfixOperator(node); - case SyntaxKind.PrefixOperator: return checkPrefixOperator(node); + case SyntaxKind.PrefixUnaryExpression: return checkPrefixOperator(node); case SyntaxKind.Property: return checkProperty(node); case SyntaxKind.PropertyAssignment: return checkPropertyAssignment(node); case SyntaxKind.ReturnStatement: return checkReturnStatement(node); @@ -4355,6 +4377,19 @@ module ts { } } + function checkDeleteExpression(node: DeleteExpression) { + if (node.parserContextFlags & ParserContextFlags.StrictMode) { + // The identifier eval or arguments may not appear as the LeftHandSideExpression of an + // Assignment operator(11.13) or of a PostfixExpression(11.3) or as the UnaryExpression + // operated upon by a Prefix Increment(11.4.4) or a Prefix Decrement(11.4.5) operator + if (node.expression.kind === SyntaxKind.Identifier) { + // When a delete operator occurs within strict mode code, a SyntaxError is thrown if its + // UnaryExpression is a direct reference to a variable, function argument, or function name + return grammarErrorOnNode(node.expression, Diagnostics.delete_cannot_be_called_on_an_identifier_in_strict_mode); + } + } + } + function checkEnumDeclaration(enumDecl: EnumDeclaration): boolean { var enumIsConst = (enumDecl.flags & NodeFlags.Const) !== 0; @@ -4396,7 +4431,7 @@ module ts { return /^[0-9]+([eE]\+?[0-9]+)?$/.test(literalExpression.text); } - if (expression.kind === SyntaxKind.PrefixOperator) { + if (expression.kind === SyntaxKind.PrefixUnaryExpression) { var unaryExpression = expression; if (unaryExpression.operator === SyntaxKind.PlusToken || unaryExpression.operator === SyntaxKind.MinusToken) { expression = unaryExpression.operand; @@ -4872,11 +4907,6 @@ module ts { if ((node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) && isEvalOrArgumentsIdentifier(node.operand)) { return reportInvalidUseInStrictMode(node.operand); } - else if (node.operator === SyntaxKind.DeleteKeyword && node.operand.kind === SyntaxKind.Identifier) { - // When a delete operator occurs within strict mode code, a SyntaxError is thrown if its - // UnaryExpression is a direct reference to a variable, function argument, or function name - return grammarErrorOnNode(node.operand, Diagnostics.delete_cannot_be_called_on_an_identifier_in_strict_mode); - } } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 597d85ccca0..660de6bf33d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -178,7 +178,10 @@ module ts { ParenExpression, FunctionExpression, ArrowFunction, - PrefixOperator, + DeleteExpression, + TypeOfExpression, + VoidExpression, + PrefixUnaryExpression, PostfixOperator, BinaryExpression, ConditionalExpression, @@ -438,6 +441,18 @@ module ts { operator: SyntaxKind; operand: Expression; } + + export interface DeleteExpression extends Expression { + expression: Expression; + } + + export interface TypeOfExpression extends Expression { + expression: Expression; + } + + export interface VoidExpression extends Expression { + expression: Expression; + } export interface YieldExpression extends Expression { asteriskToken?: Node; diff --git a/src/harness/typeWriter.ts b/src/harness/typeWriter.ts index ed8d37c9c7e..8122c639680 100644 --- a/src/harness/typeWriter.ts +++ b/src/harness/typeWriter.ts @@ -39,7 +39,10 @@ class TypeWriterWalker { case ts.SyntaxKind.ParenExpression: case ts.SyntaxKind.FunctionExpression: case ts.SyntaxKind.ArrowFunction: - case ts.SyntaxKind.PrefixOperator: + case ts.SyntaxKind.TypeOfExpression: + case ts.SyntaxKind.VoidExpression: + case ts.SyntaxKind.DeleteExpression: + case ts.SyntaxKind.PrefixUnaryExpression: case ts.SyntaxKind.PostfixOperator: case ts.SyntaxKind.BinaryExpression: case ts.SyntaxKind.ConditionalExpression: diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 2485945df0b..9985fe61b1a 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -673,7 +673,7 @@ module ts.formatting { } static IsVoidOpContext(context: FormattingContext): boolean { - return context.currentTokenSpan.kind === SyntaxKind.VoidKeyword && context.currentTokenParent.kind === SyntaxKind.PrefixOperator; + return context.currentTokenSpan.kind === SyntaxKind.VoidKeyword && context.currentTokenParent.kind === SyntaxKind.VoidExpression; } } } \ No newline at end of file diff --git a/src/services/services.ts b/src/services/services.ts index ed8cb03faee..167bf00c820 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4574,7 +4574,7 @@ module ts { var parent = node.parent; if (parent) { - if (parent.kind === SyntaxKind.PostfixOperator || parent.kind === SyntaxKind.PrefixOperator) { + if (parent.kind === SyntaxKind.PostfixOperator || parent.kind === SyntaxKind.PrefixUnaryExpression) { return true; } else if (parent.kind === SyntaxKind.BinaryExpression && (parent).left === node) { @@ -5063,7 +5063,7 @@ module ts { // the '=' in a variable declaration is special cased here. if (token.parent.kind === SyntaxKind.BinaryExpression || token.parent.kind === SyntaxKind.VariableDeclaration || - token.parent.kind === SyntaxKind.PrefixOperator || + token.parent.kind === SyntaxKind.PrefixUnaryExpression || token.parent.kind === SyntaxKind.PostfixOperator || token.parent.kind === SyntaxKind.ConditionalExpression) { return ClassificationTypeNames.operator;