mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-10 06:41:59 -06:00
Merge pull request #18979 from amcasey/DeepClone
Introduce getSynthesizedDeepClone
This commit is contained in:
commit
bada0095ed
@ -47,10 +47,15 @@ namespace ts {
|
||||
* Creates a shallow, memberwise clone of a node with no source map location.
|
||||
*/
|
||||
/* @internal */
|
||||
export function getSynthesizedClone<T extends Node>(node: T | undefined): T {
|
||||
export function getSynthesizedClone<T extends Node>(node: T | undefined): T | undefined {
|
||||
// We don't use "clone" from core.ts here, as we need to preserve the prototype chain of
|
||||
// the original node. We also need to exclude specific properties and only include own-
|
||||
// properties (to skip members already defined on the shared prototype).
|
||||
|
||||
if (node === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const clone = <T>createSynthesizedNode(node.kind);
|
||||
clone.flags |= node.flags;
|
||||
setOriginalNode(clone, node);
|
||||
|
||||
@ -1088,7 +1088,7 @@ namespace ts.refactor.extractSymbol {
|
||||
}
|
||||
}
|
||||
|
||||
function transformFunctionBody(body: Node, writes: ReadonlyArray<UsageEntry>, substitutions: ReadonlyMap<() => Node>, hasReturn: boolean): { body: Block, returnValueProperty: string } {
|
||||
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
|
||||
return { body: createBlock(body.statements, /*multLine*/ true), returnValueProperty: undefined };
|
||||
@ -1136,21 +1136,21 @@ namespace ts.refactor.extractSymbol {
|
||||
const oldIgnoreReturns = ignoreReturns;
|
||||
ignoreReturns = ignoreReturns || isFunctionLikeDeclaration(node) || isClassLike(node);
|
||||
const substitution = substitutions.get(getNodeId(node).toString());
|
||||
const result = substitution ? substitution() : visitEachChild(node, visitor, nullTransformationContext);
|
||||
const result = substitution ? getSynthesizedDeepClone(substitution) : visitEachChild(node, visitor, nullTransformationContext);
|
||||
ignoreReturns = oldIgnoreReturns;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function transformConstantInitializer(initializer: Expression, substitutions: ReadonlyMap<() => Node>): Expression {
|
||||
function transformConstantInitializer(initializer: Expression, substitutions: ReadonlyMap<Node>): Expression {
|
||||
return substitutions.size
|
||||
? visitor(initializer) as Expression
|
||||
: initializer;
|
||||
|
||||
function visitor(node: Node): VisitResult<Node> {
|
||||
const substitution = substitutions.get(getNodeId(node).toString());
|
||||
return substitution ? substitution() : visitEachChild(node, visitor, nullTransformationContext);
|
||||
return substitution ? getSynthesizedDeepClone(substitution) : visitEachChild(node, visitor, nullTransformationContext);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1279,7 +1279,7 @@ namespace ts.refactor.extractSymbol {
|
||||
interface ScopeUsages {
|
||||
readonly usages: Map<UsageEntry>;
|
||||
readonly typeParameterUsages: Map<TypeParameter>; // Key is type ID
|
||||
readonly substitutions: Map<() => Node>;
|
||||
readonly substitutions: Map<Node>;
|
||||
}
|
||||
|
||||
interface ReadsAndWrites {
|
||||
@ -1298,7 +1298,7 @@ namespace ts.refactor.extractSymbol {
|
||||
|
||||
const allTypeParameterUsages = createMap<TypeParameter>(); // Key is type ID
|
||||
const usagesPerScope: ScopeUsages[] = [];
|
||||
const substitutionsPerScope: Map<() => Node>[] = [];
|
||||
const substitutionsPerScope: Map<Node>[] = [];
|
||||
const functionErrorsPerScope: Diagnostic[][] = [];
|
||||
const constantErrorsPerScope: Diagnostic[][] = [];
|
||||
const visibleDeclarationsInExtractedRange: Symbol[] = [];
|
||||
@ -1322,8 +1322,8 @@ namespace ts.refactor.extractSymbol {
|
||||
|
||||
// initialize results
|
||||
for (const scope of scopes) {
|
||||
usagesPerScope.push({ usages: createMap<UsageEntry>(), typeParameterUsages: createMap<TypeParameter>(), substitutions: createMap<() => Expression>() });
|
||||
substitutionsPerScope.push(createMap<() => Expression>());
|
||||
usagesPerScope.push({ usages: createMap<UsageEntry>(), typeParameterUsages: createMap<TypeParameter>(), substitutions: createMap<Expression>() });
|
||||
substitutionsPerScope.push(createMap<Expression>());
|
||||
|
||||
functionErrorsPerScope.push(
|
||||
isFunctionLikeDeclaration(scope) && scope.kind !== SyntaxKind.FunctionDeclaration
|
||||
@ -1622,20 +1622,20 @@ namespace ts.refactor.extractSymbol {
|
||||
}
|
||||
}
|
||||
|
||||
function tryReplaceWithQualifiedNameOrPropertyAccess(symbol: Symbol, scopeDecl: Node, isTypeNode: boolean): () => (PropertyAccessExpression | EntityName) {
|
||||
function tryReplaceWithQualifiedNameOrPropertyAccess(symbol: Symbol, scopeDecl: Node, isTypeNode: boolean): PropertyAccessExpression | EntityName {
|
||||
if (!symbol) {
|
||||
return undefined;
|
||||
}
|
||||
if (symbol.getDeclarations().some(d => d.parent === scopeDecl)) {
|
||||
return () => createIdentifier(symbol.name);
|
||||
return createIdentifier(symbol.name);
|
||||
}
|
||||
const prefix = tryReplaceWithQualifiedNameOrPropertyAccess(symbol.parent, scopeDecl, isTypeNode);
|
||||
if (prefix === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return isTypeNode
|
||||
? () => createQualifiedName(<EntityName>prefix(), createIdentifier(symbol.name))
|
||||
: () => createPropertyAccess(<Expression>prefix(), symbol.name);
|
||||
? createQualifiedName(<EntityName>prefix, createIdentifier(symbol.name))
|
||||
: createPropertyAccess(<Expression>prefix, symbol.name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1334,4 +1334,33 @@ namespace ts {
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a deep, memberwise clone of a node with no source map location.
|
||||
*
|
||||
* WARNING: This is an expensive operation and is only intended to be used in refactorings
|
||||
* and code fixes (because those are triggered by explicit user actions).
|
||||
*/
|
||||
export function getSynthesizedDeepClone<T extends Node>(node: T | undefined): T | undefined {
|
||||
if (node === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const visited = visitEachChild(node, getSynthesizedDeepClone, nullTransformationContext);
|
||||
if (visited === node) {
|
||||
// This only happens for leaf nodes - internal nodes always see their children change.
|
||||
const clone = getSynthesizedClone(node);
|
||||
clone.pos = node.pos;
|
||||
clone.end = node.end;
|
||||
return clone;
|
||||
}
|
||||
|
||||
// PERF: As an optimization, rather than calling getSynthesizedClone, we'll update
|
||||
// the new node created by visitEachChild with the extra changes getSynthesizedClone
|
||||
// would have made.
|
||||
|
||||
visited.parent = undefined;
|
||||
|
||||
return visited;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user