Fix import tracker for dynamic import (#41473)

* chore: migrate findAllRefs_importType_js to baseline

* fix: reference for dynamic import

* fix: find all reference for typeof import()

* fix: test

* refactor: addIndirectUser

* refactor: isExported

* refactor: isExported

* resolve review
This commit is contained in:
Jack Works 2020-12-03 06:46:20 +08:00 committed by GitHub
parent 9f9eed400c
commit 0b6c9254a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 788 additions and 46 deletions

View File

@ -84,6 +84,10 @@ namespace ts.FindAllReferences {
switch (direct.kind) {
case SyntaxKind.CallExpression:
if (isImportCall(direct)) {
handleImportCall(direct);
break;
}
if (!isAvailableThroughGlobal) {
const parent = direct.parent;
if (exportKind === ExportKind.ExportEquals && parent.kind === SyntaxKind.VariableDeclaration) {
@ -121,7 +125,7 @@ namespace ts.FindAllReferences {
}
else if (direct.exportClause.kind === SyntaxKind.NamespaceExport) {
// `export * as foo from "foo"` add to indirect uses
addIndirectUsers(getSourceFileLikeForImportDeclaration(direct));
addIndirectUser(getSourceFileLikeForImportDeclaration(direct), /** addTransitiveDependencies */ true);
}
else {
// This is `export { foo } from "foo"` and creates an alias symbol, so recursive search will get handle re-exports.
@ -130,6 +134,10 @@ namespace ts.FindAllReferences {
break;
case SyntaxKind.ImportType:
// Only check for typeof import('xyz')
if (direct.isTypeOf && !direct.qualifier && isExported(direct)) {
addIndirectUser(direct.getSourceFile(), /** addTransitiveDependencies */ true);
}
directImports.push(direct);
break;
@ -140,6 +148,18 @@ namespace ts.FindAllReferences {
}
}
function handleImportCall(importCall: ImportCall) {
const top = findAncestor(importCall, isAmbientModuleDeclaration) || importCall.getSourceFile();
addIndirectUser(top, /** addTransitiveDependencies */ !!isExported(importCall, /** stopAtAmbientModule */ true));
}
function isExported(node: Node, stopAtAmbientModule = false) {
return findAncestor(node, node => {
if (stopAtAmbientModule && isAmbientModuleDeclaration(node)) return "quit";
return some(node.modifiers, mod => mod.kind === SyntaxKind.ExportKeyword);
});
}
function handleNamespaceImport(importDeclaration: ImportEqualsDeclaration | ImportDeclaration, name: Identifier, isReExport: boolean, alreadyAddedDirect: boolean): void {
if (exportKind === ExportKind.ExportEquals) {
// This is a direct import, not import-as-namespace.
@ -149,7 +169,7 @@ namespace ts.FindAllReferences {
const sourceFileLike = getSourceFileLikeForImportDeclaration(importDeclaration);
Debug.assert(sourceFileLike.kind === SyntaxKind.SourceFile || sourceFileLike.kind === SyntaxKind.ModuleDeclaration);
if (isReExport || findNamespaceReExports(sourceFileLike, name, checker)) {
addIndirectUsers(sourceFileLike);
addIndirectUser(sourceFileLike, /** addTransitiveDependencies */ true);
}
else {
addIndirectUser(sourceFileLike);
@ -157,28 +177,22 @@ namespace ts.FindAllReferences {
}
}
function addIndirectUser(sourceFileLike: SourceFileLike): boolean {
/** Adds a module and all of its transitive dependencies as possible indirect users. */
function addIndirectUser(sourceFileLike: SourceFileLike, addTransitiveDependencies = false): void {
Debug.assert(!isAvailableThroughGlobal);
const isNew = markSeenIndirectUser(sourceFileLike);
if (isNew) {
indirectUserDeclarations!.push(sourceFileLike); // TODO: GH#18217
}
return isNew;
}
/** Adds a module and all of its transitive dependencies as possible indirect users. */
function addIndirectUsers(sourceFileLike: SourceFileLike): void {
if (!addIndirectUser(sourceFileLike)) {
return;
}
if (!isNew) return;
indirectUserDeclarations!.push(sourceFileLike); // TODO: GH#18217
if (!addTransitiveDependencies) return;
const moduleSymbol = checker.getMergedSymbol(sourceFileLike.symbol);
if (!moduleSymbol) return;
Debug.assert(!!(moduleSymbol.flags & SymbolFlags.Module));
const directImports = getDirectImports(moduleSymbol);
if (directImports) {
for (const directImport of directImports) {
if (!isImportTypeNode(directImport)) {
addIndirectUsers(getSourceFileLikeForImportDeclaration(directImport));
addIndirectUser(getSourceFileLikeForImportDeclaration(directImport), /** addTransitiveDependencies */ true);
}
}
}

View File

@ -0,0 +1,98 @@
// === /app.ts ===
// /*FIND ALL REFS*/export function [|hello|]() {};
// === /indirect-use.ts ===
// import("./re-export").then(mod => mod.services.app.[|hello|]());
// === /direct-use.ts ===
// async function main() {
// const mod = await import("./app")
// mod.[|hello|]();
// }
[
{
"definition": {
"containerKind": "",
"containerName": "",
"fileName": "/app.ts",
"kind": "function",
"name": "function hello(): void",
"textSpan": {
"start": 16,
"length": 5
},
"displayParts": [
{
"text": "function",
"kind": "keyword"
},
{
"text": " ",
"kind": "space"
},
{
"text": "hello",
"kind": "functionName"
},
{
"text": "(",
"kind": "punctuation"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": ":",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "void",
"kind": "keyword"
}
],
"contextSpan": {
"start": 0,
"length": 26
}
},
"references": [
{
"textSpan": {
"start": 16,
"length": 5
},
"fileName": "/app.ts",
"contextSpan": {
"start": 0,
"length": 26
},
"isWriteAccess": true,
"isDefinition": true
},
{
"textSpan": {
"start": 51,
"length": 5
},
"fileName": "/indirect-use.ts",
"isWriteAccess": false,
"isDefinition": false
},
{
"textSpan": {
"start": 70,
"length": 5
},
"fileName": "/direct-use.ts",
"isWriteAccess": false,
"isDefinition": false
}
]
}
]

View File

@ -0,0 +1,85 @@
// === /app.ts ===
// /*FIND ALL REFS*/export function [|hello|]() {};
// === /indirect-use.ts ===
// import type { app } from "./re-export";
// declare const app: app
// app.[|hello|]();
[
{
"definition": {
"containerKind": "",
"containerName": "",
"fileName": "/app.ts",
"kind": "function",
"name": "function hello(): void",
"textSpan": {
"start": 16,
"length": 5
},
"displayParts": [
{
"text": "function",
"kind": "keyword"
},
{
"text": " ",
"kind": "space"
},
{
"text": "hello",
"kind": "functionName"
},
{
"text": "(",
"kind": "punctuation"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": ":",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "void",
"kind": "keyword"
}
],
"contextSpan": {
"start": 0,
"length": 26
}
},
"references": [
{
"textSpan": {
"start": 16,
"length": 5
},
"fileName": "/app.ts",
"contextSpan": {
"start": 0,
"length": 26
},
"isWriteAccess": true,
"isDefinition": true
},
{
"textSpan": {
"start": 67,
"length": 5
},
"fileName": "/indirect-use.ts",
"isWriteAccess": false,
"isDefinition": false
}
]
}
]

View File

@ -0,0 +1,152 @@
// === /b.js ===
// /** @type {import("[|./a|]")} */
// const x = 0;
// /** @type {import("./a").D} */
// const y = 0;
// === /a.js ===
// /*FIND ALL REFS*/module.exports = class [|C|] {};
// module.exports.D = class D {};
[
{
"definition": {
"containerKind": "",
"containerName": "",
"fileName": "/a.js",
"kind": "local class",
"name": "(local class) C",
"textSpan": {
"start": 23,
"length": 1
},
"displayParts": [
{
"text": "(",
"kind": "punctuation"
},
{
"text": "local class",
"kind": "text"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "C",
"kind": "className"
}
],
"contextSpan": {
"start": 17,
"length": 10
}
},
"references": [
{
"textSpan": {
"start": 23,
"length": 1
},
"fileName": "/a.js",
"contextSpan": {
"start": 17,
"length": 10
},
"isWriteAccess": true,
"isDefinition": true
}
]
},
{
"definition": {
"containerKind": "",
"containerName": "",
"fileName": "/a.js",
"kind": "alias",
"name": "(alias) (local class) export=\nimport export=",
"textSpan": {
"start": 0,
"length": 27
},
"displayParts": [
{
"text": "(",
"kind": "punctuation"
},
{
"text": "alias",
"kind": "text"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "(",
"kind": "punctuation"
},
{
"text": "local class",
"kind": "text"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "export=",
"kind": "aliasName"
},
{
"text": "\n",
"kind": "lineBreak"
},
{
"text": "import",
"kind": "keyword"
},
{
"text": " ",
"kind": "space"
},
{
"text": "export=",
"kind": "aliasName"
}
],
"contextSpan": {
"start": 0,
"length": 28
}
},
"references": [
{
"textSpan": {
"start": 19,
"length": 3
},
"fileName": "/b.js",
"contextSpan": {
"start": 4,
"length": 21
},
"isWriteAccess": false,
"isDefinition": false
}
]
}
]

View File

@ -0,0 +1,107 @@
// === /a.js ===
// /*FIND ALL REFS*/module.exports = class C {};
// module.exports.[|D|] = class D {};
// === /b.js ===
// /** @type {import("./a")} */
// const x = 0;
// /** @type {import("./a").[|D|]} */
// const y = 0;
[
{
"definition": {
"containerKind": "",
"containerName": "",
"fileName": "/a.js",
"kind": "alias",
"name": "(alias) (local class) D\nimport D",
"textSpan": {
"start": 44,
"length": 1
},
"displayParts": [
{
"text": "(",
"kind": "punctuation"
},
{
"text": "alias",
"kind": "text"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "(",
"kind": "punctuation"
},
{
"text": "local class",
"kind": "text"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "D",
"kind": "aliasName"
},
{
"text": "\n",
"kind": "lineBreak"
},
{
"text": "import",
"kind": "keyword"
},
{
"text": " ",
"kind": "space"
},
{
"text": "D",
"kind": "aliasName"
}
],
"contextSpan": {
"start": 29,
"length": 16
}
},
"references": [
{
"textSpan": {
"start": 44,
"length": 1
},
"fileName": "/a.js",
"contextSpan": {
"start": 29,
"length": 30
},
"isWriteAccess": true,
"isDefinition": true
},
{
"textSpan": {
"start": 67,
"length": 1
},
"fileName": "/b.js",
"isWriteAccess": false,
"isDefinition": false
}
]
}
]

View File

@ -0,0 +1,148 @@
// === /a.js ===
// /*FIND ALL REFS*/module.exports = class C {};
// module.exports.D = class [|D|] {};
// === /b.js ===
// /** @type {import("./a")} */
// const x = 0;
// /** @type {import("./a").[|D|]} */
// const y = 0;
[
{
"definition": {
"containerKind": "",
"containerName": "",
"fileName": "/a.js",
"kind": "local class",
"name": "(local class) D",
"textSpan": {
"start": 54,
"length": 1
},
"displayParts": [
{
"text": "(",
"kind": "punctuation"
},
{
"text": "local class",
"kind": "text"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "D",
"kind": "className"
}
],
"contextSpan": {
"start": 48,
"length": 10
}
},
"references": [
{
"textSpan": {
"start": 54,
"length": 1
},
"fileName": "/a.js",
"contextSpan": {
"start": 48,
"length": 10
},
"isWriteAccess": true,
"isDefinition": true
}
]
},
{
"definition": {
"containerKind": "",
"containerName": "",
"fileName": "/a.js",
"kind": "alias",
"name": "(alias) (local class) D\nimport D",
"textSpan": {
"start": 44,
"length": 1
},
"displayParts": [
{
"text": "(",
"kind": "punctuation"
},
{
"text": "alias",
"kind": "text"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "(",
"kind": "punctuation"
},
{
"text": "local class",
"kind": "text"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "D",
"kind": "aliasName"
},
{
"text": "\n",
"kind": "lineBreak"
},
{
"text": "import",
"kind": "keyword"
},
{
"text": " ",
"kind": "space"
},
{
"text": "D",
"kind": "aliasName"
}
],
"contextSpan": {
"start": 29,
"length": 16
}
},
"references": [
{
"textSpan": {
"start": 67,
"length": 1
},
"fileName": "/b.js",
"isWriteAccess": false,
"isDefinition": false
}
]
}
]

View File

@ -0,0 +1,80 @@
// === /a.js ===
// /*FIND ALL REFS*/[|module|].exports = class C {};
// module.exports.D = class D {};
// === /b.js ===
// /** @type {import("[|./a|]")} */
// const x = 0;
// /** @type {import("[|./a|]").D} */
// const y = 0;
[
{
"definition": {
"containerKind": "",
"containerName": "",
"fileName": "/a.js",
"kind": "module",
"name": "module \"/a\"",
"textSpan": {
"start": 0,
"length": 59
},
"displayParts": [
{
"text": "module",
"kind": "keyword"
},
{
"text": " ",
"kind": "space"
},
{
"text": "\"/a\"",
"kind": "stringLiteral"
}
]
},
"references": [
{
"textSpan": {
"start": 19,
"length": 3
},
"fileName": "/b.js",
"contextSpan": {
"start": 4,
"length": 21
},
"isWriteAccess": false,
"isDefinition": false
},
{
"textSpan": {
"start": 61,
"length": 3
},
"fileName": "/b.js",
"contextSpan": {
"start": 46,
"length": 23
},
"isWriteAccess": false,
"isDefinition": false
},
{
"textSpan": {
"start": 0,
"length": 6
},
"fileName": "/a.js",
"contextSpan": {
"start": 0,
"length": 28
},
"isWriteAccess": false,
"isDefinition": false
}
]
}
]

View File

@ -0,0 +1,19 @@
/// <reference path="fourslash.ts" />
// @Filename: /app.ts
//// export function he/**/llo() {};
// @Filename: /re-export.ts
//// export const services = { app: setup(() => import('./app')) }
//// function setup<T>(importee: () => Promise<T>): T { return {} as any }
// @Filename: /indirect-use.ts
//// import("./re-export").then(mod => mod.services.app.hello());
// @Filename: /direct-use.ts
//// async function main() {
//// const mod = await import("./app")
//// mod.hello();
//// }
verify.baselineFindAllReferences("");

View File

@ -0,0 +1,14 @@
/// <reference path="fourslash.ts" />
// @Filename: /app.ts
//// export function he/**/llo() {};
// @Filename: /re-export.ts
//// export type app = typeof import("./app")
// @Filename: /indirect-use.ts
//// import type { app } from "./re-export";
//// declare const app: app
//// app.hello();
verify.baselineFindAllReferences("");

View File

@ -0,0 +1,17 @@
/// <reference path='fourslash.ts' />
// @allowJs: true
// @checkJs: true
// @Filename: /a.js
////module.exports = class /**/C {};
////module.exports.D = class D {};
// @Filename: /b.js
/////** @type {import("./a")} */
////const x = 0;
/////** @type {import("./a").D} */
////const y = 0;
verify.noErrors();
verify.baselineFindAllReferences("");

View File

@ -0,0 +1,17 @@
/// <reference path='fourslash.ts' />
// @allowJs: true
// @checkJs: true
// @Filename: /a.js
////module.exports = class C {};
////module.exports./**/D = class D {};
// @Filename: /b.js
/////** @type {import("./a")} */
////const x = 0;
/////** @type {import("./a").D} */
////const y = 0;
verify.noErrors();
verify.baselineFindAllReferences("");

View File

@ -0,0 +1,17 @@
/// <reference path='fourslash.ts' />
// @allowJs: true
// @checkJs: true
// @Filename: /a.js
////module.exports = class C {};
////module.exports.D = class /**/D {};
// @Filename: /b.js
/////** @type {import("./a")} */
////const x = 0;
/////** @type {import("./a").D} */
////const y = 0;
verify.noErrors();
verify.baselineFindAllReferences("");

View File

@ -4,40 +4,14 @@
// @checkJs: true
// @Filename: /a.js
////[|[|{| "contextRangeIndex": 0 |}module|].exports = [|class [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeIndex": 2 |}C|] {}|];|]
////[|module.exports.[|{| "isWriteAccess": true, "isDefinition": true, "contextRangeIndex": 4 |}D|] = [|class [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeIndex": 6 |}D|] {}|];|]
/////**/module.exports = class C {};
////module.exports.D = class D {};
// @Filename: /b.js
/////** [|@type {import("[|{| "contextRangeIndex": 8 |}./a|]")}|] */
/////** @type {import("./a")} */
////const x = 0;
/////** [|@type {import("[|{| "contextRangeIndex": 10 |}./a|]").[|D|]}|] */
/////** @type {import("./a").D} */
////const y = 0;
verify.noErrors();
// TODO: GH#24025
const [rModuleDef, rModule, r0Def, r0, r1Def, r1, r2Def, r2, r3Def, r3, r4Def, r4, r5] = test.ranges();
verify.referenceGroups([r3, r4], [
{ definition: 'module "/a"', ranges: [r4, rModule] },
{ definition: "(local class) C", ranges: [r0] },
{ definition: "(alias) (local class) export=\nimport export=", ranges: [r3] },
]);
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
{ definition: "(alias) (local class) export=\nimport export=", ranges: [r3] },
]);
verify.referenceGroups([r1, r5], [
{ definition: "(alias) (local class) D\nimport D", ranges: [r1, r5] },
]);
verify.referenceGroups(r2, [
{ definition: "(local class) D", ranges: [r2] },
{ definition: "(alias) (local class) D\nimport D", ranges: [r5] },
]);
verify.referenceGroups([r3, r4], [
{ definition: 'module "/a"', ranges: [r4, rModule] },
//{ definition: "(local class) C", ranges: [r0] },
//{ definition: "(alias) (local class) export=\nimport export=", ranges: [r3] },
]);
verify.baselineFindAllReferences("");