Skip to content

Commit 809b2c0

Browse files
authored
Const enum inlining and synthetic comment emit support (#1599)
1 parent 4ddea0c commit 809b2c0

File tree

98 files changed

+639
-1300
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+639
-1300
lines changed

internal/checker/checker.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15087,7 +15087,7 @@ func (c *Checker) resolveEntityName(name *ast.Node, meaning ast.SymbolFlags, ign
1508715087
name.Parent != nil && name.Parent.Kind == ast.KindJSExportAssignment) {
1508815088
c.markSymbolOfAliasDeclarationIfTypeOnly(getAliasDeclarationFromName(name), symbol, nil /*finalTarget*/, true /*overwriteEmpty*/, nil, "")
1508915089
}
15090-
if symbol.Flags&meaning == 0 && !dontResolveAlias {
15090+
if symbol.Flags&meaning == 0 && !dontResolveAlias && symbol.Flags&ast.SymbolFlagsAlias != 0 {
1509115091
return c.resolveAlias(symbol)
1509215092
}
1509315093
}

internal/checker/emitresolver.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,3 +1025,10 @@ func (r *emitResolver) GetResolutionModeOverride(node *ast.Node) core.Resolution
10251025
defer r.checkerMu.Unlock()
10261026
return r.checker.GetResolutionModeOverride(node.AsImportAttributes(), false)
10271027
}
1028+
1029+
func (r *emitResolver) GetConstantValue(node *ast.Node) any {
1030+
// node = emitContext.ParseNode(node)
1031+
r.checkerMu.Lock()
1032+
defer r.checkerMu.Unlock()
1033+
return r.checker.GetConstantValue(node)
1034+
}

internal/compiler/emitter.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/microsoft/typescript-go/internal/transformers"
1515
"github.com/microsoft/typescript-go/internal/transformers/declarations"
1616
"github.com/microsoft/typescript-go/internal/transformers/estransforms"
17+
"github.com/microsoft/typescript-go/internal/transformers/inliners"
1718
"github.com/microsoft/typescript-go/internal/transformers/jsxtransforms"
1819
"github.com/microsoft/typescript-go/internal/transformers/moduletransforms"
1920
"github.com/microsoft/typescript-go/internal/transformers/tstransforms"
@@ -86,7 +87,7 @@ func getScriptTransformers(emitContext *printer.EmitContext, host printer.EmitHo
8687

8788
var emitResolver printer.EmitResolver
8889
var referenceResolver binder.ReferenceResolver
89-
if importElisionEnabled || options.GetJSXTransformEnabled() {
90+
if importElisionEnabled || options.GetJSXTransformEnabled() || !options.GetIsolatedModules() { // full emit resolver is needed for import ellision and const enum inlining
9091
emitResolver = host.GetEmitResolver()
9192
emitResolver.MarkLinkedReferencesRecursively(sourceFile)
9293
referenceResolver = emitResolver
@@ -128,6 +129,11 @@ func getScriptTransformers(emitContext *printer.EmitContext, host printer.EmitHo
128129

129130
// transform module syntax
130131
tx = append(tx, getModuleTransformer(&opts))
132+
133+
// inlining (formerly done via substitutions)
134+
if !options.GetIsolatedModules() {
135+
tx = append(tx, inliners.NewConstEnumInliningTransformer(&opts))
136+
}
131137
return tx
132138
}
133139

internal/printer/emitcontext.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,14 @@ const (
511511
hasSourceMapRange
512512
)
513513

514+
type SynthesizedComment struct {
515+
Kind ast.Kind
516+
Loc core.TextRange
517+
HasLeadingNewLine bool
518+
HasTrailingNewLine bool
519+
Text string
520+
}
521+
514522
type emitNode struct {
515523
flags emitNodeFlags
516524
emitFlags EmitFlags
@@ -519,6 +527,8 @@ type emitNode struct {
519527
tokenSourceMapRanges map[ast.Kind]core.TextRange
520528
helpers []*EmitHelper
521529
externalHelpersModuleName *ast.IdentifierNode
530+
leadingComments []SynthesizedComment
531+
trailingComments []SynthesizedComment
522532
}
523533

524534
// NOTE: This method is not guaranteed to be thread-safe
@@ -917,3 +927,37 @@ func (c *EmitContext) VisitIterationBody(body *ast.Statement, visitor *ast.NodeV
917927

918928
return updated
919929
}
930+
931+
func (c *EmitContext) SetSyntheticLeadingComments(node *ast.Node, comments []SynthesizedComment) *ast.Node {
932+
c.emitNodes.Get(node).leadingComments = comments
933+
return node
934+
}
935+
936+
func (c *EmitContext) AddSyntheticLeadingComment(node *ast.Node, kind ast.Kind, text string, hasTrailingNewLine bool) *ast.Node {
937+
c.emitNodes.Get(node).leadingComments = append(c.emitNodes.Get(node).leadingComments, SynthesizedComment{Kind: kind, Loc: core.NewTextRange(-1, -1), HasTrailingNewLine: hasTrailingNewLine, Text: text})
938+
return node
939+
}
940+
941+
func (c *EmitContext) GetSyntheticLeadingComments(node *ast.Node) []SynthesizedComment {
942+
if c.emitNodes.Has(node) {
943+
return c.emitNodes.Get(node).leadingComments
944+
}
945+
return nil
946+
}
947+
948+
func (c *EmitContext) SetSyntheticTrailingComments(node *ast.Node, comments []SynthesizedComment) *ast.Node {
949+
c.emitNodes.Get(node).trailingComments = comments
950+
return node
951+
}
952+
953+
func (c *EmitContext) AddSyntheticTrailingComment(node *ast.Node, kind ast.Kind, text string, hasTrailingNewLine bool) *ast.Node {
954+
c.emitNodes.Get(node).trailingComments = append(c.emitNodes.Get(node).trailingComments, SynthesizedComment{Kind: kind, Loc: core.NewTextRange(-1, -1), HasTrailingNewLine: hasTrailingNewLine, Text: text})
955+
return node
956+
}
957+
958+
func (c *EmitContext) GetSyntheticTrailingComments(node *ast.Node) []SynthesizedComment {
959+
if c.emitNodes.Has(node) {
960+
return c.emitNodes.Get(node).trailingComments
961+
}
962+
return nil
963+
}

internal/printer/emitresolver.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ type EmitResolver interface {
3535
GetEffectiveDeclarationFlags(node *ast.Node, flags ast.ModifierFlags) ast.ModifierFlags
3636
GetResolutionModeOverride(node *ast.Node) core.ResolutionMode
3737

38+
// const enum inlining
39+
GetConstantValue(node *ast.Node) any
40+
3841
// JSX Emit
3942
GetJsxFactoryEntity(location *ast.Node) *ast.Node
4043
GetJsxFragmentFactoryEntity(location *ast.Node) *ast.Node

internal/printer/printer.go

Lines changed: 70 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424

2525
"github.com/microsoft/typescript-go/internal/ast"
2626
"github.com/microsoft/typescript-go/internal/core"
27-
"github.com/microsoft/typescript-go/internal/jsnum"
2827
"github.com/microsoft/typescript-go/internal/scanner"
2928
"github.com/microsoft/typescript-go/internal/sourcemap"
3029
"github.com/microsoft/typescript-go/internal/stringutil"
@@ -630,26 +629,30 @@ func (p *Printer) writeCommentRange(comment ast.CommentRange) {
630629
}
631630

632631
text := p.currentSourceFile.Text()
633-
if comment.Kind == ast.KindMultiLineCommentTrivia {
634-
lineMap := p.currentSourceFile.LineMap()
632+
lineMap := p.currentSourceFile.LineMap()
633+
p.writeCommentRangeWorker(text, lineMap, comment.Kind, comment.TextRange)
634+
}
635+
636+
func (p *Printer) writeCommentRangeWorker(text string, lineMap []core.TextPos, kind ast.Kind, loc core.TextRange) {
637+
if kind == ast.KindMultiLineCommentTrivia {
635638
indentSize := len(getIndentString(1))
636-
firstLine := scanner.ComputeLineOfPosition(lineMap, comment.Pos())
639+
firstLine := scanner.ComputeLineOfPosition(lineMap, loc.Pos())
637640
lineCount := len(lineMap)
638641
firstCommentLineIndent := -1
639-
pos := comment.Pos()
642+
pos := loc.Pos()
640643
currentLine := firstLine
641-
for ; pos < comment.End(); currentLine++ {
644+
for ; pos < loc.End(); currentLine++ {
642645
var nextLineStart int
643646
if currentLine+1 == lineCount {
644647
nextLineStart = len(text) + 1
645648
} else {
646649
nextLineStart = int(lineMap[currentLine+1])
647650
}
648651

649-
if pos != comment.Pos() {
652+
if pos != loc.Pos() {
650653
// If we are not emitting first line, we need to write the spaces to adjust the alignment
651654
if firstCommentLineIndent == -1 {
652-
firstCommentLineIndent = calculateIndent(text, int(lineMap[firstLine]), comment.Pos())
655+
firstCommentLineIndent = calculateIndent(text, int(lineMap[firstLine]), loc.Pos())
653656
}
654657

655658
// These are number of spaces writer is going to write at current indent
@@ -689,11 +692,11 @@ func (p *Printer) writeCommentRange(comment ast.CommentRange) {
689692
}
690693

691694
// Write the comment line text
692-
end := min(comment.End(), nextLineStart-1)
695+
end := min(loc.End(), nextLineStart-1)
693696
currentLineText := strings.TrimSpace(text[pos:end])
694697
if len(currentLineText) > 0 {
695698
p.writeComment(currentLineText)
696-
if end != comment.End() {
699+
if end != loc.End() {
697700
p.writeLine()
698701
}
699702
} else {
@@ -705,19 +708,14 @@ func (p *Printer) writeCommentRange(comment ast.CommentRange) {
705708
}
706709
} else {
707710
// Single line comment of style //....
708-
p.writeComment(text[comment.Pos():comment.End()])
711+
p.writeComment(text[loc.Pos():loc.End()])
709712
}
710713
}
711714

712715
//
713716
// Custom emit behavior stubs (i.e., from `EmitNode`, `EmitFlags`, etc.)
714717
//
715718

716-
func (p *Printer) getConstantValue(node *ast.Node) any {
717-
// !!! Const-enum inlining (low priority)
718-
return nil
719-
}
720-
721719
func (p *Printer) shouldEmitComments(node *ast.Node) bool {
722720
return !p.commentsDisabled &&
723721
p.currentSourceFile != nil &&
@@ -2428,12 +2426,6 @@ func (p *Printer) mayNeedDotDotForPropertyAccess(expression *ast.Expression) boo
24282426
!strings.Contains(text, scanner.TokenToString(ast.KindDotToken)) &&
24292427
!strings.Contains(text, "E") &&
24302428
!strings.Contains(text, "e")
2431-
} else if ast.IsAccessExpression(expression) {
2432-
// check if constant enum value is a non-negative integer
2433-
if constantValue, ok := p.getConstantValue(expression).(jsnum.Number); ok {
2434-
return !constantValue.IsInf() && constantValue >= 0 && constantValue.Floor() == constantValue
2435-
}
2436-
return false
24372429
}
24382430
return false
24392431
}
@@ -5017,7 +5009,7 @@ func (p *Printer) emitCommentsBeforeNode(node *ast.Node) *commentState {
50175009

50185010
// Emit leading comments
50195011
p.emitLeadingCommentsOfNode(node, emitFlags, commentRange)
5020-
p.emitLeadingSyntheticCommentsOfNode(node)
5012+
p.emitLeadingSyntheticCommentsOfNode(node, emitFlags)
50215013
if emitFlags&EFNoNestedComments != 0 {
50225014
p.commentsDisabled = true
50235015
}
@@ -5043,7 +5035,7 @@ func (p *Printer) emitCommentsAfterNode(node *ast.Node, state *commentState) {
50435035
p.commentsDisabled = false
50445036
}
50455037

5046-
p.emitTrailingSyntheticCommentsOfNode(node)
5038+
p.emitTrailingSyntheticCommentsOfNode(node, emitFlags)
50475039
p.emitTrailingCommentsOfNode(node, emitFlags, commentRange, containerPos, containerEnd, declarationListContainerEnd)
50485040

50495041
// !!! Preserve comments from type annotation:
@@ -5182,12 +5174,62 @@ func (p *Printer) emitTrailingCommentsOfNode(node *ast.Node, emitFlags EmitFlags
51825174
}
51835175
}
51845176

5185-
func (p *Printer) emitLeadingSyntheticCommentsOfNode(node *ast.Node) {
5186-
// !!!
5177+
func (p *Printer) emitLeadingSyntheticCommentsOfNode(node *ast.Node, emitFlags EmitFlags) {
5178+
if emitFlags&EFNoLeadingComments != 0 {
5179+
return
5180+
}
5181+
synth := p.emitContext.GetSyntheticLeadingComments(node)
5182+
for _, c := range synth {
5183+
p.emitLeadingSynthesizedComment(c)
5184+
}
51875185
}
51885186

5189-
func (p *Printer) emitTrailingSyntheticCommentsOfNode(node *ast.Node) {
5190-
// !!!
5187+
func (p *Printer) emitLeadingSynthesizedComment(comment SynthesizedComment) {
5188+
if comment.HasLeadingNewLine || comment.Kind == ast.KindSingleLineCommentTrivia {
5189+
p.writer.WriteLine()
5190+
}
5191+
p.writeSynthesizedComment(comment)
5192+
if comment.HasTrailingNewLine || comment.Kind == ast.KindSingleLineCommentTrivia {
5193+
p.writer.WriteLine()
5194+
} else {
5195+
p.writer.WriteSpace(" ")
5196+
}
5197+
}
5198+
5199+
func (p *Printer) emitTrailingSyntheticCommentsOfNode(node *ast.Node, emitFlags EmitFlags) {
5200+
if emitFlags&EFNoTrailingComments != 0 {
5201+
return
5202+
}
5203+
synth := p.emitContext.GetSyntheticTrailingComments(node)
5204+
for _, c := range synth {
5205+
p.emitTrailingSynthesizedComment(c)
5206+
}
5207+
}
5208+
5209+
func (p *Printer) emitTrailingSynthesizedComment(comment SynthesizedComment) {
5210+
if !p.writer.IsAtStartOfLine() {
5211+
p.writer.WriteSpace(" ")
5212+
}
5213+
p.writeSynthesizedComment(comment)
5214+
if comment.HasTrailingNewLine {
5215+
p.writer.WriteLine()
5216+
}
5217+
}
5218+
5219+
func formatSynthesizedComment(comment SynthesizedComment) string {
5220+
if comment.Kind == ast.KindMultiLineCommentTrivia {
5221+
return "/*" + comment.Text + "*/"
5222+
}
5223+
return "//" + comment.Text
5224+
}
5225+
5226+
func (p *Printer) writeSynthesizedComment(comment SynthesizedComment) {
5227+
text := formatSynthesizedComment(comment)
5228+
var lineMap []core.TextPos
5229+
if comment.Kind == ast.KindMultiLineCommentTrivia {
5230+
lineMap = core.ComputeLineStarts(text)
5231+
}
5232+
p.writeCommentRangeWorker(text, lineMap, comment.Kind, core.NewTextRange(0, len(text)))
51915233
}
51925234

51935235
func (p *Printer) emitLeadingComments(pos int, elided bool) bool {
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package inliners
2+
3+
import (
4+
"strings"
5+
6+
"github.com/microsoft/typescript-go/internal/ast"
7+
"github.com/microsoft/typescript-go/internal/core"
8+
"github.com/microsoft/typescript-go/internal/debug"
9+
"github.com/microsoft/typescript-go/internal/jsnum"
10+
"github.com/microsoft/typescript-go/internal/printer"
11+
"github.com/microsoft/typescript-go/internal/scanner"
12+
"github.com/microsoft/typescript-go/internal/transformers"
13+
)
14+
15+
type ConstEnumInliningTransformer struct {
16+
transformers.Transformer
17+
compilerOptions *core.CompilerOptions
18+
currentSourceFile *ast.SourceFile
19+
emitResolver printer.EmitResolver
20+
}
21+
22+
func NewConstEnumInliningTransformer(opt *transformers.TransformOptions) *transformers.Transformer {
23+
compilerOptions := opt.CompilerOptions
24+
emitContext := opt.Context
25+
if compilerOptions.GetIsolatedModules() {
26+
debug.Fail("const enums are not inlined under isolated modules")
27+
}
28+
tx := &ConstEnumInliningTransformer{compilerOptions: compilerOptions, emitResolver: opt.EmitResolver}
29+
return tx.NewTransformer(tx.visit, emitContext)
30+
}
31+
32+
func (tx *ConstEnumInliningTransformer) visit(node *ast.Node) *ast.Node {
33+
switch node.Kind {
34+
case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression:
35+
{
36+
parse := tx.EmitContext().ParseNode(node)
37+
if parse == nil {
38+
return tx.Visitor().VisitEachChild(node)
39+
}
40+
value := tx.emitResolver.GetConstantValue(parse)
41+
if value != nil {
42+
var replacement *ast.Node
43+
switch v := value.(type) {
44+
case jsnum.Number:
45+
if v.IsInf() {
46+
if v.Abs() == v {
47+
replacement = tx.Factory().NewIdentifier("Infinity")
48+
} else {
49+
replacement = tx.Factory().NewPrefixUnaryExpression(ast.KindMinusToken, tx.Factory().NewIdentifier("Infinity"))
50+
}
51+
} else if v.IsNaN() {
52+
replacement = tx.Factory().NewIdentifier("NaN")
53+
} else if v.Abs() == v {
54+
replacement = tx.Factory().NewNumericLiteral(v.String())
55+
} else {
56+
replacement = tx.Factory().NewPrefixUnaryExpression(ast.KindMinusToken, tx.Factory().NewNumericLiteral(v.Abs().String()))
57+
}
58+
case string:
59+
replacement = tx.Factory().NewStringLiteral(v)
60+
case jsnum.PseudoBigInt: // technically not supported by strada, and issues a checker error, handled here for completeness
61+
if v == (jsnum.PseudoBigInt{}) {
62+
replacement = tx.Factory().NewBigIntLiteral("0")
63+
} else if !v.Negative {
64+
replacement = tx.Factory().NewBigIntLiteral(v.Base10Value)
65+
} else {
66+
replacement = tx.Factory().NewPrefixUnaryExpression(ast.KindMinusToken, tx.Factory().NewBigIntLiteral(v.Base10Value))
67+
}
68+
}
69+
70+
if tx.compilerOptions.RemoveComments.IsFalseOrUnknown() {
71+
original := tx.EmitContext().MostOriginal(node)
72+
if original != nil && !ast.NodeIsSynthesized(original) {
73+
originalText := scanner.GetTextOfNode(original)
74+
escapedText := " " + safeMultiLineComment(originalText) + " "
75+
tx.EmitContext().AddSyntheticTrailingComment(replacement, ast.KindMultiLineCommentTrivia, escapedText, false)
76+
}
77+
}
78+
return replacement
79+
}
80+
return tx.Visitor().VisitEachChild(node)
81+
}
82+
}
83+
return tx.Visitor().VisitEachChild(node)
84+
}
85+
86+
func safeMultiLineComment(text string) string {
87+
return strings.ReplaceAll(text, "*/", "*_/")
88+
}

testdata/baselines/reference/submodule/compiler/assignmentNonObjectTypeConstraints.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ function foo(x) {
3232
var y = x; // Ok
3333
}
3434
foo(5);
35-
foo(E.A);
35+
foo(0 /* E.A */);
3636
class A {
3737
a;
3838
}

0 commit comments

Comments
 (0)