diff --git a/TypeScript b/TypeScript index 707d61d..8038eb9 160000 --- a/TypeScript +++ b/TypeScript @@ -1 +1 @@ -Subproject commit 707d61d7fe953b04b3ad117294a33135573ced3a +Subproject commit 8038eb943e6509a5da3377853ca04986bededbb1 diff --git a/bin/ntypescript.d.ts b/bin/ntypescript.d.ts index a9f3df9..e9d9d88 100644 --- a/bin/ntypescript.d.ts +++ b/bin/ntypescript.d.ts @@ -2513,8 +2513,12 @@ declare namespace ts { function assert(expression: boolean, message?: string, verboseDebugInfo?: () => string): void; function fail(message?: string): void; } - function copyListRemovingItem(item: T, list: T[]): T[]; - function createGetCanonicalFileName(useCaseSensitivefileNames: boolean): (fileName: string) => string; + /** Remove an item from an array, moving everything to its right one space left. */ + function orderedRemoveItemAt(array: T[], index: number): void; + function unorderedRemoveItemAt(array: T[], index: number): void; + /** Remove the *first* occurrence of `item` from the array. */ + function unorderedRemoveItem(array: T[], item: T): void; + function createGetCanonicalFileName(useCaseSensitiveFileNames: boolean): (fileName: string) => string; } declare namespace ts { type FileWatcherCallback = (fileName: string, removed?: boolean) => void; @@ -2672,6 +2676,7 @@ declare namespace ts { */ function isSuperPropertyOrElementAccess(node: Node): boolean; function getEntityNameFromTypeNode(node: TypeNode): EntityNameOrEntityNameExpression; + function isCallLikeExpression(node: Node): node is CallLikeExpression; function getInvokedExpression(node: CallLikeExpression): Expression; function nodeCanBeDecorated(node: Node): boolean; function nodeIsDecorated(node: Node): boolean; @@ -2848,6 +2853,8 @@ declare namespace ts { function modifierToFlag(token: SyntaxKind): NodeFlags; function isLeftHandSideExpression(expr: Expression): boolean; function isAssignmentOperator(token: SyntaxKind): boolean; + /** Get `C` given `N` if `N` is in the position `class C extends N` where `N` is an ExpressionWithTypeArguments. */ + function tryGetClassExtendingExpressionWithTypeArguments(node: Node): ClassLikeDeclaration | undefined; function isExpressionWithTypeArgumentsInClassExtendsClause(node: Node): boolean; function isEntityNameExpression(node: Expression): node is EntityNameExpression; function isRightSideOfQualifiedNameOrPropertyAccess(node: Node): boolean; diff --git a/bin/ntypescript.js b/bin/ntypescript.js index e4799e6..7bd4d6b 100644 --- a/bin/ntypescript.js +++ b/bin/ntypescript.js @@ -2397,19 +2397,37 @@ var ts; } Debug.fail = fail; })(Debug = ts.Debug || (ts.Debug = {})); - function copyListRemovingItem(item, list) { - var copiedList = []; - for (var _i = 0, list_1 = list; _i < list_1.length; _i++) { - var e = list_1[_i]; - if (e !== item) { - copiedList.push(e); + /** Remove an item from an array, moving everything to its right one space left. */ + function orderedRemoveItemAt(array, index) { + // This seems to be faster than either `array.splice(i, 1)` or `array.copyWithin(i, i+ 1)`. + for (var i = index; i < array.length - 1; i++) { + array[i] = array[i + 1]; + } + array.pop(); + } + ts.orderedRemoveItemAt = orderedRemoveItemAt; + function unorderedRemoveItemAt(array, index) { + // Fill in the "hole" left at `index`. + array[index] = array[array.length - 1]; + array.pop(); + } + ts.unorderedRemoveItemAt = unorderedRemoveItemAt; + /** Remove the *first* occurrence of `item` from the array. */ + function unorderedRemoveItem(array, item) { + unorderedRemoveFirstItemWhere(array, function (element) { return element === item; }); + } + ts.unorderedRemoveItem = unorderedRemoveItem; + /** Remove the *first* element satisfying `predicate`. */ + function unorderedRemoveFirstItemWhere(array, predicate) { + for (var i = 0; i < array.length; i++) { + if (predicate(array[i])) { + unorderedRemoveItemAt(array, i); + break; } } - return copiedList; } - ts.copyListRemovingItem = copyListRemovingItem; - function createGetCanonicalFileName(useCaseSensitivefileNames) { - return useCaseSensitivefileNames + function createGetCanonicalFileName(useCaseSensitiveFileNames) { + return useCaseSensitiveFileNames ? (function (fileName) { return fileName; }) : (function (fileName) { return fileName.toLowerCase(); }); } @@ -2597,13 +2615,10 @@ var ts; function removeFileWatcherCallback(filePath, callback) { var callbacks = fileWatcherCallbacks[filePath]; if (callbacks) { - var newCallbacks = ts.copyListRemovingItem(callback, callbacks); - if (newCallbacks.length === 0) { + ts.unorderedRemoveItem(callbacks, callback); + if (callbacks.length === 0) { delete fileWatcherCallbacks[filePath]; } - else { - fileWatcherCallbacks[filePath] = newCallbacks; - } } } function fileEventHandler(eventName, relativeFileName, baseDirPath) { @@ -3849,6 +3864,18 @@ var ts; return undefined; } ts.getEntityNameFromTypeNode = getEntityNameFromTypeNode; + function isCallLikeExpression(node) { + switch (node.kind) { + case 174 /* CallExpression */: + case 175 /* NewExpression */: + case 176 /* TaggedTemplateExpression */: + case 143 /* Decorator */: + return true; + default: + return false; + } + } + ts.isCallLikeExpression = isCallLikeExpression; function getInvokedExpression(node) { if (node.kind === 176 /* TaggedTemplateExpression */) { return node.tag; @@ -5366,10 +5393,17 @@ var ts; return token >= 56 /* FirstAssignment */ && token <= 68 /* LastAssignment */; } ts.isAssignmentOperator = isAssignmentOperator; - function isExpressionWithTypeArgumentsInClassExtendsClause(node) { - return node.kind === 194 /* ExpressionWithTypeArguments */ && + /** Get `C` given `N` if `N` is in the position `class C extends N` where `N` is an ExpressionWithTypeArguments. */ + function tryGetClassExtendingExpressionWithTypeArguments(node) { + if (node.kind === 194 /* ExpressionWithTypeArguments */ && node.parent.token === 83 /* ExtendsKeyword */ && - isClassLike(node.parent.parent); + isClassLike(node.parent.parent)) { + return node.parent.parent; + } + } + ts.tryGetClassExtendingExpressionWithTypeArguments = tryGetClassExtendingExpressionWithTypeArguments; + function isExpressionWithTypeArgumentsInClassExtendsClause(node) { + return tryGetClassExtendingExpressionWithTypeArguments(node) !== undefined; } ts.isExpressionWithTypeArgumentsInClassExtendsClause = isExpressionWithTypeArgumentsInClassExtendsClause; function isEntityNameExpression(node) { @@ -21398,7 +21432,7 @@ var ts; while (i > 0) { i--; if (isSubtypeOfAny(types[i], types)) { - types.splice(i, 1); + ts.orderedRemoveItemAt(types, i); } } } @@ -24530,7 +24564,7 @@ var ts; // The location isn't a reference to the given symbol, meaning we're being asked // a hypothetical question of what type the symbol would have if there was a reference // to it at the given location. Since we have no control flow information for the - // hypotherical reference (control flow information is created and attached by the + // hypothetical reference (control flow information is created and attached by the // binder), we simply return the declared type of the symbol. return getTypeOfSymbol(symbol); } @@ -33532,8 +33566,8 @@ var ts; // this needs to be done after global symbol table is initialized to make sure that all ambient modules are indexed for (var _i = 0, augmentations_1 = augmentations; _i < augmentations_1.length; _i++) { var list = augmentations_1[_i]; - for (var _a = 0, list_2 = list; _a < list_2.length; _a++) { - var augmentation = list_2[_a]; + for (var _a = 0, list_1 = list; _a < list_1.length; _a++) { + var augmentation = list_1[_a]; mergeModuleAugmentation(augmentation); } } @@ -47613,10 +47647,7 @@ var ts; sourceFile.fileWatcher.close(); sourceFile.fileWatcher = undefined; if (removed) { - var index = rootFileNames.indexOf(sourceFile.fileName); - if (index >= 0) { - rootFileNames.splice(index, 1); - } + ts.unorderedRemoveItem(rootFileNames, sourceFile.fileName); } startTimerForRecompilation(); } @@ -48613,6 +48644,7 @@ var ts; case 147 /* MethodDeclaration */: case 149 /* GetAccessor */: case 150 /* SetAccessor */: + case 218 /* VariableDeclaration */: return hasSomeImportantChild(item); case 180 /* ArrowFunction */: case 220 /* FunctionDeclaration */: @@ -55572,17 +55604,35 @@ var ts; function isRightSideOfPropertyAccess(node) { return node && node.parent && node.parent.kind === 172 /* PropertyAccessExpression */ && node.parent.name === node; } + function climbPastPropertyAccess(node) { + return isRightSideOfPropertyAccess(node) ? node.parent : node; + } + /** Get `C` given `N` if `N` is in the position `class C extends N` or `class C extends foo.N` where `N` is an identifier. */ + function tryGetClassByExtendingIdentifier(node) { + return ts.tryGetClassExtendingExpressionWithTypeArguments(climbPastPropertyAccess(node).parent); + } function isCallExpressionTarget(node) { - if (isRightSideOfPropertyAccess(node)) { - node = node.parent; - } - return node && node.parent && node.parent.kind === 174 /* CallExpression */ && node.parent.expression === node; + return isCallOrNewExpressionTarget(node, 174 /* CallExpression */); } function isNewExpressionTarget(node) { - if (isRightSideOfPropertyAccess(node)) { - node = node.parent; - } - return node && node.parent && node.parent.kind === 175 /* NewExpression */ && node.parent.expression === node; + return isCallOrNewExpressionTarget(node, 175 /* NewExpression */); + } + function isCallOrNewExpressionTarget(node, kind) { + var target = climbPastPropertyAccess(node); + return target && target.parent && target.parent.kind === kind && target.parent.expression === target; + } + function climbPastManyPropertyAccesses(node) { + return isRightSideOfPropertyAccess(node) ? climbPastManyPropertyAccesses(node.parent) : node; + } + /** Returns a CallLikeExpression where `node` is the target being invoked. */ + function getAncestorCallLikeExpression(node) { + var target = climbPastManyPropertyAccesses(node); + var callLike = target.parent; + return callLike && ts.isCallLikeExpression(callLike) && ts.getInvokedExpression(callLike) === target && callLike; + } + function tryGetSignatureDeclaration(typeChecker, node) { + var callLike = getAncestorCallLikeExpression(node); + return callLike && typeChecker.getResolvedSignature(callLike).declaration; } function isNameOfModuleDeclaration(node) { return node.parent.kind === 225 /* ModuleDeclaration */ && node.parent.name === node; @@ -57606,14 +57656,23 @@ var ts; containerName: containerName }; } + function getSymbolInfo(typeChecker, symbol, node) { + return { + symbolName: typeChecker.symbolToString(symbol), + symbolKind: getSymbolKind(symbol, node), + containerName: symbol.parent ? typeChecker.symbolToString(symbol.parent, node) : "" + }; + } + function createDefinitionFromSignatureDeclaration(decl) { + var typeChecker = program.getTypeChecker(); + var _a = getSymbolInfo(typeChecker, decl.symbol, decl), symbolName = _a.symbolName, symbolKind = _a.symbolKind, containerName = _a.containerName; + return createDefinitionInfo(decl, symbolKind, symbolName, containerName); + } function getDefinitionFromSymbol(symbol, node) { var typeChecker = program.getTypeChecker(); var result = []; var declarations = symbol.getDeclarations(); - var symbolName = typeChecker.symbolToString(symbol); // Do not get scoped name, just the name of the symbol - var symbolKind = getSymbolKind(symbol, node); - var containerSymbol = symbol.parent; - var containerName = containerSymbol ? typeChecker.symbolToString(containerSymbol, node) : ""; + var _a = getSymbolInfo(typeChecker, symbol, node), symbolName = _a.symbolName, symbolKind = _a.symbolKind, containerName = _a.containerName; if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) && !tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) { // Just add all the declarations. @@ -57720,6 +57779,10 @@ var ts; return label ? [createDefinitionInfo(label, ScriptElementKind.label, labelName, /*containerName*/ undefined)] : undefined; } var typeChecker = program.getTypeChecker(); + var calledDeclaration = tryGetSignatureDeclaration(typeChecker, node); + if (calledDeclaration) { + return [createDefinitionFromSignatureDeclaration(calledDeclaration)]; + } var symbol = typeChecker.getSymbolAtLocation(node); // Could not find a symbol e.g. node is string or number keyword, // or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol @@ -58392,6 +58455,7 @@ var ts; case 69 /* Identifier */: case 97 /* ThisKeyword */: // case SyntaxKind.SuperKeyword: TODO:GH#9268 + case 121 /* ConstructorKeyword */: case 9 /* StringLiteral */: return getReferencedSymbolsForNode(node, program.getSourceFiles(), findInStrings, findInComments); } @@ -58430,6 +58494,8 @@ var ts; if (node.kind === 95 /* SuperKeyword */) { return getReferencesForSuperKeyword(node); } + // `getSymbolAtLocation` normally returns the symbol of the class when given the constructor keyword, + // so we have to specify that we want the constructor symbol. var symbol = typeChecker.getSymbolAtLocation(node); if (!symbol && node.kind === 9 /* StringLiteral */) { return getReferencesForStringLiteral(node, sourceFiles); @@ -58512,6 +58578,9 @@ var ts; } return undefined; } + function followAliasIfNecessary(symbol, location) { + return getAliasSymbolForPropertyNameSymbol(symbol, location) || symbol; + } function getPropertySymbolOfDestructuringAssignment(location) { return ts.isArrayLiteralOrObjectLiteralDestructuringPattern(location.parent.parent) && typeChecker.getPropertySymbolOfDestructuringAssignment(location); @@ -58726,7 +58795,8 @@ var ts; if (referenceSymbol) { var referenceSymbolDeclaration = referenceSymbol.valueDeclaration; var shorthandValueSymbol = typeChecker.getShorthandAssignmentValueSymbol(referenceSymbolDeclaration); - var relatedSymbol = getRelatedSymbol(searchSymbols_1, referenceSymbol, referenceLocation); + var relatedSymbol = getRelatedSymbol(searchSymbols_1, referenceSymbol, referenceLocation, + /*searchLocationIsConstructor*/ searchLocation.kind === 121 /* ConstructorKeyword */); if (relatedSymbol) { var referencedSymbol = getReferencedSymbol(relatedSymbol); referencedSymbol.references.push(getReferenceEntryFromNode(referenceLocation)); @@ -58735,10 +58805,86 @@ var ts; var referencedSymbol = getReferencedSymbol(shorthandValueSymbol); referencedSymbol.references.push(getReferenceEntryFromNode(referenceSymbolDeclaration.name)); } + else if (searchLocation.kind === 121 /* ConstructorKeyword */) { + findAdditionalConstructorReferences(referenceSymbol, referenceLocation); + } } }); } return; + /** Adds references when a constructor is used with `new this()` in its own class and `super()` calls in subclasses. */ + function findAdditionalConstructorReferences(referenceSymbol, referenceLocation) { + ts.Debug.assert(ts.isClassLike(searchSymbol.valueDeclaration)); + var referenceClass = referenceLocation.parent; + if (referenceSymbol === searchSymbol && ts.isClassLike(referenceClass)) { + ts.Debug.assert(referenceClass.name === referenceLocation); + // This is the class declaration containing the constructor. + addReferences(findOwnConstructorCalls(searchSymbol)); + } + else { + // If this class appears in `extends C`, then the extending class' "super" calls are references. + var classExtending = tryGetClassByExtendingIdentifier(referenceLocation); + if (classExtending && ts.isClassLike(classExtending) && followAliasIfNecessary(referenceSymbol, referenceLocation) === searchSymbol) { + addReferences(superConstructorAccesses(classExtending)); + } + } + } + function addReferences(references) { + if (references.length) { + var referencedSymbol = getReferencedSymbol(searchSymbol); + ts.addRange(referencedSymbol.references, ts.map(references, getReferenceEntryFromNode)); + } + } + /** `classSymbol` is the class where the constructor was defined. + * Reference the constructor and all calls to `new this()`. + */ + function findOwnConstructorCalls(classSymbol) { + var result = []; + for (var _i = 0, _a = classSymbol.members["__constructor"].declarations; _i < _a.length; _i++) { + var decl = _a[_i]; + ts.Debug.assert(decl.kind === 148 /* Constructor */); + var ctrKeyword = decl.getChildAt(0); + ts.Debug.assert(ctrKeyword.kind === 121 /* ConstructorKeyword */); + result.push(ctrKeyword); + } + ts.forEachProperty(classSymbol.exports, function (member) { + var decl = member.valueDeclaration; + if (decl && decl.kind === 147 /* MethodDeclaration */) { + var body = decl.body; + if (body) { + forEachDescendantOfKind(body, 97 /* ThisKeyword */, function (thisKeyword) { + if (isNewExpressionTarget(thisKeyword)) { + result.push(thisKeyword); + } + }); + } + } + }); + return result; + } + /** Find references to `super` in the constructor of an extending class. */ + function superConstructorAccesses(cls) { + var symbol = cls.symbol; + var ctr = symbol.members["__constructor"]; + if (!ctr) { + return []; + } + var result = []; + for (var _i = 0, _a = ctr.declarations; _i < _a.length; _i++) { + var decl = _a[_i]; + ts.Debug.assert(decl.kind === 148 /* Constructor */); + var body = decl.body; + if (body) { + forEachDescendantOfKind(body, 95 /* SuperKeyword */, function (node) { + if (isCallExpressionTarget(node)) { + result.push(node); + } + }); + } + } + ; + return result; + } function getReferencedSymbol(symbol) { var symbolId = ts.getSymbolId(symbol); var index = symbolToIndex[symbolId]; @@ -59069,15 +59215,16 @@ var ts; } } } - function getRelatedSymbol(searchSymbols, referenceSymbol, referenceLocation) { - if (searchSymbols.indexOf(referenceSymbol) >= 0) { - return referenceSymbol; + function getRelatedSymbol(searchSymbols, referenceSymbol, referenceLocation, searchLocationIsConstructor) { + if (ts.contains(searchSymbols, referenceSymbol)) { + // If we are searching for constructor uses, they must be 'new' expressions. + return (!searchLocationIsConstructor || isNewExpressionTarget(referenceLocation)) && referenceSymbol; } // If the reference symbol is an alias, check if what it is aliasing is one of the search // symbols but by looking up for related symbol of this alias so it can handle multiple level of indirectness. var aliasSymbol = getAliasSymbolForPropertyNameSymbol(referenceSymbol, referenceLocation); if (aliasSymbol) { - return getRelatedSymbol(searchSymbols, aliasSymbol, referenceLocation); + return getRelatedSymbol(searchSymbols, aliasSymbol, referenceLocation, searchLocationIsConstructor); } // If the reference location is in an object literal, try to get the contextual type for the // object literal, lookup the property symbol in the contextual type, and use this symbol to @@ -60392,6 +60539,14 @@ var ts; }; } ts.createLanguageService = createLanguageService; + function forEachDescendantOfKind(node, kind, action) { + ts.forEachChild(node, function (child) { + if (child.kind === kind) { + action(child); + } + forEachDescendantOfKind(child, kind, action); + }); + } /* @internal */ function getNameTable(sourceFile) { if (!sourceFile.nameTable) { diff --git a/bin/typescript.d.ts b/bin/typescript.d.ts index 71bc1a9..e95da9f 100644 --- a/bin/typescript.d.ts +++ b/bin/typescript.d.ts @@ -2513,8 +2513,12 @@ declare namespace ts { function assert(expression: boolean, message?: string, verboseDebugInfo?: () => string): void; function fail(message?: string): void; } - function copyListRemovingItem(item: T, list: T[]): T[]; - function createGetCanonicalFileName(useCaseSensitivefileNames: boolean): (fileName: string) => string; + /** Remove an item from an array, moving everything to its right one space left. */ + function orderedRemoveItemAt(array: T[], index: number): void; + function unorderedRemoveItemAt(array: T[], index: number): void; + /** Remove the *first* occurrence of `item` from the array. */ + function unorderedRemoveItem(array: T[], item: T): void; + function createGetCanonicalFileName(useCaseSensitiveFileNames: boolean): (fileName: string) => string; } declare namespace ts { type FileWatcherCallback = (fileName: string, removed?: boolean) => void; @@ -2672,6 +2676,7 @@ declare namespace ts { */ function isSuperPropertyOrElementAccess(node: Node): boolean; function getEntityNameFromTypeNode(node: TypeNode): EntityNameOrEntityNameExpression; + function isCallLikeExpression(node: Node): node is CallLikeExpression; function getInvokedExpression(node: CallLikeExpression): Expression; function nodeCanBeDecorated(node: Node): boolean; function nodeIsDecorated(node: Node): boolean; @@ -2848,6 +2853,8 @@ declare namespace ts { function modifierToFlag(token: SyntaxKind): NodeFlags; function isLeftHandSideExpression(expr: Expression): boolean; function isAssignmentOperator(token: SyntaxKind): boolean; + /** Get `C` given `N` if `N` is in the position `class C extends N` where `N` is an ExpressionWithTypeArguments. */ + function tryGetClassExtendingExpressionWithTypeArguments(node: Node): ClassLikeDeclaration | undefined; function isExpressionWithTypeArgumentsInClassExtendsClause(node: Node): boolean; function isEntityNameExpression(node: Expression): node is EntityNameExpression; function isRightSideOfQualifiedNameOrPropertyAccess(node: Node): boolean; diff --git a/bin/typescript.js b/bin/typescript.js index 27aaa3a..0b4b752 100644 --- a/bin/typescript.js +++ b/bin/typescript.js @@ -2397,19 +2397,37 @@ var ts; } Debug.fail = fail; })(Debug = ts.Debug || (ts.Debug = {})); - function copyListRemovingItem(item, list) { - var copiedList = []; - for (var _i = 0, list_1 = list; _i < list_1.length; _i++) { - var e = list_1[_i]; - if (e !== item) { - copiedList.push(e); + /** Remove an item from an array, moving everything to its right one space left. */ + function orderedRemoveItemAt(array, index) { + // This seems to be faster than either `array.splice(i, 1)` or `array.copyWithin(i, i+ 1)`. + for (var i = index; i < array.length - 1; i++) { + array[i] = array[i + 1]; + } + array.pop(); + } + ts.orderedRemoveItemAt = orderedRemoveItemAt; + function unorderedRemoveItemAt(array, index) { + // Fill in the "hole" left at `index`. + array[index] = array[array.length - 1]; + array.pop(); + } + ts.unorderedRemoveItemAt = unorderedRemoveItemAt; + /** Remove the *first* occurrence of `item` from the array. */ + function unorderedRemoveItem(array, item) { + unorderedRemoveFirstItemWhere(array, function (element) { return element === item; }); + } + ts.unorderedRemoveItem = unorderedRemoveItem; + /** Remove the *first* element satisfying `predicate`. */ + function unorderedRemoveFirstItemWhere(array, predicate) { + for (var i = 0; i < array.length; i++) { + if (predicate(array[i])) { + unorderedRemoveItemAt(array, i); + break; } } - return copiedList; } - ts.copyListRemovingItem = copyListRemovingItem; - function createGetCanonicalFileName(useCaseSensitivefileNames) { - return useCaseSensitivefileNames + function createGetCanonicalFileName(useCaseSensitiveFileNames) { + return useCaseSensitiveFileNames ? (function (fileName) { return fileName; }) : (function (fileName) { return fileName.toLowerCase(); }); } @@ -2597,13 +2615,10 @@ var ts; function removeFileWatcherCallback(filePath, callback) { var callbacks = fileWatcherCallbacks[filePath]; if (callbacks) { - var newCallbacks = ts.copyListRemovingItem(callback, callbacks); - if (newCallbacks.length === 0) { + ts.unorderedRemoveItem(callbacks, callback); + if (callbacks.length === 0) { delete fileWatcherCallbacks[filePath]; } - else { - fileWatcherCallbacks[filePath] = newCallbacks; - } } } function fileEventHandler(eventName, relativeFileName, baseDirPath) { @@ -3849,6 +3864,18 @@ var ts; return undefined; } ts.getEntityNameFromTypeNode = getEntityNameFromTypeNode; + function isCallLikeExpression(node) { + switch (node.kind) { + case 174 /* CallExpression */: + case 175 /* NewExpression */: + case 176 /* TaggedTemplateExpression */: + case 143 /* Decorator */: + return true; + default: + return false; + } + } + ts.isCallLikeExpression = isCallLikeExpression; function getInvokedExpression(node) { if (node.kind === 176 /* TaggedTemplateExpression */) { return node.tag; @@ -5366,10 +5393,17 @@ var ts; return token >= 56 /* FirstAssignment */ && token <= 68 /* LastAssignment */; } ts.isAssignmentOperator = isAssignmentOperator; - function isExpressionWithTypeArgumentsInClassExtendsClause(node) { - return node.kind === 194 /* ExpressionWithTypeArguments */ && + /** Get `C` given `N` if `N` is in the position `class C extends N` where `N` is an ExpressionWithTypeArguments. */ + function tryGetClassExtendingExpressionWithTypeArguments(node) { + if (node.kind === 194 /* ExpressionWithTypeArguments */ && node.parent.token === 83 /* ExtendsKeyword */ && - isClassLike(node.parent.parent); + isClassLike(node.parent.parent)) { + return node.parent.parent; + } + } + ts.tryGetClassExtendingExpressionWithTypeArguments = tryGetClassExtendingExpressionWithTypeArguments; + function isExpressionWithTypeArgumentsInClassExtendsClause(node) { + return tryGetClassExtendingExpressionWithTypeArguments(node) !== undefined; } ts.isExpressionWithTypeArgumentsInClassExtendsClause = isExpressionWithTypeArgumentsInClassExtendsClause; function isEntityNameExpression(node) { @@ -21398,7 +21432,7 @@ var ts; while (i > 0) { i--; if (isSubtypeOfAny(types[i], types)) { - types.splice(i, 1); + ts.orderedRemoveItemAt(types, i); } } } @@ -24530,7 +24564,7 @@ var ts; // The location isn't a reference to the given symbol, meaning we're being asked // a hypothetical question of what type the symbol would have if there was a reference // to it at the given location. Since we have no control flow information for the - // hypotherical reference (control flow information is created and attached by the + // hypothetical reference (control flow information is created and attached by the // binder), we simply return the declared type of the symbol. return getTypeOfSymbol(symbol); } @@ -33532,8 +33566,8 @@ var ts; // this needs to be done after global symbol table is initialized to make sure that all ambient modules are indexed for (var _i = 0, augmentations_1 = augmentations; _i < augmentations_1.length; _i++) { var list = augmentations_1[_i]; - for (var _a = 0, list_2 = list; _a < list_2.length; _a++) { - var augmentation = list_2[_a]; + for (var _a = 0, list_1 = list; _a < list_1.length; _a++) { + var augmentation = list_1[_a]; mergeModuleAugmentation(augmentation); } } @@ -47613,10 +47647,7 @@ var ts; sourceFile.fileWatcher.close(); sourceFile.fileWatcher = undefined; if (removed) { - var index = rootFileNames.indexOf(sourceFile.fileName); - if (index >= 0) { - rootFileNames.splice(index, 1); - } + ts.unorderedRemoveItem(rootFileNames, sourceFile.fileName); } startTimerForRecompilation(); } @@ -48613,6 +48644,7 @@ var ts; case 147 /* MethodDeclaration */: case 149 /* GetAccessor */: case 150 /* SetAccessor */: + case 218 /* VariableDeclaration */: return hasSomeImportantChild(item); case 180 /* ArrowFunction */: case 220 /* FunctionDeclaration */: @@ -55572,17 +55604,35 @@ var ts; function isRightSideOfPropertyAccess(node) { return node && node.parent && node.parent.kind === 172 /* PropertyAccessExpression */ && node.parent.name === node; } + function climbPastPropertyAccess(node) { + return isRightSideOfPropertyAccess(node) ? node.parent : node; + } + /** Get `C` given `N` if `N` is in the position `class C extends N` or `class C extends foo.N` where `N` is an identifier. */ + function tryGetClassByExtendingIdentifier(node) { + return ts.tryGetClassExtendingExpressionWithTypeArguments(climbPastPropertyAccess(node).parent); + } function isCallExpressionTarget(node) { - if (isRightSideOfPropertyAccess(node)) { - node = node.parent; - } - return node && node.parent && node.parent.kind === 174 /* CallExpression */ && node.parent.expression === node; + return isCallOrNewExpressionTarget(node, 174 /* CallExpression */); } function isNewExpressionTarget(node) { - if (isRightSideOfPropertyAccess(node)) { - node = node.parent; - } - return node && node.parent && node.parent.kind === 175 /* NewExpression */ && node.parent.expression === node; + return isCallOrNewExpressionTarget(node, 175 /* NewExpression */); + } + function isCallOrNewExpressionTarget(node, kind) { + var target = climbPastPropertyAccess(node); + return target && target.parent && target.parent.kind === kind && target.parent.expression === target; + } + function climbPastManyPropertyAccesses(node) { + return isRightSideOfPropertyAccess(node) ? climbPastManyPropertyAccesses(node.parent) : node; + } + /** Returns a CallLikeExpression where `node` is the target being invoked. */ + function getAncestorCallLikeExpression(node) { + var target = climbPastManyPropertyAccesses(node); + var callLike = target.parent; + return callLike && ts.isCallLikeExpression(callLike) && ts.getInvokedExpression(callLike) === target && callLike; + } + function tryGetSignatureDeclaration(typeChecker, node) { + var callLike = getAncestorCallLikeExpression(node); + return callLike && typeChecker.getResolvedSignature(callLike).declaration; } function isNameOfModuleDeclaration(node) { return node.parent.kind === 225 /* ModuleDeclaration */ && node.parent.name === node; @@ -57606,14 +57656,23 @@ var ts; containerName: containerName }; } + function getSymbolInfo(typeChecker, symbol, node) { + return { + symbolName: typeChecker.symbolToString(symbol), + symbolKind: getSymbolKind(symbol, node), + containerName: symbol.parent ? typeChecker.symbolToString(symbol.parent, node) : "" + }; + } + function createDefinitionFromSignatureDeclaration(decl) { + var typeChecker = program.getTypeChecker(); + var _a = getSymbolInfo(typeChecker, decl.symbol, decl), symbolName = _a.symbolName, symbolKind = _a.symbolKind, containerName = _a.containerName; + return createDefinitionInfo(decl, symbolKind, symbolName, containerName); + } function getDefinitionFromSymbol(symbol, node) { var typeChecker = program.getTypeChecker(); var result = []; var declarations = symbol.getDeclarations(); - var symbolName = typeChecker.symbolToString(symbol); // Do not get scoped name, just the name of the symbol - var symbolKind = getSymbolKind(symbol, node); - var containerSymbol = symbol.parent; - var containerName = containerSymbol ? typeChecker.symbolToString(containerSymbol, node) : ""; + var _a = getSymbolInfo(typeChecker, symbol, node), symbolName = _a.symbolName, symbolKind = _a.symbolKind, containerName = _a.containerName; if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) && !tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) { // Just add all the declarations. @@ -57720,6 +57779,10 @@ var ts; return label ? [createDefinitionInfo(label, ScriptElementKind.label, labelName, /*containerName*/ undefined)] : undefined; } var typeChecker = program.getTypeChecker(); + var calledDeclaration = tryGetSignatureDeclaration(typeChecker, node); + if (calledDeclaration) { + return [createDefinitionFromSignatureDeclaration(calledDeclaration)]; + } var symbol = typeChecker.getSymbolAtLocation(node); // Could not find a symbol e.g. node is string or number keyword, // or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol @@ -58392,6 +58455,7 @@ var ts; case 69 /* Identifier */: case 97 /* ThisKeyword */: // case SyntaxKind.SuperKeyword: TODO:GH#9268 + case 121 /* ConstructorKeyword */: case 9 /* StringLiteral */: return getReferencedSymbolsForNode(node, program.getSourceFiles(), findInStrings, findInComments); } @@ -58430,6 +58494,8 @@ var ts; if (node.kind === 95 /* SuperKeyword */) { return getReferencesForSuperKeyword(node); } + // `getSymbolAtLocation` normally returns the symbol of the class when given the constructor keyword, + // so we have to specify that we want the constructor symbol. var symbol = typeChecker.getSymbolAtLocation(node); if (!symbol && node.kind === 9 /* StringLiteral */) { return getReferencesForStringLiteral(node, sourceFiles); @@ -58512,6 +58578,9 @@ var ts; } return undefined; } + function followAliasIfNecessary(symbol, location) { + return getAliasSymbolForPropertyNameSymbol(symbol, location) || symbol; + } function getPropertySymbolOfDestructuringAssignment(location) { return ts.isArrayLiteralOrObjectLiteralDestructuringPattern(location.parent.parent) && typeChecker.getPropertySymbolOfDestructuringAssignment(location); @@ -58726,7 +58795,8 @@ var ts; if (referenceSymbol) { var referenceSymbolDeclaration = referenceSymbol.valueDeclaration; var shorthandValueSymbol = typeChecker.getShorthandAssignmentValueSymbol(referenceSymbolDeclaration); - var relatedSymbol = getRelatedSymbol(searchSymbols_1, referenceSymbol, referenceLocation); + var relatedSymbol = getRelatedSymbol(searchSymbols_1, referenceSymbol, referenceLocation, + /*searchLocationIsConstructor*/ searchLocation.kind === 121 /* ConstructorKeyword */); if (relatedSymbol) { var referencedSymbol = getReferencedSymbol(relatedSymbol); referencedSymbol.references.push(getReferenceEntryFromNode(referenceLocation)); @@ -58735,10 +58805,86 @@ var ts; var referencedSymbol = getReferencedSymbol(shorthandValueSymbol); referencedSymbol.references.push(getReferenceEntryFromNode(referenceSymbolDeclaration.name)); } + else if (searchLocation.kind === 121 /* ConstructorKeyword */) { + findAdditionalConstructorReferences(referenceSymbol, referenceLocation); + } } }); } return; + /** Adds references when a constructor is used with `new this()` in its own class and `super()` calls in subclasses. */ + function findAdditionalConstructorReferences(referenceSymbol, referenceLocation) { + ts.Debug.assert(ts.isClassLike(searchSymbol.valueDeclaration)); + var referenceClass = referenceLocation.parent; + if (referenceSymbol === searchSymbol && ts.isClassLike(referenceClass)) { + ts.Debug.assert(referenceClass.name === referenceLocation); + // This is the class declaration containing the constructor. + addReferences(findOwnConstructorCalls(searchSymbol)); + } + else { + // If this class appears in `extends C`, then the extending class' "super" calls are references. + var classExtending = tryGetClassByExtendingIdentifier(referenceLocation); + if (classExtending && ts.isClassLike(classExtending) && followAliasIfNecessary(referenceSymbol, referenceLocation) === searchSymbol) { + addReferences(superConstructorAccesses(classExtending)); + } + } + } + function addReferences(references) { + if (references.length) { + var referencedSymbol = getReferencedSymbol(searchSymbol); + ts.addRange(referencedSymbol.references, ts.map(references, getReferenceEntryFromNode)); + } + } + /** `classSymbol` is the class where the constructor was defined. + * Reference the constructor and all calls to `new this()`. + */ + function findOwnConstructorCalls(classSymbol) { + var result = []; + for (var _i = 0, _a = classSymbol.members["__constructor"].declarations; _i < _a.length; _i++) { + var decl = _a[_i]; + ts.Debug.assert(decl.kind === 148 /* Constructor */); + var ctrKeyword = decl.getChildAt(0); + ts.Debug.assert(ctrKeyword.kind === 121 /* ConstructorKeyword */); + result.push(ctrKeyword); + } + ts.forEachProperty(classSymbol.exports, function (member) { + var decl = member.valueDeclaration; + if (decl && decl.kind === 147 /* MethodDeclaration */) { + var body = decl.body; + if (body) { + forEachDescendantOfKind(body, 97 /* ThisKeyword */, function (thisKeyword) { + if (isNewExpressionTarget(thisKeyword)) { + result.push(thisKeyword); + } + }); + } + } + }); + return result; + } + /** Find references to `super` in the constructor of an extending class. */ + function superConstructorAccesses(cls) { + var symbol = cls.symbol; + var ctr = symbol.members["__constructor"]; + if (!ctr) { + return []; + } + var result = []; + for (var _i = 0, _a = ctr.declarations; _i < _a.length; _i++) { + var decl = _a[_i]; + ts.Debug.assert(decl.kind === 148 /* Constructor */); + var body = decl.body; + if (body) { + forEachDescendantOfKind(body, 95 /* SuperKeyword */, function (node) { + if (isCallExpressionTarget(node)) { + result.push(node); + } + }); + } + } + ; + return result; + } function getReferencedSymbol(symbol) { var symbolId = ts.getSymbolId(symbol); var index = symbolToIndex[symbolId]; @@ -59069,15 +59215,16 @@ var ts; } } } - function getRelatedSymbol(searchSymbols, referenceSymbol, referenceLocation) { - if (searchSymbols.indexOf(referenceSymbol) >= 0) { - return referenceSymbol; + function getRelatedSymbol(searchSymbols, referenceSymbol, referenceLocation, searchLocationIsConstructor) { + if (ts.contains(searchSymbols, referenceSymbol)) { + // If we are searching for constructor uses, they must be 'new' expressions. + return (!searchLocationIsConstructor || isNewExpressionTarget(referenceLocation)) && referenceSymbol; } // If the reference symbol is an alias, check if what it is aliasing is one of the search // symbols but by looking up for related symbol of this alias so it can handle multiple level of indirectness. var aliasSymbol = getAliasSymbolForPropertyNameSymbol(referenceSymbol, referenceLocation); if (aliasSymbol) { - return getRelatedSymbol(searchSymbols, aliasSymbol, referenceLocation); + return getRelatedSymbol(searchSymbols, aliasSymbol, referenceLocation, searchLocationIsConstructor); } // If the reference location is in an object literal, try to get the contextual type for the // object literal, lookup the property symbol in the contextual type, and use this symbol to @@ -60392,6 +60539,14 @@ var ts; }; } ts.createLanguageService = createLanguageService; + function forEachDescendantOfKind(node, kind, action) { + ts.forEachChild(node, function (child) { + if (child.kind === kind) { + action(child); + } + forEachDescendantOfKind(child, kind, action); + }); + } /* @internal */ function getNameTable(sourceFile) { if (!sourceFile.nameTable) { diff --git a/kicktravis b/kicktravis index 871dccf..85d43c4 100644 --- a/kicktravis +++ b/kicktravis @@ -1 +1 @@ -2016-09-01 [ci skip] Version: 1.201609010006.1+707d61d7fe953b04b3ad117294a33135573ced3a +2016-09-02 [ci skip] Version: 1.201609020105.1+8038eb943e6509a5da3377853ca04986bededbb1 diff --git a/package.json b/package.json index ea745c7..98a571b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ntypescript", - "version": "1.201609010006.1+707d61d7fe953b04b3ad117294a33135573ced3a", + "version": "1.201609020105.1+8038eb943e6509a5da3377853ca04986bededbb1", "description": "A nicer version of microsoft/typescript packaged and released for API developers", "main": "./bin/ntypescript.js", "bin": { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 863c9e7..5910022 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5378,7 +5378,7 @@ namespace ts { while (i > 0) { i--; if (isSubtypeOfAny(types[i], types)) { - types.splice(i, 1); + orderedRemoveItemAt(types, i); } } } @@ -8758,7 +8758,7 @@ namespace ts { // The location isn't a reference to the given symbol, meaning we're being asked // a hypothetical question of what type the symbol would have if there was a reference // to it at the given location. Since we have no control flow information for the - // hypotherical reference (control flow information is created and attached by the + // hypothetical reference (control flow information is created and attached by the // binder), we simply return the declared type of the symbol. return getTypeOfSymbol(symbol); } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 27b27bc..eb17740 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1500,20 +1500,39 @@ namespace ts { } } - export function copyListRemovingItem(item: T, list: T[]) { - const copiedList: T[] = []; - for (const e of list) { - if (e !== item) { - copiedList.push(e); + /** Remove an item from an array, moving everything to its right one space left. */ + export function orderedRemoveItemAt(array: T[], index: number): void { + // This seems to be faster than either `array.splice(i, 1)` or `array.copyWithin(i, i+ 1)`. + for (let i = index; i < array.length - 1; i++) { + array[i] = array[i + 1]; + } + array.pop(); + } + + export function unorderedRemoveItemAt(array: T[], index: number): void { + // Fill in the "hole" left at `index`. + array[index] = array[array.length - 1]; + array.pop(); + } + + /** Remove the *first* occurrence of `item` from the array. */ + export function unorderedRemoveItem(array: T[], item: T): void { + unorderedRemoveFirstItemWhere(array, element => element === item); + } + + /** Remove the *first* element satisfying `predicate`. */ + function unorderedRemoveFirstItemWhere(array: T[], predicate: (element: T) => boolean): void { + for (let i = 0; i < array.length; i++) { + if (predicate(array[i])) { + unorderedRemoveItemAt(array, i); + break; } } - return copiedList; } - export function createGetCanonicalFileName(useCaseSensitivefileNames: boolean): (fileName: string) => string { - return useCaseSensitivefileNames + export function createGetCanonicalFileName(useCaseSensitiveFileNames: boolean): (fileName: string) => string { + return useCaseSensitiveFileNames ? ((fileName) => fileName) : ((fileName) => fileName.toLowerCase()); } - } diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 350d754..118186b 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -285,13 +285,10 @@ namespace ts { function removeFileWatcherCallback(filePath: string, callback: FileWatcherCallback) { const callbacks = fileWatcherCallbacks[filePath]; if (callbacks) { - const newCallbacks = copyListRemovingItem(callback, callbacks); - if (newCallbacks.length === 0) { + unorderedRemoveItem(callbacks, callback); + if (callbacks.length === 0) { delete fileWatcherCallbacks[filePath]; } - else { - fileWatcherCallbacks[filePath] = newCallbacks; - } } } diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 62b5860..6192e5d 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -489,10 +489,7 @@ namespace ts { sourceFile.fileWatcher.close(); sourceFile.fileWatcher = undefined; if (removed) { - const index = rootFileNames.indexOf(sourceFile.fileName); - if (index >= 0) { - rootFileNames.splice(index, 1); - } + unorderedRemoveItem(rootFileNames, sourceFile.fileName); } startTimerForRecompilation(); } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 0c026fc..637f362 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1035,6 +1035,18 @@ namespace ts { return undefined; } + export function isCallLikeExpression(node: Node): node is CallLikeExpression { + switch (node.kind) { + case SyntaxKind.CallExpression: + case SyntaxKind.NewExpression: + case SyntaxKind.TaggedTemplateExpression: + case SyntaxKind.Decorator: + return true; + default: + return false; + } + } + export function getInvokedExpression(node: CallLikeExpression): Expression { if (node.kind === SyntaxKind.TaggedTemplateExpression) { return (node).tag; @@ -2659,10 +2671,17 @@ namespace ts { return token >= SyntaxKind.FirstAssignment && token <= SyntaxKind.LastAssignment; } - export function isExpressionWithTypeArgumentsInClassExtendsClause(node: Node): boolean { - return node.kind === SyntaxKind.ExpressionWithTypeArguments && + /** Get `C` given `N` if `N` is in the position `class C extends N` where `N` is an ExpressionWithTypeArguments. */ + export function tryGetClassExtendingExpressionWithTypeArguments(node: Node): ClassLikeDeclaration | undefined { + if (node.kind === SyntaxKind.ExpressionWithTypeArguments && (node.parent).token === SyntaxKind.ExtendsKeyword && - isClassLike(node.parent.parent); + isClassLike(node.parent.parent)) { + return node.parent.parent; + } + } + + export function isExpressionWithTypeArgumentsInClassExtendsClause(node: Node): boolean { + return tryGetClassExtendingExpressionWithTypeArguments(node) !== undefined; } export function isEntityNameExpression(node: Expression): node is EntityNameExpression { diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index dda9ef4..83c0853 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -466,6 +466,7 @@ namespace ts.NavigationBar { case SyntaxKind.MethodDeclaration: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: + case SyntaxKind.VariableDeclaration: return hasSomeImportantChild(item); case SyntaxKind.ArrowFunction: diff --git a/src/services/services.ts b/src/services/services.ts index c19eb48..c97455c 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2792,18 +2792,42 @@ namespace ts { return node && node.parent && node.parent.kind === SyntaxKind.PropertyAccessExpression && (node.parent).name === node; } + function climbPastPropertyAccess(node: Node) { + return isRightSideOfPropertyAccess(node) ? node.parent : node; + } + + /** Get `C` given `N` if `N` is in the position `class C extends N` or `class C extends foo.N` where `N` is an identifier. */ + function tryGetClassByExtendingIdentifier(node: Node): ClassLikeDeclaration | undefined { + return tryGetClassExtendingExpressionWithTypeArguments(climbPastPropertyAccess(node).parent); + } + function isCallExpressionTarget(node: Node): boolean { - if (isRightSideOfPropertyAccess(node)) { - node = node.parent; - } - return node && node.parent && node.parent.kind === SyntaxKind.CallExpression && (node.parent).expression === node; + return isCallOrNewExpressionTarget(node, SyntaxKind.CallExpression); } function isNewExpressionTarget(node: Node): boolean { - if (isRightSideOfPropertyAccess(node)) { - node = node.parent; - } - return node && node.parent && node.parent.kind === SyntaxKind.NewExpression && (node.parent).expression === node; + return isCallOrNewExpressionTarget(node, SyntaxKind.NewExpression); + } + + function isCallOrNewExpressionTarget(node: Node, kind: SyntaxKind) { + const target = climbPastPropertyAccess(node); + return target && target.parent && target.parent.kind === kind && (target.parent).expression === target; + } + + function climbPastManyPropertyAccesses(node: Node): Node { + return isRightSideOfPropertyAccess(node) ? climbPastManyPropertyAccesses(node.parent) : node; + } + + /** Returns a CallLikeExpression where `node` is the target being invoked. */ + function getAncestorCallLikeExpression(node: Node): CallLikeExpression | undefined { + const target = climbPastManyPropertyAccesses(node); + const callLike = target.parent; + return callLike && isCallLikeExpression(callLike) && getInvokedExpression(callLike) === target && callLike; + } + + function tryGetSignatureDeclaration(typeChecker: TypeChecker, node: Node): SignatureDeclaration | undefined { + const callLike = getAncestorCallLikeExpression(node); + return callLike && typeChecker.getResolvedSignature(callLike).declaration; } function isNameOfModuleDeclaration(node: Node) { @@ -4609,7 +4633,7 @@ namespace ts { const symbolFlags = symbol.flags; let symbolKind = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(symbol, symbolFlags, location); let hasAddedSymbolInfo: boolean; - const isThisExpression: boolean = location.kind === SyntaxKind.ThisKeyword && isExpression(location); + const isThisExpression = location.kind === SyntaxKind.ThisKeyword && isExpression(location); let type: Type; // Class at constructor site need to be shown as constructor apart from property,method, vars @@ -5072,14 +5096,25 @@ namespace ts { }; } + function getSymbolInfo(typeChecker: TypeChecker, symbol: Symbol, node: Node) { + return { + symbolName: typeChecker.symbolToString(symbol), // Do not get scoped name, just the name of the symbol + symbolKind: getSymbolKind(symbol, node), + containerName: symbol.parent ? typeChecker.symbolToString(symbol.parent, node) : "" + }; + } + + function createDefinitionFromSignatureDeclaration(decl: SignatureDeclaration): DefinitionInfo { + const typeChecker = program.getTypeChecker(); + const { symbolName, symbolKind, containerName } = getSymbolInfo(typeChecker, decl.symbol, decl); + return createDefinitionInfo(decl, symbolKind, symbolName, containerName); + } + function getDefinitionFromSymbol(symbol: Symbol, node: Node): DefinitionInfo[] { const typeChecker = program.getTypeChecker(); const result: DefinitionInfo[] = []; const declarations = symbol.getDeclarations(); - const symbolName = typeChecker.symbolToString(symbol); // Do not get scoped name, just the name of the symbol - const symbolKind = getSymbolKind(symbol, node); - const containerSymbol = symbol.parent; - const containerName = containerSymbol ? typeChecker.symbolToString(containerSymbol, node) : ""; + const { symbolName, symbolKind, containerName } = getSymbolInfo(typeChecker, symbol, node); if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) && !tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) { @@ -5205,6 +5240,12 @@ namespace ts { } const typeChecker = program.getTypeChecker(); + + const calledDeclaration = tryGetSignatureDeclaration(typeChecker, node); + if (calledDeclaration) { + return [createDefinitionFromSignatureDeclaration(calledDeclaration)]; + } + let symbol = typeChecker.getSymbolAtLocation(node); // Could not find a symbol e.g. node is string or number keyword, @@ -6012,6 +6053,7 @@ namespace ts { case SyntaxKind.Identifier: case SyntaxKind.ThisKeyword: // case SyntaxKind.SuperKeyword: TODO:GH#9268 + case SyntaxKind.ConstructorKeyword: case SyntaxKind.StringLiteral: return getReferencedSymbolsForNode(node, program.getSourceFiles(), findInStrings, findInComments); } @@ -6056,6 +6098,8 @@ namespace ts { return getReferencesForSuperKeyword(node); } + // `getSymbolAtLocation` normally returns the symbol of the class when given the constructor keyword, + // so we have to specify that we want the constructor symbol. const symbol = typeChecker.getSymbolAtLocation(node); if (!symbol && node.kind === SyntaxKind.StringLiteral) { @@ -6130,7 +6174,7 @@ namespace ts { }; } - function getAliasSymbolForPropertyNameSymbol(symbol: Symbol, location: Node): Symbol { + function getAliasSymbolForPropertyNameSymbol(symbol: Symbol, location: Node): Symbol | undefined { if (symbol.flags & SymbolFlags.Alias) { // Default import get alias const defaultImport = getDeclarationOfKind(symbol, SyntaxKind.ImportClause); @@ -6156,6 +6200,10 @@ namespace ts { return undefined; } + function followAliasIfNecessary(symbol: Symbol, location: Node): Symbol { + return getAliasSymbolForPropertyNameSymbol(symbol, location) || symbol; + } + function getPropertySymbolOfDestructuringAssignment(location: Node) { return isArrayLiteralOrObjectLiteralDestructuringPattern(location.parent.parent) && typeChecker.getPropertySymbolOfDestructuringAssignment(location); @@ -6420,7 +6468,8 @@ namespace ts { if (referenceSymbol) { const referenceSymbolDeclaration = referenceSymbol.valueDeclaration; const shorthandValueSymbol = typeChecker.getShorthandAssignmentValueSymbol(referenceSymbolDeclaration); - const relatedSymbol = getRelatedSymbol(searchSymbols, referenceSymbol, referenceLocation); + const relatedSymbol = getRelatedSymbol(searchSymbols, referenceSymbol, referenceLocation, + /*searchLocationIsConstructor*/ searchLocation.kind === SyntaxKind.ConstructorKeyword); if (relatedSymbol) { const referencedSymbol = getReferencedSymbol(relatedSymbol); @@ -6436,12 +6485,94 @@ namespace ts { const referencedSymbol = getReferencedSymbol(shorthandValueSymbol); referencedSymbol.references.push(getReferenceEntryFromNode(referenceSymbolDeclaration.name)); } + else if (searchLocation.kind === SyntaxKind.ConstructorKeyword) { + findAdditionalConstructorReferences(referenceSymbol, referenceLocation); + } } }); } return; + /** Adds references when a constructor is used with `new this()` in its own class and `super()` calls in subclasses. */ + function findAdditionalConstructorReferences(referenceSymbol: Symbol, referenceLocation: Node): void { + Debug.assert(isClassLike(searchSymbol.valueDeclaration)); + + const referenceClass = referenceLocation.parent; + if (referenceSymbol === searchSymbol && isClassLike(referenceClass)) { + Debug.assert(referenceClass.name === referenceLocation); + // This is the class declaration containing the constructor. + addReferences(findOwnConstructorCalls(searchSymbol)); + } + else { + // If this class appears in `extends C`, then the extending class' "super" calls are references. + const classExtending = tryGetClassByExtendingIdentifier(referenceLocation); + if (classExtending && isClassLike(classExtending) && followAliasIfNecessary(referenceSymbol, referenceLocation) === searchSymbol) { + addReferences(superConstructorAccesses(classExtending)); + } + } + } + + function addReferences(references: Node[]): void { + if (references.length) { + const referencedSymbol = getReferencedSymbol(searchSymbol); + addRange(referencedSymbol.references, map(references, getReferenceEntryFromNode)); + } + } + + /** `classSymbol` is the class where the constructor was defined. + * Reference the constructor and all calls to `new this()`. + */ + function findOwnConstructorCalls(classSymbol: Symbol): Node[] { + const result: Node[] = []; + + for (const decl of classSymbol.members["__constructor"].declarations) { + Debug.assert(decl.kind === SyntaxKind.Constructor); + const ctrKeyword = decl.getChildAt(0); + Debug.assert(ctrKeyword.kind === SyntaxKind.ConstructorKeyword); + result.push(ctrKeyword); + } + + forEachProperty(classSymbol.exports, member => { + const decl = member.valueDeclaration; + if (decl && decl.kind === SyntaxKind.MethodDeclaration) { + const body = (decl).body; + if (body) { + forEachDescendantOfKind(body, SyntaxKind.ThisKeyword, thisKeyword => { + if (isNewExpressionTarget(thisKeyword)) { + result.push(thisKeyword); + } + }); + } + } + }); + + return result; + } + + /** Find references to `super` in the constructor of an extending class. */ + function superConstructorAccesses(cls: ClassLikeDeclaration): Node[] { + const symbol = cls.symbol; + const ctr = symbol.members["__constructor"]; + if (!ctr) { + return []; + } + + const result: Node[] = []; + for (const decl of ctr.declarations) { + Debug.assert(decl.kind === SyntaxKind.Constructor); + const body = (decl).body; + if (body) { + forEachDescendantOfKind(body, SyntaxKind.SuperKeyword, node => { + if (isCallExpressionTarget(node)) { + result.push(node); + } + }); + } + }; + return result; + } + function getReferencedSymbol(symbol: Symbol): ReferencedSymbol { const symbolId = getSymbolId(symbol); let index = symbolToIndex[symbolId]; @@ -6822,16 +6953,17 @@ namespace ts { } } - function getRelatedSymbol(searchSymbols: Symbol[], referenceSymbol: Symbol, referenceLocation: Node): Symbol { - if (searchSymbols.indexOf(referenceSymbol) >= 0) { - return referenceSymbol; + function getRelatedSymbol(searchSymbols: Symbol[], referenceSymbol: Symbol, referenceLocation: Node, searchLocationIsConstructor: boolean): Symbol | undefined { + if (contains(searchSymbols, referenceSymbol)) { + // If we are searching for constructor uses, they must be 'new' expressions. + return (!searchLocationIsConstructor || isNewExpressionTarget(referenceLocation)) && referenceSymbol; } // If the reference symbol is an alias, check if what it is aliasing is one of the search // symbols but by looking up for related symbol of this alias so it can handle multiple level of indirectness. const aliasSymbol = getAliasSymbolForPropertyNameSymbol(referenceSymbol, referenceLocation); if (aliasSymbol) { - return getRelatedSymbol(searchSymbols, aliasSymbol, referenceLocation); + return getRelatedSymbol(searchSymbols, aliasSymbol, referenceLocation, searchLocationIsConstructor); } // If the reference location is in an object literal, try to get the contextual type for the @@ -8355,6 +8487,15 @@ namespace ts { }; } + function forEachDescendantOfKind(node: Node, kind: SyntaxKind, action: (node: Node) => void) { + forEachChild(node, child => { + if (child.kind === kind) { + action(child); + } + forEachDescendantOfKind(child, kind, action); + }); + } + /* @internal */ export function getNameTable(sourceFile: SourceFile): Map { if (!sourceFile.nameTable) {