mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Emit and check for safe navigation
This commit is contained in:
+91
-39
@@ -8917,6 +8917,32 @@ namespace ts {
|
||||
return strictNullChecks ? getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type;
|
||||
}
|
||||
|
||||
function getNullPropagatingType(type: Type) {
|
||||
return strictNullChecks && (getTypeFacts(type) & TypeFacts.EQUndefinedOrNull) === 0
|
||||
? neverType
|
||||
: undefinedType;
|
||||
}
|
||||
|
||||
function propagateNullType(type: Type, propagatingType: Type) {
|
||||
return propagatingType === neverType ? type : getUnionType([type, propagatingType]);
|
||||
}
|
||||
|
||||
function propagateNullReturnType(signature: Signature, propagatingType: Type) {
|
||||
if (propagatingType === neverType) {
|
||||
return signature;
|
||||
}
|
||||
|
||||
const returnType = getReturnTypeOfSignature(signature);
|
||||
const propagatingReturnType = propagateNullType(returnType, propagatingType);
|
||||
if (returnType === propagatingReturnType) {
|
||||
return signature;
|
||||
}
|
||||
|
||||
signature = cloneSignature(signature);
|
||||
signature.resolvedReturnType = propagatingReturnType;
|
||||
return signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if type was inferred from an object literal or written as an object type literal
|
||||
* with no call or construct signatures.
|
||||
@@ -13096,8 +13122,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
function checkNonNullType(type: Type, errorNode: Node): Type {
|
||||
const kind = (strictNullChecks ? getFalsyFlags(type) : type.flags) & TypeFlags.Nullable;
|
||||
if (kind) {
|
||||
const kind = (strictNullChecks ? getFalsyFlags(type) : type.flags);
|
||||
if (kind & TypeFlags.Nullable) {
|
||||
error(errorNode, kind & TypeFlags.Undefined ? kind & TypeFlags.Null ?
|
||||
Diagnostics.Object_is_possibly_null_or_undefined :
|
||||
Diagnostics.Object_is_possibly_undefined :
|
||||
@@ -13156,7 +13182,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) {
|
||||
const type = checkNonNullExpression(left);
|
||||
const propagateNull = node.flags & NodeFlags.PropagateNull;
|
||||
const objectType = propagateNull ? checkExpression(left) : checkNonNullExpression(left);
|
||||
const type = propagateNull ? getNonNullableType(objectType) : objectType;
|
||||
const propagatingType = propagateNull ? getNullPropagatingType(objectType) : neverType;
|
||||
|
||||
if (isTypeAny(type) || type === silentNeverType) {
|
||||
return type;
|
||||
}
|
||||
@@ -13205,10 +13235,10 @@ namespace ts {
|
||||
if (node.kind !== SyntaxKind.PropertyAccessExpression || assignmentKind === AssignmentKind.Definite ||
|
||||
!(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) &&
|
||||
!(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) {
|
||||
return propType;
|
||||
return propagateNullType(propType, propagatingType);
|
||||
}
|
||||
const flowType = getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true, /*flowContainer*/ undefined);
|
||||
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
|
||||
return propagateNullType(assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType, propagatingType);
|
||||
}
|
||||
|
||||
function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean {
|
||||
@@ -13279,7 +13309,10 @@ namespace ts {
|
||||
function checkIndexedAccess(node: ElementAccessExpression): Type {
|
||||
checkGrammarNullPropagation(node);
|
||||
|
||||
const objectType = checkNonNullExpression(node.expression);
|
||||
const propagateNull = node.flags & NodeFlags.PropagateNull;
|
||||
const objectType = propagateNull ? checkExpression(node.expression) : checkNonNullExpression(node.expression);
|
||||
const type = propagateNull ? getNonNullableType(objectType) : objectType;
|
||||
const propagatingType = propagateNull ? getNullPropagatingType(objectType) : neverType;
|
||||
|
||||
const indexExpression = node.argumentExpression;
|
||||
if (!indexExpression) {
|
||||
@@ -13299,16 +13332,16 @@ namespace ts {
|
||||
|
||||
const indexType = isForInVariableForNumericPropertyNames(indexExpression) ? numberType : checkExpression(indexExpression);
|
||||
|
||||
if (objectType === unknownType || objectType === silentNeverType) {
|
||||
return objectType;
|
||||
if (type === unknownType || type === silentNeverType) {
|
||||
return type;
|
||||
}
|
||||
|
||||
if (isConstEnumObjectType(objectType) && indexExpression.kind !== SyntaxKind.StringLiteral) {
|
||||
if (isConstEnumObjectType(type) && indexExpression.kind !== SyntaxKind.StringLiteral) {
|
||||
error(indexExpression, Diagnostics.A_const_enum_member_can_only_be_accessed_using_a_string_literal);
|
||||
return unknownType;
|
||||
}
|
||||
|
||||
return checkIndexedAccessIndexType(getIndexedAccessType(objectType, indexType, node), node);
|
||||
return propagateNullType(checkIndexedAccessIndexType(getIndexedAccessType(type, indexType, node), node), propagatingType);
|
||||
}
|
||||
|
||||
function checkThatExpressionIsProperSymbolReference(expression: Expression, expressionType: Type, reportError: boolean): boolean {
|
||||
@@ -14048,7 +14081,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function resolveCall(node: CallLikeExpression, signatures: Signature[], candidatesOutArray: Signature[], headMessage?: DiagnosticMessage): Signature {
|
||||
function resolveCall(node: CallLikeExpression, signatures: Signature[], candidatesOutArray: Signature[], propagatingType?: Type, headMessage?: DiagnosticMessage): Signature {
|
||||
const isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression;
|
||||
const isDecorator = node.kind === SyntaxKind.Decorator;
|
||||
const isJsxOpeningOrSelfClosingElement = isJsxOpeningLikeElement(node);
|
||||
@@ -14154,7 +14187,7 @@ namespace ts {
|
||||
result = chooseOverload(candidates, assignableRelation, signatureHelpTrailingComma);
|
||||
}
|
||||
if (result) {
|
||||
return result;
|
||||
return propagateNullReturnType(result, propagatingType || neverType);
|
||||
}
|
||||
|
||||
// No signatures were applicable. Now report errors based on the last applicable signature with
|
||||
@@ -14209,7 +14242,7 @@ namespace ts {
|
||||
if (candidate.typeParameters && typeArguments) {
|
||||
candidate = getSignatureInstantiation(candidate, map(typeArguments, getTypeFromTypeNode));
|
||||
}
|
||||
return candidate;
|
||||
return propagateNullReturnType(candidate, propagatingType || neverType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14309,12 +14342,15 @@ namespace ts {
|
||||
return resolveUntypedCall(node);
|
||||
}
|
||||
|
||||
const funcType = checkNonNullExpression(node.expression);
|
||||
if (funcType === silentNeverType) {
|
||||
|
||||
const propagateNull = node.flags & NodeFlags.PropagateNull;
|
||||
const funcType = propagateNull ? checkExpression(node.expression) : checkNonNullExpression(node.expression);
|
||||
const type = propagateNull ? getNonNullableType(funcType) : funcType;
|
||||
if (type === silentNeverType) {
|
||||
return silentNeverSignature;
|
||||
}
|
||||
const apparentType = getApparentType(funcType);
|
||||
|
||||
const apparentType = getApparentType(type);
|
||||
if (apparentType === unknownType) {
|
||||
// Another error has already been reported
|
||||
return resolveErrorCall(node);
|
||||
@@ -14330,10 +14366,10 @@ namespace ts {
|
||||
// TS 1.0 Spec: 4.12
|
||||
// In an untyped function call no TypeArgs are permitted, Args can be any argument list, no contextual
|
||||
// types are provided for the argument expressions, and the result is always of type Any.
|
||||
if (isUntypedFunctionCall(funcType, apparentType, callSignatures.length, constructSignatures.length)) {
|
||||
if (isUntypedFunctionCall(type, apparentType, callSignatures.length, constructSignatures.length)) {
|
||||
// The unknownType indicates that an error already occurred (and was reported). No
|
||||
// need to report another error in this case.
|
||||
if (funcType !== unknownType && node.typeArguments) {
|
||||
if (type !== unknownType && node.typeArguments) {
|
||||
error(node, Diagnostics.Untyped_function_calls_may_not_accept_type_arguments);
|
||||
}
|
||||
return resolveUntypedCall(node);
|
||||
@@ -14343,14 +14379,16 @@ namespace ts {
|
||||
// with multiple call signatures.
|
||||
if (!callSignatures.length) {
|
||||
if (constructSignatures.length) {
|
||||
error(node, Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, typeToString(funcType));
|
||||
error(node, Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, typeToString(type));
|
||||
}
|
||||
else {
|
||||
error(node, Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures, typeToString(apparentType));
|
||||
}
|
||||
return resolveErrorCall(node);
|
||||
}
|
||||
return resolveCall(node, callSignatures, candidatesOutArray);
|
||||
|
||||
const propagatingType = propagateNull ? getNullPropagatingType(funcType) : neverType;
|
||||
return resolveCall(node, callSignatures, candidatesOutArray, propagatingType);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -14384,8 +14422,10 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
let expressionType = checkNonNullExpression(node.expression);
|
||||
if (expressionType === silentNeverType) {
|
||||
const propagateNull = node.flags & NodeFlags.PropagateNull;
|
||||
const funcType = propagateNull ? checkExpression(node.expression) : checkNonNullExpression(node.expression);
|
||||
const type = propagateNull ? getNonNullableType(funcType) : funcType;
|
||||
if (type === silentNeverType) {
|
||||
return silentNeverSignature;
|
||||
}
|
||||
|
||||
@@ -14394,8 +14434,8 @@ namespace ts {
|
||||
// function call, but using the construct signatures as the initial set of candidate
|
||||
// signatures for overload resolution. The result type of the function call becomes
|
||||
// the result type of the operation.
|
||||
expressionType = getApparentType(expressionType);
|
||||
if (expressionType === unknownType) {
|
||||
const apparentType = getApparentType(type);
|
||||
if (apparentType === unknownType) {
|
||||
// Another error has already been reported
|
||||
return resolveErrorCall(node);
|
||||
}
|
||||
@@ -14404,7 +14444,7 @@ namespace ts {
|
||||
// Note, only class declarations can be declared abstract.
|
||||
// In the case of a merged class-module or class-interface declaration,
|
||||
// only the class declaration node will have the Abstract flag set.
|
||||
const valueDecl = expressionType.symbol && getClassLikeDeclarationOfSymbol(expressionType.symbol);
|
||||
const valueDecl = apparentType.symbol && getClassLikeDeclarationOfSymbol(apparentType.symbol);
|
||||
if (valueDecl && getModifierFlags(valueDecl) & ModifierFlags.Abstract) {
|
||||
error(node, Diagnostics.Cannot_create_an_instance_of_the_abstract_class_0, declarationNameToString(valueDecl.name));
|
||||
return resolveErrorCall(node);
|
||||
@@ -14413,7 +14453,7 @@ namespace ts {
|
||||
// TS 1.0 spec: 4.11
|
||||
// If expressionType is of type Any, Args can be any argument
|
||||
// list and the result of the operation is of type Any.
|
||||
if (isTypeAny(expressionType)) {
|
||||
if (isTypeAny(apparentType)) {
|
||||
if (node.typeArguments) {
|
||||
error(node, Diagnostics.Untyped_function_calls_may_not_accept_type_arguments);
|
||||
}
|
||||
@@ -14424,19 +14464,21 @@ namespace ts {
|
||||
// but we are not including construct signatures that may have been added to the Object or
|
||||
// Function interface, since they have none by default. This is a bit of a leap of faith
|
||||
// that the user will not add any.
|
||||
const constructSignatures = getSignaturesOfType(expressionType, SignatureKind.Construct);
|
||||
const constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct);
|
||||
if (constructSignatures.length) {
|
||||
if (!isConstructorAccessible(node, constructSignatures[0])) {
|
||||
return resolveErrorCall(node);
|
||||
}
|
||||
return resolveCall(node, constructSignatures, candidatesOutArray);
|
||||
|
||||
const propagatingType = propagateNull ? getNullPropagatingType(funcType) : neverType;
|
||||
return resolveCall(node, constructSignatures, candidatesOutArray, propagatingType);
|
||||
}
|
||||
|
||||
// If expressionType's apparent type is an object type with no construct signatures but
|
||||
// one or more call signatures, the expression is processed as a function call. A compile-time
|
||||
// error occurs if the result of the function call is not Void. The type of the result of the
|
||||
// operation is Any. It is an error to have a Void this type.
|
||||
const callSignatures = getSignaturesOfType(expressionType, SignatureKind.Call);
|
||||
const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call);
|
||||
if (callSignatures.length) {
|
||||
const signature = resolveCall(node, callSignatures, candidatesOutArray);
|
||||
if (getReturnTypeOfSignature(signature) !== voidType) {
|
||||
@@ -14566,7 +14608,7 @@ namespace ts {
|
||||
return resolveErrorCall(node);
|
||||
}
|
||||
|
||||
return resolveCall(node, callSignatures, candidatesOutArray, headMessage);
|
||||
return resolveCall(node, callSignatures, candidatesOutArray, /*propagatingType*/ undefined, headMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -15405,13 +15447,15 @@ namespace ts {
|
||||
booleanType;
|
||||
case SyntaxKind.PlusPlusToken:
|
||||
case SyntaxKind.MinusMinusToken:
|
||||
const ok = checkArithmeticOperandType(node.operand, checkNonNullType(operandType, node.operand),
|
||||
Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_or_an_enum_type);
|
||||
const propagateNull = node.operand.flags & NodeFlags.PropagateNull;
|
||||
const nonNullType = propagateNull ? getNonNullableType(operandType) : checkNonNullType(operandType, node.operand);
|
||||
const propagatingType = propagateNull ? getNullPropagatingType(operandType) : neverType;
|
||||
const ok = checkArithmeticOperandType(node.operand, nonNullType, Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_or_an_enum_type);
|
||||
if (ok) {
|
||||
// run check only if former checks succeeded to avoid reporting cascading errors
|
||||
checkReferenceExpression(node.operand, Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access);
|
||||
}
|
||||
return numberType;
|
||||
return propagateNullType(numberType, propagatingType);
|
||||
}
|
||||
return unknownType;
|
||||
}
|
||||
@@ -15421,13 +15465,15 @@ namespace ts {
|
||||
if (operandType === silentNeverType) {
|
||||
return silentNeverType;
|
||||
}
|
||||
const ok = checkArithmeticOperandType(node.operand, checkNonNullType(operandType, node.operand),
|
||||
Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_or_an_enum_type);
|
||||
const propagateNull = node.operand.flags & NodeFlags.PropagateNull;
|
||||
const nonNullType = propagateNull ? getNonNullableType(operandType) : checkNonNullType(operandType, node.operand);
|
||||
const propagatingType = propagateNull ? getNullPropagatingType(operandType) : neverType;
|
||||
const ok = checkArithmeticOperandType(node.operand, nonNullType, Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_or_an_enum_type);
|
||||
if (ok) {
|
||||
// run check only if former checks succeeded to avoid reporting cascading errors
|
||||
checkReferenceExpression(node.operand, Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access);
|
||||
}
|
||||
return numberType;
|
||||
return propagateNullType(numberType, propagatingType);
|
||||
}
|
||||
|
||||
// Return true if type might be of the given kind. A union or intersection type might be of a given
|
||||
@@ -15772,6 +15818,12 @@ namespace ts {
|
||||
}
|
||||
let leftType = checkExpression(left, contextualMapper);
|
||||
let rightType = checkExpression(right, contextualMapper);
|
||||
let propagateNull = operator >= SyntaxKind.FirstAssignment && operator <= SyntaxKind.LastAssignment && left.flags & NodeFlags.PropagateNull;
|
||||
let propagatingType = propagateNull ? getNullPropagatingType(leftType) : neverType;
|
||||
if (propagateNull) {
|
||||
leftType = getNonNullableType(leftType);
|
||||
}
|
||||
|
||||
switch (operator) {
|
||||
case SyntaxKind.AsteriskToken:
|
||||
case SyntaxKind.AsteriskAsteriskToken:
|
||||
@@ -15819,7 +15871,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
return numberType;
|
||||
return propagateNullType(numberType, propagatingType);
|
||||
case SyntaxKind.PlusToken:
|
||||
case SyntaxKind.PlusEqualsToken:
|
||||
if (leftType === silentNeverType || rightType === silentNeverType) {
|
||||
@@ -15850,7 +15902,7 @@ namespace ts {
|
||||
|
||||
// Symbols are not allowed at all in arithmetic expressions
|
||||
if (resultType && !checkForDisallowedESSymbolOperand(operator)) {
|
||||
return resultType;
|
||||
return propagateNullType(resultType, propagatingType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15862,7 +15914,7 @@ namespace ts {
|
||||
if (operator === SyntaxKind.PlusEqualsToken) {
|
||||
checkAssignmentOperator(resultType);
|
||||
}
|
||||
return resultType;
|
||||
return propagateNullType(resultType, propagatingType);
|
||||
case SyntaxKind.LessThanToken:
|
||||
case SyntaxKind.GreaterThanToken:
|
||||
case SyntaxKind.LessThanEqualsToken:
|
||||
|
||||
+153
-76
@@ -1739,7 +1739,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function createAssignment(left: ObjectLiteralExpression | ArrayLiteralExpression, right: Expression): DestructuringAssignment;
|
||||
export function createAssignment(left: Expression, right: Expression): BinaryExpression;
|
||||
export function createAssignment(left: Expression, right: Expression): AssignmentExpression<EqualsToken>;
|
||||
export function createAssignment(left: Expression, right: Expression) {
|
||||
return createBinary(left, SyntaxKind.EqualsToken, right);
|
||||
}
|
||||
@@ -1748,6 +1748,10 @@ namespace ts {
|
||||
return createBinary(left, SyntaxKind.EqualsEqualsToken, right);
|
||||
}
|
||||
|
||||
export function createInequality(left: Expression, right: Expression) {
|
||||
return createBinary(left, SyntaxKind.ExclamationEqualsToken, right);
|
||||
}
|
||||
|
||||
export function createStrictEquality(left: Expression, right: Expression) {
|
||||
return createBinary(left, SyntaxKind.EqualsEqualsEqualsToken, right);
|
||||
}
|
||||
@@ -2398,26 +2402,30 @@ namespace ts {
|
||||
return updated;
|
||||
}
|
||||
|
||||
export interface CallBinding {
|
||||
target: LeftHandSideExpression;
|
||||
thisArg: Expression;
|
||||
}
|
||||
|
||||
function shouldBeCapturedInTempVariable(node: Expression, cacheIdentifiers: boolean): boolean {
|
||||
const target = skipParentheses(node);
|
||||
/**
|
||||
* Determines whether an expression should be captured in a temp variable as part of an
|
||||
* emit transformation where side-effects could be observed.
|
||||
* @param node The expression to be tested.
|
||||
* @param captureIdentifiers A value that indicates whether identifiers should be captured
|
||||
* as well.
|
||||
*/
|
||||
function shouldBeCapturedInTempVariable(node: Expression, captureIdentifiers?: boolean): boolean {
|
||||
const target = skipOuterExpressions(node);
|
||||
switch (target.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
return cacheIdentifiers;
|
||||
return captureIdentifiers;
|
||||
case SyntaxKind.ThisKeyword:
|
||||
case SyntaxKind.NullKeyword:
|
||||
case SyntaxKind.TrueKeyword:
|
||||
case SyntaxKind.FalseKeyword:
|
||||
case SyntaxKind.NumericLiteral:
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
return false;
|
||||
case SyntaxKind.VoidExpression:
|
||||
return (<VoidExpression>node).expression.kind === SyntaxKind.NumericLiteral;
|
||||
case SyntaxKind.ArrayLiteralExpression:
|
||||
const elements = (<ArrayLiteralExpression>target).elements;
|
||||
if (elements.length === 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return (<ArrayLiteralExpression>target).elements.length > 0;
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
return (<ObjectLiteralExpression>target).properties.length > 0;
|
||||
default:
|
||||
@@ -2425,82 +2433,151 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function createCallBinding(expression: Expression, recordTempVariable: (temp: Identifier) => void, languageVersion?: ScriptTarget, cacheIdentifiers?: boolean): CallBinding {
|
||||
const callee = skipOuterExpressions(expression, OuterExpressionKinds.All);
|
||||
/**
|
||||
* Captures an expression in a temp variable.
|
||||
* @param expression The expression to capture.
|
||||
* @param recordTempVariable A callback used to record new temp variables.
|
||||
* @param expressions The array of expressions into which capturing assignments should be added.
|
||||
*/
|
||||
export function captureExpression(expression: Expression, recordTempVariable: (temp: Identifier) => void, expressions: Expression[]): Expression {
|
||||
const temp = setTextRange(createTempVariable(recordTempVariable), expression);
|
||||
expressions.push(setTextRange(createAssignment(temp, expression), expression));
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures an expression in a temp variable if it possibly contains side-effects.
|
||||
* @param expression The expression to capture.
|
||||
* @param recordTempVariable A callback used to record new temp variables.
|
||||
* @param expressions The array of expressions into which capturing assignments should be added.
|
||||
* @param captureIdentifiers A value indicating whether identifiers should be captured as well.
|
||||
*/
|
||||
export function captureExpressionIfNeeded(expression: Expression, recordTempVariable: (temp: Identifier) => void, expressions: Expression[], captureIdentifiers?: boolean): Expression {
|
||||
if (shouldBeCapturedInTempVariable(expression, captureIdentifiers)) {
|
||||
return captureExpression(expression, recordTempVariable, expressions);
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
|
||||
// https://tc39.github.io/ecma262/#sec-reference-specification-type
|
||||
export interface Reference {
|
||||
baseValue: Expression;
|
||||
reference: PropertyAccessExpression | ElementAccessExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an approximation of an ECMAScript `Reference` specification type.
|
||||
* @param lhsReference The property or element access expression from which to create the reference.
|
||||
* @param recordTempVariable A callback used to record new temp variables.
|
||||
* @param expressions The array of expressions into which capturing assignments should be added.
|
||||
*/
|
||||
export function createReference(lhsReference: PropertyAccessExpression | ElementAccessExpression, recordTempVariable: (temp: Identifier) => void, expressions: Expression[]): Reference {
|
||||
const baseValue = captureExpressionIfNeeded(lhsReference.expression, recordTempVariable, expressions);
|
||||
let reference: PropertyAccessExpression | ElementAccessExpression;
|
||||
if (isPropertyAccessExpression(lhsReference)) {
|
||||
reference = setTextRange(
|
||||
createPropertyAccess(
|
||||
baseValue,
|
||||
lhsReference.name
|
||||
),
|
||||
lhsReference
|
||||
);
|
||||
}
|
||||
else {
|
||||
reference = setTextRange(
|
||||
createElementAccess(
|
||||
baseValue,
|
||||
lhsReference.argumentExpression
|
||||
),
|
||||
lhsReference
|
||||
);
|
||||
}
|
||||
return { baseValue, reference };
|
||||
}
|
||||
|
||||
export interface CallBinding {
|
||||
target: LeftHandSideExpression;
|
||||
thisArg: Expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a `CallBinding` for an expression that contains a reference to an expression to call
|
||||
* and the `this` to be used for the call.
|
||||
* @param expression The callee of a CallExpression.
|
||||
* @param recordTempVariable A callback used to record new temp variables.
|
||||
* @param languageVersion An optional `ScriptTarget` used to control the behavior of a `super` callee.
|
||||
* @param captureIdentifiers A value indicating whether identifiers should be captured as well.
|
||||
*/
|
||||
export function createCallBinding(expression: Expression, recordTempVariable: (temp: Identifier) => void, languageVersion?: ScriptTarget, captureIdentifiers?: boolean): CallBinding {
|
||||
const callee = skipOuterExpressions(expression);
|
||||
let thisArg: Expression;
|
||||
let target: LeftHandSideExpression;
|
||||
if (isSuperProperty(callee)) {
|
||||
thisArg = createThis();
|
||||
target = callee;
|
||||
}
|
||||
else if (callee.kind === SyntaxKind.SuperKeyword) {
|
||||
else if (isSuper(callee)) {
|
||||
thisArg = createThis();
|
||||
target = languageVersion < ScriptTarget.ES2015
|
||||
? setTextRange(createIdentifier("_super"), callee)
|
||||
: <PrimaryExpression>callee;
|
||||
: callee;
|
||||
}
|
||||
else if (getEmitFlags(callee) & EmitFlags.HelperName) {
|
||||
thisArg = createVoidZero();
|
||||
target = parenthesizeForAccess(callee);
|
||||
}
|
||||
else {
|
||||
switch (callee.kind) {
|
||||
case SyntaxKind.PropertyAccessExpression: {
|
||||
if (shouldBeCapturedInTempVariable((<PropertyAccessExpression>callee).expression, cacheIdentifiers)) {
|
||||
// for `a.b()` target is `(_a = a).b` and thisArg is `_a`
|
||||
thisArg = createTempVariable(recordTempVariable);
|
||||
target = createPropertyAccess(
|
||||
setTextRange(
|
||||
createAssignment(
|
||||
thisArg,
|
||||
(<PropertyAccessExpression>callee).expression
|
||||
),
|
||||
(<PropertyAccessExpression>callee).expression
|
||||
else if (isPropertyAccessExpression(callee)) {
|
||||
if (shouldBeCapturedInTempVariable(callee.expression, captureIdentifiers)) {
|
||||
// for `a.b()` target is `(_a = a).b` and thisArg is `_a`
|
||||
thisArg = createTempVariable(recordTempVariable);
|
||||
target = setTextRange(
|
||||
createPropertyAccess(
|
||||
setTextRange(
|
||||
createAssignment(
|
||||
thisArg,
|
||||
callee.expression
|
||||
),
|
||||
(<PropertyAccessExpression>callee).name,
|
||||
callee.flags & NodeFlags.PropagateNull
|
||||
);
|
||||
setTextRange(target, callee);
|
||||
}
|
||||
else {
|
||||
thisArg = (<PropertyAccessExpression>callee).expression;
|
||||
target = <PropertyAccessExpression>callee;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SyntaxKind.ElementAccessExpression: {
|
||||
if (shouldBeCapturedInTempVariable((<ElementAccessExpression>callee).expression, cacheIdentifiers)) {
|
||||
// for `a[b]()` target is `(_a = a)[b]` and thisArg is `_a`
|
||||
thisArg = createTempVariable(recordTempVariable);
|
||||
target = createElementAccess(
|
||||
setTextRange(
|
||||
createAssignment(
|
||||
thisArg,
|
||||
(<ElementAccessExpression>callee).expression
|
||||
),
|
||||
(<ElementAccessExpression>callee).expression
|
||||
),
|
||||
(<ElementAccessExpression>callee).argumentExpression,
|
||||
callee.flags & NodeFlags.PropagateNull
|
||||
);
|
||||
setTextRange(target, callee);
|
||||
}
|
||||
else {
|
||||
thisArg = (<ElementAccessExpression>callee).expression;
|
||||
target = <ElementAccessExpression>callee;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
// for `a()` target is `a` and thisArg is `void 0`
|
||||
thisArg = createVoidZero();
|
||||
target = parenthesizeForAccess(expression);
|
||||
break;
|
||||
}
|
||||
callee.expression
|
||||
),
|
||||
callee.name,
|
||||
callee.flags & NodeFlags.PropagateNull
|
||||
),
|
||||
callee
|
||||
);
|
||||
}
|
||||
else {
|
||||
thisArg = callee.expression;
|
||||
target = callee;
|
||||
}
|
||||
}
|
||||
else if (isElementAccessExpression(callee)) {
|
||||
if (shouldBeCapturedInTempVariable(callee.expression, captureIdentifiers)) {
|
||||
// for `a[b]()` target is `(_a = a)[b]` and thisArg is `_a`
|
||||
thisArg = createTempVariable(recordTempVariable);
|
||||
target = setTextRange(
|
||||
createElementAccess(
|
||||
setTextRange(
|
||||
createAssignment(
|
||||
thisArg,
|
||||
callee.expression
|
||||
),
|
||||
callee.expression
|
||||
),
|
||||
callee.argumentExpression,
|
||||
callee.flags & NodeFlags.PropagateNull
|
||||
),
|
||||
callee
|
||||
);
|
||||
}
|
||||
else {
|
||||
thisArg = callee.expression;
|
||||
target = callee;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// for `a()` target is `a` and thisArg is `void 0`
|
||||
thisArg = createVoidZero();
|
||||
target = parenthesizeForAccess(expression);
|
||||
}
|
||||
|
||||
return { target, thisArg };
|
||||
@@ -3271,8 +3348,8 @@ namespace ts {
|
||||
export function skipAssertions(node: Expression): Expression;
|
||||
export function skipAssertions(node: Node): Node;
|
||||
export function skipAssertions(node: Node): Node {
|
||||
while (isAssertionExpression(node)) {
|
||||
node = (<AssertionExpression>node).expression;
|
||||
while (isAssertionExpression(node) || node.kind === SyntaxKind.NonNullExpression) {
|
||||
node = (<AssertionExpression | NonNullExpression>node).expression;
|
||||
}
|
||||
|
||||
return node;
|
||||
|
||||
@@ -3509,7 +3509,7 @@ namespace ts {
|
||||
* 5) --UnaryExpression[?Yield]
|
||||
*/
|
||||
if (isUpdateExpression()) {
|
||||
const incrementExpression = parseIncrementExpression();
|
||||
const incrementExpression = parseUpdateExpression();
|
||||
return token() === SyntaxKind.AsteriskAsteriskToken ?
|
||||
<BinaryExpression>parseBinaryExpressionRest(getBinaryOperatorPrecedence(), incrementExpression) :
|
||||
incrementExpression;
|
||||
@@ -3577,7 +3577,7 @@ namespace ts {
|
||||
return parseAwaitExpression();
|
||||
}
|
||||
default:
|
||||
return parseIncrementExpression();
|
||||
return parseUpdateExpression();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3617,9 +3617,9 @@ namespace ts {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse ES7 IncrementExpression. IncrementExpression is used instead of ES6's PostFixExpression.
|
||||
* Parse ES7 UpdateExpression. UpdateExpression is used instead of ES6's PostFixExpression.
|
||||
*
|
||||
* ES7 IncrementExpression[yield]:
|
||||
* ES7 UpdateExpression[yield]:
|
||||
* 1) LeftHandSideExpression[?yield]
|
||||
* 2) LeftHandSideExpression[?yield] [[no LineTerminator here]]++
|
||||
* 3) LeftHandSideExpression[?yield] [[no LineTerminator here]]--
|
||||
@@ -3627,7 +3627,7 @@ namespace ts {
|
||||
* 5) --LeftHandSideExpression[?yield]
|
||||
* In TypeScript (2), (3) are parsed as PostfixUnaryExpression. (4), (5) are parsed as PrefixUnaryExpression
|
||||
*/
|
||||
function parseIncrementExpression(): IncrementExpression {
|
||||
function parseUpdateExpression(): UpdateExpression {
|
||||
if (token() === SyntaxKind.PlusPlusToken || token() === SyntaxKind.MinusMinusToken) {
|
||||
const node = <PrefixUnaryExpression>createNode(SyntaxKind.PrefixUnaryExpression);
|
||||
node.operator = <PrefixUnaryOperator>token();
|
||||
|
||||
@@ -230,6 +230,9 @@ namespace ts {
|
||||
* @param node A BinaryExpression node.
|
||||
*/
|
||||
function visitBinaryExpression(node: BinaryExpression, noDestructuringValue: boolean): Expression {
|
||||
// NOTE: null propagation is not currently handled in destructuring assignments:
|
||||
// [x?.y] = [1] -> [_a] = _b = [1], x == null ? _a : x.y = _a
|
||||
// (or)-> [{ set value(_) { x == null ? _ : x.y = _ }}.value] = [1]
|
||||
if (isDestructuringAssignment(node) && node.left.transformFlags & TransformFlags.ContainsObjectRest) {
|
||||
return flattenDestructuringAssignment(
|
||||
node,
|
||||
@@ -247,19 +250,30 @@ namespace ts {
|
||||
);
|
||||
}
|
||||
|
||||
const left = visitNode(node.left, visitor, isExpression);
|
||||
const right = visitNode(node.right, visitor, isExpression);
|
||||
if (isAssignmentExpression(node)) {
|
||||
const nilReference = getNilReference(left);
|
||||
if (nilReference) {
|
||||
return updateNilReference(
|
||||
nilReference,
|
||||
updateBinary(node, nilReference.whenFalse, right)
|
||||
const referenceExpression = skipOuterExpressions(node.left);
|
||||
if (referenceExpression.flags & NodeFlags.PropagateNull && isPropertyAccessOrElementAccess(referenceExpression)) {
|
||||
// x?.y = 1 -> _a = 1, x == null ? _a : x.y = _a
|
||||
// x?.[y()] = 1 -> _a = y(), _b = 1, x == null ? _b : x[_a] = _b
|
||||
// x.y?.z = 1 -> _a = x.y, _b = 1, _a == null ? _b : _a.z = _b
|
||||
// x.y?.[z()] = 1 -> _a = x.y, _b = z(), _c = 1, _a == null ? _c : _a[_b] = _c
|
||||
const expressions: Expression[] = [];
|
||||
const { baseValue, reference } = createReference(referenceExpression, hoistVariableDeclaration, expressions);
|
||||
const rhsValue = captureExpression(node.right, hoistVariableDeclaration, expressions);
|
||||
expressions.push(
|
||||
setTextRange(
|
||||
createConditional(
|
||||
createEquality(baseValue, createNull()),
|
||||
rhsValue,
|
||||
updateBinary(node, reference, rhsValue)
|
||||
),
|
||||
node
|
||||
)
|
||||
);
|
||||
return visitNode(inlineExpressions(expressions), visitor, isExpression);
|
||||
}
|
||||
}
|
||||
|
||||
return updateBinary(node, left, right);
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -733,38 +747,16 @@ namespace ts {
|
||||
&& node.expression.kind !== SyntaxKind.SuperKeyword;
|
||||
}
|
||||
|
||||
function getNilReference(expression: Expression): ConditionalExpression {
|
||||
expression = skipOuterExpressions(expression);
|
||||
return isConditionalExpression(expression)
|
||||
&& isBinaryExpression(expression.condition)
|
||||
&& expression.condition.operatorToken.kind === SyntaxKind.EqualsEqualsToken
|
||||
&& expression.condition.right.kind === SyntaxKind.NullKeyword
|
||||
&& isVoidZero(expression.whenTrue)
|
||||
? expression
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function updateNilReference(expression: ConditionalExpression, whenNotNil: Expression) {
|
||||
return setTextRange(
|
||||
createConditional(
|
||||
expression.condition,
|
||||
expression.whenTrue,
|
||||
whenNotNil
|
||||
),
|
||||
whenNotNil
|
||||
);
|
||||
}
|
||||
|
||||
function propagateNull<T extends Node>(finishExpression: (node: T, nullableExpression: Expression) => Expression, node: T, nullableExpression: Expression): Expression;
|
||||
function propagateNull<T extends Node, U>(finishExpression: (node: T, nullableExpression: Expression, data: U) => Expression, node: T, nullableExpression: Expression, data: U): Expression;
|
||||
function propagateNull<T extends Node, U>(finishExpression: (node: T, nullableExpression: Expression, data: U) => Expression, node: T, nullableExpression: Expression, data?: U): Expression {
|
||||
function propagateNull<T extends Node>(finishExpression: (node: T, nullableExpression: Expression) => Expression, node: T, referenceExpression: Expression): Expression;
|
||||
function propagateNull<T extends Node, U>(finishExpression: (node: T, nullableExpression: Expression, data: U) => Expression, node: T, referenceExpression: Expression, data: U): Expression;
|
||||
function propagateNull<T extends Node, U>(finishExpression: (node: T, nullableExpression: Expression, data: U) => Expression, node: T, referenceExpression: Expression, data?: U): Expression {
|
||||
if (node.flags & NodeFlags.PropagateNull) {
|
||||
if (isIdentifier(nullableExpression)) {
|
||||
if (isIdentifier(referenceExpression)) {
|
||||
return setTextRange(
|
||||
createConditional(
|
||||
createEquality(nullableExpression, createNull()),
|
||||
createEquality(referenceExpression, createNull()),
|
||||
createVoidZero(),
|
||||
finishExpression(node, nullableExpression, data)
|
||||
finishExpression(node, referenceExpression, data)
|
||||
),
|
||||
node
|
||||
);
|
||||
@@ -774,7 +766,7 @@ namespace ts {
|
||||
return setTextRange(
|
||||
createConditional(
|
||||
createEquality(
|
||||
createAssignment(temp, nullableExpression),
|
||||
createAssignment(temp, referenceExpression),
|
||||
createNull()
|
||||
),
|
||||
createVoidZero(),
|
||||
@@ -784,7 +776,7 @@ namespace ts {
|
||||
);
|
||||
}
|
||||
}
|
||||
return finishExpression(node, nullableExpression, data);
|
||||
return finishExpression(node, referenceExpression, data);
|
||||
}
|
||||
|
||||
function visitCallExpression(node: CallExpression): Expression {
|
||||
@@ -800,30 +792,7 @@ namespace ts {
|
||||
return propagateNull(finishNullableCallExpression, node, visitNode(target, visitor, isExpression), visitNode(thisArg, visitor, isExpression));
|
||||
}
|
||||
|
||||
const expression = visitNode(node.expression, visitor, isExpression);
|
||||
const argumentsArray = visitNodes(node.arguments, visitor, isExpression);
|
||||
const nilReference = getNilReference(expression);
|
||||
if (nilReference) {
|
||||
// NilReference shortcut in expression
|
||||
// x?.y() -> x == null ? void 0 : x.y();
|
||||
// x?.[y]() -> x == null ? void 0 : x[y]();
|
||||
return updateNilReference(
|
||||
nilReference,
|
||||
updateCall(
|
||||
node,
|
||||
nilReference.whenFalse,
|
||||
/*typeArguments*/ undefined,
|
||||
argumentsArray
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return updateCall(
|
||||
node,
|
||||
expression,
|
||||
/*typeArguments*/ undefined,
|
||||
argumentsArray
|
||||
);
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
|
||||
function finishNullableCallExpression(node: CallExpression, target: Expression, thisArg: Expression) {
|
||||
@@ -852,7 +821,6 @@ namespace ts {
|
||||
}
|
||||
|
||||
function visitNewExpression(node: NewExpression): Expression {
|
||||
const expression = visitNode(node.expression, visitor, isExpression);
|
||||
if (isNullPropagatingExpression(node)) {
|
||||
// null propagation in new:
|
||||
// new x?.() -> x == null ? void 0 : new x();
|
||||
@@ -861,31 +829,10 @@ namespace ts {
|
||||
// new x[y]?.() -> (_a = x[y]) == null ? void 0 : new _a();
|
||||
// new (x.y)?.() -> (_a = x.y) == null ? void 0 : new _a();
|
||||
// new (x[y])?.() -> (_a = x[y]) == null ? void 0 : new _a();
|
||||
return propagateNull(finishNullableNewExpression, node, expression);
|
||||
return propagateNull(finishNullableNewExpression, node, visitNode(node.expression, visitor, isExpression));
|
||||
}
|
||||
|
||||
const argumentsArray = visitNodes(node.arguments, visitor, isExpression);
|
||||
const nilReference = getNilReference(expression);
|
||||
if (nilReference) {
|
||||
// NilReference shortcut in expression
|
||||
// new x?.y() -> x == null ? void 0 : new x.y();
|
||||
// new x?.[y]() -> x == null ? void 0 : new x[y]();
|
||||
return updateNilReference(
|
||||
nilReference,
|
||||
updateNew(
|
||||
node,
|
||||
nilReference.whenTrue,
|
||||
/*typeArguments*/ undefined,
|
||||
argumentsArray
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return updateNew(
|
||||
node,
|
||||
expression,
|
||||
/*typeArguments*/ undefined,
|
||||
argumentsArray);
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
|
||||
function finishNullableNewExpression(node: NewExpression, expression: Expression) {
|
||||
@@ -903,35 +850,14 @@ namespace ts {
|
||||
}
|
||||
|
||||
function visitPropertyAccess(node: PropertyAccessExpression): Expression {
|
||||
const expression = visitNode(node.expression, visitor, isExpression);
|
||||
if (isNullPropagatingExpression(node)) {
|
||||
// null propagation in property access
|
||||
// x?.y -> x == null ? void 0 : x.y;
|
||||
// x.y?.z -> (_a = x.y) == null ? void 0 : _a.z;
|
||||
return propagateNull(finishNullablePropertyAccess, node, expression);
|
||||
return propagateNull(finishNullablePropertyAccess, node, visitNode(node.expression, visitor, isExpression));
|
||||
}
|
||||
|
||||
const name = visitNode(node.name, visitor, isIdentifier);
|
||||
const nilReference = getNilReference(expression);
|
||||
if (nilReference) {
|
||||
// NilReference shortcut in expression
|
||||
// x?.y.z -> x == null ? void 0 : x.y.z;
|
||||
// x.y?.z.a -> (_a = x.y) == null ? void 0 : _a.z.a;
|
||||
return updateNilReference(
|
||||
nilReference,
|
||||
updatePropertyAccess(
|
||||
node,
|
||||
nilReference.whenFalse,
|
||||
name
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return updatePropertyAccess(
|
||||
node,
|
||||
expression,
|
||||
name
|
||||
);
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
|
||||
function finishNullablePropertyAccess(node: PropertyAccessExpression, expression: Expression) {
|
||||
@@ -948,35 +874,13 @@ namespace ts {
|
||||
}
|
||||
|
||||
function visitElementAccess(node: ElementAccessExpression): Expression {
|
||||
const expression = visitNode(node.expression, visitor, isExpression);
|
||||
if (isNullPropagatingExpression(node)) {
|
||||
// null propagation in element access
|
||||
// x?.[y] -> x == null ? void 0 : x.[y];
|
||||
// x.y?.[z] -> (_a = x.y) == null ? void 0 : _a.[z];
|
||||
return propagateNull(finishNullableElementAccess, node, expression);
|
||||
return propagateNull(finishNullableElementAccess, node, visitNode(node.expression, visitor, isExpression));
|
||||
}
|
||||
|
||||
const argumentExpression = visitNode(node.argumentExpression, visitor, isExpression);
|
||||
const nilReference = getNilReference(expression);
|
||||
if (nilReference) {
|
||||
// NilReference shortcut in expression
|
||||
// x?.y.[z] -> x == null ? void 0 : x.y.[z];
|
||||
// x.y?.z.[a] -> (_a = x.y) == null ? void 0 : _a.z.[a];
|
||||
return updateNilReference(
|
||||
nilReference,
|
||||
updateElementAccess(
|
||||
node,
|
||||
nilReference.whenFalse,
|
||||
argumentExpression
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return updateElementAccess(
|
||||
node,
|
||||
expression,
|
||||
argumentExpression
|
||||
);
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
|
||||
function finishNullableElementAccess(node: ElementAccessExpression, expression: Expression) {
|
||||
@@ -992,44 +896,44 @@ namespace ts {
|
||||
);
|
||||
}
|
||||
|
||||
function visitDelete(node: DeleteExpression) {
|
||||
const expression = visitNode(node.expression, visitor, isExpression);
|
||||
const nilReference = getNilReference(expression);
|
||||
if (nilReference) {
|
||||
return setTextRange(
|
||||
createConditional(
|
||||
nilReference.condition,
|
||||
createTrue(),
|
||||
updateDelete(node, nilReference.whenFalse)
|
||||
),
|
||||
node
|
||||
function visitUnaryMutationExpression<T extends UnaryExpression>(node: T, referenceExpression: Expression, updateNode: (node: T, referenceExpression: Expression) => T): Expression {
|
||||
if (referenceExpression.flags & NodeFlags.PropagateNull && isPropertyAccessOrElementAccess(referenceExpression)) {
|
||||
const expressions: Expression[] = [];
|
||||
const { baseValue, reference } = createReference(referenceExpression, hoistVariableDeclaration, expressions);
|
||||
expressions.push(
|
||||
setTextRange(
|
||||
createConditional(
|
||||
createEquality(baseValue, createNull()),
|
||||
createVoidZero(),
|
||||
updateNode(
|
||||
node,
|
||||
reference
|
||||
)
|
||||
),
|
||||
node
|
||||
)
|
||||
);
|
||||
return visitNode(inlineExpressions(expressions), visitor, isExpression);
|
||||
}
|
||||
return updateDelete(node, expression);
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
|
||||
function visitDelete(node: DeleteExpression) {
|
||||
return visitUnaryMutationExpression(node, node.expression, updateDelete);
|
||||
}
|
||||
|
||||
function visitPrefix(node: PrefixUnaryExpression) {
|
||||
const operand = visitNode(node.operand, visitor, isExpression);
|
||||
const nilReference = getNilReference(operand);
|
||||
if (nilReference) {
|
||||
return updateNilReference(
|
||||
nilReference,
|
||||
updatePrefix(node, nilReference.whenFalse)
|
||||
);
|
||||
if (isPrefixOrPostfixUpdateExpression(node)) {
|
||||
return visitUnaryMutationExpression(node, node.operand, updatePrefix);
|
||||
}
|
||||
return updatePrefix(node, operand);
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
|
||||
function visitPostfix(node: PostfixUnaryExpression) {
|
||||
const operand = visitNode(node.operand, visitor, isExpression);
|
||||
const nilReference = getNilReference(operand);
|
||||
if (nilReference) {
|
||||
return updateNilReference(
|
||||
nilReference,
|
||||
updatePostfix(node, nilReference.whenFalse)
|
||||
);
|
||||
if (isPrefixOrPostfixUpdateExpression(node)) {
|
||||
return visitUnaryMutationExpression(node, node.operand, updatePostfix);
|
||||
}
|
||||
return updatePostfix(node, operand);
|
||||
return visitEachChild(node, visitor, context);
|
||||
}
|
||||
|
||||
function enableSubstitutionForAsyncMethodsWithSuper() {
|
||||
|
||||
@@ -1120,7 +1120,7 @@ namespace ts {
|
||||
// .mark resumeLabel
|
||||
// _b.apply(_a, _c.concat([%sent%, 2]));
|
||||
|
||||
const { target, thisArg } = createCallBinding(node.expression, hoistVariableDeclaration, languageVersion, /*cacheIdentifiers*/ true);
|
||||
const { target, thisArg } = createCallBinding(node.expression, hoistVariableDeclaration, languageVersion, /*captureIdentifiers*/ true);
|
||||
return setOriginalNode(
|
||||
createFunctionApply(
|
||||
cacheExpression(visitNode(target, visitor, isLeftHandSideExpression)),
|
||||
|
||||
@@ -980,8 +980,8 @@
|
||||
_unaryExpressionBrand: any;
|
||||
}
|
||||
|
||||
export interface IncrementExpression extends UnaryExpression {
|
||||
_incrementExpressionBrand: any;
|
||||
export interface UpdateExpression extends UnaryExpression {
|
||||
_updateExpressionBrand: any;
|
||||
}
|
||||
|
||||
// see: https://tc39.github.io/ecma262/#prod-UpdateExpression
|
||||
@@ -995,25 +995,29 @@
|
||||
| SyntaxKind.ExclamationToken
|
||||
;
|
||||
|
||||
export interface PrefixUnaryExpression extends IncrementExpression {
|
||||
export interface PrefixUnaryExpression extends UpdateExpression {
|
||||
kind: SyntaxKind.PrefixUnaryExpression;
|
||||
operator: PrefixUnaryOperator;
|
||||
operand: UnaryExpression;
|
||||
}
|
||||
|
||||
export interface PrefixUnaryUpdateExpression extends PrefixUnaryExpression {
|
||||
operator: SyntaxKind.PlusPlusToken | SyntaxKind.MinusMinusToken;
|
||||
}
|
||||
|
||||
// see: https://tc39.github.io/ecma262/#prod-UpdateExpression
|
||||
export type PostfixUnaryOperator
|
||||
= SyntaxKind.PlusPlusToken
|
||||
| SyntaxKind.MinusMinusToken
|
||||
;
|
||||
|
||||
export interface PostfixUnaryExpression extends IncrementExpression {
|
||||
export interface PostfixUnaryExpression extends UpdateExpression {
|
||||
kind: SyntaxKind.PostfixUnaryExpression;
|
||||
operand: LeftHandSideExpression;
|
||||
operator: PostfixUnaryOperator;
|
||||
}
|
||||
|
||||
export interface LeftHandSideExpression extends IncrementExpression {
|
||||
export interface LeftHandSideExpression extends UpdateExpression {
|
||||
_leftHandSideExpressionBrand: any;
|
||||
}
|
||||
|
||||
|
||||
@@ -1153,9 +1153,12 @@ namespace ts {
|
||||
* Determines whether a node is a property or element access expression for super.
|
||||
*/
|
||||
export function isSuperProperty(node: Node): node is SuperProperty {
|
||||
const kind = node.kind;
|
||||
return (kind === SyntaxKind.PropertyAccessExpression || kind === SyntaxKind.ElementAccessExpression)
|
||||
&& (<PropertyAccessExpression | ElementAccessExpression>node).expression.kind === SyntaxKind.SuperKeyword;
|
||||
return isPropertyAccessOrElementAccess(node)
|
||||
&& isSuper(node.expression);
|
||||
}
|
||||
|
||||
export function isSuper(node: Node): node is SuperExpression {
|
||||
return node.kind === SyntaxKind.SuperKeyword;
|
||||
}
|
||||
|
||||
export function getEntityNameFromTypeNode(node: TypeNode): EntityNameOrEntityNameExpression {
|
||||
@@ -3104,6 +3107,19 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isPrefixOrPostfixUpdateExpression(node: Node): node is PrefixUnaryUpdateExpression | PostfixUnaryExpression {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.PrefixUnaryExpression:
|
||||
case SyntaxKind.PostfixUnaryExpression:
|
||||
switch ((<PrefixUnaryExpression | PostfixUnaryExpression>node).operator) {
|
||||
case SyntaxKind.PlusPlusToken:
|
||||
case SyntaxKind.MinusMinusToken:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns false if this heritage clause element's expression contains something unsupported
|
||||
// (i.e. not a name or dotted name).
|
||||
export function isSupportedExpressionWithTypeArguments(node: ExpressionWithTypeArguments): boolean {
|
||||
@@ -3805,6 +3821,12 @@ namespace ts {
|
||||
return node.kind === SyntaxKind.ElementAccessExpression;
|
||||
}
|
||||
|
||||
export function isPropertyAccessOrElementAccess(node: Node): node is PropertyAccessExpression | ElementAccessExpression {
|
||||
const kind = node.kind;
|
||||
return kind === SyntaxKind.PropertyAccessExpression
|
||||
|| kind === SyntaxKind.ElementAccessExpression;
|
||||
}
|
||||
|
||||
export function isBinaryExpression(node: Node): node is BinaryExpression {
|
||||
return node.kind === SyntaxKind.BinaryExpression;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user