Skip to content

Commit 01d7f0b

Browse files
committed
Test that the return type of the extracted method counts as usage
1 parent 0c8d85f commit 01d7f0b

File tree

3 files changed

+45
-9
lines changed

3 files changed

+45
-9
lines changed

src/harness/unittests/extractMethods.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,11 @@ namespace A {
581581
function F<U extends T[]>(t2: U) {
582582
[#|t2.toString();|]
583583
}
584+
}`);
585+
// Confirm that the contextual type of an extracted expression counts as a use.
586+
testExtractMethod("extractMethod16",
587+
`function F<T>() {
588+
const array: T[] = [#|[]|];
584589
}`);
585590
});
586591

src/services/refactors/extractMethod.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,7 @@ namespace ts.refactor.extractMethod {
946946
substitutionsPerScope.push(createMap<Expression>());
947947
errorsPerScope.push([]);
948948
}
949+
949950
const seenUsages = createMap<Usage>();
950951
const target = isReadonlyArray(targetRange.range) ? createBlock(<Statement[]>targetRange.range) : targetRange.range;
951952
const containingLexicalScopeOfExtraction = isBlockScope(scopes[0], scopes[0].parent) ? scopes[0] : getEnclosingBlockScopeContainer(scopes[0]);
@@ -955,6 +956,14 @@ namespace ts.refactor.extractMethod {
955956

956957
collectUsages(target);
957958

959+
// Unfortunately, this code takes advantage of the knowledge that the generated method
960+
// will use the contextual type of an expression as the return type of the extracted
961+
// method (and will therefore "use" all the types involved).
962+
if (inGenericContext && !isReadonlyArray(targetRange.range)) {
963+
const contextualType = checker.getContextualType(targetRange.range);
964+
recordTypeParameterUsages(contextualType);
965+
}
966+
958967
if (allTypeParameterUsages.size > 0) {
959968
const seenTypeParameterUsages = createMap<TypeParameter>(); // Key is type ID
960969

@@ -1032,18 +1041,21 @@ namespace ts.refactor.extractMethod {
10321041
return false;
10331042
}
10341043

1044+
function recordTypeParameterUsages(type: Type) {
1045+
const symbolWalker = checker.getSymbolWalker();
1046+
const {visitedTypes} = symbolWalker.walkType(type);
1047+
1048+
for (const visitedType of visitedTypes) {
1049+
if (visitedType.flags & TypeFlags.TypeParameter) {
1050+
allTypeParameterUsages.set(visitedType.id.toString(), visitedType as TypeParameter);
1051+
}
1052+
}
1053+
}
1054+
10351055
function collectUsages(node: Node, valueUsage = Usage.Read) {
10361056
if (inGenericContext) {
10371057
const type = checker.getTypeAtLocation(node);
1038-
1039-
const symbolWalker = checker.getSymbolWalker();
1040-
const {visitedTypes} = symbolWalker.walkType(type);
1041-
1042-
for (const visitedType of visitedTypes) {
1043-
if (visitedType.flags & TypeFlags.TypeParameter) {
1044-
allTypeParameterUsages.set(visitedType.id.toString(), visitedType as TypeParameter);
1045-
}
1046-
}
1058+
recordTypeParameterUsages(type);
10471059
}
10481060

10491061
if (isDeclaration(node) && node.symbol) {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// ==ORIGINAL==
2+
function F<T>() {
3+
const array: T[] = [];
4+
}
5+
// ==SCOPE::function 'F'==
6+
function F<T>() {
7+
const array: T[] = newFunction();
8+
9+
function newFunction(): T[] {
10+
return [];
11+
}
12+
}
13+
// ==SCOPE::global scope==
14+
function F<T>() {
15+
const array: T[] = newFunction<T>();
16+
}
17+
function newFunction<T>(): T[] {
18+
return [];
19+
}

0 commit comments

Comments
 (0)