Merge pull request #17988 from amcasey/ExtractGeneric

Handle loose type parameters in Extract Method
This commit is contained in:
Andrew Casey
2017-08-29 13:00:03 -07:00
committed by GitHub
35 changed files with 547 additions and 1043 deletions

View File

@@ -651,6 +651,20 @@ namespace ts {
}
export interface SignatureDeclaration extends NamedDeclaration {
kind: SyntaxKind.CallSignature
| SyntaxKind.ConstructSignature
| SyntaxKind.MethodSignature
| SyntaxKind.IndexSignature
| SyntaxKind.FunctionType
| SyntaxKind.ConstructorType
| SyntaxKind.JSDocFunctionType
| SyntaxKind.FunctionDeclaration
| SyntaxKind.MethodDeclaration
| SyntaxKind.Constructor
| SyntaxKind.GetAccessor
| SyntaxKind.SetAccessor
| SyntaxKind.FunctionExpression
| SyntaxKind.ArrowFunction;
name?: PropertyName;
typeParameters?: NodeArray<TypeParameterDeclaration>;
parameters: NodeArray<ParameterDeclaration>;
@@ -1816,6 +1830,7 @@ namespace ts {
export type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag;
export interface ClassLikeDeclaration extends NamedDeclaration {
kind: SyntaxKind.ClassDeclaration | SyntaxKind.ClassExpression;
name?: Identifier;
typeParameters?: NodeArray<TypeParameterDeclaration>;
heritageClauses?: NodeArray<HeritageClause>;

View File

@@ -477,6 +477,38 @@ namespace ts {
return false;
}
/* @internal */
export function isDeclarationWithTypeParameters(node: Node): node is DeclarationWithTypeParameters;
export function isDeclarationWithTypeParameters(node: DeclarationWithTypeParameters): node is DeclarationWithTypeParameters {
switch (node.kind) {
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.MethodSignature:
case SyntaxKind.IndexSignature:
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.JSDocFunctionType:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.JSDocTemplateTag:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.Constructor:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return true;
default:
staticAssertNever(node);
return false;
}
}
function staticAssertNever(_: never): void {}
// Gets the nearest enclosing block scope container that has the provided node
// as a descendant, that is not the provided node.
export function getEnclosingBlockScopeContainer(node: Node): Node {

View File

@@ -544,13 +544,74 @@ namespace A {
return a1.x + 10;|]
}
}
}`);
// The "b" type parameters aren't used and shouldn't be passed to the extracted function.
// Type parameters should be in syntactic order (i.e. in order or character offset from BOF).
// In all cases, we could use type inference, rather than passing explicit type arguments.
// Note the inclusion of arrow functions to ensure that some type parameters are not from
// targetable scopes.
testExtractMethod("extractMethod13",
`<U1a, U1b>(u1a: U1a, u1b: U1b) => {
function F1<T1a, T1b>(t1a: T1a, t1b: T1b) {
<U2a, U2b>(u2a: U2a, u2b: U2b) => {
function F2<T2a, T2b>(t2a: T2a, t2b: T2b) {
<U3a, U3b>(u3a: U3a, u3b: U3b) => {
[#|t1a.toString();
t2a.toString();
u1a.toString();
u2a.toString();
u3a.toString();|]
}
}
}
}
}`);
// This test is descriptive, rather than normative. The current implementation
// doesn't handle type parameter shadowing.
testExtractMethod("extractMethod14",
`function F<T>(t1: T) {
function F<T>(t2: T) {
[#|t1.toString();
t2.toString();|]
}
}`);
// Confirm that the constraint is preserved.
testExtractMethod("extractMethod15",
`function F<T>(t1: T) {
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[] = [#|[]|];
}`);
// Class type parameter
testExtractMethod("extractMethod17",
`class C<T1, T2> {
M(t1: T1, t2: T2) {
[#|t1.toString()|];
}
}`);
// Method type parameter
testExtractMethod("extractMethod18",
`class C {
M<T1, T2>(t1: T1, t2: T2) {
[#|t1.toString()|];
}
}`);
// Coupled constraints
testExtractMethod("extractMethod19",
`function F<T, U extends T[], V extends U[]>(v: V) {
[#|v.toString()|];
}`);
});
function testExtractMethod(caption: string, text: string) {
it(caption, () => {
Harness.Baseline.runBaseline(`extractMethod/${caption}.js`, () => {
Harness.Baseline.runBaseline(`extractMethod/${caption}.ts`, () => {
const t = extractTest(text);
const selectionRange = t.ranges.get("selection");
if (!selectionRange) {
@@ -560,7 +621,7 @@ namespace A {
path: "/a.ts",
content: t.source
};
const host = projectSystem.createServerHost([f]);
const host = projectSystem.createServerHost([f, projectSystem.libFile]);
const projectService = projectSystem.createProjectService(host);
projectService.openClientFile(f.path);
const program = projectService.inferredProjects[0].getLanguageService().getProgram();
@@ -577,11 +638,11 @@ namespace A {
assert.equal(result.errors, undefined, "expect no errors");
const results = refactor.extractMethod.getPossibleExtractions(result.targetRange, context);
const data: string[] = [];
data.push(`==ORIGINAL==`);
data.push(`// ==ORIGINAL==`);
data.push(sourceFile.text);
for (const r of results) {
const changes = refactor.extractMethod.getPossibleExtractions(result.targetRange, context, results.indexOf(r))[0].changes;
data.push(`==SCOPE::${r.scopeDescription}==`);
data.push(`// ==SCOPE::${r.scopeDescription}==`);
data.push(textChanges.applyChanges(sourceFile.text, changes[0].textChanges));
}
return data.join(newLineCharacter);

View File

@@ -610,7 +610,7 @@ namespace ts.refactor.extractMethod {
export function extractFunctionInScope(
node: Statement | Expression | Block,
scope: Scope,
{ usages: usagesInScope, substitutions }: ScopeUsages,
{ usages: usagesInScope, typeParameterUsages, substitutions }: ScopeUsages,
range: TargetRange,
context: RefactorContext): ExtractResultForScope {
@@ -652,7 +652,18 @@ namespace ts.refactor.extractMethod {
callArguments.push(createIdentifier(name));
});
// Provide explicit return types for contexutally-typed functions
const typeParametersAndDeclarations = arrayFrom(typeParameterUsages.values()).map(type => ({ type, declaration: getFirstDeclaration(type) }));
const sortedTypeParametersAndDeclarations = typeParametersAndDeclarations.sort(compareTypesByDeclarationOrder);
const typeParameters: ReadonlyArray<TypeParameterDeclaration> = sortedTypeParametersAndDeclarations.map(t => t.declaration as TypeParameterDeclaration);
// Strictly speaking, we should check whether each name actually binds to the appropriate type
// parameter. In cases of shadowing, they may not.
const callTypeArguments: ReadonlyArray<TypeNode> | undefined = typeParameters.length > 0
? typeParameters.map(decl => createTypeReferenceNode(decl.name, /*typeArguments*/ undefined))
: undefined;
// Provide explicit return types for contextually-typed functions
// to avoid problems when there are literal types present
if (isExpression(node) && !isJS) {
const contextualType = checker.getContextualType(node);
@@ -677,7 +688,7 @@ namespace ts.refactor.extractMethod {
range.facts & RangeFacts.IsGenerator ? createToken(SyntaxKind.AsteriskToken) : undefined,
functionName,
/*questionToken*/ undefined,
/*typeParameters*/[],
typeParameters,
parameters,
returnType,
body
@@ -689,7 +700,7 @@ namespace ts.refactor.extractMethod {
range.facts & RangeFacts.IsAsyncFunction ? [createToken(SyntaxKind.AsyncKeyword)] : undefined,
range.facts & RangeFacts.IsGenerator ? createToken(SyntaxKind.AsteriskToken) : undefined,
functionName,
/*typeParameters*/[],
typeParameters,
parameters,
returnType,
body
@@ -704,7 +715,7 @@ namespace ts.refactor.extractMethod {
// replace range with function call
let call: Expression = createCall(
isClassLike(scope) ? createPropertyAccess(range.facts & RangeFacts.InStaticRegion ? createIdentifier(scope.name.getText()) : createThis(), functionReference) : functionReference,
/*typeArguments*/ undefined,
callTypeArguments, // Note that no attempt is made to take advantage of type argument inference
callArguments);
if (range.facts & RangeFacts.IsGenerator) {
call = createYield(createToken(SyntaxKind.AsteriskToken), call);
@@ -774,6 +785,51 @@ namespace ts.refactor.extractMethod {
changes: changeTracker.getChanges()
};
function getFirstDeclaration(type: Type): Declaration | undefined {
let firstDeclaration = undefined;
const symbol = type.symbol;
if (symbol && symbol.declarations) {
for (const declaration of symbol.declarations) {
if (firstDeclaration === undefined || declaration.pos < firstDeclaration.pos) {
firstDeclaration = declaration;
}
}
}
return firstDeclaration;
}
function compareTypesByDeclarationOrder(
{type: type1, declaration: declaration1}: {type: Type, declaration?: Declaration},
{type: type2, declaration: declaration2}: {type: Type, declaration?: Declaration}) {
if (declaration1) {
if (declaration2) {
const positionDiff = declaration1.pos - declaration2.pos;
if (positionDiff !== 0) {
return positionDiff;
}
}
else {
return 1; // Sort undeclared type parameters to the front.
}
}
else if (declaration2) {
return -1; // Sort undeclared type parameters to the front.
}
const name1 = type1.symbol ? type1.symbol.getName() : "";
const name2 = type2.symbol ? type2.symbol.getName() : "";
const nameDiff = compareStrings(name1, name2);
if (nameDiff !== 0) {
return nameDiff;
}
// IDs are guaranteed to be unique, so this ensures a total ordering.
return type1.id - type2.id;
}
function getPropertyAssignmentsForWrites(writes: UsageEntry[]) {
return writes.map(w => createShorthandPropertyAssignment(w.symbol.name));
}
@@ -867,6 +923,7 @@ namespace ts.refactor.extractMethod {
export interface ScopeUsages {
usages: Map<UsageEntry>;
typeParameterUsages: Map<TypeParameter>; // Key is type ID
substitutions: Map<Node>;
}
@@ -877,6 +934,7 @@ namespace ts.refactor.extractMethod {
sourceFile: SourceFile,
checker: TypeChecker) {
const allTypeParameterUsages = createMap<TypeParameter>(); // Key is type ID
const usagesPerScope: ScopeUsages[] = [];
const substitutionsPerScope: Map<Node>[] = [];
const errorsPerScope: Diagnostic[][] = [];
@@ -884,16 +942,59 @@ namespace ts.refactor.extractMethod {
// initialize results
for (const _ of scopes) {
usagesPerScope.push({ usages: createMap<UsageEntry>(), substitutions: createMap<Expression>() });
usagesPerScope.push({ usages: createMap<UsageEntry>(), typeParameterUsages: createMap<TypeParameter>(), substitutions: createMap<Expression>() });
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]);
const unmodifiedNode = isReadonlyArray(targetRange.range) ? targetRange.range[0] : targetRange.range;
const inGenericContext = isInGenericContext(unmodifiedNode);
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
let i = 0;
for (let curr: Node = unmodifiedNode; curr !== undefined && i < scopes.length; curr = curr.parent) {
if (curr === scopes[i]) {
// Copy current contents of seenTypeParameterUsages into scope.
seenTypeParameterUsages.forEach((typeParameter, id) => {
usagesPerScope[i].typeParameterUsages.set(id, typeParameter);
});
i++;
}
// Note that we add the current node's type parameters *after* updating the corresponding scope.
if (isDeclarationWithTypeParameters(curr) && curr.typeParameters) {
for (const typeParameterDecl of curr.typeParameters) {
const typeParameter = checker.getTypeAtLocation(typeParameterDecl) as TypeParameter;
if (allTypeParameterUsages.has(typeParameter.id.toString())) {
seenTypeParameterUsages.set(typeParameter.id.toString(), typeParameter);
}
}
}
}
// If we didn't get through all the scopes, then there were some that weren't in our
// parent chain (impossible at time of writing). A conservative solution would be to
// copy allTypeParameterUsages into all remaining scopes.
Debug.assert(i === scopes.length);
}
for (let i = 0; i < scopes.length; i++) {
let hasWrite = false;
let readonlyClassPropertyWrite: Declaration | undefined = undefined;
@@ -912,7 +1013,7 @@ namespace ts.refactor.extractMethod {
errorsPerScope[i].push(createDiagnosticForNode(targetRange.range, Messages.CannotCombineWritesAndReturns));
}
else if (readonlyClassPropertyWrite && i > 0) {
errorsPerScope[i].push(createDiagnosticForNode(readonlyClassPropertyWrite, Messages.CannotCombineWritesAndReturns));
errorsPerScope[i].push(createDiagnosticForNode(readonlyClassPropertyWrite, Messages.CannotExtractReadonlyPropertyInitializerOutsideConstructor));
}
}
@@ -924,7 +1025,42 @@ namespace ts.refactor.extractMethod {
return { target, usagesPerScope, errorsPerScope };
function hasTypeParameters(node: Node) {
return isDeclarationWithTypeParameters(node) &&
node.typeParameters !== undefined &&
node.typeParameters.length > 0;
}
function isInGenericContext(node: Node) {
for (; node; node = node.parent) {
if (hasTypeParameters(node)) {
return true;
}
}
return false;
}
function recordTypeParameterUsages(type: Type) {
// PERF: This is potentially very expensive. `type` could be a library type with
// a lot of properties, each of which the walker will visit. Unfortunately, the
// solution isn't as trivial as filtering to user types because of (e.g.) Array.
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);
recordTypeParameterUsages(type);
}
if (isDeclaration(node) && node.symbol) {
visibleDeclarationsInExtractedRange.push(node.symbol);
}

View File

@@ -1,4 +1,4 @@
==ORIGINAL==
// ==ORIGINAL==
namespace A {
let x = 1;
function foo() {
@@ -14,7 +14,7 @@ namespace A {
}
}
}
==SCOPE::function 'a'==
// ==SCOPE::function 'a'==
namespace A {
let x = 1;
function foo() {
@@ -34,7 +34,7 @@ namespace A {
}
}
}
==SCOPE::namespace 'B'==
// ==SCOPE::namespace 'B'==
namespace A {
let x = 1;
function foo() {
@@ -55,7 +55,7 @@ namespace A {
}
}
}
==SCOPE::namespace 'A'==
// ==SCOPE::namespace 'A'==
namespace A {
let x = 1;
function foo() {
@@ -76,7 +76,7 @@ namespace A {
return a;
}
}
==SCOPE::global scope==
// ==SCOPE::global scope==
namespace A {
let x = 1;
function foo() {

View File

@@ -1,4 +1,4 @@
==ORIGINAL==
// ==ORIGINAL==
namespace A {
export interface I { x: number };
class C {
@@ -9,7 +9,7 @@ namespace A {
}
}
}
==SCOPE::class 'C'==
// ==SCOPE::class 'C'==
namespace A {
export interface I { x: number };
class C {
@@ -24,7 +24,7 @@ namespace A {
}
}
}
==SCOPE::namespace 'A'==
// ==SCOPE::namespace 'A'==
namespace A {
export interface I { x: number };
class C {
@@ -39,7 +39,7 @@ namespace A {
return a1.x + 10;
}
}
==SCOPE::global scope==
// ==SCOPE::global scope==
namespace A {
export interface I { x: number };
class C {

View File

@@ -1,4 +1,4 @@
==ORIGINAL==
// ==ORIGINAL==
namespace A {
let y = 1;
class C {
@@ -11,7 +11,7 @@ namespace A {
}
}
}
==SCOPE::class 'C'==
// ==SCOPE::class 'C'==
namespace A {
let y = 1;
class C {
@@ -30,7 +30,7 @@ namespace A {
}
}
}
==SCOPE::namespace 'A'==
// ==SCOPE::namespace 'A'==
namespace A {
let y = 1;
class C {
@@ -49,7 +49,7 @@ namespace A {
return { __return: a1.x + 10, z };
}
}
==SCOPE::global scope==
// ==SCOPE::global scope==
namespace A {
let y = 1;
class C {

View File

@@ -1,4 +1,4 @@
==ORIGINAL==
// ==ORIGINAL==
namespace A {
let y = 1;
class C {
@@ -13,7 +13,7 @@ namespace A {
}
}
}
==SCOPE::class 'C'==
// ==SCOPE::class 'C'==
namespace A {
let y = 1;
class C {

View File

@@ -0,0 +1,75 @@
// ==ORIGINAL==
<U1a, U1b>(u1a: U1a, u1b: U1b) => {
function F1<T1a, T1b>(t1a: T1a, t1b: T1b) {
<U2a, U2b>(u2a: U2a, u2b: U2b) => {
function F2<T2a, T2b>(t2a: T2a, t2b: T2b) {
<U3a, U3b>(u3a: U3a, u3b: U3b) => {
t1a.toString();
t2a.toString();
u1a.toString();
u2a.toString();
u3a.toString();
}
}
}
}
}
// ==SCOPE::function 'F2'==
<U1a, U1b>(u1a: U1a, u1b: U1b) => {
function F1<T1a, T1b>(t1a: T1a, t1b: T1b) {
<U2a, U2b>(u2a: U2a, u2b: U2b) => {
function F2<T2a, T2b>(t2a: T2a, t2b: T2b) {
<U3a, U3b>(u3a: U3a, u3b: U3b) => {
newFunction<U3a>(u3a);
}
function newFunction<U3a>(u3a: U3a) {
t1a.toString();
t2a.toString();
u1a.toString();
u2a.toString();
u3a.toString();
}
}
}
}
}
// ==SCOPE::function 'F1'==
<U1a, U1b>(u1a: U1a, u1b: U1b) => {
function F1<T1a, T1b>(t1a: T1a, t1b: T1b) {
<U2a, U2b>(u2a: U2a, u2b: U2b) => {
function F2<T2a, T2b>(t2a: T2a, t2b: T2b) {
<U3a, U3b>(u3a: U3a, u3b: U3b) => {
newFunction<U2a, T2a, U3a>(t2a, u2a, u3a);
}
}
}
function newFunction<U2a, T2a, U3a>(t2a: T2a, u2a: U2a, u3a: U3a) {
t1a.toString();
t2a.toString();
u1a.toString();
u2a.toString();
u3a.toString();
}
}
}
// ==SCOPE::global scope==
<U1a, U1b>(u1a: U1a, u1b: U1b) => {
function F1<T1a, T1b>(t1a: T1a, t1b: T1b) {
<U2a, U2b>(u2a: U2a, u2b: U2b) => {
function F2<T2a, T2b>(t2a: T2a, t2b: T2b) {
<U3a, U3b>(u3a: U3a, u3b: U3b) => {
newFunction<U1a, T1a, U2a, T2a, U3a>(t1a, t2a, u1a, u2a, u3a);
}
}
}
}
}
function newFunction<U1a, T1a, U2a, T2a, U3a>(t1a: T1a, t2a: T2a, u1a: U1a, u2a: U2a, u3a: U3a) {
t1a.toString();
t2a.toString();
u1a.toString();
u2a.toString();
u3a.toString();
}

View File

@@ -0,0 +1,39 @@
// ==ORIGINAL==
function F<T>(t1: T) {
function F<T>(t2: T) {
t1.toString();
t2.toString();
}
}
// ==SCOPE::function 'F'==
function F<T>(t1: T) {
function F<T>(t2: T) {
newFunction();
function newFunction() {
t1.toString();
t2.toString();
}
}
}
// ==SCOPE::function 'F'==
function F<T>(t1: T) {
function F<T>(t2: T) {
newFunction<T>(t2);
}
function newFunction<T>(t2: T) {
t1.toString();
t2.toString();
}
}
// ==SCOPE::global scope==
function F<T>(t1: T) {
function F<T>(t2: T) {
newFunction<T, T>(t1, t2);
}
}
function newFunction<T, T>(t1: T, t2: T) {
t1.toString();
t2.toString();
}

View File

@@ -0,0 +1,35 @@
// ==ORIGINAL==
function F<T>(t1: T) {
function F<U extends T[]>(t2: U) {
t2.toString();
}
}
// ==SCOPE::function 'F'==
function F<T>(t1: T) {
function F<U extends T[]>(t2: U) {
newFunction();
function newFunction() {
t2.toString();
}
}
}
// ==SCOPE::function 'F'==
function F<T>(t1: T) {
function F<U extends T[]>(t2: U) {
newFunction<U>(t2);
}
function newFunction<U extends T[]>(t2: U) {
t2.toString();
}
}
// ==SCOPE::global scope==
function F<T>(t1: T) {
function F<U extends T[]>(t2: U) {
newFunction<T, U>(t2);
}
}
function newFunction<T, U extends T[]>(t2: U) {
t2.toString();
}

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 [];
}

View File

@@ -0,0 +1,25 @@
// ==ORIGINAL==
class C<T1, T2> {
M(t1: T1, t2: T2) {
t1.toString();
}
}
// ==SCOPE::class 'C'==
class C<T1, T2> {
M(t1: T1, t2: T2) {
this.newFunction(t1);
}
private newFunction(t1: T1) {
t1.toString();
}
}
// ==SCOPE::global scope==
class C<T1, T2> {
M(t1: T1, t2: T2) {
newFunction<T1>(t1);
}
}
function newFunction<T1>(t1: T1) {
t1.toString();
}

View File

@@ -0,0 +1,25 @@
// ==ORIGINAL==
class C {
M<T1, T2>(t1: T1, t2: T2) {
t1.toString();
}
}
// ==SCOPE::class 'C'==
class C {
M<T1, T2>(t1: T1, t2: T2) {
this.newFunction<T1>(t1);
}
private newFunction<T1>(t1: T1) {
t1.toString();
}
}
// ==SCOPE::global scope==
class C {
M<T1, T2>(t1: T1, t2: T2) {
newFunction<T1>(t1);
}
}
function newFunction<T1>(t1: T1) {
t1.toString();
}

View File

@@ -0,0 +1,19 @@
// ==ORIGINAL==
function F<T, U extends T[], V extends U[]>(v: V) {
v.toString();
}
// ==SCOPE::function 'F'==
function F<T, U extends T[], V extends U[]>(v: V) {
newFunction();
function newFunction() {
v.toString();
}
}
// ==SCOPE::global scope==
function F<T, U extends T[], V extends U[]>(v: V) {
newFunction<T, U, V>(v);
}
function newFunction<T, U extends T[], V extends U[]>(v: V) {
v.toString();
}

View File

@@ -1,4 +1,4 @@
==ORIGINAL==
// ==ORIGINAL==
namespace A {
let x = 1;
function foo() {
@@ -12,7 +12,7 @@ namespace A {
}
}
}
==SCOPE::function 'a'==
// ==SCOPE::function 'a'==
namespace A {
let x = 1;
function foo() {
@@ -30,7 +30,7 @@ namespace A {
}
}
}
==SCOPE::namespace 'B'==
// ==SCOPE::namespace 'B'==
namespace A {
let x = 1;
function foo() {
@@ -48,7 +48,7 @@ namespace A {
}
}
}
==SCOPE::namespace 'A'==
// ==SCOPE::namespace 'A'==
namespace A {
let x = 1;
function foo() {
@@ -66,7 +66,7 @@ namespace A {
return foo();
}
}
==SCOPE::global scope==
// ==SCOPE::global scope==
namespace A {
let x = 1;
function foo() {

View File

@@ -1,4 +1,4 @@
==ORIGINAL==
// ==ORIGINAL==
namespace A {
function foo() {
}
@@ -11,7 +11,7 @@ namespace A {
}
}
}
==SCOPE::function 'a'==
// ==SCOPE::function 'a'==
namespace A {
function foo() {
}
@@ -28,7 +28,7 @@ namespace A {
}
}
}
==SCOPE::namespace 'B'==
// ==SCOPE::namespace 'B'==
namespace A {
function foo() {
}
@@ -45,7 +45,7 @@ namespace A {
}
}
}
==SCOPE::namespace 'A'==
// ==SCOPE::namespace 'A'==
namespace A {
function foo() {
}
@@ -62,7 +62,7 @@ namespace A {
return foo();
}
}
==SCOPE::global scope==
// ==SCOPE::global scope==
namespace A {
function foo() {
}

View File

@@ -1,4 +1,4 @@
==ORIGINAL==
// ==ORIGINAL==
namespace A {
function foo() {
}
@@ -13,7 +13,7 @@ namespace A {
}
}
}
==SCOPE::function 'a'==
// ==SCOPE::function 'a'==
namespace A {
function foo() {
}
@@ -32,7 +32,7 @@ namespace A {
}
}
}
==SCOPE::namespace 'B'==
// ==SCOPE::namespace 'B'==
namespace A {
function foo() {
}
@@ -51,7 +51,7 @@ namespace A {
}
}
}
==SCOPE::namespace 'A'==
// ==SCOPE::namespace 'A'==
namespace A {
function foo() {
}
@@ -70,7 +70,7 @@ namespace A {
return foo();
}
}
==SCOPE::global scope==
// ==SCOPE::global scope==
namespace A {
function foo() {
}

View File

@@ -1,4 +1,4 @@
==ORIGINAL==
// ==ORIGINAL==
namespace A {
let x = 1;
export function foo() {
@@ -14,7 +14,7 @@ namespace A {
}
}
}
==SCOPE::function 'a'==
// ==SCOPE::function 'a'==
namespace A {
let x = 1;
export function foo() {
@@ -34,7 +34,7 @@ namespace A {
}
}
}
==SCOPE::namespace 'B'==
// ==SCOPE::namespace 'B'==
namespace A {
let x = 1;
export function foo() {
@@ -55,7 +55,7 @@ namespace A {
}
}
}
==SCOPE::namespace 'A'==
// ==SCOPE::namespace 'A'==
namespace A {
let x = 1;
export function foo() {
@@ -76,7 +76,7 @@ namespace A {
return a;
}
}
==SCOPE::global scope==
// ==SCOPE::global scope==
namespace A {
let x = 1;
export function foo() {

View File

@@ -1,4 +1,4 @@
==ORIGINAL==
// ==ORIGINAL==
namespace A {
let x = 1;
export function foo() {
@@ -14,7 +14,7 @@ namespace A {
}
}
}
==SCOPE::function 'a'==
// ==SCOPE::function 'a'==
namespace A {
let x = 1;
export function foo() {
@@ -34,7 +34,7 @@ namespace A {
}
}
}
==SCOPE::namespace 'B'==
// ==SCOPE::namespace 'B'==
namespace A {
let x = 1;
export function foo() {
@@ -56,7 +56,7 @@ namespace A {
}
}
}
==SCOPE::namespace 'A'==
// ==SCOPE::namespace 'A'==
namespace A {
let x = 1;
export function foo() {
@@ -78,7 +78,7 @@ namespace A {
return { __return: foo(), a };
}
}
==SCOPE::global scope==
// ==SCOPE::global scope==
namespace A {
let x = 1;
export function foo() {

View File

@@ -1,4 +1,4 @@
==ORIGINAL==
// ==ORIGINAL==
namespace A {
let x = 1;
export namespace C {
@@ -16,7 +16,7 @@ namespace A {
}
}
}
==SCOPE::function 'a'==
// ==SCOPE::function 'a'==
namespace A {
let x = 1;
export namespace C {
@@ -38,7 +38,7 @@ namespace A {
}
}
}
==SCOPE::namespace 'B'==
// ==SCOPE::namespace 'B'==
namespace A {
let x = 1;
export namespace C {
@@ -62,7 +62,7 @@ namespace A {
}
}
}
==SCOPE::namespace 'A'==
// ==SCOPE::namespace 'A'==
namespace A {
let x = 1;
export namespace C {
@@ -86,7 +86,7 @@ namespace A {
return { __return: C.foo(), a };
}
}
==SCOPE::global scope==
// ==SCOPE::global scope==
namespace A {
let x = 1;
export namespace C {

View File

@@ -1,4 +1,4 @@
==ORIGINAL==
// ==ORIGINAL==
namespace A {
let x = 1;
namespace B {
@@ -8,7 +8,7 @@ namespace A {
}
}
}
==SCOPE::function 'a'==
// ==SCOPE::function 'a'==
namespace A {
let x = 1;
namespace B {
@@ -22,7 +22,7 @@ namespace A {
}
}
}
==SCOPE::namespace 'B'==
// ==SCOPE::namespace 'B'==
namespace A {
let x = 1;
namespace B {
@@ -36,7 +36,7 @@ namespace A {
}
}
}
==SCOPE::namespace 'A'==
// ==SCOPE::namespace 'A'==
namespace A {
let x = 1;
namespace B {
@@ -50,7 +50,7 @@ namespace A {
return 1 + a1 + x;
}
}
==SCOPE::global scope==
// ==SCOPE::global scope==
namespace A {
let x = 1;
namespace B {

View File

@@ -1,4 +1,4 @@
==ORIGINAL==
// ==ORIGINAL==
namespace A {
export interface I { x: number };
namespace B {
@@ -8,7 +8,7 @@ namespace A {
}
}
}
==SCOPE::function 'a'==
// ==SCOPE::function 'a'==
namespace A {
export interface I { x: number };
namespace B {
@@ -22,7 +22,7 @@ namespace A {
}
}
}
==SCOPE::namespace 'B'==
// ==SCOPE::namespace 'B'==
namespace A {
export interface I { x: number };
namespace B {
@@ -36,7 +36,7 @@ namespace A {
}
}
}
==SCOPE::namespace 'A'==
// ==SCOPE::namespace 'A'==
namespace A {
export interface I { x: number };
namespace B {
@@ -50,7 +50,7 @@ namespace A {
return a1.x + 10;
}
}
==SCOPE::global scope==
// ==SCOPE::global scope==
namespace A {
export interface I { x: number };
namespace B {

View File

@@ -1,98 +0,0 @@
==ORIGINAL==
namespace A {
let x = 1;
function foo() {
}
namespace B {
function a() {
let a = 1;
let y = 5;
let z = x;
a = y;
foo();
}
}
}
==SCOPE::function a==
namespace A {
let x = 1;
function foo() {
}
namespace B {
function a() {
let a = 1;
newFunction();
function newFunction() {
let y = 5;
let z = x;
a = y;
foo();
}
}
}
}
==SCOPE::namespace B==
namespace A {
let x = 1;
function foo() {
}
namespace B {
function a() {
let a = 1;
({ a } = newFunction(a));
}
function newFunction(a: any) {
let y = 5;
let z = x;
a = y;
foo();
return { a };
}
}
}
==SCOPE::namespace A==
namespace A {
let x = 1;
function foo() {
}
namespace B {
function a() {
let a = 1;
({ a } = newFunction(a));
}
}
function newFunction(a: any) {
let y = 5;
let z = x;
a = y;
foo();
return { a };
}
}
==SCOPE::file '/a.ts'==
namespace A {
let x = 1;
function foo() {
}
namespace B {
function a() {
let a = 1;
({ a } = newFunction(x, a, foo));
}
}
}
function newFunction(x: any, a: any, foo: any) {
let y = 5;
let z = x;
a = y;
foo();
return { a };
}

View File

@@ -1,70 +0,0 @@
==ORIGINAL==
namespace A {
export interface I { x: number };
class C {
a() {
let z = 1;
let a1: I = { x: 1 };
return a1.x + 10;
}
}
}
==SCOPE::method a==
namespace A {
export interface I { x: number };
class C {
a() {
let z = 1;
return newFunction();
function newFunction() {
let a1: I = { x: 1 };
return a1.x + 10;
}
}
}
}
==SCOPE::class C==
namespace A {
export interface I { x: number };
class C {
a() {
let z = 1;
return this.newFunction();
}
private newFunction() {
let a1: I = { x: 1 };
return a1.x + 10;
}
}
}
==SCOPE::namespace A==
namespace A {
export interface I { x: number };
class C {
a() {
let z = 1;
return newFunction();
}
}
function newFunction() {
let a1: I = { x: 1 };
return a1.x + 10;
}
}
==SCOPE::file '/a.ts'==
namespace A {
export interface I { x: number };
class C {
a() {
let z = 1;
return newFunction();
}
}
}
function newFunction() {
let a1: A.I = { x: 1 };
return a1.x + 10;
}

View File

@@ -1,86 +0,0 @@
==ORIGINAL==
namespace A {
let y = 1;
class C {
a() {
let z = 1;
let a1 = { x: 1 };
y = 10;
z = 42;
return a1.x + 10;
}
}
}
==SCOPE::method a==
namespace A {
let y = 1;
class C {
a() {
let z = 1;
return newFunction();
function newFunction() {
let a1 = { x: 1 };
y = 10;
z = 42;
return a1.x + 10;
}
}
}
}
==SCOPE::class C==
namespace A {
let y = 1;
class C {
a() {
let z = 1;
var __return: any;
({ z, __return } = this.newFunction(z));
return __return;
}
private newFunction(z: any) {
let a1 = { x: 1 };
y = 10;
z = 42;
return { z, __return: a1.x + 10 };
}
}
}
==SCOPE::namespace A==
namespace A {
let y = 1;
class C {
a() {
let z = 1;
var __return: any;
({ z, __return } = newFunction(z));
return __return;
}
}
function newFunction(z: any) {
let a1 = { x: 1 };
y = 10;
z = 42;
return { z, __return: a1.x + 10 };
}
}
==SCOPE::file '/a.ts'==
namespace A {
let y = 1;
class C {
a() {
let z = 1;
var __return: any;
({ y, z, __return } = newFunction(y, z));
return __return;
}
}
}
function newFunction(y: any, z: any) {
let a1 = { x: 1 };
y = 10;
z = 42;
return { y, z, __return: a1.x + 10 };
}

View File

@@ -1,36 +0,0 @@
==ORIGINAL==
namespace A {
let y = 1;
class C {
b() {}
a() {
let z = 1;
let a1 = { x: 1 };
y = 10;
z = 42;
this.b();
return a1.x + 10;
}
}
}
==SCOPE::class C==
namespace A {
let y = 1;
class C {
b() {}
a() {
let z = 1;
var __return: any;
({ z, __return } = this.newFunction(z));
return __return;
}
private newFunction(z: any) {
let a1 = { x: 1 };
y = 10;
z = 42;
this.b();
return { z, __return: a1.x + 10 };
}
}
}

View File

@@ -1,85 +0,0 @@
==ORIGINAL==
namespace A {
let x = 1;
function foo() {
}
namespace B {
function a() {
let y = 5;
let z = x;
return foo();
}
}
}
==SCOPE::function a==
namespace A {
let x = 1;
function foo() {
}
namespace B {
function a() {
return newFunction();
function newFunction() {
let y = 5;
let z = x;
return foo();
}
}
}
}
==SCOPE::namespace B==
namespace A {
let x = 1;
function foo() {
}
namespace B {
function a() {
return newFunction();
}
function newFunction() {
let y = 5;
let z = x;
return foo();
}
}
}
==SCOPE::namespace A==
namespace A {
let x = 1;
function foo() {
}
namespace B {
function a() {
return newFunction();
}
}
function newFunction() {
let y = 5;
let z = x;
return foo();
}
}
==SCOPE::file '/a.ts'==
namespace A {
let x = 1;
function foo() {
}
namespace B {
function a() {
return newFunction(x, foo);
}
}
}
function newFunction(x: any, foo: any) {
let y = 5;
let z = x;
return foo();
}

View File

@@ -1,80 +0,0 @@
==ORIGINAL==
namespace A {
function foo() {
}
namespace B {
function* a(z: number) {
let y = 5;
yield z;
return foo();
}
}
}
==SCOPE::function a==
namespace A {
function foo() {
}
namespace B {
function* a(z: number) {
return yield* newFunction();
function* newFunction() {
let y = 5;
yield z;
return foo();
}
}
}
}
==SCOPE::namespace B==
namespace A {
function foo() {
}
namespace B {
function* a(z: number) {
return yield* newFunction(z);
}
function* newFunction(z: any) {
let y = 5;
yield z;
return foo();
}
}
}
==SCOPE::namespace A==
namespace A {
function foo() {
}
namespace B {
function* a(z: number) {
return yield* newFunction(z);
}
}
function* newFunction(z: any) {
let y = 5;
yield z;
return foo();
}
}
==SCOPE::file '/a.ts'==
namespace A {
function foo() {
}
namespace B {
function* a(z: number) {
return yield* newFunction(z, foo);
}
}
}
function* newFunction(z: any, foo: any) {
let y = 5;
yield z;
return foo();
}

View File

@@ -1,90 +0,0 @@
==ORIGINAL==
namespace A {
function foo() {
}
namespace B {
async function a(z: number, z1: any) {
let y = 5;
if (z) {
await z1;
}
return foo();
}
}
}
==SCOPE::function a==
namespace A {
function foo() {
}
namespace B {
async function a(z: number, z1: any) {
return await newFunction();
async function newFunction() {
let y = 5;
if (z) {
await z1;
}
return foo();
}
}
}
}
==SCOPE::namespace B==
namespace A {
function foo() {
}
namespace B {
async function a(z: number, z1: any) {
return await newFunction(z, z1);
}
async function newFunction(z: any, z1: any) {
let y = 5;
if (z) {
await z1;
}
return foo();
}
}
}
==SCOPE::namespace A==
namespace A {
function foo() {
}
namespace B {
async function a(z: number, z1: any) {
return await newFunction(z, z1);
}
}
async function newFunction(z: any, z1: any) {
let y = 5;
if (z) {
await z1;
}
return foo();
}
}
==SCOPE::file '/a.ts'==
namespace A {
function foo() {
}
namespace B {
async function a(z: number, z1: any) {
return await newFunction(z, z1, foo);
}
}
}
async function newFunction(z: any, z1: any, foo: any) {
let y = 5;
if (z) {
await z1;
}
return foo();
}

View File

@@ -1,98 +0,0 @@
==ORIGINAL==
namespace A {
let x = 1;
export function foo() {
}
namespace B {
function a() {
let a = 1;
let y = 5;
let z = x;
a = y;
foo();
}
}
}
==SCOPE::function a==
namespace A {
let x = 1;
export function foo() {
}
namespace B {
function a() {
let a = 1;
newFunction();
function newFunction() {
let y = 5;
let z = x;
a = y;
foo();
}
}
}
}
==SCOPE::namespace B==
namespace A {
let x = 1;
export function foo() {
}
namespace B {
function a() {
let a = 1;
({ a } = newFunction(a));
}
function newFunction(a: any) {
let y = 5;
let z = x;
a = y;
foo();
return { a };
}
}
}
==SCOPE::namespace A==
namespace A {
let x = 1;
export function foo() {
}
namespace B {
function a() {
let a = 1;
({ a } = newFunction(a));
}
}
function newFunction(a: any) {
let y = 5;
let z = x;
a = y;
foo();
return { a };
}
}
==SCOPE::file '/a.ts'==
namespace A {
let x = 1;
export function foo() {
}
namespace B {
function a() {
let a = 1;
({ a } = newFunction(x, a));
}
}
}
function newFunction(x: any, a: any) {
let y = 5;
let z = x;
a = y;
A.foo();
return { a };
}

View File

@@ -1,101 +0,0 @@
==ORIGINAL==
namespace A {
let x = 1;
export function foo() {
}
namespace B {
function a() {
let a = 1;
let y = 5;
let z = x;
a = y;
return foo();
}
}
}
==SCOPE::function a==
namespace A {
let x = 1;
export function foo() {
}
namespace B {
function a() {
let a = 1;
return newFunction();
function newFunction() {
let y = 5;
let z = x;
a = y;
return foo();
}
}
}
}
==SCOPE::namespace B==
namespace A {
let x = 1;
export function foo() {
}
namespace B {
function a() {
let a = 1;
var __return: any;
({ a, __return } = newFunction(a));
return __return;
}
function newFunction(a: any) {
let y = 5;
let z = x;
a = y;
return { a, __return: foo() };
}
}
}
==SCOPE::namespace A==
namespace A {
let x = 1;
export function foo() {
}
namespace B {
function a() {
let a = 1;
var __return: any;
({ a, __return } = newFunction(a));
return __return;
}
}
function newFunction(a: any) {
let y = 5;
let z = x;
a = y;
return { a, __return: foo() };
}
}
==SCOPE::file '/a.ts'==
namespace A {
let x = 1;
export function foo() {
}
namespace B {
function a() {
let a = 1;
var __return: any;
({ a, __return } = newFunction(x, a));
return __return;
}
}
}
function newFunction(x: any, a: any) {
let y = 5;
let z = x;
a = y;
return { a, __return: A.foo() };
}

View File

@@ -1,111 +0,0 @@
==ORIGINAL==
namespace A {
let x = 1;
export namespace C {
export function foo() {
}
}
namespace B {
function a() {
let a = 1;
let y = 5;
let z = x;
a = y;
return C.foo();
}
}
}
==SCOPE::function a==
namespace A {
let x = 1;
export namespace C {
export function foo() {
}
}
namespace B {
function a() {
let a = 1;
return newFunction();
function newFunction() {
let y = 5;
let z = x;
a = y;
return C.foo();
}
}
}
}
==SCOPE::namespace B==
namespace A {
let x = 1;
export namespace C {
export function foo() {
}
}
namespace B {
function a() {
let a = 1;
var __return: any;
({ a, __return } = newFunction(a));
return __return;
}
function newFunction(a: any) {
let y = 5;
let z = x;
a = y;
return { a, __return: C.foo() };
}
}
}
==SCOPE::namespace A==
namespace A {
let x = 1;
export namespace C {
export function foo() {
}
}
namespace B {
function a() {
let a = 1;
var __return: any;
({ a, __return } = newFunction(a));
return __return;
}
}
function newFunction(a: any) {
let y = 5;
let z = x;
a = y;
return { a, __return: C.foo() };
}
}
==SCOPE::file '/a.ts'==
namespace A {
let x = 1;
export namespace C {
export function foo() {
}
}
namespace B {
function a() {
let a = 1;
var __return: any;
({ a, __return } = newFunction(x, a));
return __return;
}
}
}
function newFunction(x: any, a: any) {
let y = 5;
let z = x;
a = y;
return { a, __return: A.C.foo() };
}

View File

@@ -1,57 +0,0 @@
==ORIGINAL==
namespace A {
let x = 1;
namespace B {
function a() {
let a1 = 1;
return 1 + a1 + x + 100;
}
}
}
==SCOPE::function a==
namespace A {
let x = 1;
namespace B {
function a() {
let a1 = 1;
return newFunction() + 100;
function newFunction() { 1 + a1 + x; }
}
}
}
==SCOPE::namespace B==
namespace A {
let x = 1;
namespace B {
function a() {
let a1 = 1;
return newFunction(a1) + 100;
}
function newFunction(a1: any) { 1 + a1 + x; }
}
}
==SCOPE::namespace A==
namespace A {
let x = 1;
namespace B {
function a() {
let a1 = 1;
return newFunction(a1) + 100;
}
}
function newFunction(a1: any) { 1 + a1 + x; }
}
==SCOPE::file '/a.ts'==
namespace A {
let x = 1;
namespace B {
function a() {
let a1 = 1;
return newFunction(a1, x) + 100;
}
}
}
function newFunction(a1: any, x: any) { 1 + a1 + x; }

View File

@@ -1,65 +0,0 @@
==ORIGINAL==
namespace A {
export interface I { x: number };
namespace B {
function a() {
let a1: I = { x: 1 };
return a1.x + 10;
}
}
}
==SCOPE::function a==
namespace A {
export interface I { x: number };
namespace B {
function a() {
return newFunction();
function newFunction() {
let a1: I = { x: 1 };
return a1.x + 10;
}
}
}
}
==SCOPE::namespace B==
namespace A {
export interface I { x: number };
namespace B {
function a() {
return newFunction();
}
function newFunction() {
let a1: I = { x: 1 };
return a1.x + 10;
}
}
}
==SCOPE::namespace A==
namespace A {
export interface I { x: number };
namespace B {
function a() {
return newFunction();
}
}
function newFunction() {
let a1: I = { x: 1 };
return a1.x + 10;
}
}
==SCOPE::file '/a.ts'==
namespace A {
export interface I { x: number };
namespace B {
function a() {
return newFunction();
}
}
}
function newFunction() {
let a1: A.I = { x: 1 };
return a1.x + 10;
}