Skip to content

Commit 3e45703

Browse files
committed
go/parser, go/ast: correctly take into account presence of } in block
Correctly track whether the closing } of a block (or a function body) is present or not in the AST and report correct End() positions in each case. There are more cases like this but this CL addresses an immediate issue and sets a precedent for how to fix similar cases if a need arises. Fixes #33649. Change-Id: Id6662ddaac09f3c15f8003edc9275fe2b0c41c78 Reviewed-on: https://go-review.googlesource.com/c/go/+/202581 Run-TryBot: Robert Griesemer <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Alan Donovan <[email protected]>
1 parent 9be36ba commit 3e45703

File tree

3 files changed

+66
-4
lines changed

3 files changed

+66
-4
lines changed

src/go/ast/ast.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,7 @@ type (
634634
BlockStmt struct {
635635
Lbrace token.Pos // position of "{"
636636
List []Stmt
637-
Rbrace token.Pos // position of "}"
637+
Rbrace token.Pos // position of "}", if any (may be absent due to syntax error)
638638
}
639639

640640
// An IfStmt node represents an if statement.
@@ -757,7 +757,15 @@ func (s *BranchStmt) End() token.Pos {
757757
}
758758
return token.Pos(int(s.TokPos) + len(s.Tok.String()))
759759
}
760-
func (s *BlockStmt) End() token.Pos { return s.Rbrace + 1 }
760+
func (s *BlockStmt) End() token.Pos {
761+
if s.Rbrace.IsValid() {
762+
return s.Rbrace + 1
763+
}
764+
if n := len(s.List); n > 0 {
765+
return s.List[n-1].End()
766+
}
767+
return s.Lbrace + 1
768+
}
761769
func (s *IfStmt) End() token.Pos {
762770
if s.Else != nil {
763771
return s.Else.End()

src/go/ast/issues_test.go

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2019 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package ast_test
6+
7+
import (
8+
"go/ast"
9+
"go/parser"
10+
"go/token"
11+
"testing"
12+
)
13+
14+
func TestIssue33649(t *testing.T) {
15+
for _, src := range []string{
16+
`package p; func _()`,
17+
`package p; func _() {`,
18+
`package p; func _() { _ = 0`,
19+
`package p; func _() { _ = 0 }`,
20+
} {
21+
fset := token.NewFileSet()
22+
f, _ := parser.ParseFile(fset, "", src, parser.AllErrors)
23+
if f == nil {
24+
panic("invalid test setup: parser didn't return an AST")
25+
}
26+
27+
// find corresponding token.File
28+
var tf *token.File
29+
fset.Iterate(func(f *token.File) bool {
30+
tf = f
31+
return true
32+
})
33+
tfEnd := tf.Base() + tf.Size()
34+
35+
fd := f.Decls[len(f.Decls)-1].(*ast.FuncDecl)
36+
fdEnd := int(fd.End())
37+
38+
if fdEnd != tfEnd {
39+
t.Errorf("%q: got fdEnd = %d; want %d (base = %d, size = %d)", src, fdEnd, tfEnd, tf.Base(), tf.Size())
40+
}
41+
}
42+
}

src/go/parser/parser.go

+14-2
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,18 @@ func (p *parser) expect(tok token.Token) token.Pos {
397397
return pos
398398
}
399399

400+
// expect2 is like expect, but it returns an invalid position
401+
// if the expected token is not found.
402+
func (p *parser) expect2(tok token.Token) (pos token.Pos) {
403+
if p.tok == tok {
404+
pos = p.pos
405+
} else {
406+
p.errorExpected(pos, "'"+tok.String()+"'")
407+
}
408+
p.next() // make progress
409+
return
410+
}
411+
400412
// expectClosing is like expect but provides a better error message
401413
// for the common case of a missing comma before a newline.
402414
//
@@ -1082,7 +1094,7 @@ func (p *parser) parseBody(scope *ast.Scope) *ast.BlockStmt {
10821094
list := p.parseStmtList()
10831095
p.closeLabelScope()
10841096
p.closeScope()
1085-
rbrace := p.expect(token.RBRACE)
1097+
rbrace := p.expect2(token.RBRACE)
10861098

10871099
return &ast.BlockStmt{Lbrace: lbrace, List: list, Rbrace: rbrace}
10881100
}
@@ -1096,7 +1108,7 @@ func (p *parser) parseBlockStmt() *ast.BlockStmt {
10961108
p.openScope()
10971109
list := p.parseStmtList()
10981110
p.closeScope()
1099-
rbrace := p.expect(token.RBRACE)
1111+
rbrace := p.expect2(token.RBRACE)
11001112

11011113
return &ast.BlockStmt{Lbrace: lbrace, List: list, Rbrace: rbrace}
11021114
}

0 commit comments

Comments
 (0)