diff --git a/Jakefile.js b/Jakefile.js index f339f567206..5131c2e3cae 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -41,6 +41,8 @@ var compilerSources = [ "utilities.ts", "binder.ts", "checker.ts", + "transform.ts", + "transform.generated.ts", "declarationEmitter.ts", "emitter.ts", "program.ts", @@ -60,6 +62,8 @@ var servicesSources = [ "utilities.ts", "binder.ts", "checker.ts", + "transform.ts", + "transform.generated.ts", "declarationEmitter.ts", "emitter.ts", "program.ts", @@ -357,6 +361,7 @@ var processTypesJs = path.join(scriptsDirectory, "processTypes.js"); var processTypesTs = path.join(scriptsDirectory, "processTypes.ts"); var typesTs = path.join(compilerDirectory, "types.ts"); var factoryGeneratedTs = path.join(compilerDirectory, "factory.generated.ts"); +var transformGeneratedTs = path.join(compilerDirectory, "transform.generated.ts"); file(processTypesTs); @@ -384,8 +389,10 @@ file(factoryGeneratedTs, [processTypesJs, typesTs], function() { ex.run(); }, { async: true }); +file(transformGeneratedTs, [factoryGeneratedTs]); + desc("Generates a TypeScript file that contains factory methods to create each Syntax Node.") -task("generate-factory", [factoryGeneratedTs]); +task("generate-factory", [factoryGeneratedTs, transformGeneratedTs]); // Local target to build the compiler and services var tscFile = path.join(builtLocalDirectory, compilerFilename); diff --git a/scripts/processTypes.ts b/scripts/processTypes.ts index 605911c7005..74a9f97023e 100644 --- a/scripts/processTypes.ts +++ b/scripts/processTypes.ts @@ -12,6 +12,7 @@ import { getSymbolId, getProperty, hasProperty, + createSourceFile, map, SyntaxKind, CompilerOptions, @@ -24,6 +25,7 @@ import { SourceFile, Declaration, ModuleDeclaration, + ModuleBlock, InterfaceDeclaration, TypeAliasDeclaration, EnumDeclaration, @@ -33,6 +35,15 @@ import { TypeReferenceNode, UnionTypeNode, ExpressionWithTypeArguments, + ExpressionStatement, + Expression, + CallExpression, + PropertyAccessExpression, + ObjectLiteralExpression, + ArrayLiteralExpression, + PropertyAssignment, + LiteralExpression, + Identifier, SymbolFlags, Symbol, SymbolTable, @@ -45,16 +56,17 @@ import { interface SyntaxNode { kind?: SyntaxKind; - kindText?: string; + kindName?: string; typeName?: string; members?: SyntaxMember[]; + options?: KindOptions; } interface SyntaxMember { - param?: string; - property?: string; - type?: string; - elementType?: string; + paramName?: string; + propertyName?: string; + typeName?: string; + elementTypeName?: string; isFactoryParam?: boolean; isNode?: boolean; isNodeArray?: boolean; @@ -85,18 +97,27 @@ interface FactoryParamAnnotation extends Annotation { propertyName?: string; } +interface KindOptions { + create: boolean; + update: boolean; + test: boolean; +} + interface KindAnnotation extends Annotation { kind: SyntaxKind; + options: KindOptions; } const columnWrap = 150; const emptyArray: any[] = []; const kindPattern = /@kind\s*\(\s*SyntaxKind\.(\w+)\s*\)/g; -const annotationPattern = /@(\w+)\s*(\([^)]*\))?/g; +const annotationPattern = /@(\w+\s*[^\r\n]*)/g; const annotationArgumentPattern = /[(,]([^,)]+)/g; const whitespacePattern = /\s/; const quotePattern = /["'`]/; const numberPattern = /^[+-]?(\d+|\d*\.\d+|\d*e\d+|0x[\da-f]+)$/i; +const arrayPattern = /^\[.*\]$/; +const objectPattern = /^\{.*\}$/; let file: string; let options: CompilerOptions; @@ -104,7 +125,6 @@ let host: CompilerHost; let program: Program; let checker: TypeChecker; let sourceFile: SourceFile; -let writer: EmitTextWriter; let tsModuleSymbol: Symbol; let nodeSymbol: Symbol; let nodeArraySymbol: Symbol; @@ -113,6 +133,9 @@ let syntaxKindSymbol: Symbol; let syntaxKindValueToSymbol: Symbol[] = []; let nodeAnnotations: Annotation[][] = []; let syntax: SyntaxNode[] = []; +let syntaxKindTypeUsages: Map = {}; +let memberTypeUsages: Map = {}; +let memberTypeUsageRedirects: Map = {}; main(); @@ -133,8 +156,11 @@ function main() { discover(); let inputDirectory = sys.resolvePath(combinePaths(file, "..")); - let output = combinePaths(inputDirectory, "factory.generated.ts"); - generate(output); + let factoryOutputFile = combinePaths(inputDirectory, "factory.generated.ts"); + generateFactory(factoryOutputFile); + + let transformOutputFile = combinePaths(inputDirectory, "transform.generated.ts"); + generateTransform(transformOutputFile); } /** @@ -148,6 +174,13 @@ function discover() { return a.kind - b.kind; }); + // Set up member type usage redirects for types with a single kind + for (let typeName in syntaxKindTypeUsages) { + if (syntaxKindTypeUsages[typeName].length === 1) { + memberTypeUsageRedirects[typeName] = syntaxKindTypeUsages[typeName][0].name; + } + } + function visit(node: Node) { switch (node.kind) { case SyntaxKind.SourceFile: @@ -231,16 +264,19 @@ function discover() { } } - function createSyntaxNodes(decl: InterfaceDeclaration | TypeAliasDeclaration, symbol: Symbol, kinds: SyntaxKind[]) { + function createSyntaxNodes(decl: InterfaceDeclaration | TypeAliasDeclaration, symbol: Symbol, kinds: KindAnnotation[]) { if (getFactoryHiddenStateForSymbol(symbol) === FactoryHiddenState.Hidden) { return; } let symbolOrder = getFactoryOrder(symbol, /*inherited*/ true); - for (let kind of kinds) { - let type = checker.getDeclaredTypeOfSymbol(symbol); + for (let kindAnnotation of kinds) { + let kind = kindAnnotation.kind; let kindSymbol = syntaxKindValueToSymbol[kind]; + recordTypeUsagesForKind(kindSymbol, symbol); + let kindOrder = getFactoryOrder(kindSymbol, /*inherited*/ false); + let type = checker.getDeclaredTypeOfSymbol(symbol); var members: SyntaxMember[] = []; for (let property of checker.getPropertiesOfType(type)) { // Skip any hidden properties @@ -255,16 +291,22 @@ function discover() { let propertyIsNodeArray = typeNode && isNodeArray(typeNode); let propertyIsModifiersArray = typeNode && isModifiersArray(typeNode); if (propertyIsFactoryParam || propertyIsNodeArray || propertyIsModifiersArray || propertyIsNode) { + let typeName = typeNode ? normalizeTypeName(typeNode.getText()) : "any"; + let elementTypeName = propertyIsNodeArray ? (typeNode).typeArguments[0].getText() : undefined; members.push({ - property: property.name, - param: property.name === "arguments" ? "_arguments" : property.name, - type: typeNode ? typeNode.getText() : "any", - elementType: propertyIsNodeArray ? (typeNode).typeArguments[0].getText() : undefined, + propertyName: property.name, + paramName: property.name === "arguments" ? "_arguments" : property.name, + typeName: typeName, + elementTypeName: elementTypeName, isFactoryParam: propertyIsFactoryParam, isNodeArray: propertyIsNodeArray, isModifiersArray: propertyIsModifiersArray, isNode: propertyIsNode }); + + if (!propertyIsFactoryParam && (propertyIsNodeArray || propertyIsNode)) { + recordTypeUsageForMember(propertyIsNode ? typeName : elementTypeName); + } } } @@ -272,8 +314,8 @@ function discover() { if (overrides) { let indices = members.map((_, i) => i); indices.sort((a, b) => { - let aOverride = overrides.indexOf(members[a].property); - let bOverride = overrides.indexOf(members[b].property); + let aOverride = overrides.indexOf(members[a].propertyName); + let bOverride = overrides.indexOf(members[b].propertyName); if (aOverride >= 0) { if (bOverride >= 0) { return aOverride - bOverride; @@ -293,347 +335,54 @@ function discover() { syntax.push({ kind, - kindText: kindSymbol.name, + kindName: kindSymbol.name, typeName: symbol.name, - members + members, + options: kindAnnotation.options }); } } + function recordTypeUsagesForKind(kindSymbol: Symbol, typeSymbol: Symbol) { + memberTypeUsages[kindSymbol.name] = false; + recordTypeUsagesForKindWorker(kindSymbol, typeSymbol); + } + + function recordTypeUsagesForKindWorker(kindSymbol: Symbol, typeSymbol: Symbol) { + let usages = syntaxKindTypeUsages[typeSymbol.name]; + if (!usages) { + syntaxKindTypeUsages[typeSymbol.name] = usages = []; + } + + if (usages.indexOf(kindSymbol) === -1) { + usages.push(kindSymbol); + } + + for (let superType of getSuperTypes(typeSymbol.declarations[0])) { + recordTypeUsagesForKindWorker(kindSymbol, superType); + } + } + + function recordTypeUsageForMember(typeName: string) { + if (!hasProperty(memberTypeUsages, typeName)) { + memberTypeUsages[typeName] = true; + } + } + + function normalizeTypeName(typeName: string) { + let parts = typeName.split(/\s*\|\s*/g); + if (parts.length === 0) { + return parts[0]; + } + + parts.sort(); + return parts.join(" | "); + } + function getTypeNodeForProperty(property: Symbol) { return (property.declarations[0]).type; } - function isTypeReferenceNode(node: Node): node is TypeReferenceNode { - return node ? node.kind === SyntaxKind.TypeReference : false; - } - - function isUnionTypeNode(node: Node): node is UnionTypeNode { - return node ? node.kind === SyntaxKind.UnionType : false; - } - - function isInterfaceDeclaration(node: Node): node is InterfaceDeclaration { - return node ? node.kind === SyntaxKind.InterfaceDeclaration : false; - } - - function isTypeAliasDeclaration(node: Node): node is TypeAliasDeclaration { - return node ? node.kind === SyntaxKind.TypeAliasDeclaration : false; - } - - function isExpressionWithTypeArguments(node: Node): node is ExpressionWithTypeArguments { - return node ? node.kind === SyntaxKind.ExpressionWithTypeArguments : false; - } - - function isNodeArray(typeNode: TypeNode): boolean { - return isTypeReferenceNode(typeNode) ? checker.getSymbolAtLocation(typeNode.typeName) === nodeArraySymbol : false; - } - - function isModifiersArray(typeNode: TypeNode): boolean { - return isTypeReferenceNode(typeNode) ? checker.getSymbolAtLocation(typeNode.typeName) === modifiersArraySymbol : false; - } - - function getSuperTypes(node: Declaration) { - let superTypes: Symbol[] = []; - let superTypeSymbolSet: boolean[] = []; - - if (isTypeAliasDeclaration(node)) { - fillSuperTypes(node.type); - } - else if (isInterfaceDeclaration(node) && node.heritageClauses) { - for (let superType of node.heritageClauses[0].types) { - fillSuperTypes(superType); - } - } - - return superTypes; - - function fillSuperTypes(node: TypeNode) { - if (isUnionTypeNode(node)) { - // Flatten union types - for (let constituentType of node.types) { - fillSuperTypes(constituentType); - } - } - else { - // Add type references - let symbol = isTypeReferenceNode(node) ? checker.getSymbolAtLocation(node.typeName) - : isExpressionWithTypeArguments(node) ? checker.getSymbolAtLocation(node.expression) - : undefined; - - if (symbol) { - if (superTypeSymbolSet[getSymbolId(symbol)]) { - return; - } - - superTypeSymbolSet[getSymbolId(symbol)] = true; - superTypes.push(symbol); - } - } - } - } - - function isSubtypeOf(node: TypeNode | Declaration, superTypeSymbol: Symbol): boolean { - if (isInterfaceDeclaration(node)) { - if (node.heritageClauses) { - for (let superType of node.heritageClauses[0].types) { - if (isSubtypeOf(superType, superTypeSymbol)) { - return true; - } - } - } - } - else if (isTypeAliasDeclaration(node)) { - return isSubtypeOf(node.type, superTypeSymbol); - } - else if (isUnionTypeNode(node)) { - for (let constituentType of node.types) { - if (isSubtypeOf(constituentType, superTypeSymbol)) { - return true; - } - } - } - else { - let typeSymbol = isTypeReferenceNode(node) ? checker.getSymbolAtLocation(node.typeName) - : isExpressionWithTypeArguments(node) ? checker.getSymbolAtLocation(node.expression) - : undefined; - - if (!typeSymbol) { - return false; - } - else if (typeSymbol === superTypeSymbol) { - return true; - } - - return isSubtypeOf(typeSymbol.declarations[0], superTypeSymbol); - } - - return false; - } - - function findAnnotation(symbol: Symbol, match: (annotation: Annotation) => boolean): TAnnotation { - for (let decl of symbol.declarations) { - for (let annotation of getAnnotationsForNode(decl)) { - if (match(annotation)) { - return annotation; - } - } - } - - return undefined; - } - - function matchAnnotations(symbol: Symbol, match: (annotation: Annotation) => boolean): TAnnotation[] { - let annotations: TAnnotation[]; - for (let decl of symbol.declarations) { - for (let annotation of getAnnotationsForNode(decl)) { - if (match(annotation)) { - if (!annotations) { - annotations = []; - } - - annotations.push(annotation); - } - } - } - - return annotations || emptyArray; - } - - function getAnnotations(symbol: Symbol): Annotation[] { - let annotations: Annotation[]; - for (let decl of symbol.declarations) { - let declAnnotations = getAnnotationsForNode(decl); - if (declAnnotations !== emptyArray) { - if (!annotations) { - annotations = []; - } - for (let annotation of declAnnotations) { - annotations.push(annotation); - } - } - } - return annotations; - } - - function getAnnotationsForNode(node: Node): Annotation[] { - let annotations = nodeAnnotations[getNodeId(node)]; - if (annotations) { - return annotations; - } - - let leadingCommentRanges = getLeadingCommentRanges(sourceFile.text, node.pos); - if (leadingCommentRanges) { - for (let range of leadingCommentRanges) { - parseAnnotations(range); - } - } - - if (!annotations) { - annotations = emptyArray; - } - - nodeAnnotations[getNodeId(node)] = annotations; - return annotations; - - function parseAnnotations(range: CommentRange) { - let text = sourceFile.text; - let comment = text.substring(range.pos, range.end); - let annotationMatch: RegExpExecArray; - while (annotationMatch = annotationPattern.exec(comment)) { - let name = annotationMatch[1]; - let _arguments: (string | number | boolean)[] = []; - if (annotationMatch[2]) { - let argumentMatch: RegExpExecArray; - let unterminatedStringLiteral: string; - let quoteToken: string; - while (argumentMatch = annotationArgumentPattern.exec(annotationMatch[2].trim())) { - let argumentText = argumentMatch[1]; - let pos = 0; - let end = argumentText.length - 1; - let ch: string; - if (unterminatedStringLiteral) { - unterminatedStringLiteral += ","; - while (end >= 0 && whitespacePattern.test(ch = argumentText.charAt(end))) { - end--; - } - - if (ch === quoteToken) { - if (end > 0) { - unterminatedStringLiteral += argumentText.substring(0, end); - } - - _arguments.push(unterminatedStringLiteral); - unterminatedStringLiteral = undefined; - quoteToken = undefined; - } - else { - unterminatedStringLiteral += "," + argumentText; - } - - continue; - } - - while (pos <= end && whitespacePattern.test(ch = argumentText.charAt(pos))) { - pos++; - } - - while (end >= pos && whitespacePattern.test(argumentText.charAt(end))) { - end--; - } - - if (end < pos || end < 0) { - _arguments.push(undefined); - continue; - } - - if (isQuote(ch)) { - if (argumentText.charAt(end) === ch) { - _arguments.push(argumentText.substring(pos + 1, end)); - } - else { - quoteToken = ch; - unterminatedStringLiteral = argumentText.substring(pos + 1); - } - - continue; - } - - argumentText = argumentText.substring(pos, end + 1); - if (argumentText === "null") { - _arguments.push(null); - } - else if (argumentText === "undefined") { - _arguments.push(undefined); - } - else if (argumentText === "true") { - _arguments.push(true); - } - else if (argumentText === "false") { - _arguments.push(false); - } - else if (numberPattern.test(argumentText)) { - _arguments.push(Number(argumentText)); - } - else { - _arguments.push(getConstantValue(node, argumentText)); - } - } - } - - if (!annotations) { - annotations = []; - } - - annotations.push(createAnnotation(name, _arguments)); - } - } - } - - function getSymbol(symbols: SymbolTable, name: string, meaning: SymbolFlags) { - if (symbols && meaning && hasProperty(symbols, name)) { - let symbol = symbols[name]; - if (symbol.flags & meaning) { - return symbol; - } - } - - return undefined; - } - - function resolveName(location: Node, name: string, meaning: SymbolFlags) { - let symbols = checker.getSymbolsInScope(location, meaning); - for (let symbol of symbols) { - if (symbol.name === name) { - return symbol; - } - } - - return undefined; - } - - function getConstantValue(location: Node, name: string) { - let qn = name.split("."); - if (qn.length === 1) { - return undefined; - } - - let namespace: Symbol; - if (qn.length > 2) { - for (let i = 0; i < qn.length - 2; i++) { - namespace = i === 0 - ? resolveName(location, qn[i], SymbolFlags.Namespace) - : getSymbol(namespace.exports, qn[i], SymbolFlags.Namespace); - - if (!namespace) { - return undefined; - } - } - } - - let container = qn.length > 2 - ? getSymbol(namespace.exports, qn[qn.length - 2], SymbolFlags.Enum) - : resolveName(location, qn[qn.length - 2], SymbolFlags.Enum); - - if (!container) { - return undefined; - } - - let member = getSymbol(container.exports, qn[qn.length - 1], SymbolFlags.EnumMember); - if (!member) { - return undefined; - } - - return checker.getConstantValue(member.declarations[0]); - } - - function isWhiteSpace(ch: string) { - return whitespacePattern.test(ch); - } - - function isQuote(ch: string) { - return quotePattern.test(ch); - } - function getFactoryHiddenStateForSymbol(symbol: Symbol): FactoryHiddenState { let annotation: FactoryHiddenAnnotation; if (annotation = findAnnotation(symbol, annotation => isFactoryHiddenAnnotation(annotation) && annotation.propertyName === undefined)) { @@ -776,16 +525,13 @@ function discover() { return propertyNames; } - function getKindsForSymbol(symbol: Symbol): SyntaxKind[] { - let annotations = matchAnnotations(symbol, isKindAnnotation); - return annotations.length > 0 - ? map(annotations, annotation => annotation.kind) - : emptyArray; + function getKindsForSymbol(symbol: Symbol): KindAnnotation[] { + return matchAnnotations(symbol, isKindAnnotation); } } -function generate(outputFile: string) { - writer = createTextWriter(host.getNewLine()); +function generateFactory(outputFile: string) { + let writer = createTextWriter(host.getNewLine()); writer.write(`// `); writer.writeLine(); writer.write(`/// `); @@ -808,235 +554,418 @@ function generate(outputFile: string) { writer.writeLine(); sys.writeFile(outputFile, writer.getText()); -} -function writeCreateAndUpdateFunctions() { - for (let syntaxNode of syntax) { - writeCreateFunction(syntaxNode); - writeUpdateFunction(syntaxNode); + function writeCreateAndUpdateFunctions() { + for (let syntaxNode of syntax) { + writeCreateFunction(syntaxNode); + writeUpdateFunction(syntaxNode); + } } -} - -function writeIsNodeFunctions() { - for (let syntaxNode of syntax) { - writeIsNodeFunction(syntaxNode); - } -} - -function writeCreateFunction(syntaxNode: SyntaxNode) { - writer.write(`export function create${syntaxNode.kindText}(`); - let indented = false; - for (let i = 0; i < syntaxNode.members.length; ++i) { - if (i > 0) { - writer.write(`, `); + function writeIsNodeFunctions() { + for (let syntaxNode of syntax) { + writeIsNodeFunction(syntaxNode); } - let member = syntaxNode.members[i]; - let paramText = - member.isNodeArray ? `${member.param}?: Array<${member.elementType}>` : - member.isModifiersArray ? `${member.param}?: Array` : - `${member.param}?: ${member.type}`; - - if (writer.getColumn() >= columnWrap - paramText.length) { - writer.writeLine(); - if (!indented) { - indented = true; - writer.increaseIndent(); + for (let typeName in memberTypeUsages) { + if (getProperty(memberTypeUsages, typeName) && !hasProperty(memberTypeUsageRedirects, typeName)) { + writeIsAnyNodeFunction(typeName); } } - - writer.write(paramText); } - let returnTypeText = `): ${syntaxNode.typeName} {`; - - if (writer.getColumn() >= columnWrap - returnTypeText.length) { - writer.writeLine(); - if (!indented) { - indented = true; - writer.increaseIndent(); - } - } - - writer.write(returnTypeText); - writer.writeLine(); - if (indented) { - writer.decreaseIndent(); - indented = false; - } - - writer.increaseIndent(); - if (syntaxNode.members.length) { - writer.write(`let node = createNode<${syntaxNode.typeName}>(SyntaxKind.${syntaxNode.kindText});`); - writer.writeLine(); - if (syntaxNode.members.length > 1) { - writer.write(`if (arguments.length) {`); - writer.writeLine(); - writer.increaseIndent(); + function writeCreateFunction(syntaxNode: SyntaxNode) { + if (!syntaxNode.options.create) { + return; } - for (let member of syntaxNode.members) { - if (member.isModifiersArray) { - writer.write(`setModifiers(node, modifiers);`); - } - else if (member.isNodeArray) { - writer.write(`node.${member.property} = ${member.param} && createNodeArray(${member.param})`); - } - else { - writer.write(`node.${member.property} = ${member.param};`); + writer.write(`export function create${syntaxNode.kindName}(`); + + let indented = false; + for (let i = 0; i < syntaxNode.members.length; ++i) { + if (i > 0) { + writer.write(`, `); } - writer.writeLine(); + let member = syntaxNode.members[i]; + let paramText = + member.isNodeArray ? `${member.paramName}?: Array<${member.elementTypeName}>` : + member.isModifiersArray ? `${member.paramName}?: Array` : + `${member.paramName}?: ${member.typeName}`; + + if (writer.getColumn() >= columnWrap - paramText.length) { + writer.writeLine(); + if (!indented) { + indented = true; + writer.increaseIndent(); + } + } + + writer.write(paramText); } - if (syntaxNode.members.length > 1) { + let returnTypeText = `): ${syntaxNode.typeName} {`; + + if (writer.getColumn() >= columnWrap - returnTypeText.length) { + writer.writeLine(); + if (!indented) { + indented = true; + writer.increaseIndent(); + } + } + + writer.write(returnTypeText); + writer.writeLine(); + if (indented) { writer.decreaseIndent(); - writer.write(`}`); + indented = false; + } + + writer.increaseIndent(); + if (syntaxNode.members.length) { + writer.write(`let node = createNode<${syntaxNode.typeName}>(SyntaxKind.${syntaxNode.kindName});`); + writer.writeLine(); + if (syntaxNode.members.length > 1) { + writer.write(`if (arguments.length) {`); + writer.writeLine(); + writer.increaseIndent(); + } + + for (let member of syntaxNode.members) { + if (member.isModifiersArray) { + writer.write(`setModifiers(node, modifiers);`); + } + else if (member.isNodeArray) { + writer.write(`node.${member.propertyName} = ${member.paramName} && createNodeArray(${member.paramName})`); + } + else { + writer.write(`node.${member.propertyName} = ${member.paramName};`); + } + + writer.writeLine(); + } + + if (syntaxNode.members.length > 1) { + writer.decreaseIndent(); + writer.write(`}`); + writer.writeLine(); + } + + writer.write(`return node;`); + writer.writeLine(); + } + else { + writer.write(`return createNode<${syntaxNode.typeName}>(SyntaxKind.${syntaxNode.kindName});`); writer.writeLine(); } + writer.decreaseIndent(); + writer.write(`}`); + writer.writeLine(); + } + + function writeIsNodeFunction(syntaxNode: SyntaxNode) { + if (!syntaxNode.options.test) { + return; + } + + writer.write(`export function is${syntaxNode.kindName}(node: Node): node is ${syntaxNode.typeName} {`); + writer.writeLine(); + writer.increaseIndent(); + writer.write(`return node && node.kind === SyntaxKind.${syntaxNode.kindName};`); + writer.writeLine(); + writer.decreaseIndent(); + writer.write(`}`); + writer.writeLine(); + } + + function fillKindsForType(typeSymbol: Symbol, kinds: Symbol[]) { + let usages = getProperty(syntaxKindTypeUsages, typeSymbol.name); + if (usages) { + for (let usage of usages) { + if (kinds.indexOf(usage) === -1) { + kinds.push(usage); + } + } + } + else if (typeSymbol.declarations[0].kind === SyntaxKind.TypeAliasDeclaration) { + for (let superType of getSuperTypes(typeSymbol.declarations[0])) { + fillKindsForType(superType, kinds); + } + } + } + + function writeIsAnyNodeFunction(typeName: string) { + let typeNames = typeName.split(/\s*\|\s*/g); + if (typeNames.length === 1) { + let typeSymbol = resolveName(tsModuleSymbol.declarations[0], typeName, SymbolFlags.Type); + if (typeSymbol && findAnnotation(typeSymbol, annotation => annotation.name === "nofactorynodetest")) { + return; + } + } + + writer.write(`export function is${typeNames.join("Or")}(node: Node): node is ${typeNames.join(" | ")} {`); + writer.writeLine(); + writer.increaseIndent(); + + writer.write(`if (node) {`); + writer.writeLine(); + writer.increaseIndent(); + + writer.write(`switch (node.kind) {`); + writer.writeLine(); + writer.increaseIndent(); + + let kinds: Symbol[] = []; + for (let typeName of typeNames) { + let typeSymbol = resolveName(tsModuleSymbol.declarations[0], typeName, SymbolFlags.Type); + if (typeSymbol) { + fillKindsForType(typeSymbol, kinds); + } + } + + for (let kind of kinds) { + writer.write(`case SyntaxKind.${kind.name}:`); + writer.writeLine(); + } + + if (kinds.length > 0) { + writer.increaseIndent(); + writer.write(`return true;`); + writer.writeLine(); + writer.decreaseIndent(); + } + + writer.decreaseIndent(); + writer.write(`}`); + writer.writeLine(); + + writer.decreaseIndent(); + writer.write(`}`); + writer.writeLine(); + + writer.write(`return false; `); + writer.writeLine(); + + writer.decreaseIndent(); + writer.write(`}`); + writer.writeLine(); + } + + function writeUpdateFunction(syntaxNode: SyntaxNode) { + if (!syntaxNode.options.update || !hasChildNodes(syntaxNode)) { + return; + } + + writer.write(`export function update${syntaxNode.kindName}(node: ${syntaxNode.typeName}`); + + let indented = false; + for (let i = 0; i < syntaxNode.members.length; ++i) { + let member = syntaxNode.members[i]; + if (member.isFactoryParam) { + continue; + } + + writer.write(`, `); + + let paramText = + member.isNodeArray ? `${member.paramName}: Array<${member.elementTypeName}>` : + member.isModifiersArray ? `${member.paramName}: Array` : + `${member.paramName}: ${member.typeName}`; + + if (writer.getColumn() >= columnWrap - paramText.length) { + writer.writeLine(); + if (!indented) { + indented = true; + writer.increaseIndent(); + } + } + + writer.write(paramText); + } + + let returnTypeText = `): ${syntaxNode.typeName} {`; + if (writer.getColumn() >= columnWrap - returnTypeText.length) { + writer.writeLine(); + if (!indented) { + indented = true; + writer.increaseIndent(); + } + } + + writer.write(returnTypeText); + writer.writeLine(); + if (indented) { + writer.decreaseIndent(); + indented = false; + } + + writer.increaseIndent(); + + writer.write(`if (`); + let first = true; + for (let member of syntaxNode.members) { + if (member.isFactoryParam) { + continue; + } + + if (first) { + first = false; + } + else { + writer.write(` || `); + } + + let conditionText = `${member.paramName} !== node.${member.propertyName}`; + if (writer.getColumn() >= columnWrap - conditionText.length) { + writer.writeLine(); + if (!indented) { + indented = true; + writer.increaseIndent(); + } + } + + writer.write(conditionText); + } + + writer.write(`) {`); + writer.writeLine(); + if (indented) { + writer.decreaseIndent(); + indented = false; + } + + writer.increaseIndent(); + + writer.write(`let newNode = create${syntaxNode.kindName}(`); + + for (let i = 0; i < syntaxNode.members.length; ++i) { + if (i > 0) { + writer.write(`, `); + } + + let member = syntaxNode.members[i]; + if (member.isFactoryParam) { + writer.write(`node.${member.propertyName}`); + } + else { + writer.write(member.paramName); + } + } + + writer.write(`);`); + writer.writeLine(); + + writer.write(`return updateFrom(node, newNode);`); + writer.writeLine(); + + writer.decreaseIndent(); + writer.write(`}`); + writer.writeLine(); + writer.write(`return node;`); writer.writeLine(); - } - else { - writer.write(`return createNode<${syntaxNode.typeName}>(SyntaxKind.${syntaxNode.kindText});`); + + writer.decreaseIndent(); + writer.write(`}`); writer.writeLine(); } - - writer.decreaseIndent(); - writer.write(`}`); - writer.writeLine(); } -function writeIsNodeFunction(syntaxNode: SyntaxNode) { - writer.write(`export function is${syntaxNode.kindText}(node: Node): node is ${syntaxNode.typeName} {`); +function generateTransform(outputFile: string) { + let writer = createTextWriter(host.getNewLine()); + writer.write(`// `); + writer.writeLine(); + writer.write(`/// `); + writer.writeLine(); + writer.write(`/// `); + writer.writeLine(); + writer.write(`/* @internal */`); + writer.writeLine(); + writer.write(`namespace ts.transform {`); writer.writeLine(); writer.increaseIndent(); - writer.write(`return node && node.kind === SyntaxKind.${syntaxNode.kindText};`); - writer.writeLine(); + writeVisitChildrenFunction(); writer.decreaseIndent(); writer.write(`}`); writer.writeLine(); -} - -function writeUpdateFunction(syntaxNode: SyntaxNode) { - if (!hasChildNodes(syntaxNode)) { - return; - } + + sys.writeFile(outputFile, writer.getText()); - writer.write(`export function update${syntaxNode.kindText}(node: ${syntaxNode.typeName}`); - - let indented = false; - for (let i = 0; i < syntaxNode.members.length; ++i) { - let member = syntaxNode.members[i]; - if (member.isFactoryParam) { - continue; - } - - writer.write(`, `); - - let paramText = - member.isNodeArray ? `${member.param}: Array<${member.elementType}>` : - member.isModifiersArray ? `${member.param}: Array` : - `${member.param}: ${member.type}`; - - if (writer.getColumn() >= columnWrap - paramText.length) { - writer.writeLine(); - if (!indented) { - indented = true; - writer.increaseIndent(); - } - } - - writer.write(paramText); - } - - let returnTypeText = `): ${syntaxNode.typeName} {`; - if (writer.getColumn() >= columnWrap - returnTypeText.length) { + function writeVisitChildrenFunction() { + writer.write(`export function visitChildren(node: TNode, transformer: Transformer): TNode;`); writer.writeLine(); - if (!indented) { - indented = true; + writer.write(`export function visitChildren(node: Node, transformer: Transformer): Node {`); + writer.writeLine(); + writer.increaseIndent(); + + writer.write(`if (!node || !transformer) {`); + writer.writeLine(); + writer.increaseIndent(); + + writer.write(`return node;`); + writer.writeLine(); + + writer.decreaseIndent(); + writer.write(`}`); + writer.writeLine(); + + writer.write(`switch (node.kind) {`); + writer.writeLine(); + writer.increaseIndent(); + + for (let syntaxNode of syntax) { + if (!hasChildNodes(syntaxNode)) { + continue; + } + + writer.write(`case SyntaxKind.${syntaxNode.kindName}:`); + writer.writeLine(); writer.increaseIndent(); - } - } - - writer.write(returnTypeText); - writer.writeLine(); - if (indented) { - writer.decreaseIndent(); - indented = false; - } - - writer.increaseIndent(); - - writer.write(`if (`); - let first = true; - for (let member of syntaxNode.members) { - if (member.isFactoryParam) { - continue; - } - - if (first) { - first = false; - } - else { - writer.write(` || `); - } - - let conditionText = `${member.param} !== node.${member.property}`; - if (writer.getColumn() >= columnWrap - conditionText.length) { + + writer.write(`return factory.update${syntaxNode.kindName}(`); writer.writeLine(); - if (!indented) { - indented = true; - writer.increaseIndent(); + writer.increaseIndent(); + writer.write(`<${syntaxNode.typeName}>node`); + + for (let member of syntaxNode.members) { + if (member.isFactoryParam) { + continue; + } + + writer.write(`, `); + writer.writeLine(); + if (member.isNodeArray) { + writer.write(`visitNodes((<${syntaxNode.typeName}>node).${member.propertyName}, transformer)`); + } + else if (member.isModifiersArray) { + writer.write(`visitNodes((<${syntaxNode.typeName}>node).${member.propertyName}, transformer)`); + } + else { + writer.write(`visit((<${syntaxNode.typeName}>node).${member.propertyName}, transformer)`); + } } - } - - writer.write(conditionText); - } - - writer.write(`) {`); - writer.writeLine(); - if (indented) { - writer.decreaseIndent(); - indented = false; - } - - writer.increaseIndent(); - - writer.write(`let newNode = create${syntaxNode.kindText}(`); - - for (let i = 0; i < syntaxNode.members.length; ++i) { - if (i > 0) { - writer.write(`, `); + + writer.write(`);`); + writer.writeLine(); + writer.decreaseIndent(); + writer.decreaseIndent(); } - let member = syntaxNode.members[i]; - if (member.isFactoryParam) { - writer.write(`node.${member.property}`); - } - else { - writer.write(member.param); - } + writer.write(`default:`); + writer.writeLine(); + writer.increaseIndent(); + writer.write(`return node;`); + writer.writeLine(); + writer.decreaseIndent(); + writer.decreaseIndent(); + writer.write(`}`); + writer.writeLine(); + + writer.decreaseIndent(); + writer.write('}'); + writer.writeLine(); } - - writer.write(`);`); - writer.writeLine(); - - writer.write(`return updateFrom(node, newNode);`); - writer.writeLine(); - - writer.decreaseIndent(); - writer.write(`}`); - writer.writeLine(); - - writer.write(`return node;`); - writer.writeLine(); - - writer.decreaseIndent(); - writer.write(`}`); - writer.writeLine(); } function hasChildNodes(syntaxNode: SyntaxNode) { @@ -1053,6 +982,112 @@ function hasChildNodes(syntaxNode: SyntaxNode) { return false; } +function isTypeReferenceNode(node: Node): node is TypeReferenceNode { + return node ? node.kind === SyntaxKind.TypeReference : false; +} + +function isUnionTypeNode(node: Node): node is UnionTypeNode { + return node ? node.kind === SyntaxKind.UnionType : false; +} + +function isInterfaceDeclaration(node: Node): node is InterfaceDeclaration { + return node ? node.kind === SyntaxKind.InterfaceDeclaration : false; +} + +function isTypeAliasDeclaration(node: Node): node is TypeAliasDeclaration { + return node ? node.kind === SyntaxKind.TypeAliasDeclaration : false; +} + +function isExpressionWithTypeArguments(node: Node): node is ExpressionWithTypeArguments { + return node ? node.kind === SyntaxKind.ExpressionWithTypeArguments : false; +} + +function isNodeArray(typeNode: TypeNode): boolean { + return isTypeReferenceNode(typeNode) ? checker.getSymbolAtLocation(typeNode.typeName) === nodeArraySymbol : false; +} + +function isModifiersArray(typeNode: TypeNode): boolean { + return isTypeReferenceNode(typeNode) ? checker.getSymbolAtLocation(typeNode.typeName) === modifiersArraySymbol : false; +} + +function isSubtypeOf(node: TypeNode | Declaration, superTypeSymbol: Symbol): boolean { + if (isInterfaceDeclaration(node)) { + if (node.heritageClauses) { + for (let superType of node.heritageClauses[0].types) { + if (isSubtypeOf(superType, superTypeSymbol)) { + return true; + } + } + } + } + else if (isTypeAliasDeclaration(node)) { + return isSubtypeOf(node.type, superTypeSymbol); + } + else if (isUnionTypeNode(node)) { + for (let constituentType of node.types) { + if (isSubtypeOf(constituentType, superTypeSymbol)) { + return true; + } + } + } + else { + let typeSymbol = isTypeReferenceNode(node) ? checker.getSymbolAtLocation(node.typeName) + : isExpressionWithTypeArguments(node) ? checker.getSymbolAtLocation(node.expression) + : undefined; + + if (!typeSymbol) { + return false; + } + else if (typeSymbol === superTypeSymbol) { + return true; + } + + return isSubtypeOf(typeSymbol.declarations[0], superTypeSymbol); + } + + return false; +} + +function getSuperTypes(node: Declaration) { + let superTypes: Symbol[] = []; + let superTypeSymbolSet: boolean[] = []; + + if (isTypeAliasDeclaration(node)) { + fillSuperTypes(node.type); + } + else if (isInterfaceDeclaration(node) && node.heritageClauses) { + for (let superType of node.heritageClauses[0].types) { + fillSuperTypes(superType); + } + } + + return superTypes; + + function fillSuperTypes(node: TypeNode) { + if (isUnionTypeNode(node)) { + // Flatten union types + for (let constituentType of node.types) { + fillSuperTypes(constituentType); + } + } + else { + // Add type references + let symbol = isTypeReferenceNode(node) ? checker.getSymbolAtLocation(node.typeName) + : isExpressionWithTypeArguments(node) ? checker.getSymbolAtLocation(node.expression) + : undefined; + + if (symbol) { + if (superTypeSymbolSet[getSymbolId(symbol)]) { + return; + } + + superTypeSymbolSet[getSymbolId(symbol)] = true; + superTypes.push(symbol); + } + } + } +} + function isFactoryHiddenAnnotation(annotation: Annotation): annotation is FactoryHiddenAnnotation { return annotation.name === "factoryhidden"; } @@ -1069,10 +1104,206 @@ function isKindAnnotation(annotation: Annotation): annotation is KindAnnotation return annotation.name === "kind"; } +function findAnnotation(symbol: Symbol, match: (annotation: Annotation) => boolean): TAnnotation { + for (let decl of symbol.declarations) { + for (let annotation of getAnnotationsForNode(decl)) { + if (match(annotation)) { + return annotation; + } + } + } + + return undefined; +} + +function matchAnnotations(symbol: Symbol, match: (annotation: Annotation) => boolean): TAnnotation[] { + let annotations: TAnnotation[]; + for (let decl of symbol.declarations) { + for (let annotation of getAnnotationsForNode(decl)) { + if (match(annotation)) { + if (!annotations) { + annotations = []; + } + + annotations.push(annotation); + } + } + } + + return annotations || emptyArray; +} + +function getAnnotations(symbol: Symbol): Annotation[] { + let annotations: Annotation[]; + for (let decl of symbol.declarations) { + let declAnnotations = getAnnotationsForNode(decl); + if (declAnnotations !== emptyArray) { + if (!annotations) { + annotations = []; + } + for (let annotation of declAnnotations) { + annotations.push(annotation); + } + } + } + return annotations; +} + +function getAnnotationsForNode(node: Node): Annotation[] { + let annotations = nodeAnnotations[getNodeId(node)]; + if (annotations) { + return annotations; + } + + let leadingCommentRanges = getLeadingCommentRanges(sourceFile.text, node.pos); + if (leadingCommentRanges) { + for (let range of leadingCommentRanges) { + parseAnnotations(range); + } + } + + if (!annotations) { + annotations = emptyArray; + } + + nodeAnnotations[getNodeId(node)] = annotations; + return annotations; + + function getLiteralValue(expr: Expression): any { + switch (expr.kind) { + case SyntaxKind.TrueKeyword: return true; + case SyntaxKind.FalseKeyword: return false; + case SyntaxKind.NullKeyword: return null; + case SyntaxKind.VoidExpression: return undefined; + case SyntaxKind.StringLiteral: return (expr).text; + case SyntaxKind.NoSubstitutionTemplateLiteral: return (expr).text; + case SyntaxKind.NumericLiteral: return Number((expr).text); + case SyntaxKind.PropertyAccessExpression: + return getEnumValue(node, expr.getText()); + case SyntaxKind.ArrayLiteralExpression: + return (expr).elements.map(getLiteralValue); + case SyntaxKind.ObjectLiteralExpression: + let obj: Map = {}; + for (let element of (expr).properties) { + if (element.kind !== SyntaxKind.PropertyAssignment + || (element).name.kind !== SyntaxKind.Identifier) { + continue; + } + + obj[((element).name).text] = + getLiteralValue((element).initializer); + } + return obj; + } + } + + function parseAnnotation(annotationSource: string) { + let evalSourceFile = createSourceFile("eval.ts", annotationSource, options.target, true); + let statements = evalSourceFile.statements; + if (statements.length === 0) { + return undefined; + } + + let stmt = statements[0]; + if (stmt.kind !== SyntaxKind.ExpressionStatement) { + return undefined; + } + + let expr = (stmt).expression; + if (expr.kind === SyntaxKind.Identifier) { + return createAnnotation((expr).text, emptyArray); + } + else if (expr.kind === SyntaxKind.CallExpression) { + let call = expr; + if (call.expression.kind !== SyntaxKind.Identifier) { + return undefined; + } + + let _arguments: any[] = []; + for (let argument of call.arguments) { + _arguments.push(getLiteralValue(argument)); + } + + return createAnnotation((call.expression).text, _arguments); + } + else { + return undefined; + } + } + + function parseAnnotations(range: CommentRange) { + let text = sourceFile.text; + let comment = text.substring(range.pos, range.end); + let annotationMatch: RegExpExecArray; + while (annotationMatch = annotationPattern.exec(comment)) { + let annotation = parseAnnotation(annotationMatch[1]); + if (annotation) { + if (!annotations) { + annotations = []; + } + + annotations.push(annotation); + } + } + } +} + +function getEnumValue(location: Node, name: string) { + let qn = name.split("."); + if (qn.length === 1) { + return undefined; + } + + let namespace: Symbol; + if (qn.length > 2) { + for (let i = 0; i < qn.length - 2; i++) { + namespace = i === 0 + ? resolveName(location, qn[i], SymbolFlags.Namespace) + : getSymbol(namespace.exports, qn[i], SymbolFlags.Namespace); + + if (!namespace) { + return undefined; + } + } + } + + let container = qn.length > 2 + ? getSymbol(namespace.exports, qn[qn.length - 2], SymbolFlags.Enum) + : resolveName(location, qn[qn.length - 2], SymbolFlags.Enum); + + if (!container) { + return undefined; + } + + let member = getSymbol(container.exports, qn[qn.length - 1], SymbolFlags.EnumMember); + if (!member) { + return undefined; + } + + return checker.getConstantValue(member.declarations[0]); +} + +function isWhiteSpace(ch: string) { + return whitespacePattern.test(ch); +} + +function isQuote(ch: string) { + return quotePattern.test(ch); +} + function createAnnotation(name: string, _arguments: any[]): Annotation { switch (name) { case "kind": - return { name, arguments: _arguments, kind: _arguments[0] }; + let options: KindOptions = { create: true, update: true, test: true }; + for (var p in _arguments[1]) { + (options)[p] = _arguments[1][p]; + } + return { + name, + arguments: _arguments, + kind: _arguments[0], + options + }; case "factoryhidden": if (_arguments.length >= 2 && typeof _arguments[0] === "string" && typeof _arguments[1] === "boolean") { @@ -1109,4 +1340,26 @@ function getCompilerOptions() { options.noResolve = true; options.noLib = true; return options; +} + +function getSymbol(symbols: SymbolTable, name: string, meaning: SymbolFlags) { + if (symbols && meaning && hasProperty(symbols, name)) { + let symbol = symbols[name]; + if (symbol.flags & meaning) { + return symbol; + } + } + + return undefined; +} + +function resolveName(location: Node, name: string, meaning: SymbolFlags) { + let symbols = checker.getSymbolsInScope(location, meaning); + for (let symbol of symbols) { + if (symbol.name === name) { + return symbol; + } + } + + return undefined; } \ No newline at end of file diff --git a/src/compiler/factory.generated.ts b/src/compiler/factory.generated.ts index d1b23fcbe7b..1ce36016bd1 100644 --- a/src/compiler/factory.generated.ts +++ b/src/compiler/factory.generated.ts @@ -91,7 +91,7 @@ namespace ts { return node; } export function createParameter(decorators?: Array, modifiers?: Array, dotDotDotToken?: Node, - name?: Identifier | BindingPattern, questionToken?: Node, type?: TypeNode, initializer?: Expression): ParameterDeclaration { + name?: BindingPattern | Identifier, questionToken?: Node, type?: TypeNode, initializer?: Expression): ParameterDeclaration { let node = createNode(SyntaxKind.Parameter); if (arguments.length) { node.decorators = decorators && createNodeArray(decorators) @@ -105,7 +105,7 @@ namespace ts { return node; } export function updateParameter(node: ParameterDeclaration, decorators: Array, modifiers: Array, - name: Identifier | BindingPattern, type: TypeNode, initializer: Expression): ParameterDeclaration { + name: BindingPattern | Identifier, type: TypeNode, initializer: Expression): ParameterDeclaration { if (decorators !== node.decorators || modifiers !== node.modifiers || name !== node.name || type !== node.type || initializer !== node.initializer) { let newNode = createParameter(decorators, modifiers, node.dotDotDotToken, name, node.questionToken, type, initializer); @@ -510,7 +510,7 @@ namespace ts { return node; } export function createBindingElement(decorators?: Array, modifiers?: Array, propertyName?: Identifier, - dotDotDotToken?: Node, name?: Identifier | BindingPattern, initializer?: Expression): BindingElement { + dotDotDotToken?: Node, name?: BindingPattern | Identifier, initializer?: Expression): BindingElement { let node = createNode(SyntaxKind.BindingElement); if (arguments.length) { node.decorators = decorators && createNodeArray(decorators) @@ -523,7 +523,7 @@ namespace ts { return node; } export function updateBindingElement(node: BindingElement, decorators: Array, modifiers: Array, propertyName: Identifier, - name: Identifier | BindingPattern, initializer: Expression): BindingElement { + name: BindingPattern | Identifier, initializer: Expression): BindingElement { if (decorators !== node.decorators || modifiers !== node.modifiers || propertyName !== node.propertyName || name !== node.name || initializer !== node.initializer) { let newNode = createBindingElement(decorators, modifiers, propertyName, node.dotDotDotToken, name, initializer); @@ -984,8 +984,8 @@ namespace ts { } return node; } - export function createEmptyStatement(): Statement { - return createNode(SyntaxKind.EmptyStatement); + export function createEmptyStatement(): EmptyStatement { + return createNode(SyntaxKind.EmptyStatement); } export function createExpressionStatement(expression?: Expression): ExpressionStatement { let node = createNode(SyntaxKind.ExpressionStatement); @@ -1046,7 +1046,7 @@ namespace ts { } return node; } - export function createForStatement(initializer?: VariableDeclarationList | Expression, condition?: Expression, incrementor?: Expression, + export function createForStatement(initializer?: Expression | VariableDeclarationList, condition?: Expression, incrementor?: Expression, statement?: Statement): ForStatement { let node = createNode(SyntaxKind.ForStatement); if (arguments.length) { @@ -1057,7 +1057,7 @@ namespace ts { } return node; } - export function updateForStatement(node: ForStatement, initializer: VariableDeclarationList | Expression, condition: Expression, + export function updateForStatement(node: ForStatement, initializer: Expression | VariableDeclarationList, condition: Expression, incrementor: Expression, statement: Statement): ForStatement { if (initializer !== node.initializer || condition !== node.condition || incrementor !== node.incrementor || statement !== node.statement) { let newNode = createForStatement(initializer, condition, incrementor, statement); @@ -1065,7 +1065,7 @@ namespace ts { } return node; } - export function createForInStatement(initializer?: VariableDeclarationList | Expression, expression?: Expression, statement?: Statement + export function createForInStatement(initializer?: Expression | VariableDeclarationList, expression?: Expression, statement?: Statement ): ForInStatement { let node = createNode(SyntaxKind.ForInStatement); if (arguments.length) { @@ -1075,7 +1075,7 @@ namespace ts { } return node; } - export function updateForInStatement(node: ForInStatement, initializer: VariableDeclarationList | Expression, expression: Expression, + export function updateForInStatement(node: ForInStatement, initializer: Expression | VariableDeclarationList, expression: Expression, statement: Statement): ForInStatement { if (initializer !== node.initializer || expression !== node.expression || statement !== node.statement) { let newNode = createForInStatement(initializer, expression, statement); @@ -1083,7 +1083,7 @@ namespace ts { } return node; } - export function createForOfStatement(initializer?: VariableDeclarationList | Expression, expression?: Expression, statement?: Statement + export function createForOfStatement(initializer?: Expression | VariableDeclarationList, expression?: Expression, statement?: Statement ): ForOfStatement { let node = createNode(SyntaxKind.ForOfStatement); if (arguments.length) { @@ -1093,7 +1093,7 @@ namespace ts { } return node; } - export function updateForOfStatement(node: ForOfStatement, initializer: VariableDeclarationList | Expression, expression: Expression, + export function updateForOfStatement(node: ForOfStatement, initializer: Expression | VariableDeclarationList, expression: Expression, statement: Statement): ForOfStatement { if (initializer !== node.initializer || expression !== node.expression || statement !== node.statement) { let newNode = createForOfStatement(initializer, expression, statement); @@ -1210,10 +1210,10 @@ namespace ts { } return node; } - export function createDebuggerStatement(): Statement { - return createNode(SyntaxKind.DebuggerStatement); + export function createDebuggerStatement(): DebuggerStatement { + return createNode(SyntaxKind.DebuggerStatement); } - export function createVariableDeclaration(decorators?: Array, modifiers?: Array, name?: Identifier | BindingPattern, + export function createVariableDeclaration(decorators?: Array, modifiers?: Array, name?: BindingPattern | Identifier, type?: TypeNode, initializer?: Expression): VariableDeclaration { let node = createNode(SyntaxKind.VariableDeclaration); if (arguments.length) { @@ -1226,7 +1226,7 @@ namespace ts { return node; } export function updateVariableDeclaration(node: VariableDeclaration, decorators: Array, modifiers: Array, - name: Identifier | BindingPattern, type: TypeNode, initializer: Expression): VariableDeclaration { + name: BindingPattern | Identifier, type: TypeNode, initializer: Expression): VariableDeclaration { if (decorators !== node.decorators || modifiers !== node.modifiers || name !== node.name || type !== node.type || initializer !== node.initializer) { let newNode = createVariableDeclaration(decorators, modifiers, name, type, initializer); @@ -1443,7 +1443,7 @@ namespace ts { } return node; } - export function createImportClause(name?: Identifier, namedBindings?: NamespaceImport | NamedImports): ImportClause { + export function createImportClause(name?: Identifier, namedBindings?: NamedImports | NamespaceImport): ImportClause { let node = createNode(SyntaxKind.ImportClause); if (arguments.length) { node.name = name; @@ -1451,7 +1451,7 @@ namespace ts { } return node; } - export function updateImportClause(node: ImportClause, name: Identifier, namedBindings: NamespaceImport | NamedImports): ImportClause { + export function updateImportClause(node: ImportClause, name: Identifier, namedBindings: NamedImports | NamespaceImport): ImportClause { if (name !== node.name || namedBindings !== node.namedBindings) { let newNode = createImportClause(name, namedBindings); return updateFrom(node, newNode); @@ -2101,6 +2101,21 @@ namespace ts { export function isIdentifier(node: Node): node is Identifier { return node && node.kind === SyntaxKind.Identifier; } + export function isFalseKeyword(node: Node): node is LeftHandSideExpression { + return node && node.kind === SyntaxKind.FalseKeyword; + } + export function isNullKeyword(node: Node): node is LeftHandSideExpression { + return node && node.kind === SyntaxKind.NullKeyword; + } + export function isSuperKeyword(node: Node): node is LeftHandSideExpression { + return node && node.kind === SyntaxKind.SuperKeyword; + } + export function isThisKeyword(node: Node): node is LeftHandSideExpression { + return node && node.kind === SyntaxKind.ThisKeyword; + } + export function isTrueKeyword(node: Node): node is LeftHandSideExpression { + return node && node.kind === SyntaxKind.TrueKeyword; + } export function isQualifiedName(node: Node): node is QualifiedName { return node && node.kind === SyntaxKind.QualifiedName; } @@ -2278,7 +2293,7 @@ namespace ts { export function isVariableStatement(node: Node): node is VariableStatement { return node && node.kind === SyntaxKind.VariableStatement; } - export function isEmptyStatement(node: Node): node is Statement { + export function isEmptyStatement(node: Node): node is EmptyStatement { return node && node.kind === SyntaxKind.EmptyStatement; } export function isExpressionStatement(node: Node): node is ExpressionStatement { @@ -2326,7 +2341,7 @@ namespace ts { export function isTryStatement(node: Node): node is TryStatement { return node && node.kind === SyntaxKind.TryStatement; } - export function isDebuggerStatement(node: Node): node is Statement { + export function isDebuggerStatement(node: Node): node is DebuggerStatement { return node && node.kind === SyntaxKind.DebuggerStatement; } export function isVariableDeclaration(node: Node): node is VariableDeclaration { @@ -2506,4 +2521,338 @@ namespace ts { export function isJSDocTemplateTag(node: Node): node is JSDocTemplateTag { return node && node.kind === SyntaxKind.JSDocTemplateTag; } + export function isEntityName(node: Node): node is EntityName { + if (node) { + switch (node.kind) { + case SyntaxKind.Identifier: + case SyntaxKind.QualifiedName: + return true; + } + } + return false; + } + export function isBindingPatternOrIdentifier(node: Node): node is BindingPattern | Identifier { + if (node) { + switch (node.kind) { + case SyntaxKind.ObjectBindingPattern: + case SyntaxKind.ArrayBindingPattern: + case SyntaxKind.Identifier: + return true; + } + } + return false; + } + export function isUnaryExpression(node: Node): node is UnaryExpression { + if (node) { + switch (node.kind) { + case SyntaxKind.Identifier: + case SyntaxKind.StringLiteral: + case SyntaxKind.PrefixUnaryExpression: + case SyntaxKind.PostfixUnaryExpression: + case SyntaxKind.TrueKeyword: + case SyntaxKind.FalseKeyword: + case SyntaxKind.NullKeyword: + case SyntaxKind.ThisKeyword: + case SyntaxKind.SuperKeyword: + case SyntaxKind.DeleteExpression: + case SyntaxKind.TypeOfExpression: + case SyntaxKind.VoidExpression: + case SyntaxKind.AwaitExpression: + case SyntaxKind.FunctionExpression: + case SyntaxKind.NumericLiteral: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.NoSubstitutionTemplateLiteral: + case SyntaxKind.TemplateHead: + case SyntaxKind.TemplateMiddle: + case SyntaxKind.TemplateTail: + case SyntaxKind.TemplateExpression: + case SyntaxKind.ParenthesizedExpression: + case SyntaxKind.ArrayLiteralExpression: + case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.PropertyAccessExpression: + case SyntaxKind.ElementAccessExpression: + case SyntaxKind.CallExpression: + case SyntaxKind.NewExpression: + case SyntaxKind.TaggedTemplateExpression: + case SyntaxKind.TypeAssertionExpression: + case SyntaxKind.JsxElement: + case SyntaxKind.JsxSelfClosingElement: + case SyntaxKind.ClassExpression: + return true; + } + } + return false; + } + export function isBlockOrExpression(node: Node): node is Block | Expression { + if (node) { + switch (node.kind) { + case SyntaxKind.Block: + case SyntaxKind.Identifier: + case SyntaxKind.StringLiteral: + case SyntaxKind.OmittedExpression: + case SyntaxKind.PrefixUnaryExpression: + case SyntaxKind.PostfixUnaryExpression: + case SyntaxKind.TrueKeyword: + case SyntaxKind.FalseKeyword: + case SyntaxKind.NullKeyword: + case SyntaxKind.ThisKeyword: + case SyntaxKind.SuperKeyword: + case SyntaxKind.DeleteExpression: + case SyntaxKind.TypeOfExpression: + case SyntaxKind.VoidExpression: + case SyntaxKind.AwaitExpression: + case SyntaxKind.YieldExpression: + case SyntaxKind.BinaryExpression: + case SyntaxKind.ConditionalExpression: + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: + case SyntaxKind.NumericLiteral: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.NoSubstitutionTemplateLiteral: + case SyntaxKind.TemplateHead: + case SyntaxKind.TemplateMiddle: + case SyntaxKind.TemplateTail: + case SyntaxKind.TemplateExpression: + case SyntaxKind.ParenthesizedExpression: + case SyntaxKind.ArrayLiteralExpression: + case SyntaxKind.SpreadElementExpression: + case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.PropertyAccessExpression: + case SyntaxKind.ElementAccessExpression: + case SyntaxKind.CallExpression: + case SyntaxKind.NewExpression: + case SyntaxKind.TaggedTemplateExpression: + case SyntaxKind.AsExpression: + case SyntaxKind.TypeAssertionExpression: + case SyntaxKind.JsxElement: + case SyntaxKind.JsxOpeningElement: + case SyntaxKind.JsxSelfClosingElement: + case SyntaxKind.JsxExpression: + case SyntaxKind.ClassExpression: + return true; + } + } + return false; + } + export function isLiteralExpression(node: Node): node is LiteralExpression { + if (node) { + switch (node.kind) { + case SyntaxKind.StringLiteral: + case SyntaxKind.NumericLiteral: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.NoSubstitutionTemplateLiteral: + case SyntaxKind.TemplateHead: + case SyntaxKind.TemplateMiddle: + case SyntaxKind.TemplateTail: + return true; + } + } + return false; + } + export function isObjectLiteralElement(node: Node): node is ObjectLiteralElement { + if (node) { + switch (node.kind) { + case SyntaxKind.PropertyAssignment: + case SyntaxKind.ShorthandPropertyAssignment: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + return true; + } + } + return false; + } + export function isLiteralExpressionOrTemplateExpression(node: Node): node is LiteralExpression | TemplateExpression { + if (node) { + switch (node.kind) { + case SyntaxKind.StringLiteral: + case SyntaxKind.NumericLiteral: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.NoSubstitutionTemplateLiteral: + case SyntaxKind.TemplateHead: + case SyntaxKind.TemplateMiddle: + case SyntaxKind.TemplateTail: + case SyntaxKind.TemplateExpression: + return true; + } + } + return false; + } + export function isJsxChild(node: Node): node is JsxChild { + if (node) { + switch (node.kind) { + case SyntaxKind.JsxText: + case SyntaxKind.JsxExpression: + case SyntaxKind.JsxElement: + case SyntaxKind.JsxSelfClosingElement: + return true; + } + } + return false; + } + export function isJsxAttributeOrJsxSpreadAttribute(node: Node): node is JsxAttribute | JsxSpreadAttribute { + if (node) { + switch (node.kind) { + case SyntaxKind.JsxAttribute: + case SyntaxKind.JsxSpreadAttribute: + return true; + } + } + return false; + } + export function isExpressionOrVariableDeclarationList(node: Node): node is Expression | VariableDeclarationList { + if (node) { + switch (node.kind) { + case SyntaxKind.Identifier: + case SyntaxKind.StringLiteral: + case SyntaxKind.OmittedExpression: + case SyntaxKind.PrefixUnaryExpression: + case SyntaxKind.PostfixUnaryExpression: + case SyntaxKind.TrueKeyword: + case SyntaxKind.FalseKeyword: + case SyntaxKind.NullKeyword: + case SyntaxKind.ThisKeyword: + case SyntaxKind.SuperKeyword: + case SyntaxKind.DeleteExpression: + case SyntaxKind.TypeOfExpression: + case SyntaxKind.VoidExpression: + case SyntaxKind.AwaitExpression: + case SyntaxKind.YieldExpression: + case SyntaxKind.BinaryExpression: + case SyntaxKind.ConditionalExpression: + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: + case SyntaxKind.NumericLiteral: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.NoSubstitutionTemplateLiteral: + case SyntaxKind.TemplateHead: + case SyntaxKind.TemplateMiddle: + case SyntaxKind.TemplateTail: + case SyntaxKind.TemplateExpression: + case SyntaxKind.ParenthesizedExpression: + case SyntaxKind.ArrayLiteralExpression: + case SyntaxKind.SpreadElementExpression: + case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.PropertyAccessExpression: + case SyntaxKind.ElementAccessExpression: + case SyntaxKind.CallExpression: + case SyntaxKind.NewExpression: + case SyntaxKind.TaggedTemplateExpression: + case SyntaxKind.AsExpression: + case SyntaxKind.TypeAssertionExpression: + case SyntaxKind.JsxElement: + case SyntaxKind.JsxOpeningElement: + case SyntaxKind.JsxSelfClosingElement: + case SyntaxKind.JsxExpression: + case SyntaxKind.ClassExpression: + case SyntaxKind.VariableDeclarationList: + return true; + } + } + return false; + } + export function isCaseOrDefaultClause(node: Node): node is CaseOrDefaultClause { + if (node) { + switch (node.kind) { + case SyntaxKind.CaseClause: + case SyntaxKind.DefaultClause: + return true; + } + } + return false; + } + export function isClassElement(node: Node): node is ClassElement { + if (node) { + switch (node.kind) { + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.Constructor: + case SyntaxKind.SemicolonClassElement: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.IndexSignature: + return true; + } + } + return false; + } + export function isIdentifierOrLiteralExpression(node: Node): node is Identifier | LiteralExpression { + if (node) { + switch (node.kind) { + case SyntaxKind.Identifier: + case SyntaxKind.StringLiteral: + case SyntaxKind.NumericLiteral: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.NoSubstitutionTemplateLiteral: + case SyntaxKind.TemplateHead: + case SyntaxKind.TemplateMiddle: + case SyntaxKind.TemplateTail: + return true; + } + } + return false; + } + export function isModuleBlockOrModuleDeclaration(node: Node): node is ModuleBlock | ModuleDeclaration { + if (node) { + switch (node.kind) { + case SyntaxKind.ModuleBlock: + case SyntaxKind.ModuleDeclaration: + return true; + } + } + return false; + } + export function isEntityNameOrExternalModuleReference(node: Node): node is EntityName | ExternalModuleReference { + if (node) { + switch (node.kind) { + case SyntaxKind.Identifier: + case SyntaxKind.QualifiedName: + case SyntaxKind.ExternalModuleReference: + return true; + } + } + return false; + } + export function isNamedImportsOrNamespaceImport(node: Node): node is NamedImports | NamespaceImport { + if (node) { + switch (node.kind) { + case SyntaxKind.NamedImports: + case SyntaxKind.NamespaceImport: + return true; + } + } + return false; + } + export function isImportOrExportSpecifier(node: Node): node is ImportOrExportSpecifier { + if (node) { + switch (node.kind) { + case SyntaxKind.ImportSpecifier: + case SyntaxKind.ExportSpecifier: + return true; + } + } + return false; + } + export function isJSDocType(node: Node): node is JSDocType { + if (node) { + switch (node.kind) { + case SyntaxKind.JSDocAllType: + case SyntaxKind.JSDocUnknownType: + case SyntaxKind.JSDocArrayType: + case SyntaxKind.JSDocUnionType: + case SyntaxKind.JSDocTupleType: + case SyntaxKind.JSDocNonNullableType: + case SyntaxKind.JSDocNullableType: + case SyntaxKind.JSDocRecordType: + case SyntaxKind.JSDocTypeReference: + case SyntaxKind.JSDocOptionalType: + case SyntaxKind.JSDocFunctionType: + case SyntaxKind.JSDocVariadicType: + case SyntaxKind.JSDocConstructorType: + case SyntaxKind.JSDocThisType: + return true; + } + } + return false; + } } diff --git a/src/compiler/transform.generated.ts b/src/compiler/transform.generated.ts new file mode 100644 index 00000000000..cb892abf3f2 --- /dev/null +++ b/src/compiler/transform.generated.ts @@ -0,0 +1,696 @@ +// +/// +/// +/* @internal */ +namespace ts.transform { + export function visitChildren(node: TNode, transformer: Transformer): TNode; + export function visitChildren(node: Node, transformer: Transformer): Node { + if (!node || !transformer) { + return node; + } + switch (node.kind) { + case SyntaxKind.QualifiedName: + return factory.updateQualifiedName( + node, + visit((node).left, transformer), + visit((node).right, transformer)); + case SyntaxKind.ComputedPropertyName: + return factory.updateComputedPropertyName( + node, + visit((node).expression, transformer)); + case SyntaxKind.TypeParameter: + return factory.updateTypeParameter( + node, + visit((node).name, transformer), + visit((node).constraint, transformer), + visit((node).expression, transformer)); + case SyntaxKind.Parameter: + return factory.updateParameter( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visit((node).name, transformer), + visit((node).type, transformer), + visit((node).initializer, transformer)); + case SyntaxKind.Decorator: + return factory.updateDecorator( + node, + visit((node).expression, transformer)); + case SyntaxKind.PropertySignature: + return factory.updatePropertySignature( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visit((node).name, transformer), + visit((node).type, transformer)); + case SyntaxKind.PropertyDeclaration: + return factory.updatePropertyDeclaration( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visit((node).name, transformer), + visit((node).type, transformer), + visit((node).initializer, transformer)); + case SyntaxKind.MethodSignature: + return factory.updateMethodSignature( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visit((node).name, transformer), + visitNodes((node).typeParameters, transformer), + visitNodes((node).parameters, transformer), + visit((node).type, transformer)); + case SyntaxKind.MethodDeclaration: + return factory.updateMethodDeclaration( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visit((node).name, transformer), + visitNodes((node).typeParameters, transformer), + visitNodes((node).parameters, transformer), + visit((node).type, transformer), + visit((node).body, transformer)); + case SyntaxKind.Constructor: + return factory.updateConstructor( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visitNodes((node).parameters, transformer), + visit((node).type, transformer), + visit((node).body, transformer)); + case SyntaxKind.GetAccessor: + return factory.updateGetAccessor( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visit((node).name, transformer), + visitNodes((node).parameters, transformer), + visit((node).type, transformer), + visit((node).body, transformer)); + case SyntaxKind.SetAccessor: + return factory.updateSetAccessor( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visit((node).name, transformer), + visitNodes((node).parameters, transformer), + visit((node).type, transformer), + visit((node).body, transformer)); + case SyntaxKind.CallSignature: + return factory.updateCallSignature( + node, + visitNodes((node).typeParameters, transformer), + visitNodes((node).parameters, transformer), + visit((node).type, transformer)); + case SyntaxKind.ConstructSignature: + return factory.updateConstructSignature( + node, + visitNodes((node).typeParameters, transformer), + visitNodes((node).parameters, transformer), + visit((node).type, transformer)); + case SyntaxKind.IndexSignature: + return factory.updateIndexSignature( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visitNodes((node).parameters, transformer), + visit((node).type, transformer)); + case SyntaxKind.TypePredicate: + return factory.updateTypePredicate( + node, + visit((node).parameterName, transformer), + visit((node).type, transformer)); + case SyntaxKind.TypeReference: + return factory.updateTypeReference( + node, + visit((node).typeName, transformer), + visitNodes((node).typeArguments, transformer)); + case SyntaxKind.FunctionType: + return factory.updateFunctionType( + node, + visitNodes((node).typeParameters, transformer), + visitNodes((node).parameters, transformer), + visit((node).type, transformer)); + case SyntaxKind.ConstructorType: + return factory.updateConstructorType( + node, + visitNodes((node).typeParameters, transformer), + visitNodes((node).parameters, transformer), + visit((node).type, transformer)); + case SyntaxKind.TypeQuery: + return factory.updateTypeQuery( + node, + visit((node).exprName, transformer)); + case SyntaxKind.TypeLiteral: + return factory.updateTypeLiteral( + node, + visitNodes((node).members, transformer)); + case SyntaxKind.ArrayType: + return factory.updateArrayType( + node, + visit((node).elementType, transformer)); + case SyntaxKind.TupleType: + return factory.updateTupleType( + node, + visitNodes((node).elementTypes, transformer)); + case SyntaxKind.UnionType: + return factory.updateUnionType( + node, + visitNodes((node).types, transformer)); + case SyntaxKind.IntersectionType: + return factory.updateIntersectionType( + node, + visitNodes((node).types, transformer)); + case SyntaxKind.ParenthesizedType: + return factory.updateParenthesizedType( + node, + visit((node).type, transformer)); + case SyntaxKind.ObjectBindingPattern: + return factory.updateObjectBindingPattern( + node, + visitNodes((node).elements, transformer)); + case SyntaxKind.ArrayBindingPattern: + return factory.updateArrayBindingPattern( + node, + visitNodes((node).elements, transformer)); + case SyntaxKind.BindingElement: + return factory.updateBindingElement( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visit((node).propertyName, transformer), + visit((node).name, transformer), + visit((node).initializer, transformer)); + case SyntaxKind.ArrayLiteralExpression: + return factory.updateArrayLiteralExpression( + node, + visitNodes((node).elements, transformer)); + case SyntaxKind.ObjectLiteralExpression: + return factory.updateObjectLiteralExpression( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visitNodes((node).properties, transformer)); + case SyntaxKind.PropertyAccessExpression: + return factory.updatePropertyAccessExpression( + node, + visit((node).expression, transformer), + visit((node).name, transformer)); + case SyntaxKind.ElementAccessExpression: + return factory.updateElementAccessExpression( + node, + visit((node).expression, transformer), + visit((node).argumentExpression, transformer)); + case SyntaxKind.CallExpression: + return factory.updateCallExpression( + node, + visit((node).expression, transformer), + visitNodes((node).typeArguments, transformer), + visitNodes((node).arguments, transformer)); + case SyntaxKind.NewExpression: + return factory.updateNewExpression( + node, + visit((node).expression, transformer), + visitNodes((node).typeArguments, transformer), + visitNodes((node).arguments, transformer)); + case SyntaxKind.TaggedTemplateExpression: + return factory.updateTaggedTemplateExpression( + node, + visit((node).tag, transformer), + visit((node).template, transformer)); + case SyntaxKind.TypeAssertionExpression: + return factory.updateTypeAssertionExpression( + node, + visit((node).type, transformer), + visit((node).expression, transformer)); + case SyntaxKind.ParenthesizedExpression: + return factory.updateParenthesizedExpression( + node, + visit((node).expression, transformer)); + case SyntaxKind.FunctionExpression: + return factory.updateFunctionExpression( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visit((node).name, transformer), + visitNodes((node).typeParameters, transformer), + visitNodes((node).parameters, transformer), + visit((node).type, transformer), + visit((node).body, transformer)); + case SyntaxKind.ArrowFunction: + return factory.updateArrowFunction( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visitNodes((node).typeParameters, transformer), + visitNodes((node).parameters, transformer), + visit((node).type, transformer), + visit((node).body, transformer)); + case SyntaxKind.DeleteExpression: + return factory.updateDeleteExpression( + node, + visit((node).expression, transformer)); + case SyntaxKind.TypeOfExpression: + return factory.updateTypeOfExpression( + node, + visit((node).expression, transformer)); + case SyntaxKind.VoidExpression: + return factory.updateVoidExpression( + node, + visit((node).expression, transformer)); + case SyntaxKind.AwaitExpression: + return factory.updateAwaitExpression( + node, + visit((node).expression, transformer)); + case SyntaxKind.PrefixUnaryExpression: + return factory.updatePrefixUnaryExpression( + node, + visit((node).operand, transformer)); + case SyntaxKind.PostfixUnaryExpression: + return factory.updatePostfixUnaryExpression( + node, + visit((node).operand, transformer)); + case SyntaxKind.BinaryExpression: + return factory.updateBinaryExpression( + node, + visit((node).left, transformer), + visit((node).right, transformer)); + case SyntaxKind.ConditionalExpression: + return factory.updateConditionalExpression( + node, + visit((node).condition, transformer), + visit((node).whenTrue, transformer), + visit((node).whenFalse, transformer)); + case SyntaxKind.TemplateExpression: + return factory.updateTemplateExpression( + node, + visit((node).head, transformer), + visitNodes((node).templateSpans, transformer)); + case SyntaxKind.YieldExpression: + return factory.updateYieldExpression( + node, + visit((node).expression, transformer)); + case SyntaxKind.SpreadElementExpression: + return factory.updateSpreadElementExpression( + node, + visit((node).expression, transformer)); + case SyntaxKind.ClassExpression: + return factory.updateClassExpression( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visit((node).name, transformer), + visitNodes((node).typeParameters, transformer), + visitNodes((node).heritageClauses, transformer), + visitNodes((node).members, transformer)); + case SyntaxKind.ExpressionWithTypeArguments: + return factory.updateExpressionWithTypeArguments( + node, + visit((node).expression, transformer), + visitNodes((node).typeArguments, transformer)); + case SyntaxKind.AsExpression: + return factory.updateAsExpression( + node, + visit((node).expression, transformer), + visit((node).type, transformer)); + case SyntaxKind.TemplateSpan: + return factory.updateTemplateSpan( + node, + visit((node).expression, transformer), + visit((node).literal, transformer)); + case SyntaxKind.Block: + return factory.updateBlock( + node, + visitNodes((node).statements, transformer)); + case SyntaxKind.VariableStatement: + return factory.updateVariableStatement( + node, + visit((node).declarationList, transformer)); + case SyntaxKind.ExpressionStatement: + return factory.updateExpressionStatement( + node, + visit((node).expression, transformer)); + case SyntaxKind.IfStatement: + return factory.updateIfStatement( + node, + visit((node).expression, transformer), + visit((node).thenStatement, transformer), + visit((node).elseStatement, transformer)); + case SyntaxKind.DoStatement: + return factory.updateDoStatement( + node, + visit((node).statement, transformer), + visit((node).expression, transformer)); + case SyntaxKind.WhileStatement: + return factory.updateWhileStatement( + node, + visit((node).expression, transformer), + visit((node).statement, transformer)); + case SyntaxKind.ForStatement: + return factory.updateForStatement( + node, + visit((node).initializer, transformer), + visit((node).condition, transformer), + visit((node).incrementor, transformer), + visit((node).statement, transformer)); + case SyntaxKind.ForInStatement: + return factory.updateForInStatement( + node, + visit((node).initializer, transformer), + visit((node).expression, transformer), + visit((node).statement, transformer)); + case SyntaxKind.ForOfStatement: + return factory.updateForOfStatement( + node, + visit((node).initializer, transformer), + visit((node).expression, transformer), + visit((node).statement, transformer)); + case SyntaxKind.ContinueStatement: + return factory.updateContinueStatement( + node, + visit((node).label, transformer)); + case SyntaxKind.BreakStatement: + return factory.updateBreakStatement( + node, + visit((node).label, transformer)); + case SyntaxKind.ReturnStatement: + return factory.updateReturnStatement( + node, + visit((node).expression, transformer)); + case SyntaxKind.WithStatement: + return factory.updateWithStatement( + node, + visit((node).expression, transformer), + visit((node).statement, transformer)); + case SyntaxKind.SwitchStatement: + return factory.updateSwitchStatement( + node, + visit((node).expression, transformer), + visit((node).caseBlock, transformer)); + case SyntaxKind.LabeledStatement: + return factory.updateLabeledStatement( + node, + visit((node).label, transformer), + visit((node).statement, transformer)); + case SyntaxKind.ThrowStatement: + return factory.updateThrowStatement( + node, + visit((node).expression, transformer)); + case SyntaxKind.TryStatement: + return factory.updateTryStatement( + node, + visit((node).tryBlock, transformer), + visit((node).catchClause, transformer), + visit((node).finallyBlock, transformer)); + case SyntaxKind.VariableDeclaration: + return factory.updateVariableDeclaration( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visit((node).name, transformer), + visit((node).type, transformer), + visit((node).initializer, transformer)); + case SyntaxKind.VariableDeclarationList: + return factory.updateVariableDeclarationList( + node, + visitNodes((node).declarations, transformer)); + case SyntaxKind.FunctionDeclaration: + return factory.updateFunctionDeclaration( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visit((node).name, transformer), + visitNodes((node).typeParameters, transformer), + visitNodes((node).parameters, transformer), + visit((node).type, transformer), + visit((node).body, transformer)); + case SyntaxKind.ClassDeclaration: + return factory.updateClassDeclaration( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visit((node).name, transformer), + visitNodes((node).typeParameters, transformer), + visitNodes((node).heritageClauses, transformer), + visitNodes((node).members, transformer)); + case SyntaxKind.InterfaceDeclaration: + return factory.updateInterfaceDeclaration( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visit((node).name, transformer), + visitNodes((node).typeParameters, transformer), + visitNodes((node).heritageClauses, transformer), + visitNodes((node).members, transformer)); + case SyntaxKind.TypeAliasDeclaration: + return factory.updateTypeAliasDeclaration( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visit((node).name, transformer), + visitNodes((node).typeParameters, transformer), + visit((node).type, transformer)); + case SyntaxKind.EnumDeclaration: + return factory.updateEnumDeclaration( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visit((node).name, transformer), + visitNodes((node).members, transformer)); + case SyntaxKind.ModuleDeclaration: + return factory.updateModuleDeclaration( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visit((node).name, transformer), + visit((node).body, transformer)); + case SyntaxKind.ModuleBlock: + return factory.updateModuleBlock( + node, + visitNodes((node).statements, transformer)); + case SyntaxKind.CaseBlock: + return factory.updateCaseBlock( + node, + visitNodes((node).clauses, transformer)); + case SyntaxKind.ImportEqualsDeclaration: + return factory.updateImportEqualsDeclaration( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visit((node).name, transformer), + visit((node).moduleReference, transformer)); + case SyntaxKind.ImportDeclaration: + return factory.updateImportDeclaration( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visit((node).importClause, transformer), + visit((node).moduleSpecifier, transformer)); + case SyntaxKind.ImportClause: + return factory.updateImportClause( + node, + visit((node).name, transformer), + visit((node).namedBindings, transformer)); + case SyntaxKind.NamespaceImport: + return factory.updateNamespaceImport( + node, + visit((node).name, transformer)); + case SyntaxKind.NamedImports: + return factory.updateNamedImports( + node, + visitNodes((node).elements, transformer)); + case SyntaxKind.ImportSpecifier: + return factory.updateImportSpecifier( + node, + visit((node).propertyName, transformer), + visit((node).name, transformer)); + case SyntaxKind.ExportAssignment: + return factory.updateExportAssignment( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visit((node).expression, transformer)); + case SyntaxKind.ExportDeclaration: + return factory.updateExportDeclaration( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer), + visit((node).exportClause, transformer), + visit((node).moduleSpecifier, transformer)); + case SyntaxKind.NamedExports: + return factory.updateNamedExports( + node, + visitNodes((node).elements, transformer)); + case SyntaxKind.ExportSpecifier: + return factory.updateExportSpecifier( + node, + visit((node).propertyName, transformer), + visit((node).name, transformer)); + case SyntaxKind.MissingDeclaration: + return factory.updateMissingDeclaration( + node, + visitNodes((node).decorators, transformer), + visitNodes((node).modifiers, transformer)); + case SyntaxKind.ExternalModuleReference: + return factory.updateExternalModuleReference( + node, + visit((node).expression, transformer)); + case SyntaxKind.JsxElement: + return factory.updateJsxElement( + node, + visit((node).openingElement, transformer), + visitNodes((node).children, transformer), + visit((node).closingElement, transformer)); + case SyntaxKind.JsxSelfClosingElement: + return factory.updateJsxSelfClosingElement( + node, + visit((node).tagName, transformer), + visitNodes((node).attributes, transformer)); + case SyntaxKind.JsxOpeningElement: + return factory.updateJsxOpeningElement( + node, + visit((node).tagName, transformer), + visitNodes((node).attributes, transformer)); + case SyntaxKind.JsxClosingElement: + return factory.updateJsxClosingElement( + node, + visit((node).tagName, transformer)); + case SyntaxKind.JsxAttribute: + return factory.updateJsxAttribute( + node, + visit((node).name, transformer), + visit((node).initializer, transformer)); + case SyntaxKind.JsxSpreadAttribute: + return factory.updateJsxSpreadAttribute( + node, + visit((node).expression, transformer)); + case SyntaxKind.JsxExpression: + return factory.updateJsxExpression( + node, + visit((node).expression, transformer)); + case SyntaxKind.CaseClause: + return factory.updateCaseClause( + node, + visit((node).expression, transformer), + visitNodes((node).statements, transformer)); + case SyntaxKind.DefaultClause: + return factory.updateDefaultClause( + node, + visitNodes((node).statements, transformer)); + case SyntaxKind.HeritageClause: + return factory.updateHeritageClause( + node, + visitNodes((node).types, transformer)); + case SyntaxKind.CatchClause: + return factory.updateCatchClause( + node, + visit((node).variableDeclaration, transformer), + visit((node).block, transformer)); + case SyntaxKind.PropertyAssignment: + return factory.updatePropertyAssignment( + node, + visit((node).name, transformer), + visit((node).initializer, transformer)); + case SyntaxKind.ShorthandPropertyAssignment: + return factory.updateShorthandPropertyAssignment( + node, + visit((node).name, transformer)); + case SyntaxKind.EnumMember: + return factory.updateEnumMember( + node, + visit((node).name, transformer), + visit((node).initializer, transformer)); + case SyntaxKind.JSDocTypeExpression: + return factory.updateJSDocTypeExpression( + node, + visit((node).type, transformer)); + case SyntaxKind.JSDocArrayType: + return factory.updateJSDocArrayType( + node, + visit((node).elementType, transformer)); + case SyntaxKind.JSDocUnionType: + return factory.updateJSDocUnionType( + node, + visitNodes((node).types, transformer)); + case SyntaxKind.JSDocTupleType: + return factory.updateJSDocTupleType( + node, + visitNodes((node).types, transformer)); + case SyntaxKind.JSDocNullableType: + return factory.updateJSDocNullableType( + node, + visit((node).type, transformer)); + case SyntaxKind.JSDocNonNullableType: + return factory.updateJSDocNonNullableType( + node, + visit((node).type, transformer)); + case SyntaxKind.JSDocRecordType: + return factory.updateJSDocRecordType( + node, + visitNodes((node).members, transformer)); + case SyntaxKind.JSDocRecordMember: + return factory.updateJSDocRecordMember( + node, + visit((node).name, transformer), + visit((node).type, transformer)); + case SyntaxKind.JSDocTypeReference: + return factory.updateJSDocTypeReference( + node, + visit((node).name, transformer), + visitNodes((node).typeArguments, transformer)); + case SyntaxKind.JSDocOptionalType: + return factory.updateJSDocOptionalType( + node, + visit((node).type, transformer)); + case SyntaxKind.JSDocFunctionType: + return factory.updateJSDocFunctionType( + node, + visitNodes((node).parameters, transformer), + visit((node).type, transformer)); + case SyntaxKind.JSDocVariadicType: + return factory.updateJSDocVariadicType( + node, + visit((node).type, transformer)); + case SyntaxKind.JSDocConstructorType: + return factory.updateJSDocConstructorType( + node, + visit((node).type, transformer)); + case SyntaxKind.JSDocThisType: + return factory.updateJSDocThisType( + node, + visit((node).type, transformer)); + case SyntaxKind.JSDocComment: + return factory.updateJSDocComment( + node, + visitNodes((node).tags, transformer)); + case SyntaxKind.JSDocTag: + return factory.updateJSDocTag( + node, + visit((node).tagName, transformer)); + case SyntaxKind.JSDocParameterTag: + return factory.updateJSDocParameterTag( + node, + visit((node).preParameterName, transformer), + visit((node).typeExpression, transformer), + visit((node).postParameterName, transformer), + visit((node).tagName, transformer)); + case SyntaxKind.JSDocReturnTag: + return factory.updateJSDocReturnTag( + node, + visit((node).typeExpression, transformer), + visit((node).tagName, transformer)); + case SyntaxKind.JSDocTypeTag: + return factory.updateJSDocTypeTag( + node, + visit((node).typeExpression, transformer), + visit((node).tagName, transformer)); + case SyntaxKind.JSDocTemplateTag: + return factory.updateJSDocTemplateTag( + node, + visitNodes((node).typeParameters, transformer), + visit((node).tagName, transformer)); + default: + return node; + } + } +} diff --git a/src/compiler/transform.ts b/src/compiler/transform.ts new file mode 100644 index 00000000000..f0807267792 --- /dev/null +++ b/src/compiler/transform.ts @@ -0,0 +1,124 @@ +/// +/// +/* @internal */ +namespace ts.transform { + export interface TransformerCacheControl { + shouldCachePreviousNodes(node: TNode): boolean; + cacheNode(node: TNode): TNode; + } + + export class Transformer { + private resolver: TransformResolver; + + constructor(resolver: TransformResolver) { + this.resolver = resolver; + } + + public shouldTransformNode(node: Node): boolean { + return false; + } + + public shouldVisitChildrenOfNode(node: Node): boolean { + return false; + } + + public transformNode(node: TNode): TNode { + if (this.shouldVisitChildrenOfNode(node)) { + return visitChildren(node, this); + } + + return node; + } + } + + export function visit(node: TNode, transformer: Transformer): TNode { + if (!node || !transformer) { + return node; + } + + let transformed: TNode; + if (transformer.shouldTransformNode(node)) { + transformed = transformer.transformNode(node); + } + else if (transformer.shouldVisitChildrenOfNode(node)) { + transformed = visitChildren(node, transformer); + } + + if (transformed && transformed !== node) { + aggregateTransformFlags(transformed); + } + + return transformed; + } + + export function visitNodes(nodes: NodeArray, transformer: Transformer, cache?: TransformerCacheControl, removeMissingNodes?: boolean): NodeArray { + if (!nodes || !transformer) { + return nodes; + } + + let updatedNodes: TNode[]; + let updatedOffset = 0; + let cacheOffset = 0; + + for (var i = 0; i < nodes.length; i++) { + let updatedIndex = i - updatedOffset; + let node = nodes[i]; + if (cache && cache.shouldCachePreviousNodes(node)) { + if (!updatedNodes) { + updatedNodes = nodes.slice(0, i); + } + + while (cacheOffset < updatedIndex) { + updatedNodes[cacheOffset] = cache.cacheNode(updatedNodes[cacheOffset]); + cacheOffset++; + } + + cacheOffset = updatedIndex; + } + + let updatedNode = visit(node, transformer); + if ((updatedNodes || updatedNode !== node || (!updatedNode && removeMissingNodes))) { + if (!updatedNodes) { + updatedNodes = nodes.slice(0, i); + } + if (!updatedNode && removeMissingNodes) { + updatedOffset++; + } + else { + updatedNodes[i - updatedOffset] = updatedNode; + } + } + } + + if (updatedNodes) { + return factory.setTextRange( + factory.createNodeArray(updatedNodes), + nodes + ); + } + + return nodes; + } + + let transformFlags: TransformFlags; + + function aggregateChildTransformFlags(child: Node) { + let saveTransformFlags = transformFlags; + aggregateTransformFlags(child); + transformFlags = saveTransformFlags | (transformFlags & ~TransformFlags.ThisNodeFlags); + } + + export function aggregateTransformFlags(node: Node) { + transformFlags = node.transformFlags; + if (transformFlags === undefined) { + forEachChild(node, aggregateChildTransformFlags); + + // TODO(rbuckton): Aggregate transform flags for each node type + switch (node.kind) { + + } + + node.transformFlags = transformFlags; + } + } +} \ No newline at end of file diff --git a/src/compiler/tsconfig.json b/src/compiler/tsconfig.json index a07e91170de..a67a7f1f317 100644 --- a/src/compiler/tsconfig.json +++ b/src/compiler/tsconfig.json @@ -18,6 +18,8 @@ "utilities.ts", "binder.ts", "checker.ts", + "transform.ts", + "transform.generated.ts", "emitter.ts", "program.ts", "commandLineParser.ts", diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 1deb52501cb..6dfc6facea5 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -418,6 +418,19 @@ namespace ts { // Used to know if we've computed data from children and cached it in this node. HasAggregatedChildData = 1 << 7 } + + /* @internal */ + export const enum TransformFlags { + ThisNodeFlags = 0 + } + + /* @internal */ + export interface TransformResolver { + getGeneratedNameForNode(node: Node): string; + makeTempVariableName(loopVariable: boolean): string; + makeUniqueName(baseName: string): string; + getEmitResolver(): EmitResolver; + } export const enum JsxFlags { None = 0, @@ -443,12 +456,14 @@ namespace ts { // @factoryhidden("jsDocComment", true) // @factoryhidden("nextContainer", true) // @factoryorder("decorators", "modifiers") + // @nofactorynodetest export interface Node extends TextRange { kind: SyntaxKind; flags: NodeFlags; // Specific context the parser was in when this node was created. Normally undefined. // Only set when the parser was in some interesting context (like async/yield). /* @internal */ parserContextFlags?: ParserContextFlags; + /* @internal */ transformFlags?: TransformFlags; decorators?: NodeArray; // Array of decorators (in document order) modifiers?: ModifiersArray; // Array of modifiers /* @internal */ id?: number; // Unique id (used to look up NodeLinks) @@ -485,10 +500,12 @@ namespace ts { export type EntityName = Identifier | QualifiedName; + // @nofactorynodetest export type DeclarationName = Identifier | LiteralExpression | ComputedPropertyName | BindingPattern; // @factoryhidden("decorators", false) // @factoryhidden("modifiers", false) + // @nofactorynodetest export interface Declaration extends Node { _declarationBrand: any; name?: DeclarationName; @@ -569,7 +586,7 @@ namespace ts { } // @kind(SyntaxKind.PropertySignature) - export interface PropertySignature extends Declaration, ClassElement { + export interface PropertySignature extends Declaration { name: DeclarationName; // Declared property name // @factoryparam questionToken?: Node; // Present on optional property @@ -578,7 +595,8 @@ namespace ts { // @kind(SyntaxKind.PropertyDeclaration) // @factoryorder("decorators", "modifiers", "name", "questionToken", "type", "initializer") - export interface PropertyDeclaration extends PropertySignature { + export interface PropertyDeclaration extends PropertySignature, ClassElement { + name: DeclarationName; // Declared property name initializer?: Expression; // Optional initializer } @@ -664,7 +682,7 @@ namespace ts { // @factoryhidden("asteriskToken", true) // @factoryhidden("body", true) // @factoryorder("decorators", "modifiers", "asteriskToken", "name", "questionToken", "typeParameters", "parameters", "type") - export interface MethodSignature extends FunctionLikeDeclaration, ClassElement, ObjectLiteralElement { + export interface MethodSignature extends FunctionLikeDeclaration { } // Note that a MethodDeclaration is considered both a ClassElement and an ObjectLiteralElement. @@ -680,7 +698,7 @@ namespace ts { // @factoryhidden("questionToken", true) // @factoryhidden("body", false) // @factoryorder("decorators", "modifiers", "asteriskToken", "name", "typeParameters", "parameters", "type", "body") - export interface MethodDeclaration extends MethodSignature { + export interface MethodDeclaration extends MethodSignature, ClassElement, ObjectLiteralElement { body?: Block; } @@ -733,6 +751,7 @@ namespace ts { _indexSignatureDeclarationBrand: any; } + // @nofactorynodetest export interface TypeNode extends Node { _typeNodeBrand: any; } @@ -818,6 +837,7 @@ namespace ts { // checker actually thinks you have something of the right type. Note: the brands are // never actually given values. At runtime they have zero cost. // @kind(SyntaxKind.OmittedExpression) + // @nofactorynodetest export interface Expression extends Node { _expressionBrand: any; contextualType?: Type; // Used to temporarily assign a contextual type during overload resolution @@ -845,6 +865,12 @@ namespace ts { _postfixExpressionBrand: any; } + // @kind(SyntaxKind.TrueKeyword, { create: false }) + // @kind(SyntaxKind.FalseKeyword, { create: false }) + // @kind(SyntaxKind.NullKeyword, { create: false }) + // @kind(SyntaxKind.ThisKeyword, { create: false }) + // @kind(SyntaxKind.SuperKeyword, { create: false }) + // @nofactorynodetest export interface LeftHandSideExpression extends PostfixExpression { _leftHandSideExpressionBrand: any; } @@ -1081,11 +1107,16 @@ namespace ts { export type JsxChild = JsxText | JsxExpression | JsxElement | JsxSelfClosingElement; - // @kind(SyntaxKind.EmptyStatement) - // @kind(SyntaxKind.DebuggerStatement); + // @nofactorynodetest export interface Statement extends Node { _statementBrand: any; } + + // @kind(SyntaxKind.EmptyStatement) + export type EmptyStatement = Statement; + + // @kind(SyntaxKind.DebuggerStatement) + export type DebuggerStatement = Statement; // @kind(SyntaxKind.MissingDeclaration) // @factoryhidden("name", true) @@ -1471,7 +1502,7 @@ namespace ts { // @factoryhidden("initializer", true) // @factoryhidden("decorators", true) // @factoryhidden("modifiers", true) - export interface JSDocRecordMember extends PropertyDeclaration { + export interface JSDocRecordMember extends PropertySignature { name: Identifier | LiteralExpression, type?: JSDocType } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 2a1778cb19c..eacfc8108ae 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1100,7 +1100,7 @@ namespace ts { return false; } - export function isDeclaration(node: Node): boolean { + export function isDeclaration(node: Node): node is Declaration { switch (node.kind) { case SyntaxKind.ArrowFunction: case SyntaxKind.BindingElement: @@ -1135,7 +1135,7 @@ namespace ts { return false; } - export function isStatement(n: Node): boolean { + export function isStatement(n: Node): n is Statement { switch (n.kind) { case SyntaxKind.BreakStatement: case SyntaxKind.ContinueStatement: @@ -1162,21 +1162,6 @@ namespace ts { } } - export function isClassElement(n: Node): boolean { - switch (n.kind) { - case SyntaxKind.Constructor: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.MethodSignature: - case SyntaxKind.IndexSignature: - return true; - default: - return false; - } - } - // True if the given identifier, string literal, or number literal is the name of a declaration node export function isDeclarationName(name: Node): boolean { if (name.kind !== SyntaxKind.Identifier && name.kind !== SyntaxKind.StringLiteral && name.kind !== SyntaxKind.NumericLiteral) { @@ -1896,7 +1881,7 @@ namespace ts { return 0; } - export function isLeftHandSideExpression(expr: Expression): boolean { + export function isLeftHandSideExpression(expr: Node): boolean { if (expr) { switch (expr.kind) { case SyntaxKind.PropertyAccessExpression: