-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Introduce getSynthesizedDeepClone #18979
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Apparently, this is a public API change. What's the correct way to handle that? |
If it's desirable, just accept the new API baselines. |
Or mark it |
@Andy-MS's right - it was an oversight on my part. |
src/compiler/factory.ts
Outdated
/* @internal */ | ||
export function getSynthesizedDeepClone<T extends Node>(node: T | undefined): T | undefined { | ||
return node | ||
? getSynthesizedClone(visitEachChild(node, child => getSynthesizedDeepClone(child), nullTransformationContext)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to write child => getSynthesizedDeepClone(child)
when just getSynthesizedDeepClone
would do.
src/compiler/factory.ts
Outdated
*/ | ||
/* @internal */ | ||
export function getSynthesizedDeepClone<T extends Node>(node: T | undefined): T | undefined { | ||
return node |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This whole operation seems somewhat expensive. The call to visitEachChild
will likely resolve in a call to one of the updateX
functions in factory.ts, which in turn calls updateNode
, which in turn sets the original
pointer on the new node, clones the original node's emitNode
, and sets the pos
and end
of the new node. Then getSynthesizedClone
discards the new object for a fresh one.
If we only need this for entity-name-like expressions, could we instead write a more specific version that doesn't do all of this extra work that is just thrown out? See serializeQualifiedNameAsExpression
in ts.ts for a function that already does this (albeit with some extra options you don't need).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that's it's overkill for this application. It was actually added for #18997 and this was a proof-of-concept application. If there's a way to make this less expensive, I'd be happy to rework it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since visitEachChild
guarantees it returns a new node if any of the children change, you can use that to ensure the result is a clone by doing this instead:
function getSynthesizedDeepClone<T extends Node>(node: T | undefined): T | undefined {
const visited = visitEachChild(node, getSynthesizedDeepClone, nullTransformationContext);
if (visited === node) return getSynthesizedClone(node);
visited.pos = -1;
visited.end = -1;
visited.parent = undefined;
return visited;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, consider moving this to services so that it isn't used unintentionally in compiler.
@rbuckton I believe I've addressed your feedback. |
@rbuckton - I believe most/all callers will want to copy trivia. Does that mean deep-clone should actually be built on getMutableClone, rather than getSynthesizedClone? |
I'd rather not use |
@rbuckton Could I just refrain from clearing |
As of this PR, the positions don't make a difference. I'll add a test once #18931 is merged. |
@rbuckton signed off offline. |
During refactoring, it seems quite common to require that a node appear in more than one place in the rewritten tree.