diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index afa6e36a6de..6eee041f7e8 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -153,10 +153,6 @@ namespace ts { const unreachableFlow: FlowNode = { flags: FlowFlags.Unreachable }; const reportedUnreachableFlow: FlowNode = { flags: FlowFlags.Unreachable }; - // state used to aggregate transform flags during bind. - let subtreeTransformFlags: TransformFlags = TransformFlags.None; - let skipTransformFlagAggregation: boolean; - /** * Inside the binder, we may create a diagnostic for an as-yet unbound node (with potentially no parent pointers, implying no accessible source file) * If so, the node _must_ be in the current file (as that's the only way anything could have traversed to it to yield it as the error node) @@ -173,7 +169,6 @@ namespace ts { inStrictMode = bindInStrictMode(file, opts); classifiableNames = createUnderscoreEscapedMap(); symbolCount = 0; - skipTransformFlagAggregation = file.isDeclarationFile; Symbol = objectAllocator.getSymbolConstructor(); @@ -203,7 +198,6 @@ namespace ts { activeLabels = undefined!; hasExplicitReturn = false; emitFlags = NodeFlags.None; - subtreeTransformFlags = TransformFlags.None; } return bindSourceFile; @@ -618,21 +612,7 @@ namespace ts { } function bindChildren(node: Node): void { - if (skipTransformFlagAggregation) { - bindChildrenWorker(node); - } - else if (node.transformFlags & TransformFlags.HasComputedFlags) { - skipTransformFlagAggregation = true; - bindChildrenWorker(node); - skipTransformFlagAggregation = false; - subtreeTransformFlags |= node.transformFlags & ~getTransformFlagsSubtreeExclusions(node.kind); - } - else { - const savedSubtreeTransformFlags = subtreeTransformFlags; - subtreeTransformFlags = 0; - bindChildrenWorker(node); - subtreeTransformFlags = savedSubtreeTransformFlags | computeTransformFlagsForNode(node, subtreeTransformFlags); - } + bindChildrenWorker(node); } function bindEachFunctionsFirst(nodes: NodeArray | undefined): void { @@ -645,20 +625,7 @@ namespace ts { return; } - if (skipTransformFlagAggregation) { - forEach(nodes, bindFunction); - } - else { - const savedSubtreeTransformFlags = subtreeTransformFlags; - subtreeTransformFlags = TransformFlags.None; - let nodeArrayFlags = TransformFlags.None; - for (const node of nodes) { - bindFunction(node); - nodeArrayFlags |= node.transformFlags & ~TransformFlags.HasComputedFlags; - } - nodes.transformFlags = nodeArrayFlags | TransformFlags.HasComputedFlags; - subtreeTransformFlags |= savedSubtreeTransformFlags; - } + forEach(nodes, bindFunction); } function bindEachChild(node: Node) { @@ -1230,8 +1197,6 @@ namespace ts { } function bindCaseBlock(node: CaseBlock): void { - const savedSubtreeTransformFlags = subtreeTransformFlags; - subtreeTransformFlags = 0; const clauses = node.clauses; let fallthroughFlow = unreachableFlow; for (let i = 0; i < clauses.length; i++) { @@ -1251,8 +1216,6 @@ namespace ts { errorOnFirstToken(clause, Diagnostics.Fallthrough_case_in_switch); } } - clauses.transformFlags = subtreeTransformFlags | TransformFlags.HasComputedFlags; - subtreeTransformFlags |= savedSubtreeTransformFlags; } function bindCaseClause(node: CaseClause): void { @@ -1801,6 +1764,8 @@ namespace ts { const saveBlockScopeContainer = blockScopeContainer; const saveParent = parent; const saveCurrentFlow = currentFlow; + // const saveSkipTransformFlagAggregation = skipTransformFlagAggregation; + // skipTransformFlagAggregation = true; for (const typeAlias of delayedTypeAliases) { const host = getJSDocHost(typeAlias); container = findAncestor(host.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer)) || file; @@ -1833,6 +1798,7 @@ namespace ts { blockScopeContainer = saveBlockScopeContainer; parent = saveParent; currentFlow = saveCurrentFlow; + // skipTransformFlagAggregation = saveSkipTransformFlagAggregation; } // The binder visits every node in the syntax tree so it is a convenient place to perform a single localized @@ -2069,8 +2035,7 @@ namespace ts { } parent = saveParent; } - else if (!skipTransformFlagAggregation && (node.transformFlags & TransformFlags.HasComputedFlags) === 0) { - subtreeTransformFlags |= computeTransformFlagsForNode(node, 0); + else { const saveParent = parent; if (node.kind === SyntaxKind.EndOfFileToken) parent = node; bindJSDoc(node); @@ -2081,6 +2046,8 @@ namespace ts { function bindJSDoc(node: Node) { if (hasJSDocNodes(node)) { + // const savedSkipTransformFlagAggregation = skipTransformFlagAggregation; + // skipTransformFlagAggregation = true; if (isInJSFile(node)) { for (const j of node.jsDoc!) { bind(j); @@ -2091,6 +2058,7 @@ namespace ts { setParentPointers(node, j); } } + // skipTransformFlagAggregation = savedSkipTransformFlagAggregation; } } @@ -3030,938 +2998,6 @@ namespace ts { return container.symbol && container.symbol.exports && container.symbol.exports.get(name); } - /** - * 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): TransformFlags { - const kind = node.kind; - switch (kind) { - case SyntaxKind.CallExpression: - return computeCallExpression(node, subtreeFlags); - - case SyntaxKind.NewExpression: - return computeNewExpression(node, subtreeFlags); - - case SyntaxKind.ModuleDeclaration: - return computeModuleDeclaration(node, subtreeFlags); - - case SyntaxKind.ParenthesizedExpression: - return computeParenthesizedExpression(node, subtreeFlags); - - case SyntaxKind.BinaryExpression: - return computeBinaryExpression(node, subtreeFlags); - - case SyntaxKind.ExpressionStatement: - return computeExpressionStatement(node, subtreeFlags); - - case SyntaxKind.Parameter: - return computeParameter(node, subtreeFlags); - - case SyntaxKind.ArrowFunction: - return computeArrowFunction(node, subtreeFlags); - - case SyntaxKind.FunctionExpression: - return computeFunctionExpression(node, subtreeFlags); - - case SyntaxKind.FunctionDeclaration: - return computeFunctionDeclaration(node, subtreeFlags); - - case SyntaxKind.VariableDeclaration: - return computeVariableDeclaration(node, subtreeFlags); - - case SyntaxKind.VariableDeclarationList: - return computeVariableDeclarationList(node, subtreeFlags); - - case SyntaxKind.VariableStatement: - return computeVariableStatement(node, subtreeFlags); - - case SyntaxKind.LabeledStatement: - return computeLabeledStatement(node, subtreeFlags); - - case SyntaxKind.ClassDeclaration: - return computeClassDeclaration(node, subtreeFlags); - - case SyntaxKind.ClassExpression: - return computeClassExpression(node, subtreeFlags); - - case SyntaxKind.HeritageClause: - return computeHeritageClause(node, subtreeFlags); - - case SyntaxKind.CatchClause: - return computeCatchClause(node, subtreeFlags); - - case SyntaxKind.ExpressionWithTypeArguments: - return computeExpressionWithTypeArguments(node, subtreeFlags); - - case SyntaxKind.Constructor: - return computeConstructor(node, subtreeFlags); - - case SyntaxKind.PropertyDeclaration: - return computePropertyDeclaration(node, subtreeFlags); - - case SyntaxKind.MethodDeclaration: - return computeMethod(node, subtreeFlags); - - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - return computeAccessor(node, subtreeFlags); - - case SyntaxKind.ImportEqualsDeclaration: - return computeImportEquals(node, subtreeFlags); - - case SyntaxKind.PropertyAccessExpression: - return computePropertyAccess(node, subtreeFlags); - - case SyntaxKind.ElementAccessExpression: - return computeElementAccess(node, subtreeFlags); - - default: - return computeOther(node, kind, subtreeFlags); - } - } - - function computeCallExpression(node: CallExpression, subtreeFlags: TransformFlags) { - let transformFlags = subtreeFlags; - const callee = skipOuterExpressions(node.expression); - const expression = node.expression; - - if (node.typeArguments) { - transformFlags |= TransformFlags.AssertTypeScript; - } - - if (subtreeFlags & TransformFlags.ContainsRestOrSpread || isSuperOrSuperProperty(callee)) { - // If the this node contains a SpreadExpression, or is a super call, then it is an ES6 - // node. - transformFlags |= TransformFlags.AssertES2015; - if (isSuperProperty(callee)) { - transformFlags |= TransformFlags.ContainsLexicalThis; - } - } - - if (expression.kind === SyntaxKind.ImportKeyword) { - transformFlags |= TransformFlags.ContainsDynamicImport; - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.ArrayLiteralOrCallOrNewExcludes; - } - - function computeNewExpression(node: NewExpression, subtreeFlags: TransformFlags) { - let transformFlags = subtreeFlags; - if (node.typeArguments) { - transformFlags |= TransformFlags.AssertTypeScript; - } - if (subtreeFlags & TransformFlags.ContainsRestOrSpread) { - // If the this node contains a SpreadElementExpression then it is an ES6 - // node. - transformFlags |= TransformFlags.AssertES2015; - } - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.ArrayLiteralOrCallOrNewExcludes; - } - - function computeBinaryExpression(node: BinaryExpression, subtreeFlags: TransformFlags) { - let transformFlags = subtreeFlags; - const operatorTokenKind = node.operatorToken.kind; - const leftKind = node.left.kind; - - if (operatorTokenKind === SyntaxKind.EqualsToken && leftKind === SyntaxKind.ObjectLiteralExpression) { - // Destructuring object assignments with are ES2015 syntax - // and possibly ES2018 if they contain rest - transformFlags |= TransformFlags.AssertES2018 | TransformFlags.AssertES2015 | TransformFlags.AssertDestructuringAssignment; - } - else if (operatorTokenKind === SyntaxKind.EqualsToken && leftKind === SyntaxKind.ArrayLiteralExpression) { - // Destructuring assignments are ES2015 syntax. - transformFlags |= TransformFlags.AssertES2015 | TransformFlags.AssertDestructuringAssignment; - } - else if (operatorTokenKind === SyntaxKind.AsteriskAsteriskToken - || operatorTokenKind === SyntaxKind.AsteriskAsteriskEqualsToken) { - // Exponentiation is ES2016 syntax. - transformFlags |= TransformFlags.AssertES2016; - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.NodeExcludes; - } - - function computeParameter(node: ParameterDeclaration, subtreeFlags: TransformFlags) { - let transformFlags = subtreeFlags; - const name = node.name; - const initializer = node.initializer; - const dotDotDotToken = node.dotDotDotToken; - - // The '?' token, type annotations, decorators, and 'this' parameters are TypeSCript - // syntax. - if (node.questionToken - || node.type - || (subtreeFlags & TransformFlags.ContainsTypeScriptClassSyntax && some(node.decorators)) - || isThisIdentifier(name)) { - transformFlags |= TransformFlags.AssertTypeScript; - } - - // If a parameter has an accessibility modifier, then it is TypeScript syntax. - if (hasModifier(node, ModifierFlags.ParameterPropertyModifier)) { - transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsTypeScriptClassSyntax; - } - - // parameters with object rest destructuring are ES2018 syntax - if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) { - transformFlags |= TransformFlags.AssertES2018; - } - - // 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 (subtreeFlags & TransformFlags.ContainsBindingPattern || initializer || dotDotDotToken) { - transformFlags |= TransformFlags.AssertES2015; - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.ParameterExcludes; - } - - function computeParenthesizedExpression(node: ParenthesizedExpression, subtreeFlags: TransformFlags) { - let transformFlags = subtreeFlags; - const expression = node.expression; - const expressionKind = expression.kind; - - // 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 (expressionKind === SyntaxKind.AsExpression - || expressionKind === SyntaxKind.TypeAssertionExpression) { - transformFlags |= TransformFlags.AssertTypeScript; - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.OuterExpressionExcludes; - } - - function computeClassDeclaration(node: ClassDeclaration, subtreeFlags: TransformFlags) { - let transformFlags: TransformFlags; - - if (hasModifier(node, ModifierFlags.Ambient)) { - // An ambient declaration is TypeScript syntax. - transformFlags = TransformFlags.AssertTypeScript; - } - else { - // A ClassDeclaration is ES6 syntax. - transformFlags = subtreeFlags | TransformFlags.AssertES2015; - - // A class with a parameter property assignment or decorator is TypeScript syntax. - // An exported declaration may be TypeScript syntax, but is handled by the visitor - // for a namespace declaration. - if ((subtreeFlags & TransformFlags.ContainsTypeScriptClassSyntax) - || node.typeParameters) { - transformFlags |= TransformFlags.AssertTypeScript; - } - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.ClassExcludes; - } - - function computeClassExpression(node: ClassExpression, subtreeFlags: TransformFlags) { - // A ClassExpression is ES6 syntax. - let transformFlags = subtreeFlags | TransformFlags.AssertES2015; - - // A class with a parameter property assignment or decorator is TypeScript syntax. - if (subtreeFlags & TransformFlags.ContainsTypeScriptClassSyntax - || node.typeParameters) { - transformFlags |= TransformFlags.AssertTypeScript; - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.ClassExcludes; - } - - function computeHeritageClause(node: HeritageClause, subtreeFlags: TransformFlags) { - let transformFlags = subtreeFlags; - - switch (node.token) { - case SyntaxKind.ExtendsKeyword: - // An `extends` HeritageClause is ES6 syntax. - transformFlags |= TransformFlags.AssertES2015; - break; - - case SyntaxKind.ImplementsKeyword: - // An `implements` HeritageClause is TypeScript syntax. - transformFlags |= TransformFlags.AssertTypeScript; - break; - - default: - Debug.fail("Unexpected token for heritage clause"); - break; - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.NodeExcludes; - } - - function computeCatchClause(node: CatchClause, subtreeFlags: TransformFlags) { - let transformFlags = subtreeFlags; - - if (!node.variableDeclaration) { - transformFlags |= TransformFlags.AssertES2019; - } - else if (isBindingPattern(node.variableDeclaration.name)) { - transformFlags |= TransformFlags.AssertES2015; - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.CatchClauseExcludes; - } - - function computeExpressionWithTypeArguments(node: ExpressionWithTypeArguments, subtreeFlags: TransformFlags) { - // An ExpressionWithTypeArguments is ES6 syntax, as it is used in the - // extends clause of a class. - let transformFlags = subtreeFlags | TransformFlags.AssertES2015; - - // If an ExpressionWithTypeArguments contains type arguments, then it - // is TypeScript syntax. - if (node.typeArguments) { - transformFlags |= TransformFlags.AssertTypeScript; - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.NodeExcludes; - } - - function computeConstructor(node: ConstructorDeclaration, subtreeFlags: TransformFlags) { - let transformFlags = subtreeFlags; - - // TypeScript-specific modifiers and overloads are TypeScript syntax - if (hasModifier(node, ModifierFlags.TypeScriptModifier) - || !node.body) { - transformFlags |= TransformFlags.AssertTypeScript; - } - - // function declarations with object rest destructuring are ES2018 syntax - if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) { - transformFlags |= TransformFlags.AssertES2018; - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.ConstructorExcludes; - } - - function computeMethod(node: MethodDeclaration, subtreeFlags: TransformFlags) { - // A MethodDeclaration is ES6 syntax. - let transformFlags = subtreeFlags | TransformFlags.AssertES2015; - - // Decorators, TypeScript-specific modifiers, type parameters, type annotations, and - // overloads are TypeScript syntax. - if (node.decorators - || hasModifier(node, ModifierFlags.TypeScriptModifier) - || node.typeParameters - || node.type - || !node.body) { - transformFlags |= TransformFlags.AssertTypeScript; - } - - // function declarations with object rest destructuring are ES2018 syntax - if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) { - transformFlags |= TransformFlags.AssertES2018; - } - - // An async method declaration is ES2017 syntax. - if (hasModifier(node, ModifierFlags.Async)) { - transformFlags |= node.asteriskToken ? TransformFlags.AssertES2018 : TransformFlags.AssertES2017; - } - - if (node.asteriskToken) { - transformFlags |= TransformFlags.AssertGenerator; - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return propagatePropertyNameFlags(node.name, transformFlags & ~TransformFlags.MethodOrAccessorExcludes); - } - - function computeAccessor(node: AccessorDeclaration, subtreeFlags: TransformFlags) { - let transformFlags = subtreeFlags; - - // Decorators, TypeScript-specific modifiers, type annotations, and overloads are - // TypeScript syntax. - if (node.decorators - || hasModifier(node, ModifierFlags.TypeScriptModifier) - || node.type - || !node.body) { - transformFlags |= TransformFlags.AssertTypeScript; - } - - // function declarations with object rest destructuring are ES2018 syntax - if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) { - transformFlags |= TransformFlags.AssertES2018; - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return propagatePropertyNameFlags(node.name, transformFlags & ~TransformFlags.MethodOrAccessorExcludes); - } - - function computePropertyDeclaration(node: PropertyDeclaration, subtreeFlags: TransformFlags) { - let transformFlags = subtreeFlags | TransformFlags.ContainsClassFields; - - // Decorators, TypeScript-specific modifiers, and type annotations are TypeScript syntax. - if (some(node.decorators) || hasModifier(node, ModifierFlags.TypeScriptModifier) || node.type) { - transformFlags |= TransformFlags.AssertTypeScript; - } - - // Hoisted variables related to class properties should live within the TypeScript class wrapper. - if (isComputedPropertyName(node.name) || (hasStaticModifier(node) && node.initializer)) { - transformFlags |= TransformFlags.ContainsTypeScriptClassSyntax; - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return propagatePropertyNameFlags(node.name, transformFlags & ~TransformFlags.PropertyExcludes); - } - - function computeFunctionDeclaration(node: FunctionDeclaration, subtreeFlags: TransformFlags) { - let transformFlags: TransformFlags; - const modifierFlags = getModifierFlags(node); - const body = node.body; - - if (!body || (modifierFlags & ModifierFlags.Ambient)) { - // An ambient declaration is TypeScript syntax. - // A FunctionDeclaration without a body is an overload and is TypeScript syntax. - transformFlags = TransformFlags.AssertTypeScript; - } - else { - transformFlags = subtreeFlags | TransformFlags.ContainsHoistedDeclarationOrCompletion; - - // TypeScript-specific modifiers, type parameters, and type annotations are TypeScript - // syntax. - if (modifierFlags & ModifierFlags.TypeScriptModifier - || node.typeParameters - || node.type) { - transformFlags |= TransformFlags.AssertTypeScript; - } - - // An async function declaration is ES2017 syntax. - if (modifierFlags & ModifierFlags.Async) { - transformFlags |= node.asteriskToken ? TransformFlags.AssertES2018 : TransformFlags.AssertES2017; - } - - // function declarations with object rest destructuring are ES2018 syntax - if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) { - transformFlags |= TransformFlags.AssertES2018; - } - - // If a FunctionDeclaration is generator function and is the body of a - // transformed async function, then this node can be transformed to a - // down-level generator. - // Currently we do not support transforming any other generator functions - // down level. - if (node.asteriskToken) { - transformFlags |= TransformFlags.AssertGenerator; - } - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.FunctionExcludes; - } - - function computeFunctionExpression(node: FunctionExpression, subtreeFlags: TransformFlags) { - let transformFlags = subtreeFlags; - - // TypeScript-specific modifiers, type parameters, and type annotations are TypeScript - // syntax. - if (hasModifier(node, ModifierFlags.TypeScriptModifier) - || node.typeParameters - || node.type) { - transformFlags |= TransformFlags.AssertTypeScript; - } - - // An async function expression is ES2017 syntax. - if (hasModifier(node, ModifierFlags.Async)) { - transformFlags |= node.asteriskToken ? TransformFlags.AssertES2018 : TransformFlags.AssertES2017; - } - - // function expressions with object rest destructuring are ES2018 syntax - if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) { - transformFlags |= TransformFlags.AssertES2018; - } - - // If a FunctionExpression is generator function and is the body of a - // transformed async function, then this node can be transformed to a - // down-level generator. - if (node.asteriskToken) { - transformFlags |= TransformFlags.AssertGenerator; - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.FunctionExcludes; - } - - function computeArrowFunction(node: ArrowFunction, subtreeFlags: TransformFlags) { - // An ArrowFunction is ES6 syntax, and excludes markers that should not escape the scope of an ArrowFunction. - let transformFlags = subtreeFlags | TransformFlags.AssertES2015; - - // TypeScript-specific modifiers, type parameters, and type annotations are TypeScript - // syntax. - if (hasModifier(node, ModifierFlags.TypeScriptModifier) - || node.typeParameters - || node.type) { - transformFlags |= TransformFlags.AssertTypeScript; - } - - // An async arrow function is ES2017 syntax. - if (hasModifier(node, ModifierFlags.Async)) { - transformFlags |= TransformFlags.AssertES2017; - } - - // arrow functions with object rest destructuring are ES2018 syntax - if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) { - transformFlags |= TransformFlags.AssertES2018; - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.ArrowFunctionExcludes; - } - - function computePropertyAccess(node: PropertyAccessExpression, subtreeFlags: TransformFlags) { - let transformFlags = subtreeFlags; - - // 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) { - // super inside of an async function requires hoisting the super access (ES2017). - // same for super inside of an async generator, which is ES2018. - transformFlags |= TransformFlags.ContainsES2017 | TransformFlags.ContainsES2018; - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.PropertyAccessExcludes; - } - - function computeElementAccess(node: ElementAccessExpression, subtreeFlags: TransformFlags) { - let transformFlags = subtreeFlags; - - // If an ElementAccessExpression starts with a super keyword, then it is - // ES6 syntax, and requires a lexical `this` binding. - if (node.expression.kind === SyntaxKind.SuperKeyword) { - // super inside of an async function requires hoisting the super access (ES2017). - // same for super inside of an async generator, which is ES2018. - transformFlags |= TransformFlags.ContainsES2017 | TransformFlags.ContainsES2018; - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.PropertyAccessExcludes; - } - - function computeVariableDeclaration(node: VariableDeclaration, subtreeFlags: TransformFlags) { - let transformFlags = subtreeFlags; - transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsBindingPattern; // TODO(rbuckton): Why are these set unconditionally? - - // A VariableDeclaration containing ObjectRest is ES2018 syntax - if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) { - transformFlags |= TransformFlags.AssertES2018; - } - - // Type annotations are TypeScript syntax. - if (node.type) { - transformFlags |= TransformFlags.AssertTypeScript; - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.NodeExcludes; - } - - function computeVariableStatement(node: VariableStatement, subtreeFlags: TransformFlags) { - let transformFlags: TransformFlags; - const declarationListTransformFlags = node.declarationList.transformFlags; - - // An ambient declaration is TypeScript syntax. - if (hasModifier(node, ModifierFlags.Ambient)) { - transformFlags = TransformFlags.AssertTypeScript; - } - else { - transformFlags = subtreeFlags; - - if (declarationListTransformFlags & TransformFlags.ContainsBindingPattern) { - transformFlags |= TransformFlags.AssertES2015; - } - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.NodeExcludes; - } - - function computeLabeledStatement(node: LabeledStatement, subtreeFlags: TransformFlags) { - let transformFlags = subtreeFlags; - - // A labeled statement containing a block scoped binding *may* need to be transformed from ES6. - if (subtreeFlags & TransformFlags.ContainsBlockScopedBinding - && isIterationStatement(node, /*lookInLabeledStatements*/ true)) { - transformFlags |= TransformFlags.AssertES2015; - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.NodeExcludes; - } - - function computeImportEquals(node: ImportEqualsDeclaration, subtreeFlags: TransformFlags) { - let transformFlags = subtreeFlags; - - // An ImportEqualsDeclaration with a namespace reference is TypeScript. - if (!isExternalModuleImportEqualsDeclaration(node)) { - transformFlags |= TransformFlags.AssertTypeScript; - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.NodeExcludes; - } - - function computeExpressionStatement(node: ExpressionStatement, subtreeFlags: TransformFlags) { - const transformFlags = subtreeFlags; - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.NodeExcludes; - } - - function computeModuleDeclaration(node: ModuleDeclaration, subtreeFlags: TransformFlags) { - let transformFlags = TransformFlags.AssertTypeScript; - const modifierFlags = getModifierFlags(node); - - if ((modifierFlags & ModifierFlags.Ambient) === 0) { - transformFlags |= subtreeFlags; - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.ModuleExcludes; - } - - function computeVariableDeclarationList(node: VariableDeclarationList, subtreeFlags: TransformFlags) { - let transformFlags = subtreeFlags | TransformFlags.ContainsHoistedDeclarationOrCompletion; - - if (subtreeFlags & TransformFlags.ContainsBindingPattern) { - transformFlags |= TransformFlags.AssertES2015; - } - - // If a VariableDeclarationList is `let` or `const`, then it is ES6 syntax. - if (node.flags & NodeFlags.BlockScoped) { - transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsBlockScopedBinding; - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.VariableDeclarationListExcludes; - } - - function computeOther(node: Node, kind: SyntaxKind, subtreeFlags: TransformFlags) { - // Mark transformations needed for each node - let transformFlags = subtreeFlags; - let excludeFlags = TransformFlags.NodeExcludes; - - switch (kind) { - case SyntaxKind.AsyncKeyword: - case SyntaxKind.AwaitExpression: - // async/await is ES2017 syntax, but may be ES2018 syntax (for async generators) - transformFlags |= TransformFlags.AssertES2018 | TransformFlags.AssertES2017; - break; - - case SyntaxKind.TypeAssertionExpression: - case SyntaxKind.AsExpression: - case SyntaxKind.PartiallyEmittedExpression: - // These nodes are TypeScript syntax. - transformFlags |= TransformFlags.AssertTypeScript; - excludeFlags = TransformFlags.OuterExpressionExcludes; - break; - case SyntaxKind.PublicKeyword: - case SyntaxKind.PrivateKeyword: - case SyntaxKind.ProtectedKeyword: - case SyntaxKind.AbstractKeyword: - case SyntaxKind.DeclareKeyword: - case SyntaxKind.ConstKeyword: - case SyntaxKind.EnumDeclaration: - case SyntaxKind.EnumMember: - case SyntaxKind.NonNullExpression: - 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.JsxFragment: - case SyntaxKind.JsxOpeningFragment: - case SyntaxKind.JsxClosingFragment: - case SyntaxKind.JsxAttribute: - case SyntaxKind.JsxAttributes: - 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.StaticKeyword: - case SyntaxKind.MetaProperty: - // These nodes are ES6 syntax. - transformFlags |= TransformFlags.AssertES2015; - break; - - case SyntaxKind.StringLiteral: - if ((node).hasExtendedUnicodeEscape) { - transformFlags |= TransformFlags.AssertES2015; - } - break; - - case SyntaxKind.NumericLiteral: - if ((node).numericLiteralFlags & TokenFlags.BinaryOrOctalSpecifier) { - transformFlags |= TransformFlags.AssertES2015; - } - break; - - case SyntaxKind.BigIntLiteral: - transformFlags |= TransformFlags.AssertESNext; - break; - - case SyntaxKind.ForOfStatement: - // This node is either ES2015 syntax or ES2017 syntax (if it is a for-await-of). - if ((node).awaitModifier) { - transformFlags |= TransformFlags.AssertES2018; - } - transformFlags |= TransformFlags.AssertES2015; - break; - - case SyntaxKind.YieldExpression: - // This node is either ES2015 syntax (in a generator) or ES2017 syntax (in an async - // generator). - transformFlags |= TransformFlags.AssertES2018 | TransformFlags.AssertES2015 | TransformFlags.ContainsYield; - break; - - case SyntaxKind.AnyKeyword: - case SyntaxKind.NumberKeyword: - case SyntaxKind.BigIntKeyword: - case SyntaxKind.NeverKeyword: - case SyntaxKind.ObjectKeyword: - 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.OptionalType: - case SyntaxKind.RestType: - case SyntaxKind.UnionType: - case SyntaxKind.IntersectionType: - case SyntaxKind.ConditionalType: - case SyntaxKind.InferType: - case SyntaxKind.ParenthesizedType: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.TypeAliasDeclaration: - case SyntaxKind.ThisType: - case SyntaxKind.TypeOperator: - case SyntaxKind.IndexedAccessType: - case SyntaxKind.MappedType: - case SyntaxKind.LiteralType: - case SyntaxKind.NamespaceExportDeclaration: - // Types and signatures are TypeScript syntax, and exclude all other facts. - transformFlags = TransformFlags.AssertTypeScript; - excludeFlags = TransformFlags.TypeExcludes; - 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.SpreadElement: - transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsRestOrSpread; - break; - - case SyntaxKind.SpreadAssignment: - transformFlags |= TransformFlags.AssertES2018 | TransformFlags.ContainsObjectRestOrSpread; - break; - - case SyntaxKind.SuperKeyword: - // This node is ES6 syntax. - transformFlags |= TransformFlags.AssertES2015; - excludeFlags = TransformFlags.OuterExpressionExcludes; // must be set to persist `Super` - break; - - case SyntaxKind.ThisKeyword: - // Mark this node and its ancestors as containing a lexical `this` keyword. - transformFlags |= TransformFlags.ContainsLexicalThis; - break; - - case SyntaxKind.ObjectBindingPattern: - transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsBindingPattern; - if (subtreeFlags & TransformFlags.ContainsRestOrSpread) { - transformFlags |= TransformFlags.AssertES2018 | TransformFlags.ContainsObjectRestOrSpread; - } - excludeFlags = TransformFlags.BindingPatternExcludes; - break; - - case SyntaxKind.ArrayBindingPattern: - transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsBindingPattern; - excludeFlags = TransformFlags.BindingPatternExcludes; - break; - - case SyntaxKind.BindingElement: - transformFlags |= TransformFlags.AssertES2015; - if ((node).dotDotDotToken) { - transformFlags |= TransformFlags.ContainsRestOrSpread; - } - break; - - case SyntaxKind.Decorator: - // This node is TypeScript syntax, and marks its container as also being TypeScript syntax. - transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsTypeScriptClassSyntax; - 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.AssertES2015; - } - - if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) { - // If an ObjectLiteralExpression contains a spread element, then it - // is an ES2018 node. - transformFlags |= TransformFlags.AssertES2018; - } - - break; - - case SyntaxKind.ArrayLiteralExpression: - excludeFlags = TransformFlags.ArrayLiteralOrCallOrNewExcludes; - break; - - case SyntaxKind.DoStatement: - case SyntaxKind.WhileStatement: - case SyntaxKind.ForStatement: - case SyntaxKind.ForInStatement: - // A loop containing a block scoped binding *may* need to be transformed from ES6. - if (subtreeFlags & TransformFlags.ContainsBlockScopedBinding) { - transformFlags |= TransformFlags.AssertES2015; - } - - break; - - case SyntaxKind.SourceFile: - break; - - case SyntaxKind.ReturnStatement: - // Return statements may require an `await` in ES2018. - transformFlags |= TransformFlags.ContainsHoistedDeclarationOrCompletion | TransformFlags.AssertES2018; - break; - - case SyntaxKind.ContinueStatement: - case SyntaxKind.BreakStatement: - transformFlags |= TransformFlags.ContainsHoistedDeclarationOrCompletion; - break; - } - - node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~excludeFlags; - } - - function propagatePropertyNameFlags(node: PropertyName, transformFlags: TransformFlags) { - return transformFlags | (node.transformFlags & TransformFlags.PropertyNamePropagatingFlags); - } - - /** - * Gets the transform flags to exclude when unioning the transform flags of a subtree. - * - * NOTE: This needs to be kept up-to-date with the exclusions used in `computeTransformFlagsForNode`. - * For performance reasons, `computeTransformFlagsForNode` uses local constant values rather - * than calling this function. - */ - export function getTransformFlagsSubtreeExclusions(kind: SyntaxKind) { - if (kind >= SyntaxKind.FirstTypeNode && kind <= SyntaxKind.LastTypeNode) { - return TransformFlags.TypeExcludes; - } - - switch (kind) { - case SyntaxKind.CallExpression: - case SyntaxKind.NewExpression: - case SyntaxKind.ArrayLiteralExpression: - return TransformFlags.ArrayLiteralOrCallOrNewExcludes; - case SyntaxKind.ModuleDeclaration: - return TransformFlags.ModuleExcludes; - case SyntaxKind.Parameter: - return TransformFlags.ParameterExcludes; - case SyntaxKind.ArrowFunction: - return TransformFlags.ArrowFunctionExcludes; - case SyntaxKind.FunctionExpression: - case SyntaxKind.FunctionDeclaration: - return TransformFlags.FunctionExcludes; - case SyntaxKind.VariableDeclarationList: - return TransformFlags.VariableDeclarationListExcludes; - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - return TransformFlags.ClassExcludes; - case SyntaxKind.Constructor: - return TransformFlags.ConstructorExcludes; - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - return TransformFlags.MethodOrAccessorExcludes; - case SyntaxKind.AnyKeyword: - case SyntaxKind.NumberKeyword: - case SyntaxKind.BigIntKeyword: - case SyntaxKind.NeverKeyword: - case SyntaxKind.StringKeyword: - case SyntaxKind.ObjectKeyword: - 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.InterfaceDeclaration: - case SyntaxKind.TypeAliasDeclaration: - return TransformFlags.TypeExcludes; - case SyntaxKind.ObjectLiteralExpression: - return TransformFlags.ObjectLiteralExcludes; - case SyntaxKind.CatchClause: - return TransformFlags.CatchClauseExcludes; - case SyntaxKind.ObjectBindingPattern: - case SyntaxKind.ArrayBindingPattern: - return TransformFlags.BindingPatternExcludes; - case SyntaxKind.TypeAssertionExpression: - case SyntaxKind.AsExpression: - case SyntaxKind.PartiallyEmittedExpression: - case SyntaxKind.ParenthesizedExpression: - case SyntaxKind.SuperKeyword: - return TransformFlags.OuterExpressionExcludes; - case SyntaxKind.PropertyAccessExpression: - case SyntaxKind.ElementAccessExpression: - return TransformFlags.PropertyAccessExcludes; - default: - return TransformFlags.NodeExcludes; - } - } - /** * "Binds" JSDoc nodes in TypeScript code. * Since we will never create symbols for JSDoc, we just set parent pointers instead. diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f8a10320244..d3b0ccd697f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3653,7 +3653,7 @@ namespace ts { } if (type.flags & TypeFlags.BooleanLiteral) { context.approximateLength += (type).intrinsicName.length; - return (type).intrinsicName === "true" ? factory.createTrue() : factory.createFalse(); + return factory.createLiteralTypeNode((type).intrinsicName === "true" ? factory.createTrue() : factory.createFalse()); } if (type.flags & TypeFlags.UniqueESSymbol) { if (!(context.flags & NodeBuilderFlags.AllowUniqueESSymbolType)) { @@ -3678,7 +3678,7 @@ namespace ts { } if (type.flags & TypeFlags.Null) { context.approximateLength += 4; - return factory.createKeywordTypeNode(SyntaxKind.NullKeyword); + return factory.createLiteralTypeNode(factory.createNull()); } if (type.flags & TypeFlags.Never) { context.approximateLength += 5; @@ -3702,7 +3702,7 @@ namespace ts { } } context.approximateLength += 4; - return factory.createThis(); + return factory.createThisTypeNode(); } const objectFlags = getObjectFlags(type); @@ -4310,11 +4310,11 @@ namespace ts { if (context.tracker.trackSymbol && isComputedPropertyName(node) && isLateBindableName(node)) { trackComputedName(node, context.enclosingDeclaration, context); } - const visited = visitEachChild(node, elideInitializerAndSetEmitFlags, nullTransformationContext, /*nodesVisitor*/ undefined, elideInitializerAndSetEmitFlags)!; - const clone = nodeIsSynthesized(visited) ? visited : getSynthesizedClone(visited); - if (clone.kind === SyntaxKind.BindingElement) { - (clone).initializer = undefined; + let visited = visitEachChild(node, elideInitializerAndSetEmitFlags, nullTransformationContext, /*nodesVisitor*/ undefined, elideInitializerAndSetEmitFlags)!; + if (isBindingElement(visited)) { + visited = factory.updateBindingElement(visited, visited.dotDotDotToken, visited.propertyName, visited.name, /*initializer*/ undefined); } + const clone = nodeIsSynthesized(visited) ? visited : getSynthesizedClone(visited); return setEmitFlags(clone, EmitFlags.SingleLine | EmitFlags.NoAsciiEscaping); } } @@ -6695,7 +6695,6 @@ namespace ts { case SyntaxKind.ObjectKeyword: case SyntaxKind.VoidKeyword: case SyntaxKind.UndefinedKeyword: - case SyntaxKind.NullKeyword: case SyntaxKind.NeverKeyword: case SyntaxKind.LiteralType: return true; @@ -11058,6 +11057,9 @@ namespace ts { } function getTypeFromLiteralTypeNode(node: LiteralTypeNode): Type { + if (node.literal.kind === SyntaxKind.NullKeyword) { + return nullType; + } const links = getNodeLinks(node); if (!links.resolvedType) { links.resolvedType = getRegularTypeOfLiteralType(checkExpression(node.literal)); @@ -11125,6 +11127,7 @@ namespace ts { case SyntaxKind.UndefinedKeyword: return undefinedType; case SyntaxKind.NullKeyword: + // Although `NullKeyword` is not currently a valid TypeNode, at one point it was so we return `nullType` here defensively. return nullType; case SyntaxKind.NeverKeyword: return neverType; @@ -11132,6 +11135,7 @@ namespace ts { return node.flags & NodeFlags.JavaScriptFile ? anyType : nonPrimitiveType; case SyntaxKind.ThisType: case SyntaxKind.ThisKeyword: + // Although `ThisKeyword` is not currently a valid TypeNode, at one point it was so we return the `this` type here defensively. return getTypeFromThisTypeNode(node as ThisExpression | ThisTypeNode); case SyntaxKind.LiteralType: return getTypeFromLiteralTypeNode(node); @@ -26904,7 +26908,7 @@ namespace ts { if (typeNode.kind === SyntaxKind.NeverKeyword) { continue; // Always elide `never` from the union/intersection if possible } - if (!strictNullChecks && (typeNode.kind === SyntaxKind.NullKeyword || typeNode.kind === SyntaxKind.UndefinedKeyword)) { + if (!strictNullChecks && (typeNode.kind === SyntaxKind.LiteralType && (typeNode as LiteralTypeNode).literal.kind === SyntaxKind.NullKeyword || typeNode.kind === SyntaxKind.UndefinedKeyword)) { continue; // Elide null and undefined from unions for metadata, just like what we did prior to the implementation of strict null checks } const individualEntityName = getEntityNameForDecoratorMetadata(typeNode); diff --git a/src/compiler/debug.ts b/src/compiler/debug.ts index 70ae9558082..b8e9869d6bf 100644 --- a/src/compiler/debug.ts +++ b/src/compiler/debug.ts @@ -89,17 +89,18 @@ namespace ts { /** * Formats an enum value as a string for debugging and debug assertions. */ - export function formatEnum(value = 0, enumObject: any, isFlags?: boolean) { + export function formatEnum(value = 0, enumObject: any, isFlags?: boolean, filterEnum?: (enumValue: number, enumName: string) => boolean) { const members = getEnumMembers(enumObject); if (value === 0) { - return members.length > 0 && members[0][0] === 0 ? members[0][1] : "0"; + const zeroMembers = members.filter(member => member[0] === 0); + return zeroMembers.length > 0 ? zeroMembers[0][1] : "0"; } if (isFlags) { let result = ""; let remainingFlags = value; for (let i = members.length - 1; i >= 0 && remainingFlags !== 0; i--) { const [enumValue, enumName] = members[i]; - if (enumValue !== 0 && (remainingFlags & enumValue) === enumValue) { + if ((!filterEnum || filterEnum(enumValue, enumName)) && enumValue !== 0 && (remainingFlags & enumValue) === enumValue) { remainingFlags &= ~enumValue; result = `${enumName}${result ? "|" : ""}${result}`; } @@ -143,7 +144,7 @@ namespace ts { } export function formatTransformFlags(flags: TransformFlags | undefined): string { - return formatEnum(flags, (ts).TransformFlags, /*isFlags*/ true); + return formatEnum(flags, (ts).TransformFlags, /*isFlags*/ true, (_, name) => !/^Assert|Excludes$|PropagatingFlags$/.test(name)); } export function formatEmitFlags(flags: EmitFlags | undefined): string { diff --git a/src/compiler/factory/converters.ts b/src/compiler/factory/converters.ts index 110638e0bc4..7697117895f 100644 --- a/src/compiler/factory/converters.ts +++ b/src/compiler/factory/converters.ts @@ -18,7 +18,6 @@ namespace ts { setTextRange(returnStatement, node); const body = factory.createBlock([returnStatement], multiLine); setTextRange(body, node); - aggregateTransformFlags(body); return body; } @@ -38,7 +37,6 @@ namespace ts { if (getStartsOnNewLine(node)) { setStartsOnNewLine(updated, /*newLine*/ true); } - aggregateTransformFlags(updated); return updated; } diff --git a/src/compiler/factory/factory.ts b/src/compiler/factory/factory.ts index 9f0e79c7032..52fbb9c72c7 100644 --- a/src/compiler/factory/factory.ts +++ b/src/compiler/factory/factory.ts @@ -3,8 +3,16 @@ namespace ts { /* @internal */ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNodeFactory, treeStateObserver?: TreeStateObserver): NodeFactory { + // TODO(rbuckton): This exists to avoid collectiong transform flags when parsing a declaration file or JSDoc comment. + // This is so that we can avoid executing code for those cases to improve performance. We should measure + // this and determine if it worthwhile to keep this optimization. + let skipTransformationFlags = false; + + // Defer loading the parenthesizer and node converters until they are requestd const parenthesizer = memoize(() => flags & NodeFactoryFlags.NoParenthesizerRules ? nullParenthesizerRules : createParenthesizerRules(factory)); const getConverters = memoize(() => flags & NodeFactoryFlags.NoNodeConverters ? nullNodeConverters : createNodeConverters(factory)); + + // Add tree state observers to base methods. const createNode = observeResult(baseFactory.createBaseNode, treeStateObserver && treeStateObserver.onCreateNode); const createTokenNode = observeResult(baseFactory.createBaseTokenNode, treeStateObserver && treeStateObserver.onCreateNode); const createIdentifierNode = observeResult(baseFactory.createBaseIdentifierNode, treeStateObserver && treeStateObserver.onCreateNode); @@ -12,11 +20,20 @@ namespace ts { const setChild = observeArguments(setChildWorker, treeStateObserver && treeStateObserver.onSetChild); const setChildren = observeArguments(setChildrenWorker, treeStateObserver && treeStateObserver.onSetChildren); const finish = observeResult(finishWorker, treeStateObserver && treeStateObserver.onFinishNode); + const finishJSDoc = observeResult(identity, treeStateObserver && treeStateObserver.onFinishNode); const update = observeArguments(updateWorker, treeStateObserver && treeStateObserver.onUpdateNode); const reuse = observeResult(reuseWorker, treeStateObserver && treeStateObserver.onReuseNode); + const factory: NodeFactory = { getParenthesizerRules: parenthesizer, getConverters, + setSkipTransformationFlags: value => { + const oldValue = skipTransformationFlags; + skipTransformationFlags = value; + return oldValue; + }, + trackExtraneousChildNode: setChild, + trackExtraneousChildNodes: setChildren, createNodeArray, createNumericLiteral, createBigIntLiteral, @@ -318,7 +335,7 @@ namespace ts { createJSDocCallbackTag, createJSDocClassTag, createJSDocEnumTag, - createJSDocTag: tagName => createJSDocTag(SyntaxKind.JSDocTag, tagName), + createJSDocTag: createJSDocUnknownTag, createJSDocComment, createJsxElement, updateJsxElement, @@ -369,6 +386,8 @@ namespace ts { updateCommaList: updateCommaListExpression, createBundle, updateBundle, + + // TODO(rbuckton): Move these to a different object or possibly remove them? createImmediatelyInvokedFunctionExpression, createImmediatelyInvokedArrowFunction, createComma, @@ -417,10 +436,16 @@ namespace ts { // @api function createNodeArray(elements?: readonly T[], hasTrailingComma?: boolean): NodeArray { - if (!elements || elements === emptyArray) { + if (elements === undefined || elements === emptyArray) { elements = []; } else if (isNodeArray(elements)) { + if (!skipTransformationFlags) { + // Ensure the transform flags have been aggregated for this NodeArray + if (elements.transformFlags === undefined) { + aggregateChildrenFlags(elements); + } + } return elements; } @@ -431,7 +456,12 @@ namespace ts { const array = >(length >= 1 && length <= 4 ? elements.slice() : elements); array.pos = -1; array.end = -1; - array.hasTrailingComma = hasTrailingComma; + if (hasTrailingComma) { + array.hasTrailingComma = hasTrailingComma; + } + if (!skipTransformationFlags) { + aggregateChildrenFlags(array); + } return array; } @@ -479,6 +509,11 @@ namespace ts { name ); setChildren(node, node.typeParameters = asNodeArray(typeParameters)); + if (!skipTransformationFlags) { + if (typeParameters) { + markTypeScript(node); + } + } return node; } @@ -500,6 +535,9 @@ namespace ts { ); setChildren(node, node.parameters = createNodeArray(parameters)); setChild(node, node.type = type); + if (!skipTransformationFlags) { + if (type) markTypeScript(node); + } return node; } @@ -528,6 +566,9 @@ namespace ts { type ); setChild(node, node.body = body); + if (!skipTransformationFlags) { + if (!body) markTypeScript(node); + } return node; } @@ -610,6 +651,9 @@ namespace ts { initializer ); setChild(node, node.type = type); + if (!skipTransformationFlags) { + if (type) markTypeScript(node); + } return node; } @@ -632,12 +676,18 @@ namespace ts { function createNumericLiteral(value: string | number, numericLiteralFlags: TokenFlags = TokenFlags.None): NumericLiteral { const node = createBaseLiteral(SyntaxKind.NumericLiteral, typeof value === "number" ? value + "" : value); node.numericLiteralFlags = numericLiteralFlags; + if (!skipTransformationFlags) { + if (numericLiteralFlags & TokenFlags.BinaryOrOctalSpecifier) markES2015(node); + } return finish(node); } // @api function createBigIntLiteral(value: string | PseudoBigInt): BigIntLiteral { const node = createBaseLiteral(SyntaxKind.BigIntLiteral, typeof value === "string" ? value : pseudoBigIntToString(value) + "n"); + if (!skipTransformationFlags) { + markESNext(node); + } return finish(node); } @@ -651,6 +701,9 @@ namespace ts { function createStringLiteral(text: string, isSingleQuote?: boolean, hasExtendedUnicodeEscape?: boolean): StringLiteral { const node = createBaseStringLiteral(text, isSingleQuote); node.hasExtendedUnicodeEscape = hasExtendedUnicodeEscape; + if (!skipTransformationFlags) { + if (hasExtendedUnicodeEscape) markES2015(node); + } return finish(node); } @@ -684,7 +737,7 @@ namespace ts { // Identifiers // - function createBaseIdentifier(text: string, typeArguments?: readonly (TypeNode | TypeParameterDeclaration)[], originalKeywordKind?: SyntaxKind): Identifier { + function createBaseIdentifier(text: string, originalKeywordKind: SyntaxKind | undefined): Identifier { if (originalKeywordKind === undefined && text) { originalKeywordKind = stringToToken(text); } @@ -694,14 +747,11 @@ namespace ts { const node = createIdentifierNode(SyntaxKind.Identifier) as Identifier; node.originalKeywordKind = originalKeywordKind; node.escapedText = escapeLeadingUnderscores(text); - if (typeArguments) { - node.typeArguments = createNodeArray(typeArguments); - } return node; } function createBaseGeneratedIdentifier(text: string, autoGenerateFlags: GeneratedIdentifierFlags) { - const node = createBaseIdentifier(text) as GeneratedIdentifier; + const node = createBaseIdentifier(text, /*originalKeywordKind*/ undefined) as GeneratedIdentifier; node.autoGenerateFlags = autoGenerateFlags; node.autoGenerateId = nextAutoGenerateId; nextAutoGenerateId++; @@ -710,7 +760,14 @@ namespace ts { // @api function createIdentifier(text: string, typeArguments?: readonly (TypeNode | TypeParameterDeclaration)[], originalKeywordKind?: SyntaxKind): Identifier { - const node = createBaseIdentifier(text, typeArguments, originalKeywordKind); + const node = createBaseIdentifier(text, originalKeywordKind); + if (typeArguments) { + // NOTE: we do not use `setChildren` here because typeArguments in an identifier do not contribute to transformations + node.typeArguments = createNodeArray(typeArguments); + if (treeStateObserver && treeStateObserver.onSetChildren) { + treeStateObserver.onSetChildren(node, node.typeArguments); + } + } return finish(node); } @@ -773,7 +830,46 @@ namespace ts { Debug.assert(token <= SyntaxKind.FirstTemplateToken || token >= SyntaxKind.LastTemplateToken, "Invalid token. Use 'createTemplateLiteralLikeNode' to create template literals."); Debug.assert(token <= SyntaxKind.FirstLiteralToken || token >= SyntaxKind.LastLiteralToken, "Invalid token. Use 'createLiteralLikeNode' to create literals."); Debug.assert(token !== SyntaxKind.Identifier, "Invalid token. Use 'createIdentifier' to create identifiers"); - return finish(createTokenNode(token) as Token); + const node = createTokenNode(token) as Token; + if (!skipTransformationFlags) { + switch (token) { + case SyntaxKind.AsyncKeyword: + // 'async' modifier is ES2017 (async functions) or ES2018 (async generators) + markES2017(node); + markES2018(node); + break; + + case SyntaxKind.PublicKeyword: + case SyntaxKind.PrivateKeyword: + case SyntaxKind.ProtectedKeyword: + case SyntaxKind.ReadonlyKeyword: + case SyntaxKind.AbstractKeyword: + case SyntaxKind.DeclareKeyword: + case SyntaxKind.ConstKeyword: + case SyntaxKind.AnyKeyword: + case SyntaxKind.NumberKeyword: + case SyntaxKind.BigIntKeyword: + case SyntaxKind.NeverKeyword: + case SyntaxKind.ObjectKeyword: + case SyntaxKind.StringKeyword: + case SyntaxKind.BooleanKeyword: + case SyntaxKind.SymbolKeyword: + case SyntaxKind.VoidKeyword: + case SyntaxKind.UnknownKeyword: + case SyntaxKind.UndefinedKeyword: // `undefined` is an Identifier in the expression case. + markTypeScript(node); + break; + case SyntaxKind.StaticKeyword: + case SyntaxKind.SuperKeyword: + markES2015(node); + break; + case SyntaxKind.ThisKeyword: + // 'this' indicates a lexical 'this' + markLexicalThis(node); + break; + } + } + return finish(node); } // @@ -855,6 +951,10 @@ namespace ts { function createComputedPropertyName(expression: Expression) { const node = createBaseNode(SyntaxKind.ComputedPropertyName); setChild(node, node.expression = parenthesizer().parenthesizeExpressionOfComputedPropertyName(expression)); + if (!skipTransformationFlags) { + markES2015(node); + markComputedPropertyName(node); + } return finish(node); } @@ -879,6 +979,9 @@ namespace ts { ); setChild(node, node.constraint = constraint); setChild(node, node.default = defaultType); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -911,6 +1014,16 @@ namespace ts { ); setChild(node, node.dotDotDotToken = dotDotDotToken); setChild(node, node.questionToken = questionToken); + if (!skipTransformationFlags) { + if (isThisIdentifier(node.name)) { + markTypeScriptOnly(node); + } + else { + if (questionToken) markTypeScript(node); + if (hasModifier(node, ModifierFlags.ParameterPropertyModifier)) markTypeScriptClassSyntax(node); + if (initializer || dotDotDotToken) markES2015(node); + } + } return finish(node); } @@ -940,6 +1053,10 @@ namespace ts { function createDecorator(expression: Expression) { const node = createBaseNode(SyntaxKind.Decorator); setChild(node, node.expression = parenthesizer().parenthesizeLeftSideOfAccess(expression)); + if (!skipTransformationFlags) { + markTypeScript(node); + markTypeScriptClassSyntax(node); + } return finish(node); } @@ -969,6 +1086,9 @@ namespace ts { ); setChild(node, node.type = type); setChild(node, node.questionToken = questionToken); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1007,6 +1127,10 @@ namespace ts { ); setChild(node, node.questionToken = questionOrExclamationToken !== undefined && questionOrExclamationToken.kind === SyntaxKind.QuestionToken ? questionOrExclamationToken : undefined); setChild(node, node.exclamationToken = questionOrExclamationToken !== undefined && questionOrExclamationToken.kind === SyntaxKind.ExclamationToken ? questionOrExclamationToken : undefined); + if (!skipTransformationFlags) { + if (isComputedPropertyName(node.name) || (hasStaticModifier(node) && node.initializer)) markTypeScriptClassSyntax(node); + markClassFields(node); + } return finish(node); } @@ -1050,6 +1174,9 @@ namespace ts { type ); setChild(node, node.questionToken = questionToken); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1097,6 +1224,20 @@ namespace ts { ); setChild(node, node.asteriskToken = asteriskToken); setChild(node, node.questionToken = questionToken); + if (!skipTransformationFlags) { + if (hasModifier(node, ModifierFlags.Async)) { + if (asteriskToken) { + markES2018(node); + } + else { + markES2017(node); + } + } + else if (asteriskToken) { + markGenerator(node); + } + markES2015(node); + } return finish(node); } @@ -1143,6 +1284,9 @@ namespace ts { /*type*/ undefined, body ); + if (!skipTransformationFlags) { + markES2015(node); + } return finish(node); } @@ -1258,6 +1402,9 @@ namespace ts { parameters, type ); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1290,6 +1437,9 @@ namespace ts { parameters, type ); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1367,6 +1517,9 @@ namespace ts { parameters, type ); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1400,6 +1553,9 @@ namespace ts { const node = createBaseNode(SyntaxKind.TypePredicate); setChild(node, node.parameterName = asName(parameterName)); setChild(node, node.type = type); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1416,6 +1572,9 @@ namespace ts { const node = createBaseNode(SyntaxKind.TypeReference); setChild(node, node.typeName = asName(typeName)); setChildren(node, node.typeArguments = typeArguments && parenthesizer().parenthesizeTypeArguments(createNodeArray(typeArguments))); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1442,6 +1601,9 @@ namespace ts { parameters, type ); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1474,6 +1636,9 @@ namespace ts { parameters, type ); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1495,6 +1660,9 @@ namespace ts { function createTypeQueryNode(exprName: EntityName) { const node = createBaseNode(SyntaxKind.TypeQuery); setChild(node, node.exprName = exprName); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1509,6 +1677,9 @@ namespace ts { function createTypeLiteralNode(members: readonly TypeElement[] | undefined) { const node = createBaseNode(SyntaxKind.TypeLiteral); setChildren(node, node.members = createNodeArray(members)); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1523,6 +1694,9 @@ namespace ts { function createArrayTypeNode(elementType: TypeNode) { const node = createBaseNode(SyntaxKind.ArrayType); setChild(node, node.elementType = parenthesizer().parenthesizeElementTypeOfArrayType(elementType)); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1537,6 +1711,9 @@ namespace ts { function createTupleTypeNode(elementTypes: readonly TypeNode[]) { const node = createBaseNode(SyntaxKind.TupleType); setChildren(node, node.elementTypes = createNodeArray(elementTypes)); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1551,6 +1728,9 @@ namespace ts { function createOptionalTypeNode(type: TypeNode) { const node = createBaseNode(SyntaxKind.OptionalType); setChild(node, node.type = parenthesizer().parenthesizeElementTypeOfArrayType(type)); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1565,6 +1745,9 @@ namespace ts { function createRestTypeNode(type: TypeNode) { const node = createBaseNode(SyntaxKind.RestType); setChild(node, node.type = type); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1578,6 +1761,9 @@ namespace ts { function createUnionOrIntersectionTypeNode(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, types: readonly TypeNode[]) { const node = createBaseNode(kind); setChildren(node, node.types = parenthesizer().parenthesizeConstituentTypesOfUnionOrIntersectionType(createNodeArray(types))); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1614,6 +1800,9 @@ namespace ts { setChild(node, node.extendsType = parenthesizer().parenthesizeMemberOfConditionalType(extendsType)); setChild(node, node.trueType = trueType); setChild(node, node.falseType = falseType); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1631,6 +1820,9 @@ namespace ts { function createInferTypeNode(typeParameter: TypeParameterDeclaration) { const node = createBaseNode(SyntaxKind.InferType); setChild(node, node.typeParameter = typeParameter); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1648,6 +1840,9 @@ namespace ts { setChild(node, node.qualifier = qualifier); setChildren(node, node.typeArguments = typeArguments && parenthesizer().parenthesizeTypeArguments(createNodeArray(typeArguments))); node.isTypeOf = isTypeOf; + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1665,6 +1860,9 @@ namespace ts { function createParenthesizedType(type: TypeNode) { const node = createBaseNode(SyntaxKind.ParenthesizedType); setChild(node, node.type = type); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1677,7 +1875,11 @@ namespace ts { // @api function createThisTypeNode() { - return finish(createBaseNode(SyntaxKind.ThisType)); + const node = createBaseNode(SyntaxKind.ThisType); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } + return finish(node); } // @api @@ -1685,6 +1887,9 @@ namespace ts { const node = createBaseNode(SyntaxKind.TypeOperator); node.operator = operator; setChild(node, node.type = parenthesizer().parenthesizeMemberOfElementType(type)); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1700,6 +1905,9 @@ namespace ts { const node = createBaseNode(SyntaxKind.IndexedAccessType); setChild(node, node.objectType = parenthesizer().parenthesizeMemberOfElementType(objectType)); setChild(node, node.indexType = indexType); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1718,6 +1926,9 @@ namespace ts { setChild(node, node.typeParameter = typeParameter); setChild(node, node.questionToken = questionToken); setChild(node, node.type = type); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1735,6 +1946,9 @@ namespace ts { function createLiteralTypeNode(literal: LiteralTypeNode["literal"]) { const node = createBaseNode(SyntaxKind.LiteralType); setChild(node, node.literal = literal); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -1753,6 +1967,14 @@ namespace ts { function createObjectBindingPattern(elements: readonly BindingElement[]) { const node = createBaseNode(SyntaxKind.ObjectBindingPattern); setChildren(node, node.elements = createNodeArray(elements)); + if (!skipTransformationFlags) { + markES2015(node); + markBindingPattern(node); + if (node.transformFlags & TransformFlags.ContainsRestOrSpread) { + markES2018(node); + markObjectRestOrSpread(node); + } + } return finish(node); } @@ -1767,6 +1989,10 @@ namespace ts { function createArrayBindingPattern(elements: readonly ArrayBindingElement[]) { const node = createBaseNode(SyntaxKind.ArrayBindingPattern); setChildren(node, node.elements = createNodeArray(elements)); + if (!skipTransformationFlags) { + markES2015(node); + markBindingPattern(node); + } return finish(node); } @@ -1779,15 +2005,19 @@ namespace ts { // @api function createBindingElement(dotDotDotToken: DotDotDotToken | undefined, propertyName: string | PropertyName | undefined, name: string | BindingName, initializer?: Expression) { - const node = createBaseNamedDeclaration( + const node = createBaseBindingLikeDeclaration( SyntaxKind.BindingElement, /*decorators*/ undefined, /*modifiers*/ undefined, - name + name, + initializer ); setChild(node, node.propertyName = asName(propertyName)); - setChild(node, node.initializer = initializer); setChild(node, node.dotDotDotToken = dotDotDotToken); + if (!skipTransformationFlags) { + if (dotDotDotToken) markRestOrSpread(node); + markES2015(node); + } return finish(node); } @@ -1840,6 +2070,14 @@ namespace ts { const node = createBaseNode(SyntaxKind.PropertyAccessExpression); setChild(node, node.expression = parenthesizer().parenthesizeLeftSideOfAccess(expression)); setChild(node, node.name = asName(name)); + if (!skipTransformationFlags) { + // super method calls require a lexical 'this' + // super method calls require 'super' hoisting in ES2017 and ES2018 async functions and async generators + if (expression.kind === SyntaxKind.SuperKeyword) { + markES2017(node); + markES2018(node); + } + } return finish(node); } @@ -1856,6 +2094,14 @@ namespace ts { const node = createBaseNode(SyntaxKind.ElementAccessExpression); setChild(node, node.expression = parenthesizer().parenthesizeLeftSideOfAccess(expression)); setChild(node, node.argumentExpression = asExpression(index)); + if (!skipTransformationFlags) { + // super method calls require a lexical 'this' + // super method calls require 'super' hoisting in ES2017 and ES2018 async functions and async generators + if (expression.kind === SyntaxKind.SuperKeyword) { + markES2017(node); + markES2018(node); + } + } return finish(node); } @@ -1873,6 +2119,17 @@ namespace ts { setChild(node, node.expression = parenthesizer().parenthesizeLeftSideOfAccess(expression)); setChildren(node, node.typeArguments = asNodeArray(typeArguments)); setChildren(node, node.arguments = parenthesizer().parenthesizeExpressionsOfCommaDelimitedList(createNodeArray(argumentsArray))); + if (!skipTransformationFlags) { + if (typeArguments) { + markTypeScript(node); + } + if (expression.kind === SyntaxKind.ImportKeyword) { + markDynamicImport(node); + } + if (isSuperProperty(expression)) { + markLexicalThis(node); + } + } return finish(node); } @@ -1891,6 +2148,11 @@ namespace ts { setChild(node, node.expression = parenthesizer().parenthesizeExpressionOfNew(expression)); setChildren(node, node.typeArguments = asNodeArray(typeArguments)); setChildren(node, node.arguments = argumentsArray ? parenthesizer().parenthesizeExpressionsOfCommaDelimitedList(createNodeArray(argumentsArray)) : undefined); + if (!skipTransformationFlags) { + if (typeArguments) { + markTypeScript(node); + } + } return finish(node); } @@ -1909,6 +2171,12 @@ namespace ts { setChild(node, node.tag = parenthesizer().parenthesizeLeftSideOfAccess(tag)); setChildren(node, node.typeArguments = asNodeArray(typeArguments)); setChild(node, node.template = template); + if (!skipTransformationFlags) { + markES2015(node); + if (typeArguments) { + markTypeScript(node); + } + } return finish(node); } @@ -1926,6 +2194,9 @@ namespace ts { const node = createBaseNode(SyntaxKind.TypeAssertionExpression); setChild(node, node.expression = parenthesizer().parenthesizeOperandOfPrefixUnary(expression)); setChild(node, node.type = type); + if (!skipTransformationFlags) { + markTypeScript(node); + } return finish(node); } @@ -1969,8 +2240,25 @@ namespace ts { typeParameters, parameters, type, - body); + body + ); setChild(node, node.asteriskToken = asteriskToken); + if (!skipTransformationFlags) { + if (typeParameters) { + markTypeScript(node); + } + if (hasModifier(node, ModifierFlags.Async)) { + if (asteriskToken) { + markES2018(node); + } + else { + markES2017(node); + } + } + else if (asteriskToken) { + markGenerator(node); + } + } return finish(node); } @@ -2016,6 +2304,10 @@ namespace ts { parenthesizer().parenthesizeConciseBodyOfArrowFunction(body) ); setChild(node, node.equalsGreaterThanToken = equalsGreaterThanToken || createToken(SyntaxKind.EqualsGreaterThanToken)); + if (!skipTransformationFlags) { + if (hasModifier(node, ModifierFlags.Async)) markES2017(node); + markES2015(node); + } return finish(node); } @@ -2106,6 +2398,10 @@ namespace ts { function createAwaitExpression(expression: Expression) { const node = createBaseNode(SyntaxKind.AwaitExpression); setChild(node, node.expression = parenthesizer().parenthesizeOperandOfPrefixUnary(expression)); + if (!skipTransformationFlags) { + markES2017(node); + markES2018(node); + } return finish(node); } @@ -2154,6 +2450,22 @@ namespace ts { setChild(node, node.left = parenthesizer().parenthesizeLeftSideOfBinary(operatorKind, left)); setChild(node, node.operatorToken = operatorToken); setChild(node, node.right = parenthesizer().parenthesizeRightSideOfBinary(operatorKind, node.left, right)); + if (!skipTransformationFlags) { + if (operatorKind === SyntaxKind.EqualsToken) { + if (node.left.kind === SyntaxKind.ObjectLiteralExpression) { + markES2015(node); + markES2018(node); + markDestructuringAssignment(node); + } + else if (node.left.kind === SyntaxKind.ArrayLiteralExpression) { + markES2015(node); + markDestructuringAssignment(node); + } + } + else if (operatorKind === SyntaxKind.AsteriskAsteriskToken || operatorKind === SyntaxKind.AsteriskAsteriskEqualsToken) { + markES2016(node); + } + } return finish(node); } @@ -2212,6 +2524,9 @@ namespace ts { const node = createBaseNode(SyntaxKind.TemplateExpression); setChild(node, node.head = head); setChildren(node, node.templateSpans = createNodeArray(templateSpans)); + if (!skipTransformationFlags) { + markES2015(node); + } return finish(node); } @@ -2249,6 +2564,9 @@ namespace ts { const node = createTokenNode(kind) as TemplateLiteralLikeNode; node.text = text; node.rawText = rawText; + if (!skipTransformationFlags) { + markES2015(node); + } return finish(node); } @@ -2278,6 +2596,11 @@ namespace ts { const node = createBaseNode(SyntaxKind.YieldExpression); setChild(node, node.expression = expression); setChild(node, node.asteriskToken = asteriskToken); + if (!skipTransformationFlags) { + markES2015(node); + markES2018(node); + markYield(node); + } return finish(node); } @@ -2293,6 +2616,10 @@ namespace ts { function createSpreadElement(expression: Expression) { const node = createBaseNode(SyntaxKind.SpreadElement); setChild(node, node.expression = parenthesizer().parenthesizeExpressionForDisallowedComma(expression)); + if (!skipTransformationFlags) { + markES2015(node); + markRestOrSpread(node); + } return finish(node); } @@ -2321,6 +2648,9 @@ namespace ts { heritageClauses, members ); + if (!skipTransformationFlags) { + markES2015(node); + } return finish(node); } @@ -2354,6 +2684,9 @@ namespace ts { const node = createBaseNode(SyntaxKind.ExpressionWithTypeArguments); setChild(node, node.expression = parenthesizer().parenthesizeLeftSideOfAccess(expression)); setChildren(node, node.typeArguments = typeArguments && parenthesizer().parenthesizeTypeArguments(typeArguments)); + if (!skipTransformationFlags) { + markES2015(node); + } return finish(node); } @@ -2370,6 +2703,9 @@ namespace ts { const node = createBaseNode(SyntaxKind.AsExpression); setChild(node, node.expression = expression); setChild(node, node.type = type); + if (!skipTransformationFlags) { + markTypeScript(node); + } return finish(node); } @@ -2385,6 +2721,9 @@ namespace ts { function createNonNullExpression(expression: Expression) { const node = createBaseNode(SyntaxKind.NonNullExpression); setChild(node, node.expression = parenthesizer().parenthesizeLeftSideOfAccess(expression)); + if (!skipTransformationFlags) { + markTypeScript(node); + } return finish(node); } @@ -2400,6 +2739,18 @@ namespace ts { const node = createBaseNode(SyntaxKind.MetaProperty); node.keywordToken = keywordToken; setChild(node, node.name = name); + if (!skipTransformationFlags) { + switch (keywordToken) { + case SyntaxKind.NewKeyword: + markES2015(node); + break; + case SyntaxKind.ImportKeyword: + markESNext(node); + break; + default: + return Debug.assertNever(keywordToken); + } + } return finish(node); } @@ -2419,6 +2770,9 @@ namespace ts { const node = createBaseNode(SyntaxKind.TemplateSpan); setChild(node, node.expression = expression); setChild(node, node.literal = literal); + if (!skipTransformationFlags) { + markES2015(node); + } return finish(node); } @@ -2432,7 +2786,9 @@ namespace ts { // @api function createSemicolonClassElement() { - return finish(createBaseNode(SyntaxKind.SemicolonClassElement)); + const node = createBaseNode(SyntaxKind.SemicolonClassElement); + markES2015(node); + return finish(node); } // @@ -2458,6 +2814,11 @@ namespace ts { function createVariableStatement(modifiers: readonly Modifier[] | undefined, declarationList: VariableDeclarationList | readonly VariableDeclaration[]) { const node = createBaseDeclaration(SyntaxKind.VariableStatement, /*decorators*/ undefined, modifiers); setChild(node, node.declarationList = isArray(declarationList) ? createVariableDeclarationList(declarationList) : declarationList); + if (!skipTransformationFlags) { + if (hasModifier(node, ModifierFlags.Ambient)) { + markTypeScriptOnly(node); + } + } return finish(node); } @@ -2583,6 +2944,10 @@ namespace ts { setChild(node, node.initializer = initializer); setChild(node, node.expression = expression); setChild(node, node.statement = asEmbeddedStatement(statement)); + if (!skipTransformationFlags) { + if (awaitModifier) markES2018(node); + markES2015(node); + } return finish(node); } @@ -2600,6 +2965,9 @@ namespace ts { function createContinueStatement(label?: string | Identifier): ContinueStatement { const node = createBaseNode(SyntaxKind.ContinueStatement); setChild(node, node.label = asName(label)); + if (!skipTransformationFlags) { + markHoistedDeclarationOrCompletion(node); + } return finish(node); } @@ -2614,6 +2982,9 @@ namespace ts { function createBreakStatement(label?: string | Identifier): BreakStatement { const node = createBaseNode(SyntaxKind.BreakStatement); setChild(node, node.label = asName(label)); + if (!skipTransformationFlags) { + markHoistedDeclarationOrCompletion(node); + } return finish(node); } @@ -2628,6 +2999,11 @@ namespace ts { function createReturnStatement(expression?: Expression): ReturnStatement { const node = createBaseNode(SyntaxKind.ReturnStatement); setChild(node, node.expression = expression); + if (!skipTransformationFlags) { + // return in an ES2018 async generator must be awaited + markES2018(node); + markHoistedDeclarationOrCompletion(node); + } return finish(node); } @@ -2760,6 +3136,13 @@ namespace ts { const node = createBaseNode(SyntaxKind.VariableDeclarationList); node.flags |= flags & NodeFlags.BlockScoped; setChildren(node, node.declarations = createNodeArray(declarations)); + if (!skipTransformationFlags) { + markHoistedDeclarationOrCompletion(node); + if (flags & NodeFlags.BlockScoped) { + markES2015(node); + markBlockScopedBinding(node); + } + } return finish(node); } @@ -2792,6 +3175,25 @@ namespace ts { body ); setChild(node, node.asteriskToken = asteriskToken); + if (!skipTransformationFlags) { + if (!body || hasModifier(node, ModifierFlags.Ambient)) { + markTypeScriptOnly(node); + } + else { + markHoistedDeclarationOrCompletion(node); + if (hasModifier(node, ModifierFlags.Async)) { + if (asteriskToken) { + markES2018(node); + } + else { + markES2017(node); + } + } + else if (asteriskToken) { + markGenerator(node); + } + } + } return finish(node); } @@ -2837,6 +3239,17 @@ namespace ts { heritageClauses, members ); + if (!skipTransformationFlags) { + if (hasModifier(node, ModifierFlags.Ambient)) { + markTypeScriptOnly(node); + } + else { + markES2015(node); + if (node.transformFlags & TransformFlags.ContainsTypeScriptClassSyntax) { + markTypeScript(node); + } + } + } return finish(node); } @@ -2878,6 +3291,9 @@ namespace ts { heritageClauses ); setChildren(node, node.members = createNodeArray(members)); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -2917,6 +3333,9 @@ namespace ts { typeParameters ); setChild(node, node.type = type); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -2952,6 +3371,9 @@ namespace ts { name ); setChildren(node, node.members = createNodeArray(members)); + if (!skipTransformationFlags) { + markTypeScript(node); + } return finish(node); } @@ -2986,6 +3408,14 @@ namespace ts { node.flags |= flags & (NodeFlags.Namespace | NodeFlags.NestedNamespace | NodeFlags.GlobalAugmentation); setChild(node, node.name = name); setChild(node, node.body = body); + if (!skipTransformationFlags) { + if (hasModifier(node, ModifierFlags.Ambient)) { + markTypeScriptOnly(node); + } + else { + markTypeScript(node); + } + } return finish(node); } @@ -3037,6 +3467,9 @@ namespace ts { function createNamespaceExportDeclaration(name: string | Identifier) { const node = createBaseNode(SyntaxKind.NamespaceExportDeclaration); setChild(node, node.name = asName(name)); + if (!skipTransformationFlags) { + markTypeScriptOnly(node); + } return finish(node); } @@ -3061,6 +3494,9 @@ namespace ts { name ); setChild(node, node.moduleReference = moduleReference); + if (!skipTransformationFlags) { + if (moduleReference.kind !== SyntaxKind.ExternalModuleReference) markTypeScript(node); + } return finish(node); } @@ -3303,33 +3739,35 @@ namespace ts { // @api function createJSDocAllType() { - return finish(createBaseNode(SyntaxKind.JSDocAllType)); + const node = createBaseNode(SyntaxKind.JSDocAllType); + return finishJSDoc(node); } // @api function createJSDocUnknownType() { - return finish(createBaseNode(SyntaxKind.JSDocUnknownType)); + const node = createBaseNode(SyntaxKind.JSDocUnknownType); + return finishJSDoc(node); } // @api function createJSDocNonNullableType(type: TypeNode) { const node = createBaseNode(SyntaxKind.JSDocNonNullableType); setChild(node, node.type = type); - return finish(node); + return finishJSDoc(node); } // @api function createJSDocNullableType(type: TypeNode) { const node = createBaseNode(SyntaxKind.JSDocNullableType); setChild(node, node.type = type); - return finish(node); + return finishJSDoc(node); } // @api function createJSDocOptionalType(type: TypeNode) { const node = createBaseNode(SyntaxKind.JSDocOptionalType); setChild(node, node.type = type); - return finish(node); + return finishJSDoc(node); } // @api @@ -3343,21 +3781,21 @@ namespace ts { parameters, type ); - return finish(node); + return finishJSDoc(node); } // @api function createJSDocVariadicType(type: TypeNode) { const node = createBaseNode(SyntaxKind.JSDocVariadicType); setChild(node, node.type = type); - return finish(node); + return finishJSDoc(node); } // @api function createJSDocNamepathType(type: TypeNode) { const node = createBaseNode(SyntaxKind.JSDocNamepathType); setChild(node, node.type = type); - return finish(node); + return finishJSDoc(node); } // @api @@ -3365,14 +3803,14 @@ namespace ts { const node = createBaseNode(SyntaxKind.JSDocTypeLiteral); setChildren(node, node.jsDocPropertyTags = asNodeArray(propertyTags)); node.isArrayType = isArrayType; - return finish(node); + return finishJSDoc(node); } // @api function createJSDocTypeExpression(type: TypeNode): JSDocTypeExpression { const node = createBaseNode(SyntaxKind.JSDocTypeExpression); setChild(node, node.type = type); - return finish(node); + return finishJSDoc(node); } // @api @@ -3381,7 +3819,7 @@ namespace ts { setChildren(node, node.typeParameters = asNodeArray(typeParameters)); setChildren(node, node.parameters = createNodeArray(parameters)); setChild(node, node.type = type); - return finish(node); + return finishJSDoc(node); } // @api @@ -3397,14 +3835,14 @@ namespace ts { const node = createBaseJSDocTag(SyntaxKind.JSDocTemplateTag, tagName || createIdentifier("template"), /*comment*/ undefined); setChild(node, node.constraint = constraint); setChildren(node, node.typeParameters = createNodeArray(typeParameters)); - return finish(node); + return finishJSDoc(node); } // @api function createJSDocTypeTag(tagName: Identifier | undefined, typeExpression: JSDocTypeExpression, comment?: string): JSDocTypeTag { const node = createBaseJSDocTag(SyntaxKind.JSDocTypeTag, tagName || createIdentifier("type"), comment); setChild(node, node.typeExpression = typeExpression); - return finish(node); + return finishJSDoc(node); } // @api @@ -3413,21 +3851,21 @@ namespace ts { setChild(node, node.typeExpression = typeExpression); setChild(node, node.fullName = fullName); setChild(node, node.name = getJSDocTypeAliasName(fullName)); - return finish(node); + return finishJSDoc(node); } // @api function createJSDocReturnTag(tagName: Identifier | undefined, typeExpression?: JSDocTypeExpression, comment?: string): JSDocReturnTag { const node = createBaseJSDocTag(SyntaxKind.JSDocReturnTag, tagName || createIdentifier("returns"), comment); setChild(node, node.typeExpression = typeExpression); - return finish(node); + return finishJSDoc(node); } // @api function createJSDocThisTag(tagName: Identifier | undefined, typeExpression?: JSDocTypeExpression): JSDocThisTag { const node = createBaseJSDocTag(SyntaxKind.JSDocThisTag, tagName || createIdentifier("this"), /*comment*/ undefined); setChild(node, node.typeExpression = typeExpression); - return finish(node); + return finishJSDoc(node); } // @api @@ -3437,7 +3875,7 @@ namespace ts { setChild(node, node.name = name); node.isNameFirst = !!isNameFirst; node.isBracketed = isBracketed; - return finish(node); + return finishJSDoc(node); } // @api @@ -3447,20 +3885,20 @@ namespace ts { setChild(node, node.name = name); node.isNameFirst = !!isNameFirst; node.isBracketed = isBracketed; - return finish(node); + return finishJSDoc(node); } // @api function createJSDocAuthorTag(tagName: Identifier | undefined, comment?: string): JSDocAuthorTag { const node = createBaseJSDocTag(SyntaxKind.JSDocAuthorTag, tagName || createIdentifier("author"), comment); - return finish(node); + return finishJSDoc(node); } // @api function createJSDocAugmentsTag(tagName: Identifier | undefined, className: JSDocAugmentsTag["class"]): JSDocAugmentsTag { const node = createBaseJSDocTag(SyntaxKind.JSDocAugmentsTag, tagName || createIdentifier("augments"), /*comment*/ undefined); setChild(node, node.class = className); - return finish(node); + return finishJSDoc(node); } // @api @@ -3469,26 +3907,26 @@ namespace ts { setChild(node, node.typeExpression = typeExpression); setChild(node, node.fullName = fullName); node.name = getJSDocTypeAliasName(fullName); - return finish(node); + return finishJSDoc(node); } // @api function createJSDocClassTag(tagName: Identifier | undefined): JSDocClassTag { const node = createBaseJSDocTag(SyntaxKind.JSDocClassTag, tagName || createIdentifier("class"), /*comment*/ undefined); - return finish(node); + return finishJSDoc(node); } // @api function createJSDocEnumTag(tagName: Identifier | undefined, typeExpression?: JSDocTypeExpression): JSDocEnumTag { const node = createBaseJSDocTag(SyntaxKind.JSDocEnumTag, tagName || createIdentifier("enum"), /*comment*/ undefined); setChild(node, node.typeExpression = typeExpression); - return finish(node); + return finishJSDoc(node); } // @api - function createJSDocTag(kind: T["kind"], tagName: Identifier): T { - const node = createBaseJSDocTag(kind, tagName, /*comment*/ undefined); - return finish(node); + function createJSDocUnknownTag(tagName: Identifier): JSDocUnknownTag { + const node = createBaseJSDocTag(SyntaxKind.JSDocTag, tagName, /*comment*/ undefined); + return finishJSDoc(node); } // @api @@ -3496,7 +3934,7 @@ namespace ts { const node = createBaseNode(SyntaxKind.JSDocComment); node.comment = comment; setChildren(node, node.tags = tags); - return finish(node); + return finishJSDoc(node); } // @@ -3509,6 +3947,9 @@ namespace ts { setChild(node, node.openingElement = openingElement); setChildren(node, node.children = createNodeArray(children)); setChild(node, node.closingElement = closingElement); + if (!skipTransformationFlags) { + markJsx(node); + } return finish(node); } @@ -3527,6 +3968,9 @@ namespace ts { setChild(node, node.tagName = tagName); setChildren(node, node.typeArguments = asNodeArray(typeArguments)); setChild(node, node.attributes = attributes); + if (!skipTransformationFlags) { + markJsx(node); + } return finish(node); } @@ -3545,6 +3989,9 @@ namespace ts { setChild(node, node.tagName = tagName); setChildren(node, node.typeArguments = asNodeArray(typeArguments)); setChild(node, node.attributes = attributes); + if (!skipTransformationFlags) { + markJsx(node); + } return finish(node); } @@ -3561,6 +4008,9 @@ namespace ts { function createJsxClosingElement(tagName: JsxTagNameExpression) { const node = createBaseNode(SyntaxKind.JsxClosingElement); setChild(node, node.tagName = tagName); + if (!skipTransformationFlags) { + markJsx(node); + } return finish(node); } @@ -3577,6 +4027,9 @@ namespace ts { setChild(node, node.openingFragment = openingFragment); setChildren(node, node.children = createNodeArray(children)); setChild(node, node.closingFragment = closingFragment); + if (!skipTransformationFlags) { + markJsx(node); + } return finish(node); } @@ -3594,6 +4047,9 @@ namespace ts { const node = createBaseNode(SyntaxKind.JsxText); node.text = text; node.containsOnlyTriviaWhiteSpaces = !!containsOnlyTriviaWhiteSpaces; + if (!skipTransformationFlags) { + markJsx(node); + } return finish(node); } @@ -3607,12 +4063,20 @@ namespace ts { // @api function createJsxOpeningFragment() { - return finish(createBaseNode(SyntaxKind.JsxOpeningFragment)); + const node = createBaseNode(SyntaxKind.JsxOpeningFragment); + if (!skipTransformationFlags) { + markJsx(node); + } + return finish(node); } // @api function createJsxJsxClosingFragment() { - return finish(createBaseNode(SyntaxKind.JsxClosingFragment)); + const node = createBaseNode(SyntaxKind.JsxClosingFragment); + if (!skipTransformationFlags) { + markJsx(node); + } + return finish(node); } // @api @@ -3620,6 +4084,9 @@ namespace ts { const node = createBaseNode(SyntaxKind.JsxAttribute); setChild(node, node.name = name); setChild(node, node.initializer = initializer); + if (!skipTransformationFlags) { + markJsx(node); + } return finish(node); } @@ -3635,6 +4102,9 @@ namespace ts { function createJsxAttributes(properties: readonly JsxAttributeLike[]) { const node = createBaseNode(SyntaxKind.JsxAttributes); setChildren(node, node.properties = createNodeArray(properties)); + if (!skipTransformationFlags) { + markJsx(node); + } return finish(node); } @@ -3649,6 +4119,9 @@ namespace ts { function createJsxSpreadAttribute(expression: Expression) { const node = createBaseNode(SyntaxKind.JsxSpreadAttribute); setChild(node, node.expression = expression); + if (!skipTransformationFlags) { + markJsx(node); + } return finish(node); } @@ -3664,6 +4137,9 @@ namespace ts { const node = createBaseNode(SyntaxKind.JsxExpression); setChild(node, node.dotDotDotToken = dotDotDotToken); setChild(node, node.expression = expression); + if (!skipTransformationFlags) { + markJsx(node); + } return finish(node); } @@ -3713,6 +4189,18 @@ namespace ts { const node = createBaseNode(SyntaxKind.HeritageClause); node.token = token; setChildren(node, node.types = createNodeArray(types)); + if (!skipTransformationFlags) { + switch (token) { + case SyntaxKind.ExtendsKeyword: + markES2015(node); + break; + case SyntaxKind.ImplementsKeyword: + markTypeScript(node); + break; + default: + return Debug.assertNever(token); + } + } return finish(node); } @@ -3726,13 +4214,17 @@ namespace ts { // @api function createCatchClause(variableDeclaration: string | VariableDeclaration | undefined, block: Block) { const node = createBaseNode(SyntaxKind.CatchClause); - setChild(node, node.variableDeclaration = !isString(variableDeclaration) ? variableDeclaration : createVariableDeclaration( + variableDeclaration = !isString(variableDeclaration) ? variableDeclaration : createVariableDeclaration( variableDeclaration, /*exclamationToken*/ undefined, /*type*/ undefined, /*initializer*/ undefined - )); + ); + setChild(node, node.variableDeclaration = variableDeclaration); setChild(node, node.block = block); + if (!skipTransformationFlags) { + if (!variableDeclaration) markES2019(node); + } return finish(node); } @@ -3784,6 +4276,9 @@ namespace ts { setChild(node, node.objectAssignmentInitializer = objectAssignmentInitializer !== undefined ? parenthesizer().parenthesizeExpressionForDisallowedComma(objectAssignmentInitializer) : undefined); + if (!skipTransformationFlags) { + markES2015(node); + } return finish(node); } @@ -3808,6 +4303,10 @@ namespace ts { function createSpreadAssignment(expression: Expression) { const node = createBaseNode(SyntaxKind.SpreadAssignment); setChild(node, node.expression = parenthesizer().parenthesizeExpressionForDisallowedComma(expression)); + if (!skipTransformationFlags) { + markES2018(node); + markObjectRestOrSpread(node); + } return finish(node); } @@ -3827,6 +4326,9 @@ namespace ts { const node = createBaseNode(SyntaxKind.EnumMember); setChild(node, node.name = asName(name)); setChild(node, node.initializer = initializer && parenthesizer().parenthesizeExpressionForDisallowedComma(initializer)); + if (!skipTransformationFlags) { + markTypeScript(node); + } return finish(node); } @@ -3960,6 +4462,9 @@ namespace ts { setChild(node, node.expression = expression); node.original = original; setTextRange(node, original); + if (!skipTransformationFlags) { + markTypeScript(node); + } return finish(node); } @@ -4581,15 +5086,26 @@ namespace ts { return statement && isNotEmittedStatement(statement) ? setTextRange(setOriginalNode(createEmptyStatement(), statement), statement) : statement; } - // tslint:disable-next-line no-empty - function setChildWorker(_parent: Node, _child: Node | undefined): void { + function setChildWorker(parent: Node, child: Node | undefined): void { + if (!skipTransformationFlags) { + if (child) { + parent.transformFlags |= propagateChildFlags(child); + } + } } - // tslint:disable-next-line no-empty - function setChildrenWorker(_parent: Node, _children: NodeArray | undefined): void { + function setChildrenWorker(parent: Node, children: NodeArray | undefined): void { + if (!skipTransformationFlags) { + if (children) { + parent.transformFlags |= propagateChildrenFlags(children); + } + } } function finishWorker(node: T) { + if (!skipTransformationFlags) { + node.transformFlags |= TransformFlags.HasComputedFlags; + } return node; } @@ -4602,8 +5118,37 @@ namespace ts { } } + // Language-edition and feature specific node markers + const markBindingPattern = createFlagMarker(TransformFlags.ContainsBindingPattern); + const markBlockScopedBinding = createFlagMarker(TransformFlags.ContainsBlockScopedBinding); + const markRestOrSpread = createFlagMarker(TransformFlags.ContainsRestOrSpread); + const markObjectRestOrSpread = createFlagMarker(TransformFlags.ContainsObjectRestOrSpread); + const markDestructuringAssignment = createFlagMarker(TransformFlags.ContainsDestructuringAssignment); + const markComputedPropertyName = createFlagMarker(TransformFlags.ContainsComputedPropertyName); + const markLexicalThis = createFlagMarker(TransformFlags.ContainsLexicalThis); + const markDynamicImport = createFlagMarker(TransformFlags.ContainsDynamicImport); + const markGenerator = createFlagMarker(TransformFlags.ContainsGenerator); + const markYield = createFlagMarker(TransformFlags.ContainsYield); + const markHoistedDeclarationOrCompletion = createFlagMarker(TransformFlags.ContainsHoistedDeclarationOrCompletion); + const markClassFields = createFlagMarker(TransformFlags.ContainsClassFields); + const markES2015 = createFlagMarker(TransformFlags.ContainsES2015); + const markES2016 = createFlagMarker(TransformFlags.ContainsES2016); + const markES2017 = createFlagMarker(TransformFlags.ContainsES2017); + const markES2018 = createFlagMarker(TransformFlags.ContainsES2018); + const markES2019 = createFlagMarker(TransformFlags.ContainsES2019); + const markESNext = createFlagMarker(TransformFlags.ContainsESNext); + const markTypeScript = createFlagMarker(TransformFlags.ContainsTypeScript); + const markTypeScriptOnly = createFlagMarker(TransformFlags.ContainsTypeScript, /*excludeSubtree*/ true); + const markTypeScriptClassSyntax = createFlagMarker(TransformFlags.ContainsTypeScriptClassSyntax); + const markJsx = createFlagMarker(TransformFlags.ContainsJsx); + + function createFlagMarker(transformFlags: TransformFlags, excludeSubtree?: boolean) { + return excludeSubtree ? (node: Node) => { node.transformFlags = transformFlags; } : + (node: Node) => { node.transformFlags |= transformFlags; }; + } + function observeArguments(action: (arg1: U, arg2: U) => U, observer: ((arg1: T, arg2: T) => void) | undefined): (arg1: U, arg2: U) => U; - function observeArguments(action: (arg1: T, arg2: U) => R, observer: ((arg1: T, arg2: U) => void) | undefined): (arg1: T, arg2: U) => R; + function observeArguments(action: (arg1: T, arg2: U | undefined) => R, observer: ((arg1: T, arg2: U) => void) | undefined): (arg1: T, arg2: U | undefined) => R; function observeArguments(action: (arg1: T, arg2: U) => R, observer: ((arg1: T, arg2: U) => void) | undefined): (arg1: T, arg2: U) => R { return !observer ? action : (arg1, arg2) => { if (arg2 !== undefined) { @@ -4676,6 +5221,101 @@ namespace ts { return tokenValue; } + function propagatePropertyNameFlags(node: PropertyName, transformFlags: TransformFlags) { + return transformFlags | (node.transformFlags & TransformFlags.PropertyNamePropagatingFlags); + } + + function propagateChildFlags(child: Node): TransformFlags { + const childFlags = child.transformFlags & ~TransformFlags.HasComputedFlags & ~getTransformFlagsSubtreeExclusions(child.kind); + return isNamedDeclaration(child) && isPropertyName(child.name) ? propagatePropertyNameFlags(child.name, childFlags) : childFlags; + } + + function propagateChildrenFlags(children: NodeArray): TransformFlags { + return children.transformFlags & ~TransformFlags.HasComputedFlags; + } + + function aggregateChildrenFlags(children: NodeArray) { + let subtreeFlags = TransformFlags.None; + for (const child of children) { + subtreeFlags |= propagateChildFlags(child); + } + children.transformFlags = subtreeFlags | TransformFlags.HasComputedFlags; + } + + /** + * Gets the transform flags to exclude when unioning the transform flags of a subtree. + */ + export function getTransformFlagsSubtreeExclusions(kind: SyntaxKind) { + if (kind >= SyntaxKind.FirstTypeNode && kind <= SyntaxKind.LastTypeNode) { + return TransformFlags.TypeExcludes; + } + + switch (kind) { + case SyntaxKind.CallExpression: + case SyntaxKind.NewExpression: + case SyntaxKind.ArrayLiteralExpression: + return TransformFlags.ArrayLiteralOrCallOrNewExcludes; + case SyntaxKind.ModuleDeclaration: + return TransformFlags.ModuleExcludes; + case SyntaxKind.Parameter: + return TransformFlags.ParameterExcludes; + case SyntaxKind.ArrowFunction: + return TransformFlags.ArrowFunctionExcludes; + case SyntaxKind.FunctionExpression: + case SyntaxKind.FunctionDeclaration: + return TransformFlags.FunctionExcludes; + case SyntaxKind.VariableDeclarationList: + return TransformFlags.VariableDeclarationListExcludes; + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ClassExpression: + return TransformFlags.ClassExcludes; + case SyntaxKind.Constructor: + return TransformFlags.ConstructorExcludes; + case SyntaxKind.PropertyDeclaration: + return TransformFlags.PropertyExcludes; + case SyntaxKind.MethodDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + return TransformFlags.MethodOrAccessorExcludes; + case SyntaxKind.AnyKeyword: + case SyntaxKind.NumberKeyword: + case SyntaxKind.BigIntKeyword: + case SyntaxKind.NeverKeyword: + case SyntaxKind.StringKeyword: + case SyntaxKind.ObjectKeyword: + 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.InterfaceDeclaration: + case SyntaxKind.TypeAliasDeclaration: + return TransformFlags.TypeExcludes; + case SyntaxKind.ObjectLiteralExpression: + return TransformFlags.ObjectLiteralExcludes; + case SyntaxKind.CatchClause: + return TransformFlags.CatchClauseExcludes; + case SyntaxKind.ObjectBindingPattern: + case SyntaxKind.ArrayBindingPattern: + return TransformFlags.BindingPatternExcludes; + case SyntaxKind.TypeAssertionExpression: + case SyntaxKind.AsExpression: + case SyntaxKind.PartiallyEmittedExpression: + case SyntaxKind.ParenthesizedExpression: + case SyntaxKind.SuperKeyword: + return TransformFlags.OuterExpressionExcludes; + case SyntaxKind.PropertyAccessExpression: + case SyntaxKind.ElementAccessExpression: + return TransformFlags.PropertyAccessExcludes; + default: + return TransformFlags.NodeExcludes; + } + } + export const factory = createNodeFactory(NodeFactoryFlags.NoIndentationOnFreshPropertyAccess, createBaseNodeFactory(), { onCreateNode: node => node.flags |= NodeFlags.Synthesized }); @@ -4731,7 +5371,6 @@ namespace ts { if (updated !== original) { setOriginalNode(updated, original); setTextRange(updated, original); - aggregateTransformFlags(updated); } return updated; } diff --git a/src/compiler/factory/utilities.ts b/src/compiler/factory/utilities.ts index d6a83b30858..add92928458 100644 --- a/src/compiler/factory/utilities.ts +++ b/src/compiler/factory/utilities.ts @@ -174,48 +174,46 @@ namespace ts { function createExpressionForAccessorDeclaration(factory: NodeFactory, properties: NodeArray, property: AccessorDeclaration, receiver: Expression, multiLine: boolean) { const { firstAccessor, getAccessor, setAccessor } = getAllAccessorDeclarations(properties, property); if (property === firstAccessor) { - return aggregateTransformFlags( - setTextRange( - factory.createObjectDefinePropertyCall( - receiver, - createExpressionForPropertyName(factory, property.name), - factory.createPropertyDescriptor({ - enumerable: true, - configurable: true, - get: getAccessor && setTextRange( - setOriginalNode( - factory.createFunctionExpression( - getAccessor.modifiers, - /*asteriskToken*/ undefined, - /*name*/ undefined, - /*typeParameters*/ undefined, - getAccessor.parameters, - /*type*/ undefined, - getAccessor.body! // TODO: GH#18217 - ), - getAccessor + return setTextRange( + factory.createObjectDefinePropertyCall( + receiver, + createExpressionForPropertyName(factory, property.name), + factory.createPropertyDescriptor({ + enumerable: true, + configurable: true, + get: getAccessor && setTextRange( + setOriginalNode( + factory.createFunctionExpression( + getAccessor.modifiers, + /*asteriskToken*/ undefined, + /*name*/ undefined, + /*typeParameters*/ undefined, + getAccessor.parameters, + /*type*/ undefined, + getAccessor.body! // TODO: GH#18217 ), getAccessor ), - set: setAccessor && setTextRange( - setOriginalNode( - factory.createFunctionExpression( - setAccessor.modifiers, - /*asteriskToken*/ undefined, - /*name*/ undefined, - /*typeParameters*/ undefined, - setAccessor.parameters, - /*type*/ undefined, - setAccessor.body! // TODO: GH#18217 - ), - setAccessor + getAccessor + ), + set: setAccessor && setTextRange( + setOriginalNode( + factory.createFunctionExpression( + setAccessor.modifiers, + /*asteriskToken*/ undefined, + /*name*/ undefined, + /*typeParameters*/ undefined, + setAccessor.parameters, + /*type*/ undefined, + setAccessor.body! // TODO: GH#18217 ), setAccessor - ) - }, !multiLine) - ), - firstAccessor - ) + ), + setAccessor + ) + }, !multiLine) + ), + firstAccessor ); } @@ -223,61 +221,55 @@ namespace ts { } function createExpressionForPropertyAssignment(factory: NodeFactory, property: PropertyAssignment, receiver: Expression) { - return aggregateTransformFlags( - setOriginalNode( - setTextRange( - factory.createAssignment( - createMemberAccessForPropertyName(factory, receiver, property.name, /*location*/ property.name), - property.initializer - ), - property + return setOriginalNode( + setTextRange( + factory.createAssignment( + createMemberAccessForPropertyName(factory, receiver, property.name, /*location*/ property.name), + property.initializer ), property - ) + ), + property ); } function createExpressionForShorthandPropertyAssignment(factory: NodeFactory, property: ShorthandPropertyAssignment, receiver: Expression) { - return aggregateTransformFlags( - setOriginalNode( - setTextRange( - factory.createAssignment( - createMemberAccessForPropertyName(factory, receiver, property.name, /*location*/ property.name), - getSynthesizedClone(property.name) - ), - /*location*/ property + return setOriginalNode( + setTextRange( + factory.createAssignment( + createMemberAccessForPropertyName(factory, receiver, property.name, /*location*/ property.name), + getSynthesizedClone(property.name) ), - /*original*/ property - ) + /*location*/ property + ), + /*original*/ property ); } function createExpressionForMethodDeclaration(factory: NodeFactory, method: MethodDeclaration, receiver: Expression) { - return aggregateTransformFlags( - setOriginalNode( - setTextRange( - factory.createAssignment( - createMemberAccessForPropertyName(factory, receiver, method.name, /*location*/ method.name), - setOriginalNode( - setTextRange( - factory.createFunctionExpression( - method.modifiers, - method.asteriskToken, - /*name*/ undefined, - /*typeParameters*/ undefined, - method.parameters, - /*type*/ undefined, - method.body! // TODO: GH#18217 - ), - /*location*/ method + return setOriginalNode( + setTextRange( + factory.createAssignment( + createMemberAccessForPropertyName(factory, receiver, method.name, /*location*/ method.name), + setOriginalNode( + setTextRange( + factory.createFunctionExpression( + method.modifiers, + method.asteriskToken, + /*name*/ undefined, + /*typeParameters*/ undefined, + method.parameters, + /*type*/ undefined, + method.body! // TODO: GH#18217 ), - /*original*/ method - ) - ), - /*location*/ method + /*location*/ method + ), + /*original*/ method + ) ), - /*original*/ method - ) + /*location*/ method + ), + /*original*/ method ); } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index db8abef3f04..c4bf285dff0 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -869,10 +869,12 @@ namespace ts { parsingContext = 0; identifiers = undefined!; notParenthesizedArrow = undefined!; + factory.setSkipTransformationFlags(/*inDeclarationFile*/ false); } function parseSourceFileWorker(fileName: string, languageVersion: ScriptTarget, setParentNodes: boolean, scriptKind: ScriptKind): SourceFile { const isDeclarationFile = isDeclarationFileName(fileName); + factory.setSkipTransformationFlags(isDeclarationFile); if (isDeclarationFile) { contextFlags |= NodeFlags.Ambient; } @@ -906,6 +908,7 @@ namespace ts { fixupParentReferences(sourceFile); } + factory.setSkipTransformationFlags(/*inDeclarationFile*/ false); return sourceFile; function reportPragmaDiagnostic(pos: number, end: number, diagnostic: DiagnosticMessage) { @@ -2578,9 +2581,7 @@ namespace ts { const defaultType = parseOptional(SyntaxKind.EqualsToken) ? parseType() : undefined; const node = factory.createTypeParameterDeclaration(name, constraint, defaultType); - if (expression) { - node.expression = expression; - } + if (expression) factory.trackExtraneousChildNode(node, node.expression = expression); return finishNode(node, pos); } @@ -2853,7 +2854,7 @@ namespace ts { // Although type literal properties cannot not have initializers, we attempt // to parse an initializer so we can report in the checker that an interface // property or type literal property cannot have an initializer. - if (token() === SyntaxKind.EqualsToken) node.initializer = parseInitializer(); + if (token() === SyntaxKind.EqualsToken) factory.trackExtraneousChildNode(node, node.initializer = parseInitializer()); } parseTypeMemberSemicolon(); return withJSDoc(finishNode(node, pos), hasJSDoc); @@ -3045,8 +3046,8 @@ namespace ts { if (negative) { nextToken(); } - let expression: BooleanLiteral | LiteralExpression | PrefixUnaryExpression = token() === SyntaxKind.TrueKeyword || token() === SyntaxKind.FalseKeyword - ? parseTokenNode() + let expression: BooleanLiteral | NullLiteral | LiteralExpression | PrefixUnaryExpression = token() === SyntaxKind.TrueKeyword || token() === SyntaxKind.FalseKeyword || token() === SyntaxKind.NullKeyword + ? parseTokenNode() : parseLiteralLikeNode(token()) as LiteralExpression; if (negative) { expression = finishNode(factory.createPrefix(SyntaxKind.MinusToken, expression), pos); @@ -3107,11 +3108,11 @@ namespace ts { case SyntaxKind.BigIntLiteral: case SyntaxKind.TrueKeyword: case SyntaxKind.FalseKeyword: + case SyntaxKind.NullKeyword: return parseLiteralTypeNode(); case SyntaxKind.MinusToken: return lookAhead(nextTokenIsNumericOrBigIntLiteral) ? parseLiteralTypeNode(/*negative*/ true) : parseTypeReference(); case SyntaxKind.VoidKeyword: - case SyntaxKind.NullKeyword: return parseTokenNode(); case SyntaxKind.ThisKeyword: { const thisKeyword = parseThisTypeNode(); @@ -4919,7 +4920,7 @@ namespace ts { node = factory.createShorthandPropertyAssignment(name as Identifier, objectAssignmentInitializer); // Save equals token for error reporting. // TODO(rbuckton): Consider manufacturing this when we need to report an error as it is otherwise not useful. - if (equalsToken) node.equalsToken = equalsToken; + if (equalsToken) factory.trackExtraneousChildNode(node, node.equalsToken = equalsToken); } else { parseExpected(SyntaxKind.ColonToken); @@ -4927,10 +4928,12 @@ namespace ts { node = factory.createPropertyAssignment(name, initializer); } // Decorators, Modifiers, questionToken, and exclamationToken are not supported by property assignments and are reported in the grammar checker - if (decorators) node.decorators = decorators; + if (decorators) factory.trackExtraneousChildNodes(node, node.decorators = decorators); + // TODO(rbuckton): Use this instead... + // if (modifiers) factory.trackExtraneousChildNodes(node, node.modifiers = modifiers); if (modifiers) node.modifiers = modifiers; - if (questionToken) node.questionToken = questionToken; - if (exclamationToken) node.exclamationToken = exclamationToken; + if (questionToken) factory.trackExtraneousChildNode(node, node.questionToken = questionToken); + if (exclamationToken) factory.trackExtraneousChildNode(node, node.exclamationToken = exclamationToken); return withJSDoc(finishNode(node, pos), hasJSDoc); } @@ -5742,7 +5745,7 @@ namespace ts { parseSemicolon(); const node = factory.createVariableStatement(modifiers, declarationList); // Decorators are not allowed on a variable statement, so we keep track of them to report them in the grammar checker. - if (decorators) node.decorators = decorators; + if (decorators) factory.trackExtraneousChildNodes(node, node.decorators = decorators); return withJSDoc(finishNode(node, pos), hasJSDoc); } @@ -5778,8 +5781,8 @@ namespace ts { const body = parseFunctionBlockOrSemicolon(SignatureFlags.None, Diagnostics.or_expected); const node = factory.createConstructorDeclaration(decorators, modifiers, parameters, body); // Attach `typeParameters` and `type` if they exist so that we can report them in the grammar checker. - if (typeParameters) node.typeParameters = typeParameters; - if (type) node.type = type; + if (typeParameters) factory.trackExtraneousChildNodes(node, node.typeParameters = typeParameters); + if (type) factory.trackExtraneousChildNode(node, node.type = type); return withJSDoc(finishNode(node, pos), hasJSDoc); } }); @@ -5812,9 +5815,8 @@ namespace ts { body ); // An exclamation token on a method is invalid syntax and will be handled by the grammar checker - if (exclamationToken) { - node.exclamationToken = exclamationToken; - } + if (exclamationToken) factory.trackExtraneousChildNode(node, node.exclamationToken = exclamationToken); + return withJSDoc(finishNode(node, pos), hasJSDoc); } @@ -5872,8 +5874,8 @@ namespace ts { ? factory.createGetAccessorDeclaration(decorators, modifiers, name, parameters, type, body) : factory.createSetAccessorDeclaration(decorators, modifiers, name, parameters, body); // Keep track of `typeParameters` (for both) and `type` (for setters) if they were parsed those indicate grammar errors - if (typeParameters) node.typeParameters = typeParameters; - if (type && kind === SyntaxKind.SetAccessor) node.type = type; + if (typeParameters) factory.trackExtraneousChildNodes(node, node.typeParameters = typeParameters); + if (type && kind === SyntaxKind.SetAccessor) factory.trackExtraneousChildNode(node, node.type = type); return withJSDoc(finishNode(node, pos), hasJSDoc); } @@ -6283,8 +6285,8 @@ namespace ts { parseSemicolon(); const node = factory.createNamespaceExportDeclaration(name); // NamespaceExportDeclaration nodes cannot have decorators or modifiers, so we attach them here so we can report them in the grammar checker - if (decorators) node.decorators = decorators; - if (modifiers) node.modifiers = modifiers; + if (decorators) factory.trackExtraneousChildNodes(node, node.decorators = decorators); + if (modifiers) factory.trackExtraneousChildNodes(node, node.modifiers = modifiers); return withJSDoc(finishNode(node, pos), hasJSDoc); } @@ -6549,6 +6551,7 @@ namespace ts { export namespace JSDocParser { export function parseJSDocTypeExpressionForTests(content: string, start: number | undefined, length: number | undefined): { jsDocTypeExpression: JSDocTypeExpression, diagnostics: Diagnostic[] } | undefined { initializeState(content, ScriptTarget.Latest, /*_syntaxCursor:*/ undefined, ScriptKind.JS); + factory.setSkipTransformationFlags(/*value*/ true); // reset in 'clearState' scanner.setText(content, start, length); currentToken = scanner.scan(); const jsDocTypeExpression = parseJSDocTypeExpression(); @@ -6567,7 +6570,7 @@ namespace ts { // Parses out a JSDoc type expression. export function parseJSDocTypeExpression(mayOmitBraces?: boolean): JSDocTypeExpression { const pos = getNodePos(); - + const saveSkipTransformationFlags = factory.setSkipTransformationFlags(/*value*/ true); const hasBrace = (mayOmitBraces ? parseOptional : parseExpected)(SyntaxKind.OpenBraceToken); const type = doInsideOfContext(NodeFlags.JSDoc, parseJSDocType); if (!mayOmitBraces || hasBrace) { @@ -6576,11 +6579,13 @@ namespace ts { const result = factory.createJSDocTypeExpression(type); fixupParentReferences(result); + factory.setSkipTransformationFlags(saveSkipTransformationFlags); return finishNode(result, pos); } export function parseIsolatedJSDocComment(content: string, start: number | undefined, length: number | undefined): { jsDoc: JSDoc, diagnostics: Diagnostic[] } | undefined { initializeState(content, ScriptTarget.Latest, /*_syntaxCursor:*/ undefined, ScriptKind.JS); + factory.setSkipTransformationFlags(/*value*/ true); // reset in 'clearState' const jsDoc = doInsideOfContext(NodeFlags.JSDoc, () => parseJSDocCommentWorker(start, length)); const sourceFile = { languageVariant: LanguageVariant.Standard, text: content }; // tslint:disable-line no-object-literal-type-assertion @@ -6591,6 +6596,7 @@ namespace ts { } export function parseJSDocComment(parent: HasJSDoc, start: number, length: number): JSDoc | undefined { + const saveSkipTransformationFlags = factory.setSkipTransformationFlags(true); const saveToken = currentToken; const saveParseDiagnosticsLength = parseDiagnostics.length; const saveParseErrorBeforeNextFinishedNode = parseErrorBeforeNextFinishedNode; @@ -6609,7 +6615,7 @@ namespace ts { currentToken = saveToken; parseDiagnostics.length = saveParseDiagnosticsLength; parseErrorBeforeNextFinishedNode = saveParseErrorBeforeNextFinishedNode; - + factory.setSkipTransformationFlags(saveSkipTransformationFlags); return comment; } diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index 30c8a2431ee..6ac7f4df0d2 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -103,13 +103,12 @@ namespace ts { expressions.push(value); } - return aggregateTransformFlags(context.factory.inlineExpressions(expressions!)) || context.factory.createOmittedExpression(); + return context.factory.inlineExpressions(expressions!) || context.factory.createOmittedExpression(); function emitExpression(expression: Expression) { // NOTE: this completely disables source maps, but aligns with the behavior of // `emitAssignment` in the old emitter. setEmitFlags(expression, EmitFlags.NoNestedSourceMaps); - aggregateTransformFlags(expression); expressions = append(expressions, expression); } @@ -222,7 +221,6 @@ namespace ts { if (isIdentifier(name)) { setEmitFlags(variable, EmitFlags.NoNestedSourceMaps); } - aggregateTransformFlags(variable); declarations.push(variable); } return declarations; diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index bd74f997f0a..6c026dd489e 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -330,10 +330,26 @@ namespace ts { && !(node).expression; } + function isOrMayContainReturnCompletion(node: Node) { + return node.transformFlags & TransformFlags.ContainsHoistedDeclarationOrCompletion + && (isReturnStatement(node) + || isIfStatement(node) + || isWithStatement(node) + || isSwitchStatement(node) + || isCaseBlock(node) + || isCaseClause(node) + || isDefaultClause(node) + || isTryStatement(node) + || isCatchClause(node) + || isLabeledStatement(node) + || isIterationStatement(node, /*lookInLabeledStatements*/ false) + || isBlock(node)); + } + function shouldVisitNode(node: Node): boolean { return (node.transformFlags & TransformFlags.ContainsES2015) !== 0 || convertedLoopState !== undefined - || (hierarchyFacts & HierarchyFacts.ConstructorWithCapturedSuper && (isStatement(node) || (node.kind === SyntaxKind.Block))) + || (hierarchyFacts & HierarchyFacts.ConstructorWithCapturedSuper && isOrMayContainReturnCompletion(node)) || (isIterationStatement(node, /*lookInLabeledStatements*/ false) && shouldConvertIterationStatement(node)) || (getEmitFlags(node) & EmitFlags.TypeScriptClassWrapper) !== 0; } @@ -2330,7 +2346,6 @@ namespace ts { // evaluated on every iteration. const assignment = factory.createAssignment(initializer, boundValue); if (isDestructuringAssignment(assignment)) { - aggregateTransformFlags(assignment); statements.push(factory.createExpressionStatement(visitBinaryExpression(assignment, /*needsDestructuringValue*/ false))); } else { @@ -2717,13 +2732,11 @@ namespace ts { } else { const clone = convertIterationStatementCore(node, initializerFunction, factory.createBlock(bodyFunction.part, /*multiLine*/ true)); - aggregateTransformFlags(clone); loop = factory.restoreEnclosingLabel(clone, outermostLabeledStatement, convertedLoopState && resetLabel); } } else { const clone = convertIterationStatementCore(node, initializerFunction, visitNode(node.statement, visitor, isStatement, factory.liftToBlock)); - aggregateTransformFlags(clone); loop = factory.restoreEnclosingLabel(clone, outermostLabeledStatement, convertedLoopState && resetLabel); } diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 8146ec94bfe..f9ab22a2131 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -72,7 +72,7 @@ namespace ts { currentSourceFile = undefined!; currentModuleInfo = undefined!; needUMDDynamicImportHelper = false; - return aggregateTransformFlags(updated); + return updated; } diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 71a4b9b4276..8800f22e422 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -135,7 +135,7 @@ namespace ts { contextObject = undefined!; hoistedStatements = undefined!; enclosingBlockScopedContainer = undefined!; - return aggregateTransformFlags(updated); + return updated; } /** diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 763ef8b1197..0fa00752bcc 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -735,7 +735,6 @@ namespace ts { emitFlags |= EmitFlags.NoTrailingSourceMap; } - aggregateTransformFlags(classDeclaration); setTextRange(classDeclaration, node); setOriginalNode(classDeclaration, node); setEmitFlags(classDeclaration, emitFlags); @@ -850,7 +849,6 @@ namespace ts { heritageClauses, members ); - aggregateTransformFlags(classExpression); setOriginalNode(classExpression, node); setTextRange(classExpression, location); @@ -886,7 +884,6 @@ namespace ts { transformClassMembers(node) ); - aggregateTransformFlags(classExpression); setOriginalNode(classExpression, node); setTextRange(classExpression, node); @@ -907,13 +904,13 @@ namespace ts { if (parametersWithPropertyAssignments) { for (const parameter of parametersWithPropertyAssignments) { if (isIdentifier(parameter.name)) { - members.push(aggregateTransformFlags(factory.createPropertyDeclaration( + members.push(factory.createPropertyDeclaration( /*decorators*/ undefined, /*modifiers*/ undefined, parameter.name, /*questionOrExclamationToken*/ undefined, /*type*/ undefined, - /*initializer*/ undefined))); + /*initializer*/ undefined)); } } } @@ -1501,7 +1498,6 @@ namespace ts { switch (node.kind) { case SyntaxKind.VoidKeyword: case SyntaxKind.UndefinedKeyword: - case SyntaxKind.NullKeyword: case SyntaxKind.NeverKeyword: return factory.createVoidZero(); @@ -1542,6 +1538,9 @@ namespace ts { case SyntaxKind.FalseKeyword: return factory.createIdentifier("Boolean"); + case SyntaxKind.NullKeyword: + return factory.createVoidZero(); + default: return Debug.failBadSyntaxKind((node).literal); } @@ -1602,7 +1601,7 @@ namespace ts { if (typeNode.kind === SyntaxKind.NeverKeyword) { continue; // Always elide `never` from the union/intersection if possible } - if (!strictNullChecks && (typeNode.kind === SyntaxKind.NullKeyword || typeNode.kind === SyntaxKind.UndefinedKeyword)) { + if (!strictNullChecks && (typeNode.kind === SyntaxKind.LiteralType && (typeNode as LiteralTypeNode).literal.kind === SyntaxKind.NullKeyword || typeNode.kind === SyntaxKind.UndefinedKeyword)) { continue; // Elide null and undefined from unions for metadata, just like what we did prior to the implementation of strict null checks } const serializedIndividual = serializeTypeNode(typeNode); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 423e1153d31..ca935b206c5 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1270,6 +1270,8 @@ namespace ts { _typeNodeBrand: any; } + // NOTE: `this` in a type position is a `ThisTypeNode`. Either `null`, `true`, or `false` in a type position + // is a `LiteralTypeNode`. This is so that the nodes for expressions and types do not overlap. export interface KeywordTypeNode extends TypeNode { kind: SyntaxKind.AnyKeyword | SyntaxKind.UnknownKeyword @@ -1279,10 +1281,8 @@ namespace ts { | SyntaxKind.BooleanKeyword | SyntaxKind.StringKeyword | SyntaxKind.SymbolKeyword - | SyntaxKind.ThisKeyword | SyntaxKind.VoidKeyword | SyntaxKind.UndefinedKeyword - | SyntaxKind.NullKeyword | SyntaxKind.NeverKeyword; } @@ -1296,6 +1296,7 @@ namespace ts { /* @internal */ export type LiteralImportTypeNode = ImportTypeNode & { argument: LiteralTypeNode & { literal: StringLiteral } }; + // A ThisTypeNode represents the `this` keyword in a type position. This is necessary since ThisKeyword is treated as the `this` expression. export interface ThisTypeNode extends TypeNode { kind: SyntaxKind.ThisType; } @@ -1421,7 +1422,7 @@ namespace ts { export interface LiteralTypeNode extends TypeNode { kind: SyntaxKind.LiteralType; - literal: BooleanLiteral | LiteralExpression | PrefixUnaryExpression; + literal: NullLiteral | BooleanLiteral | LiteralExpression | PrefixUnaryExpression; } export interface StringLiteral extends LiteralExpression { @@ -1506,21 +1507,21 @@ namespace ts { _primaryExpressionBrand: any; } - export interface NullLiteral extends PrimaryExpression, TypeNode { + export interface NullLiteral extends PrimaryExpression { kind: SyntaxKind.NullKeyword; } - export interface TrueLiteral extends PrimaryExpression, TypeNode { + export interface TrueLiteral extends PrimaryExpression { kind: SyntaxKind.TrueKeyword; } - export interface FalseLiteral extends PrimaryExpression, TypeNode { + export interface FalseLiteral extends PrimaryExpression { kind: SyntaxKind.FalseKeyword; } export type BooleanLiteral = TrueLiteral | FalseLiteral; - export interface ThisExpression extends PrimaryExpression, KeywordTypeNode { + export interface ThisExpression extends PrimaryExpression { kind: SyntaxKind.ThisKeyword; } @@ -5629,7 +5630,7 @@ namespace ts { None = 0, // Facts - // - Flags used to indicate that a node or subtree contains syntax that requires transformation. + // - Flags used to indicate that a node or subtree contains syntax that may require transformation. ContainsTypeScript = 1 << 0, ContainsJsx = 1 << 1, ContainsESNext = 1 << 2, @@ -5660,19 +5661,6 @@ namespace ts { // It is a good reminder of how much room we have left HasComputedFlags = 1 << 29, // Transform flags have been computed. - // Assertions - // - Bitmasks that are used to assert facts about the syntax of a node and its subtree. - AssertTypeScript = ContainsTypeScript, - AssertJsx = ContainsJsx, - AssertESNext = ContainsESNext, - AssertES2019 = ContainsES2019, - AssertES2018 = ContainsES2018, - AssertES2017 = ContainsES2017, - AssertES2016 = ContainsES2016, - AssertES2015 = ContainsES2015, - AssertGenerator = ContainsGenerator, - AssertDestructuringAssignment = ContainsDestructuringAssignment, - // Scope Exclusions // - Bitmasks that exclude flags from propagating out of a specific context // into the subtree flags of their container. @@ -5929,6 +5917,22 @@ namespace ts { /* @internal */ getConverters(): NodeConverters; createNodeArray(elements?: readonly T[], hasTrailingComma?: boolean): NodeArray; + /** + * This is used by the parser to skip transform flag aggregation in Declaration files or other contexts. + * @returns The previous value. + */ + /* @internal */ setSkipTransformationFlags(value: boolean): boolean; + /** + * Tracks setting an extraneous node on a parent. This is primarily used by the parser to add invalid nodes for grammar reporting purposes and is needed to + * correctly update transform flags after a node has been created. + */ + /* @internal */ trackExtraneousChildNode(parent: Node, child: Node | undefined): void; + /** + * Tracks setting an extraneous node array on a parent. This is primarily used by the parser to add invalid nodes for grammar reporting purposes and is needed to + * correctly update transform flags after a node has been created. + */ + /* @internal */ trackExtraneousChildNodes(parent: Node, children: NodeArray | undefined): void; + // // Literals // diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 8df6bba8f24..3169d3f50ef 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3951,6 +3951,14 @@ namespace ts { return flags; } + export function getModifierFlagsNoWrite(node: Node): ModifierFlags { + if (node.modifierFlagsCache & ModifierFlags.HasComputedFlags) { + return node.modifierFlagsCache & ~ModifierFlags.HasComputedFlags; + } + + return getModifierFlagsNoCache(node); + } + export function getModifierFlagsNoCache(node: Node): ModifierFlags { let flags = modifiersToFlags(node.modifiers); @@ -4693,7 +4701,6 @@ namespace ts { || kind === SyntaxKind.ThisKeyword || kind === SyntaxKind.VoidKeyword || kind === SyntaxKind.UndefinedKeyword - || kind === SyntaxKind.NullKeyword || kind === SyntaxKind.NeverKeyword || kind === SyntaxKind.ExpressionWithTypeArguments || kind === SyntaxKind.JSDocAllType @@ -6020,15 +6027,14 @@ namespace ts { } /* @internal */ - // TODO(rbuckton): Rename to 'isParameterPropertyModifierKind' - export function isParameterPropertyModifier(kind: SyntaxKind): boolean { + export function isParameterPropertyModifierKind(kind: SyntaxKind): boolean { return !!(modifierToFlag(kind) & ModifierFlags.ParameterPropertyModifier); } /* @internal */ // TODO(rbuckton): Rename to 'isClassMemberModifierKind' export function isClassMemberModifier(idToken: SyntaxKind): boolean { - return isParameterPropertyModifier(idToken) || idToken === SyntaxKind.StaticKeyword; + return isParameterPropertyModifierKind(idToken) || idToken === SyntaxKind.StaticKeyword; } // Functions diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 9d940d8e24a..b5a9c722f40 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -26,7 +26,6 @@ namespace ts { return node; } - aggregateTransformFlags(node); const visited = visitor(node); if (visited === node) { return node; @@ -44,7 +43,6 @@ namespace ts { } Debug.assertNode(visitedNode, test); - aggregateTransformFlags(visitedNode!); return visitedNode; } @@ -110,7 +108,6 @@ namespace ts { // Visit each original node. for (let i = 0; i < count; i++) { const node = nodes[i + start]; - aggregateTransformFlags(node); const visited = node !== undefined ? visitor(node) : undefined; if (updated !== undefined || visited === undefined || visited !== node) { if (updated === undefined) { @@ -124,13 +121,11 @@ namespace ts { if (isArray(visited)) { for (const visitedNode of visited) { Debug.assertNode(visitedNode, test); - aggregateTransformFlags(visitedNode); updated.push(visitedNode); } } else { Debug.assertNode(visited, test); - aggregateTransformFlags(visited); updated.push(visited); } } @@ -1504,78 +1499,4 @@ namespace ts { ? setTextRange(factory.createNodeArray(insertStatementsAfterStandardPrologue(statements.slice(), declarations)), statements) : insertStatementsAfterStandardPrologue(statements, declarations); } - - /** - * Aggregates the TransformFlags for a Node and its subtree. - */ - export function aggregateTransformFlags(node: T): T { - aggregateTransformFlagsForNode(node); - return 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 TransformFlags.None; - } - if (node.transformFlags & TransformFlags.HasComputedFlags) { - const nodeFlags = node.transformFlags & ~getTransformFlagsSubtreeExclusions(node.kind); - switch (node.kind) { - case SyntaxKind.MethodDeclaration: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - return nodeFlags | ((node as NamedDeclaration).name!.transformFlags & TransformFlags.PropertyNamePropagatingFlags); - } - return nodeFlags; - } - const subtreeFlags = aggregateTransformFlagsForSubtree(node); - return computeTransformFlagsForNode(node, subtreeFlags); - } - - function aggregateTransformFlagsForNodeArray(nodes: NodeArray): TransformFlags { - if (nodes === undefined) { - return TransformFlags.None; - } - let subtreeFlags = TransformFlags.None; - let nodeArrayFlags = TransformFlags.None; - for (const node of nodes) { - subtreeFlags |= aggregateTransformFlagsForNode(node); - nodeArrayFlags |= node.transformFlags & ~TransformFlags.HasComputedFlags; - } - nodes.transformFlags = nodeArrayFlags | TransformFlags.HasComputedFlags; - return subtreeFlags; - } - - /** - * 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 (hasModifier(node, ModifierFlags.Ambient) || (isTypeNode(node) && node.kind !== SyntaxKind.ExpressionWithTypeArguments)) { - return TransformFlags.None; - } - - // Aggregate the transform flags of each child. - return reduceEachChild(node, TransformFlags.None, aggregateTransformFlagsForChildNode, aggregateTransformFlagsForChildNodes); - } - - /** - * Aggregates the TransformFlags of a child node with the TransformFlags of its - * siblings. - */ - function aggregateTransformFlagsForChildNode(transformFlags: TransformFlags, node: Node): TransformFlags { - return transformFlags | aggregateTransformFlagsForNode(node); - } - - function aggregateTransformFlagsForChildNodes(transformFlags: TransformFlags, nodes: NodeArray): TransformFlags { - return transformFlags | aggregateTransformFlagsForNodeArray(nodes); - } } diff --git a/src/services/codefixes/inferFromUsage.ts b/src/services/codefixes/inferFromUsage.ts index 3eeb907f57c..a1b8fae094b 100644 --- a/src/services/codefixes/inferFromUsage.ts +++ b/src/services/codefixes/inferFromUsage.ts @@ -107,7 +107,7 @@ namespace ts.codefix { } function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, token: Node, errorCode: number, program: Program, cancellationToken: CancellationToken, markSeen: NodeSeenTracker, host: LanguageServiceHost): Declaration | undefined { - if (!isParameterPropertyModifier(token.kind) && token.kind !== SyntaxKind.Identifier && token.kind !== SyntaxKind.DotDotDotToken && token.kind !== SyntaxKind.ThisKeyword) { + if (!isParameterPropertyModifierKind(token.kind) && token.kind !== SyntaxKind.Identifier && token.kind !== SyntaxKind.DotDotDotToken && token.kind !== SyntaxKind.ThisKeyword) { return undefined; } diff --git a/src/services/completions.ts b/src/services/completions.ts index bf7ff041f2d..c44c02dcb71 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1620,7 +1620,7 @@ namespace ts.Completions { function isConstructorParameterCompletion(node: Node): boolean { return !!node.parent && isParameter(node.parent) && isConstructorDeclaration(node.parent.parent) - && (isParameterPropertyModifier(node.kind) || isDeclarationName(node)); + && (isParameterPropertyModifierKind(node.kind) || isDeclarationName(node)); } /** @@ -1823,7 +1823,7 @@ namespace ts.Completions { // - its name of the parameter and not being edited // eg. constructor(a |<- this shouldnt show completion if (!isIdentifier(contextToken) || - isParameterPropertyModifier(keywordForNode(contextToken)) || + isParameterPropertyModifierKind(keywordForNode(contextToken)) || isCurrentlyEditingNode(contextToken)) { return false; } @@ -2073,7 +2073,7 @@ namespace ts.Completions { case KeywordCompletionFilters.InterfaceElementKeywords: return isInterfaceOrTypeLiteralCompletionKeyword(kind); case KeywordCompletionFilters.ConstructorParameterKeywords: - return isParameterPropertyModifier(kind); + return isParameterPropertyModifierKind(kind); case KeywordCompletionFilters.TypeAssertionKeywords: return isTypeKeyword(kind) || kind === SyntaxKind.ConstKeyword; case KeywordCompletionFilters.TypeKeywords: diff --git a/src/testRunner/parallel/host.ts b/src/testRunner/parallel/host.ts index 50b18e39ac1..5606e59ed7d 100644 --- a/src/testRunner/parallel/host.ts +++ b/src/testRunner/parallel/host.ts @@ -309,8 +309,12 @@ namespace Harness.Parallel.Host { }); worker.process.on("exit", (code, _signal) => { if (code !== 0) { - console.error(`Test worker process exited with nonzero exit code! Output: - ${worker.accumulatedOutput}`); + if (worker.currentTasks) { + console.error(`Test worker process exited with nonzero exit code while processing '${worker.currentTasks[0].file}'! Output:\n ${worker.accumulatedOutput}`); + } + else { + console.error(`Test worker process exited with nonzero exit code! Output:\n ${worker.accumulatedOutput}`); + } return process.exit(2); } }); diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.keyword2.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.keyword2.json index 4259856647a..f4acddf47c9 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.keyword2.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.keyword2.json @@ -1,8 +1,16 @@ { - "kind": "NullKeyword", + "kind": "LiteralType", "pos": 1, "end": 5, "flags": "JSDoc", "modifierFlagsCache": 0, - "transformFlags": 0 + "transformFlags": 0, + "literal": { + "kind": "NullKeyword", + "pos": 1, + "end": 5, + "flags": "JSDoc", + "modifierFlagsCache": 0, + "transformFlags": 0 + } } \ No newline at end of file