Skip to content

Commit

Permalink
fix(ast): invalid idx1 for new expression (#473)
Browse files Browse the repository at this point in the history
Fix invalid Idx1() return for NewExpression when there are no
parenthesis.

Fixes #261
  • Loading branch information
stevenh authored Nov 29, 2022
1 parent 7de4257 commit 242e87a
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 24 deletions.
21 changes: 13 additions & 8 deletions ast/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 // ++ --
Expand Down
76 changes: 60 additions & 16 deletions ast/walk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
})
}
}

0 comments on commit 242e87a

Please sign in to comment.