diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index d1101ad57e..9c1e0cfe4b 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1962,16 +1962,20 @@ namespace ts { transformFlags = TransformFlags.AssertTypeScript; } } + break; case SyntaxKind.ExpressionStatement: - // if (node.flags & NodeFlags.Generated) { - // let expression = (node).expression; - // if (expression.kind === SyntaxKind.CallExpression - // && (expression).expression.kind === SyntaxKind.SuperKeyword) { - // transformFlags |= TransformFlags.AssertES6; - // } - // } + if (nodeIsSynthesized(node)) { + const expression = (node).expression; + if (nodeIsSynthesized(expression) + && isCallExpression(expression) + && expression.expression.kind === SyntaxKind.SuperKeyword) { + // A synthesized call to `super` should be transformed to a cleaner emit + // when transpiling to ES5/3. + transformFlags |= TransformFlags.AssertES6; + } + } break; @@ -2082,17 +2086,16 @@ namespace ts { case SyntaxKind.VariableDeclarationList: // If a VariableDeclarationList is `let` or `const`, then it is ES6 syntax. - if (node.flags & NodeFlags.Let - || node.flags & NodeFlags.Const) { + if (node.flags & NodeFlags.BlockScoped) { transformFlags |= TransformFlags.AssertES6; } break; case SyntaxKind.VariableStatement: - // If a VariableStatement is exported, then it is ES6 syntax. + // If a VariableStatement is exported, then it is either ES6 or TypeScript syntax. if (node.flags & NodeFlags.Export) { - transformFlags |= TransformFlags.AssertES6; + transformFlags |= TransformFlags.AssertES6 | TransformFlags.AssertTypeScript; } break; @@ -2114,13 +2117,13 @@ namespace ts { break; case SyntaxKind.HeritageClause: - // An `extends` HertiageClause is ES6 syntax. if ((node).token === SyntaxKind.ExtendsKeyword) { + // An `extends` HeritageClause is ES6 syntax. transformFlags |= TransformFlags.AssertES6; } - - // An `implements` HeritageClause is TypeScript syntax. - else if ((node).token === SyntaxKind.ImplementsKeyword) { + else { + // An `implements` HeritageClause is TypeScript syntax. + Debug.assert((node).token === SyntaxKind.ImplementsKeyword); transformFlags |= TransformFlags.AssertTypeScript; } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 0d225184a4..992c46fdcf 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -6,33 +6,91 @@ namespace ts { let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; let SourceFileConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; - function createNode(kind: SyntaxKind, location?: TextRange): Node { + function createNode(kind: SyntaxKind, location?: TextRange, flags?: NodeFlags): Node { const ConstructorForKind = kind === SyntaxKind.SourceFile ? (SourceFileConstructor || (SourceFileConstructor = objectAllocator.getSourceFileConstructor())) : (NodeConstructor || (NodeConstructor = objectAllocator.getNodeConstructor())); - return location + const node = location ? new ConstructorForKind(kind, location.pos, location.end) : new ConstructorForKind(kind, /*pos*/ -1, /*end*/ -1); + + if (flags) { + node.flags = flags; + } + + return node; } - export function createNodeArray(elements?: T[], pos?: number, end?: number): NodeArray { - const array = >(elements || []); - array.pos = pos; - array.end = end; + export function createNodeArray(elements?: T[], location?: TextRange): NodeArray { + if (elements !== undefined) { + if (isNodeArray(elements)) { + return elements; + } + } + else { + elements = []; + } + + const array = >elements; + if (location !== undefined) { + array.pos = location.pos; + array.end = location.end; + } + else { + array.pos = -1; + array.end = -1; + } + array.arrayKind = ArrayKind.NodeArray; return array; } - export function createModifiersArray(elements?: Modifier[], pos?: number, end?: number): ModifiersArray { - const array = (elements || []); - array.pos = pos; - array.end = end; + export function createModifiersArray(elements?: Modifier[], location?: TextRange): ModifiersArray { + let flags: NodeFlags; + if (elements !== undefined) { + if (isModifiersArray(elements)) { + return elements; + } + + flags = 0; + for (const modifier of elements) { + flags |= modifierToFlag(modifier.kind); + } + } + else { + elements = []; + flags = 0; + } + + const array = elements; + if (location !== undefined) { + array.pos = location.pos; + array.end = location.end; + } + else { + array.pos = -1; + array.end = -1; + } + array.arrayKind = ArrayKind.ModifiersArray; - array.flags = 0; + array.flags = flags; return array; } + export function setModifiers(node: T, modifiers: Modifier[]) { + if (modifiers !== undefined) { + const array = createModifiersArray(modifiers); + node.modifiers = array; + node.flags |= array.flags; + } + else { + node.modifiers = undefined; + } + + return node; + } + export function createSynthesizedNode(kind: SyntaxKind, startsOnNewLine?: boolean): Node { const node = createNode(kind, /*location*/ undefined); node.startsOnNewLine = startsOnNewLine; @@ -40,11 +98,11 @@ namespace ts { } export function createSynthesizedNodeArray(elements?: T[]): NodeArray { - return createNodeArray(elements, /*pos*/ -1, /*end*/ -1); + return createNodeArray(elements, /*location*/ undefined); } export function createSynthesizedModifiersArray(elements?: Modifier[]): ModifiersArray { - return createModifiersArray(elements, /*pos*/ -1, /*end*/ -1); + return createModifiersArray(elements, /*location*/ undefined); } /** diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 0f1d27a28f..61bebbdedc 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1284,17 +1284,7 @@ namespace ts { return false; } - export function isLiteralKind(kind: SyntaxKind): boolean { - return SyntaxKind.FirstLiteralToken <= kind && kind <= SyntaxKind.LastLiteralToken; - } - export function isTextualLiteralKind(kind: SyntaxKind): boolean { - return kind === SyntaxKind.StringLiteral || kind === SyntaxKind.NoSubstitutionTemplateLiteral; - } - - export function isTemplateLiteralKind(kind: SyntaxKind): boolean { - return SyntaxKind.FirstTemplateToken <= kind && kind <= SyntaxKind.LastTemplateToken; - } export function isNodeDescendentOf(node: Node, ancestor: Node): boolean { while (node) { @@ -1563,8 +1553,17 @@ namespace ts { return node; } - export function nodeStartsNewLexicalEnvironment(n: Node): boolean { - return isFunctionLike(n) || n.kind === SyntaxKind.ModuleDeclaration || n.kind === SyntaxKind.SourceFile; + export function nodeStartsNewLexicalEnvironment(node: Node): boolean { + const kind = node.kind; + return kind === SyntaxKind.Constructor + || kind === SyntaxKind.FunctionExpression + || kind === SyntaxKind.FunctionDeclaration + || kind === SyntaxKind.ArrowFunction + || kind === SyntaxKind.MethodDeclaration + || kind === SyntaxKind.GetAccessor + || kind === SyntaxKind.SetAccessor + || kind === SyntaxKind.ModuleDeclaration + || kind === SyntaxKind.SourceFile; } /** @@ -2659,23 +2658,111 @@ namespace ts { // All node tests in the following list should *not* reference parent pointers so that // they may be used with transformations. - export function isPropertyAccessExpression(node: Node): node is PropertyAccessExpression { - return node.kind === SyntaxKind.PropertyAccessExpression; + // Node Arrays + + export function isNodeArray(array: T[]): array is NodeArray { + return (>array).arrayKind === ArrayKind.NodeArray; } - export function isElementAccessExpression(node: Node): node is ElementAccessExpression { - return node.kind === SyntaxKind.ElementAccessExpression; + export function isModifiersArray(array: Modifier[]): array is ModifiersArray { + return (array).arrayKind === ArrayKind.ModifiersArray; } - function isBindingPatternKind(kind: SyntaxKind) { - return kind === SyntaxKind.ArrayBindingPattern - || kind === SyntaxKind.ObjectBindingPattern; + // Literals + + export function isLiteralKind(kind: SyntaxKind): boolean { + return SyntaxKind.FirstLiteralToken <= kind && kind <= SyntaxKind.LastLiteralToken; } - export function isBindingPattern(node: Node): node is BindingPattern { - return node && isBindingPatternKind(node.kind); + export function isTextualLiteralKind(kind: SyntaxKind): boolean { + return kind === SyntaxKind.StringLiteral || kind === SyntaxKind.NoSubstitutionTemplateLiteral; } + export function isLiteralExpression(node: Node): node is LiteralExpression { + return isLiteralKind(node.kind); + } + + // Pseudo-literals + + export function isTemplateLiteralKind(kind: SyntaxKind): boolean { + return SyntaxKind.FirstTemplateToken <= kind && kind <= SyntaxKind.LastTemplateToken; + } + + function isTemplateLiteralFragmentKind(kind: SyntaxKind) { + return kind === SyntaxKind.TemplateHead + || kind === SyntaxKind.TemplateMiddle + || kind === SyntaxKind.TemplateTail; + } + + export function isTemplateLiteralFragment(node: Node): node is TemplateLiteralFragment { + return isTemplateLiteralFragmentKind(node.kind); + } + + // Identifiers + + export function isIdentifier(node: Node): node is Identifier { + return node.kind === SyntaxKind.Identifier; + } + + // Keywords + + export function isModifier(node: Node): node is Modifier { + return isModifierKind(node.kind); + } + + // Names + + export function isQualifiedName(node: Node): node is QualifiedName { + return node.kind === SyntaxKind.QualifiedName; + } + + export function isComputedPropertyName(node: Node): node is ComputedPropertyName { + return node.kind === SyntaxKind.ComputedPropertyName; + } + + export function isEntityName(node: Node): node is EntityName { + const kind = node.kind; + return kind === SyntaxKind.QualifiedName + || kind === SyntaxKind.Identifier; + } + + export function isPropertyName(node: Node): node is PropertyName { + const kind = node.kind; + return kind === SyntaxKind.Identifier + || kind === SyntaxKind.StringLiteral + || kind === SyntaxKind.NumericLiteral + || kind === SyntaxKind.ComputedPropertyName; + } + + export function isModuleName(node: Node): node is ModuleName { + const kind = node.kind; + return kind === SyntaxKind.Identifier + || kind === SyntaxKind.StringLiteral; + } + + export function isBindingName(node: Node): node is BindingName { + const kind = node.kind; + return kind === SyntaxKind.Identifier + || kind === SyntaxKind.ObjectBindingPattern + || kind === SyntaxKind.ArrayBindingPattern; + } + + // Signature elements + + export function isTypeParameter(node: Node): node is TypeParameterDeclaration { + return node.kind === SyntaxKind.TypeParameter; + } + + export function isParameter(node: Node): node is ParameterDeclaration { + return node.kind === SyntaxKind.Parameter; + } + + export function isDecorator(node: Node): node is Decorator { + return node.kind === SyntaxKind.Decorator; + } + + // Type members + export function isClassElement(node: Node): node is ClassElement { const kind = node.kind; return kind === SyntaxKind.Constructor @@ -2687,37 +2774,80 @@ namespace ts { || kind === SyntaxKind.IndexSignature; } - export function isQualifiedName(node: Node): node is QualifiedName { - return node.kind === SyntaxKind.QualifiedName; + export function isObjectLiteralElement(node: Node): node is ObjectLiteralElement { + const kind = node.kind; + return kind === SyntaxKind.PropertyAssignment + || kind === SyntaxKind.ShorthandPropertyAssignment + || kind === SyntaxKind.MethodDeclaration + || kind === SyntaxKind.GetAccessor + || kind === SyntaxKind.SetAccessor + || kind === SyntaxKind.MissingDeclaration; } - export function isLiteralExpression(node: Node): node is LiteralExpression { - return isLiteralKind(node.kind); + // Type + + function isTypeNodeKind(kind: SyntaxKind) { + return (kind >= SyntaxKind.FirstTypeNode && kind <= SyntaxKind.LastTypeNode) + || kind === SyntaxKind.AnyKeyword + || kind === SyntaxKind.NumberKeyword + || kind === SyntaxKind.BooleanKeyword + || kind === SyntaxKind.StringKeyword + || kind === SyntaxKind.SymbolKeyword + || kind === SyntaxKind.VoidKeyword + || kind === SyntaxKind.ExpressionWithTypeArguments; } - function isEntityNameKind(kind: SyntaxKind) { - return kind === SyntaxKind.QualifiedName - || kind === SyntaxKind.Identifier; + /** + * Node test that determines whether a node is a valid type node. + * This differs from the `isPartOfTypeNode` function which determines whether a node is *part* + * of a TypeNode. + */ + export function isTypeNode(node: Node): node is TypeNode { + return isTypeNodeKind(node.kind); } - export function isEntityName(node: Node): node is EntityName { - return isEntityNameKind(node.kind); + // Binding patterns + + export function isBindingPattern(node: Node): node is BindingPattern { + if (node) { + const kind = node.kind; + return kind === SyntaxKind.ArrayBindingPattern + || kind === SyntaxKind.ObjectBindingPattern; + } + + return false; } - export function isIdentifier(node: Node): node is Identifier { - return node.kind === SyntaxKind.Identifier; + export function isBindingElement(node: Node): node is BindingElement { + return node.kind === SyntaxKind.BindingElement; } - export function isComputedPropertyName(node: Node): node is ComputedPropertyName { - return node.kind === SyntaxKind.ComputedPropertyName; + // Expression + + export function isPropertyAccessExpression(node: Node): node is PropertyAccessExpression { + return node.kind === SyntaxKind.PropertyAccessExpression; + } + + export function isElementAccessExpression(node: Node): node is ElementAccessExpression { + return node.kind === SyntaxKind.ElementAccessExpression; } export function isBinaryExpression(node: Node): node is BinaryExpression { return node.kind === SyntaxKind.BinaryExpression; } - export function isShortHandPropertyAssignment(node: Node): node is ShorthandPropertyAssignment { - return node.kind === SyntaxKind.ShorthandPropertyAssignment; + export function isCallExpression(node: Node): node is CallExpression { + return node.kind === SyntaxKind.CallExpression; + } + + export function isTemplate(node: Node): node is Template { + const kind = node.kind; + return kind === SyntaxKind.TemplateExpression + || kind === SyntaxKind.NoSubstitutionTemplateLiteral; + } + + export function isExpressionWithTypeArguments(node: Node): node is ExpressionWithTypeArguments { + return node.kind === SyntaxKind.ExpressionWithTypeArguments; } function isLeftHandSideExpressionKind(kind: SyntaxKind) { @@ -2744,7 +2874,7 @@ namespace ts { || kind === SyntaxKind.ThisKeyword || kind === SyntaxKind.TrueKeyword || kind === SyntaxKind.SuperKeyword; - } + } export function isLeftHandSideExpression(node: Node): node is LeftHandSideExpression { return isLeftHandSideExpressionKind(node.kind); @@ -2780,32 +2910,70 @@ namespace ts { return isExpressionKind(node.kind); } - export function isDecorator(node: Node): node is Decorator { - return node.kind === SyntaxKind.Decorator; + // Misc + + export function isTemplateSpan(node: Node): node is TemplateSpan { + return node.kind === SyntaxKind.TemplateSpan; } - export function isModifier(node: Node): node is Modifier { - return isModifierKind(node.kind); + // Element + + export function isBlock(node: Node): node is Block { + return node.kind === SyntaxKind.Block; } - function isTypeNodeKind(kind: SyntaxKind) { - return (kind >= SyntaxKind.FirstTypeNode && kind <= SyntaxKind.LastTypeNode) - || kind === SyntaxKind.AnyKeyword - || kind === SyntaxKind.NumberKeyword - || kind === SyntaxKind.BooleanKeyword - || kind === SyntaxKind.StringKeyword - || kind === SyntaxKind.SymbolKeyword - || kind === SyntaxKind.VoidKeyword - || kind === SyntaxKind.ExpressionWithTypeArguments; + export function isConciseBody(node: Node): node is ConciseBody { + return isBlock(node) + || isExpression(node); } - /** - * Node test that determines whether a node is a valid type node. - * This differs from the `isPartOfTypeNode` function which determines whether a node is *part* - * of a TypeNode. - */ - export function isTypeNode(node: Node): node is TypeNode { - return isTypeNodeKind(node.kind); + export function isFunctionBody(node: Node): node is FunctionBody { + return isBlock(node); + } + + export function isForInitializer(node: Node): node is ForInitializer { + return isVariableDeclarationList(node) + || isExpression(node); + } + + export function isVariableDeclaration(node: Node): node is VariableDeclaration { + return node.kind === SyntaxKind.VariableDeclaration; + } + + export function isVariableDeclarationList(node: Node): node is VariableDeclarationList { + return node.kind === SyntaxKind.VariableDeclarationList; + } + + export function isCaseBlock(node: Node): node is CaseBlock { + return node.kind === SyntaxKind.CaseBlock; + } + + export function isModuleBody(node: Node): node is ModuleBody { + const kind = node.kind; + return kind === SyntaxKind.ModuleBlock + || kind === SyntaxKind.ModuleDeclaration; + } + + export function isImportClause(node: Node): node is ImportClause { + return node.kind === SyntaxKind.ImportClause; + } + + export function isNamedImportBindings(node: Node): node is NamedImportBindings { + const kind = node.kind; + return kind === SyntaxKind.NamedImports + || kind === SyntaxKind.NamespaceImport; + } + + export function isImportSpecifier(node: Node): node is ImportSpecifier { + return node.kind === SyntaxKind.ImportSpecifier; + } + + export function isNamedExports(node: Node): node is NamedExports { + return node.kind === SyntaxKind.NamedExports; + } + + export function isExportSpecifier(node: Node): node is ExportSpecifier { + return node.kind === SyntaxKind.ExportSpecifier; } function isDeclarationKind(kind: SyntaxKind) { @@ -2839,10 +3007,6 @@ namespace ts { || kind === SyntaxKind.VariableDeclaration; } - export function isDeclaration(node: Node): node is Declaration { - return isDeclarationKind(node.kind); - } - function isDeclarationStatementKind(kind: SyntaxKind) { return kind === SyntaxKind.FunctionDeclaration || kind === SyntaxKind.MissingDeclaration @@ -2856,10 +3020,6 @@ namespace ts { || kind === SyntaxKind.ExportAssignment; } - export function isDeclarationStatement(node: Node): node is DeclarationStatement { - return isDeclarationStatementKind(node.kind); - } - function isStatementKindButNotDeclarationKind(kind: SyntaxKind) { return kind === SyntaxKind.BreakStatement || kind === SyntaxKind.ContinueStatement @@ -2881,6 +3041,14 @@ namespace ts { || kind === SyntaxKind.WithStatement; } + export function isDeclaration(node: Node): node is Declaration { + return isDeclarationKind(node.kind); + } + + export function isDeclarationStatement(node: Node): node is DeclarationStatement { + return isDeclarationStatementKind(node.kind); + } + /** * Determines whether the node is a statement that is not also a declaration */ @@ -2888,171 +3056,22 @@ namespace ts { return isStatementKindButNotDeclarationKind(node.kind); } - function isStatementKind(kind: SyntaxKind) { + export function isStatement(node: Node): node is Statement { + const kind = node.kind; return isStatementKindButNotDeclarationKind(kind) || isDeclarationStatementKind(kind); } - export function isStatement(node: Node): node is Statement { - return isStatementKind(node.kind); - } - - function isPropertyNameKind(kind: SyntaxKind) { - return kind === SyntaxKind.Identifier - || kind === SyntaxKind.StringLiteral - || kind === SyntaxKind.NumericLiteral - || kind === SyntaxKind.ComputedPropertyName; - } - - export function isPropertyName(node: Node): node is PropertyName { - return isPropertyNameKind(node.kind); - } - - function isConciseBodyKind(kind: SyntaxKind) { - return kind === SyntaxKind.Block - || isExpressionKind(kind); - } - - export function isConciseBody(node: Node): node is ConciseBody { - return isConciseBodyKind(node.kind); - } - - export function isTypeParameter(node: Node): node is TypeParameterDeclaration { - return node.kind === SyntaxKind.TypeParameter; - } - - export function isParameter(node: Node): node is ParameterDeclaration { - return node.kind === SyntaxKind.Parameter; - } - - export function isBindingElement(node: Node): node is BindingElement { - return node.kind === SyntaxKind.BindingElement; - } - - function isObjectLiteralElementKind(kind: SyntaxKind) { - return kind === SyntaxKind.PropertyAssignment - || kind === SyntaxKind.ShorthandPropertyAssignment - || kind === SyntaxKind.MethodDeclaration - || kind === SyntaxKind.GetAccessor - || kind === SyntaxKind.SetAccessor - || kind === SyntaxKind.MissingDeclaration; - } - - export function isObjectLiteralElement(node: Node): node is ObjectLiteralElement { - return isObjectLiteralElementKind(node.kind); - } - - function isTemplateKind(kind: SyntaxKind) { - return kind === SyntaxKind.TemplateExpression - || kind === SyntaxKind.NoSubstitutionTemplateLiteral; - } - - export function isTemplate(node: Node): node is Template { - return isTemplateKind(node.kind); - } - - function isTemplateLiteralFragmentKind(kind: SyntaxKind) { - return kind === SyntaxKind.TemplateHead - || kind === SyntaxKind.TemplateMiddle - || kind === SyntaxKind.TemplateTail; - } - - export function isTemplateLiteralFragment(node: Node): node is TemplateLiteralFragment { - return isTemplateLiteralFragmentKind(node.kind); - } - - export function isTemplateSpan(node: Node): node is TemplateSpan { - return node.kind === SyntaxKind.TemplateSpan; - } - - export function isHeritageClause(node: Node): node is HeritageClause { - return node.kind === SyntaxKind.HeritageClause; - } - - export function isVariableDeclarationList(node: Node): node is VariableDeclarationList { - return node.kind === SyntaxKind.VariableDeclarationList; - } - - function isForInitializerKind(kind: SyntaxKind) { - return kind === SyntaxKind.VariableDeclarationList - || isExpressionKind(kind); - } - - export function isForInitializer(node: Node): node is ForInitializer { - return isForInitializerKind(node.kind); - } - - export function isCaseBlock(node: Node): node is CaseBlock { - return node.kind === SyntaxKind.CaseBlock; - } - - export function isBlock(node: Node): node is Block { - return node.kind === SyntaxKind.Block; - } - - export function isCatchClause(node: Node): node is CatchClause { - return node.kind === SyntaxKind.CatchClause; - } - - export function isVariableDeclaration(node: Node): node is VariableDeclaration { - return node.kind === SyntaxKind.VariableDeclaration; - } - - export function isEnumMember(node: Node): node is EnumMember { - return node.kind === SyntaxKind.EnumMember; - } - - function isModuleNameKind(kind: SyntaxKind) { - return kind === SyntaxKind.Identifier - || kind === SyntaxKind.StringLiteral; - } - - export function isModuleName(node: Node): node is ModuleName { - return isModuleNameKind(node.kind); - } - - function isCaseOrDefaultClauseKind(kind: SyntaxKind) { - return kind === SyntaxKind.CaseClause - || kind === SyntaxKind.DefaultClause; - } - - export function isCaseOrDefaultClause(node: Node): node is CaseOrDefaultClause { - return isCaseOrDefaultClauseKind(node.kind); - } - - function isModuleReferenceKind(kind: SyntaxKind) { - return kind === SyntaxKind.ExternalModuleReference - || isEntityNameKind(kind); - } + // Module references export function isModuleReference(node: Node): node is ModuleReference { - return isModuleReferenceKind(node.kind); + const kind = node.kind; + return kind === SyntaxKind.ExternalModuleReference + || kind === SyntaxKind.QualifiedName + || kind === SyntaxKind.Identifier; } - export function isImportClause(node: Node): node is ImportClause { - return node.kind === SyntaxKind.ImportClause; - } - - function isNamedImportBindingsKind(kind: SyntaxKind) { - return kind === SyntaxKind.NamedImports - || kind === SyntaxKind.NamespaceImport; - } - - export function isNamedImportBindings(node: Node): node is NamedImportBindings { - return isNamedImportBindingsKind(node.kind); - } - - export function isImportSpecifier(node: Node): node is ImportSpecifier { - return node.kind === SyntaxKind.ImportSpecifier; - } - - export function isNamedExports(node: Node): node is NamedExports { - return node.kind === SyntaxKind.NamedExports; - } - - export function isExportSpecifier(node: Node): node is ExportSpecifier { - return node.kind === SyntaxKind.ExportSpecifier; - } + // JSX export function isJsxOpeningElement(node: Node): node is JsxOpeningElement { return node.kind === SyntaxKind.JsxOpeningElement; @@ -3062,55 +3081,56 @@ namespace ts { return node.kind === SyntaxKind.JsxClosingElement; } - function isJsxChildKind(kind: SyntaxKind) { + export function isJsxChild(node: Node): node is JsxChild { + const kind = node.kind; return kind === SyntaxKind.JsxElement || kind === SyntaxKind.JsxExpression || kind === SyntaxKind.JsxSelfClosingElement || kind === SyntaxKind.JsxText; } - export function isJsxChild(node: Node): node is JsxChild { - return isJsxChildKind(node.kind); - } - - function isJsxAttributeLikeKind(kind: SyntaxKind) { + export function isJsxAttributeLike(node: Node): node is JsxAttributeLike { + const kind = node.kind; return kind === SyntaxKind.JsxAttribute || kind === SyntaxKind.JsxSpreadAttribute; } - export function isJsxAttributeLike(node: Node): node is JsxAttributeLike { - return isJsxAttributeLikeKind(node.kind); + // Clauses + + export function isCaseOrDefaultClause(node: Node): node is CaseOrDefaultClause { + const kind = node.kind; + return kind === SyntaxKind.CaseClause + || kind === SyntaxKind.DefaultClause; } - export function isExpressionWithTypeArguments(node: Node): node is ExpressionWithTypeArguments { - return node.kind === SyntaxKind.ExpressionWithTypeArguments; + export function isHeritageClause(node: Node): node is HeritageClause { + return node.kind === SyntaxKind.HeritageClause; } - function isModuleBodyKind(kind: SyntaxKind) { - return kind === SyntaxKind.ModuleBlock - || kind === SyntaxKind.ModuleDeclaration; + export function isCatchClause(node: Node): node is CatchClause { + return node.kind === SyntaxKind.CatchClause; } - export function isModuleBody(node: Node): node is ModuleBody { - return isModuleBodyKind(node.kind); + + // Property assignments + + export function isShortHandPropertyAssignment(node: Node): node is ShorthandPropertyAssignment { + const kind = node.kind; + return kind === SyntaxKind.ShorthandPropertyAssignment; } - function isBindingNameKind(kind: SyntaxKind) { - return kind === SyntaxKind.Identifier - || isBindingPatternKind(kind); + // Enum + + export function isEnumMember(node: Node): node is EnumMember { + return node.kind === SyntaxKind.EnumMember; } - export function isBindingName(node: Node): node is BindingName { - return isBindingNameKind(node.kind); - } + + // Synthesized export function isNodeArrayNode(node: Node): node is NodeArrayNode { return node.kind === SyntaxKind.NodeArrayNode; } - - export function isModifiersArray(array: NodeArray): array is ModifiersArray { - return array.arrayKind === ArrayKind.ModifiersArray; - } } namespace ts { diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 6e63524a23..59a95e653a 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -2,6 +2,7 @@ /* @internal */ namespace ts { + export type OneOrMore = T | NodeArrayNode; /** * Describes an edge of a Node, used when traversing a syntax tree. */ @@ -469,10 +470,10 @@ namespace ts { * @param node The Node to visit. * @param visitor The callback used to visit the Node. * @param test A callback to execute to verify the Node is valid. - * @param lift A callback to execute to lift a NodeArrayNode into a valid Node. - * @param optional A value indicating whether the Node is optional. + * @param optional An optional value indicating whether the Node is itself optional. + * @param lift An optional callback to execute to lift a NodeArrayNode into a valid Node. */ - export function visitNode(node: T, visitor: (node: Node) => Node, test?: (node: Node) => boolean, lift?: (node: NodeArray) => T, optional?: boolean): T { + export function visitNode(node: T, visitor: (node: Node) => Node, test: (node: Node) => boolean, optional?: boolean, lift?: (node: NodeArray) => T): T { if (node === undefined) { return undefined; } @@ -499,55 +500,56 @@ namespace ts { * @param nodes The NodeArray to visit. * @param visitor The callback used to visit a Node. * @param test A node test to execute for each node. + * @param start An optional value indicating the starting offset at which to start visiting. + * @param count An optional value indicating the maximum number of nodes to visit. */ - export function visitNodes>(nodes: TArray, visitor: (node: Node) => Node, test?: (node: Node) => boolean): TArray { + export function visitNodes>(nodes: TArray, visitor: (node: Node) => Node, test: (node: Node) => boolean, start?: number, count?: number): TArray { if (nodes === undefined) { return undefined; } - let updated: NodeArray | ModifiersArray; - for (let i = 0, len = nodes.length; i < len; i++) { - const node = nodes[i]; - if (node === undefined) { - continue; - } + let updated: T[]; - const visited = visitor(node); + // Ensure start and count have valid values + const length = nodes.length; + if (start === undefined || start < 0) { + start = 0; + } + + if (count === undefined || count > length - start) { + count = length - start; + } + + // If we are not visiting all of the original nodes, we must always create a new array. + if (start > 0 || count < length) { + updated = []; + } + + // Visit each original node. + for (let i = 0; i < count; i++) { + const node = nodes[i + start]; + const visited = node && >visitor(node); if (updated !== undefined || visited === undefined || visited !== node) { if (updated === undefined) { - updated = isModifiersArray(nodes) - ? createModifiersArray(nodes.slice(0, i), nodes.pos, nodes.end) - : createNodeArray(nodes.slice(0, i), nodes.pos, nodes.end); + // Ensure we have a copy of `nodes`, up to the current index. + updated = nodes.slice(0, i); } - if (visited === undefined) { - continue; + if (visited !== node) { + aggregateTransformFlags(visited); } - if (isNodeArrayNode(visited)) { - spreadNodeArrayNode(visited, updated, test); - } - else if (visited !== undefined) { - Debug.assert(test === undefined || test(visited), "Wrong node type after visit."); - if (visited !== node) { - aggregateTransformFlags(visited); - } - - updated.push(visited); - } + addNode(updated, visited, test); } } - if (updated && isModifiersArray(updated)) { - let flags: NodeFlags = 0; - for (const node of updated) { - flags |= modifierToFlag(node.kind); - } - - updated.flags = flags; + if (updated !== undefined) { + return (isModifiersArray(nodes) + ? createModifiersArray(updated, nodes) + : createNodeArray(updated, nodes)); } - return updated || nodes; + return nodes; } /** @@ -555,36 +557,40 @@ namespace ts { * * @param node The Node whose children will be visited. * @param visitor The callback used to visit each child. - * @param environment An optional lexical environment context for the visitor. + * @param context A lexical environment context for the visitor. */ - export function visitEachChild(node: T, visitor: (node: Node) => Node, environment?: LexicalEnvironment): T { + export function visitEachChild(node: T, visitor: (node: Node) => Node, context: LexicalEnvironment): T; + export function visitEachChild(node: T & Map, visitor: (node: Node) => Node, context: LexicalEnvironment): T { if (node === undefined) { return undefined; } - const isNewLexicalEnvironment = environment !== undefined && nodeStartsNewLexicalEnvironment(node); + let updated: T & Map; + + // If this node starts a new lexical environment, start a new lexical environment on the context. + const isNewLexicalEnvironment = nodeStartsNewLexicalEnvironment(node); if (isNewLexicalEnvironment) { - environment.startLexicalEnvironment(); + context.startLexicalEnvironment(); } - let modifiers: NodeFlags; - let updated: T & Map; const edgeTraversalPath = nodeEdgeTraversalMap[node.kind]; if (edgeTraversalPath) { for (const edge of edgeTraversalPath) { - const value = (>node)[edge.name]; + const value = >node[edge.name]; if (value !== undefined) { const visited = visitEdge(edge, value, visitor); if (updated !== undefined || visited !== value) { if (updated === undefined) { - updated = cloneNode(node, /*location*/ undefined, node.flags & ~NodeFlags.Modifier, /*parent*/ undefined, /*original*/ node); + updated = cloneNode(node, /*location*/ node, node.flags & ~NodeFlags.Modifier, /*parent*/ undefined, /*original*/ node); } - updated[edge.name] = visited; - } - - if (visited && isArray(visited) && isModifiersArray(visited)) { - modifiers = visited.flags; + if (visited && isArray(visited) && isModifiersArray(visited)) { + updated[edge.name] = visited; + updated.flags |= visited.flags; + } + else { + updated[edge.name] = visited; + } } } } @@ -593,14 +599,11 @@ namespace ts { if (updated === undefined) { updated = node; } - else if (modifiers) { - updated.flags |= modifiers; - } if (isNewLexicalEnvironment) { - const declarations = environment.endLexicalEnvironment(); + const declarations = context.endLexicalEnvironment(); if (declarations !== undefined && declarations.length > 0) { - updated = mergeLexicalEnvironment(updated, declarations, /*nodeIsMutable*/ updated !== node); + updated = mergeLexicalEnvironment(updated, declarations); } } @@ -620,104 +623,177 @@ namespace ts { */ function visitEdge(edge: NodeEdge, value: Node | NodeArray, visitor: (node: Node) => Node) { return isArray(value) - ? visitNodes(>value, visitor, edge.test) - : visitNode(value, visitor, edge.test, edge.lift, edge.optional); + ? visitNodes(>value, visitor, edge.test, /*start*/ undefined, /*count*/ undefined) + : visitNode(value, visitor, edge.test, edge.optional, edge.lift); } /** - * Spreads a NodeArrayNode into a NodeArray. + * Appends a node to an array. * - * @param source The source NodeArrayNode. - * @param dest The destination NodeArray. + * @param to The destination array. + * @param from The source Node or NodeArrayNode. * @param test The node test used to validate each node. */ - function spreadNodeArrayNode(source: NodeArrayNode, dest: NodeArray, test: (node: Node) => boolean) { - for (const element of source.nodes) { - if (element === undefined) { - continue; + export function addNode(to: T[], from: OneOrMore, test?: (node: Node) => boolean) { + if (to !== undefined && from !== undefined) { + if (isNodeArrayNode(from)) { + addNodes(to, from.nodes, test); } + else { + Debug.assert(test === undefined || test(from), "Wrong node type after visit."); + to.push(from); + } + } + } - Debug.assert(test === undefined || test(element), "Wrong node type after visit."); - dest.push(element); + /** + * Appends an array of nodes to an array. + * + * @param to The destination NodeArray. + * @param from The source array of Node or NodeArrayNode. + * @param test The node test used to validate each node. + */ + export function addNodes(to: T[], from: OneOrMore[], test?: (node: Node) => boolean) { + if (to !== undefined && from !== undefined) { + for (const node of from) { + addNode(to, node, test); + } } } /** * Merge generated declarations of a lexical environment. + * + * @param node The source node. + * @param declarations The generated lexical declarations. */ - function mergeLexicalEnvironment(node: Node, declarations: Statement[], nodeIsMutable: boolean) { - const mutableNode = nodeIsMutable ? node : cloneNode(node, /*location*/ node, node.flags, /*parent*/ undefined, /*original*/ node); + function mergeLexicalEnvironment(node: Node, declarations: Statement[]): Node { switch (node.kind) { case SyntaxKind.SourceFile: - mergeSourceFileLexicalEnvironment(mutableNode, declarations); - break; + return mergeSourceFileLexicalEnvironment(node, declarations); case SyntaxKind.ModuleDeclaration: - mergeModuleDeclarationLexicalEnvironment(mutableNode, declarations); - break; + return mergeModuleDeclarationLexicalEnvironment(node, declarations); case SyntaxKind.FunctionDeclaration: case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: case SyntaxKind.MethodDeclaration: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: case SyntaxKind.Constructor: - mergeFunctionLikeLexicalEnvironment(mutableNode, declarations); - break; - - case SyntaxKind.ModuleBlock: - case SyntaxKind.Block: - mergeBlockLexicalEnvironment(mutableNode, declarations); - break; + case SyntaxKind.ArrowFunction: + return mergeFunctionLikeLexicalEnvironment(node, declarations); } - return mutableNode; + Debug.fail("Node is not a valid lexical environment."); } /** * Merge generated declarations of a lexical environment into a SourceFile. + * + * @param node The SourceFile node. + * @param declarations The generated lexical declarations. */ - function mergeSourceFileLexicalEnvironment(node: SourceFile, declarations: Statement[]) { - node.statements = mergeStatements(node.statements, declarations); + export function mergeSourceFileLexicalEnvironment(node: SourceFile, declarations: Statement[]) { + if (declarations !== undefined && declarations.length) { + const mutableNode = cloneNode(node, /*location*/ node, node.flags, /*parent*/ undefined, /*original*/ node); + mutableNode.statements = mergeStatements(mutableNode.statements, declarations); + return mutableNode; + } + + return node; } /** * Merge generated declarations of a lexical environment into a ModuleDeclaration. + * + * @param node The ModuleDeclaration node. + * @param declarations The generated lexical declarations. */ - function mergeModuleDeclarationLexicalEnvironment(node: ModuleDeclaration, declarations: Statement[]) { + export function mergeModuleDeclarationLexicalEnvironment(node: ModuleDeclaration, declarations: Statement[]) { Debug.assert(node.body.kind === SyntaxKind.ModuleBlock); - node.body = mergeLexicalEnvironment(node.body, declarations, /*nodeIsMutable*/ false); + if (declarations !== undefined && declarations.length) { + const mutableNode = cloneNode(node, /*location*/ node, node.flags, /*parent*/ undefined, /*original*/ node); + mutableNode.body = mergeBlockLexicalEnvironment(node.body, declarations); + return mutableNode; + } + + return node; } /** * Merge generated declarations of a lexical environment into a FunctionLikeDeclaration. + * + * @param node The function-like node. + * @param declarations The generated lexical declarations. */ function mergeFunctionLikeLexicalEnvironment(node: FunctionLikeDeclaration, declarations: Statement[]) { Debug.assert(node.body !== undefined); - if (node.body.kind === SyntaxKind.Block) { - node.body = mergeLexicalEnvironment(node.body, declarations, /*nodeIsMutable*/ false); + if (declarations !== undefined && declarations.length) { + const mutableNode = cloneNode(node, /*location*/ node, node.flags, /*parent*/ undefined, /*original*/ node); + mutableNode.body = mergeConciseBodyLexicalEnvironment(mutableNode.body, declarations); + return mutableNode; } - else { - node.body = createBlock([ - createReturn(node.body), - ...declarations - ]); + + return node; + } + + /** + * Merges generated lexical declarations into the FunctionBody of a non-arrow function-like declaration. + * + * @param node The ConciseBody of an arrow function. + * @param declarations The lexical declarations to merge. + */ + export function mergeFunctionBodyLexicalEnvironment(body: FunctionBody, declarations: Statement[]) { + if (declarations !== undefined && declarations.length > 0) { + return mergeBlockLexicalEnvironment(body, declarations); } + + return body; + } + + /** + * Merges generated lexical declarations into the ConciseBody of an ArrowFunction. + * + * @param node The ConciseBody of an arrow function. + * @param declarations The lexical declarations to merge. + */ + export function mergeConciseBodyLexicalEnvironment(body: ConciseBody, declarations: Statement[]) { + if (declarations !== undefined && declarations.length > 0) { + if (isBlock(body)) { + return mergeBlockLexicalEnvironment(body, declarations); + } + else { + return createBlock([ + createReturn(body), + ...declarations + ]); + } + } + + return body; } /** * Merge generated declarations of a lexical environment into a FunctionBody or ModuleBlock. + * + * @param node The block into which to merge lexical declarations. + * @param declarations The lexical declarations to merge. */ - function mergeBlockLexicalEnvironment(node: FunctionBody | ModuleBlock, declarations: Statement[]) { - node.statements = mergeStatements(node.statements, declarations); + function mergeBlockLexicalEnvironment(node: T, declarations: Statement[]) { + const mutableNode = cloneNode(node, /*location*/ node, node.flags, /*parent*/ undefined, /*original*/ node); + mutableNode.statements = mergeStatements(node.statements, declarations); + return mutableNode; } /** * Merge generated declarations of a lexical environment into a NodeArray of Statement. + * + * @param statements The node array to concatentate with the supplied lexical declarations. + * @param declarations The lexical declarations to merge. */ function mergeStatements(statements: NodeArray, declarations: Statement[]) { - return createNodeArray(statements.concat(declarations), statements.pos, statements.end); + return createNodeArray(concatenate(statements, declarations), /*location*/ statements); } /**