crypto/elliptic: add Name field to CurveParams struct (https://golang.org/cl/2133)
crypto/tls: change default minimum version to TLS 1.0. (https://golang.org/cl/1791)
encoding/base64: add unpadded encodings (https://golang.org/cl/1511)
+go/ast: add Implicit field to ast.EmptyStmt; changed meaning of ast.EmptyStmt.Semicolon position (https://golang.org/cl/5720)
log: add SetOutput functions (https://golang.org/cl/2686, https://golang.org/cl/3023)
net/http: support for setting trailers from a server Handler (https://golang.org/cl/2157)
net/http/cgi: fix REMOTE_ADDR, REMOTE_HOST, add REMOTE_PORT (https://golang.org/cl/4933)
// An EmptyStmt node represents an empty statement.
// The "position" of the empty statement is the position
- // of the immediately preceding semicolon.
+ // of the immediately following (explicit or implicit) semicolon.
//
EmptyStmt struct {
- Semicolon token.Pos // position of preceding ";"
+ Semicolon token.Pos // position of following ";"
+ Implicit bool // if set, ";" was omitted in the source
}
// A LabeledStmt node represents a labeled statement.
func (s *BadStmt) End() token.Pos { return s.To }
func (s *DeclStmt) End() token.Pos { return s.Decl.End() }
func (s *EmptyStmt) End() token.Pos {
+ if s.Implicit {
+ return s.Semicolon
+ }
return s.Semicolon + 1 /* len(";") */
}
func (s *LabeledStmt) End() token.Pos { return s.Stmt.End() }
case token.FOR:
s = p.parseForStmt()
case token.SEMICOLON:
- s = &ast.EmptyStmt{Semicolon: p.pos}
+ // Is it ever possible to have an implicit semicolon
+ // producing an empty statement in a valid program?
+ // (handle correctly anyway)
+ s = &ast.EmptyStmt{Semicolon: p.pos, Implicit: p.lit == "\n"}
p.next()
case token.RBRACE:
// a semicolon may be omitted before a closing "}"
- s = &ast.EmptyStmt{Semicolon: p.pos}
+ s = &ast.EmptyStmt{Semicolon: p.pos, Implicit: true}
default:
// no statement found
pos := p.pos
t.Error("not expected to find T.f3")
}
}
+
+// TestIssue9979 verifies that empty statements are contained within their enclosing blocks.
+func TestIssue9979(t *testing.T) {
+ for _, src := range []string{
+ "package p; func f() {;}",
+ "package p; func f() {L:}",
+ "package p; func f() {L:;}",
+ "package p; func f() {L:\n}",
+ "package p; func f() {L:\n;}",
+ "package p; func f() { ; }",
+ "package p; func f() { L: }",
+ "package p; func f() { L: ; }",
+ "package p; func f() { L: \n}",
+ "package p; func f() { L: \n; }",
+ } {
+ fset := token.NewFileSet()
+ f, err := ParseFile(fset, "", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var pos, end token.Pos
+ ast.Inspect(f, func(x ast.Node) bool {
+ switch s := x.(type) {
+ case *ast.BlockStmt:
+ pos, end = s.Pos()+1, s.End()-1 // exclude "{", "}"
+ case *ast.LabeledStmt:
+ pos, end = s.Pos()+2, s.End() // exclude "L:"
+ case *ast.EmptyStmt:
+ // check containment
+ if s.Pos() < pos || s.End() > end {
+ t.Errorf("%s: %T[%d, %d] not inside [%d, %d]", src, s, s.Pos(), s.End(), pos, end)
+ }
+ // check semicolon
+ offs := fset.Position(s.Pos()).Offset
+ if ch := src[offs]; ch != ';' != s.Implicit {
+ want := "want ';'"
+ if s.Implicit {
+ want = "but ';' is implicit"
+ }
+ t.Errorf("%s: found %q at offset %d; %s", src, ch, offs, want)
+ }
+ }
+ return true
+ })
+ }
+}