diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 2be21476fab..8054ea666f3 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -559,7 +559,8 @@ namespace ts.FindAllReferences.Core { const addRef = state.referenceAdder(exportSymbol); for (const singleRef of singleReferences) { // At `default` in `import { default as x }` or `export { default as x }`, do add a reference, but do not rename. - if (!(state.options.isForRename && (isExportSpecifier(singleRef.parent) || isImportSpecifier(singleRef.parent)) && singleRef.escapedText === InternalSymbolName.Default)) { + if (hasMatchingMeaning(singleRef, state) && + !(state.options.isForRename && (isExportSpecifier(singleRef.parent) || isImportSpecifier(singleRef.parent)) && singleRef.escapedText === InternalSymbolName.Default)) { addRef(singleRef); } } @@ -822,6 +823,10 @@ namespace ts.FindAllReferences.Core { } } + function hasMatchingMeaning(referenceLocation: Node, state: State): boolean { + return !!(getMeaningFromLocation(referenceLocation) & state.searchMeaning); + } + function getReferencesAtLocation(sourceFile: SourceFile, position: number, search: Search, state: State, addReferencesHere: boolean): void { const referenceLocation = getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true); @@ -840,9 +845,7 @@ namespace ts.FindAllReferences.Core { return; } - if (!(getMeaningFromLocation(referenceLocation) & state.searchMeaning)) { - return; - } + if (!hasMatchingMeaning(referenceLocation, state)) return; const referenceSymbol = state.checker.getSymbolAtLocation(referenceLocation); if (!referenceSymbol) { diff --git a/src/services/importTracker.ts b/src/services/importTracker.ts index 1a4b2a9f751..4858fdc4b34 100644 --- a/src/services/importTracker.ts +++ b/src/services/importTracker.ts @@ -99,6 +99,9 @@ namespace ts.FindAllReferences { } break; + case SyntaxKind.Identifier: // for 'const x = require("y"); + break; // TODO: GH#23879 + case SyntaxKind.ImportEqualsDeclaration: handleNamespaceImport(direct, direct.name, hasModifier(direct, ModifierFlags.Export)); break; @@ -130,6 +133,19 @@ namespace ts.FindAllReferences { directImports.push(direct); } break; + + case SyntaxKind.ImportType: + if (direct.qualifier) { + // `import("foo").x` named import + directImports.push(direct); + } + else { + // TODO: GH#23879 + } + break; + + default: + Debug.assertNever(direct, `Unexpected import kind: ${Debug.showSyntaxKind(direct)}`); } } } @@ -216,6 +232,9 @@ namespace ts.FindAllReferences { } if (decl.kind === SyntaxKind.ImportType) { + if (decl.qualifier) { // TODO: GH#23879 + singleReferences.push(decl.qualifier.kind === SyntaxKind.Identifier ? decl.qualifier : decl.qualifier.right); + } return; } @@ -313,16 +332,10 @@ namespace ts.FindAllReferences { const namespaceImportSymbol = checker.getSymbolAtLocation(name); return forEachPossibleImportOrExportStatement(sourceFileLike, statement => { - if (statement.kind !== SyntaxKind.ExportDeclaration) return; - - const { exportClause, moduleSpecifier } = statement as ExportDeclaration; - if (moduleSpecifier || !exportClause) return; - - for (const element of exportClause.elements) { - if (checker.getExportSpecifierLocalTargetSymbol(element) === namespaceImportSymbol) { - return true; - } - } + if (!isExportDeclaration(statement)) return; + const { exportClause, moduleSpecifier } = statement; + return !moduleSpecifier && exportClause && + exportClause.elements.some(element => checker.getExportSpecifierLocalTargetSymbol(element) === namespaceImportSymbol); }); } @@ -485,6 +498,9 @@ namespace ts.FindAllReferences { else if (isBinaryExpression(parent.parent)) { return getSpecialPropertyExport(parent.parent, /*useLhsSymbol*/ true); } + else if (isJSDocTypedefTag(parent)) { + return exportInfo(symbol, ExportKind.Named); + } } function getExportAssignmentExport(ex: ExportAssignment): ExportedSymbol { diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 0c14599d8bc..453abdea9ee 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -180,6 +180,8 @@ namespace ts { switch (node.parent.kind) { case SyntaxKind.TypeReference: return true; + case SyntaxKind.ImportType: + return !(node.parent as ImportTypeNode).isTypeOf; case SyntaxKind.ExpressionWithTypeArguments: return !isExpressionWithTypeArgumentsInClassExtendsClause(node.parent); } diff --git a/tests/cases/fourslash/findAllRefsImportType.ts b/tests/cases/fourslash/findAllRefsImportType.ts new file mode 100644 index 00000000000..dda48580b66 --- /dev/null +++ b/tests/cases/fourslash/findAllRefsImportType.ts @@ -0,0 +1,12 @@ +/// + +// @allowJs: true + +// @Filename: /a.js +////module.exports = 0; +////export type [|{| "isWriteAccess": true, "isDefinition": true |}N|] = number; + +// @Filename: /b.js +////type T = import("./a").[|N|]; + +verify.rangesReferenceEachOther(); diff --git a/tests/cases/fourslash/findAllRefsImportTypeOfModule.ts b/tests/cases/fourslash/findAllRefsImportTypeOfModule.ts new file mode 100644 index 00000000000..4c672c69548 --- /dev/null +++ b/tests/cases/fourslash/findAllRefsImportTypeOfModule.ts @@ -0,0 +1,11 @@ +/// + +////type [|{| "isWriteAccess": true, "isDefinition": true |}T|] = number; +////export = [|T|]; + +////const x: [|import("./b")|] = 0; + +// TODO: GH#23879 should just verify.rangesReferenceEachOther(); +const [r0, r1, r2] = test.ranges(); +verify.referenceGroups([r0, r1], [{ definition: "type T = number", ranges: [r0, r1] }]); +verify.referenceGroups(r2, undefined); diff --git a/tests/cases/fourslash/findAllRefsTypedef_importType.ts b/tests/cases/fourslash/findAllRefsTypedef_importType.ts new file mode 100644 index 00000000000..a9104fb398e --- /dev/null +++ b/tests/cases/fourslash/findAllRefsTypedef_importType.ts @@ -0,0 +1,14 @@ +/// + +// @allowJs: true + +// @Filename: /a.js +////module.exports = 0; +/////** @typedef {number} [|{| "isWriteAccess": true, "isDefinition": true |}Foo|] */ +////const dummy = 0; + +// @Filename: /b.js +/////** @type {import('./a').[|Foo|]} */ +////const x = 0; + +verify.rangesReferenceEachOther(); diff --git a/tests/cases/fourslash/findAllRefs_importType_meaningAtLocation.ts b/tests/cases/fourslash/findAllRefs_importType_meaningAtLocation.ts new file mode 100644 index 00000000000..21cc1afebb0 --- /dev/null +++ b/tests/cases/fourslash/findAllRefs_importType_meaningAtLocation.ts @@ -0,0 +1,13 @@ +/// + +// @Filename: /a.ts +////export type [|{| "isWriteAccess": true, "isDefinition": true |}T|] = 0; +////export const [|{| "isWriteAccess": true, "isDefinition": true |}T|] = 0; + +// @Filename: /b.ts +////const x: import("./a").[|T|] = 0; +////const x: typeof import("./a").[|T|] = 0; + +const [r0, r1, r2, r3] = test.ranges(); +verify.rangesReferenceEachOther([r0, r2]); +verify.rangesReferenceEachOther([r1, r3]);