diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 0e169ca7aca..84b2240d4e4 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -132,11 +132,17 @@ namespace ts { let Symbol: { new (flags: SymbolFlags, name: string): Symbol }; let classifiableNames: Map; + // state used to aggregate transform flags during bind. + let subtreeTransformFlags: TransformFlags; + let skipTransformFlagAggregation: boolean; + function bindSourceFile(f: SourceFile, opts: CompilerOptions) { file = f; options = opts; inStrictMode = !!file.externalModuleIndicator; classifiableNames = {}; + subtreeTransformFlags = undefined; + skipTransformFlagAggregation = isDeclarationFile(file); Symbol = objectAllocator.getSymbolConstructor(); @@ -1178,11 +1184,43 @@ namespace ts { // symbols we do specialized work when we recurse. For example, we'll keep track of // the current 'container' node when it changes. This helps us know which symbol table // a local should go into for example. - bindChildren(node); + aggregateTransformFlagsIfNeededAndBindChildren(node); inStrictMode = savedInStrictMode; } + function aggregateTransformFlagsIfNeededAndBindChildren(node: Node) { + if (node.transformFlags !== undefined) { + skipTransformFlagAggregationAndBindChildren(node); + } + else { + aggregateTransformFlagsAndBindChildren(node); + } + } + + function skipTransformFlagAggregationAndBindChildren(node: Node) { + if (!skipTransformFlagAggregation) { + skipTransformFlagAggregation = true; + bindChildren(node); + skipTransformFlagAggregation = false; + } + else { + bindChildren(node); + } + } + + function aggregateTransformFlagsAndBindChildren(node: Node) { + if (!skipTransformFlagAggregation) { + const savedSubtreeTransformFlags = subtreeTransformFlags; + subtreeTransformFlags = 0; + bindChildren(node); + subtreeTransformFlags = savedSubtreeTransformFlags | computeTransformFlagsForNode(node, subtreeTransformFlags); + } + else { + bindChildren(node); + } + } + function updateStrictMode(node: Node) { switch (node.kind) { case SyntaxKind.SourceFile: @@ -1751,4 +1789,437 @@ namespace ts { implicitLabels = []; } } + + /** + * Computes the transform flags for a node, given the transform flags of its subtree + * + * @param node The node to analyze + * @param subtreeFlags Transform flags computed for this node's subtree + */ + export function computeTransformFlagsForNode(node: Node, subtreeFlags: TransformFlags) { + // Ambient nodes are TypeScript syntax and the flags of their subtree are ignored. + if (node.flags & NodeFlags.Ambient) { + return (node.transformFlags = TransformFlags.AssertTypeScript) + & ~(node.excludeTransformFlags = TransformFlags.NodeExcludes); + } + + // Mark transformations needed for each node + let transformFlags: TransformFlags; + let excludeFlags: TransformFlags; + switch (node.kind) { + case SyntaxKind.PublicKeyword: + case SyntaxKind.PrivateKeyword: + case SyntaxKind.ProtectedKeyword: + case SyntaxKind.AbstractKeyword: + case SyntaxKind.DeclareKeyword: + case SyntaxKind.AsyncKeyword: + case SyntaxKind.ConstKeyword: + case SyntaxKind.AwaitExpression: + case SyntaxKind.EnumDeclaration: + case SyntaxKind.EnumMember: + case SyntaxKind.TypeAssertionExpression: + case SyntaxKind.AsExpression: + case SyntaxKind.ReadonlyKeyword: + // These nodes are TypeScript syntax. + transformFlags |= TransformFlags.AssertTypeScript; + break; + + case SyntaxKind.JsxElement: + case SyntaxKind.JsxSelfClosingElement: + case SyntaxKind.JsxOpeningElement: + case SyntaxKind.JsxText: + case SyntaxKind.JsxClosingElement: + case SyntaxKind.JsxAttribute: + case SyntaxKind.JsxSpreadAttribute: + case SyntaxKind.JsxExpression: + // These nodes are Jsx syntax. + transformFlags |= TransformFlags.AssertJsx; + break; + + case SyntaxKind.NoSubstitutionTemplateLiteral: + case SyntaxKind.TemplateHead: + case SyntaxKind.TemplateMiddle: + case SyntaxKind.TemplateTail: + case SyntaxKind.TemplateExpression: + case SyntaxKind.TaggedTemplateExpression: + case SyntaxKind.ShorthandPropertyAssignment: + case SyntaxKind.ForOfStatement: + case SyntaxKind.YieldExpression: + // These nodes are ES6 syntax. + transformFlags |= TransformFlags.AssertES6; + break; + + + case SyntaxKind.AnyKeyword: + case SyntaxKind.NumberKeyword: + case SyntaxKind.StringKeyword: + case SyntaxKind.BooleanKeyword: + case SyntaxKind.SymbolKeyword: + case SyntaxKind.VoidKeyword: + case SyntaxKind.TypeParameter: + case SyntaxKind.PropertySignature: + case SyntaxKind.MethodSignature: + case SyntaxKind.CallSignature: + case SyntaxKind.ConstructSignature: + case SyntaxKind.IndexSignature: + case SyntaxKind.TypePredicate: + case SyntaxKind.TypeReference: + case SyntaxKind.FunctionType: + case SyntaxKind.ConstructorType: + case SyntaxKind.TypeQuery: + case SyntaxKind.TypeLiteral: + case SyntaxKind.ArrayType: + case SyntaxKind.TupleType: + case SyntaxKind.UnionType: + case SyntaxKind.IntersectionType: + case SyntaxKind.ParenthesizedType: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.TypeAliasDeclaration: + case SyntaxKind.ThisType: + case SyntaxKind.StringLiteralType: + // Types and signatures are TypeScript syntax, and exclude all other facts. + excludeFlags = TransformFlags.TypeExcludes; + transformFlags |= TransformFlags.AssertTypeScript; + break; + + case SyntaxKind.ComputedPropertyName: + // Even though computed property names are ES6, we don't treat them as such. + // This is so that they can flow through PropertyName transforms unaffected. + // Instead, we mark the container as ES6, so that it can properly handle the transform. + transformFlags |= TransformFlags.ContainsComputedPropertyName; + break; + + case SyntaxKind.SpreadElementExpression: + // This node is ES6 syntax, but is handled by a containing node. + transformFlags |= TransformFlags.ContainsSpreadElementExpression; + break; + + case SyntaxKind.SuperKeyword: + // This node is ES6 syntax. + transformFlags |= TransformFlags.AssertES6; + break; + + case SyntaxKind.ThisKeyword: + // Mark this node and its ancestors as containing a lexical `this` keyword. + transformFlags |= TransformFlags.ContainsLexicalThis; + break; + + case SyntaxKind.ObjectBindingPattern: + case SyntaxKind.ArrayBindingPattern: + // These nodes are ES6 syntax. + transformFlags |= TransformFlags.AssertES6; + break; + + case SyntaxKind.ObjectLiteralExpression: + excludeFlags = TransformFlags.ObjectLiteralExcludes; + if (subtreeFlags & TransformFlags.ContainsComputedPropertyName) { + // If an ObjectLiteralExpression contains a ComputedPropertyName, then it + // is an ES6 node. + transformFlags |= TransformFlags.AssertES6; + } + break; + + case SyntaxKind.CallExpression: + excludeFlags = TransformFlags.ArrayLiteralOrCallOrNewExcludes; + if (subtreeFlags & TransformFlags.ContainsSpreadElementExpression + || isSuperCall(node)) { + // If the this node contains a SpreadElementExpression, or is a super call, then it is an ES6 + // node. + transformFlags |= TransformFlags.AssertES6; + } + break; + + case SyntaxKind.ArrayLiteralExpression: + case SyntaxKind.NewExpression: + excludeFlags = TransformFlags.ArrayLiteralOrCallOrNewExcludes; + if (subtreeFlags & TransformFlags.ContainsSpreadElementExpression) { + // If the this node contains a SpreadElementExpression, then it is an ES6 + // node. + transformFlags |= TransformFlags.AssertES6; + } + break; + + case SyntaxKind.Decorator: + // This node is TypeScript syntax, and marks its container as also being TypeScript syntax. + transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsDecorators; + break; + + case SyntaxKind.ModuleDeclaration: + // This node is TypeScript syntax, and excludes markers that should not escape the module scope. + excludeFlags = TransformFlags.ModuleExcludes; + transformFlags |= TransformFlags.AssertTypeScript; + break; + + case SyntaxKind.ParenthesizedExpression: + // If the node is synthesized, it means the emitter put the parentheses there, + // not the user. If we didn't want them, the emitter would not have put them + // there. + if (!nodeIsSynthesized(node)) { + if ((node).expression.kind === SyntaxKind.AsExpression + || (node).expression.kind === SyntaxKind.TypeAssertionExpression) { + transformFlags = TransformFlags.AssertTypeScript; + } + } + break; + + case SyntaxKind.ExpressionStatement: + // if (node.flags & NodeFlags.Generated) { + // let expression = (node).expression; + // if (expression.kind === SyntaxKind.CallExpression + // && (expression).expression.kind === SyntaxKind.SuperKeyword) { + // transformFlags |= TransformFlags.AssertES6; + // } + // } + + break; + + case SyntaxKind.BinaryExpression: + if (isDestructuringAssignment(node)) { + // Destructuring assignments are ES6 syntax. + transformFlags |= TransformFlags.AssertES6; + } + else if ((node).operatorToken.kind === SyntaxKind.AsteriskAsteriskToken + || (node).operatorToken.kind === SyntaxKind.AsteriskAsteriskEqualsToken) { + // Exponentiation is ES7 syntax. + transformFlags |= TransformFlags.AssertES7; + } + + break; + + case SyntaxKind.Parameter: + // If the parameter has a question token, then it is TypeScript syntax. + if ((node).questionToken) { + transformFlags |= TransformFlags.AssertTypeScript; + } + + // If a parameter has an accessibility modifier, then it is TypeScript syntax. + if ((node).flags & NodeFlags.AccessibilityModifier) { + transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsParameterPropertyAssignments; + } + + // If a parameter has an initializer, a binding pattern or a dotDotDot token, then + // it is ES6 syntax and its container must emit default value assignments or parameter destructuring downlevel. + if ((node).initializer + || (node).dotDotDotToken + || isBindingPattern((node).name)) { + transformFlags |= TransformFlags.AssertES6 | TransformFlags.ContainsDefaultValueAssignments; + } + + break; + + case SyntaxKind.ArrowFunction: + // An ArrowFunction is ES6 syntax, and excludes markers that should not escape the scope of an ArrowFunction. + excludeFlags = TransformFlags.ArrowFunctionExcludes; + transformFlags = TransformFlags.AssertES6; + + // If an ArrowFunction contains a lexical this, its container must capture the lexical this. + if (subtreeFlags & TransformFlags.ContainsLexicalThis) { + transformFlags |= TransformFlags.ContainsCapturedLexicalThis; + } + + // An async arrow function is TypeScript syntax. + if (node.flags & NodeFlags.Async) { + transformFlags |= TransformFlags.AssertTypeScript; + } + + break; + + case SyntaxKind.FunctionExpression: + // A FunctionExpression excludes markers that should not escape the scope of a FunctionExpression. + excludeFlags = TransformFlags.FunctionExcludes; + + // If a FunctionExpression contains an asterisk token, or its subtree has marked the container + // as needing to capture the lexical this, then this node is ES6 syntax. + if ((node).asteriskToken + || subtreeFlags & TransformFlags.ContainsCapturedLexicalThis + || subtreeFlags & TransformFlags.ContainsDefaultValueAssignments) { + transformFlags |= TransformFlags.AssertES6; + } + + // An async function expression is TypeScript syntax. + if (node.flags & NodeFlags.Async) { + transformFlags |= TransformFlags.AssertTypeScript; + } + + break; + + case SyntaxKind.FunctionDeclaration: + // A FunctionDeclaration excludes markers that should not escape the scope of a FunctionDeclaration. + excludeFlags = TransformFlags.FunctionExcludes; + + // A FunctionDeclaration without a body is an overload and is TypeScript syntax. + if (!(node).body) { + transformFlags = TransformFlags.AssertTypeScript; + break; + } + + // If a FunctionDeclaration has an asterisk token, is exported, or its + // subtree has marked the container as needing to capture the lexical `this`, + // then this node is ES6 syntax. + if ((node).asteriskToken + || node.flags & NodeFlags.Export + || subtreeFlags & TransformFlags.ContainsCapturedLexicalThis + || subtreeFlags & TransformFlags.ContainsDefaultValueAssignments) { + transformFlags |= TransformFlags.AssertES6; + } + + // An async function declaration is TypeScript syntax. + if (node.flags & NodeFlags.Async) { + transformFlags |= TransformFlags.AssertTypeScript; + } + + break; + + case SyntaxKind.VariableDeclaration: + // A VariableDeclaration with a binding pattern is ES6 syntax. + if (isBindingPattern((node).name)) { + transformFlags |= TransformFlags.AssertES6; + } + + break; + + case SyntaxKind.VariableDeclarationList: + // If a VariableDeclarationList is `let` or `const`, then it is ES6 syntax. + if (node.flags & NodeFlags.Let + || node.flags & NodeFlags.Const) { + transformFlags |= TransformFlags.AssertES6; + } + + break; + + case SyntaxKind.VariableStatement: + // If a VariableStatement is exported, then it is ES6 syntax. + if (node.flags & NodeFlags.Export) { + transformFlags |= TransformFlags.AssertES6; + } + + break; + + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ClassExpression: + // A ClassDeclarations or ClassExpression is ES6 syntax. + excludeFlags = TransformFlags.ClassExcludes; + transformFlags = TransformFlags.AssertES6; + + // A class with a parameter property assignment, property initializer, or decorator is + // TypeScript syntax. + if (subtreeFlags & TransformFlags.ContainsParameterPropertyAssignments + || subtreeFlags & TransformFlags.ContainsPropertyInitializer + || subtreeFlags & TransformFlags.ContainsDecorators) { + transformFlags |= TransformFlags.AssertTypeScript; + } + + break; + + case SyntaxKind.HeritageClause: + // An `extends` HertiageClause is ES6 syntax. + if ((node).token === SyntaxKind.ExtendsKeyword) { + transformFlags |= TransformFlags.AssertES6; + } + + // An `implements` HeritageClause is TypeScript syntax. + else if ((node).token === SyntaxKind.ImplementsKeyword) { + transformFlags |= TransformFlags.AssertTypeScript; + } + + break; + + case SyntaxKind.ExpressionWithTypeArguments: + // An ExpressionWithTypeArguments is ES6 syntax, as it is used in the + // extends clause of a class. + transformFlags |= TransformFlags.AssertES6; + + // If an ExpressionWithTypeArguments contains type arguments, then it + // is TypeScript syntax. + if ((node).typeArguments) { + transformFlags |= TransformFlags.AssertTypeScript; + } + + break; + + case SyntaxKind.Constructor: + // A Constructor is ES6 syntax. + excludeFlags = TransformFlags.ConstructorExcludes; + transformFlags |= TransformFlags.AssertES6; + + // An overload constructor is TypeScript syntax. + if (!(node).body) { + transformFlags |= TransformFlags.AssertTypeScript; + } + + break; + + case SyntaxKind.PropertyDeclaration: + // A PropertyDeclaration is TypeScript syntax. + transformFlags |= TransformFlags.AssertTypeScript; + + // If the PropertyDeclaration has an initializer, we need to inform its ancestor + // so that it handle the transformation. + if ((node).initializer) { + transformFlags |= TransformFlags.ContainsPropertyInitializer; + } + + break; + + case SyntaxKind.MethodDeclaration: + // A MethodDeclaration is ES6 syntax. + excludeFlags = TransformFlags.MethodOrAccessorExcludes; + transformFlags |= TransformFlags.AssertES6; + + // A MethodDeclaration is TypeScript syntax if it is either async, abstract, overloaded, + // generic, or has both a computed property name and a decorator. + if ((node).body === undefined + || (node).typeParameters !== undefined + || node.flags & NodeFlags.Async + || node.flags & NodeFlags.Abstract + || (subtreeFlags & TransformFlags.ContainsDecorators + && subtreeFlags & TransformFlags.ContainsComputedPropertyName)) { + transformFlags |= TransformFlags.AssertTypeScript; + } + + break; + + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + // A GetAccessor or SetAccessor is ES5 syntax. + excludeFlags = TransformFlags.MethodOrAccessorExcludes; + + // A GetAccessor or SetAccessor is TypeScript syntax if it is either abstract, + // or has both a computed property name and a decorator. + if (node.flags & NodeFlags.Abstract || + subtreeFlags & TransformFlags.ContainsDecorators && + subtreeFlags & TransformFlags.ContainsComputedPropertyName) { + transformFlags |= TransformFlags.AssertTypeScript; + } + + break; + + case SyntaxKind.ImportEqualsDeclaration: + // An ImportEqualsDeclaration with a namespace reference is TypeScript. + if (!isExternalModuleImportEqualsDeclaration(node)) { + transformFlags |= TransformFlags.AssertTypeScript; + } + + break; + + case SyntaxKind.PropertyAccessExpression: + // If a PropertyAccessExpression starts with a super keyword, then it is + // ES6 syntax, and requires a lexical `this` binding. + if ((node).expression.kind === SyntaxKind.SuperKeyword) { + transformFlags |= TransformFlags.ContainsLexicalThis; + } + + break; + + case SyntaxKind.SourceFile: + if (subtreeFlags & TransformFlags.ContainsCapturedLexicalThis) { + transformFlags |= TransformFlags.AssertES6; + } + + break; + } + + return (node.transformFlags = subtreeFlags | transformFlags) + & ~(node.excludeTransformFlags = excludeFlags | TransformFlags.NodeExcludes); + } } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 48abb6ae3e3..613cf1cf165 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -865,6 +865,8 @@ namespace ts { this.pos = pos; this.end = end; this.flags = NodeFlags.None; + this.transformFlags = undefined; + this.excludeTransformFlags = undefined; this.parent = undefined; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d2d436f545f..4476a794ba8 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -442,6 +442,8 @@ namespace ts { export interface Node extends TextRange { kind: SyntaxKind; flags: NodeFlags; + /* @internal */ transformFlags?: TransformFlags; + /* @internal */ excludeTransformFlags?: TransformFlags; decorators?: NodeArray; // Array of decorators (in document order) modifiers?: ModifiersArray; // Array of modifiers /* @internal */ id?: number; // Unique id (used to look up NodeLinks) @@ -2707,6 +2709,52 @@ namespace ts { resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[]; } + /* @internal */ + export const enum TransformFlags { + // Facts + // - Flags used to indicate that a node or subtree contains syntax that requires transformation. + TypeScript = 1 << 0, + ContainsTypeScript = 1 << 1, + Jsx = 1 << 2, + ContainsJsx = 1 << 3, + ES7 = 1 << 4, + ContainsES7 = 1 << 5, + ES6 = 1 << 6, + ContainsES6 = 1 << 7, + + // Assertions + // - Bitmasks that are used to assert facts about the syntax of a node and its subtree. + AssertTypeScript = TypeScript | ContainsTypeScript, + AssertJsx = Jsx | ContainsJsx, + AssertES7 = ES7 | ContainsES7, + AssertES6 = ES6 | ContainsES6, + + // Markers + // - Flags used to indicate that a subtree contains a specific transformation. + ContainsDecorators = 1 << 8, + ContainsPropertyInitializer = 1 << 9, + ContainsLexicalThis = 1 << 10, + ContainsCapturedLexicalThis = 1 << 11, + ContainsDefaultValueAssignments = 1 << 12, + ContainsParameterPropertyAssignments = 1 << 13, + ContainsSpreadElementExpression = 1 << 14, + ContainsComputedPropertyName = 1 << 15, + + // Scope Exclusions + // - Bitmasks that exclude flags from propagating out of a specific context + // into the subtree flags of their container. + NodeExcludes = TypeScript | Jsx | ES7 | ES6, + ArrowFunctionExcludes = ContainsDecorators | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsParameterPropertyAssignments, + FunctionExcludes = ContainsDecorators | ContainsDefaultValueAssignments | ContainsCapturedLexicalThis | ContainsLexicalThis | ContainsParameterPropertyAssignments, + ConstructorExcludes = ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsParameterPropertyAssignments, + MethodOrAccessorExcludes = ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis, + ClassExcludes = ContainsDecorators | ContainsPropertyInitializer | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsComputedPropertyName | ContainsParameterPropertyAssignments, + ModuleExcludes = ContainsDecorators | ContainsLexicalThis | ContainsCapturedLexicalThis, + TypeExcludes = ~ContainsTypeScript, + ObjectLiteralExcludes = ContainsDecorators | ContainsComputedPropertyName, + ArrayLiteralOrCallOrNewExcludes = ContainsSpreadElementExpression, + } + export interface TextSpan { start: number; length: number; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 9fe32c89a69..2645a6b5343 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -872,12 +872,20 @@ namespace ts { /** * Determines whether a node is a property or element access expression for super. */ - export function isSuperPropertyOrElementAccess(node: Node) { + export function isSuperPropertyOrElementAccess(node: Node): node is (PropertyAccessExpression | ElementAccessExpression) { return (node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.ElementAccessExpression) && (node).expression.kind === SyntaxKind.SuperKeyword; } + /** + * Determines whether a node is a call to either `super`, or a super property or element access. + */ + export function isSuperCall(node: Node): node is CallExpression { + return node.kind === SyntaxKind.CallExpression + && ((node).expression.kind === SyntaxKind.SuperKeyword + || isSuperPropertyOrElementAccess((node).expression)); + } export function getEntityNameFromTypeNode(node: TypeNode): EntityName | Expression { if (node) { @@ -1666,7 +1674,7 @@ namespace ts { : createSynthesizedNode(node.kind); for (const key in node) { - if (key === "parent" || key === "flags" || clone.hasOwnProperty(key) || !node.hasOwnProperty(key)) { + if (clone.hasOwnProperty(key) || !node.hasOwnProperty(key)) { continue; } @@ -2694,6 +2702,18 @@ namespace ts { || isIdentifierNode(node); } + export function isDestructuringAssignment(node: Node): node is BinaryExpression { + if (isBinaryExpression(node)) { + if (node.operatorToken.kind === SyntaxKind.EqualsToken) { + const kind = node.left.kind; + return kind === SyntaxKind.ObjectLiteralExpression + || kind === SyntaxKind.ArrayLiteralExpression; + } + } + + return false; + } + // Returns false if this heritage clause element's expression contains something unsupported // (i.e. not a name or dotted name). export function isSupportedExpressionWithTypeArguments(node: ExpressionWithTypeArguments): boolean { diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 30717578eb8..e2e1b9cae54 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -505,6 +505,7 @@ namespace ts { Debug.assert(test(visited), "Wrong node type after visit."); } + aggregateTransformFlags(visited); return visited; } @@ -542,6 +543,10 @@ namespace ts { } else if (visited !== undefined) { Debug.assert(test(visited), "Wrong node type after visit."); + if (visited !== node) { + aggregateTransformFlags(visited); + } + updated.push(visited); } } @@ -606,10 +611,14 @@ namespace ts { if (isNewLexicalEnvironment) { const declarations = environment.endLexicalEnvironment(); if (declarations !== undefined && declarations.length > 0) { - return mergeLexicalEnvironment(updated, declarations, /*nodeIsMutable*/ updated !== node); + updated = mergeLexicalEnvironment(updated, declarations, /*nodeIsMutable*/ updated !== node); } } + if (updated !== node) { + aggregateTransformFlags(updated); + } + return updated; } @@ -766,4 +775,53 @@ namespace ts { Debug.assert(nodes.length <= 1, "Too many nodes written to output."); return nodes.length > 0 ? nodes[0] : undefined; } + + /** + * Aggregates the TransformFlags for a Node and its subtree. + */ + export function aggregateTransformFlags(node: Node): void { + aggregateTransformFlagsForNode(node); + } + + /** + * Aggregates the TransformFlags for a Node and its subtree. The flags for the subtree are + * computed first, then the transform flags for the current node are computed from the subtree + * flags and the state of the current node. Finally, the transform flags of the node are + * returned, excluding any flags that should not be included in its parent node's subtree + * flags. + */ + function aggregateTransformFlagsForNode(node: Node): TransformFlags { + if (node === undefined) { + return 0; + } + + if (node.transformFlags === undefined) { + const subtreeFlags = aggregateTransformFlagsForSubtree(node); + return computeTransformFlagsForNode(node, subtreeFlags); + } + + return node.transformFlags & ~node.excludeTransformFlags; + } + + /** + * Aggregates the transform flags for the subtree of a node. + */ + function aggregateTransformFlagsForSubtree(node: Node): TransformFlags { + // We do not transform ambient declarations or types, so there is no need to + // recursively aggregate transform flags. + if (node.flags & NodeFlags.Ambient || isTypeNodeNode(node)) { + return 0; + } + + // Aggregate the transform flags of each child. + return reduceEachChild(node, aggregateTransformFlagsForChildNode, 0); + } + + /** + * Aggregates the TransformFlags of a child node with the TransformFlags of its + * siblings. + */ + function aggregateTransformFlagsForChildNode(transformFlags: TransformFlags, child: Node): TransformFlags { + return transformFlags | aggregateTransformFlagsForNode(child); + } } \ No newline at end of file diff --git a/src/services/services.ts b/src/services/services.ts index 85b2bfb58e8..5173b4edde5 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -186,6 +186,8 @@ namespace ts { public end: number; public flags: NodeFlags; public parent: Node; + public transformFlags: TransformFlags; + public excludeTransformFlags: TransformFlags; private _children: Node[]; constructor(kind: SyntaxKind, pos: number, end: number) { @@ -193,6 +195,8 @@ namespace ts { this.pos = pos; this.end = end; this.flags = NodeFlags.None; + this.transformFlags = undefined; + this.excludeTransformFlags = undefined; this.parent = undefined; } @@ -1876,7 +1880,7 @@ namespace ts { options.isolatedModules = true; - // transpileModule does not write anything to disk so there is no need to verify that there are no conflicts between input and output paths. + // transpileModule does not write anything to disk so there is no need to verify that there are no conflicts between input and output paths. options.suppressOutputPathCheck = true; // Filename can be non-ts file. @@ -6102,7 +6106,7 @@ namespace ts { } } - // For export specifiers, it can be a local symbol, e.g. + // For export specifiers, it can be a local symbol, e.g. // import {a} from "mod"; // export {a as somethingElse} // We want the local target of the export (i.e. the import symbol) and not the final target (i.e. "mod".a)