Skip to content

Rename transform-relevant subtree facts with what they actually should track #1277

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

Merged
merged 1 commit into from
Jun 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 16 additions & 19 deletions internal/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -1999,10 +1999,8 @@ func (node *Token) Clone(f NodeFactoryCoercible) *Node {

func (node *Token) computeSubtreeFacts() SubtreeFacts {
switch node.Kind {
case KindAsyncKeyword:
return SubtreeContainsES2017 | SubtreeContainsES2018
case KindUsingKeyword:
return SubtreeContainsESNext
return SubtreeContainsUsing
case KindPublicKeyword,
KindPrivateKeyword,
KindProtectedKeyword,
Expand All @@ -2029,11 +2027,11 @@ func (node *Token) computeSubtreeFacts() SubtreeFacts {
case KindAccessorKeyword:
return SubtreeContainsClassFields
case KindAsteriskAsteriskToken, KindAsteriskAsteriskEqualsToken:
return SubtreeContainsES2016
return SubtreeContainsExponentiationOperator
case KindQuestionQuestionToken, KindQuestionDotToken:
return SubtreeContainsES2020
return SubtreeContainsOptionalChaining
case KindQuestionQuestionEqualsToken, KindBarBarEqualsToken, KindAmpersandAmpersandEqualsToken:
return SubtreeContainsES2021
return SubtreeContainsLogicalAssignments
}
return SubtreeFactsNone
}
Expand Down Expand Up @@ -2930,7 +2928,7 @@ func (node *CatchClause) computeSubtreeFacts() SubtreeFacts {
res := propagateSubtreeFacts(node.VariableDeclaration) |
propagateSubtreeFacts(node.Block)
if node.VariableDeclaration == nil {
res |= SubtreeContainsES2019
res |= SubtreeContainsMissingCatchClauseVariable
}
return res
}
Expand Down Expand Up @@ -3224,7 +3222,7 @@ func (node *VariableDeclarationList) Clone(f NodeFactoryCoercible) *Node {

func (node *VariableDeclarationList) computeSubtreeFacts() SubtreeFacts {
return propagateNodeListSubtreeFacts(node.Declarations, propagateSubtreeFacts) |
core.IfElse(node.Flags&NodeFlagsUsing != 0, SubtreeContainsESNext, SubtreeFactsNone)
core.IfElse(node.Flags&NodeFlagsUsing != 0, SubtreeContainsUsing, SubtreeFactsNone)
}

func (node *VariableDeclarationList) propagateSubtreeFacts() SubtreeFacts {
Expand Down Expand Up @@ -3516,8 +3514,8 @@ func (node *FunctionDeclaration) computeSubtreeFacts() SubtreeFacts {
propagateNodeListSubtreeFacts(node.Parameters, propagateSubtreeFacts) |
propagateEraseableSyntaxSubtreeFacts(node.Type) |
propagateSubtreeFacts(node.Body) |
core.IfElse(isAsync && isGenerator, SubtreeContainsES2018, SubtreeFactsNone) |
core.IfElse(isAsync && !isGenerator, SubtreeContainsES2017, SubtreeFactsNone)
core.IfElse(isAsync && isGenerator, SubtreeContainsForAwaitOrAsyncGenerator, SubtreeFactsNone) |
core.IfElse(isAsync && !isGenerator, SubtreeContainsAnyAwait, SubtreeFactsNone)
}
}

Expand Down Expand Up @@ -5219,8 +5217,8 @@ func (node *MethodDeclaration) computeSubtreeFacts() SubtreeFacts {
propagateNodeListSubtreeFacts(node.Parameters, propagateSubtreeFacts) |
propagateSubtreeFacts(node.Body) |
propagateEraseableSyntaxSubtreeFacts(node.Type) |
core.IfElse(isAsync && isGenerator, SubtreeContainsES2018, SubtreeFactsNone) |
core.IfElse(isAsync && !isGenerator, SubtreeContainsES2017, SubtreeFactsNone)
core.IfElse(isAsync && isGenerator, SubtreeContainsForAwaitOrAsyncGenerator, SubtreeFactsNone) |
core.IfElse(isAsync && !isGenerator, SubtreeContainsAnyAwait, SubtreeFactsNone)
}
}

Expand Down Expand Up @@ -5524,7 +5522,7 @@ func (node *BigIntLiteral) Clone(f NodeFactoryCoercible) *Node {
}

func (node *BigIntLiteral) computeSubtreeFacts() SubtreeFacts {
return SubtreeContainsES2020
return SubtreeFactsNone // `bigint` is not downleveled in any way
}

func IsBigIntLiteral(node *Node) bool {
Expand Down Expand Up @@ -5802,7 +5800,7 @@ func (node *ArrowFunction) computeSubtreeFacts() SubtreeFacts {
propagateNodeListSubtreeFacts(node.Parameters, propagateSubtreeFacts) |
propagateEraseableSyntaxSubtreeFacts(node.Type) |
propagateSubtreeFacts(node.Body) |
core.IfElse(node.ModifierFlags()&ModifierFlagsAsync != 0, SubtreeContainsES2017, SubtreeFactsNone)
core.IfElse(node.ModifierFlags()&ModifierFlagsAsync != 0, SubtreeContainsAnyAwait, SubtreeFactsNone)
}

func (node *ArrowFunction) propagateSubtreeFacts() SubtreeFacts {
Expand Down Expand Up @@ -5872,8 +5870,8 @@ func (node *FunctionExpression) computeSubtreeFacts() SubtreeFacts {
propagateNodeListSubtreeFacts(node.Parameters, propagateSubtreeFacts) |
propagateEraseableSyntaxSubtreeFacts(node.Type) |
propagateSubtreeFacts(node.Body) |
core.IfElse(isAsync && isGenerator, SubtreeContainsES2018, SubtreeFactsNone) |
core.IfElse(isAsync && !isGenerator, SubtreeContainsES2017, SubtreeFactsNone)
core.IfElse(isAsync && isGenerator, SubtreeContainsForAwaitOrAsyncGenerator, SubtreeFactsNone) |
core.IfElse(isAsync && !isGenerator, SubtreeContainsAnyAwait, SubtreeFactsNone)
}

func (node *FunctionExpression) propagateSubtreeFacts() SubtreeFacts {
Expand Down Expand Up @@ -6283,8 +6281,7 @@ func (node *MetaProperty) Name() *DeclarationName {
}

func (node *MetaProperty) computeSubtreeFacts() SubtreeFacts {
return propagateSubtreeFacts(node.name) |
core.IfElse(node.KeywordToken == KindImportKeyword, SubtreeContainsES2020, SubtreeFactsNone)
return propagateSubtreeFacts(node.name) // `import.meta` is not downleveled in any way
}

func IsMetaProperty(node *Node) bool {
Expand Down Expand Up @@ -6943,7 +6940,7 @@ func (node *AwaitExpression) Clone(f NodeFactoryCoercible) *Node {
}

func (node *AwaitExpression) computeSubtreeFacts() SubtreeFacts {
return propagateSubtreeFacts(node.Expression) | SubtreeContainsES2017 | SubtreeContainsES2018 | SubtreeContainsAwait
return propagateSubtreeFacts(node.Expression) | SubtreeContainsAwait
}

func IsAwaitExpression(node *Node) bool {
Expand Down
33 changes: 24 additions & 9 deletions internal/ast/subtreefacts.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,22 @@ type SubtreeFacts int32

const (
// Facts
// - Flags used to indicate that a node or subtree contains syntax specific to a particular ECMAScript variant.
// - Flags used to indicate that a node or subtree contains syntax relevant to a specific transform

SubtreeContainsTypeScript SubtreeFacts = 1 << iota
SubtreeContainsJsx
SubtreeContainsESNext
SubtreeContainsES2022
SubtreeContainsES2021
SubtreeContainsES2020
SubtreeContainsES2019
SubtreeContainsES2018
SubtreeContainsES2017
SubtreeContainsES2016
SubtreeContainsESDecorators
SubtreeContainsUsing
SubtreeContainsClassStaticBlocks
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this list exhaustive all the way down to ES6?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe so. I double checked what our transforms cover against the list of accepted proposals from each year. The only thing I'm still a bit undecided on split-wise is if I really want to split class static blocks out from class fields - they're combined into one transform (along with the legacy class fields transform) in strada. Some decorator things compile to class static blocks, though, so it has the potential to save some node traversal on classes-which-use-decorators-but-not-fields by being split out. They're also separate tc39 proposals, even though they got accepted in the same year... eh. I'll finalize that one way or another when I put up the PR for it - this split is fine for now. Class fields and async transforms are probably going to be the last two I post, given their size - honestly I forgot core async wasn't es6 and so was still in the wheelhouse of needing transformation logic when I started working on these.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just surprised that it's such a short list.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eh, some end up in the module transforms (like import attributes) and some just don't get downlevel support due to complexity (like bigint), but most new es features end up just being lib updates and not new syntax. 🤷‍♂️

SubtreeContainsESClassFields
SubtreeContainsLogicalAssignments
SubtreeContainsNullishCoalescing
SubtreeContainsOptionalChaining
SubtreeContainsMissingCatchClauseVariable
SubtreeContainsESObjectRestOrSpread
SubtreeContainsForAwaitOrAsyncGenerator
SubtreeContainsAnyAwait
SubtreeContainsExponentiationOperator

// Markers
// - Flags used to indicate that a node or subtree contains a particular kind of syntax.
Expand All @@ -37,6 +41,17 @@ const (
SubtreeFactsComputed // NOTE: This should always be last
SubtreeFactsNone SubtreeFacts = 0

// Aliases (unused, for documentation purposes only - correspond to combinations in transformers/estransforms/definitions.go)

SubtreeContainsESNext = SubtreeContainsESDecorators | SubtreeContainsUsing
SubtreeContainsES2022 = SubtreeContainsClassStaticBlocks | SubtreeContainsESClassFields
SubtreeContainsES2021 = SubtreeContainsLogicalAssignments
SubtreeContainsES2020 = SubtreeContainsNullishCoalescing | SubtreeContainsOptionalChaining
SubtreeContainsES2019 = SubtreeContainsMissingCatchClauseVariable
SubtreeContainsES2018 = SubtreeContainsESObjectRestOrSpread | SubtreeContainsForAwaitOrAsyncGenerator
SubtreeContainsES2017 = SubtreeContainsAnyAwait
SubtreeContainsES2016 = SubtreeContainsExponentiationOperator

// Scope Exclusions
// - Bitmasks that exclude flags from propagating out of a specific context
// into the subtree flags of their container.
Expand Down
2 changes: 1 addition & 1 deletion internal/transformers/estransforms/optionalcatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type optionalCatchTransformer struct {
}

func (ch *optionalCatchTransformer) visit(node *ast.Node) *ast.Node {
if node.SubtreeFacts()&ast.SubtreeContainsES2019 == 0 {
if node.SubtreeFacts()&ast.SubtreeContainsMissingCatchClauseVariable == 0 {
return node
}
switch node.Kind {
Expand Down
2 changes: 1 addition & 1 deletion internal/transformers/estransforms/using.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const (
)

func (tx *usingDeclarationTransformer) visit(node *ast.Node) *ast.Node {
if node.SubtreeFacts()&ast.SubtreeContainsESNext == 0 {
if node.SubtreeFacts()&ast.SubtreeContainsUsing == 0 {
return node
}

Expand Down