Skip to content

Commit

Permalink
migrate nullish coalescing commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Kingwl committed Aug 14, 2019
1 parent dbabc12 commit b33eeac
Show file tree
Hide file tree
Showing 16 changed files with 671 additions and 5 deletions.
10 changes: 7 additions & 3 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -934,7 +934,8 @@ namespace ts {
else {
return node.kind === SyntaxKind.BinaryExpression && (
(<BinaryExpression>node).operatorToken.kind === SyntaxKind.AmpersandAmpersandToken ||
(<BinaryExpression>node).operatorToken.kind === SyntaxKind.BarBarToken);
(<BinaryExpression>node).operatorToken.kind === SyntaxKind.BarBarToken ||
(<BinaryExpression>node).operatorToken.kind === SyntaxKind.QuestionQuestionToken);
}
}
}
Expand Down Expand Up @@ -1367,7 +1368,7 @@ namespace ts {

function bindBinaryExpressionFlow(node: BinaryExpression) {
const operator = node.operatorToken.kind;
if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken) {
if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken || operator === SyntaxKind.QuestionQuestionToken) {
if (isTopLevelLogicalExpression(node)) {
const postExpressionLabel = createBranchLabel();
bindLogicalExpression(node, postExpressionLabel, postExpressionLabel);
Expand Down Expand Up @@ -3147,7 +3148,10 @@ namespace ts {
const operatorTokenKind = node.operatorToken.kind;
const leftKind = node.left.kind;

if (operatorTokenKind === SyntaxKind.EqualsToken && leftKind === SyntaxKind.ObjectLiteralExpression) {
if (operatorTokenKind === SyntaxKind.QuestionQuestionToken) {
transformFlags |= TransformFlags.AssertESNext;
}
else if (operatorTokenKind === SyntaxKind.EqualsToken && leftKind === SyntaxKind.ObjectLiteralExpression) {
// Destructuring object assignments with are ES2015 syntax
// and possibly ES2018 if they contain rest
transformFlags |= TransformFlags.AssertES2018 | TransformFlags.AssertES2015 | TransformFlags.AssertDestructuringAssignment;
Expand Down
9 changes: 7 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11524,7 +11524,7 @@ namespace ts {
return isContextSensitive((<ConditionalExpression>node).whenTrue) ||
isContextSensitive((<ConditionalExpression>node).whenFalse);
case SyntaxKind.BinaryExpression:
return (<BinaryExpression>node).operatorToken.kind === SyntaxKind.BarBarToken &&
return ((<BinaryExpression>node).operatorToken.kind === SyntaxKind.BarBarToken || (<BinaryExpression>node).operatorToken.kind === SyntaxKind.QuestionQuestionToken) &&
(isContextSensitive((<BinaryExpression>node).left) || isContextSensitive((<BinaryExpression>node).right));
case SyntaxKind.PropertyAssignment:
return isContextSensitive((<PropertyAssignment>node).initializer);
Expand Down Expand Up @@ -18477,6 +18477,7 @@ namespace ts {
}
return contextSensitive === true ? getTypeOfExpression(left) : contextSensitive;
case SyntaxKind.BarBarToken:
case SyntaxKind.QuestionQuestionToken:
// When an || expression has a contextual type, the operands are contextually typed by that type, except
// when that type originates in a binding pattern, the right operand is contextually typed by the type of
// the left operand. When an || expression has no contextual type, the right operand is contextually typed
Expand Down Expand Up @@ -23761,7 +23762,7 @@ namespace ts {
return checkDestructuringAssignment(left, checkExpression(right, checkMode), checkMode, right.kind === SyntaxKind.ThisKeyword);
}
let leftType: Type;
if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken) {
if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken || operator === SyntaxKind.QuestionQuestionToken) {
leftType = checkTruthinessExpression(left, checkMode);
}
else {
Expand Down Expand Up @@ -23917,6 +23918,10 @@ namespace ts {
return getTypeFacts(leftType) & TypeFacts.Falsy ?
getUnionType([removeDefinitelyFalsyTypes(leftType), rightType], UnionReduction.Subtype) :
leftType;
case SyntaxKind.QuestionQuestionToken:
return getTypeFacts(leftType) & TypeFacts.EQUndefinedOrNull ?
getUnionType([getNonNullableType(leftType), rightType]) :
leftType;
case SyntaxKind.EqualsToken:
const declKind = isBinaryExpression(left.parent) ? getAssignmentDeclarationKind(left.parent) : AssignmentDeclarationKind.None;
checkAssignmentDeclaration(declKind, rightType);
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3044,6 +3044,10 @@ namespace ts {
return createBinary(left, SyntaxKind.BarBarToken, right);
}

export function createNullishCoalescing(left: Expression, right: Expression) {
return createBinary(left, SyntaxKind.QuestionQuestionToken, right);
}

export function createLogicalNot(operand: Expression) {
return createPrefix(SyntaxKind.ExclamationToken, operand);
}
Expand Down
1 change: 1 addition & 0 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4659,6 +4659,7 @@ namespace ts {
case SyntaxKind.ExclamationEqualsEqualsToken: // foo<x> !==
case SyntaxKind.AmpersandAmpersandToken: // foo<x> &&
case SyntaxKind.BarBarToken: // foo<x> ||
case SyntaxKind.QuestionQuestionToken: // foo<x> ??
case SyntaxKind.CaretToken: // foo<x> ^
case SyntaxKind.AmpersandToken: // foo<x> &
case SyntaxKind.BarToken: // foo<x> |
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ namespace ts {
"&&": SyntaxKind.AmpersandAmpersandToken,
"||": SyntaxKind.BarBarToken,
"?": SyntaxKind.QuestionToken,
"??": SyntaxKind.QuestionQuestionToken,
":": SyntaxKind.ColonToken,
"=": SyntaxKind.EqualsToken,
"+=": SyntaxKind.PlusEqualsToken,
Expand Down Expand Up @@ -1778,6 +1779,9 @@ namespace ts {
pos++;
return token = SyntaxKind.GreaterThanToken;
case CharacterCodes.question:
if (text.charCodeAt(pos + 1) === CharacterCodes.question) {
return pos += 2, token = SyntaxKind.QuestionQuestionToken;
}
pos++;
return token = SyntaxKind.QuestionToken;
case CharacterCodes.openBracket:
Expand Down
32 changes: 32 additions & 0 deletions src/compiler/transformers/esnext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,41 @@ namespace ts {
return node;
}
switch (node.kind) {
case SyntaxKind.BinaryExpression:
if ((<BinaryExpression>node).operatorToken.kind === SyntaxKind.QuestionQuestionToken) {
return transformNullishCoalescingExpression(<BinaryExpression>node)
}
default:
return visitEachChild(node, visitor, context);
}
}

function createNotUndefinedCondition(node: Expression) {
return isIdentifier(node) && !isGeneratedIdentifier(node)
? createStrictInequality(createTypeOf(node), createLiteral("undefined"))
: createStrictInequality(node, createVoidZero());
}

function createNotNullCondition(node: Expression) {
return createStrictInequality(node, createNull());
}

function transformNullishCoalescingExpression(node: BinaryExpression) {
const expressions: Expression[] = [];
let left = visitNode(node.left, visitor, isExpression);
if (!isIdentifier(left)) {
const temp = createTempVariable(/*recordTempVariable*/ undefined);
expressions.push(createAssignment(temp, left));
left = temp;
}
expressions.push(
createConditional(
createLogicalAnd(
createNotUndefinedCondition(left),
createNotNullCondition(left)),
left,
visitNode(node.right, visitor, isExpression)));
return inlineExpressions(expressions);
}
}
}
2 changes: 2 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ namespace ts {
QuestionToken,
ColonToken,
AtToken,
QuestionQuestionToken,
/** Only the JSDoc scanner produces BacktickToken. The normal scanner produces NoSubstitutionTemplateLiteral and related kinds. */
BacktickToken,
// Assignments
Expand Down Expand Up @@ -1497,6 +1498,7 @@ namespace ts {
export type LogicalOperator
= SyntaxKind.AmpersandAmpersandToken
| SyntaxKind.BarBarToken
| SyntaxKind.QuestionQuestionToken
;

// see: https://tc39.github.io/ecma262/#prod-LogicalANDExpression
Expand Down
1 change: 1 addition & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2988,6 +2988,7 @@ namespace ts {

export function getBinaryOperatorPrecedence(kind: SyntaxKind): number {
switch (kind) {
case SyntaxKind.QuestionQuestionToken:
case SyntaxKind.BarBarToken:
return 5;
case SyntaxKind.AmpersandAmpersandToken:
Expand Down
60 changes: 60 additions & 0 deletions tests/baselines/reference/nullishCoalescingOperator1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//// [nullishCoalescingOperator1.ts]
declare const a1: string | undefined | null
declare const a2: string | undefined | null
declare const a3: string | undefined | null
declare const a4: string | undefined | null

declare const b1: number | undefined | null
declare const b2: number | undefined | null
declare const b3: number | undefined | null
declare const b4: number | undefined | null

declare const c1: boolean | undefined | null
declare const c2: boolean | undefined | null
declare const c3: boolean | undefined | null
declare const c4: boolean | undefined | null

interface I { a: string }
declare const d1: I | undefined | null
declare const d2: I | undefined | null
declare const d3: I | undefined | null
declare const d4: I | undefined | null

const aa1 = a1 ?? 'whatever';
const aa2 = a2 ?? 'whatever';
const aa3 = a3 ?? 'whatever';
const aa4 = a4 ?? 'whatever';

const bb1 = b1 ?? 1;
const bb2 = b2 ?? 1;
const bb3 = b3 ?? 1;
const bb4 = b4 ?? 1;

const cc1 = c1 ?? true;
const cc2 = c2 ?? true;
const cc3 = c3 ?? true;
const cc4 = c4 ?? true;

const dd1 = d1 ?? {b: 1};
const dd2 = d2 ?? {b: 1};
const dd3 = d3 ?? {b: 1};
const dd4 = d4 ?? {b: 1};

//// [nullishCoalescingOperator1.js]
"use strict";
var aa1 = typeof a1 !== "undefined" && a1 !== null ? a1 : 'whatever';
var aa2 = typeof a2 !== "undefined" && a2 !== null ? a2 : 'whatever';
var aa3 = typeof a3 !== "undefined" && a3 !== null ? a3 : 'whatever';
var aa4 = typeof a4 !== "undefined" && a4 !== null ? a4 : 'whatever';
var bb1 = typeof b1 !== "undefined" && b1 !== null ? b1 : 1;
var bb2 = typeof b2 !== "undefined" && b2 !== null ? b2 : 1;
var bb3 = typeof b3 !== "undefined" && b3 !== null ? b3 : 1;
var bb4 = typeof b4 !== "undefined" && b4 !== null ? b4 : 1;
var cc1 = typeof c1 !== "undefined" && c1 !== null ? c1 : true;
var cc2 = typeof c2 !== "undefined" && c2 !== null ? c2 : true;
var cc3 = typeof c3 !== "undefined" && c3 !== null ? c3 : true;
var cc4 = typeof c4 !== "undefined" && c4 !== null ? c4 : true;
var dd1 = typeof d1 !== "undefined" && d1 !== null ? d1 : { b: 1 };
var dd2 = typeof d2 !== "undefined" && d2 !== null ? d2 : { b: 1 };
var dd3 = typeof d3 !== "undefined" && d3 !== null ? d3 : { b: 1 };
var dd4 = typeof d4 !== "undefined" && d4 !== null ? d4 : { b: 1 };
125 changes: 125 additions & 0 deletions tests/baselines/reference/nullishCoalescingOperator1.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
=== tests/cases/conformance/expressions/nullishCoalescingOperator/nullishCoalescingOperator1.ts ===
declare const a1: string | undefined | null
>a1 : Symbol(a1, Decl(nullishCoalescingOperator1.ts, 0, 13))

declare const a2: string | undefined | null
>a2 : Symbol(a2, Decl(nullishCoalescingOperator1.ts, 1, 13))

declare const a3: string | undefined | null
>a3 : Symbol(a3, Decl(nullishCoalescingOperator1.ts, 2, 13))

declare const a4: string | undefined | null
>a4 : Symbol(a4, Decl(nullishCoalescingOperator1.ts, 3, 13))

declare const b1: number | undefined | null
>b1 : Symbol(b1, Decl(nullishCoalescingOperator1.ts, 5, 13))

declare const b2: number | undefined | null
>b2 : Symbol(b2, Decl(nullishCoalescingOperator1.ts, 6, 13))

declare const b3: number | undefined | null
>b3 : Symbol(b3, Decl(nullishCoalescingOperator1.ts, 7, 13))

declare const b4: number | undefined | null
>b4 : Symbol(b4, Decl(nullishCoalescingOperator1.ts, 8, 13))

declare const c1: boolean | undefined | null
>c1 : Symbol(c1, Decl(nullishCoalescingOperator1.ts, 10, 13))

declare const c2: boolean | undefined | null
>c2 : Symbol(c2, Decl(nullishCoalescingOperator1.ts, 11, 13))

declare const c3: boolean | undefined | null
>c3 : Symbol(c3, Decl(nullishCoalescingOperator1.ts, 12, 13))

declare const c4: boolean | undefined | null
>c4 : Symbol(c4, Decl(nullishCoalescingOperator1.ts, 13, 13))

interface I { a: string }
>I : Symbol(I, Decl(nullishCoalescingOperator1.ts, 13, 44))
>a : Symbol(I.a, Decl(nullishCoalescingOperator1.ts, 15, 13))

declare const d1: I | undefined | null
>d1 : Symbol(d1, Decl(nullishCoalescingOperator1.ts, 16, 13))
>I : Symbol(I, Decl(nullishCoalescingOperator1.ts, 13, 44))

declare const d2: I | undefined | null
>d2 : Symbol(d2, Decl(nullishCoalescingOperator1.ts, 17, 13))
>I : Symbol(I, Decl(nullishCoalescingOperator1.ts, 13, 44))

declare const d3: I | undefined | null
>d3 : Symbol(d3, Decl(nullishCoalescingOperator1.ts, 18, 13))
>I : Symbol(I, Decl(nullishCoalescingOperator1.ts, 13, 44))

declare const d4: I | undefined | null
>d4 : Symbol(d4, Decl(nullishCoalescingOperator1.ts, 19, 13))
>I : Symbol(I, Decl(nullishCoalescingOperator1.ts, 13, 44))

const aa1 = a1 ?? 'whatever';
>aa1 : Symbol(aa1, Decl(nullishCoalescingOperator1.ts, 21, 5))
>a1 : Symbol(a1, Decl(nullishCoalescingOperator1.ts, 0, 13))

const aa2 = a2 ?? 'whatever';
>aa2 : Symbol(aa2, Decl(nullishCoalescingOperator1.ts, 22, 5))
>a2 : Symbol(a2, Decl(nullishCoalescingOperator1.ts, 1, 13))

const aa3 = a3 ?? 'whatever';
>aa3 : Symbol(aa3, Decl(nullishCoalescingOperator1.ts, 23, 5))
>a3 : Symbol(a3, Decl(nullishCoalescingOperator1.ts, 2, 13))

const aa4 = a4 ?? 'whatever';
>aa4 : Symbol(aa4, Decl(nullishCoalescingOperator1.ts, 24, 5))
>a4 : Symbol(a4, Decl(nullishCoalescingOperator1.ts, 3, 13))

const bb1 = b1 ?? 1;
>bb1 : Symbol(bb1, Decl(nullishCoalescingOperator1.ts, 26, 5))
>b1 : Symbol(b1, Decl(nullishCoalescingOperator1.ts, 5, 13))

const bb2 = b2 ?? 1;
>bb2 : Symbol(bb2, Decl(nullishCoalescingOperator1.ts, 27, 5))
>b2 : Symbol(b2, Decl(nullishCoalescingOperator1.ts, 6, 13))

const bb3 = b3 ?? 1;
>bb3 : Symbol(bb3, Decl(nullishCoalescingOperator1.ts, 28, 5))
>b3 : Symbol(b3, Decl(nullishCoalescingOperator1.ts, 7, 13))

const bb4 = b4 ?? 1;
>bb4 : Symbol(bb4, Decl(nullishCoalescingOperator1.ts, 29, 5))
>b4 : Symbol(b4, Decl(nullishCoalescingOperator1.ts, 8, 13))

const cc1 = c1 ?? true;
>cc1 : Symbol(cc1, Decl(nullishCoalescingOperator1.ts, 31, 5))
>c1 : Symbol(c1, Decl(nullishCoalescingOperator1.ts, 10, 13))

const cc2 = c2 ?? true;
>cc2 : Symbol(cc2, Decl(nullishCoalescingOperator1.ts, 32, 5))
>c2 : Symbol(c2, Decl(nullishCoalescingOperator1.ts, 11, 13))

const cc3 = c3 ?? true;
>cc3 : Symbol(cc3, Decl(nullishCoalescingOperator1.ts, 33, 5))
>c3 : Symbol(c3, Decl(nullishCoalescingOperator1.ts, 12, 13))

const cc4 = c4 ?? true;
>cc4 : Symbol(cc4, Decl(nullishCoalescingOperator1.ts, 34, 5))
>c4 : Symbol(c4, Decl(nullishCoalescingOperator1.ts, 13, 13))

const dd1 = d1 ?? {b: 1};
>dd1 : Symbol(dd1, Decl(nullishCoalescingOperator1.ts, 36, 5))
>d1 : Symbol(d1, Decl(nullishCoalescingOperator1.ts, 16, 13))
>b : Symbol(b, Decl(nullishCoalescingOperator1.ts, 36, 19))

const dd2 = d2 ?? {b: 1};
>dd2 : Symbol(dd2, Decl(nullishCoalescingOperator1.ts, 37, 5))
>d2 : Symbol(d2, Decl(nullishCoalescingOperator1.ts, 17, 13))
>b : Symbol(b, Decl(nullishCoalescingOperator1.ts, 37, 19))

const dd3 = d3 ?? {b: 1};
>dd3 : Symbol(dd3, Decl(nullishCoalescingOperator1.ts, 38, 5))
>d3 : Symbol(d3, Decl(nullishCoalescingOperator1.ts, 18, 13))
>b : Symbol(b, Decl(nullishCoalescingOperator1.ts, 38, 19))

const dd4 = d4 ?? {b: 1};
>dd4 : Symbol(dd4, Decl(nullishCoalescingOperator1.ts, 39, 5))
>d4 : Symbol(d4, Decl(nullishCoalescingOperator1.ts, 19, 13))
>b : Symbol(b, Decl(nullishCoalescingOperator1.ts, 39, 19))

Loading

0 comments on commit b33eeac

Please sign in to comment.