]> Cypherpunks repositories - gostls13.git/commitdiff
go/ast, go/parser: correct End() position for *ast.EmptyStmt
authorRobert Griesemer <gri@golang.org>
Tue, 24 Feb 2015 07:51:05 +0000 (23:51 -0800)
committerRobert Griesemer <gri@golang.org>
Tue, 24 Feb 2015 21:34:05 +0000 (21:34 +0000)
- added a new field ast.EmptyStmt.Implicit to indicate explicit
  or implicit semicolon
- fix ast.EmptyStmt.End() accordingly
- adjusted parser and added test case

Fixes #9979.

Change-Id: I72b0983b3a0cabea085598e1bf6c8df629776b57
Reviewed-on: https://go-review.googlesource.com/5720
Reviewed-by: Russ Cox <rsc@golang.org>
doc/go1.5.txt
src/go/ast/ast.go
src/go/parser/parser.go
src/go/parser/parser_test.go

index f17c7b9a5eecbeec0bba4f5b5dcc51a158e586c1..f70359d9d4efe6fa2d4880396646412c42bd64f2 100644 (file)
@@ -12,6 +12,7 @@ crypto/cipher: clarify what will happen if len(src) != len(dst) for the Stream i
 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)
index 312e3d1b9890e788c844048bbfc5f0a5568ddeb7..d21390ee55c1910f651e55b299617d73768585d1 100644 (file)
@@ -562,10 +562,11 @@ type (
 
        // 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.
@@ -734,6 +735,9 @@ func (s *RangeStmt) Pos() token.Pos      { return s.For }
 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() }
index 0409122c81787dcd1d8df2c95255f7e1f99b0bf0..d1b766cfbbe05ff1394eaaf624aa795969e055f9 100644 (file)
@@ -2152,11 +2152,14 @@ func (p *parser) parseStmt() (s ast.Stmt) {
        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
index 51ce1a9337ee1fb8ae689c905dc76a87a954fed4..4b960d9e577faf84b200d6a35bd1e0cbc694f370 100644 (file)
@@ -445,3 +445,50 @@ type T struct {
                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
+               })
+       }
+}