Allow extraction of variable decls used outside the extracted range

If there are only declarations, use the new function as the initializer
for a destructuring declaration.

If there are declarations and writes, changes all of the `const`
declarations to `let` and add `| undefined` onto any explicit types.
Use destructuring assignment to accomplish both "initialization" and
writes.

I don't believe there is a case where there are both declarations and a
return (since the declarations wouldn't be available after the return).

UNDONE: this could probably be generalized to handle binding patterns
but,
for now, only identifiers are supported.

Fixes #18242
Fixes #18855
This commit is contained in:
Andrew Casey
2017-10-11 16:38:38 -07:00
parent bada0095ed
commit 568c8a3298
38 changed files with 1117 additions and 70 deletions
+136
View File
@@ -360,6 +360,142 @@ function parsePrimaryExpression(): any {
export const j = 10;
export const y = [#|j * j|];
}`);
testExtractFunction("extractFunction_VariableDeclaration_Var", `
[#|var x = 1;|]
x;
`);
testExtractFunction("extractFunction_VariableDeclaration_Let_Type", `
[#|let x: number = 1;|]
x;
`);
testExtractFunction("extractFunction_VariableDeclaration_Let_NoType", `
[#|let x = 1;|]
x;
`);
testExtractFunction("extractFunction_VariableDeclaration_Const_Type", `
[#|const x: number = 1;|]
x;
`);
testExtractFunction("extractFunction_VariableDeclaration_Const_NoType", `
[#|const x = 1;|]
x;
`);
testExtractFunction("extractFunction_VariableDeclaration_Multiple1", `
[#|const x = 1, y: string = "a";|]
x; y;
`);
testExtractFunction("extractFunction_VariableDeclaration_Multiple2", `
[#|const x = 1, y = "a";
const z = 3;|]
x; y; z;
`);
testExtractFunction("extractFunction_VariableDeclaration_Multiple3", `
[#|const x = 1, y: string = "a";
let z = 3;|]
x; y; z;
`);
testExtractFunction("extractFunction_VariableDeclaration_ConsumedTwice", `
[#|const x: number = 1;|]
x; x;
`);
testExtractFunction("extractFunction_VariableDeclaration_DeclaredTwice", `
[#|var x = 1;
var x = 2;|]
x;
`);
testExtractFunction("extractFunction_VariableDeclaration_Writes_Var", `
function f() {
let a = 1;
[#|var x = 1;
a++;|]
a; x;
}`);
testExtractFunction("extractFunction_VariableDeclaration_Writes_Let_NoType", `
function f() {
let a = 1;
[#|let x = 1;
a++;|]
a; x;
}`);
testExtractFunction("extractFunction_VariableDeclaration_Writes_Let_Type", `
function f() {
let a = 1;
[#|let x: number = 1;
a++;|]
a; x;
}`);
testExtractFunction("extractFunction_VariableDeclaration_Writes_Const_NoType", `
function f() {
let a = 1;
[#|const x = 1;
a++;|]
a; x;
}`);
testExtractFunction("extractFunction_VariableDeclaration_Writes_Const_Type", `
function f() {
let a = 1;
[#|const x: number = 1;
a++;|]
a; x;
}`);
testExtractFunction("extractFunction_VariableDeclaration_Writes_Mixed1", `
function f() {
let a = 1;
[#|const x = 1;
let y = 2;
a++;|]
a; x; y;
}`);
testExtractFunction("extractFunction_VariableDeclaration_Writes_Mixed2", `
function f() {
let a = 1;
[#|var x = 1;
let y = 2;
a++;|]
a; x; y;
}`);
testExtractFunction("extractFunction_VariableDeclaration_Writes_Mixed3", `
function f() {
let a = 1;
[#|let x: number = 1;
let y = 2;
a++;|]
a; x; y;
}`);
testExtractFunction("extractFunction_VariableDeclaration_Writes_UnionUndefined", `
function f() {
let a = 1;
[#|let x: number | undefined = 1;
let y: undefined | number = 2;
let z: (undefined | number) = 3;
a++;|]
a; x; y; z;
}`);
testExtractFunction("extractFunction_VariableDeclaration_ShorthandProperty", `
function f() {
[#|let x;|]
return { x };
}`);
});
function testExtractFunction(caption: string, text: string) {
+192 -59
View File
@@ -137,7 +137,7 @@ namespace ts.refactor.extractSymbol {
export const FunctionWillNotBeVisibleInTheNewScope = createMessage("Function will not visible in the new scope.");
export const CannotExtractIdentifier = createMessage("Select more than a single identifier.");
export const CannotExtractExportedEntity = createMessage("Cannot extract exported declaration");
export const CannotCombineWritesAndReturns = createMessage("Cannot combine writes and returns");
export const CannotWriteInExpression = createMessage("Cannot write back side-effects when extracting an expression");
export const CannotExtractReadonlyPropertyInitializerOutsideConstructor = createMessage("Cannot move initialization of read-only class property outside of the constructor");
export const CannotExtractAmbientBlock = createMessage("Cannot extract code from ambient contexts");
export const CannotAccessVariablesFromNestedScopes = createMessage("Cannot access variables from nested scopes");
@@ -507,15 +507,16 @@ namespace ts.refactor.extractSymbol {
}
function getFunctionExtractionAtIndex(targetRange: TargetRange, context: RefactorContext, requestedChangesIndex: number): RefactorEditInfo {
const { scopes, readsAndWrites: { target, usagesPerScope, functionErrorsPerScope } } = getPossibleExtractionsWorker(targetRange, context);
const { scopes, readsAndWrites: { target, usagesPerScope, functionErrorsPerScope, exposedVariableDeclarations } } = getPossibleExtractionsWorker(targetRange, context);
Debug.assert(!functionErrorsPerScope[requestedChangesIndex].length, "The extraction went missing? How?");
context.cancellationToken.throwIfCancellationRequested();
return extractFunctionInScope(target, scopes[requestedChangesIndex], usagesPerScope[requestedChangesIndex], targetRange, context);
return extractFunctionInScope(target, scopes[requestedChangesIndex], usagesPerScope[requestedChangesIndex], exposedVariableDeclarations, targetRange, context);
}
function getConstantExtractionAtIndex(targetRange: TargetRange, context: RefactorContext, requestedChangesIndex: number): RefactorEditInfo {
const { scopes, readsAndWrites: { target, usagesPerScope, constantErrorsPerScope } } = getPossibleExtractionsWorker(targetRange, context);
const { scopes, readsAndWrites: { target, usagesPerScope, constantErrorsPerScope, exposedVariableDeclarations } } = getPossibleExtractionsWorker(targetRange, context);
Debug.assert(!constantErrorsPerScope[requestedChangesIndex].length, "The extraction went missing? How?");
Debug.assert(exposedVariableDeclarations.length === 0, "Extract constant accepted a range containing a variable declaration?");
context.cancellationToken.throwIfCancellationRequested();
const expression = isExpression(target)
? target
@@ -674,6 +675,7 @@ namespace ts.refactor.extractSymbol {
node: Statement | Expression | Block,
scope: Scope,
{ usages: usagesInScope, typeParameterUsages, substitutions }: ScopeUsages,
exposedVariableDeclarations: ReadonlyArray<VariableDeclaration>,
range: TargetRange,
context: RefactorContext): RefactorEditInfo {
@@ -731,10 +733,10 @@ namespace ts.refactor.extractSymbol {
// to avoid problems when there are literal types present
if (isExpression(node) && !isJS) {
const contextualType = checker.getContextualType(node);
returnType = checker.typeToTypeNode(contextualType);
returnType = checker.typeToTypeNode(contextualType, scope, NodeBuilderFlags.NoTruncation);
}
const { body, returnValueProperty } = transformFunctionBody(node, writes, substitutions, !!(range.facts & RangeFacts.HasReturn));
const { body, returnValueProperty } = transformFunctionBody(node, exposedVariableDeclarations, writes, substitutions, !!(range.facts & RangeFacts.HasReturn));
let newFunction: MethodDeclaration | FunctionDeclaration;
if (isClassLike(scope)) {
@@ -796,38 +798,114 @@ namespace ts.refactor.extractSymbol {
call = createAwait(call);
}
if (writes) {
if (exposedVariableDeclarations.length && !writes) {
// No need to mix declarations and writes.
// How could any variables be exposed if there's a return statement?
Debug.assert(!returnValueProperty);
Debug.assert(!(range.facts & RangeFacts.HasReturn));
if (exposedVariableDeclarations.length === 1) {
// Declaring exactly one variable: let x = newFunction();
const variableDeclaration = exposedVariableDeclarations[0];
newNodes.push(createVariableStatement(
/*modifiers*/ undefined,
createVariableDeclarationList(
[createVariableDeclaration(getSynthesizedDeepClone(variableDeclaration.name), /*type*/ getSynthesizedDeepClone(variableDeclaration.type), /*initializer*/ call)], // TODO (acasey): test binding patterns
variableDeclaration.parent.flags)));
}
else {
// Declaring multiple variables / return properties:
// let {x, y} = newFunction();
const bindingElements: BindingElement[] = [];
const typeElements: TypeElement[] = [];
let commonNodeFlags = exposedVariableDeclarations[0].parent.flags;
let sawExplicitType = false;
for (const variableDeclaration of exposedVariableDeclarations) {
bindingElements.push(createBindingElement(
/*dotDotDotToken*/ undefined,
/*propertyName*/ undefined,
/*name*/ getSynthesizedDeepClone(variableDeclaration.name)));
// Being returned through an object literal will have widened the type.
const variableType: TypeNode = checker.typeToTypeNode(
checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(variableDeclaration)),
scope,
NodeBuilderFlags.NoTruncation);
typeElements.push(createPropertySignature(
/*modifiers*/ undefined,
/*name*/ variableDeclaration.symbol.name,
/*questionToken*/ undefined,
/*type*/ variableType,
/*initializer*/ undefined));
sawExplicitType = sawExplicitType || variableDeclaration.type !== undefined;
commonNodeFlags = commonNodeFlags & variableDeclaration.parent.flags;
}
const typeLiteral: TypeLiteralNode | undefined = sawExplicitType ? createTypeLiteralNode(typeElements) : undefined;
if (typeLiteral) {
setEmitFlags(typeLiteral, EmitFlags.SingleLine);
}
newNodes.push(createVariableStatement(
/*modifiers*/ undefined,
createVariableDeclarationList(
[createVariableDeclaration(
createObjectBindingPattern(bindingElements),
/*type*/ typeLiteral,
/*initializer*/call)],
commonNodeFlags)));
}
}
else if (exposedVariableDeclarations.length || writes) {
if (exposedVariableDeclarations.length) {
// CONSIDER: we're going to create one statement per variable, but we could actually preserve their original grouping.
for (const variableDeclaration of exposedVariableDeclarations) {
let flags: NodeFlags = variableDeclaration.parent.flags;
if (flags & NodeFlags.Const) {
flags = (flags & ~NodeFlags.Const) | NodeFlags.Let;
}
newNodes.push(createVariableStatement(
/*modifiers*/ undefined,
createVariableDeclarationList(
[createVariableDeclaration(variableDeclaration.symbol.name, getTypeDeepCloneUnionUndefined(variableDeclaration.type))],
flags)));
}
}
if (returnValueProperty) {
// has both writes and return, need to create variable declaration to hold return value;
newNodes.push(createVariableStatement(
/*modifiers*/ undefined,
[createVariableDeclaration(returnValueProperty, createKeywordTypeNode(SyntaxKind.AnyKeyword))]
));
createVariableDeclarationList(
[createVariableDeclaration(returnValueProperty, getTypeDeepCloneUnionUndefined(returnType))],
NodeFlags.Let)));
}
const assignments = getPropertyAssignmentsForWrites(writes);
const assignments = getPropertyAssignmentsForWritesAndVariableDeclarations(exposedVariableDeclarations, writes);
if (returnValueProperty) {
assignments.unshift(createShorthandPropertyAssignment(returnValueProperty));
}
// propagate writes back
if (assignments.length === 1) {
if (returnValueProperty) {
newNodes.push(createReturn(createIdentifier(returnValueProperty)));
}
else {
newNodes.push(createStatement(createBinary(assignments[0].name, SyntaxKind.EqualsToken, call)));
// We would only have introduced a return value property if there had been
// other assignments to make.
Debug.assert(!returnValueProperty);
if (range.facts & RangeFacts.HasReturn) {
newNodes.push(createReturn());
}
newNodes.push(createStatement(createAssignment(assignments[0].name, call)));
if (range.facts & RangeFacts.HasReturn) {
newNodes.push(createReturn());
}
}
else {
// emit e.g.
// { a, b, __return } = newFunction(a, b);
// return __return;
newNodes.push(createStatement(createBinary(createObjectLiteral(assignments), SyntaxKind.EqualsToken, call)));
newNodes.push(createStatement(createAssignment(createObjectLiteral(assignments), call)));
if (returnValueProperty) {
newNodes.push(createReturn(createIdentifier(returnValueProperty)));
}
@@ -861,6 +939,21 @@ namespace ts.refactor.extractSymbol {
const renameFilename = renameRange.getSourceFile().fileName;
const renameLocation = getRenameLocation(edits, renameFilename, functionNameText, /*isDeclaredBeforeUse*/ false);
return { renameFilename, renameLocation, edits };
function getTypeDeepCloneUnionUndefined(typeNode: TypeNode | undefined): TypeNode | undefined {
if (typeNode === undefined) {
return undefined;
}
const clone = getSynthesizedDeepClone(typeNode);
let withoutParens = clone;
while (isParenthesizedTypeNode(withoutParens)) {
withoutParens = withoutParens.type;
}
return isUnionTypeNode(withoutParens) && find(withoutParens.types, t => t.kind === SyntaxKind.UndefinedKeyword)
? clone
: createUnionTypeNode([clone, createKeywordTypeNode(SyntaxKind.UndefinedKeyword)]);
}
}
/**
@@ -883,7 +976,7 @@ namespace ts.refactor.extractSymbol {
const variableType = isJS
? undefined
: checker.typeToTypeNode(checker.getContextualType(node));
: checker.typeToTypeNode(checker.getContextualType(node), scope, NodeBuilderFlags.NoTruncation);
const initializer = transformConstantInitializer(node, substitutions);
@@ -1088,21 +1181,22 @@ namespace ts.refactor.extractSymbol {
}
}
function transformFunctionBody(body: Node, writes: ReadonlyArray<UsageEntry>, substitutions: ReadonlyMap<Node>, hasReturn: boolean): { body: Block, returnValueProperty: string } {
if (isBlock(body) && !writes && substitutions.size === 0) {
// already block, no writes to propagate back, no substitutions - can use node as is
function transformFunctionBody(body: Node, exposedVariableDeclarations: ReadonlyArray<VariableDeclaration>, writes: ReadonlyArray<UsageEntry>, substitutions: ReadonlyMap<Node>, hasReturn: boolean): { body: Block, returnValueProperty: string } {
const hasWritesOrVariableDeclarations = writes !== undefined || exposedVariableDeclarations.length > 0;
if (isBlock(body) && !hasWritesOrVariableDeclarations && substitutions.size === 0) {
// already block, no declarations or writes to propagate back, no substitutions - can use node as is
return { body: createBlock(body.statements, /*multLine*/ true), returnValueProperty: undefined };
}
let returnValueProperty: string;
let ignoreReturns = false;
const statements = createNodeArray(isBlock(body) ? body.statements.slice(0) : [isStatement(body) ? body : createReturn(<Expression>body)]);
// rewrite body if either there are writes that should be propagated back via return statements or there are substitutions
if (writes || substitutions.size) {
if (hasWritesOrVariableDeclarations || substitutions.size) {
const rewrittenStatements = visitNodes(statements, visitor).slice();
if (writes && !hasReturn && isStatement(body)) {
if (hasWritesOrVariableDeclarations && !hasReturn && isStatement(body)) {
// add return at the end to propagate writes back in case if control flow falls out of the function body
// it is ok to know that range has at least one return since it we only allow unconditional returns
const assignments = getPropertyAssignmentsForWrites(writes);
const assignments = getPropertyAssignmentsForWritesAndVariableDeclarations(exposedVariableDeclarations, writes);
if (assignments.length === 1) {
rewrittenStatements.push(createReturn(assignments[0].name));
}
@@ -1117,8 +1211,8 @@ namespace ts.refactor.extractSymbol {
}
function visitor(node: Node): VisitResult<Node> {
if (!ignoreReturns && node.kind === SyntaxKind.ReturnStatement && writes) {
const assignments: ObjectLiteralElementLike[] = getPropertyAssignmentsForWrites(writes);
if (!ignoreReturns && node.kind === SyntaxKind.ReturnStatement && hasWritesOrVariableDeclarations) {
const assignments: ObjectLiteralElementLike[] = getPropertyAssignmentsForWritesAndVariableDeclarations(exposedVariableDeclarations, writes);
if ((<ReturnStatement>node).expression) {
if (!returnValueProperty) {
returnValueProperty = "__return";
@@ -1240,8 +1334,18 @@ namespace ts.refactor.extractSymbol {
}
}
function getPropertyAssignmentsForWrites(writes: ReadonlyArray<UsageEntry>): ShorthandPropertyAssignment[] {
return writes.map(w => createShorthandPropertyAssignment(w.symbol.name));
function getPropertyAssignmentsForWritesAndVariableDeclarations(
exposedVariableDeclarations: ReadonlyArray<ts.VariableDeclaration>,
writes: ReadonlyArray<UsageEntry>) {
const variableAssignments = map(exposedVariableDeclarations, v => createShorthandPropertyAssignment(v.symbol.name));
const writeAssignments = map(writes, w => createShorthandPropertyAssignment(w.symbol.name));
return variableAssignments === undefined
? writeAssignments
: writeAssignments === undefined
? variableAssignments
: variableAssignments.concat(writeAssignments);
}
function isReadonlyArray(v: any): v is ReadonlyArray<any> {
@@ -1287,6 +1391,7 @@ namespace ts.refactor.extractSymbol {
readonly usagesPerScope: ReadonlyArray<ScopeUsages>;
readonly functionErrorsPerScope: ReadonlyArray<ReadonlyArray<Diagnostic>>;
readonly constantErrorsPerScope: ReadonlyArray<ReadonlyArray<Diagnostic>>;
readonly exposedVariableDeclarations: ReadonlyArray<VariableDeclaration>;
}
function collectReadsAndWrites(
targetRange: TargetRange,
@@ -1301,7 +1406,10 @@ namespace ts.refactor.extractSymbol {
const substitutionsPerScope: Map<Node>[] = [];
const functionErrorsPerScope: Diagnostic[][] = [];
const constantErrorsPerScope: Diagnostic[][] = [];
const visibleDeclarationsInExtractedRange: Symbol[] = [];
const visibleDeclarationsInExtractedRange: NamedDeclaration[] = [];
const exposedVariableSymbolSet = createMap<true>(); // Key is symbol ID
const exposedVariableDeclarations: VariableDeclaration[] = [];
let firstExposedNonVariableDeclaration: NamedDeclaration | undefined = undefined;
const expression = !isReadonlyArray(targetRange.range)
? targetRange.range
@@ -1346,7 +1454,6 @@ namespace ts.refactor.extractSymbol {
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) ? first(targetRange.range) : targetRange.range;
const inGenericContext = isInGenericContext(unmodifiedNode);
@@ -1392,6 +1499,15 @@ namespace ts.refactor.extractSymbol {
Debug.assert(i === scopes.length);
}
// If there are any declarations in the extracted block that are used in the same enclosing
// lexical scope, we can't move the extraction "up" as those declarations will become unreachable
if (visibleDeclarationsInExtractedRange.length) {
const containingLexicalScopeOfExtraction = isBlockScope(scopes[0], scopes[0].parent)
? scopes[0]
: getEnclosingBlockScopeContainer(scopes[0]);
forEachChild(containingLexicalScopeOfExtraction, checkForUsedDeclarations);
}
for (let i = 0; i < scopes.length; i++) {
const scopeUsages = usagesPerScope[i];
// Special case: in the innermost scope, all usages are available.
@@ -1415,8 +1531,11 @@ namespace ts.refactor.extractSymbol {
}
});
if (hasWrite && !isReadonlyArray(targetRange.range) && isExpression(targetRange.range)) {
const diag = createDiagnosticForNode(targetRange.range, Messages.CannotCombineWritesAndReturns);
// If an expression was extracted, then there shouldn't have been any variable declarations.
Debug.assert(isReadonlyArray(targetRange.range) || exposedVariableDeclarations.length === 0);
if (hasWrite && !isReadonlyArray(targetRange.range)) {
const diag = createDiagnosticForNode(targetRange.range, Messages.CannotWriteInExpression);
functionErrorsPerScope[i].push(diag);
constantErrorsPerScope[i].push(diag);
}
@@ -1425,15 +1544,14 @@ namespace ts.refactor.extractSymbol {
functionErrorsPerScope[i].push(diag);
constantErrorsPerScope[i].push(diag);
}
else if (firstExposedNonVariableDeclaration) {
const diag = createDiagnosticForNode(firstExposedNonVariableDeclaration, Messages.CannotExtractExportedEntity);
functionErrorsPerScope[i].push(diag);
constantErrorsPerScope[i].push(diag);
}
}
// If there are any declarations in the extracted block that are used in the same enclosing
// lexical scope, we can't move the extraction "up" as those declarations will become unreachable
if (visibleDeclarationsInExtractedRange.length) {
forEachChild(containingLexicalScopeOfExtraction, checkForUsedDeclarations);
}
return { target, usagesPerScope, functionErrorsPerScope, constantErrorsPerScope };
return { target, usagesPerScope, functionErrorsPerScope, constantErrorsPerScope, exposedVariableDeclarations };
function hasTypeParameters(node: Node) {
return isDeclarationWithTypeParameters(node) &&
@@ -1472,7 +1590,7 @@ namespace ts.refactor.extractSymbol {
}
if (isDeclaration(node) && node.symbol) {
visibleDeclarationsInExtractedRange.push(node.symbol);
visibleDeclarationsInExtractedRange.push(node);
}
if (isAssignmentExpression(node)) {
@@ -1518,11 +1636,7 @@ namespace ts.refactor.extractSymbol {
}
function recordUsagebySymbol(identifier: Identifier, usage: Usage, isTypeName: boolean) {
// If the identifier is both a property name and its value, we're only interested in its value
// (since the name is a declaration and will be included in the extracted range).
const symbol = identifier.parent && isShorthandPropertyAssignment(identifier.parent) && identifier.parent.name === identifier
? checker.getShorthandAssignmentValueSymbol(identifier.parent)
: checker.getSymbolAtLocation(identifier);
const symbol = getSymbolReferencedByIdentifier(identifier);
if (!symbol) {
// cannot find symbol - do nothing
return undefined;
@@ -1606,20 +1720,39 @@ namespace ts.refactor.extractSymbol {
}
// Otherwise check and recurse.
const sym = checker.getSymbolAtLocation(node);
if (sym && visibleDeclarationsInExtractedRange.some(d => d === sym)) {
const diag = createDiagnosticForNode(node, Messages.CannotExtractExportedEntity);
for (const errors of functionErrorsPerScope) {
errors.push(diag);
const sym = isIdentifier(node)
? getSymbolReferencedByIdentifier(node)
: checker.getSymbolAtLocation(node);
if (sym) {
const decl = find(visibleDeclarationsInExtractedRange, d => d.symbol === sym);
if (decl) {
if (isVariableDeclaration(decl)) {
const idString = decl.symbol.id.toString();
if (!exposedVariableSymbolSet.has(idString)) {
exposedVariableDeclarations.push(decl);
exposedVariableSymbolSet.set(idString, true);
}
}
else {
// CONSIDER: this includes binding elements, which we could
// expose in the same way as variables.
firstExposedNonVariableDeclaration = firstExposedNonVariableDeclaration || decl;
}
}
for (const errors of constantErrorsPerScope) {
errors.push(diag);
}
return true;
}
else {
forEachChild(node, checkForUsedDeclarations);
}
forEachChild(node, checkForUsedDeclarations);
}
/**
* Return the symbol referenced by an identifier (even if it declares a different symbol).
*/
function getSymbolReferencedByIdentifier(identifier: Identifier) {
// If the identifier is both a property name and its value, we're only interested in its value
// (since the name is a declaration and will be included in the extracted range).
return identifier.parent && isShorthandPropertyAssignment(identifier.parent) && identifier.parent.name === identifier
? checker.getShorthandAssignmentValueSymbol(identifier.parent)
: checker.getSymbolAtLocation(identifier);
}
function tryReplaceWithQualifiedNameOrPropertyAccess(symbol: Symbol, scopeDecl: Node, isTypeNode: boolean): PropertyAccessExpression | EntityName {
@@ -17,7 +17,7 @@ namespace A {
class C {
a() {
let z = 1;
var __return: any;
let __return;
({ __return, z } = this./*RENAME*/newMethod(z));
return __return;
}
@@ -36,7 +36,7 @@ namespace A {
class C {
a() {
let z = 1;
var __return: any;
let __return;
({ __return, z } = /*RENAME*/newFunction(z));
return __return;
}
@@ -55,7 +55,7 @@ namespace A {
class C {
a() {
let z = 1;
var __return: any;
let __return;
({ __return, y, z } = /*RENAME*/newFunction(y, z));
return __return;
}
@@ -20,7 +20,7 @@ namespace A {
b() {}
a() {
let z = 1;
var __return: any;
let __return;
({ __return, z } = this./*RENAME*/newMethod(z));
return __return;
}
@@ -43,7 +43,7 @@ namespace A {
function a() {
let a = 1;
var __return: any;
let __return;
({ __return, a } = /*RENAME*/newFunction(a));
return __return;
}
@@ -65,7 +65,7 @@ namespace A {
function a() {
let a = 1;
var __return: any;
let __return;
({ __return, a } = /*RENAME*/newFunction(a));
return __return;
}
@@ -87,7 +87,7 @@ namespace A {
function a() {
let a = 1;
var __return: any;
let __return;
({ __return, a } = /*RENAME*/newFunction(x, a));
return __return;
}
@@ -49,7 +49,7 @@ namespace A {
function a() {
let a = 1;
var __return: any;
let __return;
({ __return, a } = /*RENAME*/newFunction(a));
return __return;
}
@@ -73,7 +73,7 @@ namespace A {
function a() {
let a = 1;
var __return: any;
let __return;
({ __return, a } = /*RENAME*/newFunction(a));
return __return;
}
@@ -97,7 +97,7 @@ namespace A {
function a() {
let a = 1;
var __return: any;
let __return;
({ __return, a } = /*RENAME*/newFunction(x, a));
return __return;
}
@@ -0,0 +1,14 @@
// ==ORIGINAL==
/*[#|*/const x = 1;/*|]*/
x;
// ==SCOPE::Extract to function in global scope==
const x = /*RENAME*/newFunction();
x;
function newFunction() {
const x = 1;
return x;
}
@@ -0,0 +1,14 @@
// ==ORIGINAL==
/*[#|*/const x = 1;/*|]*/
x;
// ==SCOPE::Extract to function in global scope==
const x = /*RENAME*/newFunction();
x;
function newFunction() {
const x = 1;
return x;
}
@@ -0,0 +1,14 @@
// ==ORIGINAL==
/*[#|*/const x: number = 1;/*|]*/
x;
// ==SCOPE::Extract to function in global scope==
const x: number = /*RENAME*/newFunction();
x;
function newFunction() {
const x: number = 1;
return x;
}
@@ -0,0 +1,14 @@
// ==ORIGINAL==
/*[#|*/const x: number = 1;/*|]*/
x; x;
// ==SCOPE::Extract to function in global scope==
const x: number = /*RENAME*/newFunction();
x; x;
function newFunction() {
const x: number = 1;
return x;
}
@@ -0,0 +1,16 @@
// ==ORIGINAL==
/*[#|*/var x = 1;
var x = 2;/*|]*/
x;
// ==SCOPE::Extract to function in global scope==
var x = /*RENAME*/newFunction();
x;
function newFunction() {
var x = 1;
var x = 2;
return x;
}
@@ -0,0 +1,16 @@
// ==ORIGINAL==
/*[#|*/var x = 1;
var x = 2;/*|]*/
x;
// ==SCOPE::Extract to function in global scope==
var x = /*RENAME*/newFunction();
x;
function newFunction() {
var x = 1;
var x = 2;
return x;
}
@@ -0,0 +1,14 @@
// ==ORIGINAL==
/*[#|*/let x = 1;/*|]*/
x;
// ==SCOPE::Extract to function in global scope==
let x = /*RENAME*/newFunction();
x;
function newFunction() {
let x = 1;
return x;
}
@@ -0,0 +1,14 @@
// ==ORIGINAL==
/*[#|*/let x = 1;/*|]*/
x;
// ==SCOPE::Extract to function in global scope==
let x = /*RENAME*/newFunction();
x;
function newFunction() {
let x = 1;
return x;
}
@@ -0,0 +1,14 @@
// ==ORIGINAL==
/*[#|*/let x: number = 1;/*|]*/
x;
// ==SCOPE::Extract to function in global scope==
let x: number = /*RENAME*/newFunction();
x;
function newFunction() {
let x: number = 1;
return x;
}
@@ -0,0 +1,14 @@
// ==ORIGINAL==
/*[#|*/const x = 1, y: string = "a";/*|]*/
x; y;
// ==SCOPE::Extract to function in global scope==
const { x, y }: { x: number; y: string; } = /*RENAME*/newFunction();
x; y;
function newFunction() {
const x = 1, y: string = "a";
return { x, y };
}
@@ -0,0 +1,16 @@
// ==ORIGINAL==
/*[#|*/const x = 1, y = "a";
const z = 3;/*|]*/
x; y; z;
// ==SCOPE::Extract to function in global scope==
const { x, y, z } = /*RENAME*/newFunction();
x; y; z;
function newFunction() {
const x = 1, y = "a";
const z = 3;
return { x, y, z };
}
@@ -0,0 +1,16 @@
// ==ORIGINAL==
/*[#|*/const x = 1, y = "a";
const z = 3;/*|]*/
x; y; z;
// ==SCOPE::Extract to function in global scope==
const { x, y, z } = /*RENAME*/newFunction();
x; y; z;
function newFunction() {
const x = 1, y = "a";
const z = 3;
return { x, y, z };
}
@@ -0,0 +1,16 @@
// ==ORIGINAL==
/*[#|*/const x = 1, y: string = "a";
let z = 3;/*|]*/
x; y; z;
// ==SCOPE::Extract to function in global scope==
var { x, y, z }: { x: number; y: string; z: number; } = /*RENAME*/newFunction();
x; y; z;
function newFunction() {
const x = 1, y: string = "a";
let z = 3;
return { x, y, z };
}
@@ -0,0 +1,27 @@
// ==ORIGINAL==
function f() {
/*[#|*/let x;/*|]*/
return { x };
}
// ==SCOPE::Extract to inner function in function 'f'==
function f() {
let x = /*RENAME*/newFunction();
return { x };
function newFunction() {
let x;
return x;
}
}
// ==SCOPE::Extract to function in global scope==
function f() {
let x = /*RENAME*/newFunction();
return { x };
}
function newFunction() {
let x;
return x;
}
@@ -0,0 +1,27 @@
// ==ORIGINAL==
function f() {
/*[#|*/let x;/*|]*/
return { x };
}
// ==SCOPE::Extract to inner function in function 'f'==
function f() {
let x = /*RENAME*/newFunction();
return { x };
function newFunction() {
let x;
return x;
}
}
// ==SCOPE::Extract to function in global scope==
function f() {
let x = /*RENAME*/newFunction();
return { x };
}
function newFunction() {
let x;
return x;
}
@@ -0,0 +1,14 @@
// ==ORIGINAL==
/*[#|*/var x = 1;/*|]*/
x;
// ==SCOPE::Extract to function in global scope==
var x = /*RENAME*/newFunction();
x;
function newFunction() {
var x = 1;
return x;
}
@@ -0,0 +1,14 @@
// ==ORIGINAL==
/*[#|*/var x = 1;/*|]*/
x;
// ==SCOPE::Extract to function in global scope==
var x = /*RENAME*/newFunction();
x;
function newFunction() {
var x = 1;
return x;
}
@@ -0,0 +1,34 @@
// ==ORIGINAL==
function f() {
let a = 1;
/*[#|*/const x = 1;
a++;/*|]*/
a; x;
}
// ==SCOPE::Extract to inner function in function 'f'==
function f() {
let a = 1;
const x = /*RENAME*/newFunction();
a; x;
function newFunction() {
const x = 1;
a++;
return x;
}
}
// ==SCOPE::Extract to function in global scope==
function f() {
let a = 1;
let x;
({ x, a } = /*RENAME*/newFunction(a));
a; x;
}
function newFunction(a) {
const x = 1;
a++;
return { x, a };
}
@@ -0,0 +1,34 @@
// ==ORIGINAL==
function f() {
let a = 1;
/*[#|*/const x = 1;
a++;/*|]*/
a; x;
}
// ==SCOPE::Extract to inner function in function 'f'==
function f() {
let a = 1;
const x = /*RENAME*/newFunction();
a; x;
function newFunction() {
const x = 1;
a++;
return x;
}
}
// ==SCOPE::Extract to function in global scope==
function f() {
let a = 1;
let x;
({ x, a } = /*RENAME*/newFunction(a));
a; x;
}
function newFunction(a: number) {
const x = 1;
a++;
return { x, a };
}
@@ -0,0 +1,34 @@
// ==ORIGINAL==
function f() {
let a = 1;
/*[#|*/const x: number = 1;
a++;/*|]*/
a; x;
}
// ==SCOPE::Extract to inner function in function 'f'==
function f() {
let a = 1;
const x: number = /*RENAME*/newFunction();
a; x;
function newFunction() {
const x: number = 1;
a++;
return x;
}
}
// ==SCOPE::Extract to function in global scope==
function f() {
let a = 1;
let x: number | undefined;
({ x, a } = /*RENAME*/newFunction(a));
a; x;
}
function newFunction(a: number) {
const x: number = 1;
a++;
return { x, a };
}
@@ -0,0 +1,34 @@
// ==ORIGINAL==
function f() {
let a = 1;
/*[#|*/let x = 1;
a++;/*|]*/
a; x;
}
// ==SCOPE::Extract to inner function in function 'f'==
function f() {
let a = 1;
let x = /*RENAME*/newFunction();
a; x;
function newFunction() {
let x = 1;
a++;
return x;
}
}
// ==SCOPE::Extract to function in global scope==
function f() {
let a = 1;
let x;
({ x, a } = /*RENAME*/newFunction(a));
a; x;
}
function newFunction(a) {
let x = 1;
a++;
return { x, a };
}
@@ -0,0 +1,34 @@
// ==ORIGINAL==
function f() {
let a = 1;
/*[#|*/let x = 1;
a++;/*|]*/
a; x;
}
// ==SCOPE::Extract to inner function in function 'f'==
function f() {
let a = 1;
let x = /*RENAME*/newFunction();
a; x;
function newFunction() {
let x = 1;
a++;
return x;
}
}
// ==SCOPE::Extract to function in global scope==
function f() {
let a = 1;
let x;
({ x, a } = /*RENAME*/newFunction(a));
a; x;
}
function newFunction(a: number) {
let x = 1;
a++;
return { x, a };
}
@@ -0,0 +1,34 @@
// ==ORIGINAL==
function f() {
let a = 1;
/*[#|*/let x: number = 1;
a++;/*|]*/
a; x;
}
// ==SCOPE::Extract to inner function in function 'f'==
function f() {
let a = 1;
let x: number = /*RENAME*/newFunction();
a; x;
function newFunction() {
let x: number = 1;
a++;
return x;
}
}
// ==SCOPE::Extract to function in global scope==
function f() {
let a = 1;
let x: number | undefined;
({ x, a } = /*RENAME*/newFunction(a));
a; x;
}
function newFunction(a: number) {
let x: number = 1;
a++;
return { x, a };
}
@@ -0,0 +1,38 @@
// ==ORIGINAL==
function f() {
let a = 1;
/*[#|*/const x = 1;
let y = 2;
a++;/*|]*/
a; x; y;
}
// ==SCOPE::Extract to inner function in function 'f'==
function f() {
let a = 1;
var { x, y } = /*RENAME*/newFunction();
a; x; y;
function newFunction() {
const x = 1;
let y = 2;
a++;
return { x, y };
}
}
// ==SCOPE::Extract to function in global scope==
function f() {
let a = 1;
let x;
let y;
({ x, y, a } = /*RENAME*/newFunction(a));
a; x; y;
}
function newFunction(a) {
const x = 1;
let y = 2;
a++;
return { x, y, a };
}
@@ -0,0 +1,38 @@
// ==ORIGINAL==
function f() {
let a = 1;
/*[#|*/const x = 1;
let y = 2;
a++;/*|]*/
a; x; y;
}
// ==SCOPE::Extract to inner function in function 'f'==
function f() {
let a = 1;
var { x, y } = /*RENAME*/newFunction();
a; x; y;
function newFunction() {
const x = 1;
let y = 2;
a++;
return { x, y };
}
}
// ==SCOPE::Extract to function in global scope==
function f() {
let a = 1;
let x;
let y;
({ x, y, a } = /*RENAME*/newFunction(a));
a; x; y;
}
function newFunction(a: number) {
const x = 1;
let y = 2;
a++;
return { x, y, a };
}
@@ -0,0 +1,38 @@
// ==ORIGINAL==
function f() {
let a = 1;
/*[#|*/var x = 1;
let y = 2;
a++;/*|]*/
a; x; y;
}
// ==SCOPE::Extract to inner function in function 'f'==
function f() {
let a = 1;
var { x, y } = /*RENAME*/newFunction();
a; x; y;
function newFunction() {
var x = 1;
let y = 2;
a++;
return { x, y };
}
}
// ==SCOPE::Extract to function in global scope==
function f() {
let a = 1;
var x;
let y;
({ x, y, a } = /*RENAME*/newFunction(a));
a; x; y;
}
function newFunction(a) {
var x = 1;
let y = 2;
a++;
return { x, y, a };
}
@@ -0,0 +1,38 @@
// ==ORIGINAL==
function f() {
let a = 1;
/*[#|*/var x = 1;
let y = 2;
a++;/*|]*/
a; x; y;
}
// ==SCOPE::Extract to inner function in function 'f'==
function f() {
let a = 1;
var { x, y } = /*RENAME*/newFunction();
a; x; y;
function newFunction() {
var x = 1;
let y = 2;
a++;
return { x, y };
}
}
// ==SCOPE::Extract to function in global scope==
function f() {
let a = 1;
var x;
let y;
({ x, y, a } = /*RENAME*/newFunction(a));
a; x; y;
}
function newFunction(a: number) {
var x = 1;
let y = 2;
a++;
return { x, y, a };
}
@@ -0,0 +1,38 @@
// ==ORIGINAL==
function f() {
let a = 1;
/*[#|*/let x: number = 1;
let y = 2;
a++;/*|]*/
a; x; y;
}
// ==SCOPE::Extract to inner function in function 'f'==
function f() {
let a = 1;
let { x, y }: { x: number; y: number; } = /*RENAME*/newFunction();
a; x; y;
function newFunction() {
let x: number = 1;
let y = 2;
a++;
return { x, y };
}
}
// ==SCOPE::Extract to function in global scope==
function f() {
let a = 1;
let x: number | undefined;
let y;
({ x, y, a } = /*RENAME*/newFunction(a));
a; x; y;
}
function newFunction(a: number) {
let x: number = 1;
let y = 2;
a++;
return { x, y, a };
}
@@ -0,0 +1,42 @@
// ==ORIGINAL==
function f() {
let a = 1;
/*[#|*/let x: number | undefined = 1;
let y: undefined | number = 2;
let z: (undefined | number) = 3;
a++;/*|]*/
a; x; y; z;
}
// ==SCOPE::Extract to inner function in function 'f'==
function f() {
let a = 1;
let { x, y, z }: { x: number; y: number; z: number; } = /*RENAME*/newFunction();
a; x; y; z;
function newFunction() {
let x: number | undefined = 1;
let y: undefined | number = 2;
let z: (undefined | number) = 3;
a++;
return { x, y, z };
}
}
// ==SCOPE::Extract to function in global scope==
function f() {
let a = 1;
let x: number | undefined;
let y: undefined | number;
let z: (undefined | number);
({ x, y, z, a } = /*RENAME*/newFunction(a));
a; x; y; z;
}
function newFunction(a: number) {
let x: number | undefined = 1;
let y: undefined | number = 2;
let z: (undefined | number) = 3;
a++;
return { x, y, z, a };
}
@@ -0,0 +1,34 @@
// ==ORIGINAL==
function f() {
let a = 1;
/*[#|*/var x = 1;
a++;/*|]*/
a; x;
}
// ==SCOPE::Extract to inner function in function 'f'==
function f() {
let a = 1;
var x = /*RENAME*/newFunction();
a; x;
function newFunction() {
var x = 1;
a++;
return x;
}
}
// ==SCOPE::Extract to function in global scope==
function f() {
let a = 1;
var x;
({ x, a } = /*RENAME*/newFunction(a));
a; x;
}
function newFunction(a) {
var x = 1;
a++;
return { x, a };
}
@@ -0,0 +1,34 @@
// ==ORIGINAL==
function f() {
let a = 1;
/*[#|*/var x = 1;
a++;/*|]*/
a; x;
}
// ==SCOPE::Extract to inner function in function 'f'==
function f() {
let a = 1;
var x = /*RENAME*/newFunction();
a; x;
function newFunction() {
var x = 1;
a++;
return x;
}
}
// ==SCOPE::Extract to function in global scope==
function f() {
let a = 1;
var x;
({ x, a } = /*RENAME*/newFunction(a));
a; x;
}
function newFunction(a: number) {
var x = 1;
a++;
return { x, a };
}
+1 -1
View File
@@ -18,7 +18,7 @@ edit.applyRefactor({
newContent:
`function foo() {
var i = 10;
var __return: any;
let __return;
({ __return, i } = /*RENAME*/newFunction(i));
return __return;
}