Skip to content

Commit 0e7b287

Browse files
authored
Fix trailing comma detection after transformation (microsoft#450)
1 parent 093107c commit 0e7b287

File tree

3 files changed

+124
-7
lines changed

3 files changed

+124
-7
lines changed

internal/ast/ast.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -197,12 +197,11 @@ func (n *Node) DeclarationData() *DeclarationBase { return n.data.Declar
197197
func (n *Node) ExportableData() *ExportableBase { return n.data.ExportableData() }
198198
func (n *Node) LocalsContainerData() *LocalsContainerBase { return n.data.LocalsContainerData() }
199199
func (n *Node) FunctionLikeData() *FunctionLikeBase { return n.data.FunctionLikeData() }
200-
func (n *Node) Parameters() []*ParameterDeclarationNode {
201-
return n.data.FunctionLikeData().Parameters.Nodes
202-
}
203-
func (n *Node) ClassLikeData() *ClassLikeBase { return n.data.ClassLikeData() }
204-
func (n *Node) BodyData() *BodyBase { return n.data.BodyData() }
205-
func (n *Node) LiteralLikeData() *LiteralLikeBase { return n.data.LiteralLikeData() }
200+
func (n *Node) ParameterList() *ParameterList { return n.data.FunctionLikeData().Parameters }
201+
func (n *Node) Parameters() []*ParameterDeclarationNode { return n.ParameterList().Nodes }
202+
func (n *Node) ClassLikeData() *ClassLikeBase { return n.data.ClassLikeData() }
203+
func (n *Node) BodyData() *BodyBase { return n.data.BodyData() }
204+
func (n *Node) LiteralLikeData() *LiteralLikeBase { return n.data.LiteralLikeData() }
206205
func (n *Node) TemplateLiteralLikeData() *TemplateLiteralLikeBase {
207206
return n.data.TemplateLiteralLikeData()
208207
}

internal/printer/printer.go

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4216,7 +4216,7 @@ func (p *Printer) emitListRange(emit func(p *Printer, node *ast.Node), parentNod
42164216
end = length
42174217
}
42184218

4219-
p.emitListItems(emit, parentNode, children.Nodes[start:end], format, children.HasTrailingComma(), children.Loc)
4219+
p.emitListItems(emit, parentNode, children.Nodes[start:end], format, p.hasTrailingComma(parentNode, children), children.Loc)
42204220
}
42214221

42224222
if p.OnAfterEmitNodeList != nil {
@@ -4231,6 +4231,82 @@ func (p *Printer) emitListRange(emit func(p *Printer, node *ast.Node), parentNod
42314231
}
42324232
}
42334233

4234+
func (p *Printer) hasTrailingComma(parentNode *ast.Node, children *ast.NodeList) bool {
4235+
// NodeList.HasTrailingComma() is unreliable on transformed nodes as some nodes may have been removed. In the event
4236+
// we believe we may need to emit a trailing comma, we must first look to the respective node list on the original
4237+
// node first.
4238+
if !children.HasTrailingComma() {
4239+
return false
4240+
}
4241+
4242+
originalParent := p.emitContext.MostOriginal(parentNode)
4243+
if originalParent == parentNode {
4244+
// if this node is the original node, we can trust the result
4245+
return true
4246+
}
4247+
4248+
if originalParent.Kind != parentNode.Kind {
4249+
// if the original node is some other kind of node, we cannot correlate the list
4250+
return false
4251+
}
4252+
4253+
// find the respective node list on the original parent
4254+
originalList := children
4255+
switch originalParent.Kind {
4256+
case ast.KindObjectLiteralExpression:
4257+
originalList = originalParent.AsObjectLiteralExpression().Properties
4258+
case ast.KindArrayLiteralExpression:
4259+
originalList = originalParent.AsArrayLiteralExpression().Elements
4260+
case ast.KindCallExpression, ast.KindNewExpression:
4261+
switch children {
4262+
case parentNode.TypeArgumentList():
4263+
originalList = originalParent.TypeArgumentList()
4264+
case parentNode.ArgumentList():
4265+
originalList = originalParent.ArgumentList()
4266+
}
4267+
case ast.KindConstructor,
4268+
ast.KindMethodDeclaration,
4269+
ast.KindGetAccessor,
4270+
ast.KindSetAccessor,
4271+
ast.KindFunctionDeclaration,
4272+
ast.KindFunctionExpression,
4273+
ast.KindArrowFunction,
4274+
ast.KindFunctionType,
4275+
ast.KindConstructorType,
4276+
ast.KindCallSignature,
4277+
ast.KindConstructSignature:
4278+
switch children {
4279+
case parentNode.TypeParameterList():
4280+
originalList = originalParent.TypeParameterList()
4281+
case parentNode.ParameterList():
4282+
originalList = originalParent.ParameterList()
4283+
}
4284+
case ast.KindClassDeclaration, ast.KindClassExpression, ast.KindInterfaceDeclaration, ast.KindTypeAliasDeclaration:
4285+
switch children {
4286+
case parentNode.TypeParameterList():
4287+
originalList = originalParent.TypeParameterList()
4288+
}
4289+
case ast.KindObjectBindingPattern, ast.KindArrayBindingPattern:
4290+
switch children {
4291+
case parentNode.AsBindingPattern().Elements:
4292+
originalList = originalParent.AsBindingPattern().Elements
4293+
}
4294+
case ast.KindNamedImports:
4295+
originalList = originalParent.AsNamedImports().Elements
4296+
case ast.KindNamedExports:
4297+
originalList = originalParent.AsNamedImports().Elements
4298+
case ast.KindImportAttributes:
4299+
originalList = originalParent.AsImportAttributes().Attributes
4300+
}
4301+
4302+
// if we have the original list, we can use it's result.
4303+
if originalList != nil {
4304+
return originalList.HasTrailingComma()
4305+
}
4306+
4307+
return false
4308+
}
4309+
42344310
func (p *Printer) writeDelimiter(format ListFormat) {
42354311
switch format & LFDelimitersMask {
42364312
case LFNone:

internal/printer/printer_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2381,3 +2381,45 @@ func TestNameGeneration(t *testing.T) {
23812381
parsetestutil.MarkSyntheticRecursive(file)
23822382
emittestutil.CheckEmit(t, ec, file.AsSourceFile(), "var _a;\nfunction f() {\n var _a;\n}")
23832383
}
2384+
2385+
func TestNoTrailingCommaAfterTransform(t *testing.T) {
2386+
t.Parallel()
2387+
2388+
file := parsetestutil.ParseTypeScript("[a!]", false /*jsx*/)
2389+
emitContext := printer.NewEmitContext()
2390+
2391+
var visitor *ast.NodeVisitor
2392+
visitor = emitContext.NewNodeVisitor(func(node *ast.Node) *ast.Node {
2393+
switch node.Kind {
2394+
case ast.KindNonNullExpression:
2395+
node = node.AsNonNullExpression().Expression
2396+
default:
2397+
node = node.VisitEachChild(visitor)
2398+
}
2399+
return node
2400+
})
2401+
file = visitor.VisitSourceFile(file)
2402+
2403+
emittestutil.CheckEmit(t, emitContext, file.AsSourceFile(), "[a];")
2404+
}
2405+
2406+
func TestTrailingCommaAfterTransform(t *testing.T) {
2407+
t.Parallel()
2408+
2409+
file := parsetestutil.ParseTypeScript("[a!,]", false /*jsx*/)
2410+
emitContext := printer.NewEmitContext()
2411+
2412+
var visitor *ast.NodeVisitor
2413+
visitor = emitContext.NewNodeVisitor(func(node *ast.Node) *ast.Node {
2414+
switch node.Kind {
2415+
case ast.KindNonNullExpression:
2416+
node = node.AsNonNullExpression().Expression
2417+
default:
2418+
node = node.VisitEachChild(visitor)
2419+
}
2420+
return node
2421+
})
2422+
file = visitor.VisitSourceFile(file)
2423+
2424+
emittestutil.CheckEmit(t, emitContext, file.AsSourceFile(), "[a,];")
2425+
}

0 commit comments

Comments
 (0)