From 4fdef8560862acd47d75f4d7ec21d3b340fe2f93 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Thu, 15 Feb 2018 09:13:06 -0800 Subject: [PATCH] Naming and duplication cleanup --- src/compiler/binder.ts | 25 ++++++++----------------- src/compiler/checker.ts | 8 ++++---- src/compiler/utilities.ts | 28 +++++++++++++--------------- 3 files changed, 25 insertions(+), 36 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index b40833db7b3..f419c1e0e2c 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2403,34 +2403,25 @@ namespace ts { return lookupSymbolForNameWorker(container, node.escapedText); } else { - const symbol = follow(lookupSymbolForPropertyAccess(node.expression)); + const symbol = getJSInitializerSymbol(lookupSymbolForPropertyAccess(node.expression)); return symbol && symbol.exports && symbol.exports.get(node.name.escapedText); } } - function isNamespaceableInitializer(initializer: Node) { - return initializer.kind === SyntaxKind.ClassExpression || - initializer.kind === SyntaxKind.FunctionExpression || - initializer.kind === SyntaxKind.ObjectLiteralExpression && (initializer as ObjectLiteralExpression).properties.length === 0 || - initializer.kind === SyntaxKind.CallExpression && (skipParentheses((initializer as CallExpression).expression).kind === SyntaxKind.FunctionExpression || - skipParentheses((initializer as CallExpression).expression).kind === SyntaxKind.ArrowFunction); - } - function bindPropertyAssignment(name: EntityNameExpression, propertyAccess: PropertyAccessEntityNameExpression, isPrototypeProperty: boolean) { // Look up the property in the local scope, since property assignments should follow the declaration - let symbol = follow(lookupSymbolForPropertyAccess(name)); - // TODO: Should be able to structure this with less duplication + let symbol = getJSInitializerSymbol(lookupSymbolForPropertyAccess(name)); Debug.assert(propertyAccess.parent.kind === SyntaxKind.BinaryExpression || propertyAccess.parent.kind === SyntaxKind.ExpressionStatement || propertyAccess.parent.kind === SyntaxKind.PropertyAccessExpression); - const isToplevelNamespaceableInitializer = propertyAccess.parent.kind === SyntaxKind.BinaryExpression ? - propertyAccess.parent.parent.parent.kind === SyntaxKind.SourceFile && isNamespaceableInitializer((propertyAccess.parent as BinaryExpression).right) : + const isToplevelNamespaceableInitializer = isBinaryExpression(propertyAccess.parent) ? + propertyAccess.parent.parent.parent.kind === SyntaxKind.SourceFile && getJavascriptInitializer(propertyAccess.parent.right) : propertyAccess.parent.parent.kind === SyntaxKind.SourceFile; if (!isPrototypeProperty && (!symbol || !(symbol.flags & SymbolFlags.Namespace)) && isToplevelNamespaceableInitializer) { const flags = SymbolFlags.Module | SymbolFlags.JSContainer; const excludeFlags = SymbolFlags.ValueModuleExcludes & ~SymbolFlags.JSContainer; - // addDeclarationToSymbol is only needed for things that could be further containers, because it makes the intermediate namespace symbol. - iterateEntityNameExpression(propertyAccess.expression, (id, original) => { + // make symbols are add declarations for intermediate containers + forEachIdentifierInEntityName(propertyAccess.expression, (id, original) => { if (original) { // Note: add declaration to original symbol, not the special-syntax's symbol, so that namespaces work for type lookup addDeclarationToSymbol(original, id, flags); @@ -2460,12 +2451,12 @@ namespace ts { declareSymbol(symbolTable, symbol, propertyAccess, SymbolFlags.Property, SymbolFlags.PropertyExcludes); } - function iterateEntityNameExpression(e: EntityNameExpression, action: (e: Identifier, symbol: Symbol) => Symbol): Symbol { + function forEachIdentifierInEntityName(e: EntityNameExpression, action: (e: Identifier, symbol: Symbol) => Symbol): Symbol { if (isIdentifier(e)) { return action(e, lookupSymbolForPropertyAccess(e)); } else { - const s = follow(iterateEntityNameExpression(e.expression, action)); + const s = getJSInitializerSymbol(forEachIdentifierInEntityName(e.expression, action)); Debug.assert(!!s, "lost the chant"); Debug.assert(!!s.exports, "has no exports"); return action(e.name, s.exports.get(e.name.escapedText)); diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9f10767fa1b..2870aba2589 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -858,11 +858,11 @@ namespace ts { mergeSymbolTable(target.exports, source.exports); } if ((source.flags | target.flags) & SymbolFlags.JSContainer) { - const fs = follow(source); - const ft = follow(target); + const fs = getJSInitializerSymbol(source); + const ft = getJSInitializerSymbol(target); if (fs !== source || ft !== target) { // also follow the source's valueDeclaration and merge its symbol - mergeSymbol(follow(target), follow(source)); + mergeSymbol(getJSInitializerSymbol(target), getJSInitializerSymbol(source)); } } recordMergedSymbol(target, source); @@ -18987,7 +18987,7 @@ namespace ts { } function checkBinaryExpression(node: BinaryExpression, checkMode?: CheckMode) { - if (node.operatorToken.kind === SyntaxKind.BarBarToken && isInJavaScriptFile(node) && getAssignedJavascriptInitializer(node)) { + if (isInJavaScriptFile(node) && getAssignedJavascriptInitializer(node)) { return checkExpression(node.right, checkMode); } return checkBinaryLikeExpression(node.left, node.operatorToken, node.right, checkMode, node); diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 1973108a76f..5cc23dcc2a0 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1472,21 +1472,19 @@ namespace ts { return getSourceTextOfNodeFromSourceFile(sourceFile, str).charCodeAt(0) === CharacterCodes.doubleQuote; } - // TODO: All 5 (!) of these need to be de-duped /** * Returns true if the node is a variable declaration whose initializer is a function or class expression. * This function does not test if the node is in a JavaScript file or not. */ export function isDeclarationOfFunctionOrClassExpression(s: Symbol) { - if (s.valueDeclaration && s.valueDeclaration.kind === SyntaxKind.VariableDeclaration) { - const declaration = s.valueDeclaration as VariableDeclaration; - return declaration.initializer && - (declaration.initializer.kind === SyntaxKind.FunctionExpression || declaration.initializer.kind === SyntaxKind.ClassExpression); + if (s.valueDeclaration && isVariableDeclaration(s.valueDeclaration)) { + return s.valueDeclaration.initializer && + (s.valueDeclaration.initializer.kind === SyntaxKind.FunctionExpression || s.valueDeclaration.initializer.kind === SyntaxKind.ClassExpression); } return false; } - export function follow(symbol: Symbol) { + export function getJSInitializerSymbol(symbol: Symbol) { if (!symbol || !symbol.valueDeclaration) { return symbol; } @@ -1495,10 +1493,6 @@ namespace ts { return e ? e.symbol : symbol; } - /** - * Returns true if the node is a variable declaration whose initializer is a function or class expression, or an empty object literal. - * This function does not test if the node is in a JavaScript file or not. - */ export function getDeclaredJavascriptInitializer(node: Node) { if (node && isVariableDeclaration(node) && node.initializer) { return getJavascriptInitializer(node.initializer) || @@ -1513,11 +1507,15 @@ namespace ts { (getJavascriptInitializer(node.parent.right) || getDefaultedJavascriptInitializer(node.parent.left as EntityNameExpression, node.parent.right)); } - function getJavascriptInitializer(e: Expression) { - if(e.kind === SyntaxKind.FunctionExpression || - e.kind === SyntaxKind.ClassExpression || - isObjectLiteralExpression(e) && e.properties.length === 0) { - return e; + export function getJavascriptInitializer(initializer: Expression) { + if (isCallExpression(initializer)) { + const e = skipParentheses(initializer.expression); + return e.kind === SyntaxKind.FunctionExpression || e.kind === SyntaxKind.ArrowFunction ? initializer : undefined; + } + if(initializer.kind === SyntaxKind.FunctionExpression || + initializer.kind === SyntaxKind.ClassExpression || + isObjectLiteralExpression(initializer) && initializer.properties.length === 0) { + return initializer; } }