This commit is contained in:
Andy Hanson 2017-03-27 13:53:43 -07:00
parent 2dd23fa0a8
commit 28a3604714
4 changed files with 95 additions and 25 deletions

View file

@ -935,8 +935,6 @@ namespace ts.FindAllReferences.Core {
addReference(referenceLocation, search.symbol, search.location, state);
}
const classSymbol = skipAliases(search.symbol, state.checker);
Debug.assert(isClassLike(classSymbol.valueDeclaration));
const pusher = state.referenceAdder(search.symbol, search.location);
if (isClassLike(referenceLocation.parent)) {
@ -1687,10 +1685,6 @@ namespace ts.FindAllReferences.Core {
return false;
}
function skipAliases(symbol: Symbol, checker: TypeChecker): Symbol {
return symbol.flags & SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
}
/**
* If we are just looking for implementations and this is a property access expression, we need to get the
* symbol of the local type of the symbol the property is being accessed on. This is because our search

View file

@ -32,7 +32,8 @@ namespace ts.FindAllReferences {
interface AmbientModuleDeclaration extends ModuleDeclaration { body?: ModuleBlock; }
type SourceFileLike = SourceFile | AmbientModuleDeclaration;
type Importer = AnyImportSyntax | ExportDeclaration;
// Identifier for the case of `const x = require("y")`.
type Importer = AnyImportSyntax | Identifier | ExportDeclaration;
type ImporterOrCallExpression = Importer | CallExpression;
/** Returns import statements that directly reference the exporting module, and a list of files that may access the module through a namespace. */
@ -55,7 +56,7 @@ namespace ts.FindAllReferences {
// Module augmentations may use this module's exports without importing it.
for (const decl of exportingModuleSymbol.declarations) {
if (ts.isExternalModuleAugmentation(decl)) {
if (isExternalModuleAugmentation(decl)) {
addIndirectUser(decl as SourceFileLike);
}
}
@ -74,6 +75,15 @@ namespace ts.FindAllReferences {
switch (direct.kind) {
case SyntaxKind.CallExpression:
if (!isAvailableThroughGlobal) {
const parent = direct.parent!;
if (exportKind === ExportKind.ExportEquals && parent.kind === SyntaxKind.VariableDeclaration) {
const { name } = parent as ts.VariableDeclaration;
if (name.kind === SyntaxKind.Identifier) {
directImports.push(name);
break;
}
}
// Don't support re-exporting 'require()' calls, so just add a single indirect user.
addIndirectUser(direct.getSourceFile());
}
@ -179,6 +189,11 @@ namespace ts.FindAllReferences {
return;
}
if (decl.kind === ts.SyntaxKind.Identifier) {
handleNamespaceImportLike(decl);
return;
}
// Ignore if there's a grammar error
if (decl.moduleSpecifier.kind !== SyntaxKind.StringLiteral) {
return;
@ -192,7 +207,7 @@ namespace ts.FindAllReferences {
const { importClause } = decl;
const { namedBindings } = importClause;
if (namedBindings && namedBindings.kind === ts.SyntaxKind.NamespaceImport) {
if (namedBindings && namedBindings.kind === SyntaxKind.NamespaceImport) {
handleNamespaceImportLike(namedBindings.name);
return;
}
@ -333,13 +348,13 @@ namespace ts.FindAllReferences {
if (sourceFile.flags & NodeFlags.JavaScriptFile) {
// Find all 'require()' calls.
recur(sourceFile);
function recur(node: Node): void {
sourceFile.forEachChild(function recur(node: Node): void {
if (isRequireCall(node, /*checkArgumentIsStringLiteral*/true)) {
action(node, node.arguments[0] as StringLiteral);
} else {
node.forEachChild(recur);
}
forEachChild(node, recur);
}
});
}
}
}
@ -379,18 +394,9 @@ namespace ts.FindAllReferences {
if (parent.kind === SyntaxKind.PropertyAccessExpression) {
// When accessing an export of a JS module, there's no alias. The symbol will still be flagged as an export even though we're at the use.
// So check that we are at the declaration.
if (!symbol.declarations.some(d => d === parent)) {
return undefined;
}
switch (getSpecialPropertyAssignmentKind(parent.parent)) {
case SpecialPropertyAssignmentKind.ExportsProperty:
return exportInfo(symbol, ExportKind.Named);
case SpecialPropertyAssignmentKind.ModuleExports:
return exportInfo(symbol, ExportKind.ExportEquals);
default:
return undefined;
}
return symbol.declarations.some(d => d === parent) && parent.parent.kind === ts.SyntaxKind.BinaryExpression
? getSpecialPropertyExport(parent.parent as ts.BinaryExpression, /*useLhsSymbol*/ false)
: undefined;
}
else {
const { exportSymbol } = symbol;
@ -420,6 +426,29 @@ namespace ts.FindAllReferences {
Debug.assert(!!exportingModuleSymbol);
return { kind: ImportExport.Export, symbol, exportInfo: { exportingModuleSymbol, exportKind: ExportKind.ExportEquals } };
}
else if (parent.kind === ts.SyntaxKind.BinaryExpression) {
return getSpecialPropertyExport(parent as ts.BinaryExpression, /*useLhsSymbol*/ true);
}
else if (parent.parent.kind === SyntaxKind.BinaryExpression) {
return getSpecialPropertyExport(parent.parent as ts.BinaryExpression, /*useLhsSymbol*/ true);
}
}
function getSpecialPropertyExport(node: ts.BinaryExpression, useLhsSymbol: boolean): ExportedSymbol | undefined {
let kind: ExportKind;
switch (getSpecialPropertyAssignmentKind(node)) {
case SpecialPropertyAssignmentKind.ExportsProperty:
kind = ExportKind.Named;
break;
case SpecialPropertyAssignmentKind.ModuleExports:
kind = ExportKind.ExportEquals;
break;
default:
return undefined;
}
const sym = useLhsSymbol ? checker.getSymbolAtLocation((node.left as ts.PropertyAccessExpression).name) : symbol;
return sym && exportInfo(sym, kind);
}
}

View file

@ -0,0 +1,16 @@
/// <reference path='fourslash.ts'/>
// @allowJs: true
// @Filename: a.js
////module.exports = class [|{| "isWriteAccess": true, "isDefinition": true |}A|] {}
// @Filename: b.js
////const [|{| "isWriteAccess": true, "isDefinition": true |}A|] = require("./a");
const [r0, r1] = test.ranges();
verify.referenceGroups(r0, [
{ definition: "(local class) A", ranges: [r0] },
{ definition: "const A: typeof A", ranges: [r1] }
]);
verify.singleReferenceGroup("const A: typeof A", [r1]);

View file

@ -0,0 +1,31 @@
/// <reference path='fourslash.ts'/>
// @allowJs: true
// @Filename: a.js
////class [|{| "isWriteAccess": true, "isDefinition": true |}A|] {
//// [|constructor|]() { }
////}
////module.exports = [|A|];
// @Filename: b.js
////const [|{| "isWriteAccess": true, "isDefinition": true |}A|] = require("./a");
////new [|A|];
const [r0, r1, r2, r3, r4] = test.ranges();
verify.referenceGroups([r0, r2], [
{ definition: "class A", ranges: [r0, r2] },
{ definition: "const A: typeof A", ranges: [r3, r4] }
]);
verify.referenceGroups(r1, [
{ definition: "constructor A(): A", ranges: [r1] },
{ definition: "const A: typeof A", ranges: [r4] }
]);
verify.referenceGroups(r3, [
{ definition: "const A: typeof A", ranges: [r3, r4] }
]);
verify.referenceGroups(r4, [
{ definition: "const A: new () => A", ranges: [r3, r4] }
]);