Parse and Emit for safe navigation

This commit is contained in:
Ron Buckton 2017-02-22 15:17:52 -08:00
parent 359823b4be
commit d9fceab72d
9 changed files with 487 additions and 54 deletions

View File

@ -2610,6 +2610,10 @@ namespace ts {
transformFlags |= TransformFlags.AssertTypeScript;
}
if (node.flags & NodeFlags.PropagateNull) {
transformFlags |= TransformFlags.AssertESNext;
}
if (subtreeFlags & TransformFlags.ContainsSpread
|| isSuperOrSuperProperty(expression, expressionKind)) {
// If the this node contains a SpreadExpression, or is a super call, then it is an ES6
@ -2641,11 +2645,17 @@ namespace ts {
if (node.typeArguments) {
transformFlags |= TransformFlags.AssertTypeScript;
}
if (node.flags & NodeFlags.PropagateNull) {
transformFlags |= TransformFlags.AssertESNext;
}
if (subtreeFlags & TransformFlags.ContainsSpread) {
// If the this node contains a SpreadElementExpression then it is an ES6
// node.
transformFlags |= TransformFlags.AssertES2015;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.ArrayLiteralOrCallOrNewExcludes;
}
@ -3049,6 +3059,10 @@ namespace ts {
const expression = node.expression;
const expressionKind = expression.kind;
if (node.flags & NodeFlags.PropagateNull) {
transformFlags |= TransformFlags.AssertESNext;
}
// If a PropertyAccessExpression starts with a super keyword, then it is
// ES6 syntax, and requires a lexical `this` binding.
if (expressionKind === SyntaxKind.SuperKeyword) {
@ -3354,7 +3368,6 @@ namespace ts {
break;
case SyntaxKind.ArrayLiteralExpression:
case SyntaxKind.NewExpression:
excludeFlags = TransformFlags.ArrayLiteralOrCallOrNewExcludes;
if (subtreeFlags & TransformFlags.ContainsSpread) {
// If the this node contains a SpreadExpression, then it is an ES6
@ -3364,6 +3377,13 @@ namespace ts {
break;
case SyntaxKind.ElementAccessExpression:
if (node.flags & NodeFlags.PropagateNull) {
transformFlags |= TransformFlags.AssertESNext;
}
break;
case SyntaxKind.DoStatement:
case SyntaxKind.WhileStatement:
case SyntaxKind.ForStatement:

View File

@ -13109,6 +13109,7 @@ namespace ts {
}
function checkPropertyAccessExpression(node: PropertyAccessExpression) {
checkGrammarNullPropagation(node);
return checkPropertyAccessExpressionOrQualifiedName(node, node.expression, node.name);
}
@ -13276,6 +13277,8 @@ namespace ts {
}
function checkIndexedAccess(node: ElementAccessExpression): Type {
checkGrammarNullPropagation(node);
const objectType = checkNonNullExpression(node.expression);
const indexExpression = node.argumentExpression;
@ -14678,7 +14681,7 @@ namespace ts {
*/
function checkCallExpression(node: CallExpression | NewExpression): Type {
// Grammar checking; stop grammar-checking if checkGrammarTypeArguments return true
checkGrammarTypeArguments(node, node.typeArguments) || checkGrammarArguments(node, node.arguments);
checkGrammarNullPropagation(node) || checkGrammarTypeArguments(node, node.typeArguments) || checkGrammarArguments(node, node.arguments);
const signature = getResolvedSignature(node);
@ -22386,6 +22389,18 @@ namespace ts {
return checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarIndexSignatureParameters(node);
}
function checkGrammarNullPropagation(node: CallExpression | NewExpression | PropertyAccessExpression | ElementAccessExpression) {
if (node.flags & NodeFlags.PropagateNull && node.expression.kind === SyntaxKind.SuperKeyword) {
const sourceFile = getSourceFileOfNode(node);
const start = skipTrivia(sourceFile.text, node.expression.end);
if (node.kind === SyntaxKind.PropertyAccessExpression) {
return grammarErrorAtPos(sourceFile, start, 2, Diagnostics._0_expected, tokenToString(SyntaxKind.DotToken));
}
return grammarErrorAtPos(sourceFile, start, 2, Diagnostics.Unexpected_token);
}
}
function checkGrammarForAtLeastOneTypeArgument(node: Node, typeArguments: NodeArray<TypeNode>): boolean {
if (typeArguments && typeArguments.length === 0) {
const sourceFile = getSourceFileOfNode(node);

View File

@ -1092,12 +1092,13 @@ namespace ts {
}
function emitPropertyAccessExpression(node: PropertyAccessExpression) {
const propagatesNull = node.flags & NodeFlags.PropagateNull;
let indentBeforeDot = false;
let indentAfterDot = false;
if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) {
const dotRangeStart = node.expression.end;
const dotRangeEnd = skipTrivia(currentSourceFile.text, node.expression.end) + 1;
const dotToken = <Node>{ kind: SyntaxKind.DotToken, pos: dotRangeStart, end: dotRangeEnd };
const dotRangeEnd = skipTrivia(currentSourceFile.text, node.expression.end) + (propagatesNull ? 2 : 1);
const dotToken = <Node>{ kind: propagatesNull ? SyntaxKind.QuestionDotToken : SyntaxKind.DotToken, pos: dotRangeStart, end: dotRangeEnd };
indentBeforeDot = needsIndentation(node, node.expression, dotToken);
indentAfterDot = needsIndentation(node, dotToken, node.name);
}
@ -1105,8 +1106,8 @@ namespace ts {
emitExpression(node.expression);
increaseIndentIf(indentBeforeDot);
const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression);
write(shouldEmitDotDot ? ".." : ".");
const shouldEmitDotDot = !propagatesNull && !indentBeforeDot && needsDotDotForPropertyAccess(node.expression);
write(shouldEmitDotDot ? ".." : propagatesNull ? "?." : ".");
increaseIndentIf(indentAfterDot);
emit(node.name);
@ -1135,13 +1136,16 @@ namespace ts {
function emitElementAccessExpression(node: ElementAccessExpression) {
emitExpression(node.expression);
write("[");
write(node.flags & NodeFlags.PropagateNull ? "?.[" : "[");
emitExpression(node.argumentExpression);
write("]");
}
function emitCallExpression(node: CallExpression) {
emitExpression(node.expression);
if (node.flags & NodeFlags.PropagateNull) {
write("?.");
}
emitTypeArguments(node, node.typeArguments);
emitExpressionList(node, node.arguments, ListFormat.CallExpressionArguments);
}
@ -1149,6 +1153,9 @@ namespace ts {
function emitNewExpression(node: NewExpression) {
write("new ");
emitExpression(node.expression);
if (node.flags & NodeFlags.PropagateNull) {
write("?.");
}
emitTypeArguments(node, node.typeArguments);
emitExpressionList(node, node.arguments, ListFormat.NewExpressionArguments);
}

View File

@ -440,8 +440,9 @@ namespace ts {
: node;
}
export function createPropertyAccess(expression: Expression, name: string | Identifier) {
export function createPropertyAccess(expression: Expression, name: string | Identifier, flags?: NodeFlags) {
const node = <PropertyAccessExpression>createSynthesizedNode(SyntaxKind.PropertyAccessExpression);
node.flags |= flags;
node.expression = parenthesizeForAccess(expression);
node.name = asName(name);
setEmitFlags(node, EmitFlags.NoIndentation);
@ -453,12 +454,13 @@ namespace ts {
// instead of using the default from createPropertyAccess
return node.expression !== expression
|| node.name !== name
? updateNode(setEmitFlags(createPropertyAccess(expression, name), getEmitFlags(node)), node)
? updateNode(setEmitFlags(createPropertyAccess(expression, name, node.flags), getEmitFlags(node)), node)
: node;
}
export function createElementAccess(expression: Expression, index: number | Expression) {
export function createElementAccess(expression: Expression, index: number | Expression, flags?: NodeFlags) {
const node = <ElementAccessExpression>createSynthesizedNode(SyntaxKind.ElementAccessExpression);
node.flags |= flags;
node.expression = parenthesizeForAccess(expression);
node.argumentExpression = asExpression(index);
return node;
@ -467,12 +469,13 @@ namespace ts {
export function updateElementAccess(node: ElementAccessExpression, expression: Expression, argumentExpression: Expression) {
return node.expression !== expression
|| node.argumentExpression !== argumentExpression
? updateNode(createElementAccess(expression, argumentExpression), node)
? updateNode(createElementAccess(expression, argumentExpression, node.flags), node)
: node;
}
export function createCall(expression: Expression, typeArguments: TypeNode[] | undefined, argumentsArray: Expression[]) {
export function createCall(expression: Expression, typeArguments: TypeNode[] | undefined, argumentsArray: Expression[], flags?: NodeFlags) {
const node = <CallExpression>createSynthesizedNode(SyntaxKind.CallExpression);
node.flags |= flags;
node.expression = parenthesizeForAccess(expression);
node.typeArguments = asNodeArray(typeArguments);
node.arguments = parenthesizeListElements(createNodeArray(argumentsArray));
@ -483,12 +486,13 @@ namespace ts {
return expression !== node.expression
|| typeArguments !== node.typeArguments
|| argumentsArray !== node.arguments
? updateNode(createCall(expression, typeArguments, argumentsArray), node)
? updateNode(createCall(expression, typeArguments, argumentsArray, node.flags), node)
: node;
}
export function createNew(expression: Expression, typeArguments: TypeNode[] | undefined, argumentsArray: Expression[] | undefined) {
export function createNew(expression: Expression, typeArguments: TypeNode[] | undefined, argumentsArray: Expression[] | undefined, flags?: NodeFlags) {
const node = <NewExpression>createSynthesizedNode(SyntaxKind.NewExpression);
node.flags |= flags;
node.expression = parenthesizeForNew(expression);
node.typeArguments = asNodeArray(typeArguments);
node.arguments = argumentsArray ? parenthesizeListElements(createNodeArray(argumentsArray)) : undefined;
@ -499,7 +503,7 @@ namespace ts {
return node.expression !== expression
|| node.typeArguments !== typeArguments
|| node.arguments !== argumentsArray
? updateNode(createNew(expression, typeArguments, argumentsArray), node)
? updateNode(createNew(expression, typeArguments, argumentsArray, node.flags), node)
: node;
}
@ -1740,6 +1744,10 @@ namespace ts {
return createBinary(left, SyntaxKind.EqualsToken, right);
}
export function createEquality(left: Expression, right: Expression) {
return createBinary(left, SyntaxKind.EqualsEqualsToken, right);
}
export function createStrictEquality(left: Expression, right: Expression) {
return createBinary(left, SyntaxKind.EqualsEqualsEqualsToken, right);
}
@ -2449,7 +2457,8 @@ namespace ts {
),
(<PropertyAccessExpression>callee).expression
),
(<PropertyAccessExpression>callee).name
(<PropertyAccessExpression>callee).name,
callee.flags & NodeFlags.PropagateNull
);
setTextRange(target, callee);
}
@ -2472,7 +2481,8 @@ namespace ts {
),
(<ElementAccessExpression>callee).expression
),
(<ElementAccessExpression>callee).argumentExpression
(<ElementAccessExpression>callee).argumentExpression,
callee.flags & NodeFlags.PropagateNull
);
setTextRange(target, callee);
}

View File

@ -3748,7 +3748,10 @@ namespace ts {
function parseSuperExpression(): MemberExpression {
const expression = parseTokenNode<PrimaryExpression>();
if (token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.DotToken || token() === SyntaxKind.OpenBracketToken) {
if (token() === SyntaxKind.OpenParenToken ||
token() === SyntaxKind.DotToken ||
token() === SyntaxKind.OpenBracketToken ||
token() === SyntaxKind.QuestionDotToken) {
return expression;
}
@ -4004,11 +4007,32 @@ namespace ts {
return finishNode(node);
}
function isNullPropagatingCallOrNewExpression() {
return token() === SyntaxKind.QuestionDotToken
&& lookAhead(nextTokenIsOpenParenOrLessThanToken);
}
function nextTokenIsOpenParenOrLessThanToken() {
nextToken();
return token() === SyntaxKind.OpenParenToken
|| token() === SyntaxKind.LessThanToken;
}
function parseMemberExpressionRest(expression: LeftHandSideExpression): MemberExpression {
while (true) {
if (isNullPropagatingCallOrNewExpression()) {
// In a null-propagating call or new expression, we defer parsing `.?` to parseCallExpressionRest.
return <MemberExpression>expression;
}
const dotToken = parseOptionalToken(SyntaxKind.DotToken);
if (dotToken) {
const questionDotToken = !dotToken && parseOptionalToken(SyntaxKind.QuestionDotToken);
if (dotToken || (questionDotToken && token() !== SyntaxKind.OpenBracketToken)) {
const propertyAccess = <PropertyAccessExpression>createNode(SyntaxKind.PropertyAccessExpression, expression.pos);
if (questionDotToken) {
propertyAccess.flags |= NodeFlags.PropagateNull;
}
propertyAccess.expression = expression;
propertyAccess.name = parseRightSideOfDot(/*allowIdentifierNames*/ true);
expression = finishNode(propertyAccess);
@ -4024,8 +4048,13 @@ namespace ts {
}
// when in the [Decorator] context, we do not parse ElementAccess as it could be part of a ComputedPropertyName
if (!inDecoratorContext() && parseOptional(SyntaxKind.OpenBracketToken)) {
// however, `?.[` is unambiguously *not* a ComputedPropertyName.
if ((questionDotToken || !inDecoratorContext()) && parseOptional(SyntaxKind.OpenBracketToken)) {
const indexedAccess = <ElementAccessExpression>createNode(SyntaxKind.ElementAccessExpression, expression.pos);
if (questionDotToken) {
indexedAccess.flags |= NodeFlags.PropagateNull;
}
indexedAccess.expression = expression;
// It's not uncommon for a user to write: "new Type[]".
@ -4060,32 +4089,34 @@ namespace ts {
function parseCallExpressionRest(expression: LeftHandSideExpression): LeftHandSideExpression {
while (true) {
expression = parseMemberExpressionRest(expression);
const questionDot = parseOptionalToken(SyntaxKind.QuestionDotToken);
let typeArguments: NodeArray<TypeNode>;
if (token() === SyntaxKind.LessThanToken) {
// See if this is the start of a generic invocation. If so, consume it and
// keep checking for postfix expressions. Otherwise, it's just a '<' that's
// part of an arithmetic expression. Break out so we consume it higher in the
// stack.
const typeArguments = tryParse(parseTypeArgumentsInExpression);
// If we have seen `?.<` then this is definately a call expression.
typeArguments = questionDot
? parseTypeArgumentsInExpression()
: tryParse(parseTypeArgumentsInExpression);
if (!typeArguments) {
return expression;
}
const callExpr = <CallExpression>createNode(SyntaxKind.CallExpression, expression.pos);
callExpr.expression = expression;
callExpr.typeArguments = typeArguments;
callExpr.arguments = parseArgumentList();
expression = finishNode(callExpr);
continue;
}
else if (token() === SyntaxKind.OpenParenToken) {
const callExpr = <CallExpression>createNode(SyntaxKind.CallExpression, expression.pos);
callExpr.expression = expression;
callExpr.arguments = parseArgumentList();
expression = finishNode(callExpr);
continue;
else if (token() !== SyntaxKind.OpenParenToken) {
return expression;
}
return expression;
const callExpr = <CallExpression>createNode(SyntaxKind.CallExpression, expression.pos);
if (questionDot) {
callExpr.flags |= NodeFlags.PropagateNull;
}
callExpr.expression = expression;
callExpr.typeArguments = typeArguments;
callExpr.arguments = parseArgumentList();
expression = finishNode(callExpr);
}
}
@ -4121,6 +4152,7 @@ namespace ts {
// list. So we definitely want to treat this as a type arg list.
case SyntaxKind.DotToken: // foo<x>.
case SyntaxKind.QuestionDotToken: // foo<x>?.
case SyntaxKind.CloseParenToken: // foo<x>)
case SyntaxKind.CloseBracketToken: // foo<x>]
case SyntaxKind.ColonToken: // foo<x>:
@ -4365,6 +4397,10 @@ namespace ts {
const node = <NewExpression>createNode(SyntaxKind.NewExpression, fullStart);
node.expression = parseMemberExpressionOrHigher();
if (parseOptional(SyntaxKind.QuestionDotToken)) {
node.flags |= NodeFlags.PropagateNull;
}
node.typeArguments = tryParse(parseTypeArgumentsInExpression);
if (node.typeArguments || token() === SyntaxKind.OpenParenToken) {
node.arguments = parseArgumentList();

View File

@ -167,6 +167,7 @@ namespace ts {
"&&": SyntaxKind.AmpersandAmpersandToken,
"||": SyntaxKind.BarBarToken,
"?": SyntaxKind.QuestionToken,
"?.": SyntaxKind.QuestionDotToken,
":": SyntaxKind.ColonToken,
"=": SyntaxKind.EqualsToken,
"+=": SyntaxKind.PlusEqualsToken,
@ -1529,6 +1530,9 @@ namespace ts {
pos++;
return token = SyntaxKind.GreaterThanToken;
case CharacterCodes.question:
if (text.charCodeAt(pos + 1) === CharacterCodes.dot && !isDigit(text.charCodeAt(pos + 2))) {
return pos += 2, token = SyntaxKind.QuestionDotToken;
}
pos++;
return token = SyntaxKind.QuestionToken;
case CharacterCodes.openBracket:

View File

@ -26,6 +26,7 @@ namespace ts {
const previousOnSubstituteNode = context.onSubstituteNode;
context.onSubstituteNode = onSubstituteNode;
const nullPropagatingExpressions = createMap<boolean>();
let enabledSubstitutions: ESNextSubstitutionFlags;
let enclosingFunctionFlags: FunctionFlags;
let enclosingSuperContainerFlags: NodeCheckFlags = 0;
@ -39,6 +40,7 @@ namespace ts {
const visited = visitEachChild(node, visitor, context);
addEmitHelpers(visited, context.readEmitHelpers());
nullPropagatingExpressions.clear();
return visited;
}
@ -101,6 +103,20 @@ namespace ts {
return visitExpressionStatement(node as ExpressionStatement);
case SyntaxKind.ParenthesizedExpression:
return visitParenthesizedExpression(node as ParenthesizedExpression, noDestructuringValue);
case SyntaxKind.CallExpression:
return visitCallExpression(node as CallExpression);
case SyntaxKind.NewExpression:
return visitNewExpression(node as NewExpression);
case SyntaxKind.PropertyAccessExpression:
return visitPropertyAccess(node as PropertyAccessExpression);
case SyntaxKind.ElementAccessExpression:
return visitElementAccess(node as ElementAccessExpression);
case SyntaxKind.DeleteExpression:
return visitDelete(node as DeleteExpression);
case SyntaxKind.PrefixUnaryExpression:
return visitPrefix(node as PrefixUnaryExpression);
case SyntaxKind.PostfixUnaryExpression:
return visitPostfix(node as PostfixUnaryExpression);
default:
return visitEachChild(node, visitor, context);
}
@ -230,7 +246,20 @@ namespace ts {
visitNode(node.right, noDestructuringValue ? visitorNoDestructuringValue : visitor, isExpression)
);
}
return visitEachChild(node, visitor, context);
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)
);
}
}
return updateBinary(node, left, right);
}
/**
@ -699,6 +728,310 @@ namespace ts {
return statements;
}
function isNullPropagatingExpression(node: CallExpression | NewExpression | PropertyAccessExpression | ElementAccessExpression) {
return (node.flags & NodeFlags.PropagateNull) !== 0
&& 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 {
if (node.flags & NodeFlags.PropagateNull) {
if (isIdentifier(nullableExpression)) {
return setTextRange(
createConditional(
createEquality(nullableExpression, createNull()),
createVoidZero(),
finishExpression(node, nullableExpression, data)
),
node
);
}
else {
const temp = createTempVariable(hoistVariableDeclaration);
return setTextRange(
createConditional(
createEquality(
createAssignment(temp, nullableExpression),
createNull()
),
createVoidZero(),
finishExpression(node, temp, data)
),
node
);
}
}
return finishExpression(node, nullableExpression, data);
}
function visitCallExpression(node: CallExpression): Expression {
if (isNullPropagatingExpression(node)) {
// null propagation in call:
// x?.() -> x == null ? void 0 : x()
// (x)?.() -> (_a = (x)) == null ? void 0 : _a()
// x.y?.() -> (_a = x.y) == null ? void 0 : _a.call(x);
// x[y]?.() -> (_a = x[y]) == null ? void 0 : _a.call(x);
// (x.y)?.() -> (_a = x.y) == null ? void 0 : _a.call(x);
// (x[y])?.() -> (_a = x[y]) == null ? void 0 : _a.call(x);
const { target, thisArg } = createCallBinding(node.expression, hoistVariableDeclaration);
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
);
}
function finishNullableCallExpression(node: CallExpression, target: Expression, thisArg: Expression) {
if (!isVoidZero(thisArg)) {
return setOriginalNode(
createFunctionCall(
target,
thisArg,
visitNodes(node.arguments, visitor, isExpression),
node
),
node
);
}
return setOriginalNode(
setTextRange(
createCall(
target,
/*typeArguments*/ undefined,
visitNodes(node.arguments, visitor, isExpression)
),
node
),
node
);
}
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();
// new (x)?.() -> (_a = (x)) == 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();
// 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);
}
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);
}
function finishNullableNewExpression(node: NewExpression, expression: Expression) {
return setOriginalNode(
setTextRange(
createNew(
expression,
/*typeArguments*/ undefined,
visitNodes(node.arguments, visitor, isExpression)
),
node
),
node
);
}
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);
}
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
);
}
function finishNullablePropertyAccess(node: PropertyAccessExpression, expression: Expression) {
return setOriginalNode(
setTextRange(
createPropertyAccess(
expression,
node.name
),
node
),
node
);
}
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);
}
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
);
}
function finishNullableElementAccess(node: ElementAccessExpression, expression: Expression) {
return setOriginalNode(
setTextRange(
createElementAccess(
expression,
visitNode(node.argumentExpression, visitor, isExpression)
),
node
),
node
);
}
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
);
}
return updateDelete(node, expression);
}
function visitPrefix(node: PrefixUnaryExpression) {
const operand = visitNode(node.operand, visitor, isExpression);
const nilReference = getNilReference(operand);
if (nilReference) {
return updateNilReference(
nilReference,
updatePrefix(node, nilReference.whenFalse)
);
}
return updatePrefix(node, operand);
}
function visitPostfix(node: PostfixUnaryExpression) {
const operand = visitNode(node.operand, visitor, isExpression);
const nilReference = getNilReference(operand);
if (nilReference) {
return updateNilReference(
nilReference,
updatePostfix(node, nilReference.whenFalse)
);
}
return updatePostfix(node, operand);
}
function enableSubstitutionForAsyncMethodsWithSuper() {
if ((enabledSubstitutions & ESNextSubstitutionFlags.AsyncMethodsWithSuper) === 0) {
enabledSubstitutions |= ESNextSubstitutionFlags.AsyncMethodsWithSuper;

View File

@ -82,6 +82,7 @@
DotDotDotToken,
SemicolonToken,
CommaToken,
QuestionDotToken,
LessThanToken,
LessThanSlashToken,
GreaterThanToken,
@ -435,20 +436,21 @@
NestedNamespace = 1 << 2, // Namespace declaration
Synthesized = 1 << 3, // Node was synthesized during transformation
Namespace = 1 << 4, // Namespace declaration
ExportContext = 1 << 5, // Export context (initialized by binding)
ContainsThis = 1 << 6, // Interface contains references to "this"
HasImplicitReturn = 1 << 7, // If function implicitly returns on one of codepaths (initialized by binding)
HasExplicitReturn = 1 << 8, // If function has explicit reachable return on one of codepaths (initialized by binding)
GlobalAugmentation = 1 << 9, // Set if module declaration is an augmentation for the global scope
HasAsyncFunctions = 1 << 10, // If the file has async functions (initialized by binding)
DisallowInContext = 1 << 11, // If node was parsed in a context where 'in-expressions' are not allowed
YieldContext = 1 << 12, // If node was parsed in the 'yield' context created when parsing a generator
DecoratorContext = 1 << 13, // If node was parsed as part of a decorator
AwaitContext = 1 << 14, // If node was parsed in the 'await' context created when parsing an async function
ThisNodeHasError = 1 << 15, // If the parser encountered an error when parsing the code that created this node
JavaScriptFile = 1 << 16, // If node was parsed in a JavaScript
ThisNodeOrAnySubNodesHasError = 1 << 17, // If this node or any of its children had an error
HasAggregatedChildData = 1 << 18, // If we've computed data from children and cached it in this node
PropagateNull = 1 << 5, // Expression
ExportContext = 1 << 6, // Export context (initialized by binding)
ContainsThis = 1 << 7, // Interface contains references to "this"
HasImplicitReturn = 1 << 8, // If function implicitly returns on one of codepaths (initialized by binding)
HasExplicitReturn = 1 << 9, // If function has explicit reachable return on one of codepaths (initialized by binding)
GlobalAugmentation = 1 << 10, // Set if module declaration is an augmentation for the global scope
HasAsyncFunctions = 1 << 11, // If the file has async functions (initialized by binding)
DisallowInContext = 1 << 12, // If node was parsed in a context where 'in-expressions' are not allowed
YieldContext = 1 << 13, // If node was parsed in the 'yield' context created when parsing a generator
DecoratorContext = 1 << 14, // If node was parsed as part of a decorator
AwaitContext = 1 << 15, // If node was parsed in the 'await' context created when parsing an async function
ThisNodeHasError = 1 << 16, // If the parser encountered an error when parsing the code that created this node
JavaScriptFile = 1 << 17, // If node was parsed in a JavaScript
ThisNodeOrAnySubNodesHasError = 1 << 18, // If this node or any of its children had an error
HasAggregatedChildData = 1 << 19, // If we've computed data from children and cached it in this node
BlockScoped = Let | Const,

View File

@ -3583,10 +3583,6 @@ namespace ts {
return node.kind === SyntaxKind.Identifier;
}
export function isVoidExpression(node: Node): node is VoidExpression {
return node.kind === SyntaxKind.VoidExpression;
}
export function isGeneratedIdentifier(node: Node): node is GeneratedIdentifier {
// Using `>` here catches both `GeneratedIdentifierKind.None` and `undefined`.
return isIdentifier(node) && node.autoGenerateKind > GeneratedIdentifierKind.None;
@ -3897,6 +3893,16 @@ namespace ts {
return isExpressionKind(skipPartiallyEmittedExpressions(node).kind);
}
export function isVoidExpression(node: Node): node is VoidExpression {
return node.kind === SyntaxKind.VoidExpression;
}
export function isVoidZero(node: Node): boolean {
return isVoidExpression(node)
&& node.expression.kind === SyntaxKind.NumericLiteral
&& (<NumericLiteral>node.expression).text === "0";
}
export function isAssertionExpression(node: Node): node is AssertionExpression {
const kind = node.kind;
return kind === SyntaxKind.TypeAssertionExpression