diff --git a/src/compiler/core.ts b/src/compiler/core.ts index cf2d85c018f..1d5717fce4d 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -355,7 +355,9 @@ namespace ts { export function addRange(to: T[], from: T[]): void { if (to && from) { for (const v of from) { - to.push(v); + if (v !== undefined) { + to.push(v); + } } } } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 57ddd67b0a3..1930481115c 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -21,11 +21,13 @@ namespace ts { } export function updateNode(updated: T, original: T): T { - setOriginalNode(updated, original); - if (original.startsOnNewLine) { - updated.startsOnNewLine = true; + if (updated !== original) { + setOriginalNode(updated, original); + if (original.startsOnNewLine) { + updated.startsOnNewLine = true; + } + aggregateTransformFlags(updated); } - return updated; } @@ -183,6 +185,12 @@ namespace ts { return name; } + // Punctuation + + export function createToken(token: SyntaxKind) { + return createNode(token); + } + // Reserved words export function createSuper() { @@ -208,8 +216,68 @@ namespace ts { return node; } + export function updateComputedPropertyName(node: ComputedPropertyName, expression: Expression) { + if (node.expression !== expression) { + return updateNode(createComputedPropertyName(expression, node), node); + } + return node; + } + + // Signature elements + + export function createParameter(name: string | Identifier | BindingPattern, initializer?: Expression, location?: TextRange) { + return createParameterDeclaration( + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*dotDotDotToken*/ undefined, + name, + /*questionToken*/ undefined, + /*type*/ undefined, + initializer, + location + ); + } + + export function createParameterDeclaration(decorators: Decorator[], modifiers: Modifier[], dotDotDotToken: Node, name: string | Identifier | BindingPattern, questionToken: Node, type: TypeNode, initializer: Expression, location?: TextRange, flags?: NodeFlags) { + const node = createNode(SyntaxKind.Parameter, location, flags); + node.decorators = decorators ? createNodeArray(decorators) : undefined; + node.modifiers = modifiers ? createNodeArray(modifiers) : undefined; + node.dotDotDotToken = dotDotDotToken; + node.name = typeof name === "string" ? createIdentifier(name) : name; + node.questionToken = questionToken; + node.type = type; + node.initializer = initializer ? parenthesizeExpressionForList(initializer) : undefined; + return node; + } + + export function updateParameterDeclaration(node: ParameterDeclaration, decorators: Decorator[], modifiers: Modifier[], name: BindingName, type: TypeNode, initializer: Expression) { + if (node.decorators !== decorators || node.modifiers !== modifiers || node.name !== name || node.type !== type || node.initializer !== initializer) { + return updateNode(createParameterDeclaration(decorators, modifiers, node.dotDotDotToken, name, node.questionToken, type, initializer, /*location*/ node, /*flags*/ node.flags), node); + } + + return node; + } + // Type members + export function createProperty(decorators: Decorator[], modifiers: Modifier[], name: string | PropertyName, questionToken: Node, type: TypeNode, initializer: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.PropertyDeclaration, location); + node.decorators = decorators ? createNodeArray(decorators) : undefined; + node.modifiers = modifiers ? createNodeArray(modifiers) : undefined; + node.name = typeof name === "string" ? createIdentifier(name) : name; + node.questionToken = questionToken; + node.type = type; + node.initializer = initializer; + return node; + } + + export function updateProperty(node: PropertyDeclaration, decorators: Decorator[], modifiers: Modifier[], name: PropertyName, type: TypeNode, initializer: Expression) { + if (node.decorators !== decorators || node.modifiers !== modifiers || node.name !== name || node.type !== type || node.initializer !== initializer) { + return updateNode(createProperty(decorators, modifiers, name, node.questionToken, type, initializer, node), node); + } + return node; + } + export function createMethod(decorators: Decorator[], modifiers: Modifier[], asteriskToken: Node, name: string | PropertyName, typeParameters: TypeParameterDeclaration[], parameters: ParameterDeclaration[], type: TypeNode, body: Block, location?: TextRange, flags?: NodeFlags) { const node = createNode(SyntaxKind.MethodDeclaration, location, flags); node.decorators = decorators ? createNodeArray(decorators) : undefined; @@ -223,9 +291,9 @@ namespace ts { return node; } - export function updateMethod(node: MethodDeclaration, decorators: Decorator[], modifiers: Modifier[], asteriskToken: Node, name: PropertyName, typeParameters: TypeParameterDeclaration[], parameters: ParameterDeclaration[], type: TypeNode, body: Block) { - if (node.decorators !== decorators || node.modifiers !== modifiers || node.asteriskToken !== asteriskToken || node.name !== name || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.body !== body) { - return updateNode(createMethod(decorators, modifiers, asteriskToken, name, typeParameters, parameters, type, body, /*location*/ node, node.flags), node); + export function updateMethod(node: MethodDeclaration, decorators: Decorator[], modifiers: Modifier[], name: PropertyName, typeParameters: TypeParameterDeclaration[], parameters: ParameterDeclaration[], type: TypeNode, body: Block) { + if (node.decorators !== decorators || node.modifiers !== modifiers || node.name !== name || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.body !== body) { + return updateNode(createMethod(decorators, modifiers, node.asteriskToken, name, typeParameters, parameters, type, body, /*location*/ node, node.flags), node); } return node; } @@ -285,36 +353,47 @@ namespace ts { return node; } - export function createParameter(name: string | Identifier | BindingPattern, initializer?: Expression, location?: TextRange) { - return createParameterDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - /*dotDotDotToken*/ undefined, - name, - /*questionToken*/ undefined, - /*type*/ undefined, - initializer, - location - ); - } + // Binding Patterns - export function createParameterDeclaration(decorators: Decorator[], modifiers: Modifier[], dotDotDotToken: Node, name: string | Identifier | BindingPattern, questionToken: Node, type: TypeNode, initializer: Expression, location?: TextRange, flags?: NodeFlags) { - const node = createNode(SyntaxKind.Parameter, location, flags); - node.decorators = decorators ? createNodeArray(decorators) : undefined; - node.modifiers = modifiers ? createNodeArray(modifiers) : undefined; - node.dotDotDotToken = dotDotDotToken; - node.name = typeof name === "string" ? createIdentifier(name) : name; - node.questionToken = questionToken; - node.type = type; - node.initializer = initializer ? parenthesizeExpressionForList(initializer) : undefined; + export function createObjectBindingPattern(elements: BindingElement[], location?: TextRange) { + const node = createNode(SyntaxKind.ObjectBindingPattern, location); + node.elements = createNodeArray(elements); return node; } - export function updateParameterDeclaration(node: ParameterDeclaration, decorators: Decorator[], modifiers: Modifier[], dotDotDotToken: Node, name: BindingName, questionToken: Node, type: TypeNode, initializer: Expression) { - if (node.decorators !== decorators || node.modifiers !== modifiers || node.dotDotDotToken !== dotDotDotToken || node.name !== name || node.questionToken !== questionToken || node.type !== type || node.initializer !== initializer) { - return updateNode(createParameterDeclaration(decorators, modifiers, dotDotDotToken, name, questionToken, type, initializer, /*location*/ node, /*flags*/ node.flags), node); + export function updateObjectBindingPattern(node: ObjectBindingPattern, elements: BindingElement[]) { + if (node.elements !== elements) { + return updateNode(createObjectBindingPattern(elements, node), node); } + return node; + } + export function createArrayBindingPattern(elements: BindingElement[], location?: TextRange) { + const node = createNode(SyntaxKind.ArrayBindingPattern, location); + node.elements = createNodeArray(elements); + return node; + } + + export function updateArrayBindingPattern(node: ArrayBindingPattern, elements: BindingElement[]) { + if (node.elements !== elements) { + return updateNode(createArrayBindingPattern(elements, node), node); + } + return node; + } + + export function createBindingElement(propertyName: string | PropertyName, dotDotDotToken: Node, name: string | BindingName, initializer?: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.BindingElement, location); + node.propertyName = typeof propertyName === "string" ? createIdentifier(propertyName) : propertyName; + node.dotDotDotToken = dotDotDotToken; + node.name = typeof name === "string" ? createIdentifier(name) : name; + node.initializer = initializer; + return node; + } + + export function updateBindingElement(node: BindingElement, propertyName: PropertyName, name: BindingName, initializer: Expression) { + if (node.propertyName !== propertyName || node.name !== name || node.initializer !== initializer) { + return updateNode(createBindingElement(propertyName, node.dotDotDotToken, name, initializer, node), node); + } return node; } @@ -330,13 +409,26 @@ namespace ts { return node; } + export function updateArrayLiteral(node: ArrayLiteralExpression, elements: Expression[]) { + if (node.elements !== elements) { + return updateNode(createArrayLiteral(elements, node, node.multiLine), node); + } + return node; + } + export function createObjectLiteral(properties?: ObjectLiteralElement[], location?: TextRange, multiLine?: boolean) { const node = createNode(SyntaxKind.ObjectLiteralExpression, location); node.properties = createNodeArray(properties); if (multiLine) { node.multiLine = true; } + return node; + } + export function updateObjectLiteral(node: ObjectLiteralExpression, properties: ObjectLiteralElement[]) { + if (node.properties !== properties) { + return updateNode(createObjectLiteral(properties, node, node.multiLine), node); + } return node; } @@ -387,7 +479,6 @@ namespace ts { if (expression !== node.expression || typeArguments !== node.typeArguments || argumentsArray !== node.arguments) { return updateNode(createCall(expression, typeArguments, argumentsArray, /*location*/ node, node.flags), node); } - return node; } @@ -406,12 +497,33 @@ namespace ts { return node; } + export function createTaggedTemplate(tag: Expression, template: Template, location?: TextRange) { + const node = createNode(SyntaxKind.TaggedTemplateExpression, location); + node.tag = parenthesizeForAccess(tag); + node.template = template; + return node; + } + + export function updateTaggedTemplate(node: TaggedTemplateExpression, tag: Expression, template: Template) { + if (node.tag !== tag || node.template !== template) { + return updateNode(createTaggedTemplate(tag, template, node), node); + } + return node; + } + export function createParen(expression: Expression, location?: TextRange) { const node = createNode(SyntaxKind.ParenthesizedExpression, location); node.expression = expression; return node; } + export function updateParen(node: ParenthesizedExpression, expression: Expression) { + if (node.expression !== expression) { + return updateNode(createParen(expression, node), node); + } + return node; + } + export function createFunctionExpression(asteriskToken: Node, name: string | Identifier, typeParameters: TypeParameterDeclaration[], parameters: ParameterDeclaration[], type: TypeNode, body: Block, location?: TextRange, flags?: NodeFlags) { const node = createNode(SyntaxKind.FunctionExpression, location, flags); node.modifiers = undefined; @@ -449,18 +561,58 @@ namespace ts { return node; } - export function createTypeOf(expression: Expression) { - const node = createNode(SyntaxKind.TypeOfExpression); + export function createDelete(expression: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.DeleteExpression, location); node.expression = parenthesizePrefixOperand(expression); return node; } - export function createVoid(expression: Expression) { - const node = createNode(SyntaxKind.VoidExpression); + export function updateDelete(node: DeleteExpression, expression: Expression) { + if (node.expression !== expression) { + return updateNode(createDelete(expression, node), expression); + } + return node; + } + + export function createTypeOf(expression: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.TypeOfExpression, location); node.expression = parenthesizePrefixOperand(expression); return node; } + export function updateTypeOf(node: TypeOfExpression, expression: Expression) { + if (node.expression !== expression) { + return updateNode(createTypeOf(expression, node), expression); + } + return node; + } + + export function createVoid(expression: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.VoidExpression, location); + node.expression = parenthesizePrefixOperand(expression); + return node; + } + + export function updateVoid(node: VoidExpression, expression: Expression) { + if (node.expression !== expression) { + return updateNode(createVoid(expression, node), node); + } + return node; + } + + export function createAwait(expression: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.AwaitExpression, location); + node.expression = parenthesizePrefixOperand(expression); + return node; + } + + export function updateAwait(node: AwaitExpression, expression: Expression) { + if (node.expression !== expression) { + return updateNode(createAwait(expression, node), node); + } + return node; + } + export function createPrefix(operator: SyntaxKind, operand: Expression, location?: TextRange) { const node = createNode(SyntaxKind.PrefixUnaryExpression, location); node.operator = operator; @@ -468,6 +620,13 @@ namespace ts { return node; } + export function updatePrefix(node: PrefixUnaryExpression, operand: Expression) { + if (node.operand !== operand) { + return updateNode(createPrefix(node.operator, operand, node), node); + } + return node; + } + export function createPostfix(operand: Expression, operator: SyntaxKind, location?: TextRange) { const node = createNode(SyntaxKind.PostfixUnaryExpression, location); node.operand = parenthesizePostfixOperand(operand); @@ -475,70 +634,142 @@ namespace ts { return node; } - export function createBinary(left: Expression, operator: SyntaxKind, right: Expression, location?: TextRange) { - return createBinaryWithOperatorToken(left, createSynthesizedNode(operator), right, location); + export function updatePostfix(node: PostfixUnaryExpression, operand: Expression) { + if (node.operand !== operand) { + return updateNode(createPostfix(operand, node.operator, node), node); + } + return node; } - export function createBinaryWithOperatorToken(left: Expression, operatorToken: Node, right: Expression, location?: TextRange) { + export function createBinary(left: Expression, operator: SyntaxKind | Node, right: Expression, location?: TextRange) { + const operatorToken = typeof operator === "number" ? createSynthesizedNode(operator) : operator; + const operatorKind = operatorToken.kind; const node = createNode(SyntaxKind.BinaryExpression, location); - node.left = parenthesizeBinaryOperand(operatorToken.kind, left, /*isLeftSideOfBinary*/ true, /*leftOperand*/ undefined); + node.left = parenthesizeBinaryOperand(operatorKind, left, /*isLeftSideOfBinary*/ true, /*leftOperand*/ undefined); node.operatorToken = operatorToken; - node.right = parenthesizeBinaryOperand(operatorToken.kind, right, /*isLeftSideOfBinary*/ false, node.left); + node.right = parenthesizeBinaryOperand(operatorKind, right, /*isLeftSideOfBinary*/ false, node.left); return node; } export function updateBinary(node: BinaryExpression, left: Expression, right: Expression) { if (node.left !== left || node.right !== right) { - return updateNode(createBinaryWithOperatorToken(left, node.operatorToken, right, /*location*/ node), node); + return updateNode(createBinary(left, node.operatorToken, right, /*location*/ node), node); } return node; } - export function createConditional(condition: Expression, whenTrue: Expression, whenFalse: Expression) { - const node = createNode(SyntaxKind.ConditionalExpression); + export function createConditional(condition: Expression, questionToken: Node, whenTrue: Expression, colonToken: Node, whenFalse: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.ConditionalExpression, location); node.condition = condition; - node.questionToken = createSynthesizedNode(SyntaxKind.QualifiedName); + node.questionToken = questionToken; node.whenTrue = whenTrue; - node.colonToken = createSynthesizedNode(SyntaxKind.ColonToken); + node.colonToken = colonToken; node.whenFalse = whenFalse; return node; } - export function createYield(expression: Expression, location?: TextRange) { + export function updateConditional(node: ConditionalExpression, condition: Expression, whenTrue: Expression, whenFalse: Expression) { + if (node.condition !== condition || node.whenTrue !== whenTrue || node.whenFalse !== whenFalse) { + return updateNode(createConditional(condition, node.questionToken, whenTrue, node.colonToken, whenFalse, node), node); + } + return node; + } + + export function createTemplateExpression(head: TemplateLiteralFragment, templateSpans: TemplateSpan[], location?: TextRange) { + const node = createNode(SyntaxKind.TemplateExpression, location); + node.head = head; + node.templateSpans = createNodeArray(templateSpans); + return node; + } + + export function updateTemplateExpression(node: TemplateExpression, head: TemplateLiteralFragment, templateSpans: TemplateSpan[]) { + if (node.head !== head || node.templateSpans !== templateSpans) { + return updateNode(createTemplateExpression(head, templateSpans, node), node); + } + return node; + } + + export function createYield(asteriskToken: Node, expression: Expression, location?: TextRange) { const node = createNode(SyntaxKind.YieldExpression, location); + node.asteriskToken = asteriskToken; node.expression = expression; return node; } - export function createSpread(expression: Expression) { - const node = createNode(SyntaxKind.SpreadElementExpression); + export function updateYield(node: YieldExpression, expression: Expression) { + if (node.expression !== expression) { + return updateNode(createYield(node.asteriskToken, expression, node), node); + } + return node; + } + + export function createSpread(expression: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.SpreadElementExpression, location); node.expression = parenthesizeExpressionForList(expression); return node; } - export function createClassExpression(name: Identifier, heritageClauses: HeritageClause[], members: ClassElement[], location?: TextRange) { + export function updateSpread(node: SpreadElementExpression, expression: Expression) { + if (node.expression !== expression) { + return updateNode(createSpread(expression, node), node); + } + return node; + } + + export function createClassExpression(modifiers: Modifier[], name: Identifier, typeParameters: TypeParameterDeclaration[], heritageClauses: HeritageClause[], members: ClassElement[], location?: TextRange) { const node = createNode(SyntaxKind.ClassExpression, location); node.decorators = undefined; - node.modifiers = undefined; + node.modifiers = modifiers ? createNodeArray(modifiers) : undefined; node.name = name; - node.typeParameters = undefined; + node.typeParameters = typeParameters ? createNodeArray(typeParameters) : undefined; node.heritageClauses = createNodeArray(heritageClauses); node.members = createNodeArray(members); return node; } + export function updateClassExpression(node: ClassExpression, modifiers: Modifier[], name: Identifier, typeParameters: TypeParameterDeclaration[], heritageClauses: HeritageClause[], members: ClassElement[]) { + if (node.modifiers !== modifiers || node.name !== name || node.typeParameters !== typeParameters || node.heritageClauses !== heritageClauses || node.members !== members) { + return updateNode(createClassExpression(modifiers, name, typeParameters, heritageClauses, members, node), node); + } + return node; + } + export function createOmittedExpression(location?: TextRange) { const node = createNode(SyntaxKind.OmittedExpression, location); return node; } - export function createExpressionWithTypeArguments(expression: Expression, location?: TextRange) { + export function createExpressionWithTypeArguments(typeArguments: TypeNode[], expression: Expression, location?: TextRange) { const node = createNode(SyntaxKind.ExpressionWithTypeArguments, location); - node.typeArguments = undefined; + node.typeArguments = typeArguments ? createNodeArray(typeArguments) : undefined; node.expression = parenthesizeForAccess(expression); return node; } + export function updateExpressionWithTypeArguments(node: ExpressionWithTypeArguments, typeArguments: TypeNode[], expression: Expression) { + if (node.typeArguments !== typeArguments || node.expression !== expression) { + return updateNode(createExpressionWithTypeArguments(typeArguments, expression, node), node); + } + return node; + } + + + // Misc + + export function createTemplateSpan(expression: Expression, literal: TemplateLiteralFragment, location?: TextRange) { + const node = createNode(SyntaxKind.TemplateSpan, location); + node.expression = expression; + node.literal = literal; + return node; + } + + export function updateTemplateSpan(node: TemplateSpan, expression: Expression, literal: TemplateLiteralFragment) { + if (node.expression !== expression || node.literal !== literal) { + return updateNode(createTemplateSpan(expression, literal, node), node); + } + return node; + } + // Element export function createBlock(statements: Statement[], location?: TextRange, multiLine?: boolean, flags?: NodeFlags): Block { @@ -547,7 +778,6 @@ namespace ts { if (multiLine) { block.multiLine = true; } - return block; } @@ -587,14 +817,6 @@ namespace ts { return node; } - export function createLetDeclarationList(declarations: VariableDeclaration[], location?: TextRange) { - return createVariableDeclarationList(declarations, location, NodeFlags.Let); - } - - export function createConstDeclarationList(declarations: VariableDeclaration[], location?: TextRange) { - return createVariableDeclarationList(declarations, location, NodeFlags.Const); - } - export function createVariableDeclaration(name: string | BindingPattern | Identifier, type?: TypeNode, initializer?: Expression, location?: TextRange, flags?: NodeFlags): VariableDeclaration { const node = createNode(SyntaxKind.VariableDeclaration, location, flags); node.name = typeof name === "string" ? createIdentifier(name) : name; @@ -643,16 +865,31 @@ namespace ts { return node; } - export function createSwitch(expression: Expression, caseBlock: CaseBlock, location?: TextRange): SwitchStatement { - const node = createNode(SyntaxKind.SwitchStatement, location); - node.expression = parenthesizeExpressionForList(expression); - node.caseBlock = caseBlock; + export function createDo(statement: Statement, expression: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.DoStatement, location); + node.statement = statement; + node.expression = expression; return node; } - export function createCaseBlock(clauses: CaseOrDefaultClause[], location?: TextRange): CaseBlock { - const node = createNode(SyntaxKind.CaseBlock, location); - node.clauses = createNodeArray(clauses); + export function updateDo(node: DoStatement, statement: Statement, expression: Expression) { + if (node.statement !== statement || node.expression !== expression) { + return updateNode(createDo(statement, expression, node), node); + } + return node; + } + + export function createWhile(expression: Expression, statement: Statement, location?: TextRange) { + const node = createNode(SyntaxKind.WhileStatement, location); + node.expression = expression; + node.statement = statement; + return node; + } + + export function updateWhile(node: WhileStatement, expression: Expression, statement: Statement) { + if (node.expression !== expression || node.statement !== statement) { + return updateNode(createWhile(expression, statement, node), node); + } return node; } @@ -672,27 +909,6 @@ namespace ts { return node; } - export function createLabel(label: string | Identifier, statement: Statement, location?: TextRange) { - const node = createNode(SyntaxKind.LabeledStatement, location); - node.label = typeof label === "string" ? createIdentifier(label) : label; - node.statement = statement; - return node; - } - - export function createDo(statement: Statement, expression: Expression, location?: TextRange) { - const node = createNode(SyntaxKind.DoStatement, location); - node.statement = statement; - node.expression = expression; - return node; - } - - export function createWhile(expression: Expression, statement: Statement, location?: TextRange) { - const node = createNode(SyntaxKind.WhileStatement, location); - node.expression = expression; - node.statement = statement; - return node; - } - export function createForIn(initializer: ForInitializer, expression: Expression, statement: Statement, location?: TextRange) { const node = createNode(SyntaxKind.ForInStatement, location); node.initializer = initializer; @@ -716,26 +932,112 @@ namespace ts { return node; } + export function updateForOf(node: ForInStatement, initializer: ForInitializer, expression: Expression, statement: Statement) { + if (node.initializer !== initializer || node.expression !== expression || node.statement !== statement) { + return updateNode(createForOf(initializer, expression, statement, node), node); + } + return node; + } + + export function createContinue(label?: Identifier, location?: TextRange): BreakStatement { + const node = createNode(SyntaxKind.ContinueStatement, location); + if (label) { + node.label = label; + } + return node; + } + + export function updateContinue(node: ContinueStatement, label: Identifier) { + if (node.label !== label) { + return updateNode(createContinue(label, node), node); + } + return node; + } + + export function createBreak(label?: Identifier, location?: TextRange): BreakStatement { + const node = createNode(SyntaxKind.BreakStatement, location); + if (label) { + node.label = label; + } + return node; + } + + export function updateBreak(node: BreakStatement, label: Identifier) { + if (node.label !== label) { + return updateNode(createBreak(label, node), node); + } + return node; + } + export function createReturn(expression?: Expression, location?: TextRange): ReturnStatement { const node = createNode(SyntaxKind.ReturnStatement, location); node.expression = expression; return node; } - export function createWith(expression: Expression, statement: Statement, location?: TextRange): ReturnStatement { + export function updateReturn(node: ReturnStatement, expression: Expression) { + if (node.expression !== expression) { + return updateNode(createReturn(expression, /*location*/ node), node); + } + return node; + } + + export function createWith(expression: Expression, statement: Statement, location?: TextRange) { const node = createNode(SyntaxKind.WithStatement, location); node.expression = expression; node.statement = statement; return node; } - export function createThrow(expression: Expression, location?: TextRange): ReturnStatement { + export function updateWith(node: WithStatement, expression: Expression, statement: Statement) { + if (node.expression !== expression || node.statement !== statement) { + return updateNode(createWith(expression, statement, node), node); + } + return node; + } + + export function createSwitch(expression: Expression, caseBlock: CaseBlock, location?: TextRange): SwitchStatement { + const node = createNode(SyntaxKind.SwitchStatement, location); + node.expression = parenthesizeExpressionForList(expression); + node.caseBlock = caseBlock; + return node; + } + + export function updateSwitch(node: SwitchStatement, expression: Expression, caseBlock: CaseBlock) { + if (node.expression !== expression || node.caseBlock !== caseBlock) { + return updateNode(createSwitch(expression, caseBlock, node), node); + } + return node; + } + + export function createLabel(label: string | Identifier, statement: Statement, location?: TextRange) { + const node = createNode(SyntaxKind.LabeledStatement, location); + node.label = typeof label === "string" ? createIdentifier(label) : label; + node.statement = statement; + return node; + } + + export function updateLabel(node: LabeledStatement, label: Identifier, statement: Statement) { + if (node.label !== label || node.statement !== statement) { + return updateNode(createLabel(label, statement, node), node); + } + return node; + } + + export function createThrow(expression: Expression, location?: TextRange) { const node = createNode(SyntaxKind.ThrowStatement, location); node.expression = expression; return node; } - export function createTryCatchFinally(tryBlock: Block, catchClause: CatchClause, finallyBlock: Block, location?: TextRange) { + export function updateThrow(node: ThrowStatement, expression: Expression) { + if (node.expression !== expression) { + return updateNode(createThrow(expression, node), node); + } + return node; + } + + export function createTry(tryBlock: Block, catchClause: CatchClause, finallyBlock: Block, location?: TextRange) { const node = createNode(SyntaxKind.TryStatement, location); node.tryBlock = tryBlock; node.catchClause = catchClause; @@ -743,17 +1045,22 @@ namespace ts { return node; } - export function createTryCatch(tryBlock: Block, catchClause: CatchClause, location?: TextRange) { - return createTryCatchFinally(tryBlock, catchClause, /*finallyBlock*/ undefined, location); + export function updateTry(node: TryStatement, tryBlock: Block, catchClause: CatchClause, finallyBlock: Block) { + if (node.tryBlock !== tryBlock || node.catchClause !== catchClause || node.finallyBlock !== finallyBlock) { + return updateNode(createTry(tryBlock, catchClause, finallyBlock, node), node); + } + return node; } - export function createTryFinally(tryBlock: Block, finallyBlock: Block, location?: TextRange) { - return createTryCatchFinally(tryBlock, /*catchClause*/ undefined, finallyBlock, location); + export function createCaseBlock(clauses: CaseOrDefaultClause[], location?: TextRange): CaseBlock { + const node = createNode(SyntaxKind.CaseBlock, location); + node.clauses = createNodeArray(clauses); + return node; } - export function updateReturn(node: ReturnStatement, expression: Expression) { - if (node.expression !== expression) { - return updateNode(createReturn(expression, /*location*/ node), node); + export function updateCaseBlock(node: CaseBlock, clauses: CaseOrDefaultClause[]) { + if (node.clauses !== clauses) { + return updateNode(createCaseBlock(clauses, node), node); } return node; } @@ -778,44 +1085,251 @@ namespace ts { return node; } - export function createClassDeclaration(modifiers: Modifier[], name: Identifier, heritageClauses: HeritageClause[], members: ClassElement[], location?: TextRange) { + export function createClassDeclaration(decorators: Decorator[], modifiers: Modifier[], name: Identifier, typeParameters: TypeParameterDeclaration[], heritageClauses: HeritageClause[], members: ClassElement[], location?: TextRange) { const node = createNode(SyntaxKind.ClassDeclaration, location); - node.decorators = undefined; + node.decorators = decorators ? createNodeArray(decorators) : undefined; node.modifiers = modifiers ? createNodeArray(modifiers) : undefined; node.name = name; - node.typeParameters = undefined; + node.typeParameters = typeParameters ? createNodeArray(typeParameters) : undefined; node.heritageClauses = createNodeArray(heritageClauses); node.members = createNodeArray(members); return node; } - export function createExportDefault(expression: Expression) { - const node = createNode(SyntaxKind.ExportAssignment); - node.isExportEquals = false; + export function updateClassDeclaration(node: ClassDeclaration, decorators: Decorator[], modifiers: Modifier[], name: Identifier, typeParameters: TypeParameterDeclaration[], heritageClauses: HeritageClause[], members: ClassElement[]) { + if (node.decorators !== decorators || node.modifiers !== modifiers || node.name !== name || node.typeParameters !== typeParameters || node.heritageClauses !== heritageClauses || node.members !== members) { + return updateNode(createClassDeclaration(decorators, modifiers, name, typeParameters, heritageClauses, members, node), node); + } + return node; + } + + export function createImportDeclaration(decorators: Decorator[], modifiers: Modifier[], importClause: ImportClause, moduleSpecifier?: Expression, location?: TextRange): ImportDeclaration { + const node = createNode(SyntaxKind.ImportDeclaration, location); + node.decorators = decorators ? createNodeArray(decorators) : undefined; + node.modifiers = modifiers ? createNodeArray(modifiers) : undefined; + node.importClause = importClause; + node.moduleSpecifier = moduleSpecifier; + return node; + } + + export function updateImportDeclaration(node: ImportDeclaration, decorators: Decorator[], modifiers: Modifier[], importClause: ImportClause, moduleSpecifier: Expression) { + if (node.decorators !== decorators || node.modifiers !== modifiers || node.importClause !== importClause || node.moduleSpecifier !== moduleSpecifier) { + return updateNode(createImportDeclaration(decorators, modifiers, importClause, moduleSpecifier, node), node); + } + return node; + } + + export function createImportClause(name: Identifier, namedBindings: NamedImportBindings, location?: TextRange): ImportClause { + const node = createNode(SyntaxKind.ImportClause, location); + node.name = name; + node.namedBindings = namedBindings; + return node; + } + + export function updateImportClause(node: ImportClause, name: Identifier, namedBindings: NamedImportBindings) { + if (node.name !== name || node.namedBindings !== namedBindings) { + return updateNode(createImportClause(name, namedBindings, node), node); + } + return node; + } + + export function createNamespaceImport(name: Identifier, location?: TextRange): NamespaceImport { + const node = createNode(SyntaxKind.NamespaceImport, location); + node.name = name; + return node; + } + + export function updateNamespaceImport(node: NamespaceImport, name: Identifier) { + if (node.name !== name) { + return updateNode(createNamespaceImport(name, node), node); + } + return node; + } + + export function createNamedImports(elements: ImportSpecifier[], location?: TextRange): NamedImports { + const node = createNode(SyntaxKind.NamedImports, location); + node.elements = createNodeArray(elements); + return node; + } + + export function updateNamedImports(node: NamedImports, elements: ImportSpecifier[]) { + if (node.elements !== elements) { + return updateNode(createNamedImports(elements, node), node); + } + return node; + } + + export function createImportSpecifier(propertyName: Identifier, name: Identifier, location?: TextRange) { + const node = createNode(SyntaxKind.ImportSpecifier, location); + node.propertyName = propertyName; + node.name = name; + return node; + } + + export function updateImportSpecifier(node: ImportSpecifier, propertyName: Identifier, name: Identifier) { + if (node.propertyName !== propertyName || node.name !== name) { + return updateNode(createImportSpecifier(propertyName, name, node), node); + } + return node; + } + + export function createExportAssignment(decorators: Decorator[], modifiers: Modifier[], isExportEquals: boolean, expression: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.ExportAssignment, location); + node.decorators = decorators ? createNodeArray(decorators) : undefined; + node.modifiers = modifiers ? createNodeArray(modifiers) : undefined; + node.isExportEquals = isExportEquals; node.expression = expression; return node; } - export function createExportDeclaration(exportClause: NamedExports, moduleSpecifier?: Expression) { - const node = createNode(SyntaxKind.ExportDeclaration); + export function updateExportAssignment(node: ExportAssignment, decorators: Decorator[], modifiers: Modifier[], expression: Expression) { + if (node.decorators !== decorators || node.modifiers !== modifiers || node.expression !== expression) { + return updateNode(createExportAssignment(decorators, modifiers, node.isExportEquals, expression, node), node); + } + return node; + } + + export function createExportDeclaration(decorators: Decorator[], modifiers: Modifier[], exportClause: NamedExports, moduleSpecifier?: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.ExportDeclaration, location); + node.decorators = decorators ? createNodeArray(decorators) : undefined; + node.modifiers = modifiers ? createNodeArray(modifiers) : undefined; node.exportClause = exportClause; node.moduleSpecifier = moduleSpecifier; return node; } - export function createNamedExports(elements: ExportSpecifier[]) { - const node = createNode(SyntaxKind.NamedExports); + export function updateExportDeclaration(node: ExportDeclaration, decorators: Decorator[], modifiers: Modifier[], exportClause: NamedExports, moduleSpecifier: Expression) { + if (node.decorators !== decorators || node.modifiers !== modifiers || node.exportClause !== exportClause || node.moduleSpecifier !== moduleSpecifier) { + return updateNode(createExportDeclaration(decorators, modifiers, exportClause, moduleSpecifier, node), node); + } + return node; + } + + export function createNamedExports(elements: ExportSpecifier[], location?: TextRange) { + const node = createNode(SyntaxKind.NamedExports, location); node.elements = createNodeArray(elements); return node; } - export function createExportSpecifier(name: string | Identifier, propertyName?: string | Identifier) { - const node = createNode(SyntaxKind.ExportSpecifier); + export function updateNamedExports(node: NamedExports, elements: ExportSpecifier[]) { + if (node.elements !== elements) { + return updateNode(createNamedExports(elements, node), node); + } + return node; + } + + export function createExportSpecifier(name: string | Identifier, propertyName?: string | Identifier, location?: TextRange) { + const node = createNode(SyntaxKind.ExportSpecifier, location); node.name = typeof name === "string" ? createIdentifier(name) : name; node.propertyName = typeof propertyName === "string" ? createIdentifier(propertyName) : propertyName; return node; } + export function updateExportSpecifier(node: ExportSpecifier, name: Identifier, propertyName: Identifier) { + if (node.name !== name || node.propertyName !== propertyName) { + return updateNode(createExportSpecifier(name, propertyName, node), node); + } + return node; + } + + // JSX + + export function createJsxElement(openingElement: JsxOpeningElement, children: JsxChild[], closingElement: JsxClosingElement, location?: TextRange) { + const node = createNode(SyntaxKind.JsxElement, location); + node.openingElement = openingElement; + node.children = createNodeArray(children); + node.closingElement = closingElement; + return node; + } + + export function updateJsxElement(node: JsxElement, openingElement: JsxOpeningElement, children: JsxChild[], closingElement: JsxClosingElement) { + if (node.openingElement !== openingElement || node.children !== children || node.closingElement !== closingElement) { + return updateNode(createJsxElement(openingElement, children, closingElement, node), node); + } + return node; + } + + export function createJsxSelfClosingElement(tagName: JsxTagNameExpression, attributes: JsxAttributeLike[], location?: TextRange) { + const node = createNode(SyntaxKind.JsxSelfClosingElement, location); + node.tagName = tagName; + node.attributes = createNodeArray(attributes); + return node; + } + + export function updateJsxSelfClosingElement(node: JsxSelfClosingElement, tagName: JsxTagNameExpression, attributes: JsxAttributeLike[]) { + if (node.tagName !== tagName || node.attributes !== attributes) { + return updateNode(createJsxSelfClosingElement(tagName, attributes, node), node); + } + return node; + } + + export function createJsxOpeningElement(tagName: JsxTagNameExpression, attributes: JsxAttributeLike[], location?: TextRange) { + const node = createNode(SyntaxKind.JsxOpeningElement, location); + node.tagName = tagName; + node.attributes = createNodeArray(attributes); + return node; + } + + export function updateJsxOpeningElement(node: JsxOpeningElement, tagName: JsxTagNameExpression, attributes: JsxAttributeLike[]) { + if (node.tagName !== tagName || node.attributes !== attributes) { + return updateNode(createJsxOpeningElement(tagName, attributes, node), node); + } + return node; + } + + export function createJsxClosingElement(tagName: JsxTagNameExpression, location?: TextRange) { + const node = createNode(SyntaxKind.JsxClosingElement, location); + node.tagName = tagName; + return node; + } + + export function updateJsxClosingElement(node: JsxClosingElement, tagName: JsxTagNameExpression) { + if (node.tagName !== tagName) { + return updateNode(createJsxClosingElement(tagName, node), node); + } + return node; + } + + export function createJsxAttribute(name: Identifier, initializer: StringLiteral | JsxExpression, location?: TextRange) { + const node = createNode(SyntaxKind.JsxAttribute, location); + node.name = name; + node.initializer = initializer; + return node; + } + + export function updateJsxAttribute(node: JsxAttribute, name: Identifier, initializer: StringLiteral | JsxExpression) { + if (node.name !== name || node.initializer !== initializer) { + return updateNode(createJsxAttribute(name, initializer, node), node); + } + return node; + } + + export function createJsxSpreadAttribute(expression: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.JsxSpreadAttribute, location); + node.expression = expression; + return node; + } + + export function updateJsxSpreadAttribute(node: JsxSpreadAttribute, expression: Expression) { + if (node.expression !== expression) { + return updateNode(createJsxSpreadAttribute(expression, node), node); + } + return node; + } + + export function createJsxExpression(expression: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.JsxExpression, location); + node.expression = expression; + return node; + } + + export function updateJsxExpression(node: JsxExpression, expression: Expression) { + if (node.expression !== expression) { + return updateNode(createJsxExpression(expression, node), node); + } + return node; + } + // Clauses export function createHeritageClause(token: SyntaxKind, types: ExpressionWithTypeArguments[], location?: TextRange) { @@ -825,6 +1339,13 @@ namespace ts { return node; } + export function updateHeritageClause(node: HeritageClause, types: ExpressionWithTypeArguments[]) { + if (node.types !== types) { + return updateNode(createHeritageClause(node.token, types, node), node); + } + return node; + } + export function createCaseClause(expression: Expression, statements: Statement[], location?: TextRange) { const node = createNode(SyntaxKind.CaseClause, location); node.expression = parenthesizeExpressionForList(expression); @@ -832,6 +1353,40 @@ namespace ts { return node; } + export function updateCaseClause(node: CaseClause, expression: Expression, statements: Statement[]) { + if (node.expression !== expression || node.statements !== statements) { + return updateNode(createCaseClause(expression, statements, node), node); + } + return node; + } + + export function createDefaultClause(statements: Statement[], location?: TextRange) { + const node = createNode(SyntaxKind.DefaultClause, location); + node.statements = createNodeArray(statements); + return node; + } + + export function updateDefaultClause(node: DefaultClause, statements: Statement[]) { + if (node.statements !== statements) { + return updateNode(createDefaultClause(statements, node), node); + } + return node; + } + + export function createCatchClause(variableDeclaration: string | VariableDeclaration, block: Block, location?: TextRange) { + const node = createNode(SyntaxKind.CatchClause, location); + node.variableDeclaration = typeof variableDeclaration === "string" ? createVariableDeclaration(variableDeclaration) : variableDeclaration; + node.block = block; + return node; + } + + export function updateCatchClause(node: CatchClause, variableDeclaration: VariableDeclaration, block: Block) { + if (node.variableDeclaration !== variableDeclaration || node.block !== block) { + return updateNode(createCatchClause(variableDeclaration, block, node), node); + } + return node; + } + // Property assignments export function createPropertyAssignment(name: string | PropertyName, initializer: Expression, location?: TextRange) { @@ -842,6 +1397,27 @@ namespace ts { return node; } + export function updatePropertyAssignment(node: PropertyAssignment, name: PropertyName, initializer: Expression) { + if (node.name !== name || node.initializer !== initializer) { + return updateNode(createPropertyAssignment(name, initializer, node), node); + } + return node; + } + + export function createShorthandPropertyAssignment(name: string | Identifier, objectAssignmentInitializer: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.ShorthandPropertyAssignment, location); + node.name = typeof name === "string" ? createIdentifier(name) : name; + node.objectAssignmentInitializer = objectAssignmentInitializer !== undefined ? parenthesizeExpressionForList(objectAssignmentInitializer) : undefined; + return node; + } + + export function updateShorthandPropertyAssignment(node: ShorthandPropertyAssignment, name: Identifier, objectAssignmentInitializer: Expression) { + if (node.name !== name || node.objectAssignmentInitializer !== objectAssignmentInitializer) { + return updateNode(createShorthandPropertyAssignment(name, objectAssignmentInitializer, node), node); + } + return node; + } + // Top-level nodes export function updateSourceFileNode(node: SourceFile, statements: Statement[]) { @@ -912,6 +1488,13 @@ namespace ts { return node; } + export function updatePartiallyEmittedExpression(node: PartiallyEmittedExpression, expression: Expression) { + if (node.expression !== expression) { + return updateNode(createPartiallyEmittedExpression(expression, node.original, node), node); + } + return node; + } + // Compound nodes export function createComma(left: Expression, right: Expression) { @@ -962,32 +1545,6 @@ namespace ts { return createVoid(createLiteral(0)); } - export function createImportDeclaration(importClause: ImportClause, moduleSpecifier?: Expression, location?: TextRange): ImportDeclaration { - const node = createNode(SyntaxKind.ImportDeclaration, location); - node.importClause = importClause; - node.moduleSpecifier = moduleSpecifier; - return node; - } - - export function createImportClause(name: Identifier, namedBindings: NamedImportBindings, location?: TextRange): ImportClause { - const node = createNode(SyntaxKind.ImportClause, location); - node.name = name; - node.namedBindings = namedBindings; - return node; - } - - export function createNamespaceImport(name: Identifier): NamespaceImport { - const node = createNode(SyntaxKind.NamespaceImport); - node.name = name; - return node; - } - - export function createNamedImports(elements: NodeArray, location?: TextRange): NamedImports { - const node = createNode(SyntaxKind.NamedImports, location); - node.elements = elements; - return node; - } - export function createMemberAccessForPropertyName(target: Expression, memberName: PropertyName, location?: TextRange): MemberExpression { if (isComputedPropertyName(memberName)) { return createElementAccess(target, memberName.expression, location); @@ -1023,22 +1580,6 @@ namespace ts { ); } - export function createBreak(label?: Identifier, location?: TextRange): BreakStatement { - const node = createNode(SyntaxKind.BreakStatement, location); - if (label) { - node.label = label; - } - return node; - } - - export function createContinue(label?: Identifier, location?: TextRange): BreakStatement { - const node = createNode(SyntaxKind.ContinueStatement, location); - if (label) { - node.label = label; - } - return node; - } - export function createFunctionApply(func: Expression, thisArg: Expression, argumentsExpression: Expression, location?: TextRange) { return createCall( createPropertyAccess(func, "apply"), @@ -1098,7 +1639,15 @@ namespace ts { argumentsList.push(createNull()); } - addNodes(argumentsList, children, /*startOnNewLine*/ children.length > 1); + if (children.length > 1) { + for (const child of children) { + child.startsOnNewLine = true; + argumentsList.push(child); + } + } + else { + argumentsList.push(children[0]); + } } return createCall( @@ -1112,6 +1661,14 @@ namespace ts { ); } + export function createLetDeclarationList(declarations: VariableDeclaration[], location?: TextRange) { + return createVariableDeclarationList(declarations, location, NodeFlags.Let); + } + + export function createConstDeclarationList(declarations: VariableDeclaration[], location?: TextRange) { + return createVariableDeclarationList(declarations, location, NodeFlags.Const); + } + // Helpers export function createHelperName(externalHelpersModuleName: Identifier | undefined, name: string) { diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 1e25bd8cf0e..44565945d51 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1441,6 +1441,7 @@ namespace ts { // checked is to not pass the file to getEmitResolver. const emitResolver = getDiagnosticsProducingTypeChecker().getEmitResolver((options.outFile || options.out) ? undefined : sourceFile); + performance.emit("beforeEmit"); const emitStart = performance.mark(); const emitResult = emitFiles( @@ -1449,6 +1450,7 @@ namespace ts { sourceFile); performance.measure("emitTime", emitStart); + performance.emit("afterEmit"); return emitResult; } diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index 2d39b3a1c59..e211ca61f36 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -351,7 +351,9 @@ namespace ts { value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); return createConditional( createStrictEquality(value, createVoidZero()), + createToken(SyntaxKind.QuestionToken), defaultValue, + createToken(SyntaxKind.ColonToken), value ); } diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index 7deaa38d148..e6c8355c878 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -1196,7 +1196,7 @@ namespace ts { */ function transformAccessorsToStatement(receiver: LeftHandSideExpression, accessors: AllAccessorDeclarations): Statement { const statement = createStatement( - transformAccessorsToExpression(receiver, accessors), + transformAccessorsToExpression(receiver, accessors, /*startsOnNewLine*/ false), /*location*/ getSourceMapRange(accessors.firstAccessor) ); @@ -1213,7 +1213,7 @@ namespace ts { * * @param receiver The receiver for the member. */ - function transformAccessorsToExpression(receiver: LeftHandSideExpression, { firstAccessor, getAccessor, setAccessor }: AllAccessorDeclarations): Expression { + function transformAccessorsToExpression(receiver: LeftHandSideExpression, { firstAccessor, getAccessor, setAccessor }: AllAccessorDeclarations, startsOnNewLine: boolean): Expression { // To align with source maps in the old emitter, the receiver and property name // arguments are both mapped contiguously to the accessor name. const target = getMutableClone(receiver); @@ -1246,7 +1246,7 @@ namespace ts { createPropertyAssignment("configurable", createLiteral(true)) ); - return createCall( + const call = createCall( createPropertyAccess(createIdentifier("Object"), "defineProperty"), /*typeArguments*/ undefined, [ @@ -1255,6 +1255,10 @@ namespace ts { createObjectLiteral(properties, /*location*/ undefined, /*multiLine*/ true) ] ); + if (startsOnNewLine) { + call.startsOnNewLine = true; + } + return call; } /** @@ -1895,26 +1899,27 @@ namespace ts { // Write out the first non-computed properties, then emit the rest through indexing on the temp variable. const expressions: Expression[] = []; - addNode(expressions, - createAssignment( - temp, - setNodeEmitFlags( - createObjectLiteral( - visitNodes(properties, visitor, isObjectLiteralElement, 0, numInitialProperties), - /*location*/ undefined, - node.multiLine - ), - NodeEmitFlags.Indented - ) - ), - node.multiLine + const assignment = createAssignment( + temp, + setNodeEmitFlags( + createObjectLiteral( + visitNodes(properties, visitor, isObjectLiteralElement, 0, numInitialProperties), + /*location*/ undefined, + node.multiLine + ), + NodeEmitFlags.Indented + ) ); + if (node.multiLine) { + assignment.startsOnNewLine = true; + } + expressions.push(assignment); addObjectLiteralMembers(expressions, node, temp, numInitialProperties); // We need to clone the temporary identifier so that we can write it on a // new line - addNode(expressions, getMutableClone(temp), node.multiLine); + expressions.push(node.multiLine ? startOnNewLine(getMutableClone(temp)) : temp); return inlineExpressions(expressions); } @@ -2313,21 +2318,21 @@ namespace ts { case SyntaxKind.SetAccessor: const accessors = getAllAccessorDeclarations(node.properties, property); if (property === accessors.firstAccessor) { - addNode(expressions, transformAccessorsToExpression(receiver, accessors), node.multiLine); + expressions.push(transformAccessorsToExpression(receiver, accessors, node.multiLine)); } break; case SyntaxKind.PropertyAssignment: - addNode(expressions, transformPropertyAssignmentToExpression(node, property, receiver), node.multiLine); + expressions.push(transformPropertyAssignmentToExpression(node, property, receiver, node.multiLine)); break; case SyntaxKind.ShorthandPropertyAssignment: - addNode(expressions, transformShorthandPropertyAssignmentToExpression(node, property, receiver), node.multiLine); + expressions.push(transformShorthandPropertyAssignmentToExpression(node, property, receiver, node.multiLine)); break; case SyntaxKind.MethodDeclaration: - addNode(expressions, transformObjectLiteralMethodDeclarationToExpression(node, property, receiver), node.multiLine); + expressions.push(transformObjectLiteralMethodDeclarationToExpression(node, property, receiver, node.multiLine)); break; default: @@ -2344,8 +2349,8 @@ namespace ts { * @param property The PropertyAssignment node. * @param receiver The receiver for the assignment. */ - function transformPropertyAssignmentToExpression(node: ObjectLiteralExpression, property: PropertyAssignment, receiver: Expression) { - return createAssignment( + function transformPropertyAssignmentToExpression(node: ObjectLiteralExpression, property: PropertyAssignment, receiver: Expression, startsOnNewLine: boolean) { + const expression = createAssignment( createMemberAccessForPropertyName( receiver, visitNode(property.name, visitor, isPropertyName) @@ -2353,6 +2358,10 @@ namespace ts { visitNode(property.initializer, visitor, isExpression), /*location*/ property ); + if (startsOnNewLine) { + expression.startsOnNewLine = true; + } + return expression; } /** @@ -2362,8 +2371,8 @@ namespace ts { * @param property The ShorthandPropertyAssignment node. * @param receiver The receiver for the assignment. */ - function transformShorthandPropertyAssignmentToExpression(node: ObjectLiteralExpression, property: ShorthandPropertyAssignment, receiver: Expression) { - return createAssignment( + function transformShorthandPropertyAssignmentToExpression(node: ObjectLiteralExpression, property: ShorthandPropertyAssignment, receiver: Expression, startsOnNewLine: boolean) { + const expression = createAssignment( createMemberAccessForPropertyName( receiver, visitNode(property.name, visitor, isPropertyName) @@ -2371,6 +2380,10 @@ namespace ts { getSynthesizedClone(property.name), /*location*/ property ); + if (startsOnNewLine) { + expression.startsOnNewLine = true; + } + return expression; } /** @@ -2380,8 +2393,8 @@ namespace ts { * @param method The MethodDeclaration node. * @param receiver The receiver for the assignment. */ - function transformObjectLiteralMethodDeclarationToExpression(node: ObjectLiteralExpression, method: MethodDeclaration, receiver: Expression) { - return createAssignment( + function transformObjectLiteralMethodDeclarationToExpression(node: ObjectLiteralExpression, method: MethodDeclaration, receiver: Expression, startsOnNewLine: boolean) { + const expression = createAssignment( createMemberAccessForPropertyName( receiver, visitNode(method.name, visitor, isPropertyName) @@ -2389,6 +2402,10 @@ namespace ts { transformFunctionLikeToExpression(method, /*location*/ method, /*name*/ undefined), /*location*/ method ); + if (startsOnNewLine) { + expression.startsOnNewLine = true; + } + return expression; } /** diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index f96aacdce45..d161eb907a4 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -580,8 +580,8 @@ namespace ts { transformAndEmitStatements(body.statements, statementOffset); const buildResult = build(); - addNodes(statements, endLexicalEnvironment()); - addNode(statements, createReturn(buildResult)); + addRange(statements, endLexicalEnvironment()); + statements.push(createReturn(buildResult)); // Restore previous generator state inGeneratorFunctionBody = savedInGeneratorFunctionBody; @@ -1019,7 +1019,7 @@ namespace ts { ); const expressions = reduceLeft(properties, reduceProperty, [], numInitialProperties); - addNode(expressions, getMutableClone(temp), multiLine); + expressions.push(multiLine ? startOnNewLine(getMutableClone(temp)) : temp); return inlineExpressions(expressions); function reduceProperty(expressions: Expression[], property: ObjectLiteralElement) { @@ -1029,7 +1029,13 @@ namespace ts { } const expression = createExpressionForObjectLiteralElement(node, property, temp); - addNode(expressions, visitNode(expression, visitor, isExpression), multiLine); + const visited = visitNode(expression, visitor, isExpression); + if (visited) { + if (multiLine) { + visited.startsOnNewLine = true; + } + expressions.push(visited); + } return expressions; } } diff --git a/src/compiler/transformers/module/es6.ts b/src/compiler/transformers/module/es6.ts index 5bf8a972aff..2716165f2d3 100644 --- a/src/compiler/transformers/module/es6.ts +++ b/src/compiler/transformers/module/es6.ts @@ -65,7 +65,11 @@ namespace ts { return node; } return newExportClause - ? createExportDeclaration(newExportClause, node.moduleSpecifier) + ? createExportDeclaration( + /*decorators*/ undefined, + /*modifiers*/ undefined, + newExportClause, + node.moduleSpecifier) : undefined; } @@ -92,7 +96,11 @@ namespace ts { return undefined; } else if (newImportClause !== node.importClause) { - return createImportDeclaration(newImportClause, node.moduleSpecifier); + return createImportDeclaration( + /*decorators*/ undefined, + /*modifiers*/ undefined, + newImportClause, + node.moduleSpecifier); } } return node; diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index ce699630ba7..29f1971568e 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -734,8 +734,10 @@ namespace ts { statements.push( setOriginalNode( createClassDeclaration( + /*decorators*/ undefined, /*modifiers*/ undefined, name, + /*typeParameters*/ undefined, node.heritageClauses, node.members, /*location*/ node @@ -908,7 +910,7 @@ namespace ts { setNodeEmitFlags(node, NodeEmitFlags.NoSubstitution); let transformedUnaryExpression: BinaryExpression; if (node.kind === SyntaxKind.PostfixUnaryExpression) { - transformedUnaryExpression = createBinaryWithOperatorToken( + transformedUnaryExpression = createBinary( operand, createNode(operator === SyntaxKind.PlusPlusToken ? SyntaxKind.PlusEqualsToken : SyntaxKind.MinusEqualsToken), createLiteral(1), diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 8f956c19adc..1f7897b760c 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -197,7 +197,7 @@ namespace ts { const statementOffset = addPrologueDirectives(statements, node.statements, /*ensureUseStrict*/ !compilerOptions.noImplicitUseStrict, visitSourceElement); // var __moduleName = context_1 && context_1.id; - addNode(statements, + statements.push( createVariableStatement( /*modifiers*/ undefined, createVariableDeclarationList([ @@ -226,14 +226,14 @@ namespace ts { // - Temporary variables will appear at the top rather than at the bottom of the file // - Calls to the exporter for exported function declarations are grouped after // the declarations. - addNodes(statements, endLexicalEnvironment()); + addRange(statements, endLexicalEnvironment()); // Emit early exports for function declarations. - addNodes(statements, exportedFunctionDeclarations); + addRange(statements, exportedFunctionDeclarations); const exportStarFunction = addExportStarIfNeeded(statements); - addNode(statements, + statements.push( createReturn( setMultiLine( createObjectLiteral([ @@ -292,7 +292,7 @@ namespace ts { if (exportedLocalNames) { for (const exportedLocalName of exportedLocalNames) { // write name of exported declaration, i.e 'export var x...' - addNode(exportedNames, + exportedNames.push( createPropertyAssignment( createLiteral(exportedLocalName.text), createLiteral(true) @@ -314,7 +314,7 @@ namespace ts { for (const element of exportDecl.exportClause.elements) { // write name of indirectly exported entry, i.e. 'export {x} from ...' - addNode(exportedNames, + exportedNames.push( createPropertyAssignment( createLiteral((element.name || element.propertyName).text), createLiteral(true) @@ -324,7 +324,7 @@ namespace ts { } const exportedNamesStorageRef = createUniqueName("exportedNames"); - addNode(statements, + statements.push( createVariableStatement( /*modifiers*/ undefined, createVariableDeclarationList([ @@ -365,7 +365,7 @@ namespace ts { case SyntaxKind.ImportEqualsDeclaration: Debug.assert(importVariableName !== undefined); // save import into the local - addNode(statements, + statements.push( createStatement( createAssignment(importVariableName, parameterName) ) @@ -396,7 +396,7 @@ namespace ts { ); } - addNode(statements, + statements.push( createStatement( createCall( exportFunctionForFile, @@ -412,7 +412,7 @@ namespace ts { // emit as: // // exportStar(foo_1_1); - addNode(statements, + statements.push( createStatement( createCall( exportStarFunction, @@ -426,7 +426,7 @@ namespace ts { } } - addNode(setters, + setters.push( createFunctionExpression( /*asteriskToken*/ undefined, /*name*/ undefined, @@ -563,7 +563,7 @@ namespace ts { function visitExportDeclaration(node: ExportDeclaration): VisitResult { if (!node.moduleSpecifier) { const statements: Statement[] = []; - addNodes(statements, map(node.exportClause.elements, visitExportSpecifier)); + addRange(statements, map(node.exportClause.elements, visitExportSpecifier)); return statements; } @@ -612,7 +612,10 @@ namespace ts { const isExported = hasModifier(node, ModifierFlags.Export); const expressions: Expression[] = []; for (const variable of node.declarationList.declarations) { - addNode(expressions, transformVariable(variable, isExported)); + const visited = transformVariable(variable, isExported); + if (visited) { + expressions.push(visited); + } } if (expressions.length) { @@ -715,12 +718,14 @@ namespace ts { const statements: Statement[] = []; // Rewrite the class declaration into an assignment of a class expression. - addNode(statements, + statements.push( createStatement( createAssignment( name, createClassExpression( + /*modifiers*/ undefined, node.name, + /*typeParameters*/ undefined, node.heritageClauses, node.members, /*location*/ node @@ -736,7 +741,7 @@ namespace ts { recordExportName(name); } - addNode(statements, createDeclarationExport(node)); + statements.push(createDeclarationExport(node)); } return statements; @@ -756,7 +761,10 @@ namespace ts { if (shouldHoistLoopInitializer(initializer)) { const expressions: Expression[] = []; for (const variable of (initializer).declarations) { - addNode(expressions, transformVariable(variable, /*isExported*/ false)); + const visited = transformVariable(variable, /*isExported*/ false); + if (visited) { + expressions.push(visited); + } }; return createFor( @@ -1207,7 +1215,7 @@ namespace ts { ); } - addNode(statements, + statements.push( createFunctionDeclaration( /*decorators*/ undefined, /*modifiers*/ undefined, diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index d4bf1250731..ee69e5927c8 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -428,6 +428,8 @@ namespace ts { const statementOffset = addPrologueDirectives(statements, node.statements, /*ensureUseStrict*/ false, visitor); const externalHelpersModuleName = createUniqueName(externalHelpersModuleNameText); const externalHelpersModuleImport = createImportDeclaration( + /*decorators*/ undefined, + /*modifiers*/ undefined, createImportClause(/*name*/ undefined, createNamespaceImport(externalHelpersModuleName)), createLiteral(externalHelpersModuleNameText) ); @@ -506,8 +508,10 @@ namespace ts { // ${members} // } const classDeclaration = createClassDeclaration( + /*decorators*/ undefined, visitNodes(node.modifiers, visitor, isModifier), name, + /*typeParameters*/ undefined, visitNodes(node.heritageClauses, visitor, isHeritageClause), transformClassMembers(node, hasExtendsClause), /*location*/ node @@ -548,7 +552,11 @@ namespace ts { } else if (isDecoratedClass) { if (isDefaultExternalModuleExport(node)) { - statements.push(createExportDefault(getLocalName(node))); + statements.push(createExportAssignment( + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*isExportEquals*/ false, + getLocalName(node))); } else if (isNamedExternalModuleExport(node)) { statements.push(createExternalModuleExport(name)); @@ -660,7 +668,9 @@ namespace ts { // } const classExpression: Expression = setOriginalNode( createClassExpression( + /*modifiers*/ undefined, name, + /*typeParameters*/ undefined, visitNodes(node.heritageClauses, visitor, isHeritageClause), transformClassMembers(node, hasExtendsClause), /*location*/ location @@ -684,7 +694,7 @@ namespace ts { // let ${name} = ${classExpression} where name is either declaredName if the class doesn't contain self-reference // or decoratedClassAlias if the class contain self-reference. - addNode(statements, + statements.push( setOriginalNode( createVariableStatement( /*modifiers*/ undefined, @@ -706,7 +716,7 @@ namespace ts { // TDZ as the class. // let ${declaredName} = ${decoratedClassAlias} - addNode(statements, + statements.push( setOriginalNode( createVariableStatement( /*modifiers*/ undefined, @@ -743,7 +753,9 @@ namespace ts { const classExpression = setOriginalNode( createClassExpression( + /*modifiers*/ undefined, node.name, + /*typeParameters*/ undefined, heritageClauses, members, /*location*/ node @@ -757,15 +769,15 @@ namespace ts { if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference) { // record an alias as the class name is not in scope for statics. enableSubstitutionForClassAliases(); - classAliases[getOriginalNodeId(node)] = temp; + classAliases[getOriginalNodeId(node)] = getSynthesizedClone(temp); } // To preserve the behavior of the old emitter, we explicitly indent // the body of a class with static initializers. setNodeEmitFlags(classExpression, NodeEmitFlags.Indented | getNodeEmitFlags(classExpression)); - addNode(expressions, createAssignment(temp, classExpression), true); - addNodes(expressions, generateInitializedPropertyExpressions(node, staticProperties, temp), true); - addNode(expressions, temp, true); + expressions.push(startOnNewLine(createAssignment(temp, classExpression))); + addRange(expressions, generateInitializedPropertyExpressions(node, staticProperties, temp)); + expressions.push(startOnNewLine(temp)); return inlineExpressions(expressions); } @@ -780,8 +792,12 @@ namespace ts { */ function transformClassMembers(node: ClassDeclaration | ClassExpression, hasExtendsClause: boolean) { const members: ClassElement[] = []; - addNode(members, transformConstructor(node, hasExtendsClause)); - addNodes(members, visitNodes(node.members, classElementVisitor, isClassElement)); + const constructor = transformConstructor(node, hasExtendsClause); + if (constructor) { + members.push(constructor); + } + + addRange(members, visitNodes(node.members, classElementVisitor, isClassElement)); return createNodeArray(members, /*location*/ node.members); } @@ -870,7 +886,7 @@ namespace ts { // } // const propertyAssignments = getParametersWithPropertyAssignments(constructor); - addNodes(statements, map(propertyAssignments, transformParameterWithPropertyAssignment)); + addRange(statements, map(propertyAssignments, transformParameterWithPropertyAssignment)); } else if (hasExtendsClause) { Debug.assert(parameters.length === 1 && isIdentifier(parameters[0].name)); @@ -879,7 +895,7 @@ namespace ts { // // super(...args); // - addNode(statements, + statements.push( createStatement( createCall( createSuper(), @@ -905,11 +921,11 @@ namespace ts { if (constructor) { // The class already had a constructor, so we should add the existing statements, skipping the initial super call. - addNodes(statements, visitNodes(constructor.body.statements, visitor, isStatement, indexOfFirstStatement)); + addRange(statements, visitNodes(constructor.body.statements, visitor, isStatement, indexOfFirstStatement)); } // End the lexical environment. - addNodes(statements, endLexicalEnvironment()); + addRange(statements, endLexicalEnvironment()); return setMultiLine( createBlock( createNodeArray( @@ -1065,6 +1081,7 @@ namespace ts { const expressions: Expression[] = []; for (const property of properties) { const expression = transformInitializedProperty(node, property, receiver); + expression.startsOnNewLine = true; setSourceMapRange(expression, moveRangePastModifiers(property)); setCommentRange(expression, property); expressions.push(expression); @@ -1277,8 +1294,8 @@ namespace ts { } const decoratorExpressions: Expression[] = []; - addNodes(decoratorExpressions, map(allDecorators.decorators, transformDecorator)); - addNodes(decoratorExpressions, flatMap(allDecorators.parameters, transformDecoratorsOfParameter)); + addRange(decoratorExpressions, map(allDecorators.decorators, transformDecorator)); + addRange(decoratorExpressions, flatMap(allDecorators.parameters, transformDecoratorsOfParameter)); addTypeMetadata(node, decoratorExpressions); return decoratorExpressions; } @@ -1874,7 +1891,9 @@ namespace ts { createTypeOf(createIdentifier("Symbol")), createLiteral("function") ), + createToken(SyntaxKind.QuestionToken), createIdentifier("Symbol"), + createToken(SyntaxKind.ColonToken), createIdentifier("Object") ); } @@ -1960,6 +1979,7 @@ namespace ts { function visitExpressionWithTypeArguments(node: ExpressionWithTypeArguments): ExpressionWithTypeArguments { const expression = visitNode(node.expression, visitor, isLeftHandSideExpression); return createExpressionWithTypeArguments( + /*typeArguments*/ undefined, expression, node ); @@ -2208,7 +2228,7 @@ namespace ts { startLexicalEnvironment(); const visited: Expression | Block = visitNode(body, visitor, isConciseBody); const declarations = endLexicalEnvironment(); - const merged = mergeConciseBodyLexicalEnvironment(visited, declarations); + const merged = mergeFunctionBodyLexicalEnvironment(visited, declarations); if (forceBlockFunctionBody && !isBlock(merged)) { return createBlock([ createReturn(merged) @@ -2378,6 +2398,7 @@ namespace ts { function visitAwaitExpression(node: AwaitExpression): Expression { return setOriginalNode( createYield( + /*asteriskToken*/ undefined, visitNode(node.expression, visitor, isExpression), /*location*/ node ), @@ -2546,8 +2567,8 @@ namespace ts { const statements: Statement[] = []; startLexicalEnvironment(); - addNodes(statements, map(node.members, transformEnumMember)); - addNodes(statements, endLexicalEnvironment()); + addRange(statements, map(node.members, transformEnumMember)); + addRange(statements, endLexicalEnvironment()); currentNamespaceContainerName = savedCurrentNamespaceLocalName; return createBlock( @@ -2784,17 +2805,26 @@ namespace ts { let blockLocation: TextRange; const body = node.body; if (body.kind === SyntaxKind.ModuleBlock) { - addNodes(statements, visitNodes((body).statements, namespaceElementVisitor, isStatement)); + addRange(statements, visitNodes((body).statements, namespaceElementVisitor, isStatement)); statementsLocation = (body).statements; blockLocation = body; } else { - addNode(statements, visitModuleDeclaration(body)); + const result = visitModuleDeclaration(body); + if (result) { + if (isArray(result)) { + addRange(statements, result); + } + else { + statements.push(result); + } + } + const moduleBlock = getInnerMostModuleDeclarationFromDottedModule(node).body; statementsLocation = moveRangePos(moduleBlock.statements, -1); } - addNodes(statements, endLexicalEnvironment()); + addRange(statements, endLexicalEnvironment()); currentNamespaceContainerName = savedCurrentNamespaceContainerName; currentNamespace = savedCurrentNamespace; @@ -2955,6 +2985,8 @@ namespace ts { function createExternalModuleExport(exportName: Identifier) { return createExportDeclaration( + /*decorators*/ undefined, + /*modifiers*/ undefined, createNamedExports([ createExportSpecifier(exportName) ]) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index a8222d0ebc5..7ab126556ec 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4057,6 +4057,13 @@ namespace ts { return node.kind === SyntaxKind.JsxClosingElement; } + export function isJsxTagNameExpression(node: Node): node is JsxTagNameExpression { + const kind = node.kind; + return kind === SyntaxKind.ThisKeyword + || kind === SyntaxKind.Identifier + || kind === SyntaxKind.PropertyAccessExpression; + } + export function isJsxChild(node: Node): node is JsxChild { const kind = node.kind; return kind === SyntaxKind.JsxElement diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index bb991760dcb..58f203a0a6f 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -38,6 +38,8 @@ namespace ts { * Each edge corresponds to a property in a Node subtype that should be traversed when visiting * each child. The properties are assigned in the order in which traversal should occur. * + * We only add entries for nodes that do not have a create/update pair defined in factory.ts + * * NOTE: This needs to be kept up to date with changes to nodes in "types.ts". Currently, this * map is not comprehensive. Only node edges relevant to tree transformation are * currently defined. We may extend this to be more comprehensive, and eventually @@ -49,173 +51,13 @@ namespace ts { { name: "left", test: isEntityName }, { name: "right", test: isIdentifier } ], - [SyntaxKind.ComputedPropertyName]: [ - { name: "expression", test: isExpression } - ], - [SyntaxKind.Parameter]: [ - { name: "decorators", test: isDecorator }, - { name: "modifiers", test: isModifier }, - { name: "name", test: isBindingName }, - { name: "type", test: isTypeNode, optional: true }, - { name: "initializer", test: isExpression, optional: true, parenthesize: parenthesizeExpressionForList } - ], [SyntaxKind.Decorator]: [ { name: "expression", test: isLeftHandSideExpression } ], - [SyntaxKind.PropertyDeclaration]: [ - { name: "decorators", test: isDecorator }, - { name: "modifiers", test: isModifier }, - { name: "name", test: isPropertyName }, - { name: "type", test: isTypeNode, optional: true }, - { name: "initializer", test: isExpression, optional: true } - ], - [SyntaxKind.MethodDeclaration]: [ - { name: "decorators", test: isDecorator }, - { name: "modifiers", test: isModifier }, - { name: "name", test: isPropertyName }, - { name: "typeParameters", test: isTypeParameter }, - { name: "parameters", test: isParameter }, - { name: "type", test: isTypeNode, optional: true }, - { name: "body", test: isBlock, optional: true } - ], - [SyntaxKind.Constructor]: [ - { name: "decorators", test: isDecorator }, - { name: "modifiers", test: isModifier }, - { name: "typeParameters", test: isTypeParameter }, - { name: "parameters", test: isParameter }, - { name: "type", test: isTypeNode, optional: true }, - { name: "body", test: isBlock, optional: true } - ], - [SyntaxKind.GetAccessor]: [ - { name: "decorators", test: isDecorator }, - { name: "modifiers", test: isModifier }, - { name: "name", test: isPropertyName }, - { name: "typeParameters", test: isTypeParameter }, - { name: "parameters", test: isParameter }, - { name: "type", test: isTypeNode, optional: true }, - { name: "body", test: isBlock, optional: true } - ], - [SyntaxKind.SetAccessor]: [ - { name: "decorators", test: isDecorator }, - { name: "modifiers", test: isModifier }, - { name: "name", test: isPropertyName }, - { name: "typeParameters", test: isTypeParameter }, - { name: "parameters", test: isParameter }, - { name: "type", test: isTypeNode, optional: true }, - { name: "body", test: isBlock, optional: true } - ], - [SyntaxKind.ObjectBindingPattern]: [ - { name: "elements", test: isBindingElement } - ], - [SyntaxKind.ArrayBindingPattern]: [ - { name: "elements", test: isBindingElement } - ], - [SyntaxKind.BindingElement]: [ - { name: "propertyName", test: isPropertyName, optional: true }, - { name: "name", test: isBindingName }, - { name: "initializer", test: isExpression, optional: true, parenthesize: parenthesizeExpressionForList } - ], - [SyntaxKind.ArrayLiteralExpression]: [ - { name: "elements", test: isExpression, parenthesize: parenthesizeExpressionForList } - ], - [SyntaxKind.ObjectLiteralExpression]: [ - { name: "properties", test: isObjectLiteralElement } - ], - [SyntaxKind.PropertyAccessExpression]: [ - { name: "expression", test: isLeftHandSideExpression, parenthesize: parenthesizeForAccess }, - { name: "name", test: isIdentifier } - ], - [SyntaxKind.ElementAccessExpression]: [ - { name: "expression", test: isLeftHandSideExpression, parenthesize: parenthesizeForAccess }, - { name: "argumentExpression", test: isExpression } - ], - [SyntaxKind.CallExpression]: [ - { name: "expression", test: isLeftHandSideExpression, parenthesize: parenthesizeForAccess }, - { name: "typeArguments", test: isTypeNode }, - { name: "arguments", test: isExpression, parenthesize: parenthesizeExpressionForList } - ], - [SyntaxKind.NewExpression]: [ - { name: "expression", test: isLeftHandSideExpression, parenthesize: parenthesizeForNew }, - { name: "typeArguments", test: isTypeNode }, - { name: "arguments", test: isExpression, parenthesize: parenthesizeExpressionForList } - ], - [SyntaxKind.TaggedTemplateExpression]: [ - { name: "tag", test: isLeftHandSideExpression, parenthesize: parenthesizeForAccess }, - { name: "template", test: isTemplate } - ], [SyntaxKind.TypeAssertionExpression]: [ { name: "type", test: isTypeNode }, { name: "expression", test: isUnaryExpression } ], - [SyntaxKind.ParenthesizedExpression]: [ - { name: "expression", test: isExpression } - ], - [SyntaxKind.FunctionExpression]: [ - { name: "decorators", test: isDecorator }, - { name: "modifiers", test: isModifier }, - { name: "name", test: isIdentifier, optional: true }, - { name: "typeParameters", test: isTypeParameter }, - { name: "parameters", test: isParameter }, - { name: "type", test: isTypeNode, optional: true }, - { name: "body", test: isBlock, optional: true } - ], - [SyntaxKind.ArrowFunction]: [ - { name: "decorators", test: isDecorator }, - { name: "modifiers", test: isModifier }, - { name: "typeParameters", test: isTypeParameter }, - { name: "parameters", test: isParameter }, - { name: "type", test: isTypeNode, optional: true }, - { name: "body", test: isConciseBody, lift: liftToBlock, parenthesize: parenthesizeConciseBody } - ], - [SyntaxKind.DeleteExpression]: [ - { name: "expression", test: isUnaryExpression, parenthesize: parenthesizePrefixOperand } - ], - [SyntaxKind.TypeOfExpression]: [ - { name: "expression", test: isUnaryExpression, parenthesize: parenthesizePrefixOperand } - ], - [SyntaxKind.VoidExpression]: [ - { name: "expression", test: isUnaryExpression, parenthesize: parenthesizePrefixOperand } - ], - [SyntaxKind.AwaitExpression]: [ - { name: "expression", test: isUnaryExpression, parenthesize: parenthesizePrefixOperand } - ], - [SyntaxKind.PrefixUnaryExpression]: [ - { name: "operand", test: isUnaryExpression, parenthesize: parenthesizePrefixOperand } - ], - [SyntaxKind.PostfixUnaryExpression]: [ - { name: "operand", test: isLeftHandSideExpression, parenthesize: parenthesizePostfixOperand } - ], - [SyntaxKind.BinaryExpression]: [ - { name: "left", test: isExpression, parenthesize: (node: Expression, parent: BinaryExpression) => parenthesizeBinaryOperand(getOperator(parent), node, true, /*leftOperand*/ undefined) }, - { name: "right", test: isExpression, parenthesize: (node: Expression, parent: BinaryExpression) => parenthesizeBinaryOperand(getOperator(parent), node, false, parent.left) } - ], - [SyntaxKind.ConditionalExpression]: [ - { name: "condition", test: isExpression }, - { name: "whenTrue", test: isExpression }, - { name: "whenFalse", test: isExpression } - ], - [SyntaxKind.TemplateExpression]: [ - { name: "head", test: isTemplateLiteralFragment }, - { name: "templateSpans", test: isTemplateSpan } - ], - [SyntaxKind.YieldExpression]: [ - { name: "expression", test: isExpression, optional: true } - ], - [SyntaxKind.SpreadElementExpression]: [ - { name: "expression", test: isExpression, parenthesize: parenthesizeExpressionForList } - ], - [SyntaxKind.ClassExpression]: [ - { name: "decorators", test: isDecorator }, - { name: "modifiers", test: isModifier }, - { name: "name", test: isIdentifier, optional: true }, - { name: "typeParameters", test: isTypeParameter }, - { name: "heritageClauses", test: isHeritageClause }, - { name: "members", test: isClassElement } - ], - [SyntaxKind.ExpressionWithTypeArguments]: [ - { name: "expression", test: isLeftHandSideExpression, parenthesize: parenthesizeForAccess }, - { name: "typeArguments", test: isTypeNode } - ], [SyntaxKind.AsExpression]: [ { name: "expression", test: isExpression }, { name: "type", test: isTypeNode } @@ -223,104 +65,6 @@ namespace ts { [SyntaxKind.NonNullExpression]: [ { name: "expression", test: isLeftHandSideExpression } ], - [SyntaxKind.TemplateSpan]: [ - { name: "expression", test: isExpression }, - { name: "literal", test: isTemplateLiteralFragment } - ], - [SyntaxKind.Block]: [ - { name: "statements", test: isStatement } - ], - [SyntaxKind.VariableStatement]: [ - { name: "decorators", test: isDecorator }, - { name: "modifiers", test: isModifier }, - { name: "declarationList", test: isVariableDeclarationList } - ], - [SyntaxKind.ExpressionStatement]: [ - { name: "expression", test: isExpression, parenthesize: parenthesizeExpressionForExpressionStatement } - ], - [SyntaxKind.IfStatement]: [ - { name: "expression", test: isExpression }, - { name: "thenStatement", test: isStatement, lift: liftToBlock }, - { name: "elseStatement", test: isStatement, lift: liftToBlock, optional: true } - ], - [SyntaxKind.DoStatement]: [ - { name: "statement", test: isStatement, lift: liftToBlock }, - { name: "expression", test: isExpression } - ], - [SyntaxKind.WhileStatement]: [ - { name: "expression", test: isExpression }, - { name: "statement", test: isStatement, lift: liftToBlock } - ], - [SyntaxKind.ForStatement]: [ - { name: "initializer", test: isForInitializer, optional: true }, - { name: "condition", test: isExpression, optional: true }, - { name: "incrementor", test: isExpression, optional: true }, - { name: "statement", test: isStatement, lift: liftToBlock } - ], - [SyntaxKind.ForInStatement]: [ - { name: "initializer", test: isForInitializer }, - { name: "expression", test: isExpression }, - { name: "statement", test: isStatement, lift: liftToBlock } - ], - [SyntaxKind.ForOfStatement]: [ - { name: "initializer", test: isForInitializer }, - { name: "expression", test: isExpression }, - { name: "statement", test: isStatement, lift: liftToBlock } - ], - [SyntaxKind.ContinueStatement]: [ - { name: "label", test: isIdentifier, optional: true } - ], - [SyntaxKind.BreakStatement]: [ - { name: "label", test: isIdentifier, optional: true } - ], - [SyntaxKind.ReturnStatement]: [ - { name: "expression", test: isExpression, optional: true } - ], - [SyntaxKind.WithStatement]: [ - { name: "expression", test: isExpression }, - { name: "statement", test: isStatement, lift: liftToBlock } - ], - [SyntaxKind.SwitchStatement]: [ - { name: "expression", test: isExpression }, - { name: "caseBlock", test: isCaseBlock } - ], - [SyntaxKind.LabeledStatement]: [ - { name: "label", test: isIdentifier }, - { name: "statement", test: isStatement, lift: liftToBlock } - ], - [SyntaxKind.ThrowStatement]: [ - { name: "expression", test: isExpression } - ], - [SyntaxKind.TryStatement]: [ - { name: "tryBlock", test: isBlock }, - { name: "catchClause", test: isCatchClause, optional: true }, - { name: "finallyBlock", test: isBlock, optional: true } - ], - [SyntaxKind.VariableDeclaration]: [ - { name: "name", test: isBindingName }, - { name: "type", test: isTypeNode, optional: true }, - { name: "initializer", test: isExpression, optional: true, parenthesize: parenthesizeExpressionForList } - ], - [SyntaxKind.VariableDeclarationList]: [ - { name: "declarations", test: isVariableDeclaration } - ], - [SyntaxKind.FunctionDeclaration]: [ - { name: "decorators", test: isDecorator }, - { name: "modifiers", test: isModifier }, - { name: "name", test: isIdentifier, optional: true }, - { name: "typeParameters", test: isTypeParameter }, - { name: "parameters", test: isParameter }, - { name: "type", test: isTypeNode, optional: true }, - { name: "body", test: isBlock, optional: true } - ], - [SyntaxKind.ClassDeclaration]: [ - { name: "decorators", test: isDecorator }, - { name: "modifiers", test: isModifier }, - { name: "name", test: isIdentifier, optional: true }, - { name: "typeParameters", test: isTypeParameter }, - { name: "heritageClauses", test: isHeritageClause }, - { name: "members", test: isClassElement } - ], [SyntaxKind.EnumDeclaration]: [ { name: "decorators", test: isDecorator }, { name: "modifiers", test: isModifier }, @@ -336,117 +80,25 @@ namespace ts { [SyntaxKind.ModuleBlock]: [ { name: "statements", test: isStatement } ], - [SyntaxKind.CaseBlock]: [ - { name: "clauses", test: isCaseOrDefaultClause } - ], [SyntaxKind.ImportEqualsDeclaration]: [ { name: "decorators", test: isDecorator }, { name: "modifiers", test: isModifier }, { name: "name", test: isIdentifier }, { name: "moduleReference", test: isModuleReference } ], - [SyntaxKind.ImportDeclaration]: [ - { name: "decorators", test: isDecorator }, - { name: "modifiers", test: isModifier }, - { name: "importClause", test: isImportClause, optional: true }, - { name: "moduleSpecifier", test: isExpression } - ], - [SyntaxKind.ImportClause]: [ - { name: "name", test: isIdentifier, optional: true }, - { name: "namedBindings", test: isNamedImportBindings, optional: true } - ], - [SyntaxKind.NamespaceImport]: [ - { name: "name", test: isIdentifier } - ], - [SyntaxKind.NamedImports]: [ - { name: "elements", test: isImportSpecifier } - ], - [SyntaxKind.ImportSpecifier]: [ - { name: "propertyName", test: isIdentifier, optional: true }, - { name: "name", test: isIdentifier } - ], - [SyntaxKind.ExportAssignment]: [ - { name: "decorators", test: isDecorator }, - { name: "modifiers", test: isModifier }, - { name: "expression", test: isExpression } - ], - [SyntaxKind.ExportDeclaration]: [ - { name: "decorators", test: isDecorator }, - { name: "modifiers", test: isModifier }, - { name: "exportClause", test: isNamedExports, optional: true }, - { name: "moduleSpecifier", test: isExpression, optional: true } - ], - [SyntaxKind.NamedExports]: [ - { name: "elements", test: isExportSpecifier } - ], - [SyntaxKind.ExportSpecifier]: [ - { name: "propertyName", test: isIdentifier, optional: true }, - { name: "name", test: isIdentifier } - ], [SyntaxKind.ExternalModuleReference]: [ { name: "expression", test: isExpression, optional: true } ], - [SyntaxKind.JsxElement]: [ - { name: "openingElement", test: isJsxOpeningElement }, - { name: "children", test: isJsxChild }, - { name: "closingElement", test: isJsxClosingElement } - ], - [SyntaxKind.JsxSelfClosingElement]: [ - { name: "tagName", test: isEntityName }, - { name: "attributes", test: isJsxAttributeLike } - ], - [SyntaxKind.JsxOpeningElement]: [ - { name: "tagName", test: isEntityName }, - { name: "attributes", test: isJsxAttributeLike } - ], - [SyntaxKind.JsxClosingElement]: [ - { name: "tagName", test: isEntityName } - ], - [SyntaxKind.JsxAttribute]: [ - { name: "name", test: isIdentifier }, - { name: "initializer", test: isStringLiteralOrJsxExpression, optional: true } - ], - [SyntaxKind.JsxSpreadAttribute]: [ - { name: "expression", test: isExpression } - ], - [SyntaxKind.JsxExpression]: [ - { name: "expression", test: isExpression, optional: true } - ], - [SyntaxKind.CaseClause]: [ - { name: "expression", test: isExpression, parenthesize: parenthesizeExpressionForList }, - { name: "statements", test: isStatement } - ], - [SyntaxKind.DefaultClause]: [ - { name: "statements", test: isStatement } - ], - [SyntaxKind.HeritageClause]: [ - { name: "types", test: isExpressionWithTypeArguments } - ], - [SyntaxKind.CatchClause]: [ - { name: "variableDeclaration", test: isVariableDeclaration }, - { name: "block", test: isBlock } - ], - [SyntaxKind.PropertyAssignment]: [ - { name: "name", test: isPropertyName }, - { name: "initializer", test: isExpression, parenthesize: parenthesizeExpressionForList } - ], - [SyntaxKind.ShorthandPropertyAssignment]: [ - { name: "name", test: isIdentifier }, - { name: "objectAssignmentInitializer", test: isExpression, optional: true } - ], [SyntaxKind.EnumMember]: [ { name: "name", test: isPropertyName }, { name: "initializer", test: isExpression, optional: true, parenthesize: parenthesizeExpressionForList } - ], - [SyntaxKind.SourceFile]: [ - { name: "statements", test: isStatement } - ], - [SyntaxKind.NotEmittedStatement]: [], - [SyntaxKind.PartiallyEmittedExpression]: [ - { name: "expression", test: isExpression } ] }; + function reduceNode(node: Node, f: (memo: T, node: Node) => T, initial: T) { + return node ? f(initial, node) : initial; + } + /** * Similar to `reduceLeft`, performs a reduction against each child of a node. * NOTE: Unlike `forEachChild`, this does *not* visit every node. Only nodes added to the @@ -458,20 +110,428 @@ namespace ts { */ export function reduceEachChild(node: Node, f: (memo: T, node: Node) => T, initial: T): T { if (node === undefined) { - return undefined; + return initial; + } + + const kind = node.kind; + + // No need to visit nodes with no children. + if ((kind > SyntaxKind.FirstToken && kind <= SyntaxKind.LastToken)) { + return initial; + } + + // We do not yet support types. + if ((kind >= SyntaxKind.TypePredicate && kind <= SyntaxKind.StringLiteralType)) { + return initial; } let result = initial; - const edgeTraversalPath = nodeEdgeTraversalMap[node.kind]; - if (edgeTraversalPath) { - for (const edge of edgeTraversalPath) { - const value = (>node)[edge.name]; - if (value !== undefined) { - result = isArray(value) - ? reduceLeft(>value, f, result) - : f(result, value); + switch (node.kind) { + // Leaf nodes + case SyntaxKind.SemicolonClassElement: + case SyntaxKind.EmptyStatement: + case SyntaxKind.OmittedExpression: + case SyntaxKind.DebuggerStatement: + case SyntaxKind.NotEmittedStatement: + // No need to visit nodes with no children. + break; + + // Names + case SyntaxKind.ComputedPropertyName: + result = reduceNode((node).expression, f, result); + break; + + // Signature elements + case SyntaxKind.Parameter: + result = reduceLeft((node).decorators, f, result); + result = reduceLeft((node).modifiers, f, result); + result = reduceNode((node).name, f, result); + result = reduceNode((node).type, f, result); + result = reduceNode((node).initializer, f, result); + break; + + case SyntaxKind.Decorator: + result = reduceNode((node).expression, f, result); + break; + + // Type member + case SyntaxKind.PropertyDeclaration: + result = reduceLeft((node).decorators, f, result); + result = reduceLeft((node).modifiers, f, result); + result = reduceNode((node).name, f, result); + result = reduceNode((node).type, f, result); + result = reduceNode((node).initializer, f, result); + break; + + case SyntaxKind.MethodDeclaration: + result = reduceLeft((node).decorators, f, result); + result = reduceLeft((node).modifiers, f, result); + result = reduceNode((node).name, f, result); + result = reduceLeft((node).typeParameters, f, result); + result = reduceLeft((node).parameters, f, result); + result = reduceNode((node).type, f, result); + result = reduceNode((node).body, f, result); + break; + + case SyntaxKind.Constructor: + result = reduceLeft((node).modifiers, f, result); + result = reduceLeft((node).parameters, f, result); + result = reduceNode((node).body, f, result); + break; + + case SyntaxKind.GetAccessor: + result = reduceLeft((node).decorators, f, result); + result = reduceLeft((node).modifiers, f, result); + result = reduceNode((node).name, f, result); + result = reduceLeft((node).parameters, f, result); + result = reduceNode((node).type, f, result); + result = reduceNode((node).body, f, result); + break; + + case SyntaxKind.SetAccessor: + result = reduceLeft((node).decorators, f, result); + result = reduceLeft((node).modifiers, f, result); + result = reduceNode((node).name, f, result); + result = reduceLeft((node).parameters, f, result); + result = reduceNode((node).body, f, result); + break; + + // Binding patterns + case SyntaxKind.ObjectBindingPattern: + case SyntaxKind.ArrayBindingPattern: + result = reduceLeft((node).elements, f, result); + break; + + case SyntaxKind.BindingElement: + result = reduceNode((node).propertyName, f, result); + result = reduceNode((node).name, f, result); + result = reduceNode((node).initializer, f, result); + break; + + // Expression + case SyntaxKind.ArrayLiteralExpression: + result = reduceLeft((node).elements, f, result); + break; + + case SyntaxKind.ObjectLiteralExpression: + result = reduceLeft((node).properties, f, result); + break; + + case SyntaxKind.PropertyAccessExpression: + result = reduceNode((node).expression, f, result); + result = reduceNode((node).name, f, result); + break; + + case SyntaxKind.ElementAccessExpression: + result = reduceNode((node).expression, f, result); + result = reduceNode((node).argumentExpression, f, result); + break; + + case SyntaxKind.CallExpression: + result = reduceNode((node).expression, f, result); + result = reduceLeft((node).typeArguments, f, result); + result = reduceLeft((node).arguments, f, result); + break; + + case SyntaxKind.NewExpression: + result = reduceNode((node).expression, f, result); + result = reduceLeft((node).typeArguments, f, result); + result = reduceLeft((node).arguments, f, result); + break; + + case SyntaxKind.TaggedTemplateExpression: + result = reduceNode((node).tag, f, result); + result = reduceNode((node).template, f, result); + break; + + case SyntaxKind.FunctionExpression: + result = reduceLeft((node).modifiers, f, result); + result = reduceNode((node).name, f, result); + result = reduceLeft((node).typeParameters, f, result); + result = reduceLeft((node).parameters, f, result); + result = reduceNode((node).type, f, result); + result = reduceNode((node).body, f, result); + break; + + case SyntaxKind.ArrowFunction: + result = reduceLeft((node).modifiers, f, result); + result = reduceLeft((node).typeParameters, f, result); + result = reduceLeft((node).parameters, f, result); + result = reduceNode((node).type, f, result); + result = reduceNode((node).body, f, result); + break; + + case SyntaxKind.ParenthesizedExpression: + case SyntaxKind.DeleteExpression: + case SyntaxKind.TypeOfExpression: + case SyntaxKind.VoidExpression: + case SyntaxKind.AwaitExpression: + case SyntaxKind.YieldExpression: + case SyntaxKind.SpreadElementExpression: + case SyntaxKind.NonNullExpression: + result = reduceNode((node).expression, f, result); + break; + + case SyntaxKind.PrefixUnaryExpression: + case SyntaxKind.PostfixUnaryExpression: + result = reduceNode((node).operand, f, result); + break; + + case SyntaxKind.BinaryExpression: + result = reduceNode((node).left, f, result); + result = reduceNode((node).right, f, result); + break; + + case SyntaxKind.ConditionalExpression: + result = reduceNode((node).condition, f, result); + result = reduceNode((node).whenTrue, f, result); + result = reduceNode((node).whenFalse, f, result); + break; + + case SyntaxKind.TemplateExpression: + result = reduceNode((node).head, f, result); + result = reduceLeft((node).templateSpans, f, result); + break; + + case SyntaxKind.ClassExpression: + result = reduceLeft((node).modifiers, f, result); + result = reduceNode((node).name, f, result); + result = reduceLeft((node).typeParameters, f, result); + result = reduceLeft((node).heritageClauses, f, result); + result = reduceLeft((node).members, f, result); + break; + + case SyntaxKind.ExpressionWithTypeArguments: + result = reduceNode((node).expression, f, result); + result = reduceLeft((node).typeArguments, f, result); + break; + + // Misc + case SyntaxKind.TemplateSpan: + result = reduceNode((node).expression, f, result); + result = reduceNode((node).literal, f, result); + break; + + // Element + case SyntaxKind.Block: + result = reduceLeft((node).statements, f, result); + break; + + case SyntaxKind.VariableStatement: + result = reduceLeft((node).modifiers, f, result); + result = reduceNode((node).declarationList, f, result); + break; + + case SyntaxKind.ExpressionStatement: + result = reduceNode((node).expression, f, result); + break; + + case SyntaxKind.IfStatement: + result = reduceNode((node).expression, f, result); + result = reduceNode((node).thenStatement, f, result); + result = reduceNode((node).elseStatement, f, result); + break; + + case SyntaxKind.DoStatement: + result = reduceNode((node).statement, f, result); + result = reduceNode((node).expression, f, result); + break; + + case SyntaxKind.WhileStatement: + case SyntaxKind.WithStatement: + result = reduceNode((node).expression, f, result); + result = reduceNode((node).statement, f, result); + break; + + case SyntaxKind.ForStatement: + result = reduceNode((node).initializer, f, result); + result = reduceNode((node).condition, f, result); + result = reduceNode((node).incrementor, f, result); + result = reduceNode((node).statement, f, result); + break; + + case SyntaxKind.ForInStatement: + case SyntaxKind.ForOfStatement: + result = reduceNode((node).initializer, f, result); + result = reduceNode((node).expression, f, result); + result = reduceNode((node).statement, f, result); + break; + + case SyntaxKind.ReturnStatement: + case SyntaxKind.ThrowStatement: + result = reduceNode((node).expression, f, result); + break; + + case SyntaxKind.SwitchStatement: + result = reduceNode((node).expression, f, result); + result = reduceNode((node).caseBlock, f, result); + break; + + case SyntaxKind.LabeledStatement: + result = reduceNode((node).label, f, result); + result = reduceNode((node).statement, f, result); + break; + + case SyntaxKind.TryStatement: + result = reduceNode((node).tryBlock, f, result); + result = reduceNode((node).catchClause, f, result); + result = reduceNode((node).finallyBlock, f, result); + break; + + case SyntaxKind.VariableDeclaration: + result = reduceNode((node).name, f, result); + result = reduceNode((node).type, f, result); + result = reduceNode((node).initializer, f, result); + break; + + case SyntaxKind.VariableDeclarationList: + result = reduceLeft((node).declarations, f, result); + break; + + case SyntaxKind.FunctionDeclaration: + result = reduceLeft((node).decorators, f, result); + result = reduceLeft((node).modifiers, f, result); + result = reduceNode((node).name, f, result); + result = reduceLeft((node).typeParameters, f, result); + result = reduceLeft((node).parameters, f, result); + result = reduceNode((node).type, f, result); + result = reduceNode((node).body, f, result); + break; + + case SyntaxKind.ClassDeclaration: + result = reduceLeft((node).decorators, f, result); + result = reduceLeft((node).modifiers, f, result); + result = reduceNode((node).name, f, result); + result = reduceLeft((node).typeParameters, f, result); + result = reduceLeft((node).heritageClauses, f, result); + result = reduceLeft((node).members, f, result); + break; + + case SyntaxKind.CaseBlock: + result = reduceLeft((node).clauses, f, result); + break; + + case SyntaxKind.ImportDeclaration: + result = reduceLeft((node).decorators, f, result); + result = reduceLeft((node).modifiers, f, result); + result = reduceNode((node).importClause, f, result); + result = reduceNode((node).moduleSpecifier, f, result); + break; + + case SyntaxKind.ImportClause: + result = reduceNode((node).name, f, result); + result = reduceNode((node).namedBindings, f, result); + break; + + case SyntaxKind.NamespaceImport: + result = reduceNode((node).name, f, result); + break; + + case SyntaxKind.NamedImports: + case SyntaxKind.NamedExports: + result = reduceLeft((node).elements, f, result); + break; + + case SyntaxKind.ImportSpecifier: + case SyntaxKind.ExportSpecifier: + result = reduceNode((node).propertyName, f, result); + result = reduceNode((node).name, f, result); + break; + + case SyntaxKind.ExportAssignment: + result = reduceLeft((node).decorators, f, result); + result = reduceLeft((node).modifiers, f, result); + result = reduceNode((node).expression, f, result); + break; + + case SyntaxKind.ExportDeclaration: + result = reduceLeft((node).decorators, f, result); + result = reduceLeft((node).modifiers, f, result); + result = reduceNode((node).exportClause, f, result); + result = reduceNode((node).moduleSpecifier, f, result); + break; + + // JSX + case SyntaxKind.JsxElement: + result = reduceNode((node).openingElement, f, result); + result = reduceLeft((node).children, f, result); + result = reduceNode((node).closingElement, f, result); + break; + + case SyntaxKind.JsxSelfClosingElement: + case SyntaxKind.JsxOpeningElement: + result = reduceNode((node).tagName, f, result); + result = reduceLeft((node).attributes, f, result); + break; + + case SyntaxKind.JsxClosingElement: + result = reduceNode((node).tagName, f, result); + break; + + case SyntaxKind.JsxAttribute: + result = reduceNode((node).name, f, result); + result = reduceNode((node).initializer, f, result); + break; + + case SyntaxKind.JsxSpreadAttribute: + result = reduceNode((node).expression, f, result); + break; + + case SyntaxKind.JsxExpression: + result = reduceNode((node).expression, f, result); + break; + + // Clauses + case SyntaxKind.CaseClause: + result = reduceNode((node).expression, f, result); + // fall-through + + case SyntaxKind.DefaultClause: + result = reduceLeft((node).statements, f, result); + break; + + case SyntaxKind.HeritageClause: + result = reduceLeft((node).types, f, result); + break; + + case SyntaxKind.CatchClause: + result = reduceNode((node).variableDeclaration, f, result); + result = reduceNode((node).block, f, result); + break; + + // Property assignments + case SyntaxKind.PropertyAssignment: + result = reduceNode((node).name, f, result); + result = reduceNode((node).initializer, f, result); + break; + + case SyntaxKind.ShorthandPropertyAssignment: + result = reduceNode((node).name, f, result); + result = reduceNode((node).objectAssignmentInitializer, f, result); + break; + + // Top-level nodes + case SyntaxKind.SourceFile: + result = reduceLeft((node).statements, f, result); + break; + + case SyntaxKind.PartiallyEmittedExpression: + result = reduceNode((node).expression, f, result); + break; + + default: + const edgeTraversalPath = nodeEdgeTraversalMap[kind]; + if (edgeTraversalPath) { + for (const edge of edgeTraversalPath) { + const value = (>node)[edge.name]; + if (value !== undefined) { + result = isArray(value) + ? reduceLeft(>value, f, result) + : f(result, value); + } + } } - } + break; } return result; @@ -486,22 +546,9 @@ namespace ts { * @param optional An optional value indicating whether the Node is itself optional. * @param lift An optional callback to execute to lift a NodeArrayNode into a valid Node. */ - export function visitNode(node: T, visitor: (node: Node) => VisitResult, test: (node: Node) => boolean, optional?: boolean, lift?: (node: NodeArray) => T): T { - return visitNodeWorker(node, visitor, test, optional, lift, /*parenthesize*/ undefined, /*parentNode*/ undefined); - } - - /** - * Visits a Node using the supplied visitor, possibly returning a new Node in its place. - * - * @param node The Node to visit. - * @param visitor The callback used to visit the Node. - * @param test A callback to execute to verify the Node is valid. - * @param optional A value indicating whether the Node is itself optional. - * @param lift A callback to execute to lift a NodeArrayNode into a valid Node. - * @param parenthesize A callback used to parenthesize the node if needed. - * @param parentNode A parentNode for the node. - */ - function visitNodeWorker(node: Node, visitor: (node: Node) => VisitResult, test: (node: Node) => boolean, optional: boolean, lift: (node: Node[]) => Node, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node): Node { + export function visitNode(node: T, visitor: (node: Node) => VisitResult, test: (node: Node) => boolean, optional?: boolean, lift?: (node: NodeArray) => T): T; + export function visitNode(node: T, visitor: (node: Node) => VisitResult, test: (node: Node) => boolean, optional: boolean, lift: (node: NodeArray) => T, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node): T; + export function visitNode(node: Node, visitor: (node: Node) => VisitResult, test: (node: Node) => boolean, optional?: boolean, lift?: (node: Node[]) => Node, parenthesize?: (node: Node, parentNode: Node) => Node, parentNode?: Node): Node { if (node === undefined) { return undefined; } @@ -544,20 +591,9 @@ namespace ts { * @param start An optional value indicating the starting offset at which to start visiting. * @param count An optional value indicating the maximum number of nodes to visit. */ - export function visitNodes>(nodes: TArray, visitor: (node: Node) => VisitResult, test: (node: Node) => boolean, start?: number, count?: number): TArray { - return visitNodesWorker(nodes, visitor, test, /*parenthesize*/ undefined, /*parentNode*/ undefined, start, count); - } - - /** - * Visits a NodeArray using the supplied visitor, possibly returning a new NodeArray in its place. - * - * @param nodes The NodeArray to visit. - * @param visitor The callback used to visit a Node. - * @param test A node test to execute for each node. - * @param start An optional value indicating the starting offset at which to start visiting. - * @param count An optional value indicating the maximum number of nodes to visit. - */ - function visitNodesWorker(nodes: NodeArray, visitor: (node: Node) => VisitResult, test: (node: Node) => boolean, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node, start: number, count: number): NodeArray { + export function visitNodes(nodes: NodeArray, visitor: (node: Node) => VisitResult, test: (node: Node) => boolean, start?: number, count?: number): NodeArray; + export function visitNodes(nodes: NodeArray, visitor: (node: Node) => VisitResult, test: (node: Node) => boolean, start: number, count: number, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node): NodeArray; + export function visitNodes(nodes: NodeArray, visitor: (node: Node) => VisitResult, test: (node: Node) => boolean, start?: number, count?: number, parenthesize?: (node: Node, parentNode: Node) => Node, parentNode?: Node): NodeArray { if (nodes === undefined) { return undefined; } @@ -591,8 +627,26 @@ namespace ts { // Ensure we have a copy of `nodes`, up to the current index. updated = createNodeArray(nodes.slice(0, i), /*location*/ nodes, nodes.hasTrailingComma); } - - addNodeWorker(updated, visited, /*addOnNewLine*/ undefined, test, parenthesize, parentNode, /*isVisiting*/ visited !== node); + if (visited) { + if (isArray(visited)) { + for (let visitedNode of visited) { + visitedNode = parenthesize + ? parenthesize(visitedNode, parentNode) + : visitedNode; + Debug.assertNode(visitedNode, test); + aggregateTransformFlags(visitedNode); + updated.push(visitedNode); + } + } + else { + const visitedNode = parenthesize + ? parenthesize(visited, parentNode) + : visited; + Debug.assertNode(visitedNode, test); + aggregateTransformFlags(visitedNode); + updated.push(visitedNode); + } + } } } @@ -618,11 +672,12 @@ namespace ts { return node; } - // Special cases for frequent visitors to improve performance. - let visited: Node; - switch (kind) { - case SyntaxKind.ThisType: - case SyntaxKind.StringLiteralType: + // We do not yet support types. + if ((kind >= SyntaxKind.TypePredicate && kind <= SyntaxKind.StringLiteralType)) { + return node; + } + + switch (node.kind) { case SyntaxKind.SemicolonClassElement: case SyntaxKind.EmptyStatement: case SyntaxKind.OmittedExpression: @@ -630,580 +685,485 @@ namespace ts { // No need to visit nodes with no children. return node; - // Signature elements + // Names + case SyntaxKind.ComputedPropertyName: + return updateComputedPropertyName(node, + visitNode((node).expression, visitor, isExpression)); + // Signature elements case SyntaxKind.Parameter: - visited = visitEachChildOfParameter(node, visitor); - break; + return updateParameterDeclaration(node, + visitNodes((node).decorators, visitor, isDecorator), + visitNodes((node).modifiers, visitor, isModifier), + visitNode((node).name, visitor, isBindingName), + visitNode((node).type, visitor, isTypeNode, /*optional*/ true), + visitNode((node).initializer, visitor, isExpression, /*optional*/ true)); // Type member + case SyntaxKind.PropertyDeclaration: + return updateProperty(node, + visitNodes((node).decorators, visitor, isDecorator), + visitNodes((node).modifiers, visitor, isModifier), + visitNode((node).name, visitor, isPropertyName), + visitNode((node).type, visitor, isTypeNode, /*optional*/ true), + visitNode((node).initializer, visitor, isExpression, /*optional*/ true)); case SyntaxKind.MethodDeclaration: - visited = visitEachChildOfMethod(node, visitor, context); - break; + return updateMethod(node, + visitNodes((node).decorators, visitor, isDecorator), + visitNodes((node).modifiers, visitor, isModifier), + visitNode((node).name, visitor, isPropertyName), + visitNodes((node).typeParameters, visitor, isTypeParameter), + (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitNode((node).type, visitor, isTypeNode, /*optional*/ true), + mergeFunctionBodyLexicalEnvironment( + visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), + context.endLexicalEnvironment())); case SyntaxKind.Constructor: - visited = visitEachChildOfConstructor(node, visitor, context); - break; + return updateConstructor(node, + visitNodes((node).decorators, visitor, isDecorator), + visitNodes((node).modifiers, visitor, isModifier), + (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + mergeFunctionBodyLexicalEnvironment( + visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), + context.endLexicalEnvironment())); case SyntaxKind.GetAccessor: - visited = visitEachChildOfGetAccessor(node, visitor, context); - break; + return updateGetAccessor(node, + visitNodes((node).decorators, visitor, isDecorator), + visitNodes((node).modifiers, visitor, isModifier), + visitNode((node).name, visitor, isPropertyName), + (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitNode((node).type, visitor, isTypeNode, /*optional*/ true), + mergeFunctionBodyLexicalEnvironment( + visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), + context.endLexicalEnvironment())); case SyntaxKind.SetAccessor: - visited = visitEachChildOfSetAccessor(node, visitor, context); - break; + return updateSetAccessor(node, + visitNodes((node).decorators, visitor, isDecorator), + visitNodes((node).modifiers, visitor, isModifier), + visitNode((node).name, visitor, isPropertyName), + (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + mergeFunctionBodyLexicalEnvironment( + visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), + context.endLexicalEnvironment())); + + // Binding patterns + case SyntaxKind.ObjectBindingPattern: + return updateObjectBindingPattern(node, + visitNodes((node).elements, visitor, isBindingElement)); + + case SyntaxKind.ArrayBindingPattern: + return updateArrayBindingPattern(node, + visitNodes((node).elements, visitor, isBindingElement)); + + case SyntaxKind.BindingElement: + return updateBindingElement(node, + visitNode((node).propertyName, visitor, isPropertyName, /*optional*/ true), + visitNode((node).name, visitor, isBindingName), + visitNode((node).initializer, visitor, isExpression, /*optional*/ true)); // Expression + case SyntaxKind.ArrayLiteralExpression: + return updateArrayLiteral(node, + visitNodes((node).elements, visitor, isExpression)); + + case SyntaxKind.ObjectLiteralExpression: + return updateObjectLiteral(node, + visitNodes((node).properties, visitor, isObjectLiteralElement)); case SyntaxKind.PropertyAccessExpression: - visited = visitEachChildOfPropertyAccess(node, visitor); - break; + return updatePropertyAccess(node, + visitNode((node).expression, visitor, isExpression), + visitNode((node).name, visitor, isIdentifier)); + + case SyntaxKind.ElementAccessExpression: + return updateElementAccess(node, + visitNode((node).expression, visitor, isExpression), + visitNode((node).argumentExpression, visitor, isExpression)); case SyntaxKind.CallExpression: - visited = visitEachChildOfCall(node, visitor); - break; + return updateCall(node, + visitNode((node).expression, visitor, isExpression), + visitNodes((node).typeArguments, visitor, isTypeNode), + visitNodes((node).arguments, visitor, isExpression)); case SyntaxKind.NewExpression: - visited = visitEachChildOfNew(node, visitor); - break; + return updateNew(node, + visitNode((node).expression, visitor, isExpression), + visitNodes((node).typeArguments, visitor, isTypeNode), + visitNodes((node).arguments, visitor, isExpression)); + + case SyntaxKind.TaggedTemplateExpression: + return updateTaggedTemplate(node, + visitNode((node).tag, visitor, isExpression), + visitNode((node).template, visitor, isTemplate)); + + case SyntaxKind.ParenthesizedExpression: + return updateParen(node, + visitNode((node).expression, visitor, isExpression)); + + case SyntaxKind.FunctionExpression: + return updateFunctionExpression(node, + visitNode((node).name, visitor, isPropertyName), + visitNodes((node).typeParameters, visitor, isTypeParameter), + (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitNode((node).type, visitor, isTypeNode, /*optional*/ true), + mergeFunctionBodyLexicalEnvironment( + visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), + context.endLexicalEnvironment())); + + case SyntaxKind.ArrowFunction: + return updateArrowFunction(node, + visitNodes((node).modifiers, visitor, isModifier), + visitNodes((node).typeParameters, visitor, isTypeParameter), + (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitNode((node).type, visitor, isTypeNode, /*optional*/ true), + mergeFunctionBodyLexicalEnvironment( + visitNode((node).body, visitor, isConciseBody, /*optional*/ true), + context.endLexicalEnvironment())); + + case SyntaxKind.DeleteExpression: + return updateDelete(node, + visitNode((node).expression, visitor, isExpression)); + + case SyntaxKind.TypeOfExpression: + return updateTypeOf(node, + visitNode((node).expression, visitor, isExpression)); + + case SyntaxKind.VoidExpression: + return updateVoid(node, + visitNode((node).expression, visitor, isExpression)); + + case SyntaxKind.AwaitExpression: + return updateAwait(node, + visitNode((node).expression, visitor, isExpression)); case SyntaxKind.BinaryExpression: - visited = visitEachChildOfBinary(node, visitor); - break; + return updateBinary(node, + visitNode((node).left, visitor, isExpression), + visitNode((node).right, visitor, isExpression)); - case SyntaxKind.FunctionExpression: - visited = visitEachChildOfFunctionExpression(node, visitor, context); - break; + case SyntaxKind.PrefixUnaryExpression: + return updatePrefix(node, + visitNode((node).operand, visitor, isExpression)); - case SyntaxKind.ArrowFunction: - visited = visitEachChildOfArrowFunction(node, visitor, context); - break; + case SyntaxKind.PostfixUnaryExpression: + return updatePostfix(node, + visitNode((node).operand, visitor, isExpression)); + + case SyntaxKind.ConditionalExpression: + return updateConditional(node, + visitNode((node).condition, visitor, isExpression), + visitNode((node).whenTrue, visitor, isExpression), + visitNode((node).whenFalse, visitor, isExpression)); + + case SyntaxKind.TemplateExpression: + return updateTemplateExpression(node, + visitNode((node).head, visitor, isTemplateLiteralFragment), + visitNodes((node).templateSpans, visitor, isTemplateSpan)); + + case SyntaxKind.YieldExpression: + return updateYield(node, + visitNode((node).expression, visitor, isExpression)); + + case SyntaxKind.SpreadElementExpression: + return updateSpread(node, + visitNode((node).expression, visitor, isExpression)); + + case SyntaxKind.ClassExpression: + return updateClassExpression(node, + visitNodes((node).modifiers, visitor, isModifier), + visitNode((node).name, visitor, isIdentifier, /*optional*/ true), + visitNodes((node).typeParameters, visitor, isTypeParameter), + visitNodes((node).heritageClauses, visitor, isHeritageClause), + visitNodes((node).members, visitor, isClassElement)); + + case SyntaxKind.ExpressionWithTypeArguments: + return updateExpressionWithTypeArguments(node, + visitNodes((node).typeArguments, visitor, isTypeNode), + visitNode((node).expression, visitor, isExpression)); + + // Misc + case SyntaxKind.TemplateSpan: + return updateTemplateSpan(node, + visitNode((node).expression, visitor, isExpression), + visitNode((node).literal, visitor, isTemplateLiteralFragment)); // Element - case SyntaxKind.Block: - visited = visitEachChildOfBlock(node, visitor); - break; + return updateBlock(node, + visitNodes((node).statements, visitor, isStatement)); case SyntaxKind.VariableStatement: - visited = visitEachChildOfVaribleStatement(node, visitor); - break; + return updateVariableStatement(node, + visitNodes((node).modifiers, visitor, isModifier), + visitNode((node).declarationList, visitor, isVariableDeclarationList)); case SyntaxKind.ExpressionStatement: - visited = visitEachChildOfStatement(node, visitor); - break; + return updateStatement(node, + visitNode((node).expression, visitor, isExpression)); case SyntaxKind.IfStatement: - visited = visitEachChildOfIf(node, visitor); - break; + return updateIf(node, + visitNode((node).expression, visitor, isExpression), + visitNode((node).thenStatement, visitor, isStatement, /*optional*/ false, liftToBlock), + visitNode((node).elseStatement, visitor, isStatement, /*optional*/ true, liftToBlock)); + + case SyntaxKind.DoStatement: + return updateDo(node, + visitNode((node).statement, visitor, isStatement, /*optional*/ false, liftToBlock), + visitNode((node).expression, visitor, isExpression)); + + case SyntaxKind.WhileStatement: + return updateWhile(node, + visitNode((node).expression, visitor, isExpression), + visitNode((node).statement, visitor, isStatement, /*optional*/ false, liftToBlock)); case SyntaxKind.ForStatement: - visited = visitEachChildOfFor(node, visitor); - break; + return updateFor(node, + visitNode((node).initializer, visitor, isForInitializer), + visitNode((node).condition, visitor, isExpression), + visitNode((node).incrementor, visitor, isExpression), + visitNode((node).statement, visitor, isStatement, /*optional*/ false, liftToBlock)); case SyntaxKind.ForInStatement: - visited = visitEachChildOfForIn(node, visitor); - break; + return updateForIn(node, + visitNode((node).initializer, visitor, isForInitializer), + visitNode((node).expression, visitor, isExpression), + visitNode((node).statement, visitor, isStatement, /*optional*/ false, liftToBlock)); + + case SyntaxKind.ForOfStatement: + return updateForOf(node, + visitNode((node).initializer, visitor, isForInitializer), + visitNode((node).expression, visitor, isExpression), + visitNode((node).statement, visitor, isStatement, /*optional*/ false, liftToBlock)); + + case SyntaxKind.ContinueStatement: + return updateContinue(node, + visitNode((node).label, visitor, isIdentifier, /*optional*/ true)); + + case SyntaxKind.BreakStatement: + return updateBreak(node, + visitNode((node).label, visitor, isIdentifier, /*optional*/ true)); case SyntaxKind.ReturnStatement: - visited = visitEachChildOfReturn(node, visitor); - break; + return updateReturn(node, + visitNode((node).expression, visitor, isExpression, /*optional*/ true)); + + case SyntaxKind.WithStatement: + return updateWith(node, + visitNode((node).expression, visitor, isExpression), + visitNode((node).statement, visitor, isStatement, /*optional*/ false, liftToBlock)); + + case SyntaxKind.SwitchStatement: + return updateSwitch(node, + visitNode((node).expression, visitor, isExpression), + visitNode((node).caseBlock, visitor, isCaseBlock)); + + case SyntaxKind.LabeledStatement: + return updateLabel(node, + visitNode((node).label, visitor, isIdentifier), + visitNode((node).statement, visitor, isStatement, /*optional*/ false, liftToBlock)); + + case SyntaxKind.ThrowStatement: + return updateThrow(node, + visitNode((node).expression, visitor, isExpression)); + + case SyntaxKind.TryStatement: + return updateTry(node, + visitNode((node).tryBlock, visitor, isBlock), + visitNode((node).catchClause, visitor, isCatchClause, /*optional*/ true), + visitNode((node).finallyBlock, visitor, isBlock, /*optional*/ true)); case SyntaxKind.VariableDeclaration: - visited = visitEachChildOfVariableDeclaration(node, visitor); - break; + return updateVariableDeclaration(node, + visitNode((node).name, visitor, isBindingName), + visitNode((node).type, visitor, isTypeNode, /*optional*/ true), + visitNode((node).initializer, visitor, isExpression, /*optional*/ true)); case SyntaxKind.VariableDeclarationList: - visited = visitEachChildOfVariableDeclarationList(node, visitor); - break; + return updateVariableDeclarationList(node, + visitNodes((node).declarations, visitor, isVariableDeclaration)); case SyntaxKind.FunctionDeclaration: - visited = visitEachChildOfFunctionDeclaration(node, visitor, context); - break; + return updateFunctionDeclaration(node, + visitNodes((node).decorators, visitor, isDecorator), + visitNodes((node).modifiers, visitor, isModifier), + visitNode((node).name, visitor, isPropertyName), + visitNodes((node).typeParameters, visitor, isTypeParameter), + (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitNode((node).type, visitor, isTypeNode, /*optional*/ true), + mergeFunctionBodyLexicalEnvironment( + visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), + context.endLexicalEnvironment())); + + case SyntaxKind.ClassDeclaration: + return updateClassDeclaration(node, + visitNodes((node).decorators, visitor, isDecorator), + visitNodes((node).modifiers, visitor, isModifier), + visitNode((node).name, visitor, isIdentifier, /*optional*/ true), + visitNodes((node).typeParameters, visitor, isTypeParameter), + visitNodes((node).heritageClauses, visitor, isHeritageClause), + visitNodes((node).members, visitor, isClassElement)); + + case SyntaxKind.CaseBlock: + return updateCaseBlock(node, + visitNodes((node).clauses, visitor, isCaseOrDefaultClause)); + + case SyntaxKind.ImportDeclaration: + return updateImportDeclaration(node, + visitNodes((node).decorators, visitor, isDecorator), + visitNodes((node).modifiers, visitor, isModifier), + visitNode((node).importClause, visitor, isImportClause, /*optional*/ true), + visitNode((node).moduleSpecifier, visitor, isExpression)); + + case SyntaxKind.ImportClause: + return updateImportClause(node, + visitNode((node).name, visitor, isIdentifier, /*optional*/ true), + visitNode((node).namedBindings, visitor, isNamedImportBindings, /*optional*/ true)); + + case SyntaxKind.NamespaceImport: + return updateNamespaceImport(node, + visitNode((node).name, visitor, isIdentifier)); + + case SyntaxKind.NamedImports: + return updateNamedImports(node, + visitNodes((node).elements, visitor, isImportSpecifier)); + + case SyntaxKind.ImportSpecifier: + return updateImportSpecifier(node, + visitNode((node).propertyName, visitor, isIdentifier, /*optional*/ true), + visitNode((node).name, visitor, isIdentifier)); + + case SyntaxKind.ExportAssignment: + return updateExportAssignment(node, + visitNodes((node).decorators, visitor, isDecorator), + visitNodes((node).modifiers, visitor, isModifier), + visitNode((node).expression, visitor, isExpression)); + + case SyntaxKind.ExportDeclaration: + return updateExportDeclaration(node, + visitNodes((node).decorators, visitor, isDecorator), + visitNodes((node).modifiers, visitor, isModifier), + visitNode((node).exportClause, visitor, isNamedExports, /*optional*/ true), + visitNode((node).moduleSpecifier, visitor, isExpression, /*optional*/ true)); + + case SyntaxKind.NamedExports: + return updateNamedExports(node, + visitNodes((node).elements, visitor, isExportSpecifier)); + + case SyntaxKind.ExportSpecifier: + return updateExportSpecifier(node, + visitNode((node).propertyName, visitor, isIdentifier, /*optional*/ true), + visitNode((node).name, visitor, isIdentifier)); + + // JSX + case SyntaxKind.JsxElement: + return updateJsxElement(node, + visitNode((node).openingElement, visitor, isJsxOpeningElement), + visitNodes((node).children, visitor, isJsxChild), + visitNode((node).closingElement, visitor, isJsxClosingElement)); + + case SyntaxKind.JsxSelfClosingElement: + return updateJsxSelfClosingElement(node, + visitNode((node).tagName, visitor, isJsxTagNameExpression), + visitNodes((node).attributes, visitor, isJsxAttributeLike)); + + case SyntaxKind.JsxOpeningElement: + return updateJsxOpeningElement(node, + visitNode((node).tagName, visitor, isJsxTagNameExpression), + visitNodes((node).attributes, visitor, isJsxAttributeLike)); + + case SyntaxKind.JsxClosingElement: + return updateJsxClosingElement(node, + visitNode((node).tagName, visitor, isJsxTagNameExpression)); + + case SyntaxKind.JsxAttribute: + return updateJsxAttribute(node, + visitNode((node).name, visitor, isIdentifier), + visitNode((node).initializer, visitor, isStringLiteralOrJsxExpression)); + + case SyntaxKind.JsxSpreadAttribute: + return updateJsxSpreadAttribute(node, + visitNode((node).expression, visitor, isExpression)); + + case SyntaxKind.JsxExpression: + return updateJsxExpression(node, + visitNode((node).expression, visitor, isExpression)); + + // Clauses + case SyntaxKind.CaseClause: + return updateCaseClause(node, + visitNode((node).expression, visitor, isExpression), + visitNodes((node).statements, visitor, isStatement)); + + case SyntaxKind.DefaultClause: + return updateDefaultClause(node, + visitNodes((node).statements, visitor, isStatement)); + + case SyntaxKind.HeritageClause: + return updateHeritageClause(node, + visitNodes((node).types, visitor, isExpressionWithTypeArguments)); + + case SyntaxKind.CatchClause: + return updateCatchClause(node, + visitNode((node).variableDeclaration, visitor, isVariableDeclaration), + visitNode((node).block, visitor, isBlock)); + + // Property assignments + case SyntaxKind.PropertyAssignment: + return updatePropertyAssignment(node, + visitNode((node).name, visitor, isPropertyName), + visitNode((node).initializer, visitor, isExpression)); + + case SyntaxKind.ShorthandPropertyAssignment: + return updateShorthandPropertyAssignment(node, + visitNode((node).name, visitor, isIdentifier), + visitNode((node).objectAssignmentInitializer, visitor, isExpression)); // Top-level nodes - case SyntaxKind.SourceFile: - visited = visitEachChildOfSourceFile(node, visitor, context); - break; + context.startLexicalEnvironment(); + return updateSourceFileNode(node, + createNodeArray( + concatenate( + visitNodes((node).statements, visitor, isStatement), + context.endLexicalEnvironment()), + (node).statements)); + + // Transformation nodes + case SyntaxKind.PartiallyEmittedExpression: + return updatePartiallyEmittedExpression(node, + visitNode((node).expression, visitor, isExpression)); default: - visited = visitEachChildOfNode(node, visitor, context); - break; - } - - if (visited !== node) { - aggregateTransformFlags(visited); - } - - return visited; - } - - function visitEachChildOfSourceFile(node: SourceFile, visitor: (node: Node) => VisitResult, context: LexicalEnvironment) { - context.startLexicalEnvironment(); - const statements = visitNodes(node.statements, visitor, isStatement); - const declarations = context.endLexicalEnvironment(); - return updateSourceFileNode(node, - createNodeArray(concatenate(statements, declarations), statements) - ); - } - - function visitEachChildOfCall(node: CallExpression, visitor: (node: Node) => VisitResult) { - return updateCall(node, - visitNode(node.expression, visitor, isExpression), - visitNodes(node.typeArguments, visitor, isTypeNode), - visitNodes(node.arguments, visitor, isExpression) - ); - } - - function visitEachChildOfParameter(node: ParameterDeclaration, visitor: (node: Node) => VisitResult) { - return updateParameterDeclaration(node, - visitNodes(node.decorators, visitor, isDecorator), - visitNodes(node.modifiers, visitor, isModifier), - node.dotDotDotToken, - visitNode(node.name, visitor, isBindingName), - node.questionToken, - visitNode(node.type, visitor, isTypeNode, /*optional*/ true), - visitNode(node.initializer, visitor, isExpression, /*optional*/ true) - ); - } - - function visitEachChildOfStatement(node: ExpressionStatement, visitor: (node: Node) => VisitResult) { - return updateStatement(node, - visitNode(node.expression, visitor, isExpression) - ); - } - - function visitEachChildOfVaribleStatement(node: VariableStatement, visitor: (node: Node) => VisitResult) { - return updateVariableStatement(node, - visitNodes(node.modifiers, visitor, isModifier), - visitNode(node.declarationList, visitor, isVariableDeclarationList) - ); - } - - function visitEachChildOfVariableDeclarationList(node: VariableDeclarationList, visitor: (node: Node) => VisitResult) { - return updateVariableDeclarationList(node, - visitNodes(node.declarations, visitor, isVariableDeclaration) - ); - } - - function visitEachChildOfVariableDeclaration(node: VariableDeclaration, visitor: (node: Node) => VisitResult) { - return updateVariableDeclaration(node, - visitNode(node.name, visitor, isBindingName), - visitNode(node.type, visitor, isTypeNode, /*optional*/ true), - visitNode(node.initializer, visitor, isExpression, /*optional*/ true) - ); - } - - function visitEachChildOfConstructor(node: ConstructorDeclaration, visitor: (node: Node) => VisitResult, context: LexicalEnvironment) { - const decorators = visitNodes(node.decorators, visitor, isDecorator); - const modifiers = visitNodes(node.modifiers, visitor, isModifier); - context.startLexicalEnvironment(); - const parameters = visitNodes(node.parameters, visitor, isParameter); - const body = visitNode(node.body, visitor, isFunctionBody, /*optional*/ true); - const declarations = context.endLexicalEnvironment(); - return updateConstructor(node, - decorators, - modifiers, - parameters, - body ? updateBlock(body, createNodeArray(concatenate(body.statements, declarations), body.statements)) : undefined - ); - } - - function visitEachChildOfMethod(node: MethodDeclaration, visitor: (node: Node) => VisitResult, context: LexicalEnvironment) { - const decorators = visitNodes(node.decorators, visitor, isDecorator); - const modifiers = visitNodes(node.modifiers, visitor, isModifier); - const name = visitNode(node.name, visitor, isPropertyName); - const typeParameters = visitNodes(node.typeParameters, visitor, isTypeParameter); - context.startLexicalEnvironment(); - const parameters = visitNodes(node.parameters, visitor, isParameter); - const type = visitNode(node.type, visitor, isTypeNode, /*optional*/ true); - const body = visitNode(node.body, visitor, isFunctionBody, /*optional*/ true); - const declarations = context.endLexicalEnvironment(); - return updateMethod(node, - decorators, - modifiers, - node.asteriskToken, - name, - typeParameters, - parameters, - type, - body ? updateBlock(body, createNodeArray(concatenate(body.statements, declarations), body.statements)) : undefined - ); - } - - function visitEachChildOfGetAccessor(node: GetAccessorDeclaration, visitor: (node: Node) => VisitResult, context: LexicalEnvironment) { - const decorators = visitNodes(node.decorators, visitor, isDecorator); - const modifiers = visitNodes(node.modifiers, visitor, isModifier); - const name = visitNode(node.name, visitor, isPropertyName); - context.startLexicalEnvironment(); - const parameters = visitNodes(node.parameters, visitor, isParameter); - const type = visitNode(node.type, visitor, isTypeNode, /*optional*/ true); - const body = visitNode(node.body, visitor, isFunctionBody, /*optional*/ true); - const declarations = context.endLexicalEnvironment(); - return updateGetAccessor(node, - decorators, - modifiers, - name, - parameters, - type, - body ? updateBlock(body, createNodeArray(concatenate(body.statements, declarations), body.statements)) : undefined - ); - } - - function visitEachChildOfSetAccessor(node: SetAccessorDeclaration, visitor: (node: Node) => VisitResult, context: LexicalEnvironment) { - const decorators = visitNodes(node.decorators, visitor, isDecorator); - const modifiers = visitNodes(node.modifiers, visitor, isModifier); - const name = visitNode(node.name, visitor, isPropertyName); - context.startLexicalEnvironment(); - const parameters = visitNodes(node.parameters, visitor, isParameter); - const body = visitNode(node.body, visitor, isFunctionBody, /*optional*/ true); - const declarations = context.endLexicalEnvironment(); - return updateSetAccessor(node, - decorators, - modifiers, - name, - parameters, - body ? updateBlock(body, createNodeArray(concatenate(body.statements, declarations), body.statements)) : undefined - ); - } - - function visitEachChildOfBlock(node: Block, visitor: (node: Node) => VisitResult) { - return updateBlock(node, - visitNodes(node.statements, visitor, isStatement) - ); - } - - function visitEachChildOfPropertyAccess(node: PropertyAccessExpression, visitor: (node: Node) => VisitResult) { - return updatePropertyAccess(node, - visitNode(node.expression, visitor, isExpression), - visitNode(node.name, visitor, isIdentifier) - ); - } - - function visitEachChildOfIf(node: IfStatement, visitor: (node: Node) => VisitResult) { - return updateIf(node, - visitNode(node.expression, visitor, isExpression), - visitNode(node.thenStatement, visitor, isStatement, /*optional*/ false, liftToBlock), - visitNode(node.elseStatement, visitor, isStatement, /*optional*/ true, liftToBlock) - ); - } - - function visitEachChildOfBinary(node: BinaryExpression, visitor: (node: Node) => VisitResult) { - return updateBinary(node, - visitNode(node.left, visitor, isExpression), - visitNode(node.right, visitor, isExpression) - ); - } - - function visitEachChildOfReturn(node: ReturnStatement, visitor: (node: Node) => VisitResult) { - return updateReturn(node, - visitNode(node.expression, visitor, isExpression, /*optional*/ true) - ); - } - - function visitEachChildOfFunctionDeclaration(node: FunctionDeclaration, visitor: (node: Node) => VisitResult, context: LexicalEnvironment) { - const decorators = visitNodes(node.decorators, visitor, isDecorator); - const modifiers = visitNodes(node.modifiers, visitor, isModifier); - const name = visitNode(node.name, visitor, isIdentifier, /*optional*/ true); - const typeParameters = visitNodes(node.typeParameters, visitor, isTypeParameter); - context.startLexicalEnvironment(); - const parameters = visitNodes(node.parameters, visitor, isParameter); - const type = visitNode(node.type, visitor, isTypeNode, /*optional*/ true); - const body = visitNode(node.body, visitor, isFunctionBody, /*optional*/ true); - const declarations = context.endLexicalEnvironment(); - return updateFunctionDeclaration(node, - decorators, - modifiers, - name, - typeParameters, - parameters, - type, - body ? updateBlock(body, createNodeArray(concatenate(body.statements, declarations), body.statements)) : undefined - ); - } - - function visitEachChildOfFunctionExpression(node: FunctionExpression, visitor: (node: Node) => VisitResult, context: LexicalEnvironment) { - const name = visitNode(node.name, visitor, isIdentifier, /*optional*/ true); - const typeParameters = visitNodes(node.typeParameters, visitor, isTypeParameter); - context.startLexicalEnvironment(); - const parameters = visitNodes(node.parameters, visitor, isParameter); - const type = visitNode(node.type, visitor, isTypeNode, /*optional*/ true); - const body = visitNode(node.body, visitor, isFunctionBody, /*optional*/ true); - const declarations = context.endLexicalEnvironment(); - return updateFunctionExpression(node, - name, - typeParameters, - parameters, - type, - body ? updateBlock(body, createNodeArray(concatenate(body.statements, declarations), body.statements)) : undefined - ); - } - - function visitEachChildOfArrowFunction(node: ArrowFunction, visitor: (node: Node) => VisitResult, context: LexicalEnvironment) { - const modifiers = visitNodes(node.modifiers, visitor, isModifier); - const typeParameters = visitNodes(node.typeParameters, visitor, isTypeParameter); - context.startLexicalEnvironment(); - const parameters = visitNodes(node.parameters, visitor, isParameter); - const type = visitNode(node.type, visitor, isTypeNode, /*optional*/ true); - let body = visitNode(node.body, visitor, isConciseBody, /*optional*/ true); - const declarations = context.endLexicalEnvironment(); - if (declarations && declarations.length) { - const statements: Statement[] = []; - let statementsLocation: TextRange; - let multiLine = false; - if (isBlock(body)) { - addRange(statements, body.statements); - statementsLocation = body.statements; - multiLine = body.multiLine; - } - else { - statements.push(createReturn(body, /*location*/ body)); - statementsLocation = body; - multiLine = true; - } - - addRange(statements, declarations); - body = createBlock( - createNodeArray(statements, statementsLocation), - /*location*/ body, - multiLine - ); - } - - return updateArrowFunction(node, - modifiers, - typeParameters, - parameters, - type, - body - ); - } - - function visitEachChildOfNew(node: NewExpression, visitor: (node: Node) => VisitResult) { - return updateNew(node, - visitNode(node.expression, visitor, isExpression), - visitNodes(node.typeArguments, visitor, isTypeNode), - visitNodes(node.arguments, visitor, isExpression) - ); - } - - function visitEachChildOfFor(node: ForStatement, visitor: (node: Node) => VisitResult) { - return updateFor(node, - visitNode(node.initializer, visitor, isForInitializer, /*optional*/ true), - visitNode(node.condition, visitor, isExpression, /*optional*/ true), - visitNode(node.incrementor, visitor, isExpression, /*optional*/ true), - visitNode(node.statement, visitor, isStatement, /*optional*/ false, liftToBlock) - ); - } - - function visitEachChildOfForIn(node: ForInStatement, visitor: (node: Node) => VisitResult) { - return updateForIn(node, - visitNode(node.initializer, visitor, isForInitializer), - visitNode(node.expression, visitor, isExpression), - visitNode(node.statement, visitor, isStatement, /*optional*/ false, liftToBlock) - ); - } - - /** - * Visits each child of a Node using the supplied visitor, possibly returning a new Node of the same kind in its place. - * - * @param node The Node whose children will be visited. - * @param visitor The callback used to visit each child. - * @param context A lexical environment context for the visitor. - */ - function visitEachChildOfNode(node: T, visitor: (node: Node) => VisitResult, context: LexicalEnvironment): T; - function visitEachChildOfNode(node: T & Map, visitor: (node: Node) => VisitResult, context: LexicalEnvironment): T { - // const markName = `visitEachChild-${formatSyntaxKind(node.kind)}`; - // const measureName = `visitEachChildTime-${formatSyntaxKind(node.kind)}`; - // performance.mark(markName); - - let updated: T & Map; - - // If this node starts a new lexical environment, start a new lexical environment on the context. - const isNewLexicalEnvironment = nodeStartsNewLexicalEnvironment(node); - if (isNewLexicalEnvironment) { - context.startLexicalEnvironment(); - } - - const edgeTraversalPath = nodeEdgeTraversalMap[node.kind]; - if (edgeTraversalPath) { - for (const edge of edgeTraversalPath) { - const value = >node[edge.name]; - if (value !== undefined) { - let visited: Node | NodeArray; - // performance.measure(measureName, markName); - - if (isArray(value)) { - const visitedArray = visitNodesWorker(value, visitor, edge.test, edge.parenthesize, node, 0, value.length); - visited = visitedArray; - } - else { - visited = visitNodeWorker(value, visitor, edge.test, edge.optional, edge.lift, edge.parenthesize, node); - } - - // performance.mark(markName); - if (updated !== undefined || visited !== value) { - if (updated === undefined) { - updated = getMutableClone(node); - } - - if (visited !== value) { - updated[edge.name] = visited; + let updated: Node & Map; + const edgeTraversalPath = nodeEdgeTraversalMap[kind]; + if (edgeTraversalPath) { + for (const edge of edgeTraversalPath) { + const value = >(>node)[edge.name]; + if (value !== undefined) { + const visited = isArray(value) + ? visitNodes(value, visitor, edge.test, 0, value.length, edge.parenthesize, node) + : visitNode(value, visitor, edge.test, edge.optional, edge.lift, edge.parenthesize, node); + if (updated !== undefined || visited !== value) { + if (updated === undefined) { + updated = getMutableClone(node); + } + if (visited !== value) { + updated[edge.name] = visited; + } + } } } } - } + return updated ? updateNode(updated, node) : node; } - if (updated === undefined) { - updated = node; - } - - if (isNewLexicalEnvironment) { - const declarations = context.endLexicalEnvironment(); - if (declarations !== undefined && declarations.length > 0) { - updated = mergeLexicalEnvironment(updated, declarations); - } - } - - if (updated !== node) { - updateNode(updated, node); - } - - // performance.measure(measureName, markName); - return updated; - } - - /** - * Appends a node to an array. - * - * @param to The destination array. - * @param from The source Node or NodeArrayNode. - */ - export function addNode(to: T[], from: VisitResult, startOnNewLine?: boolean): void { - addNodeWorker(to, from, startOnNewLine, /*test*/ undefined, /*parenthesize*/ undefined, /*parentNode*/ undefined, /*isVisiting*/ false); - } - - /** - * Appends an array of nodes to an array. - * - * @param to The destination NodeArray. - * @param from The source array of Node or NodeArrayNode. - */ - export function addNodes(to: T[], from: VisitResult[], startOnNewLine?: boolean): void { - addNodesWorker(to, from, startOnNewLine, /*test*/ undefined, /*parenthesize*/ undefined, /*parentNode*/ undefined, /*isVisiting*/ false); - } - - function addNodeWorker(to: Node[], from: VisitResult, startOnNewLine: boolean, test: (node: Node) => boolean, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node, isVisiting: boolean): void { - if (to && from) { - if (isArray(from)) { - addNodesWorker(to, from, startOnNewLine, test, parenthesize, parentNode, isVisiting); - } - else { - const node = parenthesize !== undefined - ? parenthesize(from, parentNode) - : from; - - Debug.assertNode(node, test); - - if (startOnNewLine) { - node.startsOnNewLine = true; - } - - if (isVisiting) { - aggregateTransformFlags(node); - } - - to.push(node); - } - } - } - - function addNodesWorker(to: Node[], from: VisitResult[], startOnNewLine: boolean, test: (node: Node) => boolean, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node, isVisiting: boolean): void { - if (to && from) { - for (const node of from) { - addNodeWorker(to, node, startOnNewLine, test, parenthesize, parentNode, isVisiting); - } - } - } - - /** - * Merge generated declarations of a lexical environment. - * - * @param node The source node. - * @param declarations The generated lexical declarations. - */ - function mergeLexicalEnvironment(node: Node, declarations: Statement[]): Node { - switch (node.kind) { - case SyntaxKind.SourceFile: - return mergeSourceFileLexicalEnvironment(node, declarations); - - case SyntaxKind.ModuleDeclaration: - return mergeModuleDeclarationLexicalEnvironment(node, declarations); - - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.FunctionExpression: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.Constructor: - case SyntaxKind.ArrowFunction: - return mergeFunctionLikeLexicalEnvironment(node, declarations); - } - - Debug.fail("Node is not a valid lexical environment."); - } - - /** - * Merge generated declarations of a lexical environment into a SourceFile. - * - * @param node The SourceFile node. - * @param declarations The generated lexical declarations. - */ - export function mergeSourceFileLexicalEnvironment(node: SourceFile, declarations: Statement[]): SourceFile { - if (declarations !== undefined && declarations.length) { - const mutableNode = getMutableClone(node); - mutableNode.statements = mergeStatements(mutableNode.statements, declarations); - return mutableNode; - } - - return node; - } - - /** - * Merge generated declarations of a lexical environment into a ModuleDeclaration. - * - * @param node The ModuleDeclaration node. - * @param declarations The generated lexical declarations. - */ - export function mergeModuleDeclarationLexicalEnvironment(node: ModuleDeclaration, declarations: Statement[]): ModuleDeclaration { - Debug.assert(node.body.kind === SyntaxKind.ModuleBlock); - if (declarations !== undefined && declarations.length) { - const mutableNode = getMutableClone(node); - mutableNode.body = mergeBlockLexicalEnvironment(node.body, declarations); - return mutableNode; - } - - return node; - } - - /** - * Merge generated declarations of a lexical environment into a FunctionLikeDeclaration. - * - * @param node The function-like node. - * @param declarations The generated lexical declarations. - */ - function mergeFunctionLikeLexicalEnvironment(node: FunctionLikeDeclaration, declarations: Statement[]): FunctionLikeDeclaration { - Debug.assert(node.body !== undefined); - if (declarations !== undefined && declarations.length) { - const mutableNode = getMutableClone(node); - mutableNode.body = mergeConciseBodyLexicalEnvironment(mutableNode.body, declarations); - return mutableNode; - } - - return node; + // return node; } /** @@ -1212,13 +1172,7 @@ namespace ts { * @param node The ConciseBody of an arrow function. * @param declarations The lexical declarations to merge. */ - export function mergeFunctionBodyLexicalEnvironment(body: FunctionBody, declarations: Statement[]): FunctionBody { - if (declarations !== undefined && declarations.length > 0) { - return mergeBlockLexicalEnvironment(body, declarations); - } - - return body; - } + export function mergeFunctionBodyLexicalEnvironment(body: FunctionBody, declarations: Statement[]): FunctionBody; /** * Merges generated lexical declarations into the ConciseBody of an ArrowFunction. @@ -1226,44 +1180,23 @@ namespace ts { * @param node The ConciseBody of an arrow function. * @param declarations The lexical declarations to merge. */ - export function mergeConciseBodyLexicalEnvironment(body: ConciseBody, declarations: Statement[]): ConciseBody { - if (declarations !== undefined && declarations.length > 0) { + export function mergeFunctionBodyLexicalEnvironment(body: ConciseBody, declarations: Statement[]): ConciseBody; + + export function mergeFunctionBodyLexicalEnvironment(body: ConciseBody, declarations: Statement[]): ConciseBody { + if (body && declarations !== undefined && declarations.length > 0) { if (isBlock(body)) { - return mergeBlockLexicalEnvironment(body, declarations); + return updateBlock(body, createNodeArray(concatenate(body.statements, declarations), body.statements)); } else { - return createBlock([ - createReturn(body), - ...declarations - ]); + return createBlock( + createNodeArray([createReturn(body, /*location*/ body), ...declarations], body), + /*location*/ body, + /*multiLine*/ true); } } - return body; } - /** - * Merge generated declarations of a lexical environment into a FunctionBody or ModuleBlock. - * - * @param node The block into which to merge lexical declarations. - * @param declarations The lexical declarations to merge. - */ - function mergeBlockLexicalEnvironment(node: T, declarations: Statement[]): T { - const mutableNode = getMutableClone(node); - mutableNode.statements = mergeStatements(node.statements, declarations); - return mutableNode; - } - - /** - * Merge generated declarations of a lexical environment into a NodeArray of Statement. - * - * @param statements The node array to concatentate with the supplied lexical declarations. - * @param declarations The lexical declarations to merge. - */ - function mergeStatements(statements: NodeArray, declarations: Statement[]): NodeArray { - return createNodeArray(concatenate(statements, declarations), /*location*/ statements); - } - /** * Lifts a NodeArray containing only Statement nodes to a block. * @@ -1395,28 +1328,20 @@ namespace ts { } export namespace Debug { - export function failNotOptional(message?: string) { - if (shouldAssert(AssertionLevel.Normal)) { - Debug.assert(false, message || "Node not optional."); - } - } + export const failNotOptional = shouldAssert(AssertionLevel.Normal) + ? (message?: string) => assert(false, message || "Node not optional.") + : (message?: string) => {}; - export function failBadSyntaxKind(node: Node, message?: string) { - if (shouldAssert(AssertionLevel.Normal)) { - Debug.assert(false, - message || "Unexpected node.", - () => `Node ${formatSyntaxKind(node.kind)} was unexpected.`); - } - } + export const failBadSyntaxKind = shouldAssert(AssertionLevel.Normal) + ? (node: Node, message?: string) => assert(false, message || "Unexpected node.", () => `Node ${formatSyntaxKind(node.kind)} was unexpected.`) + : (node: Node, message?: string) => {}; - export function assertNode(node: Node, test: (node: Node) => boolean, message?: string): void { - if (shouldAssert(AssertionLevel.Normal)) { - Debug.assert( + export const assertNode = shouldAssert(AssertionLevel.Normal) + ? (node: Node, test: (node: Node) => boolean, message?: string) => assert( test === undefined || test(node), message || "Unexpected node.", - () => `Node ${formatSyntaxKind(node.kind)} did not pass test '${getFunctionName(test)}'.`); - }; - } + () => `Node ${formatSyntaxKind(node.kind)} did not pass test '${getFunctionName(test)}'.`) + : (node: Node, test: (node: Node) => boolean, message?: string) => {}; function getFunctionName(func: Function) { if (typeof func !== "function") {