From 968a56924f7d723abee27b2d5268ef0a54b85ed8 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 8 Jan 2015 14:27:39 -0800 Subject: [PATCH 1/4] Type guard narrows type any in a primitive type check --- src/compiler/checker.ts | 15 ++--- src/compiler/types.ts | 2 - .../reference/typeGuardsWithAny.errors.txt | 49 +++++++++++++++ .../baselines/reference/typeGuardsWithAny.js | 61 +++++++++++++++++-- .../reference/typeGuardsWithAny.types | 23 ------- .../typeGuards/typeGuardsWithAny.ts | 33 +++++++++- 6 files changed, 142 insertions(+), 41 deletions(-) create mode 100644 tests/baselines/reference/typeGuardsWithAny.errors.txt delete mode 100644 tests/baselines/reference/typeGuardsWithAny.types diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2911322052da3..96fab3368ca0c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4589,8 +4589,8 @@ module ts { // Get the narrowed type of a given symbol at a given location function getNarrowedTypeOfSymbol(symbol: Symbol, node: Node) { var type = getTypeOfSymbol(symbol); - // Only narrow when symbol is variable of an object, union, or type parameter type - if (node && symbol.flags & SymbolFlags.Variable && type.flags & (TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter)) { + // Only narrow when symbol is variable of type any or an object, union, or type parameter type + if (node && symbol.flags & SymbolFlags.Variable && type.flags & (TypeFlags.Any | TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter)) { loop: while (node.parent) { var child = node; node = node.parent; @@ -4646,21 +4646,16 @@ module ts { if (expr.left.kind !== SyntaxKind.TypeOfExpression || expr.right.kind !== SyntaxKind.StringLiteral) { return type; } - var left = expr.left; var right = expr.right; - if (left.expression.kind !== SyntaxKind.Identifier || - getResolvedSymbol(left.expression) !== symbol) { - + if (left.expression.kind !== SyntaxKind.Identifier || getResolvedSymbol(left.expression) !== symbol) { return type; } - var t = right.text; var checkType: Type = t === "string" ? stringType : t === "number" ? numberType : t === "boolean" ? booleanType : emptyObjectType; if (expr.operator === SyntaxKind.ExclamationEqualsEqualsToken) { assumeTrue = !assumeTrue; } - if (assumeTrue) { // The assumed result is true. If check was for a primitive type, that type is the narrowed type. Otherwise we can // remove the primitive types from the narrowed type. @@ -4704,8 +4699,8 @@ module ts { } function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { - // Check that assumed result is true and we have variable symbol on the left - if (!assumeTrue || expr.left.kind !== SyntaxKind.Identifier || getResolvedSymbol(expr.left) !== symbol) { + // Check that type is not any, assumed result is true, and we have variable symbol on the left + if (type.flags & TypeFlags.Any || !assumeTrue || expr.left.kind !== SyntaxKind.Identifier || getResolvedSymbol(expr.left) !== symbol) { return type; } // Check that right operand is a function type with a prototype property diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 60ed8352b9a3d..9ebab8dc2207c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1331,8 +1331,6 @@ module ts { // Generic class and interface types export interface GenericType extends InterfaceType, TypeReference { instantiations: Map; // Generic instantiation cache - openReferenceTargets: GenericType[]; // Open type reference targets - openReferenceChecks: Map; // Open type reference check cache } export interface TupleType extends ObjectType { diff --git a/tests/baselines/reference/typeGuardsWithAny.errors.txt b/tests/baselines/reference/typeGuardsWithAny.errors.txt new file mode 100644 index 0000000000000..653c89c7554ad --- /dev/null +++ b/tests/baselines/reference/typeGuardsWithAny.errors.txt @@ -0,0 +1,49 @@ +tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts(11,7): error TS2339: Property 'p' does not exist on type 'string'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts(18,7): error TS2339: Property 'p' does not exist on type 'number'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts(25,7): error TS2339: Property 'p' does not exist on type 'boolean'. + + +==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts (3 errors) ==== + var x: any = { p: 0 }; + + if (x instanceof Object) { + x.p; // No error, type any unaffected by instanceof type guard + } + else { + x.p; // No error, type any unaffected by instanceof type guard + } + + if (typeof x === "string") { + x.p; // Error, type any narrowed by primitive type check + ~ +!!! error TS2339: Property 'p' does not exist on type 'string'. + } + else { + x.p; // No error, type unaffected in this branch + } + + if (typeof x === "number") { + x.p; // Error, type any narrowed by primitive type check + ~ +!!! error TS2339: Property 'p' does not exist on type 'number'. + } + else { + x.p; // No error, type unaffected in this branch + } + + if (typeof x === "boolean") { + x.p; // Error, type any narrowed by primitive type check + ~ +!!! error TS2339: Property 'p' does not exist on type 'boolean'. + } + else { + x.p; // No error, type unaffected in this branch + } + + if (typeof x === "object") { + x.p; // No error, type any only affected by primitive type check + } + else { + x.p; // No error, type unaffected in this branch + } + \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardsWithAny.js b/tests/baselines/reference/typeGuardsWithAny.js index 56676fb6d6884..be3d64b806fc1 100644 --- a/tests/baselines/reference/typeGuardsWithAny.js +++ b/tests/baselines/reference/typeGuardsWithAny.js @@ -1,18 +1,71 @@ //// [typeGuardsWithAny.ts] var x: any = { p: 0 }; + if (x instanceof Object) { - x.p; // No error, type any unaffected by type guard + x.p; // No error, type any unaffected by instanceof type guard } else { - x.p; // No error, type any unaffected by type guard + x.p; // No error, type any unaffected by instanceof type guard +} + +if (typeof x === "string") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} + +if (typeof x === "number") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} + +if (typeof x === "boolean") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} + +if (typeof x === "object") { + x.p; // No error, type any only affected by primitive type check +} +else { + x.p; // No error, type unaffected in this branch } //// [typeGuardsWithAny.js] var x = { p: 0 }; if (x instanceof Object) { - x.p; // No error, type any unaffected by type guard + x.p; // No error, type any unaffected by instanceof type guard +} +else { + x.p; // No error, type any unaffected by instanceof type guard +} +if (typeof x === "string") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} +if (typeof x === "number") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} +if (typeof x === "boolean") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} +if (typeof x === "object") { + x.p; // No error, type any only affected by primitive type check } else { - x.p; // No error, type any unaffected by type guard + x.p; // No error, type unaffected in this branch } diff --git a/tests/baselines/reference/typeGuardsWithAny.types b/tests/baselines/reference/typeGuardsWithAny.types deleted file mode 100644 index ee628c9c2497d..0000000000000 --- a/tests/baselines/reference/typeGuardsWithAny.types +++ /dev/null @@ -1,23 +0,0 @@ -=== tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts === -var x: any = { p: 0 }; ->x : any ->{ p: 0 } : { p: number; } ->p : number - -if (x instanceof Object) { ->x instanceof Object : boolean ->x : any ->Object : ObjectConstructor - - x.p; // No error, type any unaffected by type guard ->x.p : any ->x : any ->p : any -} -else { - x.p; // No error, type any unaffected by type guard ->x.p : any ->x : any ->p : any -} - diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts index e7e756ea8e041..a6379bf95b082 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts @@ -1,7 +1,36 @@ var x: any = { p: 0 }; + if (x instanceof Object) { - x.p; // No error, type any unaffected by type guard + x.p; // No error, type any unaffected by instanceof type guard } else { - x.p; // No error, type any unaffected by type guard + x.p; // No error, type any unaffected by instanceof type guard +} + +if (typeof x === "string") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} + +if (typeof x === "number") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} + +if (typeof x === "boolean") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} + +if (typeof x === "object") { + x.p; // No error, type any only affected by primitive type check +} +else { + x.p; // No error, type unaffected in this branch } From 78bb71f837ef2677e1ac541370dd19e6637741ec Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 8 Jan 2015 16:43:37 -0800 Subject: [PATCH 2/4] Optimizing forEachChild function to not create closures --- src/compiler/parser.ts | 307 +++++++++++++++++++++-------------------- 1 file changed, 156 insertions(+), 151 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 00cd54b94ebcb..87f4c20c97d38 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -12,43 +12,48 @@ module ts { return new (getNodeConstructor(kind))(); } + function child(cbNode: (node: Node) => T, node: Node): T { + return node ? cbNode(node) : void 0; + } + + function children1(cbNodes: (nodes: Node[]) => T, nodes: Node[]) { + return nodes ? cbNodes(nodes) : void 0; + } + + function children2(cbNode: (node: Node) => T, nodes: Node[]) { + if (nodes) { + for (var i = 0, len = nodes.length; i < len; i++) { + var result = cbNode(nodes[i]) + if (result) { + return result; + } + } + } + } + // Invokes a callback for each child of the given node. The 'cbNode' callback is invoked for all child nodes // stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; otherwise, // embedded arrays are flattened and the 'cbNode' callback is invoked for each element. If a callback returns // a truthy value, iteration stops and that value is returned. Otherwise, undefined is returned. export function forEachChild(node: Node, cbNode: (node: Node) => T, cbNodes?: (nodes: Node[]) => T): T { - function child(node: Node): T { - if (node) { - return cbNode(node); - } - } - function children(nodes: Node[]) { - if (nodes) { - if (cbNodes) { - return cbNodes(nodes); - } - - for (var i = 0, len = nodes.length; i < len; i++) { - var result = cbNode(nodes[i]) - if (result) { - return result; - } - } - - return undefined; - } - } if (!node) { return; } + if (cbNodes) { + var children = children1; + } + else { + cbNodes = cbNode; + children = children2; + } switch (node.kind) { case SyntaxKind.QualifiedName: - return child((node).left) || - child((node).right); + return child(cbNode, (node).left) || + child(cbNode, (node).right); case SyntaxKind.TypeParameter: - return child((node).name) || - child((node).constraint) || - child((node).expression); + return child(cbNode, (node).name) || + child(cbNode, (node).constraint) || + child(cbNode, (node).expression); case SyntaxKind.Parameter: case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: @@ -56,22 +61,22 @@ module ts { case SyntaxKind.ShorthandPropertyAssignment: case SyntaxKind.VariableDeclaration: case SyntaxKind.BindingElement: - return children(node.modifiers) || - child((node).propertyName) || - child((node).dotDotDotToken) || - child((node).name) || - child((node).questionToken) || - child((node).type) || - child((node).initializer); + return children(cbNodes, node.modifiers) || + child(cbNode, (node).propertyName) || + child(cbNode, (node).dotDotDotToken) || + child(cbNode, (node).name) || + child(cbNode, (node).questionToken) || + child(cbNode, (node).type) || + child(cbNode, (node).initializer); case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: case SyntaxKind.IndexSignature: - return children(node.modifiers) || - children((node).typeParameters) || - children((node).parameters) || - child((node).type); + return children(cbNodes, node.modifiers) || + children(cbNodes, (node).typeParameters) || + children(cbNodes, (node).parameters) || + child(cbNode, (node).type); case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: case SyntaxKind.Constructor: @@ -80,182 +85,182 @@ module ts { case SyntaxKind.FunctionExpression: case SyntaxKind.FunctionDeclaration: case SyntaxKind.ArrowFunction: - return children(node.modifiers) || - child((node).asteriskToken) || - child((node).name) || - child((node).questionToken) || - children((node).typeParameters) || - children((node).parameters) || - child((node).type) || - child((node).body); + return children(cbNodes, node.modifiers) || + child(cbNode, (node).asteriskToken) || + child(cbNode, (node).name) || + child(cbNode, (node).questionToken) || + children(cbNodes, (node).typeParameters) || + children(cbNodes, (node).parameters) || + child(cbNode, (node).type) || + child(cbNode, (node).body); case SyntaxKind.TypeReference: - return child((node).typeName) || - children((node).typeArguments); + return child(cbNode, (node).typeName) || + children(cbNodes, (node).typeArguments); case SyntaxKind.TypeQuery: - return child((node).exprName); + return child(cbNode, (node).exprName); case SyntaxKind.TypeLiteral: - return children((node).members); + return children(cbNodes, (node).members); case SyntaxKind.ArrayType: - return child((node).elementType); + return child(cbNode, (node).elementType); case SyntaxKind.TupleType: - return children((node).elementTypes); + return children(cbNodes, (node).elementTypes); case SyntaxKind.UnionType: - return children((node).types); + return children(cbNodes, (node).types); case SyntaxKind.ParenthesizedType: - return child((node).type); + return child(cbNode, (node).type); case SyntaxKind.ObjectBindingPattern: case SyntaxKind.ArrayBindingPattern: - return children((node).elements); + return children(cbNodes, (node).elements); case SyntaxKind.ArrayLiteralExpression: - return children((node).elements); + return children(cbNodes, (node).elements); case SyntaxKind.ObjectLiteralExpression: - return children((node).properties); + return children(cbNodes, (node).properties); case SyntaxKind.PropertyAccessExpression: - return child((node).expression) || - child((node).name); + return child(cbNode, (node).expression) || + child(cbNode, (node).name); case SyntaxKind.ElementAccessExpression: - return child((node).expression) || - child((node).argumentExpression); + return child(cbNode, (node).expression) || + child(cbNode, (node).argumentExpression); case SyntaxKind.CallExpression: case SyntaxKind.NewExpression: - return child((node).expression) || - children((node).typeArguments) || - children((node).arguments); + return child(cbNode, (node).expression) || + children(cbNodes, (node).typeArguments) || + children(cbNodes, (node).arguments); case SyntaxKind.TaggedTemplateExpression: - return child((node).tag) || - child((node).template); + return child(cbNode, (node).tag) || + child(cbNode, (node).template); case SyntaxKind.TypeAssertionExpression: - return child((node).type) || - child((node).expression); + return child(cbNode, (node).type) || + child(cbNode, (node).expression); case SyntaxKind.ParenthesizedExpression: - return child((node).expression); + return child(cbNode, (node).expression); case SyntaxKind.DeleteExpression: - return child((node).expression); + return child(cbNode, (node).expression); case SyntaxKind.TypeOfExpression: - return child((node).expression); + return child(cbNode, (node).expression); case SyntaxKind.VoidExpression: - return child((node).expression); + return child(cbNode, (node).expression); case SyntaxKind.PrefixUnaryExpression: - return child((node).operand); + return child(cbNode, (node).operand); case SyntaxKind.YieldExpression: - return child((node).asteriskToken) || - child((node).expression); + return child(cbNode, (node).asteriskToken) || + child(cbNode, (node).expression); case SyntaxKind.PostfixUnaryExpression: - return child((node).operand); + return child(cbNode, (node).operand); case SyntaxKind.BinaryExpression: - return child((node).left) || - child((node).right); + return child(cbNode, (node).left) || + child(cbNode, (node).right); case SyntaxKind.ConditionalExpression: - return child((node).condition) || - child((node).whenTrue) || - child((node).whenFalse); + return child(cbNode, (node).condition) || + child(cbNode, (node).whenTrue) || + child(cbNode, (node).whenFalse); case SyntaxKind.SpreadElementExpression: - return child((node).expression); + return child(cbNode, (node).expression); case SyntaxKind.Block: case SyntaxKind.ModuleBlock: - return children((node).statements); + return children(cbNodes, (node).statements); case SyntaxKind.SourceFile: - return children((node).statements) || - child((node).endOfFileToken); + return children(cbNodes, (node).statements) || + child(cbNode, (node).endOfFileToken); case SyntaxKind.VariableStatement: - return children(node.modifiers) || - child((node).declarationList); + return children(cbNodes, node.modifiers) || + child(cbNode, (node).declarationList); case SyntaxKind.VariableDeclarationList: - return children((node).declarations); + return children(cbNodes, (node).declarations); case SyntaxKind.ExpressionStatement: - return child((node).expression); + return child(cbNode, (node).expression); case SyntaxKind.IfStatement: - return child((node).expression) || - child((node).thenStatement) || - child((node).elseStatement); + return child(cbNode, (node).expression) || + child(cbNode, (node).thenStatement) || + child(cbNode, (node).elseStatement); case SyntaxKind.DoStatement: - return child((node).statement) || - child((node).expression); + return child(cbNode, (node).statement) || + child(cbNode, (node).expression); case SyntaxKind.WhileStatement: - return child((node).expression) || - child((node).statement); + return child(cbNode, (node).expression) || + child(cbNode, (node).statement); case SyntaxKind.ForStatement: - return child((node).initializer) || - child((node).condition) || - child((node).iterator) || - child((node).statement); + return child(cbNode, (node).initializer) || + child(cbNode, (node).condition) || + child(cbNode, (node).iterator) || + child(cbNode, (node).statement); case SyntaxKind.ForInStatement: - return child((node).initializer) || - child((node).expression) || - child((node).statement); + return child(cbNode, (node).initializer) || + child(cbNode, (node).expression) || + child(cbNode, (node).statement); case SyntaxKind.ContinueStatement: case SyntaxKind.BreakStatement: - return child((node).label); + return child(cbNode, (node).label); case SyntaxKind.ReturnStatement: - return child((node).expression); + return child(cbNode, (node).expression); case SyntaxKind.WithStatement: - return child((node).expression) || - child((node).statement); + return child(cbNode, (node).expression) || + child(cbNode, (node).statement); case SyntaxKind.SwitchStatement: - return child((node).expression) || - children((node).clauses); + return child(cbNode, (node).expression) || + children(cbNodes, (node).clauses); case SyntaxKind.CaseClause: - return child((node).expression) || - children((node).statements); + return child(cbNode, (node).expression) || + children(cbNodes, (node).statements); case SyntaxKind.DefaultClause: - return children((node).statements); + return children(cbNodes, (node).statements); case SyntaxKind.LabeledStatement: - return child((node).label) || - child((node).statement); + return child(cbNode, (node).label) || + child(cbNode, (node).statement); case SyntaxKind.ThrowStatement: - return child((node).expression); + return child(cbNode, (node).expression); case SyntaxKind.TryStatement: - return child((node).tryBlock) || - child((node).catchClause) || - child((node).finallyBlock); + return child(cbNode, (node).tryBlock) || + child(cbNode, (node).catchClause) || + child(cbNode, (node).finallyBlock); case SyntaxKind.CatchClause: - return child((node).name) || - child((node).type) || - child((node).block); + return child(cbNode, (node).name) || + child(cbNode, (node).type) || + child(cbNode, (node).block); case SyntaxKind.ClassDeclaration: - return children(node.modifiers) || - child((node).name) || - children((node).typeParameters) || - children((node).heritageClauses) || - children((node).members); + return children(cbNodes, node.modifiers) || + child(cbNode, (node).name) || + children(cbNodes, (node).typeParameters) || + children(cbNodes, (node).heritageClauses) || + children(cbNodes, (node).members); case SyntaxKind.InterfaceDeclaration: - return children(node.modifiers) || - child((node).name) || - children((node).typeParameters) || - children((node).heritageClauses) || - children((node).members); + return children(cbNodes, node.modifiers) || + child(cbNode, (node).name) || + children(cbNodes, (node).typeParameters) || + children(cbNodes, (node).heritageClauses) || + children(cbNodes, (node).members); case SyntaxKind.TypeAliasDeclaration: - return children(node.modifiers) || - child((node).name) || - child((node).type); + return children(cbNodes, node.modifiers) || + child(cbNode, (node).name) || + child(cbNode, (node).type); case SyntaxKind.EnumDeclaration: - return children(node.modifiers) || - child((node).name) || - children((node).members); + return children(cbNodes, node.modifiers) || + child(cbNode, (node).name) || + children(cbNodes, (node).members); case SyntaxKind.EnumMember: - return child((node).name) || - child((node).initializer); + return child(cbNode, (node).name) || + child(cbNode, (node).initializer); case SyntaxKind.ModuleDeclaration: - return children(node.modifiers) || - child((node).name) || - child((node).body); + return children(cbNodes, node.modifiers) || + child(cbNode, (node).name) || + child(cbNode, (node).body); case SyntaxKind.ImportDeclaration: - return children(node.modifiers) || - child((node).name) || - child((node).moduleReference); + return children(cbNodes, node.modifiers) || + child(cbNode, (node).name) || + child(cbNode, (node).moduleReference); case SyntaxKind.ExportAssignment: - return children(node.modifiers) || - child((node).exportName); + return children(cbNodes, node.modifiers) || + child(cbNode, (node).exportName); case SyntaxKind.TemplateExpression: - return child((node).head) || children((node).templateSpans); + return child(cbNode, (node).head) || children(cbNodes, (node).templateSpans); case SyntaxKind.TemplateSpan: - return child((node).expression) || child((node).literal); + return child(cbNode, (node).expression) || child(cbNode, (node).literal); case SyntaxKind.ComputedPropertyName: - return child((node).expression); + return child(cbNode, (node).expression); case SyntaxKind.HeritageClause: - return children((node).types); + return children(cbNodes, (node).types); case SyntaxKind.ExternalModuleReference: - return child((node).expression); + return child(cbNode, (node).expression); } } From bdfb655d6659d913f09067cad859b3bc70bb33aa Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 8 Jan 2015 18:46:48 -0800 Subject: [PATCH 3/4] Renaming helpers and cleaning up logic --- src/compiler/parser.ts | 279 ++++++++++++++++++++--------------------- 1 file changed, 137 insertions(+), 142 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 87f4c20c97d38..3fd2bbf4676c9 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -12,18 +12,18 @@ module ts { return new (getNodeConstructor(kind))(); } - function child(cbNode: (node: Node) => T, node: Node): T { + function visitNode(cbNode: (node: Node) => T, node: Node): T { return node ? cbNode(node) : void 0; } - function children1(cbNodes: (nodes: Node[]) => T, nodes: Node[]) { + function visitNodeArray(cbNodes: (nodes: Node[]) => T, nodes: Node[]) { return nodes ? cbNodes(nodes) : void 0; } - function children2(cbNode: (node: Node) => T, nodes: Node[]) { + function visitEachNode(cbNode: (node: Node) => T, nodes: Node[]) { if (nodes) { for (var i = 0, len = nodes.length; i < len; i++) { - var result = cbNode(nodes[i]) + var result = cbNode(nodes[i]); if (result) { return result; } @@ -35,25 +35,20 @@ module ts { // stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; otherwise, // embedded arrays are flattened and the 'cbNode' callback is invoked for each element. If a callback returns // a truthy value, iteration stops and that value is returned. Otherwise, undefined is returned. - export function forEachChild(node: Node, cbNode: (node: Node) => T, cbNodes?: (nodes: Node[]) => T): T { + export function forEachChild(node: Node, cbNode: (node: Node) => T, cbNodeArray?: (nodes: Node[]) => T): T { if (!node) { return; } - if (cbNodes) { - var children = children1; - } - else { - cbNodes = cbNode; - children = children2; - } + var visitNodes: (cb: (node: Node | Node[]) => T, nodes: Node[]) => T = cbNodeArray ? visitNodeArray : visitEachNode; + var cbNodes = cbNodeArray || cbNode; switch (node.kind) { case SyntaxKind.QualifiedName: - return child(cbNode, (node).left) || - child(cbNode, (node).right); + return visitNode(cbNode, (node).left) || + visitNode(cbNode, (node).right); case SyntaxKind.TypeParameter: - return child(cbNode, (node).name) || - child(cbNode, (node).constraint) || - child(cbNode, (node).expression); + return visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).constraint) || + visitNode(cbNode, (node).expression); case SyntaxKind.Parameter: case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: @@ -61,22 +56,22 @@ module ts { case SyntaxKind.ShorthandPropertyAssignment: case SyntaxKind.VariableDeclaration: case SyntaxKind.BindingElement: - return children(cbNodes, node.modifiers) || - child(cbNode, (node).propertyName) || - child(cbNode, (node).dotDotDotToken) || - child(cbNode, (node).name) || - child(cbNode, (node).questionToken) || - child(cbNode, (node).type) || - child(cbNode, (node).initializer); + return visitNodes(cbNodes, node.modifiers) || + visitNode(cbNode, (node).propertyName) || + visitNode(cbNode, (node).dotDotDotToken) || + visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).questionToken) || + visitNode(cbNode, (node).type) || + visitNode(cbNode, (node).initializer); case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: case SyntaxKind.IndexSignature: - return children(cbNodes, node.modifiers) || - children(cbNodes, (node).typeParameters) || - children(cbNodes, (node).parameters) || - child(cbNode, (node).type); + return visitNodes(cbNodes, node.modifiers) || + visitNodes(cbNodes, (node).typeParameters) || + visitNodes(cbNodes, (node).parameters) || + visitNode(cbNode, (node).type); case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: case SyntaxKind.Constructor: @@ -85,182 +80,182 @@ module ts { case SyntaxKind.FunctionExpression: case SyntaxKind.FunctionDeclaration: case SyntaxKind.ArrowFunction: - return children(cbNodes, node.modifiers) || - child(cbNode, (node).asteriskToken) || - child(cbNode, (node).name) || - child(cbNode, (node).questionToken) || - children(cbNodes, (node).typeParameters) || - children(cbNodes, (node).parameters) || - child(cbNode, (node).type) || - child(cbNode, (node).body); + return visitNodes(cbNodes, node.modifiers) || + visitNode(cbNode, (node).asteriskToken) || + visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).questionToken) || + visitNodes(cbNodes, (node).typeParameters) || + visitNodes(cbNodes, (node).parameters) || + visitNode(cbNode, (node).type) || + visitNode(cbNode, (node).body); case SyntaxKind.TypeReference: - return child(cbNode, (node).typeName) || - children(cbNodes, (node).typeArguments); + return visitNode(cbNode, (node).typeName) || + visitNodes(cbNodes, (node).typeArguments); case SyntaxKind.TypeQuery: - return child(cbNode, (node).exprName); + return visitNode(cbNode, (node).exprName); case SyntaxKind.TypeLiteral: - return children(cbNodes, (node).members); + return visitNodes(cbNodes, (node).members); case SyntaxKind.ArrayType: - return child(cbNode, (node).elementType); + return visitNode(cbNode, (node).elementType); case SyntaxKind.TupleType: - return children(cbNodes, (node).elementTypes); + return visitNodes(cbNodes, (node).elementTypes); case SyntaxKind.UnionType: - return children(cbNodes, (node).types); + return visitNodes(cbNodes, (node).types); case SyntaxKind.ParenthesizedType: - return child(cbNode, (node).type); + return visitNode(cbNode, (node).type); case SyntaxKind.ObjectBindingPattern: case SyntaxKind.ArrayBindingPattern: - return children(cbNodes, (node).elements); + return visitNodes(cbNodes, (node).elements); case SyntaxKind.ArrayLiteralExpression: - return children(cbNodes, (node).elements); + return visitNodes(cbNodes, (node).elements); case SyntaxKind.ObjectLiteralExpression: - return children(cbNodes, (node).properties); + return visitNodes(cbNodes, (node).properties); case SyntaxKind.PropertyAccessExpression: - return child(cbNode, (node).expression) || - child(cbNode, (node).name); + return visitNode(cbNode, (node).expression) || + visitNode(cbNode, (node).name); case SyntaxKind.ElementAccessExpression: - return child(cbNode, (node).expression) || - child(cbNode, (node).argumentExpression); + return visitNode(cbNode, (node).expression) || + visitNode(cbNode, (node).argumentExpression); case SyntaxKind.CallExpression: case SyntaxKind.NewExpression: - return child(cbNode, (node).expression) || - children(cbNodes, (node).typeArguments) || - children(cbNodes, (node).arguments); + return visitNode(cbNode, (node).expression) || + visitNodes(cbNodes, (node).typeArguments) || + visitNodes(cbNodes, (node).arguments); case SyntaxKind.TaggedTemplateExpression: - return child(cbNode, (node).tag) || - child(cbNode, (node).template); + return visitNode(cbNode, (node).tag) || + visitNode(cbNode, (node).template); case SyntaxKind.TypeAssertionExpression: - return child(cbNode, (node).type) || - child(cbNode, (node).expression); + return visitNode(cbNode, (node).type) || + visitNode(cbNode, (node).expression); case SyntaxKind.ParenthesizedExpression: - return child(cbNode, (node).expression); + return visitNode(cbNode, (node).expression); case SyntaxKind.DeleteExpression: - return child(cbNode, (node).expression); + return visitNode(cbNode, (node).expression); case SyntaxKind.TypeOfExpression: - return child(cbNode, (node).expression); + return visitNode(cbNode, (node).expression); case SyntaxKind.VoidExpression: - return child(cbNode, (node).expression); + return visitNode(cbNode, (node).expression); case SyntaxKind.PrefixUnaryExpression: - return child(cbNode, (node).operand); + return visitNode(cbNode, (node).operand); case SyntaxKind.YieldExpression: - return child(cbNode, (node).asteriskToken) || - child(cbNode, (node).expression); + return visitNode(cbNode, (node).asteriskToken) || + visitNode(cbNode, (node).expression); case SyntaxKind.PostfixUnaryExpression: - return child(cbNode, (node).operand); + return visitNode(cbNode, (node).operand); case SyntaxKind.BinaryExpression: - return child(cbNode, (node).left) || - child(cbNode, (node).right); + return visitNode(cbNode, (node).left) || + visitNode(cbNode, (node).right); case SyntaxKind.ConditionalExpression: - return child(cbNode, (node).condition) || - child(cbNode, (node).whenTrue) || - child(cbNode, (node).whenFalse); + return visitNode(cbNode, (node).condition) || + visitNode(cbNode, (node).whenTrue) || + visitNode(cbNode, (node).whenFalse); case SyntaxKind.SpreadElementExpression: - return child(cbNode, (node).expression); + return visitNode(cbNode, (node).expression); case SyntaxKind.Block: case SyntaxKind.ModuleBlock: - return children(cbNodes, (node).statements); + return visitNodes(cbNodes, (node).statements); case SyntaxKind.SourceFile: - return children(cbNodes, (node).statements) || - child(cbNode, (node).endOfFileToken); + return visitNodes(cbNodes, (node).statements) || + visitNode(cbNode, (node).endOfFileToken); case SyntaxKind.VariableStatement: - return children(cbNodes, node.modifiers) || - child(cbNode, (node).declarationList); + return visitNodes(cbNodes, node.modifiers) || + visitNode(cbNode, (node).declarationList); case SyntaxKind.VariableDeclarationList: - return children(cbNodes, (node).declarations); + return visitNodes(cbNodes, (node).declarations); case SyntaxKind.ExpressionStatement: - return child(cbNode, (node).expression); + return visitNode(cbNode, (node).expression); case SyntaxKind.IfStatement: - return child(cbNode, (node).expression) || - child(cbNode, (node).thenStatement) || - child(cbNode, (node).elseStatement); + return visitNode(cbNode, (node).expression) || + visitNode(cbNode, (node).thenStatement) || + visitNode(cbNode, (node).elseStatement); case SyntaxKind.DoStatement: - return child(cbNode, (node).statement) || - child(cbNode, (node).expression); + return visitNode(cbNode, (node).statement) || + visitNode(cbNode, (node).expression); case SyntaxKind.WhileStatement: - return child(cbNode, (node).expression) || - child(cbNode, (node).statement); + return visitNode(cbNode, (node).expression) || + visitNode(cbNode, (node).statement); case SyntaxKind.ForStatement: - return child(cbNode, (node).initializer) || - child(cbNode, (node).condition) || - child(cbNode, (node).iterator) || - child(cbNode, (node).statement); + return visitNode(cbNode, (node).initializer) || + visitNode(cbNode, (node).condition) || + visitNode(cbNode, (node).iterator) || + visitNode(cbNode, (node).statement); case SyntaxKind.ForInStatement: - return child(cbNode, (node).initializer) || - child(cbNode, (node).expression) || - child(cbNode, (node).statement); + return visitNode(cbNode, (node).initializer) || + visitNode(cbNode, (node).expression) || + visitNode(cbNode, (node).statement); case SyntaxKind.ContinueStatement: case SyntaxKind.BreakStatement: - return child(cbNode, (node).label); + return visitNode(cbNode, (node).label); case SyntaxKind.ReturnStatement: - return child(cbNode, (node).expression); + return visitNode(cbNode, (node).expression); case SyntaxKind.WithStatement: - return child(cbNode, (node).expression) || - child(cbNode, (node).statement); + return visitNode(cbNode, (node).expression) || + visitNode(cbNode, (node).statement); case SyntaxKind.SwitchStatement: - return child(cbNode, (node).expression) || - children(cbNodes, (node).clauses); + return visitNode(cbNode, (node).expression) || + visitNodes(cbNodes, (node).clauses); case SyntaxKind.CaseClause: - return child(cbNode, (node).expression) || - children(cbNodes, (node).statements); + return visitNode(cbNode, (node).expression) || + visitNodes(cbNodes, (node).statements); case SyntaxKind.DefaultClause: - return children(cbNodes, (node).statements); + return visitNodes(cbNodes, (node).statements); case SyntaxKind.LabeledStatement: - return child(cbNode, (node).label) || - child(cbNode, (node).statement); + return visitNode(cbNode, (node).label) || + visitNode(cbNode, (node).statement); case SyntaxKind.ThrowStatement: - return child(cbNode, (node).expression); + return visitNode(cbNode, (node).expression); case SyntaxKind.TryStatement: - return child(cbNode, (node).tryBlock) || - child(cbNode, (node).catchClause) || - child(cbNode, (node).finallyBlock); + return visitNode(cbNode, (node).tryBlock) || + visitNode(cbNode, (node).catchClause) || + visitNode(cbNode, (node).finallyBlock); case SyntaxKind.CatchClause: - return child(cbNode, (node).name) || - child(cbNode, (node).type) || - child(cbNode, (node).block); + return visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).type) || + visitNode(cbNode, (node).block); case SyntaxKind.ClassDeclaration: - return children(cbNodes, node.modifiers) || - child(cbNode, (node).name) || - children(cbNodes, (node).typeParameters) || - children(cbNodes, (node).heritageClauses) || - children(cbNodes, (node).members); + return visitNodes(cbNodes, node.modifiers) || + visitNode(cbNode, (node).name) || + visitNodes(cbNodes, (node).typeParameters) || + visitNodes(cbNodes, (node).heritageClauses) || + visitNodes(cbNodes, (node).members); case SyntaxKind.InterfaceDeclaration: - return children(cbNodes, node.modifiers) || - child(cbNode, (node).name) || - children(cbNodes, (node).typeParameters) || - children(cbNodes, (node).heritageClauses) || - children(cbNodes, (node).members); + return visitNodes(cbNodes, node.modifiers) || + visitNode(cbNode, (node).name) || + visitNodes(cbNodes, (node).typeParameters) || + visitNodes(cbNodes, (node).heritageClauses) || + visitNodes(cbNodes, (node).members); case SyntaxKind.TypeAliasDeclaration: - return children(cbNodes, node.modifiers) || - child(cbNode, (node).name) || - child(cbNode, (node).type); + return visitNodes(cbNodes, node.modifiers) || + visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).type); case SyntaxKind.EnumDeclaration: - return children(cbNodes, node.modifiers) || - child(cbNode, (node).name) || - children(cbNodes, (node).members); + return visitNodes(cbNodes, node.modifiers) || + visitNode(cbNode, (node).name) || + visitNodes(cbNodes, (node).members); case SyntaxKind.EnumMember: - return child(cbNode, (node).name) || - child(cbNode, (node).initializer); + return visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).initializer); case SyntaxKind.ModuleDeclaration: - return children(cbNodes, node.modifiers) || - child(cbNode, (node).name) || - child(cbNode, (node).body); + return visitNodes(cbNodes, node.modifiers) || + visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).body); case SyntaxKind.ImportDeclaration: - return children(cbNodes, node.modifiers) || - child(cbNode, (node).name) || - child(cbNode, (node).moduleReference); + return visitNodes(cbNodes, node.modifiers) || + visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).moduleReference); case SyntaxKind.ExportAssignment: - return children(cbNodes, node.modifiers) || - child(cbNode, (node).exportName); + return visitNodes(cbNodes, node.modifiers) || + visitNode(cbNode, (node).exportName); case SyntaxKind.TemplateExpression: - return child(cbNode, (node).head) || children(cbNodes, (node).templateSpans); + return visitNode(cbNode, (node).head) || visitNodes(cbNodes, (node).templateSpans); case SyntaxKind.TemplateSpan: - return child(cbNode, (node).expression) || child(cbNode, (node).literal); + return visitNode(cbNode, (node).expression) || visitNode(cbNode, (node).literal); case SyntaxKind.ComputedPropertyName: - return child(cbNode, (node).expression); + return visitNode(cbNode, (node).expression); case SyntaxKind.HeritageClause: - return children(cbNodes, (node).types); + return visitNodes(cbNodes, (node).types); case SyntaxKind.ExternalModuleReference: - return child(cbNode, (node).expression); + return visitNode(cbNode, (node).expression); } } From a8cf58939bb7aabb36ee6c43bf0d36ee33a2ff3c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 9 Jan 2015 06:52:24 -0800 Subject: [PATCH 4/4] Adding comment --- src/compiler/parser.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 3fd2bbf4676c9..b70c4a7a1d5cb 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -13,11 +13,15 @@ module ts { } function visitNode(cbNode: (node: Node) => T, node: Node): T { - return node ? cbNode(node) : void 0; + if (node) { + return cbNode(node); + } } function visitNodeArray(cbNodes: (nodes: Node[]) => T, nodes: Node[]) { - return nodes ? cbNodes(nodes) : void 0; + if (nodes) { + return cbNodes(nodes); + } } function visitEachNode(cbNode: (node: Node) => T, nodes: Node[]) { @@ -39,6 +43,9 @@ module ts { if (!node) { return; } + // The visitXXX functions could be written as local functions that close over the cbNode and cbNodeArray + // callback parameters, but that causes a closure allocation for each invocation with noticeable effects + // on performance. var visitNodes: (cb: (node: Node | Node[]) => T, nodes: Node[]) => T = cbNodeArray ? visitNodeArray : visitEachNode; var cbNodes = cbNodeArray || cbNode; switch (node.kind) {