Test that the return type of the extracted method counts as usage

This commit is contained in:
Andrew Casey 2017-08-16 17:50:50 -07:00
parent 0c8d85fbc4
commit 01d7f0b699
3 changed files with 45 additions and 9 deletions

View File

@ -581,6 +581,11 @@ namespace A {
function F<U extends T[]>(t2: U) {
[#|t2.toString();|]
}
}`);
// Confirm that the contextual type of an extracted expression counts as a use.
testExtractMethod("extractMethod16",
`function F<T>() {
const array: T[] = [#|[]|];
}`);
});

View File

@ -946,6 +946,7 @@ namespace ts.refactor.extractMethod {
substitutionsPerScope.push(createMap<Expression>());
errorsPerScope.push([]);
}
const seenUsages = createMap<Usage>();
const target = isReadonlyArray(targetRange.range) ? createBlock(<Statement[]>targetRange.range) : targetRange.range;
const containingLexicalScopeOfExtraction = isBlockScope(scopes[0], scopes[0].parent) ? scopes[0] : getEnclosingBlockScopeContainer(scopes[0]);
@ -955,6 +956,14 @@ namespace ts.refactor.extractMethod {
collectUsages(target);
// Unfortunately, this code takes advantage of the knowledge that the generated method
// will use the contextual type of an expression as the return type of the extracted
// method (and will therefore "use" all the types involved).
if (inGenericContext && !isReadonlyArray(targetRange.range)) {
const contextualType = checker.getContextualType(targetRange.range);
recordTypeParameterUsages(contextualType);
}
if (allTypeParameterUsages.size > 0) {
const seenTypeParameterUsages = createMap<TypeParameter>(); // Key is type ID
@ -1032,18 +1041,21 @@ namespace ts.refactor.extractMethod {
return false;
}
function recordTypeParameterUsages(type: Type) {
const symbolWalker = checker.getSymbolWalker();
const {visitedTypes} = symbolWalker.walkType(type);
for (const visitedType of visitedTypes) {
if (visitedType.flags & TypeFlags.TypeParameter) {
allTypeParameterUsages.set(visitedType.id.toString(), visitedType as TypeParameter);
}
}
}
function collectUsages(node: Node, valueUsage = Usage.Read) {
if (inGenericContext) {
const type = checker.getTypeAtLocation(node);
const symbolWalker = checker.getSymbolWalker();
const {visitedTypes} = symbolWalker.walkType(type);
for (const visitedType of visitedTypes) {
if (visitedType.flags & TypeFlags.TypeParameter) {
allTypeParameterUsages.set(visitedType.id.toString(), visitedType as TypeParameter);
}
}
recordTypeParameterUsages(type);
}
if (isDeclaration(node) && node.symbol) {

View File

@ -0,0 +1,19 @@
// ==ORIGINAL==
function F<T>() {
const array: T[] = [];
}
// ==SCOPE::function 'F'==
function F<T>() {
const array: T[] = newFunction();
function newFunction(): T[] {
return [];
}
}
// ==SCOPE::global scope==
function F<T>() {
const array: T[] = newFunction<T>();
}
function newFunction<T>(): T[] {
return [];
}