Repository: robertkrimen/otto
Pull request #506 (2023-07-24 04:37:34)

Fix positions of statements
* Fix Idx1 of BranchStatement so that it points to the character after the label if Label exists, or the one after Token if Label does not exist.
* Fix Idx1 of LabelledStatement so that it points to the character after the statement.
* Fix Idx1 of ReturnStatement so that it points to the character after the argument if Argument exists, or the one after return keyword if Argument does not exist.
* Set Idx0 of SwitchStatement and fix Idx1 of SwitchStatement so that it points to the character after the right brace.
* Fix Idx0 of ThrowStatement to point to the start of throw keyword and fix Idx1 of ThrowStatement so that it points to the next character after Argument.
* Fix Idx1 of TryStatement to point to the character after Finally if Finally exists, or after Catch if Finally does not exist.
* Set Idx0 of WithStatement which was not previously set.
* Set WhileStatement.While so that Idx0 points to the right place.
* Set Idx0 of DoWhileStatement and fix Idx1 to point to the next character after the right parenthesis.




Patch:
diff --git a/ast/node.go b/ast/node.go
index 4ac013e..ec49c73 100644
--- a/ast/node.go
+++ b/ast/node.go
@@ -543,16 +543,19 @@ type BranchStatement struct {
 	Label *Identifier
 }
 
-// Idx1 implements Node.
-func (bs *BranchStatement) Idx1() file.Idx {
-	return bs.Idx
-}
-
 // Idx0 implements Node.
 func (bs *BranchStatement) Idx0() file.Idx {
 	return bs.Idx
 }
 
+// Idx1 implements Node.
+func (bs *BranchStatement) Idx1() file.Idx {
+	if bs.Label == nil {
+		return file.Idx(int(bs.Idx) + len(bs.Token.String()))
+	}
+	return bs.Label.Idx1()
+}
+
 // expression implements Statement.
 func (*BranchStatement) statement() {}
 
@@ -616,9 +619,10 @@ func (*DebuggerStatement) statement() {}
 
 // DoWhileStatement represents a do while statement.
 type DoWhileStatement struct {
-	Do   file.Idx
-	Test Expression
-	Body Statement
+	Do               file.Idx
+	Test             Expression
+	Body             Statement
+	RightParenthesis file.Idx
 }
 
 // Idx0 implements Node.
@@ -628,7 +632,7 @@ func (dws *DoWhileStatement) Idx0() file.Idx {
 
 // Idx1 implements Node.
 func (dws *DoWhileStatement) Idx1() file.Idx {
-	return dws.Test.Idx1()
+	return dws.RightParenthesis + 1
 }
 
 // expression implements Statement.
@@ -769,7 +773,7 @@ func (ls *LabelledStatement) Idx0() file.Idx {
 
 // Idx1 implements Node.
 func (ls *LabelledStatement) Idx1() file.Idx {
-	return ls.Colon + 1
+	return ls.Statement.Idx1()
 }
 
 // expression implements Statement.
@@ -788,7 +792,10 @@ func (rs *ReturnStatement) Idx0() file.Idx {
 
 // Idx1 implements Node.
 func (rs *ReturnStatement) Idx1() file.Idx {
-	return rs.Return
+	if rs.Argument != nil {
+		return rs.Argument.Idx1()
+	}
+	return rs.Return + 6
 }
 
 // expression implements Statement.
@@ -800,6 +807,7 @@ type SwitchStatement struct {
 	Discriminant Expression
 	Default      int
 	Body         []*CaseStatement
+	RightBrace   file.Idx
 }
 
 // Idx0 implements Node.
@@ -809,7 +817,7 @@ func (ss *SwitchStatement) Idx0() file.Idx {
 
 // Idx1 implements Node.
 func (ss *SwitchStatement) Idx1() file.Idx {
-	return ss.Body[len(ss.Body)-1].Idx1()
+	return ss.RightBrace + 1
 }
 
 // expression implements Statement.
@@ -828,7 +836,7 @@ func (ts *ThrowStatement) Idx0() file.Idx {
 
 // Idx1 implements Node.
 func (ts *ThrowStatement) Idx1() file.Idx {
-	return ts.Throw
+	return ts.Argument.Idx1()
 }
 
 // expression implements Statement.
@@ -849,7 +857,10 @@ func (ts *TryStatement) Idx0() file.Idx {
 
 // Idx1 implements Node.
 func (ts *TryStatement) Idx1() file.Idx {
-	return ts.Try
+	if ts.Finally != nil {
+		return ts.Finally.Idx1()
+	}
+	return ts.Catch.Idx1()
 }
 
 // expression implements Statement.
diff --git a/parser/statement.go b/parser/statement.go
index f853afc..3abe175 100644
--- a/parser/statement.go
+++ b/parser/statement.go
@@ -356,7 +356,7 @@ func (p *parser) parseThrowStatement() ast.Statement {
 	}
 
 	node := &ast.ThrowStatement{
-		Throw:    p.idx,
+		Throw:    idx,
 		Argument: p.parseExpression(),
 	}
 	if p.mode&StoreComments != 0 {
@@ -373,12 +373,13 @@ func (p *parser) parseSwitchStatement() ast.Statement {
 	if p.mode&StoreComments != 0 {
 		comments = p.comments.FetchAll()
 	}
-	p.expect(token.SWITCH)
+	idx := p.expect(token.SWITCH)
 	if p.mode&StoreComments != 0 {
 		comments = append(comments, p.comments.FetchAll()...)
 	}
 	p.expect(token.LEFT_PARENTHESIS)
 	node := &ast.SwitchStatement{
+		Switch:       idx,
 		Discriminant: p.parseExpression(),
 		Default:      -1,
 	}
@@ -397,6 +398,7 @@ func (p *parser) parseSwitchStatement() ast.Statement {
 
 	for index := 0; p.token != token.EOF; index++ {
 		if p.token == token.RIGHT_BRACE {
+			node.RightBrace = p.idx
 			p.next()
 			break
 		}
@@ -423,7 +425,7 @@ func (p *parser) parseWithStatement() ast.Statement {
 	if p.mode&StoreComments != 0 {
 		comments = p.comments.FetchAll()
 	}
-	p.expect(token.WITH)
+	idx := p.expect(token.WITH)
 	var withComments []*ast.Comment
 	if p.mode&StoreComments != 0 {
 		withComments = p.comments.FetchAll()
@@ -432,6 +434,7 @@ func (p *parser) parseWithStatement() ast.Statement {
 	p.expect(token.LEFT_PARENTHESIS)
 
 	node := &ast.WithStatement{
+		With:   idx,
 		Object: p.parseExpression(),
 	}
 	p.expect(token.RIGHT_PARENTHESIS)
@@ -658,13 +661,13 @@ func (p *parser) parseDoWhileStatement() ast.Statement {
 	if p.mode&StoreComments != 0 {
 		comments = p.comments.FetchAll()
 	}
-	p.expect(token.DO)
+	idx := p.expect(token.DO)
 	var doComments []*ast.Comment
 	if p.mode&StoreComments != 0 {
 		doComments = p.comments.FetchAll()
 	}
 
-	node := &ast.DoWhileStatement{}
+	node := &ast.DoWhileStatement{Do: idx}
 	if p.token == token.LEFT_BRACE {
 		node.Body = p.parseBlockStatement()
 	} else {
@@ -678,7 +681,7 @@ func (p *parser) parseDoWhileStatement() ast.Statement {
 	}
 	p.expect(token.LEFT_PARENTHESIS)
 	node.Test = p.parseExpression()
-	p.expect(token.RIGHT_PARENTHESIS)
+	node.RightParenthesis = p.expect(token.RIGHT_PARENTHESIS)
 
 	p.implicitSemicolon = true
 	p.optionalSemicolon()
@@ -697,7 +700,7 @@ func (p *parser) parseWhileStatement() ast.Statement {
 	if p.mode&StoreComments != 0 {
 		comments = p.comments.FetchAll()
 	}
-	p.expect(token.WHILE)
+	idx := p.expect(token.WHILE)
 
 	var whileComments []*ast.Comment
 	if p.mode&StoreComments != 0 {
@@ -706,7 +709,8 @@ func (p *parser) parseWhileStatement() ast.Statement {
 
 	p.expect(token.LEFT_PARENTHESIS)
 	node := &ast.WhileStatement{
-		Test: p.parseExpression(),
+		While: idx,
+		Test:  p.parseExpression(),
 	}
 	p.expect(token.RIGHT_PARENTHESIS)
 	node.Body = p.parseIterationStatement()


Test patch:
diff --git a/parser/parser_test.go b/parser/parser_test.go
index e026030..727d83c 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -1025,7 +1025,8 @@ func TestPosition(t *testing.T) {
 		is(node.Idx0(), 14)
 		is(parser.slice(node.Idx0(), node.Idx1()), "if (abc) { throw 'failed'; }")
 		node = node.(*ast.IfStatement).Consequent.(*ast.BlockStatement).List[0].(*ast.ThrowStatement)
-		is(node.Idx0(), 39)
+		is(node.Idx0(), 25)
+		is(parser.slice(node.Idx0(), node.Idx1()), "throw 'failed'")
 
 		parser = newParser("", "(function(){ for (x=1; x<=4; x++) { console.log(x); } })", 1, nil)
 		program, err = parser.parse()
@@ -1100,6 +1101,91 @@ func TestPosition(t *testing.T) {
 		node = block.List[0].(*ast.VariableStatement).List[1].(*ast.VariableExpression)
 		is(node.Idx0(), 23)
 		is(parser.slice(node.Idx0(), node.Idx1()), "xyz = 1")
+
+		parser = newParser("", "for (i = 0; i < 10; i++) { if (i == 5) break; }", 1, nil)
+		program, err = parser.parse()
+		is(err, nil)
+		block = program.Body[0].(*ast.ForStatement).Body.(*ast.BlockStatement)
+		node = block.List[0].(*ast.IfStatement).Consequent.(*ast.BranchStatement)
+		is(node.Idx0(), 40)
+		is(parser.slice(node.Idx0(), node.Idx1()), "break")
+
+		parser = newParser("", "(function(){ xyz: for (i = 0; i < 10; i++) { if (i == 5) continue xyz; } })", 1, nil)
+		program, err = parser.parse()
+		is(err, nil)
+		block = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement)
+		node = block.List[0].(*ast.LabelledStatement)
+		is(node.Idx0(), 14)
+		is(parser.slice(node.Idx0(), node.Idx1()), "xyz: for (i = 0; i < 10; i++) { if (i == 5) continue xyz; }")
+		block = node.(*ast.LabelledStatement).Statement.(*ast.ForStatement).Body.(*ast.BlockStatement)
+		node = block.List[0].(*ast.IfStatement).Consequent.(*ast.BranchStatement)
+		is(node.Idx0(), 58)
+		is(parser.slice(node.Idx0(), node.Idx1()), "continue xyz")
+
+		parser = newParser("", "(function(){ return; })", 1, nil)
+		program, err = parser.parse()
+		is(err, nil)
+		block = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement)
+		node = block.List[0].(*ast.ReturnStatement)
+		is(node.Idx0(), 14)
+		is(parser.slice(node.Idx0(), node.Idx1()), "return")
+
+		parser = newParser("", "(function(){ return 10; })", 1, nil)
+		program, err = parser.parse()
+		is(err, nil)
+		block = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement)
+		node = block.List[0].(*ast.ReturnStatement)
+		is(node.Idx0(), 14)
+		is(parser.slice(node.Idx0(), node.Idx1()), "return 10")
+
+		parser = newParser("", "(function(){ switch (a) { default: return; }})", 1, nil)
+		program, err = parser.parse()
+		is(err, nil)
+		block = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement)
+		node = block.List[0].(*ast.SwitchStatement)
+		is(node.Idx0(), 14)
+		is(parser.slice(node.Idx0(), node.Idx1()), "switch (a) { default: return; }")
+
+		parser = newParser("", "(function(){ try { a(); } catch (error) { b(); } })", 1, nil)
+		program, err = parser.parse()
+		is(err, nil)
+		block = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement)
+		node = block.List[0].(*ast.TryStatement)
+		is(node.Idx0(), 14)
+		is(parser.slice(node.Idx0(), node.Idx1()), "try { a(); } catch (error) { b(); }")
+		node = block.List[0].(*ast.TryStatement).Catch
+		is(node.Idx0(), 27)
+		is(parser.slice(node.Idx0(), node.Idx1()), "catch (error) { b(); }")
+
+		parser = newParser("", "(function(){ try { a(); } catch (error) { b(); } finally { c(); } })", 1, nil)
+		program, err = parser.parse()
+		is(err, nil)
+		block = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement)
+		node = block.List[0].(*ast.TryStatement)
+		is(node.Idx0(), 14)
+		is(parser.slice(node.Idx0(), node.Idx1()), "try { a(); } catch (error) { b(); } finally { c(); }")
+
+		parser = newParser("", "(function(){ with (1) {} })", 1, nil)
+		program, err = parser.parse()
+		is(err, nil)
+		block = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement)
+		node = block.List[0].(*ast.WithStatement)
+		is(node.Idx0(), 14)
+		is(parser.slice(node.Idx0(), node.Idx1()), "with (1) {}")
+
+		parser = newParser("", "while (i < 10) { i++; }", 1, nil)
+		program, err = parser.parse()
+		is(err, nil)
+		node = program.Body[0].(*ast.WhileStatement)
+		is(node.Idx0(), 1)
+		is(parser.slice(node.Idx0(), node.Idx1()), "while (i < 10) { i++; }")
+
+		parser = newParser("", "do { i++; } while (i < 10 )", 1, nil)
+		program, err = parser.parse()
+		is(err, nil)
+		node = program.Body[0].(*ast.DoWhileStatement)
+		is(node.Idx0(), 1)
+		is(parser.slice(node.Idx0(), node.Idx1()), "do { i++; } while (i < 10 )")
 	})
 }