diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index e516d9d146..230613b0a9 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() { @@ -202,14 +210,101 @@ namespace ts { // Names + export function createQualifiedName(left: EntityName, right: Identifier, location?: TextRange) { + const node = createNode(SyntaxKind.QualifiedName, location); + node.left = left; + node.right = right; + return node; + } + + export function updateQualifiedName(node: QualifiedName, left: EntityName, right: Identifier) { + if (node.left !== left || node.right !== right) { + return updateNode(createQualifiedName(left, right, node), node); + } + return node; + } + export function createComputedPropertyName(expression: Expression, location?: TextRange) { const node = createNode(SyntaxKind.ComputedPropertyName, location); node.expression = expression; 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; + } + + export function createDecorator(expression: LeftHandSideExpression, location?: TextRange) { + const node = createNode(SyntaxKind.Decorator, location); + node.expression = expression; + return node; + } + + export function updateDecorator(node: Decorator, expression: LeftHandSideExpression) { + if (node.expression !== expression) { + return updateNode(createDecorator(expression, node), 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 +318,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 +380,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; } @@ -329,6 +435,13 @@ 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); @@ -338,6 +451,13 @@ namespace ts { return node; } + export function updateObjectLiteral(node: ObjectLiteralExpression, properties: ObjectLiteralElement[]) { + if (node.properties !== properties) { + return updateNode(createObjectLiteral(properties, node, node.multiLine), node); + } + return node; + } + export function createPropertyAccess(expression: Expression, name: string | Identifier, location?: TextRange, flags?: NodeFlags) { const node = createNode(SyntaxKind.PropertyAccessExpression, location, flags); node.expression = parenthesizeForAccess(expression); @@ -363,6 +483,13 @@ namespace ts { return node; } + export function updateElementAccess(node: ElementAccessExpression, expression: Expression, argumentExpression: Expression) { + if (node.expression !== expression || node.argumentExpression !== argumentExpression) { + return updateNode(createElementAccess(expression, argumentExpression, node), node); + } + return node; + } + export function createCall(expression: Expression, typeArguments: TypeNode[], argumentsArray: Expression[], location?: TextRange, flags?: NodeFlags) { const node = createNode(SyntaxKind.CallExpression, location, flags); node.expression = parenthesizeForAccess(expression); @@ -397,12 +524,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; @@ -440,18 +588,45 @@ 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 createPrefix(operator: SyntaxKind, operand: Expression, location?: TextRange) { const node = createNode(SyntaxKind.PrefixUnaryExpression, location); node.operator = operator; @@ -459,6 +634,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); @@ -466,70 +648,141 @@ 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 { @@ -633,16 +886,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: CaseClause[], 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; } @@ -655,24 +923,10 @@ 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(expression: Expression, statement: Statement, location?: TextRange) { - const node = createNode(SyntaxKind.DoStatement, location); - node.expression = expression; - node.statement = statement; - return node; - } - - export function createWhile(statement: Statement, expression: Expression, location?: TextRange) { - const node = createNode(SyntaxKind.WhileStatement, location); - node.statement = statement; - node.expression = expression; + export function updateFor(node: ForStatement, initializer: ForInitializer, condition: Expression, incrementor: Expression, statement: Statement) { + if (node.initializer !== initializer || node.condition !== condition || node.incrementor !== incrementor || node.statement !== statement) { + return updateNode(createFor(initializer, condition, incrementor, statement, node), node); + } return node; } @@ -684,6 +938,13 @@ namespace ts { return node; } + export function updateForIn(node: ForInStatement, initializer: ForInitializer, expression: Expression, statement: Statement) { + if (node.initializer !== initializer || node.expression !== expression || node.statement !== statement) { + return updateNode(createForIn(initializer, expression, statement, node), node); + } + return node; + } + export function createForOf(initializer: ForInitializer, expression: Expression, statement: Statement, location?: TextRange) { const node = createNode(SyntaxKind.ForOfStatement, location); node.initializer = initializer; @@ -692,6 +953,43 @@ 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; @@ -705,6 +1003,74 @@ namespace ts { 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 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 updateThrow(node: ThrowStatement, expression: Expression) { + if (node.expression !== expression) { + return updateNode(createThrow(expression, node), node); + } + return node; + } + + export function createCaseBlock(clauses: CaseOrDefaultClause[], location?: TextRange): CaseBlock { + const node = createNode(SyntaxKind.CaseBlock, location); + node.clauses = createNodeArray(clauses); + return node; + } + + export function updateCaseBlock(node: CaseBlock, clauses: CaseOrDefaultClause[]) { + if (node.clauses !== clauses) { + return updateNode(createCaseBlock(clauses, node), node); + } + return node; + } + export function createFunctionDeclaration(decorators: Decorator[], modifiers: Modifier[], asteriskToken: Node, name: string | Identifier, typeParameters: TypeParameterDeclaration[], parameters: ParameterDeclaration[], type: TypeNode, body: Block, location?: TextRange, flags?: NodeFlags) { const node = createNode(SyntaxKind.FunctionDeclaration, location, flags); node.decorators = decorators ? createNodeArray(decorators) : undefined; @@ -725,44 +1091,79 @@ 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 createExportAssignment(isExportEquals: boolean, expression: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.ExportAssignment, location); + 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, expression: Expression) { + if (node.expression !== expression) { + return updateNode(createExportAssignment(node.isExportEquals, expression, node), node); + } + return node; + } + + export function createExportDeclaration(exportClause: NamedExports, moduleSpecifier?: Expression, location?: TextRange) { + const node = createNode(SyntaxKind.ExportDeclaration, location); node.exportClause = exportClause; node.moduleSpecifier = moduleSpecifier; return node; } - export function createNamedExports(elements: ExportSpecifier[]) { - const node = createNode(SyntaxKind.NamedExports); + export function updateExportDeclaration(node: ExportDeclaration, exportClause: NamedExports, moduleSpecifier: Expression) { + if (node.exportClause !== exportClause || node.moduleSpecifier !== moduleSpecifier) { + return updateNode(createExportDeclaration(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; + } + // Clauses export function createHeritageClause(token: SyntaxKind, types: ExpressionWithTypeArguments[], location?: TextRange) { @@ -772,6 +1173,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); @@ -779,6 +1187,26 @@ 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; + } + // Property assignments export function createPropertyAssignment(name: string | PropertyName, initializer: Expression, location?: TextRange) { @@ -789,6 +1217,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[]) { @@ -859,6 +1308,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) { @@ -970,22 +1426,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"), diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 1e25bd8cf0..44565945d5 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 2d39b3a1c5..e211ca61f3 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/module/module.ts b/src/compiler/transformers/module/module.ts index 7eb86a1143..5cfcd13f8a 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 1f601ae4b5..eb616c2fc7 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -720,7 +720,9 @@ namespace ts { createAssignment( name, createClassExpression( + /*modifiers*/ undefined, node.name, + /*typeParameters*/ undefined, node.heritageClauses, node.members, /*location*/ node diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 30ec1f67dc..b0c32e34c9 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -506,8 +506,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 +550,7 @@ namespace ts { } else if (isDecoratedClass) { if (isDefaultExternalModuleExport(node)) { - statements.push(createExportDefault(getLocalName(node))); + statements.push(createExportAssignment(/*isExportEquals*/ false, getLocalName(node))); } else if (isNamedExternalModuleExport(node)) { statements.push(createExternalModuleExport(name)); @@ -660,7 +662,9 @@ namespace ts { // } const classExpression: Expression = setOriginalNode( createClassExpression( + /*modifiers*/ undefined, name, + /*typeParameters*/ undefined, visitNodes(node.heritageClauses, visitor, isHeritageClause), transformClassMembers(node, hasExtendsClause), /*location*/ location @@ -743,7 +747,9 @@ namespace ts { const classExpression = setOriginalNode( createClassExpression( + /*modifiers*/ undefined, node.name, + /*typeParameters*/ undefined, heritageClauses, members, /*location*/ node @@ -1871,7 +1877,9 @@ namespace ts { createTypeOf(createIdentifier("Symbol")), createLiteral("function") ), + createToken(SyntaxKind.QuestionToken), createIdentifier("Symbol"), + createToken(SyntaxKind.ColonToken), createIdentifier("Object") ); } @@ -1957,6 +1965,7 @@ namespace ts { function visitExpressionWithTypeArguments(node: ExpressionWithTypeArguments): ExpressionWithTypeArguments { const expression = visitNode(node.expression, visitor, isLeftHandSideExpression); return createExpressionWithTypeArguments( + /*typeArguments*/ undefined, expression, node ); @@ -2360,6 +2369,7 @@ namespace ts { function visitAwaitExpression(node: AwaitExpression): Expression { return setOriginalNode( createYield( + /*asteriskToken*/ undefined, visitNode(node.expression, visitor, isExpression), /*location*/ node ), diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 0f1fd49487..e749f313bf 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -45,177 +45,13 @@ namespace ts { * significantly impacted. */ const nodeEdgeTraversalMap: Map = { - [SyntaxKind.QualifiedName]: [ - { 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 +59,11 @@ 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,9 +79,6 @@ namespace ts { [SyntaxKind.ModuleBlock]: [ { name: "statements", test: isStatement } ], - [SyntaxKind.CaseBlock]: [ - { name: "clauses", test: isCaseOrDefaultClause } - ], [SyntaxKind.ImportEqualsDeclaration]: [ { name: "decorators", test: isDecorator }, { name: "modifiers", test: isModifier }, @@ -412,41 +152,20 @@ namespace ts { [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,23 +177,353 @@ 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; } 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 (kind) { + // Leaf nodes + case SyntaxKind.ThisType: + case SyntaxKind.StringLiteralType: + case SyntaxKind.SemicolonClassElement: + case SyntaxKind.EmptyStatement: + case SyntaxKind.OmittedExpression: + case SyntaxKind.DebuggerStatement: + // No need to visit nodes with no children. + break; + + // Names + case SyntaxKind.QualifiedName: + result = reduceNode((node).left, f, result); + result = reduceNode((node).right, f, result); + break; + + 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.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: + 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; + + // 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; + + 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 +535,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 +580,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; } @@ -592,7 +617,7 @@ namespace ts { updated = createNodeArray(nodes.slice(0, i), /*location*/ nodes, nodes.hasTrailingComma); } - addNodeWorker(updated, visited, /*addOnNewLine*/ undefined, test, parenthesize, parentNode, /*isVisiting*/ visited !== node); + addNode(updated, visited, /*addOnNewLine*/ undefined, test, parenthesize, parentNode, /*isVisiting*/ visited !== node); } } @@ -619,7 +644,6 @@ namespace ts { } // Special cases for frequent visitors to improve performance. - let visited: Node; switch (kind) { case SyntaxKind.ThisType: case SyntaxKind.StringLiteralType: @@ -630,425 +654,417 @@ namespace ts { // No need to visit nodes with no children. return node; - // Signature elements + // Names + case SyntaxKind.QualifiedName: + return updateQualifiedName(node, + visitNode((node).left, visitor, isEntityName), + visitNode((node).right, visitor, isIdentifier)); + 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)); + + case SyntaxKind.Decorator: + return updateDecorator(node, + visitNode((node).expression, visitor, isLeftHandSideExpression)); // 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), + startLexicalEnvironmentAndVisitParameters((node).parameters, visitor, context), + visitNode((node).type, visitor, isTypeNode, /*optional*/ true), + endLexicalEnvironmentAndUpdateBody( + visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), + context)); case SyntaxKind.Constructor: - visited = visitEachChildOfConstructor(node, visitor, context); - break; + return updateConstructor(node, + visitNodes((node).decorators, visitor, isDecorator), + visitNodes((node).modifiers, visitor, isModifier), + startLexicalEnvironmentAndVisitParameters((node).parameters, visitor, context), + endLexicalEnvironmentAndUpdateBody( + visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), + context)); 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), + startLexicalEnvironmentAndVisitParameters((node).parameters, visitor, context), + visitNode((node).type, visitor, isTypeNode, /*optional*/ true), + endLexicalEnvironmentAndUpdateBody( + visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), + context)); 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), + startLexicalEnvironmentAndVisitParameters((node).parameters, visitor, context), + endLexicalEnvironmentAndUpdateBody( + visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), + context)); + + // 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, isExpression)); 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.BinaryExpression: - visited = visitEachChildOfBinary(node, visitor); - break; + 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: - visited = visitEachChildOfFunctionExpression(node, visitor, context); - break; + return updateFunctionExpression(node, + visitNode((node).name, visitor, isPropertyName), + visitNodes((node).typeParameters, visitor, isTypeParameter), + startLexicalEnvironmentAndVisitParameters((node).parameters, visitor, context), + visitNode((node).type, visitor, isTypeNode, /*optional*/ true), + endLexicalEnvironmentAndUpdateBody( + visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), + context)); case SyntaxKind.ArrowFunction: - visited = visitEachChildOfArrowFunction(node, visitor, context); - break; + return updateArrowFunction(node, + visitNodes((node).modifiers, visitor, isModifier), + visitNodes((node).typeParameters, visitor, isTypeParameter), + startLexicalEnvironmentAndVisitParameters((node).parameters, visitor, context), + visitNode((node).type, visitor, isTypeNode, /*optional*/ true), + endLexicalEnvironmentAndUpdateBody( + visitNode((node).body, visitor, isConciseBody, /*optional*/ true), + context)); + + case SyntaxKind.DeleteExpression: + return updateDelete(node, + visitNode((node).expression, visitor, isUnaryExpression)); + + case SyntaxKind.TypeOfExpression: + return updateTypeOf(node, + visitNode((node).expression, visitor, isUnaryExpression)); + + case SyntaxKind.VoidExpression: + return updateVoid(node, + visitNode((node).expression, visitor, isUnaryExpression)); + + case SyntaxKind.BinaryExpression: + return updateBinary((node), + visitNode((node).left, visitor, isExpression), + visitNode((node).right, visitor, isExpression)); + + case SyntaxKind.PrefixUnaryExpression: + return updatePrefix(node, + visitNode((node).operand, visitor, isExpression)); + + 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: + 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: + 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.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), + startLexicalEnvironmentAndVisitParameters((node).parameters, visitor, context), + visitNode((node).type, visitor, isTypeNode, /*optional*/ true), + endLexicalEnvironmentAndUpdateBody( + visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), + context)); + + 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)); + + // 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)); + + // 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) - ); - } - - /** - * 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; + } + } + + function startLexicalEnvironmentAndVisitParameters(nodes: NodeArray, visitor: (node: Node) => VisitResult, context: LexicalEnvironment): NodeArray { + context.startLexicalEnvironment(); + return visitNodes(nodes, visitor, isParameter); + } + + function endLexicalEnvironmentAndUpdateBody(body: FunctionBody, context: LexicalEnvironment): FunctionBody; + function endLexicalEnvironmentAndUpdateBody(body: ConciseBody, context: LexicalEnvironment): ConciseBody; + function endLexicalEnvironmentAndUpdateBody(body: ConciseBody, context: LexicalEnvironment) { + const declarations = context.endLexicalEnvironment(); + if (body && declarations && declarations.length) { + if (isBlock(body)) { + return updateBlock(body, createNodeArray(concatenate(body.statements, declarations), body.statements)); } + return createBlock( + createNodeArray([createReturn(body, /*location*/ body), ...declarations], body), + /*location*/ body, + /*multiLine*/ true); } - - 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; + return body; } /** @@ -1057,24 +1073,12 @@ namespace ts { * @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 { + export function addNode(to: T[], from: VisitResult, startOnNewLine?: boolean): void; + export function addNode(to: Node[], from: VisitResult, startOnNewLine: boolean, test: (node: Node) => boolean, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node, isVisiting: boolean): void; + export function addNode(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); + addNodes(to, from, startOnNewLine, test, parenthesize, parentNode, isVisiting); } else { const node = parenthesize !== undefined @@ -1096,91 +1100,22 @@ namespace ts { } } - function addNodesWorker(to: Node[], from: VisitResult[], startOnNewLine: boolean, test: (node: Node) => boolean, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node, isVisiting: boolean): void { + /** + * 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; + export function addNodes(to: Node[], from: VisitResult[], startOnNewLine: boolean, test: (node: Node) => boolean, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node, isVisiting: boolean): void; + export function addNodes(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); + addNode(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; - } - /** * Merges generated lexical declarations into the FunctionBody of a non-arrow function-like declaration. * @@ -1369,28 +1304,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") {