diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 04d383748c..2de7eeb021 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -1435,22 +1435,27 @@ namespace ts.FindAllReferences.Core { const bindingElementPropertySymbol = getPropertySymbolOfObjectBindingPatternWithoutPropertyName(symbol, checker); if (bindingElementPropertySymbol) { result.push(bindingElementPropertySymbol); + addRootSymbols(bindingElementPropertySymbol); } - // If this is a union property, add all the symbols from all its source symbols in all unioned types. - // If the symbol is an instantiation from a another symbol (e.g. widened symbol) , add the root the list - for (const rootSymbol of checker.getRootSymbols(symbol)) { - if (rootSymbol !== symbol) { - result.push(rootSymbol); - } - - // Add symbol of properties/methods of the same name in base classes and implemented interfaces definitions - if (!implementations && rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { - getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.name, result, /*previousIterationSymbolsCache*/ createSymbolTable(), checker); - } - } + addRootSymbols(symbol); return result; + + function addRootSymbols(sym: Symbol): void { + // If this is a union property, add all the symbols from all its source symbols in all unioned types. + // If the symbol is an instantiation from a another symbol (e.g. widened symbol) , add the root the list + for (const rootSymbol of checker.getRootSymbols(sym)) { + if (rootSymbol !== sym) { + result.push(rootSymbol); + } + + // Add symbol of properties/methods of the same name in base classes and implemented interfaces definitions + if (!implementations && rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { + getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.name, result, /*previousIterationSymbolsCache*/ createSymbolTable(), checker); + } + } + } } /** @@ -1542,34 +1547,39 @@ namespace ts.FindAllReferences.Core { // then include the binding element in the related symbols // let { a } : { a }; const bindingElementPropertySymbol = getPropertySymbolOfObjectBindingPatternWithoutPropertyName(referenceSymbol, state.checker); - if (bindingElementPropertySymbol && search.includes(bindingElementPropertySymbol)) { - return bindingElementPropertySymbol; + if (bindingElementPropertySymbol) { + const fromBindingElement = findRootSymbol(bindingElementPropertySymbol); + if (fromBindingElement) return fromBindingElement; } - // Unwrap symbols to get to the root (e.g. transient symbols as a result of widening) - // Or a union property, use its underlying unioned symbols - return forEach(state.checker.getRootSymbols(referenceSymbol), rootSymbol => { - // if it is in the list, then we are done - if (search.includes(rootSymbol)) { - return rootSymbol; - } + return findRootSymbol(referenceSymbol); - // Finally, try all properties with the same name in any type the containing type extended or implemented, and - // see if any is in the list. If we were passed a parent symbol, only include types that are subtypes of the - // parent symbol - if (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { - // Parents will only be defined if implementations is true - if (search.parents && !some(search.parents, parent => explicitlyInheritsFrom(rootSymbol.parent, parent, state.inheritsFromCache, state.checker))) { - return undefined; + function findRootSymbol(sym: Symbol): Symbol | undefined { + // Unwrap symbols to get to the root (e.g. transient symbols as a result of widening) + // Or a union property, use its underlying unioned symbols + return forEach(state.checker.getRootSymbols(sym), rootSymbol => { + // if it is in the list, then we are done + if (search.includes(rootSymbol)) { + return rootSymbol; } - const result: Symbol[] = []; - getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.name, result, /*previousIterationSymbolsCache*/ createSymbolTable(), state.checker); - return find(result, search.includes); - } + // Finally, try all properties with the same name in any type the containing type extended or implemented, and + // see if any is in the list. If we were passed a parent symbol, only include types that are subtypes of the + // parent symbol + if (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { + // Parents will only be defined if implementations is true + if (search.parents && !some(search.parents, parent => explicitlyInheritsFrom(rootSymbol.parent, parent, state.inheritsFromCache, state.checker))) { + return undefined; + } - return undefined; - }); + const result: Symbol[] = []; + getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.name, result, /*previousIterationSymbolsCache*/ createSymbolTable(), state.checker); + return find(result, search.includes); + } + + return undefined; + }); + } } function getNameFromObjectLiteralElement(node: ObjectLiteralElement): string { diff --git a/tests/cases/fourslash/findAllRefsDestructureGeneric.ts b/tests/cases/fourslash/findAllRefsDestructureGeneric.ts new file mode 100644 index 0000000000..f3d4635ceb --- /dev/null +++ b/tests/cases/fourslash/findAllRefsDestructureGeneric.ts @@ -0,0 +1,15 @@ +/// + +////interface I { +//// [|{| "isWriteAccess": true, "isDefinition": true |}x|]: boolean; +////} +////declare const i: I; +////const { [|{| "isWriteAccess": true, "isDefinition": true |}x|] } = i; + +const [r0, r1] = test.ranges(); + +verify.referenceGroups(r0, [{ definition: "(property) I.x: boolean", ranges: [r0, r1] }]); +verify.referenceGroups(r1, [ + { definition: "(property) I.x: boolean", ranges: [r0] }, + { definition: "const x: boolean", ranges: [r1] } +]); diff --git a/tests/cases/fourslash/getOccurrencesIsDefinitionOfBindingPattern.ts b/tests/cases/fourslash/getOccurrencesIsDefinitionOfBindingPattern.ts index 7725b2e94f..58814b45e9 100644 --- a/tests/cases/fourslash/getOccurrencesIsDefinitionOfBindingPattern.ts +++ b/tests/cases/fourslash/getOccurrencesIsDefinitionOfBindingPattern.ts @@ -1,5 +1,10 @@ /// -////const { [|{| "isWriteAccess": true, "isDefinition": true |}x|], y } = { x: 1, y: 2 }; -////const z = [|{| "isDefinition": false |}x|]; +////const { [|{| "isWriteAccess": true, "isDefinition": true |}x|], y } = { [|{| "isWriteAccess": true, "isDefinition": true |}x|]: 1, y: 2 }; +////const z = [|x|]; -verify.singleReferenceGroup("const x: number"); +const [r0, r1, r2] = test.ranges(); +verify.referenceGroups([r0, r2], [ + { definition: "const x: number", ranges: [r0, r2] }, + { definition: "(property) x: number", ranges: [r1] }, +]); +verify.referenceGroups(r1, [{ definition: "(property) x: number", ranges: [r0, r1, r2] }]);