mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-29 16:29:19 -05:00
Added visitor API
This commit is contained in:
@@ -40,6 +40,8 @@ var compilerSources = [
|
||||
"utilities.ts",
|
||||
"binder.ts",
|
||||
"checker.ts",
|
||||
"factory.ts",
|
||||
"visitor.ts",
|
||||
"sourcemap.ts",
|
||||
"declarationEmitter.ts",
|
||||
"emitter.ts",
|
||||
@@ -60,6 +62,8 @@ var servicesSources = [
|
||||
"utilities.ts",
|
||||
"binder.ts",
|
||||
"checker.ts",
|
||||
"factory.ts",
|
||||
"visitor.ts",
|
||||
"sourcemap.ts",
|
||||
"declarationEmitter.ts",
|
||||
"emitter.ts",
|
||||
|
||||
@@ -91,6 +91,23 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through `array` by index and performs the callback on each element of array until the callback
|
||||
* returns a falsey value, then returns false.
|
||||
* If no such value is found, the callback is applied to each element of array and `true` is returned.
|
||||
*/
|
||||
export function trueForAll<T>(array: T[], callback: (element: T, index: number) => boolean): boolean {
|
||||
if (array) {
|
||||
for (let i = 0, len = array.length; i < len; i++) {
|
||||
if (!callback(array[i], i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function contains<T>(array: T[], value: T): boolean {
|
||||
if (array) {
|
||||
for (const v of array) {
|
||||
@@ -242,8 +259,14 @@ namespace ts {
|
||||
const count = array.length;
|
||||
if (count > 0) {
|
||||
let pos = 0;
|
||||
let result = arguments.length <= 2 ? array[pos] : initial;
|
||||
pos++;
|
||||
let result: T | U;
|
||||
if (arguments.length <= 2) {
|
||||
result = array[pos];
|
||||
pos++;
|
||||
}
|
||||
else {
|
||||
result = initial;
|
||||
}
|
||||
while (pos < count) {
|
||||
result = f(<U>result, array[pos]);
|
||||
pos++;
|
||||
@@ -260,8 +283,14 @@ namespace ts {
|
||||
if (array) {
|
||||
let pos = array.length - 1;
|
||||
if (pos >= 0) {
|
||||
let result = arguments.length <= 2 ? array[pos] : initial;
|
||||
pos--;
|
||||
let result: T | U;
|
||||
if (arguments.length <= 2) {
|
||||
result = array[pos];
|
||||
pos--;
|
||||
}
|
||||
else {
|
||||
result = initial;
|
||||
}
|
||||
while (pos >= 0) {
|
||||
result = f(<U>result, array[pos]);
|
||||
pos--;
|
||||
|
||||
56
src/compiler/factory.ts
Normal file
56
src/compiler/factory.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
/// <reference path="types.ts"/>
|
||||
/// <reference path="utilities.ts"/>
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
export function createNodeArray<T extends Node>(elements?: T[], location?: TextRange): NodeArray<T>;
|
||||
export function createNodeArray<T extends Node, TArray extends NodeArray<T>>(elements: TArray, location?: TextRange): TArray;
|
||||
export function createNodeArray<T extends Node, TArray extends NodeArray<T>>(elements?: T[], location?: TextRange): TArray {
|
||||
const array = <TArray>(elements || []);
|
||||
if (location) {
|
||||
array.pos = location.pos;
|
||||
array.end = location.end;
|
||||
}
|
||||
else if (array.pos === undefined || array.end === undefined) {
|
||||
array.pos = -1;
|
||||
array.end = -1;
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
export function createNodeArrayNode<T extends Node>(elements?: (T | NodeArrayNode<T>)[]): NodeArrayNode<T> {
|
||||
const array = <NodeArrayNode<T>>createNodeArray(elements);
|
||||
array.kind = SyntaxKind.NodeArrayNode;
|
||||
return array;
|
||||
}
|
||||
|
||||
export function createReturn(expression?: Expression): ReturnStatement {
|
||||
const node = <ReturnStatement>createSynthesizedNode(SyntaxKind.ReturnStatement);
|
||||
node.expression = expression;
|
||||
return node;
|
||||
}
|
||||
|
||||
export function createStatement(expression: Expression): ExpressionStatement {
|
||||
const node = <ExpressionStatement>createSynthesizedNode(SyntaxKind.ExpressionStatement);
|
||||
node.expression = expression;
|
||||
return node;
|
||||
}
|
||||
|
||||
export function createVariableStatement(declarationList: VariableDeclarationList): VariableStatement {
|
||||
const node = <VariableStatement>createSynthesizedNode(SyntaxKind.VariableStatement);
|
||||
node.declarationList = declarationList;
|
||||
return node;
|
||||
}
|
||||
|
||||
export function createVariableDeclarationList(declarations: VariableDeclaration[]): VariableDeclarationList {
|
||||
const node = <VariableDeclarationList>createSynthesizedNode(SyntaxKind.VariableDeclarationList);
|
||||
node.declarations = createNodeArray(declarations);
|
||||
return node;
|
||||
}
|
||||
|
||||
export function createBlock(statements: Statement[]): Block {
|
||||
const block = <Block>createSynthesizedNode(SyntaxKind.Block);
|
||||
block.statements = createNodeArray(statements);
|
||||
return block;
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@
|
||||
"utilities.ts",
|
||||
"binder.ts",
|
||||
"checker.ts",
|
||||
"factory.ts",
|
||||
"visitor.ts",
|
||||
"emitter.ts",
|
||||
"program.ts",
|
||||
"commandLineParser.ts",
|
||||
|
||||
@@ -341,6 +341,7 @@ namespace ts {
|
||||
|
||||
// Synthesized list
|
||||
SyntaxList,
|
||||
NodeArrayNode,
|
||||
// Enum value count
|
||||
Count,
|
||||
// Markers
|
||||
@@ -444,7 +445,8 @@ namespace ts {
|
||||
decorators?: NodeArray<Decorator>; // Array of decorators (in document order)
|
||||
modifiers?: ModifiersArray; // Array of modifiers
|
||||
/* @internal */ id?: number; // Unique id (used to look up NodeLinks)
|
||||
parent?: Node; // Parent node (initialized by binding
|
||||
parent?: Node; // Parent node (initialized by binding)
|
||||
/* @internal */ original?: Node; // The original node if this is an updated node.
|
||||
/* @internal */ jsDocComment?: JSDocComment; // JSDoc for the node, if it has any. Only for .js files.
|
||||
/* @internal */ symbol?: Symbol; // Symbol declared by node (initialized by binding)
|
||||
/* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding)
|
||||
@@ -456,6 +458,10 @@ namespace ts {
|
||||
hasTrailingComma?: boolean;
|
||||
}
|
||||
|
||||
// @kind(SyntaxKind.NodeArrayNode)
|
||||
export interface NodeArrayNode<T> extends Node, NodeArray<T | NodeArrayNode<T>> {
|
||||
}
|
||||
|
||||
export interface ModifiersArray extends NodeArray<Modifier> {
|
||||
flags: number;
|
||||
}
|
||||
@@ -1287,13 +1293,15 @@ namespace ts {
|
||||
statements: NodeArray<Statement>;
|
||||
}
|
||||
|
||||
export type ModuleReference = EntityName | ExternalModuleReference;
|
||||
|
||||
// @kind(SyntaxKind.ImportEqualsDeclaration)
|
||||
export interface ImportEqualsDeclaration extends DeclarationStatement {
|
||||
name: Identifier;
|
||||
|
||||
// 'EntityName' for an internal module reference, 'ExternalModuleReference' for an external
|
||||
// module reference.
|
||||
moduleReference: EntityName | ExternalModuleReference;
|
||||
moduleReference: ModuleReference;
|
||||
}
|
||||
|
||||
// @kind(SyntaxKind.ExternalModuleReference)
|
||||
|
||||
@@ -1194,10 +1194,10 @@ namespace ts {
|
||||
if (node.jsDocComment) {
|
||||
return node.jsDocComment;
|
||||
}
|
||||
// Try to recognize this pattern when node is initializer of variable declaration and JSDoc comments are on containing variable statement.
|
||||
// /**
|
||||
// Try to recognize this pattern when node is initializer of variable declaration and JSDoc comments are on containing variable statement.
|
||||
// /**
|
||||
// * @param {number} name
|
||||
// * @returns {number}
|
||||
// * @returns {number}
|
||||
// */
|
||||
// var x = function(name) { return name.length; }
|
||||
if (checkParentVariableStatement) {
|
||||
@@ -1379,7 +1379,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function isClassElement(n: Node): boolean {
|
||||
export function isClassElement(n: Node): n is ClassElement {
|
||||
switch (n.kind) {
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
@@ -1655,8 +1655,9 @@ namespace ts {
|
||||
* @param location An optional TextRange to use to supply the new position.
|
||||
* @param flags The NodeFlags to use for the cloned node.
|
||||
* @param parent The parent for the new node.
|
||||
* @param original An optional pointer to the original node.
|
||||
*/
|
||||
export function cloneNode<T extends Node>(node: T, location?: TextRange, flags?: NodeFlags, parent?: Node): T {
|
||||
export function cloneNode<T extends Node>(node: T, location?: TextRange, flags?: NodeFlags, parent?: Node, original?: Node): T {
|
||||
// We don't use "clone" from core.ts here, as we need to preserve the prototype chain of
|
||||
// the original node. We also need to exclude specific properties and only include own-
|
||||
// properties (to skip members already defined on the shared prototype).
|
||||
@@ -1665,7 +1666,7 @@ namespace ts {
|
||||
: <T>createSynthesizedNode(node.kind);
|
||||
|
||||
for (const key in node) {
|
||||
if (clone.hasOwnProperty(key) || !node.hasOwnProperty(key)) {
|
||||
if (key === "parent" || key === "flags" || clone.hasOwnProperty(key) || !node.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1680,6 +1681,10 @@ namespace ts {
|
||||
clone.parent = parent;
|
||||
}
|
||||
|
||||
if (original !== undefined) {
|
||||
clone.original = original;
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -1703,8 +1708,15 @@ namespace ts {
|
||||
return node.kind === SyntaxKind.QualifiedName;
|
||||
}
|
||||
|
||||
export function nodeIsSynthesized(node: Node): boolean {
|
||||
return node.pos === -1;
|
||||
export function nodeIsSynthesized(node: TextRange): boolean {
|
||||
return positionIsSynthesized(node.pos)
|
||||
|| positionIsSynthesized(node.end);
|
||||
}
|
||||
|
||||
export function positionIsSynthesized(pos: number): boolean {
|
||||
// This is a fast way of testing the following conditions:
|
||||
// pos === undefined || pos === null || isNaN(pos) || pos < 0;
|
||||
return !(pos >= 0);
|
||||
}
|
||||
|
||||
export function createSynthesizedNode(kind: SyntaxKind, startsOnNewLine?: boolean): Node {
|
||||
@@ -1720,6 +1732,19 @@ namespace ts {
|
||||
return array;
|
||||
}
|
||||
|
||||
export function getOriginalNode(node: Node): Node {
|
||||
while (node.original !== undefined) {
|
||||
node = node.original;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
export function getOriginalNodeId(node: Node) {
|
||||
node = getOriginalNode(node);
|
||||
return node ? getNodeId(node) : 0;
|
||||
}
|
||||
|
||||
export function createDiagnosticCollection(): DiagnosticCollection {
|
||||
let nonFileDiagnostics: Diagnostic[] = [];
|
||||
const fileDiagnostics: Map<Diagnostic[]> = {};
|
||||
@@ -2361,9 +2386,13 @@ namespace ts {
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function isLeftHandSideExpression(expr: Expression): boolean {
|
||||
if (expr) {
|
||||
switch (expr.kind) {
|
||||
export function isLiteralExpression(node: Node): node is LiteralExpression {
|
||||
return isLiteralKind(node.kind);
|
||||
}
|
||||
|
||||
export function isLeftHandSideExpression(node: Node): node is LeftHandSideExpression {
|
||||
if (node) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
case SyntaxKind.ElementAccessExpression:
|
||||
case SyntaxKind.NewExpression:
|
||||
@@ -2394,6 +2423,28 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isEntityName(node: Node): node is EntityName {
|
||||
const kind = node.kind;
|
||||
return kind === SyntaxKind.QualifiedName
|
||||
|| kind === SyntaxKind.Identifier;
|
||||
}
|
||||
|
||||
export function isIdentifierNode(node: Node): node is Identifier {
|
||||
return node.kind === SyntaxKind.Identifier;
|
||||
}
|
||||
|
||||
export function isComputedPropertyName(node: Node): node is ComputedPropertyName {
|
||||
return node.kind === SyntaxKind.ComputedPropertyName;
|
||||
}
|
||||
|
||||
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 isAssignmentOperator(token: SyntaxKind): boolean {
|
||||
return token >= SyntaxKind.FirstAssignment && token <= SyntaxKind.LastAssignment;
|
||||
}
|
||||
@@ -2404,6 +2455,245 @@ namespace ts {
|
||||
isClassLike(node.parent.parent);
|
||||
}
|
||||
|
||||
export function isExpressionNode(node: Node): node is Expression {
|
||||
const kind = node.kind;
|
||||
return isUnaryExpression(node)
|
||||
|| kind === SyntaxKind.ConditionalExpression
|
||||
|| kind === SyntaxKind.YieldExpression
|
||||
|| kind === SyntaxKind.ArrowFunction
|
||||
|| kind === SyntaxKind.BinaryExpression
|
||||
|| kind === SyntaxKind.SpreadElementExpression
|
||||
|| kind === SyntaxKind.AsExpression
|
||||
|| kind === SyntaxKind.OmittedExpression;
|
||||
}
|
||||
|
||||
export function isDecorator(node: Node): node is Decorator {
|
||||
return node.kind === SyntaxKind.Decorator;
|
||||
}
|
||||
|
||||
export function isModifier(node: Node): node is Modifier {
|
||||
return isModifierKind(node.kind);
|
||||
}
|
||||
|
||||
/**
|
||||
* Node test that determines whether a node is a valid type node.
|
||||
* This differs from the existing `isTypeNode` function which
|
||||
* determines whether a node is *part* of a TypeNode.
|
||||
*/
|
||||
export function isTypeNodeNode(node: Node): node is TypeNode {
|
||||
const kind = node.kind;
|
||||
return kind === SyntaxKind.AnyKeyword
|
||||
|| kind === SyntaxKind.NumberKeyword
|
||||
|| kind === SyntaxKind.BooleanKeyword
|
||||
|| kind === SyntaxKind.StringKeyword
|
||||
|| kind === SyntaxKind.SymbolKeyword
|
||||
|| kind === SyntaxKind.VoidKeyword
|
||||
|| kind === SyntaxKind.FunctionType
|
||||
|| kind === SyntaxKind.ConstructorType
|
||||
|| kind === SyntaxKind.TypeReference
|
||||
|| kind === SyntaxKind.TypePredicate
|
||||
|| kind === SyntaxKind.TypeQuery
|
||||
|| kind === SyntaxKind.TypeLiteral
|
||||
|| kind === SyntaxKind.ArrayType
|
||||
|| kind === SyntaxKind.TupleType
|
||||
|| kind === SyntaxKind.UnionType
|
||||
|| kind === SyntaxKind.IntersectionType
|
||||
|| kind === SyntaxKind.ParenthesizedType
|
||||
|| kind === SyntaxKind.StringLiteralType
|
||||
|| kind === SyntaxKind.ExpressionWithTypeArguments
|
||||
|| kind === SyntaxKind.ThisType;
|
||||
}
|
||||
|
||||
export function isStatementNode(node: Node): node is Statement {
|
||||
return isStatement(node)
|
||||
|| isDeclarationStatement(node);
|
||||
}
|
||||
|
||||
export function isDeclarationStatement(node: Node): node is DeclarationStatement {
|
||||
const kind = node.kind;
|
||||
return kind === SyntaxKind.FunctionDeclaration
|
||||
|| kind === SyntaxKind.MissingDeclaration
|
||||
|| kind === SyntaxKind.ClassDeclaration
|
||||
|| kind === SyntaxKind.InterfaceDeclaration
|
||||
|| kind === SyntaxKind.TypeAliasDeclaration
|
||||
|| kind === SyntaxKind.EnumDeclaration
|
||||
|| kind === SyntaxKind.ModuleDeclaration
|
||||
|| kind === SyntaxKind.ImportEqualsDeclaration
|
||||
|| kind === SyntaxKind.ExportDeclaration
|
||||
|| kind === SyntaxKind.ExportAssignment;
|
||||
}
|
||||
|
||||
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 isConciseBody(node: Node): node is Expression | Block {
|
||||
return isBlock(node)
|
||||
|| isExpressionNode(node);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 isTemplate(node: Node): node is LiteralExpression | TemplateExpression {
|
||||
const kind = node.kind;
|
||||
return kind === SyntaxKind.TemplateExpression
|
||||
|| kind === SyntaxKind.NoSubstitutionTemplateLiteral;
|
||||
}
|
||||
|
||||
export function isUnaryExpression(node: Node): node is UnaryExpression {
|
||||
const kind = node.kind;
|
||||
return isLeftHandSideExpression(node)
|
||||
|| kind === SyntaxKind.PrefixUnaryExpression
|
||||
|| kind === SyntaxKind.PostfixUnaryExpression
|
||||
|| kind === SyntaxKind.DeleteExpression
|
||||
|| kind === SyntaxKind.TypeOfExpression
|
||||
|| kind === SyntaxKind.VoidExpression
|
||||
|| kind === SyntaxKind.AwaitExpression
|
||||
|| kind === SyntaxKind.TypeAssertionExpression;
|
||||
}
|
||||
|
||||
export function isTemplateLiteralFragment(node: Node): node is TemplateLiteralFragment {
|
||||
const kind = node.kind;
|
||||
return kind === SyntaxKind.TemplateHead
|
||||
|| kind === SyntaxKind.TemplateMiddle
|
||||
|| kind === SyntaxKind.TemplateTail;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
export function isExpressionOrVariableDeclarationList(node: Node): node is Expression | VariableDeclarationList {
|
||||
return isVariableDeclarationList(node)
|
||||
|| isExpressionNode(node);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
export function isModuleName(node: Node): node is Identifier | LiteralExpression {
|
||||
return node.kind === SyntaxKind.Identifier
|
||||
|| node.kind === SyntaxKind.StringLiteral;
|
||||
}
|
||||
|
||||
export function isCaseOrDefaultClause(node: Node): node is CaseOrDefaultClause {
|
||||
return node.kind === SyntaxKind.CaseClause
|
||||
|| node.kind === SyntaxKind.DefaultClause;
|
||||
}
|
||||
|
||||
export function isModuleReference(node: Node): node is EntityName | ExternalModuleReference {
|
||||
return node.kind === SyntaxKind.ExternalModuleReference
|
||||
|| isEntityName(node);
|
||||
}
|
||||
|
||||
export function isImportClause(node: Node): node is ImportClause {
|
||||
return node.kind === SyntaxKind.ImportClause;
|
||||
}
|
||||
|
||||
export function isNamedImportsOrNamespaceImport(node: Node): node is NamedImports | NamespaceImport {
|
||||
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;
|
||||
}
|
||||
|
||||
export function isJsxOpeningElement(node: Node): node is JsxOpeningElement {
|
||||
return node.kind === SyntaxKind.JsxOpeningElement;
|
||||
}
|
||||
|
||||
export function isJsxClosingElement(node: Node): node is JsxClosingElement {
|
||||
return node.kind === SyntaxKind.JsxClosingElement;
|
||||
}
|
||||
|
||||
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 isJsxAttributeOrJsxSpreadAttribute(node: Node): node is JsxAttribute | JsxSpreadAttribute {
|
||||
const kind = node.kind;
|
||||
return kind === SyntaxKind.JsxAttribute
|
||||
|| kind === SyntaxKind.JsxSpreadAttribute;
|
||||
}
|
||||
|
||||
export function isExpressionWithTypeArguments(node: Node): node is ExpressionWithTypeArguments {
|
||||
return node.kind === SyntaxKind.ExpressionWithTypeArguments;
|
||||
}
|
||||
|
||||
export function isModuleBody(node: Node): node is ModuleBody {
|
||||
const kind = node.kind;
|
||||
return kind === SyntaxKind.ModuleBlock
|
||||
|| kind === SyntaxKind.ModuleDeclaration;
|
||||
}
|
||||
|
||||
export function isBindingPatternOrIdentifier(node: Node): node is (BindingPattern | Identifier) {
|
||||
return isBindingPattern(node)
|
||||
|| isIdentifierNode(node);
|
||||
}
|
||||
|
||||
// Returns false if this heritage clause element's expression contains something unsupported
|
||||
// (i.e. not a name or dotted name).
|
||||
export function isSupportedExpressionWithTypeArguments(node: ExpressionWithTypeArguments): boolean {
|
||||
@@ -2438,6 +2728,14 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isModifiersArray(nodes: NodeArray<Node>): nodes is ModifiersArray {
|
||||
return !isNodeArrayNode(nodes) && typeof (<ModifiersArray>nodes).flags === "number";
|
||||
}
|
||||
|
||||
export function isNodeArrayNode<T extends Node>(value: Node | NodeArray<T | NodeArrayNode<T>>): value is NodeArrayNode<T> {
|
||||
return (<Node>value).kind === SyntaxKind.NodeArrayNode;
|
||||
}
|
||||
|
||||
export function getLocalSymbolForExportDefault(symbol: Symbol) {
|
||||
return symbol && symbol.valueDeclaration && (symbol.valueDeclaration.flags & NodeFlags.Default) ? symbol.valueDeclaration.localSymbol : undefined;
|
||||
}
|
||||
|
||||
778
src/compiler/visitor.ts
Normal file
778
src/compiler/visitor.ts
Normal file
@@ -0,0 +1,778 @@
|
||||
/// <reference path="checker.ts" />
|
||||
/// <reference path="factory.ts" />
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
/** Additional context provided to `visitEachChild` */
|
||||
export interface LexicalEnvironment {
|
||||
/** Starts a new lexical environment. */
|
||||
startLexicalEnvironment(): void;
|
||||
|
||||
/** Ends a lexical environment, returning any declarations. */
|
||||
endLexicalEnvironment(): Statement[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes an edge of a Node, used when traversing a syntax tree.
|
||||
*/
|
||||
interface NodeEdge {
|
||||
/** Indicates that the edge is a NodeArray. */
|
||||
array?: boolean;
|
||||
|
||||
/** Indicates that the result is optional. */
|
||||
optional?: boolean;
|
||||
|
||||
/** A callback used to test whether a node is valid. */
|
||||
test?: (node: Node) => node is Node;
|
||||
|
||||
/** A callback used to lift a NodeArrayNode into a valid node. */
|
||||
lift?: (nodes: NodeArrayNode<Node>) => Node;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the shape of a Node.
|
||||
*/
|
||||
type NodeTraversalPath = Map<NodeEdge>;
|
||||
|
||||
/**
|
||||
* This map contains information about the shape of each Node in "types.ts" pertaining to how
|
||||
* each node should be traversed during a transformation.
|
||||
*
|
||||
* Each edge corresponds to a property in a Node subtype that should be traversed when visiting
|
||||
* each child. The properties are assigned in the order in which traversal should occur.
|
||||
*
|
||||
* NOTE: This needs to be kept up to date with changes to nodes in "types.ts". Currently, this
|
||||
* map is not comprehensive. Only node edges relevant to tree transformation are
|
||||
* currently defined. We may extend this to be more comprehensive, and eventually
|
||||
* supplant the existing `forEachChild` implementation if performance is not
|
||||
* significantly impacted.
|
||||
*/
|
||||
const nodeEdgeTraversalMap: Map<NodeTraversalPath> = {
|
||||
[SyntaxKind.QualifiedName]: {
|
||||
left: { test: isEntityName },
|
||||
right: { test: isIdentifierNode },
|
||||
},
|
||||
[SyntaxKind.ComputedPropertyName]: {
|
||||
expression: { test: isExpressionNode },
|
||||
},
|
||||
[SyntaxKind.Parameter]: {
|
||||
decorators: { test: isDecorator, array: true },
|
||||
modifiers: { test: isModifier, array: true },
|
||||
name: { test: isBindingPatternOrIdentifier },
|
||||
type: { test: isTypeNodeNode, optional: true },
|
||||
initializer: { test: isExpressionNode, optional: true },
|
||||
},
|
||||
[SyntaxKind.Decorator]: {
|
||||
expression: { test: isLeftHandSideExpression },
|
||||
},
|
||||
[SyntaxKind.PropertyDeclaration]: {
|
||||
decorators: { test: isDecorator, array: true },
|
||||
modifiers: { test: isModifier, array: true },
|
||||
name: { test: isPropertyName },
|
||||
type: { test: isTypeNodeNode, optional: true },
|
||||
initializer: { test: isExpressionNode, optional: true },
|
||||
},
|
||||
[SyntaxKind.MethodDeclaration]: {
|
||||
decorators: { test: isDecorator, array: true },
|
||||
modifiers: { test: isModifier, array: true },
|
||||
name: { test: isPropertyName },
|
||||
typeParameters: { test: isTypeParameter, array: true },
|
||||
parameters: { test: isParameter, array: true },
|
||||
type: { test: isTypeNodeNode, optional: true },
|
||||
body: { test: isBlock, optional: true },
|
||||
},
|
||||
[SyntaxKind.Constructor]: {
|
||||
decorators: { test: isDecorator, array: true },
|
||||
modifiers: { test: isModifier, array: true },
|
||||
typeParameters: { test: isTypeParameter, array: true },
|
||||
parameters: { test: isParameter, array: true },
|
||||
type: { test: isTypeNodeNode, optional: true },
|
||||
body: { test: isBlock, optional: true },
|
||||
},
|
||||
[SyntaxKind.GetAccessor]: {
|
||||
decorators: { test: isDecorator, array: true },
|
||||
modifiers: { test: isModifier, array: true },
|
||||
name: { test: isPropertyName },
|
||||
typeParameters: { test: isTypeParameter, array: true },
|
||||
parameters: { test: isParameter, array: true },
|
||||
type: { test: isTypeNodeNode, optional: true },
|
||||
body: { test: isBlock, optional: true },
|
||||
},
|
||||
[SyntaxKind.SetAccessor]: {
|
||||
decorators: { test: isDecorator, array: true },
|
||||
modifiers: { test: isModifier, array: true },
|
||||
name: { test: isPropertyName },
|
||||
typeParameters: { test: isTypeParameter, array: true },
|
||||
parameters: { test: isParameter, array: true },
|
||||
type: { test: isTypeNodeNode, optional: true },
|
||||
body: { test: isBlock, optional: true },
|
||||
},
|
||||
[SyntaxKind.ObjectBindingPattern]: {
|
||||
elements: { test: isBindingElement, array: true },
|
||||
},
|
||||
[SyntaxKind.ArrayBindingPattern]: {
|
||||
elements: { test: isBindingElement, array: true },
|
||||
},
|
||||
[SyntaxKind.BindingElement]: {
|
||||
propertyName: { test: isPropertyName, optional: true },
|
||||
name: { test: isBindingPatternOrIdentifier },
|
||||
initializer: { test: isExpressionNode, optional: true },
|
||||
},
|
||||
[SyntaxKind.ArrayLiteralExpression]: {
|
||||
elements: { test: isExpressionNode, array: true },
|
||||
},
|
||||
[SyntaxKind.ObjectLiteralExpression]: {
|
||||
properties: { test: isObjectLiteralElement, array: true },
|
||||
},
|
||||
[SyntaxKind.PropertyAccessExpression]: {
|
||||
expression: { test: isLeftHandSideExpression },
|
||||
name: { test: isIdentifierNode },
|
||||
},
|
||||
[SyntaxKind.ElementAccessExpression]: {
|
||||
expression: { test: isLeftHandSideExpression },
|
||||
argumentExpression: { test: isExpressionNode },
|
||||
},
|
||||
[SyntaxKind.CallExpression]: {
|
||||
expression: { test: isLeftHandSideExpression },
|
||||
typeArguments: { test: isTypeNodeNode, array: true },
|
||||
arguments: { test: isExpressionNode, array: true },
|
||||
},
|
||||
[SyntaxKind.NewExpression]: {
|
||||
expression: { test: isLeftHandSideExpression },
|
||||
typeArguments: { test: isTypeNodeNode, array: true },
|
||||
arguments: { test: isExpressionNode, array: true },
|
||||
},
|
||||
[SyntaxKind.TaggedTemplateExpression]: {
|
||||
tag: { test: isLeftHandSideExpression },
|
||||
template: { test: isTemplate },
|
||||
},
|
||||
[SyntaxKind.TypeAssertionExpression]: {
|
||||
type: { test: isTypeNodeNode },
|
||||
expression: { test: isUnaryExpression },
|
||||
},
|
||||
[SyntaxKind.ParenthesizedExpression]: {
|
||||
expression: { test: isExpressionNode },
|
||||
},
|
||||
[SyntaxKind.FunctionExpression]: {
|
||||
decorators: { test: isDecorator, array: true },
|
||||
modifiers: { test: isModifier, array: true },
|
||||
name: { test: isIdentifierNode, optional: true },
|
||||
typeParameters: { test: isTypeParameter, array: true },
|
||||
parameters: { test: isParameter, array: true },
|
||||
type: { test: isTypeNodeNode, optional: true },
|
||||
body: { test: isBlock, optional: true },
|
||||
},
|
||||
[SyntaxKind.ArrowFunction]: {
|
||||
decorators: { test: isDecorator, array: true },
|
||||
modifiers: { test: isModifier, array: true },
|
||||
typeParameters: { test: isTypeParameter, array: true },
|
||||
parameters: { test: isParameter, array: true },
|
||||
type: { test: isTypeNodeNode, optional: true },
|
||||
body: { test: isConciseBody, lift: liftToBlock },
|
||||
},
|
||||
[SyntaxKind.DeleteExpression]: {
|
||||
expression: { test: isUnaryExpression },
|
||||
},
|
||||
[SyntaxKind.TypeOfExpression]: {
|
||||
expression: { test: isUnaryExpression },
|
||||
},
|
||||
[SyntaxKind.VoidExpression]: {
|
||||
expression: { test: isUnaryExpression },
|
||||
},
|
||||
[SyntaxKind.AwaitExpression]: {
|
||||
expression: { test: isUnaryExpression },
|
||||
},
|
||||
[SyntaxKind.PrefixUnaryExpression]: {
|
||||
operand: { test: isUnaryExpression },
|
||||
},
|
||||
[SyntaxKind.PostfixUnaryExpression]: {
|
||||
operand: { test: isLeftHandSideExpression },
|
||||
},
|
||||
[SyntaxKind.BinaryExpression]: {
|
||||
left: { test: isExpressionNode },
|
||||
right: { test: isExpressionNode },
|
||||
},
|
||||
[SyntaxKind.ConditionalExpression]: {
|
||||
condition: { test: isExpressionNode },
|
||||
whenTrue: { test: isExpressionNode },
|
||||
whenFalse: { test: isExpressionNode },
|
||||
},
|
||||
[SyntaxKind.TemplateExpression]: {
|
||||
head: { test: isTemplateLiteralFragment },
|
||||
templateSpans: { test: isTemplateSpan, array: true },
|
||||
},
|
||||
[SyntaxKind.YieldExpression]: {
|
||||
expression: { test: isExpressionNode, optional: true },
|
||||
},
|
||||
[SyntaxKind.SpreadElementExpression]: {
|
||||
expression: { test: isExpressionNode },
|
||||
},
|
||||
[SyntaxKind.ClassExpression]: {
|
||||
decorators: { test: isDecorator, array: true },
|
||||
modifiers: { test: isModifier, array: true },
|
||||
name: { test: isIdentifierNode, optional: true },
|
||||
typeParameters: { test: isTypeParameter, array: true },
|
||||
heritageClauses: { test: isHeritageClause, array: true },
|
||||
members: { test: isClassElement, array: true },
|
||||
},
|
||||
[SyntaxKind.ExpressionWithTypeArguments]: {
|
||||
expression: { test: isLeftHandSideExpression },
|
||||
typeArguments: { test: isTypeNodeNode, array: true },
|
||||
},
|
||||
[SyntaxKind.AsExpression]: {
|
||||
expression: { test: isExpressionNode },
|
||||
type: { test: isTypeNodeNode },
|
||||
},
|
||||
[SyntaxKind.TemplateSpan]: {
|
||||
expression: { test: isExpressionNode },
|
||||
literal: { test: isTemplateLiteralFragment },
|
||||
},
|
||||
[SyntaxKind.Block]: {
|
||||
statements: { test: isStatementNode, array: true },
|
||||
},
|
||||
[SyntaxKind.VariableStatement]: {
|
||||
decorators: { test: isDecorator, array: true },
|
||||
modifiers: { test: isModifier, array: true },
|
||||
declarationList: { test: isVariableDeclarationList },
|
||||
},
|
||||
[SyntaxKind.ExpressionStatement]: {
|
||||
expression: { test: isExpressionNode },
|
||||
},
|
||||
[SyntaxKind.IfStatement]: {
|
||||
expression: { test: isExpressionNode },
|
||||
thenStatement: { test: isStatementNode, lift: liftToBlock },
|
||||
elseStatement: { test: isStatementNode, lift: liftToBlock, optional: true},
|
||||
},
|
||||
[SyntaxKind.DoStatement]: {
|
||||
statement: { test: isStatementNode, lift: liftToBlock },
|
||||
expression: { test: isExpressionNode },
|
||||
},
|
||||
[SyntaxKind.WhileStatement]: {
|
||||
expression: { test: isExpressionNode },
|
||||
statement: { test: isStatementNode, lift: liftToBlock },
|
||||
},
|
||||
[SyntaxKind.ForStatement]: {
|
||||
initializer: { test: isExpressionOrVariableDeclarationList, optional: true },
|
||||
condition: { test: isExpressionNode, optional: true },
|
||||
incrementor: { test: isExpressionNode, optional: true },
|
||||
statement: { test: isStatementNode, lift: liftToBlock },
|
||||
},
|
||||
[SyntaxKind.ForInStatement]: {
|
||||
initializer: { test: isExpressionOrVariableDeclarationList },
|
||||
expression: { test: isExpressionNode },
|
||||
statement: { test: isStatementNode, lift: liftToBlock },
|
||||
},
|
||||
[SyntaxKind.ForOfStatement]: {
|
||||
initializer: { test: isExpressionOrVariableDeclarationList },
|
||||
expression: { test: isExpressionNode },
|
||||
statement: { test: isStatementNode, lift: liftToBlock },
|
||||
},
|
||||
[SyntaxKind.ContinueStatement]: {
|
||||
label: { test: isIdentifierNode, optional: true },
|
||||
},
|
||||
[SyntaxKind.BreakStatement]: {
|
||||
label: { test: isIdentifierNode, optional: true },
|
||||
},
|
||||
[SyntaxKind.ReturnStatement]: {
|
||||
expression: { test: isExpressionNode, optional: true },
|
||||
},
|
||||
[SyntaxKind.WithStatement]: {
|
||||
expression: { test: isExpressionNode },
|
||||
statement: { test: isStatementNode, lift: liftToBlock },
|
||||
},
|
||||
[SyntaxKind.SwitchStatement]: {
|
||||
expression: { test: isExpressionNode },
|
||||
caseBlock: { test: isCaseBlock },
|
||||
},
|
||||
[SyntaxKind.LabeledStatement]: {
|
||||
label: { test: isIdentifierNode },
|
||||
statement: { test: isStatementNode, lift: liftToBlock },
|
||||
},
|
||||
[SyntaxKind.ThrowStatement]: {
|
||||
expression: { test: isExpressionNode },
|
||||
},
|
||||
[SyntaxKind.TryStatement]: {
|
||||
tryBlock: { test: isBlock },
|
||||
catchClause: { test: isCatchClause, optional: true },
|
||||
finallyBlock: { test: isBlock, optional: true },
|
||||
},
|
||||
[SyntaxKind.VariableDeclaration]: {
|
||||
name: { test: isBindingPatternOrIdentifier },
|
||||
type: { test: isTypeNodeNode, optional: true },
|
||||
initializer: { test: isExpressionNode, optional: true },
|
||||
},
|
||||
[SyntaxKind.VariableDeclarationList]: {
|
||||
declarations: { test: isVariableDeclaration, array: true },
|
||||
},
|
||||
[SyntaxKind.FunctionDeclaration]: {
|
||||
decorators: { test: isDecorator, array: true },
|
||||
modifiers: { test: isModifier, array: true },
|
||||
name: { test: isIdentifierNode, optional: true },
|
||||
typeParameters: { test: isTypeParameter, array: true },
|
||||
parameters: { test: isParameter, array: true },
|
||||
type: { test: isTypeNodeNode, optional: true },
|
||||
body: { test: isBlock, optional: true },
|
||||
},
|
||||
[SyntaxKind.ClassDeclaration]: {
|
||||
decorators: { test: isDecorator, array: true },
|
||||
modifiers: { test: isModifier, array: true },
|
||||
name: { test: isIdentifierNode, optional: true },
|
||||
typeParameters: { test: isTypeParameter, array: true },
|
||||
heritageClauses: { test: isHeritageClause, array: true },
|
||||
members: { test: isClassElement, array: true },
|
||||
},
|
||||
[SyntaxKind.EnumDeclaration]: {
|
||||
decorators: { test: isDecorator, array: true },
|
||||
modifiers: { test: isModifier, array: true },
|
||||
name: { test: isIdentifierNode },
|
||||
members: { test: isEnumMember, array: true },
|
||||
},
|
||||
[SyntaxKind.ModuleDeclaration]: {
|
||||
decorators: { test: isDecorator, array: true },
|
||||
modifiers: { test: isModifier, array: true },
|
||||
name: { test: isModuleName },
|
||||
body: { test: isModuleBody },
|
||||
},
|
||||
[SyntaxKind.ModuleBlock]: {
|
||||
statements: { test: isStatementNode, array: true },
|
||||
},
|
||||
[SyntaxKind.CaseBlock]: {
|
||||
clauses: { test: isCaseOrDefaultClause, array: true },
|
||||
},
|
||||
[SyntaxKind.ImportEqualsDeclaration]: {
|
||||
decorators: { test: isDecorator, array: true },
|
||||
modifiers: { test: isModifier, array: true },
|
||||
name: { test: isIdentifierNode },
|
||||
moduleReference: { test: isModuleReference },
|
||||
},
|
||||
[SyntaxKind.ImportDeclaration]: {
|
||||
decorators: { test: isDecorator, array: true },
|
||||
modifiers: { test: isModifier, array: true },
|
||||
importClause: { test: isImportClause, optional: true },
|
||||
moduleSpecifier: { test: isExpressionNode },
|
||||
},
|
||||
[SyntaxKind.ImportClause]: {
|
||||
name: { test: isIdentifierNode, optional: true },
|
||||
namedBindings: { test: isNamedImportsOrNamespaceImport, optional: true },
|
||||
},
|
||||
[SyntaxKind.NamespaceImport]: {
|
||||
name: { test: isIdentifierNode },
|
||||
},
|
||||
[SyntaxKind.NamedImports]: {
|
||||
elements: { test: isImportSpecifier, array: true },
|
||||
},
|
||||
[SyntaxKind.ImportSpecifier]: {
|
||||
propertyName: { test: isIdentifierNode, optional: true },
|
||||
name: { test: isIdentifierNode },
|
||||
},
|
||||
[SyntaxKind.ExportAssignment]: {
|
||||
decorators: { test: isDecorator, array: true },
|
||||
modifiers: { test: isModifier, array: true },
|
||||
expression: { test: isExpressionNode },
|
||||
},
|
||||
[SyntaxKind.ExportDeclaration]: {
|
||||
decorators: { test: isDecorator, array: true },
|
||||
modifiers: { test: isModifier, array: true },
|
||||
exportClause: { test: isNamedExports, optional: true },
|
||||
moduleSpecifier: { test: isExpressionNode, optional: true },
|
||||
},
|
||||
[SyntaxKind.NamedExports]: {
|
||||
elements: { test: isExportSpecifier, array: true },
|
||||
},
|
||||
[SyntaxKind.ExportSpecifier]: {
|
||||
propertyName: { test: isIdentifierNode, optional: true },
|
||||
name: { test: isIdentifierNode },
|
||||
},
|
||||
[SyntaxKind.ExternalModuleReference]: {
|
||||
expression: { test: isExpressionNode, optional: true },
|
||||
},
|
||||
[SyntaxKind.JsxElement]: {
|
||||
openingElement: { test: isJsxOpeningElement },
|
||||
children: { test: isJsxChild, array: true },
|
||||
closingElement: { test: isJsxClosingElement },
|
||||
},
|
||||
[SyntaxKind.JsxSelfClosingElement]: {
|
||||
tagName: { test: isEntityName },
|
||||
attributes: { test: isJsxAttributeOrJsxSpreadAttribute, array: true },
|
||||
},
|
||||
[SyntaxKind.JsxOpeningElement]: {
|
||||
tagName: { test: isEntityName },
|
||||
attributes: { test: isJsxAttributeOrJsxSpreadAttribute, array: true },
|
||||
},
|
||||
[SyntaxKind.JsxClosingElement]: {
|
||||
tagName: { test: isEntityName },
|
||||
},
|
||||
[SyntaxKind.JsxAttribute]: {
|
||||
name: { test: isIdentifierNode },
|
||||
initializer: { test: isExpressionNode, optional: true },
|
||||
},
|
||||
[SyntaxKind.JsxSpreadAttribute]: {
|
||||
expression: { test: isExpressionNode },
|
||||
},
|
||||
[SyntaxKind.JsxExpression]: {
|
||||
expression: { test: isExpressionNode, optional: true },
|
||||
},
|
||||
[SyntaxKind.CaseClause]: {
|
||||
expression: { test: isExpressionNode },
|
||||
statements: { test: isStatementNode, array: true },
|
||||
},
|
||||
[SyntaxKind.DefaultClause]: {
|
||||
statements: { test: isStatementNode, array: true },
|
||||
},
|
||||
[SyntaxKind.HeritageClause]: {
|
||||
types: { test: isExpressionWithTypeArguments, array: true },
|
||||
},
|
||||
[SyntaxKind.CatchClause]: {
|
||||
variableDeclaration: { test: isVariableDeclaration },
|
||||
block: { test: isBlock },
|
||||
},
|
||||
[SyntaxKind.PropertyAssignment]: {
|
||||
name: { test: isPropertyName },
|
||||
initializer: { test: isExpressionNode },
|
||||
},
|
||||
[SyntaxKind.ShorthandPropertyAssignment]: {
|
||||
name: { test: isIdentifierNode },
|
||||
objectAssignmentInitializer: { test: isExpressionNode, optional: true },
|
||||
},
|
||||
[SyntaxKind.EnumMember]: {
|
||||
name: { test: isPropertyName },
|
||||
initializer: { test: isExpressionNode, optional: true },
|
||||
},
|
||||
[SyntaxKind.SourceFile]: {
|
||||
statements: { test: isStatementNode, array: true },
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Similar to `reduceLeft`, performs a reduction against each child of a node.
|
||||
* NOTE: Unlike `forEachChild`, this does *not* visit every node. Only nodes added to the
|
||||
* `nodeEdgeTraversalMap` above will be visited.
|
||||
*
|
||||
* @param node The node containing the children to reduce.
|
||||
* @param f The callback function
|
||||
* @param initial The initial value to supply to the reduction.
|
||||
*/
|
||||
export function reduceEachChild<T>(node: Node, f: (memo: T, node: Node) => T, initial: T) {
|
||||
if (node === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let result = initial;
|
||||
const edgeTraversalPath = nodeEdgeTraversalMap[node.kind];
|
||||
if (edgeTraversalPath) {
|
||||
for (const propertyName in edgeTraversalPath) {
|
||||
const value = (<Map<any>>node)[propertyName];
|
||||
if (value !== undefined) {
|
||||
const edge = edgeTraversalPath[propertyName];
|
||||
if (edge.array) {
|
||||
result = reduceLeft(<NodeArray<Node>>value, f, result);
|
||||
}
|
||||
else {
|
||||
result = f(result, <Node>value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a Node using the supplied visitor, possibly returning a new Node in its place.
|
||||
*
|
||||
* @param node The Node to visit.
|
||||
* @param visitor The callback used to visit the Node.
|
||||
* @param test A callback to execute to verify the Node is valid.
|
||||
* @param lift A callback to execute to lift a NodeArrayNode into a valid Node.
|
||||
* @param optional A value indicating whether the Node is optional.
|
||||
*/
|
||||
export function visitNode<T extends Node>(node: T, visitor: (node: Node) => Node, test?: (node: Node) => boolean, lift?: (node: NodeArrayNode<T>) => T, optional?: boolean): T {
|
||||
if (node === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const visited = visitor(node);
|
||||
if (visited === node) {
|
||||
return node;
|
||||
}
|
||||
|
||||
const lifted = liftNode(visited, lift);
|
||||
if (lifted === undefined) {
|
||||
Debug.assert(optional, "Node not optional.");
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (test !== undefined) {
|
||||
Debug.assert(test(visited), "Wrong node type after visit.");
|
||||
}
|
||||
|
||||
return <T>visited;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a NodeArray using the supplied visitor, possibly returning a new NodeArray in its place.
|
||||
*
|
||||
* @param nodes The NodeArray to visit.
|
||||
* @param visitor The callback used to visit a Node.
|
||||
* @param test A node test to execute for each node.
|
||||
*/
|
||||
export function visitNodes<T extends Node, TArray extends NodeArray<T>>(nodes: TArray, visitor: (node: Node) => Node, test?: (node: Node) => boolean): TArray {
|
||||
if (nodes === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let updated: TArray;
|
||||
for (let i = 0, len = nodes.length; i < len; i++) {
|
||||
const node = nodes[i];
|
||||
if (node === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const visited = visitor(node);
|
||||
if (updated !== undefined || visited === undefined || visited !== node) {
|
||||
if (updated === undefined) {
|
||||
updated = <TArray>createNodeArray(nodes.slice(0, i), /*location*/ nodes);
|
||||
}
|
||||
|
||||
if (visited === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isNodeArrayNode<T>(visited)) {
|
||||
spreadNodeArrayNode(visited, updated, test);
|
||||
}
|
||||
else if (visited !== undefined) {
|
||||
Debug.assert(test(visited), "Wrong node type after visit.");
|
||||
updated.push(<T>visited);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updated && isModifiersArray(nodes)) {
|
||||
let flags: NodeFlags = 0;
|
||||
for (const node of updated) {
|
||||
flags |= modifierToFlag(node.kind);
|
||||
}
|
||||
|
||||
(<ModifiersArray><NodeArray<Node>>updated).flags = flags;
|
||||
}
|
||||
|
||||
return updated || nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits each child of a Node using the supplied visitor, possibly returning a new Node of the same kind in its place.
|
||||
*
|
||||
* @param node The Node whose children will be visited.
|
||||
* @param visitor The callback used to visit each child.
|
||||
* @param environment An optional lexical environment context for the visitor.
|
||||
*/
|
||||
export function visitEachChild<T extends Node>(node: T, visitor: (node: Node) => Node, environment?: LexicalEnvironment): T {
|
||||
if (node === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const isNewLexicalEnvironment = environment !== undefined && nodeStartsNewLexicalEnvironment(node);
|
||||
if (isNewLexicalEnvironment) {
|
||||
environment.startLexicalEnvironment();
|
||||
}
|
||||
|
||||
let updated: T & Map<any>;
|
||||
const edgeTraversalPath = nodeEdgeTraversalMap[node.kind];
|
||||
if (edgeTraversalPath) {
|
||||
for (const propertyName in edgeTraversalPath) {
|
||||
const value = (<Map<any>>node)[propertyName];
|
||||
if (value !== undefined) {
|
||||
const edge = edgeTraversalPath[propertyName];
|
||||
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[propertyName] = visited;
|
||||
}
|
||||
|
||||
if (visited && edge.array && isModifiersArray(<NodeArray<Node>>visited)) {
|
||||
updated.flags |= (<ModifiersArray>visited).flags;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updated === undefined) {
|
||||
updated = node;
|
||||
}
|
||||
|
||||
if (isNewLexicalEnvironment) {
|
||||
const declarations = environment.endLexicalEnvironment();
|
||||
if (declarations !== undefined && declarations.length > 0) {
|
||||
return <T>mergeLexicalEnvironment(updated, declarations, /*nodeIsMutable*/ updated !== node);
|
||||
}
|
||||
}
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a node edge.
|
||||
*
|
||||
* @param edge The edge of the Node.
|
||||
* @param value The Node or NodeArray value for the edge.
|
||||
* @param visitor A callback used to visit the node.
|
||||
*/
|
||||
function visitEdge(edge: NodeEdge, value: Node | NodeArray<Node>, visitor: (node: Node) => Node) {
|
||||
return edge.array
|
||||
? visitNodes(<NodeArray<Node>>value, visitor, edge.test)
|
||||
: visitNode(<Node>value, visitor, edge.test, edge.lift, edge.optional);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spreads a NodeArrayNode into a NodeArray.
|
||||
*
|
||||
* @param source The source NodeArrayNode.
|
||||
* @param dest The destination NodeArray.
|
||||
* @param test The node test used to validate each node.
|
||||
*/
|
||||
function spreadNodeArrayNode<T extends Node>(source: NodeArrayNode<T>, dest: NodeArray<T>, test: (node: Node) => boolean) {
|
||||
for (const element of source) {
|
||||
if (element === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isNodeArrayNode<T>(element)) {
|
||||
spreadNodeArrayNode(element, dest, test);
|
||||
}
|
||||
else {
|
||||
Debug.assert(test === undefined || test(element), "Wrong node type after visit.");
|
||||
dest.push(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a mutable Node for updates, setting the `original` pointer on the Node.
|
||||
*/
|
||||
function updateNode<T extends Node>(node: T, flags: NodeFlags) {
|
||||
const updated = cloneNode(node, /*location*/ node, flags, /*parent*/ undefined, /*original*/ node);
|
||||
updated.original = node;
|
||||
return updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge generated declarations of a lexical environment.
|
||||
*/
|
||||
function mergeLexicalEnvironment(node: Node, declarations: Statement[], nodeIsMutable: boolean) {
|
||||
const mutableNode = nodeIsMutable ? node : cloneNode(node, /*location*/ node, node.flags, /*parent*/ undefined, /*original*/ node);
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.SourceFile:
|
||||
mergeSourceFileLexicalEnvironment(<SourceFile>mutableNode, declarations);
|
||||
break;
|
||||
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
mergeModuleDeclarationLexicalEnvironment(<ModuleDeclaration>mutableNode, declarations);
|
||||
break;
|
||||
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.Constructor:
|
||||
mergeFunctionLikeLexicalEnvironment(<FunctionLikeDeclaration>mutableNode, declarations);
|
||||
break;
|
||||
|
||||
case SyntaxKind.ModuleBlock:
|
||||
case SyntaxKind.Block:
|
||||
mergeBlockLexicalEnvironment(<Block>mutableNode, declarations);
|
||||
break;
|
||||
}
|
||||
|
||||
return mutableNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge generated declarations of a lexical environment into a SourceFile.
|
||||
*/
|
||||
function mergeSourceFileLexicalEnvironment(node: SourceFile, declarations: Statement[]) {
|
||||
node.statements = mergeStatements(node.statements, declarations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge generated declarations of a lexical environment into a ModuleDeclaration.
|
||||
*/
|
||||
function mergeModuleDeclarationLexicalEnvironment(node: ModuleDeclaration, declarations: Statement[]) {
|
||||
Debug.assert(node.body.kind === SyntaxKind.ModuleBlock);
|
||||
node.body = <ModuleBlock>mergeLexicalEnvironment(node.body, declarations, /*nodeIsMutable*/ false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge generated declarations of a lexical environment into a FunctionLikeDeclaration.
|
||||
*/
|
||||
function mergeFunctionLikeLexicalEnvironment(node: FunctionLikeDeclaration, declarations: Statement[]) {
|
||||
Debug.assert(node.body !== undefined);
|
||||
if (node.body.kind === SyntaxKind.Block) {
|
||||
node.body = <Block>mergeLexicalEnvironment(node.body, declarations, /*nodeIsMutable*/ false);
|
||||
}
|
||||
else {
|
||||
node.body = createBlock([
|
||||
createReturn(<Expression>node.body),
|
||||
...declarations
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge generated declarations of a lexical environment into a FunctionBody or ModuleBlock.
|
||||
*/
|
||||
function mergeBlockLexicalEnvironment(node: FunctionBody | ModuleBlock, declarations: Statement[]) {
|
||||
node.statements = mergeStatements(node.statements, declarations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge generated declarations of a lexical environment into a NodeArray of Statement.
|
||||
*/
|
||||
function mergeStatements(statements: NodeArray<Statement>, declarations: Statement[]) {
|
||||
return createNodeArray(statements.concat(declarations), /*location*/ statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to lift a NodeArrayNode to a Node. This is primarily used to
|
||||
* lift multiple statements into a single Block.
|
||||
*
|
||||
* @param node The visited Node.
|
||||
* @param options Options used to control lift behavior.
|
||||
*/
|
||||
function liftNode(node: Node, lifter: (nodes: NodeArrayNode<Node>) => Node): Node {
|
||||
if (node === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
else if (isNodeArrayNode(node)) {
|
||||
const lift = lifter || extractSingleNode;
|
||||
return lift(node);
|
||||
}
|
||||
else {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifts a NodeArray containing only Statement nodes to a block.
|
||||
*
|
||||
* @param nodes The NodeArray.
|
||||
*/
|
||||
function liftToBlock(nodes: NodeArray<Node>) {
|
||||
Debug.assert(trueForAll(nodes, isStatementNode), "Cannot lift nodes to a Block.");
|
||||
return createBlock(<NodeArray<Statement>>nodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the single node from a NodeArray.
|
||||
*
|
||||
* @param nodes The NodeArray.
|
||||
*/
|
||||
function extractSingleNode(nodes: NodeArray<Node>) {
|
||||
Debug.assert(nodes.length <= 1, "Too many nodes written to output.");
|
||||
return nodes.length > 0 ? nodes[0] : undefined;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user