From 242e87ac0e36de2504513188f7436b8548b6e724 Mon Sep 17 00:00:00 2001 From: Steven Hartland Date: Tue, 29 Nov 2022 13:55:47 +0000 Subject: [PATCH] fix(ast): invalid idx1 for new expression (#473) Fix invalid Idx1() return for NewExpression when there are no parenthesis. Fixes #261 --- ast/node.go | 21 ++++++++----- ast/walk_test.go | 76 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 73 insertions(+), 24 deletions(-) diff --git a/ast/node.go b/ast/node.go index 80372c3d..ad98198e 100644 --- a/ast/node.go +++ b/ast/node.go @@ -464,14 +464,19 @@ func (self *DotExpression) Idx1() file.Idx { return self.Identifier.Idx1 func (self *EmptyExpression) Idx1() file.Idx { return self.End } func (self *FunctionLiteral) Idx1() file.Idx { return self.Body.Idx1() } func (self *Identifier) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Name)) } -func (self *NewExpression) Idx1() file.Idx { return self.RightParenthesis + 1 } -func (self *NullLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + 4) } // "null" -func (self *NumberLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) } -func (self *ObjectLiteral) Idx1() file.Idx { return self.RightBrace } -func (self *RegExpLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) } -func (self *SequenceExpression) Idx1() file.Idx { return self.Sequence[0].Idx1() } -func (self *StringLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) } -func (self *ThisExpression) Idx1() file.Idx { return self.Idx + 4 } +func (self *NewExpression) Idx1() file.Idx { + if self.RightParenthesis > 0 { + return self.RightParenthesis + 1 + } + return self.Callee.Idx1() +} +func (self *NullLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + 4) } // "null" +func (self *NumberLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) } +func (self *ObjectLiteral) Idx1() file.Idx { return self.RightBrace } +func (self *RegExpLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) } +func (self *SequenceExpression) Idx1() file.Idx { return self.Sequence[0].Idx1() } +func (self *StringLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) } +func (self *ThisExpression) Idx1() file.Idx { return self.Idx + 4 } func (self *UnaryExpression) Idx1() file.Idx { if self.Postfix { return self.Operand.Idx1() + 2 // ++ -- diff --git a/ast/walk_test.go b/ast/walk_test.go index ad0e2f21..f063a181 100644 --- a/ast/walk_test.go +++ b/ast/walk_test.go @@ -10,11 +10,12 @@ import ( ) type walker struct { - stack []ast.Node - source string - shift file.Idx - seen map[ast.Node]struct{} - duplicate int + stack []ast.Node + source string + shift file.Idx + seen map[ast.Node]struct{} + duplicate int + newExpressionIdx1 file.Idx } // push and pop below are to prove the symmetry of Enter/Exit calls @@ -48,17 +49,23 @@ func (w *walker) Enter(n ast.Node) ast.Visitor { w.seen[n] = struct{}{} - if id, ok := n.(*ast.Identifier); ok && id != nil { - idx := n.Idx0() + w.shift - 1 - s := w.source[:idx] + "IDENT_" + w.source[idx:] - w.source = s - w.shift += 6 - } - if v, ok := n.(*ast.VariableExpression); ok && v != nil { - idx := n.Idx0() + w.shift - 1 - s := w.source[:idx] + "VAR_" + w.source[idx:] - w.source = s - w.shift += 4 + switch t := n.(type) { + case *ast.Identifier: + if t != nil { + idx := n.Idx0() + w.shift - 1 + s := w.source[:idx] + "IDENT_" + w.source[idx:] + w.source = s + w.shift += 6 + } + case *ast.VariableExpression: + if t != nil { + idx := n.Idx0() + w.shift - 1 + s := w.source[:idx] + "VAR_" + w.source[idx:] + w.source = s + w.shift += 4 + } + case *ast.NewExpression: + w.newExpressionIdx1 = n.Idx1() } return w @@ -93,3 +100,40 @@ func TestVisitorRewrite(t *testing.T) { require.Len(t, w.stack, 0) require.Equal(t, w.duplicate, 0) } + +func Test_issue261(t *testing.T) { + tests := map[string]struct { + code string + want file.Idx + }{ + "no-parenthesis": { + code: `var i = new Image;`, + want: 18, + }, + "no-args": { + code: `var i = new Image();`, + want: 20, + }, + "two-args": { + code: `var i = new Image(1, 2);`, + want: 24, + }, + } + + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + prog, err := parser.ParseFile(nil, "", tt.code, 0) + require.NoError(t, err) + + w := &walker{ + source: tt.code, + seen: make(map[ast.Node]struct{}), + } + ast.Walk(w, prog) + + require.Equal(t, tt.want, w.newExpressionIdx1) + require.Len(t, w.stack, 0) + require.Equal(t, w.duplicate, 0) + }) + } +}