diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index 8129fd373e441..5e7ba4cd844a1 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -462,7 +462,7 @@ namespace FourSlashInterface { this.state.verifyRangesWithSameTextAreRenameLocations(...texts); } - public rangesAreRenameLocations(options?: FourSlash.Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges?: FourSlash.Range[] }) { + public rangesAreRenameLocations(options?: FourSlash.Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges?: FourSlash.Range[], providePrefixAndSuffixTextForRename?: boolean }) { this.state.verifyRangesAreRenameLocations(options); } @@ -1602,4 +1602,4 @@ namespace FourSlashInterface { readonly providePrefixAndSuffixTextForRename?: boolean; }; export type RenameLocationOptions = FourSlash.Range | { readonly range: FourSlash.Range, readonly prefixText?: string, readonly suffixText?: string }; -} \ No newline at end of file +} diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index a0260eac2a54f..d2edb6335aa0d 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -380,7 +380,10 @@ namespace ts.FindAllReferences { return contains(originalSymbol!.declarations, entry.node.parent) ? { prefixText: name + " as " } : emptyOptions; } else if (isExportSpecifier(entry.node.parent) && !entry.node.parent.propertyName) { - return originalNode === entry.node ? { prefixText: name + " as " } : { suffixText: " as " + name }; + // If the symbol for the node is same as declared node symbol use prefix text + return originalNode === entry.node || checker.getSymbolAtLocation(originalNode) === checker.getSymbolAtLocation(entry.node) ? + { prefixText: name + " as " } : + { suffixText: " as " + name }; } } @@ -758,7 +761,6 @@ namespace ts.FindAllReferences { // Compute the meaning from the location and the symbol it references const searchMeaning = node ? getIntersectingMeaningFromDeclarations(node, symbol) : SemanticMeaning.All; - const result: SymbolAndEntries[] = []; const state = new State(sourceFiles, sourceFilesSet, node ? getSpecialSearchKind(node) : SpecialSearchKind.None, checker, cancellationToken, searchMeaning, options, result); @@ -1894,6 +1896,13 @@ namespace ts.FindAllReferences { return fromRoot(symbol.flags & SymbolFlags.FunctionScopedVariable ? paramProps[1] : paramProps[0]); } + const exportSpecifier = getDeclarationOfKind(symbol, SyntaxKind.ExportSpecifier); + const localSymbol = exportSpecifier && checker.getExportSpecifierLocalTargetSymbol(exportSpecifier); + if (localSymbol) { + const res = cbSymbol(localSymbol, /*rootSymbol*/ undefined, /*baseSymbol*/ undefined, EntryKind.Node); + if (res) return res; + } + // symbolAtLocation for a binding element is the local symbol. See if the search symbol is the property. // Don't do this when populating search set for a rename when prefix and suffix text will be provided -- just rename the local. if (!isForRenamePopulateSearchSymbolSet) { diff --git a/src/services/importTracker.ts b/src/services/importTracker.ts index 85d2027603c7f..24273cf815a65 100644 --- a/src/services/importTracker.ts +++ b/src/services/importTracker.ts @@ -176,7 +176,9 @@ namespace ts.FindAllReferences { const directImports = getDirectImports(moduleSymbol); if (directImports) { for (const directImport of directImports) { - addIndirectUsers(getSourceFileLikeForImportDeclaration(directImport)); + if (!isImportTypeNode(directImport)) { + addIndirectUsers(getSourceFileLikeForImportDeclaration(directImport)); + } } } } @@ -221,8 +223,9 @@ namespace ts.FindAllReferences { if (decl.kind === SyntaxKind.ImportType) { if (decl.qualifier) { - if (isIdentifier(decl.qualifier) && decl.qualifier.escapedText === symbolName(exportSymbol)) { - singleReferences.push(decl.qualifier); + const firstIdentifier = getFirstIdentifier(decl.qualifier); + if (firstIdentifier.escapedText === symbolName(exportSymbol)) { + singleReferences.push(firstIdentifier); } } else if (exportKind === ExportKind.ExportEquals) { diff --git a/tests/cases/fourslash/findAllRefsReExportsUseInImportType.ts b/tests/cases/fourslash/findAllRefsReExportsUseInImportType.ts new file mode 100644 index 0000000000000..c15c410ccbaa6 --- /dev/null +++ b/tests/cases/fourslash/findAllRefsReExportsUseInImportType.ts @@ -0,0 +1,49 @@ +/// + +// @Filename: /foo/types/types.ts +////[|export type [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeIndex": 0 |}Full|] = { prop: string; };|] + +// @Filename: /foo/types/index.ts +////[|import * as [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeIndex": 2 |}foo|] from './types';|] +////[|export { [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeIndex": 4 |}foo|] };|] + +// @Filename: /app.ts +////[|import { [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeIndex": 6 |}foo|] } from './foo/types';|] +////export type fullType = [|foo|].[|Full|]; +////type namespaceImport = typeof import('./foo/types'); +////type fullType2 = import('./foo/types').[|foo|].[|Full|]; + +verify.noErrors(); +const [full0Def, full0, foo0Def, foo0, foo1Def, foo1, foo2Def, foo2, foo3, full1, foo4, full2] = test.ranges(); +const fullRanges = [full0, full1, full2]; +const full = { + definition: "type Full = {\n prop: string;\n}", + ranges: fullRanges +}; +verify.referenceGroups(fullRanges, [full]); +verify.renameLocations(fullRanges, fullRanges); + +const fooTypesRanges = [foo0, foo1]; +const fooTypes = { + definition: "import foo", + ranges: fooTypesRanges +}; +const fooAppRanges = [foo2, foo3]; +const fooApp = { + definition: "import foo", + ranges: fooAppRanges +}; +const exportFooRanges = [foo4]; +const fooExport = { + definition: "export foo", + ranges: exportFooRanges +}; +verify.referenceGroups(fooTypesRanges, [fooTypes, fooExport, fooApp]); +verify.referenceGroups(fooAppRanges, [fooApp, fooTypes, fooExport]); +verify.referenceGroups(exportFooRanges, [fooTypes, fooExport, fooApp]); + +verify.renameLocations([foo0], [foo0, { range: foo1, suffixText: " as foo" }]); +verify.renameLocations([foo1, foo4], [foo2, foo3, foo4, { range: foo1, prefixText: "foo as " }]); +verify.renameLocations(fooAppRanges, [{ range: foo2, prefixText: "foo as " }, foo3]); + +verify.rangesAreRenameLocations({ ranges: [foo2, foo3, foo4, foo0, foo1], providePrefixAndSuffixTextForRename: false }); \ No newline at end of file diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index e2626774a998a..714883553cfcb 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -297,7 +297,7 @@ declare namespace FourSlashInterface { singleReferenceGroup(definition: ReferencesDefinition, ranges?: Range[] | string): void; rangesAreOccurrences(isWriteAccess?: boolean, ranges?: Range[]): void; rangesWithSameTextAreRenameLocations(...texts: string[]): void; - rangesAreRenameLocations(options?: Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges?: Range[] }); + rangesAreRenameLocations(options?: Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges?: Range[], providePrefixAndSuffixTextForRename?: boolean }); findReferencesDefinitionDisplayPartsAtCaretAre(expected: ts.SymbolDisplayPart[]): void; noSignatureHelp(...markers: (string | Marker)[]): void; noSignatureHelpForTriggerReason(triggerReason: SignatureHelpTriggerReason, ...markers: (string | Marker)[]): void diff --git a/tests/cases/fourslash/renameImportOfReExport.ts b/tests/cases/fourslash/renameImportOfReExport.ts index c80da246699f7..7844a78633bbb 100644 --- a/tests/cases/fourslash/renameImportOfReExport.ts +++ b/tests/cases/fourslash/renameImportOfReExport.ts @@ -25,5 +25,5 @@ const classes = { definition: "class C", ranges: [r0] }; const bs = { definition: "(alias) class C\nexport C", ranges: [r1] }; const imports = { definition: "(alias) class C\nimport C", ranges: importRanges }; verify.referenceGroups(r0, [classes, bs, imports]); -verify.referenceGroups(r1, [bs, imports, classes]); +verify.referenceGroups(r1, [classes, bs, imports]); verify.referenceGroups(importRanges, [imports, bs, classes]);