-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Transformer with NodeFactory.update* removes parent/symbol information from AST branch #43407
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
Comments
I don't believe parent preservation is an invariant of tree transforms. @rbuckton can clarify |
The TypeScript AST is essentially immutable. What happens when you call const expr1 = ts.factory.createNumericLiteral(1);
const returnStmt1 = ts.factory.createReturnStatement(expr1);
const returnStmt2 = ts.factory.updateReturnStatement(returnStmt1, expr1); // same expression
console.log(returnStmt1 === returnStmt2); // true
const expr2 = ts.factory.createNumericLiteral(2);
const returnStmt3 = ts.factory.updateReturnStatement(returnStmt1, expr2); // different expression
console.log(returnStmt1 === returnStmt3); // false Whenever we create an "updated" node, we set a property on that node called We only maintain parents and symbols on "parse tree" nodes (i.e., nodes created by the parser). If you pass an "updated" node into a checker API, we will automatically follow all
If you create a purely synthetic node and want it to participate in the checker API, you can explicitly set the If you want to find the "parse tree" node for a given synthetic node, you can call Finally, we don't change or set
As a result, we caution anyone from relying on |
I find this part of your example surprising however: console.log(typeChecker.getSymbolAtLocation(updatedFunctionDecl) !== undefined); // False That should have worked just fine, because the entrypoint for getSymbolAtLocation: nodeIn => {
const node = getParseTreeNode(nodeIn);
// set ignoreErrors: true because any lookups invoked by the API shouldn't cause any new errors
return node ? getSymbolAtLocation(node, /*ignoreErrors*/ true) : undefined;
}, So, One final note: You shouldn't rely on const transformerFactory: ts.TransformerFactory<ts.SourceFile> = context => {
const { factory } = context;
function visit(node: ts.Node): ts.Node {
// ONLY update return statement
if (ts.isReturnStatement(node)) {
return factory.updateReturnStatement(node, factory.createFalse());
}
return ts.visitEachChild(node, visit, context)
}
return (sourceFile: ts.SourceFile) => ts.visitNode(sourceFile, visit);
} |
@rbuckton Thanks for the clear answer! I checked out Also had a quick look at why the symbol is undefined, but it also is undefined for the original |
@rbuckton Can you clarify why you caution against using if (
isTypeLiteral(node) &&
!node.members.length &&
isIntersectionOrUnion(node.parent || ts.getParseTreeNode(node)?.parent)
) {
return undefined;
} Should I instead visit the intersection/union parent and then remove children that are empty type literals? I don't actually know how to do that: if (isIntersectionOrUnion(node)) {
node.types.forEach(() => ???)
// or
ts.visitEachChild(...) // Doesn't appear that the nodes contained in the intersection/union are actually children -- they don't get visited.
} |
Bug Report
I am not sure if this is intended behavior or a bug, but it is surprising: after updating a node in the AST using the standard TS transformer API, the node loses its parent/symbol information. Also all parents of this node seem to lose their parent and symbol information, even if they themselves were not transformed.
🔎 Search Terms
NodeFactory visitNode visitEachChild transformer update parent symbol sourceFile undefined compiler API
🕗 Version & Regression Information
⏯ Playground Link
Playground link with relevant code
💻 Code
example.ts
transformation.ts
🙁 Actual behavior
After updating the return statement, all nodes in that branch (traversing up to root) of the AST have their parent/symbol information removed.
🙂 Expected behavior
I would expect the AST nodes to still have their parent nodes set, as they are still in an AST. Initially I also expected only the updated node to be changed and not to affect parent/symbol of its parent nodes - though I am no longer sure about that one.
The text was updated successfully, but these errors were encountered: