Skip to content

Commit da37457

Browse files
author
Andy
authored
When renaming an imported symbol, rename only in current file (#28406)
* When renaming an imported symbol, rename only in current file * Improve re-export references * Remember to use 'range' in for loop * Uncomment tests
1 parent f4fb28d commit da37457

File tree

121 files changed

+519
-532
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

121 files changed

+519
-532
lines changed

src/compiler/checker.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -242,17 +242,7 @@ namespace ts {
242242
const parsed = getParseTreeNode(node, isFunctionLike);
243243
return parsed ? isImplementationOfOverload(parsed) : undefined;
244244
},
245-
getImmediateAliasedSymbol: symbol => {
246-
Debug.assert((symbol.flags & SymbolFlags.Alias) !== 0, "Should only get Alias here.");
247-
const links = getSymbolLinks(symbol);
248-
if (!links.immediateTarget) {
249-
const node = getDeclarationOfAliasSymbol(symbol);
250-
if (!node) return Debug.fail();
251-
links.immediateTarget = getTargetOfAliasDeclaration(node, /*dontRecursivelyResolve*/ true);
252-
}
253-
254-
return links.immediateTarget;
255-
},
245+
getImmediateAliasedSymbol,
256246
getAliasedSymbol: resolveAlias,
257247
getEmitResolver,
258248
getExportsOfModule: getExportsOfModuleAsArray,
@@ -17828,6 +17818,18 @@ namespace ts {
1782817818
return createIndexInfo(unionType, /*isReadonly*/ false);
1782917819
}
1783017820

17821+
function getImmediateAliasedSymbol(symbol: Symbol): Symbol | undefined {
17822+
Debug.assert((symbol.flags & SymbolFlags.Alias) !== 0, "Should only get Alias here.");
17823+
const links = getSymbolLinks(symbol);
17824+
if (!links.immediateTarget) {
17825+
const node = getDeclarationOfAliasSymbol(symbol);
17826+
if (!node) return Debug.fail();
17827+
links.immediateTarget = getTargetOfAliasDeclaration(node, /*dontRecursivelyResolve*/ true);
17828+
}
17829+
17830+
return links.immediateTarget;
17831+
}
17832+
1783117833
function checkObjectLiteral(node: ObjectLiteralExpression, checkMode?: CheckMode): Type {
1783217834
const inDestructuringPattern = isAssignmentTarget(node);
1783317835
// Grammar checking
@@ -28218,7 +28220,10 @@ namespace ts {
2821828220

2821928221
if (isDeclarationNameOrImportPropertyName(node)) {
2822028222
// This is a declaration, call getSymbolOfNode
28221-
return getSymbolOfNode(parent);
28223+
const parentSymbol = getSymbolOfNode(parent)!;
28224+
return isImportOrExportSpecifier(node.parent) && node.parent.propertyName === node
28225+
? getImmediateAliasedSymbol(parentSymbol)
28226+
: parentSymbol;
2822228227
}
2822328228
else if (isLiteralComputedPropertyDeclarationName(node)) {
2822428229
return getSymbolOfNode(parent.parent);

src/compiler/utilities.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6111,6 +6111,10 @@ namespace ts {
61116111
|| kind === SyntaxKind.TemplateTail;
61126112
}
61136113

6114+
export function isImportOrExportSpecifier(node: Node): node is ImportSpecifier | ExportSpecifier {
6115+
return isImportSpecifier(node) || isExportSpecifier(node);
6116+
}
6117+
61146118
export function isStringTextContainingNode(node: Node): node is StringLiteral | TemplateLiteralToken {
61156119
return node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind);
61166120
}

src/services/findAllReferences.ts

Lines changed: 52 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,10 @@ namespace ts.FindAllReferences {
8989
program: Program, cancellationToken: CancellationToken, sourceFiles: ReadonlyArray<SourceFile>, node: Node, position: number, options: Options | undefined,
9090
convertEntry: ToReferenceOrRenameEntry<T>,
9191
): T[] | undefined {
92-
return map(flattenEntries(Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, options)), entry => convertEntry(entry, node));
92+
return map(flattenEntries(Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, options)), entry => convertEntry(entry, node, program.getTypeChecker()));
9393
}
9494

95-
export type ToReferenceOrRenameEntry<T> = (entry: Entry, originalNode: Node) => T;
95+
export type ToReferenceOrRenameEntry<T> = (entry: Entry, originalNode: Node, checker: TypeChecker) => T;
9696

9797
export function getReferenceEntriesForNode(
9898
position: number,
@@ -157,8 +157,8 @@ namespace ts.FindAllReferences {
157157
return { displayParts, kind: symbolKind };
158158
}
159159

160-
export function toRenameLocation(entry: Entry, originalNode: Node): RenameLocation {
161-
return { ...entryToDocumentSpan(entry), ...getPrefixAndSuffixText(entry, originalNode) };
160+
export function toRenameLocation(entry: Entry, originalNode: Node, checker: TypeChecker): RenameLocation {
161+
return { ...entryToDocumentSpan(entry), ...getPrefixAndSuffixText(entry, originalNode, checker) };
162162
}
163163

164164
export function toReferenceEntry(entry: Entry): ReferenceEntry {
@@ -188,25 +188,28 @@ namespace ts.FindAllReferences {
188188
}
189189
}
190190

191-
function getPrefixAndSuffixText(entry: Entry, originalNode: Node): { readonly prefixText?: string, readonly suffixText?: string } {
191+
interface PrefixAndSuffix { readonly prefixText?: string; readonly suffixText?: string; }
192+
function getPrefixAndSuffixText(entry: Entry, originalNode: Node, checker: TypeChecker): PrefixAndSuffix {
192193
if (entry.kind !== EntryKind.Span && isIdentifier(originalNode)) {
193194
const { node, kind } = entry;
194195
const name = originalNode.text;
195196
const isShorthandAssignment = isShorthandPropertyAssignment(node.parent);
196197
if (isShorthandAssignment || isObjectBindingElementWithoutPropertyName(node.parent)) {
197-
if (kind === EntryKind.SearchedLocalFoundProperty) {
198-
return { prefixText: name + ": " };
199-
}
200-
else if (kind === EntryKind.SearchedPropertyFoundLocal) {
201-
return { suffixText: ": " + name };
202-
}
203-
else {
204-
return isShorthandAssignment
205-
// In `const o = { x }; o.x`, symbolAtLocation at `x` in `{ x }` is the property symbol.
206-
? { suffixText: ": " + name }
207-
// For a binding element `const { x } = o;`, symbolAtLocation at `x` is the property symbol.
208-
: { prefixText: name + ": " };
209-
}
198+
const prefixColon: PrefixAndSuffix = { prefixText: name + ": " };
199+
const suffixColon: PrefixAndSuffix = { suffixText: ": " + name };
200+
return kind === EntryKind.SearchedLocalFoundProperty ? prefixColon
201+
: kind === EntryKind.SearchedPropertyFoundLocal ? suffixColon
202+
// In `const o = { x }; o.x`, symbolAtLocation at `x` in `{ x }` is the property symbol.
203+
// For a binding element `const { x } = o;`, symbolAtLocation at `x` is the property symbol.
204+
: isShorthandAssignment ? suffixColon : prefixColon;
205+
}
206+
else if (isImportSpecifier(entry.node.parent) && !entry.node.parent.propertyName) {
207+
// If the original symbol was using this alias, just rename the alias.
208+
const originalSymbol = isExportSpecifier(originalNode.parent) ? checker.getExportSpecifierLocalTargetSymbol(originalNode.parent) : checker.getSymbolAtLocation(originalNode);
209+
return contains(originalSymbol!.declarations, entry.node.parent) ? { prefixText: name + " as " } : emptyOptions;
210+
}
211+
else if (isExportSpecifier(entry.node.parent) && !entry.node.parent.propertyName) {
212+
return originalNode === entry.node ? { prefixText: name + " as " } : { suffixText: " as " + name };
210213
}
211214
}
212215

@@ -480,16 +483,21 @@ namespace ts.FindAllReferences.Core {
480483
}
481484

482485
/** Core find-all-references algorithm for a normal symbol. */
483-
function getReferencedSymbolsForSymbol(symbol: Symbol, node: Node | undefined, sourceFiles: ReadonlyArray<SourceFile>, sourceFilesSet: ReadonlyMap<true>, checker: TypeChecker, cancellationToken: CancellationToken, options: Options): SymbolAndEntries[] {
484-
symbol = node && skipPastExportOrImportSpecifierOrUnion(symbol, node, checker) || symbol;
486+
function getReferencedSymbolsForSymbol(originalSymbol: Symbol, node: Node | undefined, sourceFiles: ReadonlyArray<SourceFile>, sourceFilesSet: ReadonlyMap<true>, checker: TypeChecker, cancellationToken: CancellationToken, options: Options): SymbolAndEntries[] {
487+
const symbol = node && skipPastExportOrImportSpecifierOrUnion(originalSymbol, node, checker, !!options.isForRename) || originalSymbol;
485488

486489
// Compute the meaning from the location and the symbol it references
487490
const searchMeaning = node ? getIntersectingMeaningFromDeclarations(node, symbol) : SemanticMeaning.All;
488491

489492
const result: SymbolAndEntries[] = [];
490493
const state = new State(sourceFiles, sourceFilesSet, node ? getSpecialSearchKind(node) : SpecialSearchKind.None, checker, cancellationToken, searchMeaning, options, result);
491494

492-
if (node && node.kind === SyntaxKind.DefaultKeyword) {
495+
const exportSpecifier = !options.isForRename ? undefined : find(symbol.declarations, isExportSpecifier);
496+
if (exportSpecifier) {
497+
// When renaming at an export specifier, rename the export and not the thing being exported.
498+
getReferencesAtExportSpecifier(exportSpecifier.name, symbol, exportSpecifier, state.createSearch(node, originalSymbol, /*comingFrom*/ undefined), state, /*addReferencesHere*/ true, /*alwaysGetReferences*/ true);
499+
}
500+
else if (node && node.kind === SyntaxKind.DefaultKeyword) {
493501
addReference(node, symbol, state);
494502
searchForImportsOfExport(node, symbol, { exportingModuleSymbol: Debug.assertDefined(symbol.parent, "Expected export symbol to have a parent"), exportKind: ExportKind.Default }, state);
495503
}
@@ -530,16 +538,11 @@ namespace ts.FindAllReferences.Core {
530538
}
531539

532540
/** Handle a few special cases relating to export/import specifiers. */
533-
function skipPastExportOrImportSpecifierOrUnion(symbol: Symbol, node: Node, checker: TypeChecker): Symbol | undefined {
541+
function skipPastExportOrImportSpecifierOrUnion(symbol: Symbol, node: Node, checker: TypeChecker, isForRename: boolean): Symbol | undefined {
534542
const { parent } = node;
535-
if (isExportSpecifier(parent)) {
543+
if (isExportSpecifier(parent) && !isForRename) {
536544
return getLocalSymbolForExportSpecifier(node as Identifier, symbol, parent, checker);
537545
}
538-
if (isImportSpecifier(parent) && parent.propertyName === node) {
539-
// We're at `foo` in `import { foo as bar }`. Probably intended to find all refs on the original, not just on the import.
540-
return checker.getImmediateAliasedSymbol(symbol)!;
541-
}
542-
543546
// If the symbol is declared as part of a declaration like `{ type: "a" } | { type: "b" }`, use the property on the union type to get more references.
544547
return firstDefined(symbol.declarations, decl => {
545548
if (!decl.parent) {
@@ -741,7 +744,8 @@ namespace ts.FindAllReferences.Core {
741744
}
742745
for (const indirectUser of indirectUsers) {
743746
for (const node of getPossibleSymbolReferenceNodes(indirectUser, isDefaultExport ? "default" : exportName)) {
744-
if (isIdentifier(node) && checker.getSymbolAtLocation(node) === exportSymbol) {
747+
// Import specifiers should be handled by importSearches
748+
if (isIdentifier(node) && !isImportOrExportSpecifier(node.parent) && checker.getSymbolAtLocation(node) === exportSymbol) {
745749
cb(node);
746750
}
747751
}
@@ -754,7 +758,7 @@ namespace ts.FindAllReferences.Core {
754758
// Don't rename an import type `import("./module-name")` when renaming `name` in `export = name;`
755759
if (!isIdentifier(singleRef)) return false;
756760
// At `default` in `import { default as x }` or `export { default as x }`, do add a reference, but do not rename.
757-
return !((isExportSpecifier(singleRef.parent) || isImportSpecifier(singleRef.parent)) && singleRef.escapedText === InternalSymbolName.Default);
761+
return !(isImportOrExportSpecifier(singleRef.parent) && singleRef.escapedText === InternalSymbolName.Default);
758762
}
759763

760764
// Go to the symbol we imported from and find references for it.
@@ -1058,17 +1062,25 @@ namespace ts.FindAllReferences.Core {
10581062
getImportOrExportReferences(referenceLocation, referenceSymbol, search, state);
10591063
}
10601064

1061-
function getReferencesAtExportSpecifier(referenceLocation: Identifier, referenceSymbol: Symbol, exportSpecifier: ExportSpecifier, search: Search, state: State, addReferencesHere: boolean): void {
1065+
function getReferencesAtExportSpecifier(
1066+
referenceLocation: Identifier,
1067+
referenceSymbol: Symbol,
1068+
exportSpecifier: ExportSpecifier,
1069+
search: Search,
1070+
state: State,
1071+
addReferencesHere: boolean,
1072+
alwaysGetReferences?: boolean,
1073+
): void {
10621074
const { parent, propertyName, name } = exportSpecifier;
10631075
const exportDeclaration = parent.parent;
10641076
const localSymbol = getLocalSymbolForExportSpecifier(referenceLocation, referenceSymbol, exportSpecifier, state.checker);
1065-
if (!search.includes(localSymbol)) {
1077+
if (!alwaysGetReferences && !search.includes(localSymbol)) {
10661078
return;
10671079
}
10681080

10691081
if (!propertyName) {
10701082
// Don't rename at `export { default } from "m";`. (but do continue to search for imports of the re-export)
1071-
if (!(state.options.isForRename && name.escapedText === InternalSymbolName.Default)) {
1083+
if (!(state.options.isForRename && (name.escapedText === InternalSymbolName.Default))) {
10721084
addRef();
10731085
}
10741086
}
@@ -1080,7 +1092,7 @@ namespace ts.FindAllReferences.Core {
10801092
}
10811093

10821094
if (addReferencesHere && !state.options.isForRename && state.markSeenReExportRHS(name)) {
1083-
addReference(name, referenceSymbol, state);
1095+
addReference(name, Debug.assertDefined(exportSpecifier.symbol), state);
10841096
}
10851097
}
10861098
else {
@@ -1090,15 +1102,15 @@ namespace ts.FindAllReferences.Core {
10901102
}
10911103

10921104
// For `export { foo as bar }`, rename `foo`, but not `bar`.
1093-
if (!(referenceLocation === propertyName && state.options.isForRename)) {
1105+
if (!state.options.isForRename || alwaysGetReferences) {
10941106
const exportKind = referenceLocation.originalKeywordKind === SyntaxKind.DefaultKeyword ? ExportKind.Default : ExportKind.Named;
1095-
const exportInfo = getExportInfo(referenceSymbol, exportKind, state.checker);
1096-
if (!exportInfo) return Debug.fail();
1097-
searchForImportsOfExport(referenceLocation, referenceSymbol, exportInfo, state);
1107+
const exportSymbol = Debug.assertDefined(exportSpecifier.symbol);
1108+
const exportInfo = Debug.assertDefined(getExportInfo(exportSymbol, exportKind, state.checker));
1109+
searchForImportsOfExport(referenceLocation, exportSymbol, exportInfo, state);
10981110
}
10991111

11001112
// At `export { x } from "foo"`, also search for the imported symbol `"foo".x`.
1101-
if (search.comingFrom !== ImportExport.Export && exportDeclaration.moduleSpecifier && !propertyName) {
1113+
if (search.comingFrom !== ImportExport.Export && exportDeclaration.moduleSpecifier && !propertyName && !state.options.isForRename) {
11021114
const imported = state.checker.getExportSpecifierLocalTargetSymbol(exportSpecifier);
11031115
if (imported) searchForImportedSymbol(imported, state);
11041116
}
@@ -1133,12 +1145,11 @@ namespace ts.FindAllReferences.Core {
11331145
const { symbol } = importOrExport;
11341146

11351147
if (importOrExport.kind === ImportExport.Import) {
1136-
if (!state.options.isForRename || importOrExport.isNamedImport) {
1148+
if (!state.options.isForRename) {
11371149
searchForImportedSymbol(symbol, state);
11381150
}
11391151
}
11401152
else {
1141-
// We don't check for `state.isForRename`, even for default exports, because importers that previously matched the export name should be updated to continue matching.
11421153
searchForImportsOfExport(referenceLocation, symbol, importOrExport.exportInfo, state);
11431154
}
11441155
}

src/services/importTracker.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,6 @@ namespace ts.FindAllReferences {
424424
export interface ImportedSymbol {
425425
kind: ImportExport.Import;
426426
symbol: Symbol;
427-
isNamedImport: boolean;
428427
}
429428
export interface ExportedSymbol {
430429
kind: ImportExport.Export;
@@ -467,7 +466,7 @@ namespace ts.FindAllReferences {
467466
}
468467

469468
const lhsSymbol = checker.getSymbolAtLocation(exportNode.name)!;
470-
return { kind: ImportExport.Import, symbol: lhsSymbol, isNamedImport: false };
469+
return { kind: ImportExport.Import, symbol: lhsSymbol };
471470
}
472471
else {
473472
return exportInfo(symbol, getExportKindForDeclaration(exportNode));
@@ -542,7 +541,7 @@ namespace ts.FindAllReferences {
542541
// (All imports returned from this function will be ignored anyway if we are in rename and this is a not a named export.)
543542
const importedName = symbolEscapedNameNoDefault(importedSymbol);
544543
if (importedName === undefined || importedName === InternalSymbolName.Default || importedName === symbol.escapedName) {
545-
return { kind: ImportExport.Import, symbol: importedSymbol, ...isImport };
544+
return { kind: ImportExport.Import, symbol: importedSymbol };
546545
}
547546
}
548547

@@ -588,22 +587,20 @@ namespace ts.FindAllReferences {
588587
}
589588
}
590589

591-
function isNodeImport(node: Node): { isNamedImport: boolean } | undefined {
590+
function isNodeImport(node: Node): boolean {
592591
const { parent } = node;
593592
switch (parent.kind) {
594593
case SyntaxKind.ImportEqualsDeclaration:
595-
return (parent as ImportEqualsDeclaration).name === node && isExternalModuleImportEquals(parent as ImportEqualsDeclaration)
596-
? { isNamedImport: false }
597-
: undefined;
594+
return (parent as ImportEqualsDeclaration).name === node && isExternalModuleImportEquals(parent as ImportEqualsDeclaration);
598595
case SyntaxKind.ImportSpecifier:
599596
// For a rename import `{ foo as bar }`, don't search for the imported symbol. Just find local uses of `bar`.
600-
return (parent as ImportSpecifier).propertyName ? undefined : { isNamedImport: true };
597+
return !(parent as ImportSpecifier).propertyName;
601598
case SyntaxKind.ImportClause:
602599
case SyntaxKind.NamespaceImport:
603600
Debug.assert((parent as ImportClause | NamespaceImport).name === node);
604-
return { isNamedImport: false };
601+
return true;
605602
default:
606-
return undefined;
603+
return false;
607604
}
608605
}
609606

src/services/utilities.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1628,9 +1628,7 @@ namespace ts {
16281628
}
16291629

16301630
export function isImportOrExportSpecifierName(location: Node): location is Identifier {
1631-
return !!location.parent &&
1632-
(location.parent.kind === SyntaxKind.ImportSpecifier || location.parent.kind === SyntaxKind.ExportSpecifier) &&
1633-
(<ImportOrExportSpecifier>location.parent).propertyName === location;
1631+
return !!location.parent && isImportOrExportSpecifier(location.parent) && location.parent.propertyName === location;
16341632
}
16351633

16361634
/**

0 commit comments

Comments
 (0)