From b554a3691d527930259a5e33db3aa8306afa1425 Mon Sep 17 00:00:00 2001 From: andy-ms Date: Sun, 10 Dec 2017 16:58:17 -0800 Subject: [PATCH] Ensure getRootSymbols always works recursively --- src/compiler/checker.ts | 42 +++++++------------ src/compiler/core.ts | 6 ++- src/services/utilities.ts | 4 -- .../cases/fourslash/findAllRefsRootSymbols.ts | 17 ++++++++ 4 files changed, 38 insertions(+), 31 deletions(-) create mode 100644 tests/cases/fourslash/findAllRefsRootSymbols.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4cc0bb79531..26bd364ad0f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24587,36 +24587,26 @@ namespace ts { } function getRootSymbols(symbol: Symbol): Symbol[] { + const roots = getImmediateRootSymbols(symbol); + return roots === undefined ? [symbol] : flatMap(roots, getRootSymbols); + } + function getImmediateRootSymbols(symbol: Symbol): ReadonlyArray | undefined { if (getCheckFlags(symbol) & CheckFlags.Synthetic) { - const symbols: Symbol[] = []; - const name = symbol.escapedName; - forEach(getSymbolLinks(symbol).containingType.types, t => { - const symbol = getPropertyOfType(t, name); - if (symbol) { - symbols.push(symbol); - } - }); - return symbols; + return mapDefined(getSymbolLinks(symbol).containingType.types, type => getPropertyOfType(type, symbol.escapedName)); } else if (symbol.flags & SymbolFlags.Transient) { - const transient = symbol as TransientSymbol; - if (transient.leftSpread) { - return [...getRootSymbols(transient.leftSpread), ...getRootSymbols(transient.rightSpread)]; - } - if (transient.syntheticOrigin) { - return getRootSymbols(transient.syntheticOrigin); - } - - let target: Symbol; - let next = symbol; - while (next = getSymbolLinks(next).target) { - target = next; - } - if (target) { - return [target]; - } + const { leftSpread, rightSpread, syntheticOrigin } = symbol as TransientSymbol; + return leftSpread ? [leftSpread, rightSpread] : syntheticOrigin ? [syntheticOrigin] : singleElementArray(tryGetAliasTarget(symbol)); } - return [symbol]; + return undefined; + } + function tryGetAliasTarget(symbol: Symbol): Symbol | undefined { + let target: Symbol | undefined; + let next = symbol; + while (next = getSymbolLinks(next).target) { + target = next; + } + return target; } // Emitter support diff --git a/src/compiler/core.ts b/src/compiler/core.ts index b11750c25af..188cd250605 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1417,8 +1417,8 @@ namespace ts { return Array.isArray ? Array.isArray(value) : value instanceof Array; } - export function toArray(value: T | ReadonlyArray): ReadonlyArray; export function toArray(value: T | T[]): T[]; + export function toArray(value: T | ReadonlyArray): ReadonlyArray; export function toArray(value: T | T[]): T[] { return isArray(value) ? value : [value]; } @@ -3261,4 +3261,8 @@ namespace ts { cachedReadDirectoryResult.clear(); } } + + export function singleElementArray(t: T | undefined): T[] { + return t === undefined ? undefined : [t]; + } } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 358db5e8b4b..d8fe5bd3848 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1110,10 +1110,6 @@ namespace ts { return true; } - export function singleElementArray(t: T | undefined): T[] { - return t === undefined ? undefined : [t]; - } - export function getFirstChildOfKind(node: Node, sourceFile: SourceFile, kind: SyntaxKind): Node | undefined { return find(node.getChildren(sourceFile), c => c.kind === kind); } diff --git a/tests/cases/fourslash/findAllRefsRootSymbols.ts b/tests/cases/fourslash/findAllRefsRootSymbols.ts new file mode 100644 index 00000000000..f72dcb54aef --- /dev/null +++ b/tests/cases/fourslash/findAllRefsRootSymbols.ts @@ -0,0 +1,17 @@ +/// + +////interface I { [|{| "isWriteAccess": true, "isDefinition": true |}x|]: {}; } +////interface J { [|{| "isWriteAccess": true, "isDefinition": true |}x|]: {}; } +////declare const o: (I | J) & { [|{| "isWriteAccess": true, "isDefinition": true |}x|]: string }; +////o.[|x|]; + +const [r0, r1, r2, r3] = test.ranges(); +verify.referenceGroups(r0, [{ definition: "(property) I.x: {}", ranges: [r0, r3] }]); +verify.referenceGroups(r1, [{ definition: "(property) J.x: {}", ranges: [r1, r3] }]); +verify.referenceGroups(r2, [{ definition: "(property) x: string", ranges: [r2, r3] }]); +verify.referenceGroups(r3, [ + { definition: "(property) I.x: {}", ranges: [r0] }, + { definition: "(property) J.x: {}", ranges: [r1] }, + { definition: "(property) x: string", ranges: [r2] }, + { definition: "(property) x: string & {}", ranges: [r3] }, +]);