diff --git a/src/compiler/core.ts b/src/compiler/core.ts index aeb908c20cc..c9a0da63cfe 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1035,7 +1035,12 @@ namespace ts { } export namespace Debug { - const currentAssertionLevel = AssertionLevel.None; + declare var process: any; + declare var require: any; + + const currentAssertionLevel = getDevelopmentMode() === "development" + ? AssertionLevel.Normal + : AssertionLevel.None; export function shouldAssert(level: AssertionLevel): boolean { return currentAssertionLevel >= level; @@ -1055,6 +1060,17 @@ namespace ts { export function fail(message?: string): void { Debug.assert(/*expression*/ false, message); } + + function getDevelopmentMode() { + return typeof require !== "undefined" + && typeof process !== "undefined" + && !process.browser + && process.nextTick + && process.env + && process.env.NODE_ENV + ? String(process.env.NODE_ENV).toLowerCase() + : undefined; + } } export function copyListRemovingItem(item: T, list: T[]) { diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index aae8fbd8ce0..59dbe0307bc 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -16,7 +16,7 @@ namespace ts { node: BinaryExpression, needsValue: boolean, recordTempVariable: (node: Identifier) => void, - visitor?: (node: Node) => OneOrMany) { + visitor?: (node: Node) => VisitResult) { if (isEmptyObjectLiteralOrArrayLiteral(node.left)) { return node.right; @@ -79,7 +79,7 @@ namespace ts { * @param value The rhs value for the binding pattern. * @param visitor An optional visitor to use to visit expressions. */ - export function flattenParameterDestructuring(node: ParameterDeclaration, value: Expression, visitor?: (node: Node) => OneOrMany) { + export function flattenParameterDestructuring(node: ParameterDeclaration, value: Expression, visitor?: (node: Node) => VisitResult) { const declarations: VariableDeclaration[] = []; flattenDestructuring(node, value, node, emitAssignment, emitTempVariableAssignment, visitor); @@ -110,7 +110,7 @@ namespace ts { * @param value An optional rhs value for the binding pattern. * @param visitor An optional visitor to use to visit expressions. */ - export function flattenVariableDestructuring(node: VariableDeclaration, value?: Expression, visitor?: (node: Node) => OneOrMany) { + export function flattenVariableDestructuring(node: VariableDeclaration, value?: Expression, visitor?: (node: Node) => VisitResult) { const declarations: VariableDeclaration[] = []; flattenDestructuring(node, value, node, emitAssignment, emitTempVariableAssignment, visitor); @@ -151,7 +151,7 @@ namespace ts { node: VariableDeclaration, recordTempVariable: (name: Identifier) => void, nameSubstitution?: (name: Identifier) => Expression, - visitor?: (node: Node) => OneOrMany) { + visitor?: (node: Node) => VisitResult) { const pendingAssignments: Expression[] = []; @@ -191,7 +191,7 @@ namespace ts { location: TextRange, emitAssignment: (name: Identifier, value: Expression, location: TextRange, original: Node) => void, emitTempVariableAssignment: (value: Expression, location: TextRange) => Identifier, - visitor?: (node: Node) => OneOrMany) { + visitor?: (node: Node) => VisitResult) { if (value && visitor) { value = visitNode(value, visitor, isExpression); } diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index d0fa179372b..41850d0db16 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -55,7 +55,7 @@ namespace ts { return visitEachChild(node, visitor, context); } - function visitor(node: Node): OneOrMany { + function visitor(node: Node): VisitResult { const savedContainingNonArrowFunction = containingNonArrowFunction; const savedCurrentParent = currentParent; const savedCurrentNode = currentNode; @@ -74,7 +74,7 @@ namespace ts { return visited; } - function visitorWorker(node: Node): OneOrMany { + function visitorWorker(node: Node): VisitResult { if (node.transformFlags & TransformFlags.ES6) { return visitJavaScript(node); } @@ -86,7 +86,7 @@ namespace ts { } } - function visitJavaScript(node: Node): OneOrMany { + function visitJavaScript(node: Node): VisitResult { switch (node.kind) { case SyntaxKind.ClassDeclaration: return visitClassDeclaration(node); @@ -174,9 +174,12 @@ namespace ts { case SyntaxKind.SourceFile: return visitSourceFileNode(node); + + default: + Debug.failBadSyntaxKind(node); + return visitEachChild(node, visitor, context); } - Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); } function onBeforeVisitNode(node: Node) { @@ -695,7 +698,7 @@ namespace ts { break; default: - Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); + Debug.failBadSyntaxKind(node); break; } } @@ -936,7 +939,7 @@ namespace ts { enableSubstitutionsForBlockScopedBindings(); return setOriginalNode( createVariableDeclarationList( - flattenNodes(map(node.declarations, visitVariableDeclarationInLetDeclarationList)), + flatten(map(node.declarations, visitVariableDeclarationInLetDeclarationList)), /*location*/ node ), node @@ -1043,7 +1046,7 @@ namespace ts { * * @param node A VariableDeclaration node. */ - function visitVariableDeclaration(node: VariableDeclaration): OneOrMany { + function visitVariableDeclaration(node: VariableDeclaration): VisitResult { // If we are here it is because the name contains a binding pattern. Debug.assert(isBindingPattern(node.name)); @@ -1293,7 +1296,7 @@ namespace ts { break; default: - Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); + Debug.failBadSyntaxKind(node); break; } } diff --git a/src/compiler/transformers/es7.ts b/src/compiler/transformers/es7.ts index fc6e878799f..d04ca55db4b 100644 --- a/src/compiler/transformers/es7.ts +++ b/src/compiler/transformers/es7.ts @@ -12,7 +12,7 @@ namespace ts { return visitEachChild(node, visitor, context); } - function visitor(node: Node): OneOrMany { + function visitor(node: Node): VisitResult { if (node.transformFlags & TransformFlags.ES7) { return visitorWorker(node); } @@ -24,13 +24,15 @@ namespace ts { } } - function visitorWorker(node: Node): OneOrMany { + function visitorWorker(node: Node): VisitResult { switch (node.kind) { case SyntaxKind.BinaryExpression: return visitBinaryExpression(node); - } - Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); + default: + Debug.failBadSyntaxKind(node); + return visitEachChild(node, visitor, context); + } } function visitBinaryExpression(node: BinaryExpression): Expression { @@ -90,7 +92,8 @@ namespace ts { return createMathPow(left, right, /*location*/ node); } else { - Debug.fail(`Unexpected operator kind: ${formatSyntaxKind(node.operatorToken.kind)}.`); + Debug.failBadSyntaxKind(node); + return visitEachChild(node, visitor, context); } } } diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 3a68f1f2e28..dc423e8da7b 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -18,7 +18,7 @@ namespace ts { return visitEachChild(node, visitor, context); } - function visitor(node: Node): OneOrMany { + function visitor(node: Node): VisitResult { if (node.transformFlags & TransformFlags.Jsx) { return visitorWorker(node); } @@ -30,16 +30,18 @@ namespace ts { } } - function visitorWorker(node: Node): OneOrMany { + function visitorWorker(node: Node): VisitResult { switch (node.kind) { case SyntaxKind.JsxElement: return visitJsxElement(node); case SyntaxKind.JsxSelfClosingElement: return visitJsxSelfClosingElement(node); - } - Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); + default: + Debug.failBadSyntaxKind(node); + return undefined; + } } function transformJsxChildToExpression(node: JsxChild): Expression { @@ -55,9 +57,11 @@ namespace ts { case SyntaxKind.JsxSelfClosingElement: return visitJsxSelfClosingElement(node); - } - Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); + default: + Debug.failBadSyntaxKind(node); + return undefined; + } } function visitJsxElement(node: JsxElement) { diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index a258cb914bb..bff1396b7a3 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -123,7 +123,7 @@ namespace ts { createStatement( createCall( define, - flattenNodes([ + flatten([ // Add the module name (if provided). moduleName, @@ -142,7 +142,7 @@ namespace ts { setNodeEmitFlags( setMultiLine( createBlock( - flattenNodes([ + flatten([ // Visit each statement of the module body. ...visitNodes(node.statements, visitor, isStatement), @@ -173,7 +173,7 @@ namespace ts { * * @param node The node. */ - function visitor(node: Node): OneOrMany { + function visitor(node: Node): VisitResult { switch (node.kind) { case SyntaxKind.ImportDeclaration: return visitImportDeclaration(node); @@ -208,7 +208,7 @@ namespace ts { * * @param node The ImportDeclaration node. */ - function visitImportDeclaration(node: ImportDeclaration): OneOrMany { + function visitImportDeclaration(node: ImportDeclaration): VisitResult { if (contains(externalImports, node)) { const statements: Statement[] = []; const namespaceDeclaration = getNamespaceDeclarationNode(node); @@ -287,7 +287,7 @@ namespace ts { return undefined; } - function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): OneOrMany { + function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): VisitResult { if (contains(externalImports, node)) { const statements: Statement[] = []; if (moduleKind !== ModuleKind.AMD) { @@ -335,7 +335,7 @@ namespace ts { return undefined; } - function visitExportDeclaration(node: ExportDeclaration): OneOrMany { + function visitExportDeclaration(node: ExportDeclaration): VisitResult { if (contains(externalImports, node)) { const generatedName = getGeneratedNameForNode(node); if (node.exportClause) { @@ -391,7 +391,7 @@ namespace ts { return undefined; } - function visitExportAssignment(node: ExportAssignment): OneOrMany { + function visitExportAssignment(node: ExportAssignment): VisitResult { if (!node.isExportEquals && resolver.isValueAliasDeclaration(node)) { const statements: Statement[] = []; addExportDefault(statements, node.expression, /*location*/ node); @@ -470,7 +470,7 @@ namespace ts { } } - function visitVariableStatement(node: VariableStatement): OneOrMany { + function visitVariableStatement(node: VariableStatement): VisitResult { const variables = getInitializedVariables(node.declarationList); if (variables.length === 0) { // elide statement if there are no initialized variables @@ -502,7 +502,7 @@ namespace ts { } } - function visitFunctionDeclaration(node: FunctionDeclaration): OneOrMany { + function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult { const statements: Statement[] = []; if (node.name) { if (hasModifier(node, ModifierFlags.Export)) { @@ -543,7 +543,7 @@ namespace ts { return statements; } - function visitClassDeclaration(node: ClassDeclaration): OneOrMany { + function visitClassDeclaration(node: ClassDeclaration): VisitResult { const statements: Statement[] = []; if (node.name) { if (hasModifier(node, ModifierFlags.Export)) { diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 1ba5d365fcf..aab231dd2f4 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -408,7 +408,7 @@ namespace ts { return createArrayLiteral(setters); } - function visitSourceElement(node: Node): OneOrMany { + function visitSourceElement(node: Node): VisitResult { switch (node.kind) { case SyntaxKind.ImportDeclaration: return visitImportDeclaration(node); @@ -427,7 +427,7 @@ namespace ts { } } - function visitNestedNode(node: Node): OneOrMany { + function visitNestedNode(node: Node): VisitResult { switch (node.kind) { case SyntaxKind.VariableStatement: return visitVariableStatement(node); @@ -502,7 +502,7 @@ namespace ts { return undefined; } - function visitExportDeclaration(node: ExportDeclaration): OneOrMany { + function visitExportDeclaration(node: ExportDeclaration): VisitResult { if (!node.moduleSpecifier) { const statements: Statement[] = []; addNodes(statements, map(node.exportClause.elements, visitExportSpecifier)); @@ -541,7 +541,7 @@ namespace ts { * * @param node The variable statement to visit. */ - function visitVariableStatement(node: VariableStatement): OneOrMany { + function visitVariableStatement(node: VariableStatement): VisitResult { const isExported = hasModifier(node, ModifierFlags.Export); const expressions: Expression[] = []; for (const variable of node.declarationList.declarations) { @@ -616,7 +616,7 @@ namespace ts { * * @param node The class declaration to visit. */ - function visitClassDeclaration(node: ClassDeclaration): OneOrMany { + function visitClassDeclaration(node: ClassDeclaration): VisitResult { // Hoist the name of the class declaration to the outer module body function. const name = getDeclarationName(node); hoistVariableDeclaration(name); diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index b92e98d7cd4..8251c0c467e 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -92,7 +92,7 @@ namespace ts { * * @param node The node to visit. */ - function visitWithStack(node: Node, visitor: (node: Node) => OneOrMany): OneOrMany { + function visitWithStack(node: Node, visitor: (node: Node) => VisitResult): VisitResult { // Save state const savedCurrentNamespace = currentNamespace; const savedCurrentScope = currentScope; @@ -118,7 +118,7 @@ namespace ts { * * @param node The node to visit. */ - function visitor(node: Node): OneOrMany { + function visitor(node: Node): VisitResult { return visitWithStack(node, visitorWorker); } @@ -127,7 +127,7 @@ namespace ts { * * @param node The node to visit. */ - function visitorWorker(node: Node): OneOrMany { + function visitorWorker(node: Node): VisitResult { if (node.transformFlags & TransformFlags.TypeScript) { // This node is explicitly marked as TypeScript, so we should transform the node. return visitTypeScript(node); @@ -145,7 +145,7 @@ namespace ts { * * @param node The node to visit. */ - function namespaceElementVisitor(node: Node): OneOrMany { + function namespaceElementVisitor(node: Node): VisitResult { return visitWithStack(node, namespaceElementVisitorWorker); } @@ -154,7 +154,7 @@ namespace ts { * * @param node The node to visit. */ - function namespaceElementVisitorWorker(node: Node): OneOrMany { + function namespaceElementVisitorWorker(node: Node): VisitResult { if (node.transformFlags & TransformFlags.TypeScript || isExported(node)) { // This node is explicitly marked as TypeScript, or is exported at the namespace // level, so we should transform the node. @@ -173,7 +173,7 @@ namespace ts { * * @param node The node to visit. */ - function classElementVisitor(node: Node): OneOrMany { + function classElementVisitor(node: Node): VisitResult { return visitWithStack(node, classElementVisitorWorker); } @@ -182,7 +182,7 @@ namespace ts { * * @param node The node to visit. */ - function classElementVisitorWorker(node: Node): OneOrMany { + function classElementVisitorWorker(node: Node): VisitResult { switch (node.kind) { case SyntaxKind.Constructor: // TypeScript constructors are transformed in `transformClassDeclaration`. @@ -202,8 +202,8 @@ namespace ts { return node; default: - Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); - break; + Debug.failBadSyntaxKind(node); + return undefined; } } @@ -212,7 +212,7 @@ namespace ts { * * @param node The node to visit. */ - function visitTypeScript(node: Node): OneOrMany { + function visitTypeScript(node: Node): VisitResult { if (hasModifier(node, ModifierFlags.Ambient)) { // TypeScript ambient declarations are elided. return undefined; @@ -373,8 +373,8 @@ namespace ts { return visitImportEqualsDeclaration(node); default: - Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); - break; + Debug.failBadSyntaxKind(node); + return undefined; } } @@ -408,7 +408,7 @@ namespace ts { * * @param node The node to transform. */ - function visitClassDeclaration(node: ClassDeclaration): OneOrMany { + function visitClassDeclaration(node: ClassDeclaration): VisitResult { const staticProperties = getInitializedProperties(node, /*isStatic*/ true); const hasExtendsClause = getClassExtendsHeritageClauseElement(node) !== undefined; @@ -1553,7 +1553,7 @@ namespace ts { break; default: - Debug.fail(`Unexpected node kind: ${formatSyntaxKind(node.kind)}.`); + Debug.failBadSyntaxKind(node); break; } @@ -1855,7 +1855,7 @@ namespace ts { * * @param node The function node. */ - function visitFunctionDeclaration(node: FunctionDeclaration): OneOrMany { + function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult { if (shouldElideFunctionLikeDeclaration(node)) { return undefined; } @@ -2150,7 +2150,7 @@ namespace ts { * * @param node The enum declaration node. */ - function visitEnumDeclaration(node: EnumDeclaration): OneOrMany { + function visitEnumDeclaration(node: EnumDeclaration): VisitResult { if (shouldElideEnumDeclaration(node)) { return undefined; } @@ -2362,7 +2362,7 @@ namespace ts { * * @param node The module declaration node. */ - function visitModuleDeclaration(node: ModuleDeclaration): OneOrMany { + function visitModuleDeclaration(node: ModuleDeclaration): VisitResult { if (shouldElideModuleDeclaration(node)) { return undefined; } @@ -2481,7 +2481,7 @@ namespace ts { * * @param node The import equals declaration node. */ - function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): OneOrMany { + function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): VisitResult { Debug.assert(!isExternalModuleImportEqualsDeclaration(node)); if (shouldElideImportEqualsDeclaration(node)) { return undefined; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 7e9dafabe7a..0ca234f482b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2735,6 +2735,8 @@ namespace ts { /* @internal */ export const enum TransformFlags { + None = 0, + // Facts // - Flags used to indicate that a node or subtree contains syntax that requires transformation. TypeScript = 1 << 0, diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 5eec55005fa..ee88d531577 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -3,7 +3,7 @@ /* @internal */ namespace ts { - export type OneOrMany = T | T[]; + export type VisitResult = T | T[]; /** * Describes an edge of a Node, used when traversing a syntax tree. @@ -448,7 +448,7 @@ namespace ts { * @param f The callback function * @param initial The initial value to supply to the reduction. */ - export function reduceEachChild(node: Node, f: (memo: T, node: Node) => T, initial: T) { + export function reduceEachChild(node: Node, f: (memo: T, node: Node) => T, initial: T): T { if (node === undefined) { return undefined; } @@ -478,7 +478,7 @@ 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) => OneOrMany, 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): T { return visitNodeWorker(node, visitor, test, optional, lift, /*parenthesize*/ undefined, /*parentNode*/ undefined); } @@ -493,24 +493,26 @@ namespace ts { * @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) => OneOrMany, test: (node: Node) => boolean, optional: boolean, lift: (node: NodeArray) => Node, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node): 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 { if (node === undefined) { return undefined; } - let visited = visitor(node); + const visited = visitor(node); if (visited === node) { return node; } + let visitedNode: Node; if (visited === undefined) { - Debug.assert(optional, "Node not optional."); + if (!optional) { + Debug.failNotOptional(); + } + return undefined; } - - let visitedNode: Node; - if (isArray(visited)) { - visitedNode = (lift || extractSingleNode)(>visited); + else if (isArray(visited)) { + visitedNode = (lift || extractSingleNode)(visited); } else { visitedNode = visited; @@ -520,7 +522,7 @@ namespace ts { visitedNode = parenthesize(visitedNode, parentNode); } - Debug.assert(test === undefined || test(visitedNode), "Wrong node type after visit.", () => `Node ${formatSyntaxKind(visitedNode.kind)} did not pass test ${(test).name}.`); + Debug.assertNode(visitedNode, test); aggregateTransformFlags(visitedNode); return visitedNode; } @@ -534,7 +536,7 @@ 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) => OneOrMany, test: (node: Node) => boolean, start?: number, count?: number): TArray { + 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); } @@ -547,12 +549,12 @@ 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. */ - function visitNodesWorker(nodes: NodeArray, visitor: (node: Node) => OneOrMany, test: (node: Node) => boolean, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node, start: number, count: number): NodeArray { + 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 { if (nodes === undefined) { return undefined; } - let updated: Node[]; + let updated: NodeArray; // Ensure start and count have valid values const length = nodes.length; @@ -564,9 +566,12 @@ namespace ts { count = length - start; } - // If we are not visiting all of the original nodes, we must always create a new array. if (start > 0 || count < length) { - updated = []; + // If we are not visiting all of the original nodes, we must always create a new array. + // Since this is a fragment of a node array, we do not copy over the previous location + // and will only copy over `hasTrailingComma` if we are including the last element. + updated = createNodeArray([], /*location*/ undefined, + /*hasTrailingComma*/ nodes.hasTrailingComma && start + count === length); } // Visit each original node. @@ -576,18 +581,14 @@ namespace ts { if (updated !== undefined || visited === undefined || visited !== node) { if (updated === undefined) { // Ensure we have a copy of `nodes`, up to the current index. - updated = nodes.slice(0, i); + updated = createNodeArray(nodes.slice(0, i), /*location*/ nodes, nodes.hasTrailingComma); } addNodeWorker(updated, visited, /*addOnNewLine*/ undefined, test, parenthesize, parentNode, /*isVisiting*/ visited !== node); } } - if (updated !== undefined) { - return createNodeArray(updated, nodes, nodes.hasTrailingComma); - } - - return nodes; + return updated || nodes; } /** @@ -597,8 +598,8 @@ namespace ts { * @param visitor The callback used to visit each child. * @param context A lexical environment context for the visitor. */ - export function visitEachChild(node: T, visitor: (node: Node) => OneOrMany, context: LexicalEnvironment): T; - export function visitEachChild(node: T & Map, visitor: (node: Node) => OneOrMany, context: LexicalEnvironment): T { + export function visitEachChild(node: T, visitor: (node: Node) => VisitResult, context: LexicalEnvironment): T; + export function visitEachChild(node: T & Map, visitor: (node: Node) => VisitResult, context: LexicalEnvironment): T { if (node === undefined) { return undefined; } @@ -657,33 +658,13 @@ namespace ts { return updated; } - /** - * Flattens an array of nodes that could contain NodeArrayNodes. - */ - export function flattenNodes(nodes: OneOrMany[]): T[] { - let result: T[]; - if (nodes) { - for (let i = 0; i < nodes.length; i++) { - const node = nodes[i]; - if (result || node === undefined || isArray(node)) { - if (!result) { - result = nodes.slice(0, i); - } - addNode(result, node); - } - } - } - - return result || nodes; - } - /** * Appends a node to an array. * * @param to The destination array. * @param from The source Node or NodeArrayNode. */ - export function addNode(to: T[], from: OneOrMany, startOnNewLine?: boolean) { + export function addNode(to: T[], from: VisitResult, startOnNewLine?: boolean): void { addNodeWorker(to, from, startOnNewLine, /*test*/ undefined, /*parenthesize*/ undefined, /*parentNode*/ undefined, /*isVisiting*/ false); } @@ -693,18 +674,21 @@ namespace ts { * @param to The destination NodeArray. * @param from The source array of Node or NodeArrayNode. */ - export function addNodes(to: T[], from: OneOrMany[], startOnNewLine?: boolean) { + 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: OneOrMany, startOnNewLine: boolean, test: (node: Node) => boolean, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node, isVisiting: boolean) { + function addNodeWorker(to: Node[], from: VisitResult, startOnNewLine: boolean, test: (node: Node) => boolean, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node, isVisiting: boolean): void { if (to && from) { if (isArray(from)) { addNodesWorker(to, from, startOnNewLine, test, parenthesize, parentNode, isVisiting); } else { - const node = parenthesize !== undefined ? parenthesize(from, parentNode) : from; - Debug.assert(test === undefined || test(node), "Wrong node type after visit.", () => `Node ${formatSyntaxKind(node.kind)} did not pass test ${(test).name}.`); + const node = parenthesize !== undefined + ? parenthesize(from, parentNode) + : from; + + Debug.assertNode(node, test); if (startOnNewLine) { node.startsOnNewLine = true; @@ -719,7 +703,7 @@ namespace ts { } } - function addNodesWorker(to: Node[], from: OneOrMany[], startOnNewLine: boolean, test: (node: Node) => boolean, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node, isVisiting: boolean) { + function addNodesWorker(to: Node[], from: VisitResult[], startOnNewLine: boolean, test: (node: Node) => boolean, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node, isVisiting: boolean): void { if (to && from) { for (const node of from) { addNodeWorker(to, node, startOnNewLine, test, parenthesize, parentNode, isVisiting); @@ -760,7 +744,7 @@ namespace ts { * @param node The SourceFile node. * @param declarations The generated lexical declarations. */ - export function mergeSourceFileLexicalEnvironment(node: SourceFile, declarations: Statement[]) { + export function mergeSourceFileLexicalEnvironment(node: SourceFile, declarations: Statement[]): SourceFile { if (declarations !== undefined && declarations.length) { const mutableNode = getMutableClone(node); mutableNode.statements = mergeStatements(mutableNode.statements, declarations); @@ -776,7 +760,7 @@ namespace ts { * @param node The ModuleDeclaration node. * @param declarations The generated lexical declarations. */ - export function mergeModuleDeclarationLexicalEnvironment(node: ModuleDeclaration, declarations: Statement[]) { + export function mergeModuleDeclarationLexicalEnvironment(node: ModuleDeclaration, declarations: Statement[]): ModuleDeclaration { Debug.assert(node.body.kind === SyntaxKind.ModuleBlock); if (declarations !== undefined && declarations.length) { const mutableNode = getMutableClone(node); @@ -793,7 +777,7 @@ namespace ts { * @param node The function-like node. * @param declarations The generated lexical declarations. */ - function mergeFunctionLikeLexicalEnvironment(node: FunctionLikeDeclaration, declarations: Statement[]) { + function mergeFunctionLikeLexicalEnvironment(node: FunctionLikeDeclaration, declarations: Statement[]): FunctionLikeDeclaration { Debug.assert(node.body !== undefined); if (declarations !== undefined && declarations.length) { const mutableNode = getMutableClone(node); @@ -810,7 +794,7 @@ namespace ts { * @param node The ConciseBody of an arrow function. * @param declarations The lexical declarations to merge. */ - export function mergeFunctionBodyLexicalEnvironment(body: FunctionBody, declarations: Statement[]) { + export function mergeFunctionBodyLexicalEnvironment(body: FunctionBody, declarations: Statement[]): FunctionBody { if (declarations !== undefined && declarations.length > 0) { return mergeBlockLexicalEnvironment(body, declarations); } @@ -824,7 +808,7 @@ namespace ts { * @param node The ConciseBody of an arrow function. * @param declarations The lexical declarations to merge. */ - export function mergeConciseBodyLexicalEnvironment(body: ConciseBody, declarations: Statement[]) { + export function mergeConciseBodyLexicalEnvironment(body: ConciseBody, declarations: Statement[]): ConciseBody { if (declarations !== undefined && declarations.length > 0) { if (isBlock(body)) { return mergeBlockLexicalEnvironment(body, declarations); @@ -846,7 +830,7 @@ namespace ts { * @param node The block into which to merge lexical declarations. * @param declarations The lexical declarations to merge. */ - function mergeBlockLexicalEnvironment(node: T, declarations: Statement[]) { + function mergeBlockLexicalEnvironment(node: T, declarations: Statement[]): T { const mutableNode = getMutableClone(node); mutableNode.statements = mergeStatements(node.statements, declarations); return mutableNode; @@ -858,7 +842,7 @@ namespace ts { * @param statements The node array to concatentate with the supplied lexical declarations. * @param declarations The lexical declarations to merge. */ - function mergeStatements(statements: NodeArray, declarations: Statement[]) { + function mergeStatements(statements: NodeArray, declarations: Statement[]): NodeArray { return createNodeArray(concatenate(statements, declarations), /*location*/ statements); } @@ -867,7 +851,7 @@ namespace ts { * * @param nodes The NodeArray. */ - export function liftToBlock(nodes: NodeArray) { + export function liftToBlock(nodes: Node[]): Block { Debug.assert(every(nodes, isStatement), "Cannot lift nodes to a Block."); return createBlock(>nodes); } @@ -877,9 +861,9 @@ namespace ts { * * @param nodes The NodeArray. */ - function extractSingleNode(nodes: NodeArray) { + function extractSingleNode(nodes: Node[]): Node { Debug.assert(nodes.length <= 1, "Too many nodes written to output."); - return nodes.length > 0 ? nodes[0] : undefined; + return singleOrUndefined(nodes); } /** @@ -916,11 +900,11 @@ namespace ts { // We do not transform ambient declarations or types, so there is no need to // recursively aggregate transform flags. if (hasModifier(node, ModifierFlags.Ambient) || isTypeNode(node)) { - return 0; + return TransformFlags.None; } // Aggregate the transform flags of each child. - return reduceEachChild(node, aggregateTransformFlagsForChildNode, 0); + return reduceEachChild(node, aggregateTransformFlagsForChildNode, TransformFlags.None); } /** @@ -930,4 +914,43 @@ namespace ts { function aggregateTransformFlagsForChildNode(transformFlags: TransformFlags, child: Node): TransformFlags { return transformFlags | aggregateTransformFlagsForNode(child); } + + export namespace Debug { + export function failNotOptional(message?: string) { + if (shouldAssert(AssertionLevel.Normal)) { + Debug.assert(false, message || "Node not optional."); + } + } + + export function failBadSyntaxKind(node: Node, message?: string) { + if (shouldAssert(AssertionLevel.Normal)) { + Debug.assert(false, + message || "Unexpected node.", + () => `Node ${formatSyntaxKind(node.kind)} was unexpected.`); + } + } + + export function assertNode(node: Node, test: (node: Node) => boolean, message?: string): void { + if (shouldAssert(AssertionLevel.Normal)) { + Debug.assert( + test === undefined || test(node), + message || "Unexpected node.", + () => `Node ${formatSyntaxKind(node.kind)} did not pass test '${getFunctionName(test)}'.`); + }; + } + + function getFunctionName(func: Function) { + if (typeof func !== "function") { + return ""; + } + else if (func.hasOwnProperty("name")) { + return (func).name; + } + else { + const text = Function.prototype.toString.call(func); + const match = /^function\s+([\w\$]+)\s*\(/.exec(text); + return match ? match[1] : ""; + } + } + } } \ No newline at end of file