From aaf1d8055bef0d1128db52dc57ed7a6dac110c97 Mon Sep 17 00:00:00 2001 From: Andy Date: Fri, 9 Nov 2018 09:38:45 -0800 Subject: [PATCH] Support finding references at `module` in `module.exports =` or `export` in `export =` (#28221) * Support finding references at `module` in `module.exports =` or `export` in `export =` * Add json test --- src/compiler/checker.ts | 3 +++ src/services/findAllReferences.ts | 20 ++++++++++++++++ .../fourslash/findAllRefsExportEquals.ts | 17 ++++++------- .../findAllRefsImportEqualsJsonFile.ts | 24 +++++++++++++++++++ .../fourslash/findAllRefsModuleDotExports.ts | 11 +++++++++ .../findAllRefs_importType_exportEquals.ts | 13 +++++----- .../fourslash/findAllRefs_importType_js.ts | 7 +++--- tests/cases/fourslash/fourslash.ts | 6 ++++- 8 files changed, 82 insertions(+), 19 deletions(-) create mode 100644 tests/cases/fourslash/findAllRefsImportEqualsJsonFile.ts create mode 100644 tests/cases/fourslash/findAllRefsModuleDotExports.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 103200600b2..4f04c6f8e82 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -28100,6 +28100,9 @@ namespace ts { case SyntaxKind.ImportType: return isLiteralImportTypeNode(node) ? getSymbolAtLocation(node.argument.literal) : undefined; + case SyntaxKind.ExportKeyword: + return isExportAssignment(node.parent) ? Debug.assertDefined(node.parent.symbol) : undefined; + default: return undefined; } diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 30b69c7ce19..16e34565b7e 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -368,6 +368,10 @@ namespace ts.FindAllReferences.Core { return !options.implementations && isStringLiteral(node) ? getReferencesForStringLiteral(node, sourceFiles, cancellationToken) : undefined; } + if (symbol.escapedName === InternalSymbolName.ExportEquals) { + return getReferencedSymbolsForModule(program, symbol.parent!, /*excludeImportTypeOfExportEquals*/ false, sourceFiles, sourceFilesSet); + } + let moduleReferences: SymbolAndEntries[] = emptyArray; const moduleSourceFile = isModuleSymbol(symbol); let referencedNode: Node | undefined = node; @@ -427,6 +431,22 @@ namespace ts.FindAllReferences.Core { } } + const exported = symbol.exports!.get(InternalSymbolName.ExportEquals); + if (exported) { + for (const decl of exported.declarations) { + const sourceFile = decl.getSourceFile(); + if (sourceFilesSet.has(sourceFile.fileName)) { + // At `module.exports = ...`, reference node is `module` + const node = isBinaryExpression(decl) && isPropertyAccessExpression(decl.left) + ? decl.left.expression + : isExportAssignment(decl) + ? Debug.assertDefined(findChildOfKind(decl, SyntaxKind.ExportKeyword, sourceFile)) + : getNameOfDeclaration(decl) || decl; + references.push(nodeEntry(node)); + } + } + } + return references.length ? [{ definition: { type: DefinitionKind.Symbol, symbol }, references }] : emptyArray; } diff --git a/tests/cases/fourslash/findAllRefsExportEquals.ts b/tests/cases/fourslash/findAllRefsExportEquals.ts index 6bbdc613554..3580c8fe4ee 100644 --- a/tests/cases/fourslash/findAllRefsExportEquals.ts +++ b/tests/cases/fourslash/findAllRefsExportEquals.ts @@ -2,15 +2,16 @@ // @Filename: /a.ts ////type [|{| "isWriteAccess": true, "isDefinition": true |}T|] = number; -////export = [|T|]; +////[|export|] = [|T|]; // @Filename: /b.ts ////import [|{| "isWriteAccess": true, "isDefinition": true |}T|] = require("[|./a|]"); -const [r0, r1, r2, r3] = test.ranges(); -const mod = { definition: 'module "/a"', ranges: [r3] }; -const a = { definition: "type T = number", ranges: [r0, r1] }; -const b = { definition: '(alias) type T = number\nimport T = require("./a")', ranges: [r2] }; -verify.referenceGroups([r0, r1], [a, b]); -verify.referenceGroups(r2, [b, a]); -verify.referenceGroups(r3, [mod, a, b]); +const [r0, r1, r2, r3, r4] = test.ranges(); +const mod = { definition: 'module "/a"', ranges: [r4, r1] }; +const a = { definition: "type T = number", ranges: [r0, r2] }; +const b = { definition: '(alias) type T = number\nimport T = require("./a")', ranges: [r3] }; +verify.referenceGroups([r0, r2], [a, b]); +verify.referenceGroups(r3, [b, a]); +verify.referenceGroups(r4, [mod, a, b]); +verify.referenceGroups(r1, [mod]); diff --git a/tests/cases/fourslash/findAllRefsImportEqualsJsonFile.ts b/tests/cases/fourslash/findAllRefsImportEqualsJsonFile.ts new file mode 100644 index 00000000000..3c3c0dbb2e4 --- /dev/null +++ b/tests/cases/fourslash/findAllRefsImportEqualsJsonFile.ts @@ -0,0 +1,24 @@ +/// + +// @allowJs: true +// @checkJs: true +// @resolveJsonModule: true + +// @Filename: /a.ts +////import [|{| "isWriteAccess": true, "isDefinition": true |}j|] = require("[|./j.json|]"); +////[|j|]; + +// @Filename: /b.js +////const [|{| "isWriteAccess": true, "isDefinition": true |}j|] = require("[|./j.json|]"); +////[|j|]; + +// @Filename: /j.json +////[|{ "x": 0 }|] + +verify.noErrors(); + +const [r0, r1, r2, r3, r4, r5, r6] = test.ranges(); +verify.singleReferenceGroup('import j = require("./j.json")', [r0, r2]); +verify.referenceGroups([r1, r4], [{ definition: 'module "/j"', ranges: [r1, r4, r6] }]); +verify.singleReferenceGroup('const j: {\n "x": number;\n}', [r3, r5]); +verify.referenceGroups(r6, undefined); diff --git a/tests/cases/fourslash/findAllRefsModuleDotExports.ts b/tests/cases/fourslash/findAllRefsModuleDotExports.ts new file mode 100644 index 00000000000..bc1bd705c2f --- /dev/null +++ b/tests/cases/fourslash/findAllRefsModuleDotExports.ts @@ -0,0 +1,11 @@ +/// + +// @allowJs: true + +// @Filename: /a.js +////const b = require("[|./b|]"); + +// @Filename: /b.js +////[|module|].exports = 0; + +verify.singleReferenceGroup('module "/b"') diff --git a/tests/cases/fourslash/findAllRefs_importType_exportEquals.ts b/tests/cases/fourslash/findAllRefs_importType_exportEquals.ts index 7fe0696f952..496fa8a9d3b 100644 --- a/tests/cases/fourslash/findAllRefs_importType_exportEquals.ts +++ b/tests/cases/fourslash/findAllRefs_importType_exportEquals.ts @@ -5,7 +5,7 @@ ////namespace [|{| "isWriteAccess": true, "isDefinition": true |}T|] { //// export type U = string; ////} -////export = [|T|]; +////[|export|] = [|T|]; // @Filename: /b.ts ////const x: import("[|./[|a|]|]") = 0; @@ -13,14 +13,13 @@ verify.noErrors(); -const [r0, r1, r2, r3, r3b, r4, r4b] = test.ranges(); +const [r0, r1, rExport, r2, r3, r3b, r4, r4b] = test.ranges(); verify.referenceGroups(r0, [{ definition: "type T = number\nnamespace T", ranges: [r0, r2, r3] }]); verify.referenceGroups(r1, [{ definition: "namespace T", ranges: [r1, r2] }]); -verify.referenceGroups(r2, [{ definition: "type T = number\nnamespace T", ranges: [r0, r1, r2, r3] }]); -verify.referenceGroups([r3, r4], [ - { definition: 'module "/a"', ranges: [r4] }, - { definition: "type T = number\nnamespace T", ranges: [r0, r1, r2, r3] }, -]); +const t: FourSlashInterface.ReferenceGroup = { definition: "type T = number\nnamespace T", ranges: [r0, r1, r2, r3] }; +verify.referenceGroups(r2, [t]); +verify.referenceGroups([r3, r4], [{ definition: 'module "/a"', ranges: [r4, rExport] }, t]); +verify.referenceGroups(rExport, [{ definition: 'module "/a"', ranges: [r3, r4, rExport] }]); verify.renameLocations(r0, [r0, r2]); verify.renameLocations(r1, [r1, r2]); diff --git a/tests/cases/fourslash/findAllRefs_importType_js.ts b/tests/cases/fourslash/findAllRefs_importType_js.ts index 863ee05b12f..b1f612c8e45 100644 --- a/tests/cases/fourslash/findAllRefs_importType_js.ts +++ b/tests/cases/fourslash/findAllRefs_importType_js.ts @@ -4,7 +4,7 @@ // @checkJs: true // @Filename: /a.js -////module.exports = class [|{| "isWriteAccess": true, "isDefinition": true |}C|] {}; +////[|module|].exports = class [|{| "isWriteAccess": true, "isDefinition": true |}C|] {}; ////module.exports.[|{| "isWriteAccess": true, "isDefinition": true |}D|] = class [|{| "isWriteAccess": true, "isDefinition": true |}D|] {}; // @Filename: /b.js @@ -17,7 +17,8 @@ verify.noErrors(); // TODO: GH#24025 -const [r0, r1, r2, r3, r4, r5] = test.ranges(); +const [rModule, r0, r1, r2, r3, r4, r5] = test.ranges(); +verify.referenceGroups(rModule, [{ definition: 'module "/a"', ranges: [r3, r4, rModule] }]); verify.referenceGroups(r0, [ { definition: "(local class) C", ranges: [r0] }, // TODO: This definition is really ugly @@ -31,7 +32,7 @@ verify.referenceGroups(r2, [ { definition: "class D\n(property) D: typeof D", ranges: [r5] }, ]); verify.referenceGroups([r3, r4], [ - { definition: 'module "/a"', ranges: [r4] }, + { definition: 'module "/a"', ranges: [r4, rModule] }, { definition: "(local class) C", ranges: [r0] }, { definition: "(alias) (local class) export=\nimport export=", ranges: [r3] }, ]); diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index ddf55fb41b5..b6df330b85c 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -231,7 +231,7 @@ declare namespace FourSlashInterface { * For each of starts, asserts the ranges that are referenced from there. * This uses the 'findReferences' command instead of 'getReferencesAtPosition', so references are grouped by their definition. */ - referenceGroups(starts: ArrayOrSingle | ArrayOrSingle, parts: Array<{ definition: ReferencesDefinition, ranges: Range[] }>): void; + referenceGroups(starts: ArrayOrSingle | ArrayOrSingle, parts: ReadonlyArray): void; singleReferenceGroup(definition: ReferencesDefinition, ranges?: Range[]): void; rangesAreOccurrences(isWriteAccess?: boolean): void; rangesWithSameTextAreRenameLocations(): void; @@ -487,6 +487,10 @@ declare namespace FourSlashInterface { }; } + interface ReferenceGroup { + readonly definition: ReferencesDefinition; + readonly ranges: ReadonlyArray; + } type ReferencesDefinition = string | { text: string; range: Range;