Skip to content

Conversation

a-tarasyuk
Copy link
Contributor

Fixes #1632

@a-tarasyuk a-tarasyuk force-pushed the fix/1632 branch 2 times, most recently from 798d12b to c5a62a9 Compare August 27, 2025 13:15
@a-tarasyuk a-tarasyuk marked this pull request as draft August 27, 2025 13:15
@a-tarasyuk a-tarasyuk force-pushed the fix/1632 branch 2 times, most recently from 441cca9 to ded3fa0 Compare August 27, 2025 18:42
@a-tarasyuk a-tarasyuk marked this pull request as ready for review August 27, 2025 19:59
@a-tarasyuk a-tarasyuk requested a review from jakebailey August 27, 2025 21:10
@a-tarasyuk a-tarasyuk requested a review from jakebailey August 29, 2025 12:16
Copy link
Member

@jakebailey jakebailey left a comment

Choose a reason for hiding this comment

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

So, this seems probably right but will defer to @weswigham 😄

@jakebailey jakebailey requested a review from weswigham September 3, 2025 03:17
@a-tarasyuk
Copy link
Contributor Author

@jakebailey ok, thanks

Copy link
Member

@weswigham weswigham left a comment

Choose a reason for hiding this comment

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

I was unsure if we wanted this behavior back and didn't just wanna make const enums always preserved, since that's what we were doing before we re-added inlining, but I suppose since vscode reported it, they'd like the runtime source elision behavior back, too. Tbh, preserveConstEnums should at least be the default nowadays, but that's a change for another PR.

We also need to elide namespace declarations containing exclusively const enums and/or types (preserveConstEnums dependent) for the same reasons you'd want the const enums elided. See the missing shouldEmitModuleDeclaration call from the original ts.ts source. ast.GetModuleInstanceState is already ported, so it shouldn't be too bad.

@a-tarasyuk a-tarasyuk requested a review from weswigham September 3, 2025 22:31
Copy link
Member

@weswigham weswigham left a comment

Choose a reason for hiding this comment

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

Pretty good - some last comments on comment preservation, since we're working with NotEmittedStatements which explicitly exist to copy those.

return nil
}
if ast.IsNotEmittedStatement(updated) {
return visitor.Factory.NewEmptyStatement()
Copy link
Member

Choose a reason for hiding this comment

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

This needs to copy position/comment emit node information from updated - NotEmittedStatements are injected into the tree explicitly to preserve that kind of thing, so failing to copy it here unintentionally drops it.

Though, why is this needed at all, actually? Is it just so follow-on transforms can see an EmptyStatement instead of a NotEmittedStatement and react appropriately? It might be better to not perform a remapping on transform like this, and instead use a ast.IsEmptyStatementLike helper that includes both EmptyStatement and NotEmittedStatement. Either one of the two strategies to preserve comment information should be fine, though (either copying that info here or dropping the remapping entirely and updating downstream transforms/printers to handle NotEmittedStatements like EmptyStatements).

Copy link
Member

Choose a reason for hiding this comment

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

^^ this comment still needs to be addressed - this remapping drops comments and location information.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@weswigham Thanks for pointing that out. The main issue is using remapping to handle cases related to embedded statements

// ts
if (true)
  const enum E1 { A = 1 }
else 
  const enum E2 { A = 1 }

// js
if (true)
    ;
else
    ;

func (p *Printer) emitEmptyStatement(node *ast.EmptyStatement, isEmbeddedStatement bool) {
state := p.enterNode(node.AsNode())
// While most trailing semicolons are possibly insignificant, an embedded "empty"
// statement is significant and cannot be elided by a trailing-semicolon-omitting writer.
if isEmbeddedStatement {
p.writePunctuation(";")
} else {
p.writeTrailingSemicolon()
}
p.exitNode(node.AsNode(), state)
}

Strada uses this helper to wrap all embedded statements

https://github.com/microsoft/TypeScript/blob/7956c00166df552a1d6da6c2f996b558e5dd94ff/src/compiler/factory/nodeFactory.ts#L7163-L7167

It might be an option to extend

func (p *Printer) emitNotEmittedStatement(node *ast.NotEmittedStatement) {
p.exitNode(node.AsNode(), p.enterNode(node.AsNode()))
}

for cases involving embedded statements.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, you can use the transform hook to automatically do the thing the embedded statement helper is doing like you are here (probably - we're moving the remapping from node construction later a step to the generic visitor base, so it's possible a transform could witness the difference before returning; but transforms don't usually inspect what the node factory returns anyway), you definitely just need the corsa equivalents of the setTextRange+setOriginal calls.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@weswigham I was thinking about the following options:

  • Extend emitEmbeddedStatement to emit a ; for NotEmittedStatement, since it currently feels more like an alias used only at runtime

if node.Kind == ast.KindEmptyStatement {
p.emitEmptyStatement(node.AsEmptyStatement(), true /*isEmbeddedStatement*/)
} else {
p.emitStatement(node)
}

  • Add handling logic to runtimesyntax so that when a NotEmittedStatement appears inside an EmbeddedStatement, an EmptyStatement is emitted instead.

@a-tarasyuk a-tarasyuk requested a review from weswigham September 4, 2025 20:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Const enums shouldn't be emitted after inlining
4 participants