mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
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:
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+14
@@ -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;
|
||||
}
|
||||
+14
@@ -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;
|
||||
}
|
||||
+14
@@ -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;
|
||||
}
|
||||
+14
@@ -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;
|
||||
}
|
||||
+16
@@ -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;
|
||||
}
|
||||
+16
@@ -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;
|
||||
}
|
||||
+14
@@ -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;
|
||||
}
|
||||
+14
@@ -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;
|
||||
}
|
||||
+14
@@ -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;
|
||||
}
|
||||
+14
@@ -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 };
|
||||
}
|
||||
+16
@@ -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 };
|
||||
}
|
||||
+16
@@ -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 };
|
||||
}
|
||||
+16
@@ -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 };
|
||||
}
|
||||
+27
@@ -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;
|
||||
}
|
||||
+27
@@ -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;
|
||||
}
|
||||
+34
@@ -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 };
|
||||
}
|
||||
+34
@@ -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 };
|
||||
}
|
||||
+34
@@ -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 };
|
||||
}
|
||||
+34
@@ -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 };
|
||||
}
|
||||
+34
@@ -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 };
|
||||
}
|
||||
+34
@@ -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 };
|
||||
}
|
||||
+38
@@ -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 };
|
||||
}
|
||||
+38
@@ -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 };
|
||||
}
|
||||
+38
@@ -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 };
|
||||
}
|
||||
+38
@@ -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 };
|
||||
}
|
||||
+38
@@ -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 };
|
||||
}
|
||||
+42
@@ -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 };
|
||||
}
|
||||
+34
@@ -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 };
|
||||
}
|
||||
+34
@@ -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 };
|
||||
}
|
||||
@@ -18,7 +18,7 @@ edit.applyRefactor({
|
||||
newContent:
|
||||
`function foo() {
|
||||
var i = 10;
|
||||
var __return: any;
|
||||
let __return;
|
||||
({ __return, i } = /*RENAME*/newFunction(i));
|
||||
return __return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user