PR feedback and cleanup
This commit is contained in:
parent
ceae78bea0
commit
f8ed021f1e
3 changed files with 150 additions and 65 deletions
|
@ -169,8 +169,19 @@ namespace ts {
|
||||||
export function concatenate<T>(array1: T[], array2: T[]): T[] {
|
export function concatenate<T>(array1: T[], array2: T[]): T[] {
|
||||||
if (!array2 || !array2.length) return array1;
|
if (!array2 || !array2.length) return array1;
|
||||||
if (!array1 || !array1.length) return array2;
|
if (!array1 || !array1.length) return array2;
|
||||||
|
return [...array1, ...array2];
|
||||||
|
}
|
||||||
|
|
||||||
return array1.concat(array2);
|
export function append<T>(array: T[], value: T): T[] {
|
||||||
|
if (value === undefined) return array;
|
||||||
|
if (!array || !array.length) return [value];
|
||||||
|
return [...array, value];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function prepend<T>(array: T[], value: T): T[] {
|
||||||
|
if (value === undefined) return array;
|
||||||
|
if (!array || !array.length) return [value];
|
||||||
|
return [value, ...array];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deduplicate<T>(array: T[]): T[] {
|
export function deduplicate<T>(array: T[]): T[] {
|
||||||
|
|
|
@ -233,14 +233,14 @@ namespace ts {
|
||||||
const node = <PropertyAccessExpression>createNode(SyntaxKind.PropertyAccessExpression);
|
const node = <PropertyAccessExpression>createNode(SyntaxKind.PropertyAccessExpression);
|
||||||
node.expression = parenthesizeForAccess(expression);
|
node.expression = parenthesizeForAccess(expression);
|
||||||
node.dotToken = createSynthesizedNode(SyntaxKind.DotToken);
|
node.dotToken = createSynthesizedNode(SyntaxKind.DotToken);
|
||||||
node.name = coerceIdentifier(name);
|
node.name = typeof name === "string" ? createIdentifier(name) : name;
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createElementAccess(expression: Expression, index: string | number | Expression) {
|
export function createElementAccess(expression: Expression, index: number | Expression) {
|
||||||
const node = <ElementAccessExpression>createNode(SyntaxKind.ElementAccessExpression);
|
const node = <ElementAccessExpression>createNode(SyntaxKind.ElementAccessExpression);
|
||||||
node.expression = parenthesizeForAccess(expression);
|
node.expression = parenthesizeForAccess(expression);
|
||||||
node.argumentExpression = coerceExpression(index);
|
node.argumentExpression = typeof index === "number" ? createLiteral(index) : index;
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,9 +256,9 @@ namespace ts {
|
||||||
|
|
||||||
export function createBinary(left: Expression, operator: SyntaxKind, right: Expression, location?: TextRange) {
|
export function createBinary(left: Expression, operator: SyntaxKind, right: Expression, location?: TextRange) {
|
||||||
const node = <BinaryExpression>createNode(SyntaxKind.BinaryExpression, location);
|
const node = <BinaryExpression>createNode(SyntaxKind.BinaryExpression, location);
|
||||||
node.left = parenthesizeForBinary(left, operator, BinaryOperand.Left);
|
node.left = parenthesizeBinaryOperand(operator, left, /*isLeftSideOfBinary*/ true);
|
||||||
node.operatorToken = createSynthesizedNode(operator);
|
node.operatorToken = createSynthesizedNode(operator);
|
||||||
node.right = parenthesizeForBinary(right, operator, BinaryOperand.Right);
|
node.right = parenthesizeBinaryOperand(operator, right, /*isLeftSideOfBinary*/ false);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,7 +282,11 @@ namespace ts {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createArraySlice(array: Expression, start?: number | Expression) {
|
export function createArraySlice(array: Expression, start?: number | Expression) {
|
||||||
const argumentsList: Expression[] = start !== undefined ? [coerceExpression(start)] : [];
|
const argumentsList: Expression[] = [];
|
||||||
|
if (start !== undefined) {
|
||||||
|
argumentsList.push(typeof start === "number" ? createLiteral(start) : start);
|
||||||
|
}
|
||||||
|
|
||||||
return createCall(createPropertyAccess(array, "slice"), argumentsList);
|
return createCall(createPropertyAccess(array, "slice"), argumentsList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,94 +300,165 @@ namespace ts {
|
||||||
return reduceLeft(expressions, createComma);
|
return reduceLeft(expressions, createComma);
|
||||||
}
|
}
|
||||||
|
|
||||||
function coerceIdentifier(value: string | Identifier) {
|
/**
|
||||||
if (typeof value === "string") {
|
* Wraps the operand to a BinaryExpression in parentheses if they are needed to preserve the intended
|
||||||
return createIdentifier(value);
|
* order of operations.
|
||||||
}
|
*
|
||||||
else {
|
* @param binaryOperator The operator for the BinaryExpression.
|
||||||
return value;
|
* @param operand The operand for the BinaryExpression.
|
||||||
}
|
* @param isLeftSideOfBinary A value indicating whether the operand is the left side of the
|
||||||
}
|
* BinaryExpression.
|
||||||
|
*/
|
||||||
function coerceExpression(value: string | number | boolean | Expression): Expression {
|
function parenthesizeBinaryOperand(binaryOperator: SyntaxKind, operand: Expression, isLeftSideOfBinary: boolean) {
|
||||||
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
||||||
return createLiteral(value);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const enum BinaryOperand {
|
|
||||||
Left,
|
|
||||||
Right
|
|
||||||
}
|
|
||||||
|
|
||||||
function parenthesizeForBinary(operand: Expression, operator: SyntaxKind, side: BinaryOperand) {
|
|
||||||
// When diagnosing whether the expression needs parentheses, the decision should be based
|
// When diagnosing whether the expression needs parentheses, the decision should be based
|
||||||
// on the innermost expression in a chain of nested type assertions.
|
// on the innermost expression in a chain of nested type assertions.
|
||||||
while (operand.kind === SyntaxKind.TypeAssertionExpression || operand.kind === SyntaxKind.AsExpression) {
|
operand = skipAssertions(operand);
|
||||||
operand = (<AssertionExpression>operand).expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the resulting expression is already parenthesized, we do not need to do any further processing.
|
// If the resulting expression is already parenthesized, we do not need to do any further processing.
|
||||||
if (operand.kind === SyntaxKind.ParenthesizedExpression) {
|
if (operand.kind === SyntaxKind.ParenthesizedExpression) {
|
||||||
return operand;
|
return operand;
|
||||||
}
|
}
|
||||||
|
|
||||||
return needsParenthesesForBinary(operand, operator, side)
|
return binaryOperandNeedsParentheses(binaryOperator, operand, isLeftSideOfBinary)
|
||||||
? parenthesizeExpression(operand)
|
? parenthesizeExpression(operand)
|
||||||
: operand;
|
: operand;
|
||||||
}
|
}
|
||||||
|
|
||||||
function needsParenthesesForBinary(operand: Expression, operator: SyntaxKind, side: BinaryOperand) {
|
/**
|
||||||
|
* Determines whether the operand to a BinaryExpression needs to be parenthesized.
|
||||||
|
*
|
||||||
|
* @param binaryOperator The operator for the BinaryExpression.
|
||||||
|
* @param operand The operand for the BinaryExpression.
|
||||||
|
* @param isLeftSideOfBinary A value indicating whether the operand is the left side of the
|
||||||
|
* BinaryExpression.
|
||||||
|
*/
|
||||||
|
function binaryOperandNeedsParentheses(binaryOperator: SyntaxKind, operand: Expression, isLeftSideOfBinary: boolean) {
|
||||||
|
// If the operand has lower precedence, then it needs to be parenthesized to preserve the
|
||||||
|
// intent of the expression. For example, if the operand is `a + b` and the operator is
|
||||||
|
// `*`, then we need to parenthesize the operand to preserve the intended order of
|
||||||
|
// operations: `(a + b) * x`.
|
||||||
|
//
|
||||||
|
// If the operand has higher precedence, then it does not need to be parenthesized. For
|
||||||
|
// example, if the operand is `a * b` and the operator is `+`, then we do not need to
|
||||||
|
// parenthesize to preserve the intended order of operations: `a * b + x`.
|
||||||
|
//
|
||||||
|
// If the operand has the same precedence, then we need to check the associativity of
|
||||||
|
// the operator based on whether this is the left or right operand of the expression.
|
||||||
|
//
|
||||||
|
// For example, if `a / d` is on the right of operator `*`, we need to parenthesize
|
||||||
|
// to preserve the intended order of operations: `x * (a / d)`
|
||||||
|
//
|
||||||
|
// If `a ** d` is on the left of operator `**`, we need to parenthesize to preserve
|
||||||
|
// the intended order of operations: `(a ** b) ** c`
|
||||||
|
const binaryOperatorPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, binaryOperator);
|
||||||
const operandPrecedence = getExpressionPrecedence(operand);
|
const operandPrecedence = getExpressionPrecedence(operand);
|
||||||
const operatorPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, operator);
|
switch (compareValues(operandPrecedence, binaryOperatorPrecedence)) {
|
||||||
switch (compareValues(operandPrecedence, operatorPrecedence)) {
|
|
||||||
case Comparison.LessThan:
|
case Comparison.LessThan:
|
||||||
return true;
|
return true;
|
||||||
case Comparison.EqualTo:
|
|
||||||
return isRightAssociativeOperandOnLeftHandSide(operand, side)
|
|
||||||
|| isModuloOperandOnRightHandSide(operand, operator, side);
|
|
||||||
case Comparison.GreaterThan:
|
case Comparison.GreaterThan:
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
case Comparison.EqualTo:
|
||||||
|
if (isLeftSideOfBinary) {
|
||||||
|
// No need to parenthesize the left operand when the binary operator is
|
||||||
|
// left associative:
|
||||||
|
// (a*b)/x -> a*b/x
|
||||||
|
// (a**b)/x -> a**b/x
|
||||||
|
|
||||||
|
// Parentheses are needed for the left operand when the binary operator is
|
||||||
|
// right associative:
|
||||||
|
// (a/b)**x -> (a/b)**x
|
||||||
|
// (a**b)**x -> (a**b)**x
|
||||||
|
const binaryOperatorAssociativity = getOperatorAssociativity(SyntaxKind.BinaryExpression, binaryOperator);
|
||||||
|
return binaryOperatorAssociativity === Associativity.Right;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// No need to parenthesize the right operand when the binary operator and
|
||||||
|
// operand are the same and one of the following:
|
||||||
|
// x*(a*b) => x*a*b
|
||||||
|
// x|(a|b) => x|a|b
|
||||||
|
// x&(a&b) => x&a&b
|
||||||
|
// x^(a^b) => x^a^b
|
||||||
|
if (isBinaryExpression(operand)
|
||||||
|
&& operand.operatorToken.kind === binaryOperator
|
||||||
|
&& isMathAssociativeOperator(binaryOperator)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No need to parenthesize the right operand when the operand is right
|
||||||
|
// associative:
|
||||||
|
// x/(a**b) -> x/a**b
|
||||||
|
// x**(a**b) -> x**a**b
|
||||||
|
|
||||||
|
// Parentheses are needed for the right operand when the operand is left
|
||||||
|
// associative:
|
||||||
|
// x/(a*b) -> x/(a*b)
|
||||||
|
// x**(a/b) -> x**(a/b)
|
||||||
|
const operandAssociativity = getExpressionAssociativity(operand);
|
||||||
|
return operandAssociativity === Associativity.Left;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isRightAssociativeOperandOnLeftHandSide(operand: Expression, side: BinaryOperand) {
|
/**
|
||||||
return side === BinaryOperand.Left
|
* Determines whether a binary operator is mathematically associative.
|
||||||
&& getExpressionAssociativity(operand) === Associativity.Right;
|
*
|
||||||
}
|
* @param binaryOperator The binary operator.
|
||||||
|
*/
|
||||||
function isModuloOperandOnRightHandSide(operand: Expression, operator: SyntaxKind, side: BinaryOperand) {
|
function isMathAssociativeOperator(binaryOperator: SyntaxKind) {
|
||||||
return side === BinaryOperand.Right
|
// The following operators are associative in JavaScript:
|
||||||
&& operator !== SyntaxKind.PercentToken
|
// (a*b)*c -> a*(b*c) -> a*b*c
|
||||||
&& operand.kind === SyntaxKind.BinaryExpression
|
// (a|b)|c -> a|(b|c) -> a|b|c
|
||||||
&& (<BinaryExpression>operand).operatorToken.kind === SyntaxKind.PercentToken;
|
// (a&b)&c -> a&(b&c) -> a&b&c
|
||||||
|
// (a^b)^c -> a^(b^c) -> a^b^c
|
||||||
|
//
|
||||||
|
// While addition is associative in mathematics, JavaScript's `+` is not
|
||||||
|
// guaranteed to be associative as it is overloaded with string concatenation.
|
||||||
|
return binaryOperator === SyntaxKind.AsteriskToken
|
||||||
|
|| binaryOperator === SyntaxKind.BarToken
|
||||||
|
|| binaryOperator === SyntaxKind.AmpersandToken
|
||||||
|
|| binaryOperator === SyntaxKind.CaretToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps an expression in parentheses if it is needed in order to use the expression for
|
||||||
|
* property or element access.
|
||||||
|
*
|
||||||
|
* @param expr The expression node.
|
||||||
|
*/
|
||||||
function parenthesizeForAccess(expr: Expression): LeftHandSideExpression {
|
function parenthesizeForAccess(expr: Expression): LeftHandSideExpression {
|
||||||
// When diagnosing whether the expression needs parentheses, the decision should be based
|
// When diagnosing whether the expression needs parentheses, the decision should be based
|
||||||
// on the innermost expression in a chain of nested type assertions.
|
// on the innermost expression in a chain of nested type assertions.
|
||||||
while (expr.kind === SyntaxKind.TypeAssertionExpression || expr.kind === SyntaxKind.AsExpression) {
|
expr = skipAssertions(expr);
|
||||||
expr = (<AssertionExpression>expr).expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
// isLeftHandSideExpression is almost the correct criterion for when it is not necessary
|
// isLeftHandSideExpression is almost the correct criterion for when it is not necessary
|
||||||
// to parenthesize the expression before a dot. The known exceptions are:
|
// to parenthesize the expression before a dot. The known exceptions are:
|
||||||
//
|
//
|
||||||
// NewExpression:
|
// NewExpression:
|
||||||
// new C.x -> not the same as (new C).x
|
// new C.x -> not the same as (new C).x
|
||||||
// NumberLiteral
|
// NumericLiteral
|
||||||
// 1.x -> not the same as (1).x
|
// 1.x -> not the same as (1).x
|
||||||
//
|
//
|
||||||
if (isLeftHandSideExpression(expr) &&
|
if (isLeftHandSideExpression(expr) &&
|
||||||
expr.kind !== SyntaxKind.NewExpression &&
|
expr.kind !== SyntaxKind.NewExpression &&
|
||||||
expr.kind !== SyntaxKind.NumericLiteral) {
|
expr.kind !== SyntaxKind.NumericLiteral) {
|
||||||
|
return expr;
|
||||||
return <LeftHandSideExpression>expr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return parenthesizeExpression(expr);
|
return parenthesizeExpression(expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skips past any TypeAssertionExpression or AsExpression nodes to their inner expression.
|
||||||
|
*
|
||||||
|
* @param node The expression node.
|
||||||
|
*/
|
||||||
|
function skipAssertions(node: Expression) {
|
||||||
|
while (node.kind === SyntaxKind.TypeAssertionExpression || node.kind === SyntaxKind.AsExpression) {
|
||||||
|
node = (<AssertionExpression>node).expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -162,6 +162,8 @@ namespace ts {
|
||||||
return generateNameForImportOrExportDeclaration(<ImportDeclaration | ExportDeclaration>node);
|
return generateNameForImportOrExportDeclaration(<ImportDeclaration | ExportDeclaration>node);
|
||||||
case SyntaxKind.FunctionDeclaration:
|
case SyntaxKind.FunctionDeclaration:
|
||||||
case SyntaxKind.ClassDeclaration:
|
case SyntaxKind.ClassDeclaration:
|
||||||
|
Debug.assert((node.flags & NodeFlags.Default) !== 0, "Can only generate a name for a default export.");
|
||||||
|
return generateNameForExportDefault();
|
||||||
case SyntaxKind.ExportAssignment:
|
case SyntaxKind.ExportAssignment:
|
||||||
return generateNameForExportDefault();
|
return generateNameForExportDefault();
|
||||||
case SyntaxKind.ClassExpression:
|
case SyntaxKind.ClassExpression:
|
||||||
|
@ -227,7 +229,6 @@ namespace ts {
|
||||||
lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset] = hoistedVariableDeclarations;
|
lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset] = hoistedVariableDeclarations;
|
||||||
lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset] = hoistedFunctionDeclarations;
|
lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset] = hoistedFunctionDeclarations;
|
||||||
lexicalEnvironmentStackOffset++;
|
lexicalEnvironmentStackOffset++;
|
||||||
|
|
||||||
hoistedVariableDeclarations = undefined;
|
hoistedVariableDeclarations = undefined;
|
||||||
hoistedFunctionDeclarations = undefined;
|
hoistedFunctionDeclarations = undefined;
|
||||||
}
|
}
|
||||||
|
@ -239,15 +240,12 @@ namespace ts {
|
||||||
function endLexicalEnvironment(): Statement[] {
|
function endLexicalEnvironment(): Statement[] {
|
||||||
let statements: Statement[];
|
let statements: Statement[];
|
||||||
if (hoistedVariableDeclarations || hoistedFunctionDeclarations) {
|
if (hoistedVariableDeclarations || hoistedFunctionDeclarations) {
|
||||||
statements = [];
|
|
||||||
if (hoistedFunctionDeclarations) {
|
if (hoistedFunctionDeclarations) {
|
||||||
for (const declaration of hoistedFunctionDeclarations) {
|
statements = [...hoistedFunctionDeclarations];
|
||||||
statements.push(declaration);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hoistedVariableDeclarations) {
|
if (hoistedVariableDeclarations) {
|
||||||
statements.push(
|
statements = append(statements,
|
||||||
createVariableStatement(
|
createVariableStatement(
|
||||||
createVariableDeclarationList(hoistedVariableDeclarations)
|
createVariableDeclarationList(hoistedVariableDeclarations)
|
||||||
)
|
)
|
||||||
|
@ -325,8 +323,9 @@ namespace ts {
|
||||||
* Makes an array from an ArrayLike.
|
* Makes an array from an ArrayLike.
|
||||||
*/
|
*/
|
||||||
function arrayOf<T>(arrayLike: ArrayLike<T>) {
|
function arrayOf<T>(arrayLike: ArrayLike<T>) {
|
||||||
const array: T[] = [];
|
const length = arrayLike.length;
|
||||||
for (let i = 0; i < arrayLike.length; i++) {
|
const array: T[] = new Array<T>(length);
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
array[i] = arrayLike[i];
|
array[i] = arrayLike[i];
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
|
|
Loading…
Reference in a new issue