]> Cypherpunks repositories - gostls13.git/commitdiff
go/parser: set File{Start,End} correctly in all cases
authorAlan Donovan <adonovan@google.com>
Sat, 2 Nov 2024 17:38:44 +0000 (13:38 -0400)
committerAlan Donovan <adonovan@google.com>
Mon, 4 Nov 2024 14:39:37 +0000 (14:39 +0000)
...even when the file is empty or lacks a valid package decl.

+ test

Fixes #70162

Change-Id: Idf33998911475fe8cdfaa4786ac3ba1745f54963
Reviewed-on: https://go-review.googlesource.com/c/go/+/624655
Reviewed-by: Robert Griesemer <gri@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Findley <rfindley@google.com>
src/go/ast/ast.go
src/go/parser/interface.go
src/go/parser/parser.go
src/go/parser/parser_test.go

index 8d95eb12a95df6d0a70d81fbdb394e6c87971891..afe0a750fea21f1b99ac1b88e69db14644112c91 100644 (file)
@@ -1065,11 +1065,15 @@ type File struct {
 }
 
 // Pos returns the position of the package declaration.
-// (Use FileStart for the start of the entire file.)
+// It may be invalid, for example in an empty file.
+//
+// (Use FileStart for the start of the entire file. It is always valid.)
 func (f *File) Pos() token.Pos { return f.Package }
 
 // End returns the end of the last declaration in the file.
-// (Use FileEnd for the end of the entire file.)
+// It may be invalid, for example in an empty file.
+//
+// (Use FileEnd for the end of the entire file. It is always valid.)
 func (f *File) End() token.Pos {
        if n := len(f.Decls); n > 0 {
                return f.Decls[n-1].End()
index 11d42642f0d20098ad6ac441d4295be30fdb1c5a..71a612f7cf49670c331d5ba0f0e57d7538254746 100644 (file)
@@ -92,6 +92,8 @@ func ParseFile(fset *token.FileSet, filename string, src any, mode Mode) (f *ast
                return nil, err
        }
 
+       file := fset.AddFile(filename, -1, len(text))
+
        var p parser
        defer func() {
                if e := recover(); e != nil {
@@ -115,12 +117,17 @@ func ParseFile(fset *token.FileSet, filename string, src any, mode Mode) (f *ast
                        }
                }
 
+               // Ensure the start/end are consistent,
+               // whether parsing succeeded or not.
+               f.FileStart = token.Pos(file.Base())
+               f.FileEnd = token.Pos(file.Base() + file.Size())
+
                p.errors.Sort()
                err = p.errors.Err()
        }()
 
        // parse source
-       p.init(fset, filename, text, mode)
+       p.init(file, text, mode)
        f = p.parseFile()
 
        return
@@ -215,7 +222,8 @@ func ParseExprFrom(fset *token.FileSet, filename string, src any, mode Mode) (ex
        }()
 
        // parse expr
-       p.init(fset, filename, text, mode)
+       file := fset.AddFile(filename, -1, len(text))
+       p.init(file, text, mode)
        expr = p.parseRhs()
 
        // If a semicolon was inserted, consume it;
index cbd1d93fa131014e4cecf7d5fefb7ee4e8fbd9e2..46b2d4e7dc90c583a2e7be14febebdaeca96c624 100644 (file)
@@ -65,8 +65,8 @@ type parser struct {
        nestLev int
 }
 
-func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode Mode) {
-       p.file = fset.AddFile(filename, -1, len(src))
+func (p *parser) init(file *token.File, src []byte, mode Mode) {
+       p.file = file
        eh := func(pos token.Position, msg string) { p.errors.Add(pos, msg) }
        p.scanner.Init(p.file, src, eh, scanner.ScanComments)
 
@@ -2900,12 +2900,11 @@ func (p *parser) parseFile() *ast.File {
        }
 
        f := &ast.File{
-               Doc:       doc,
-               Package:   pos,
-               Name:      ident,
-               Decls:     decls,
-               FileStart: token.Pos(p.file.Base()),
-               FileEnd:   token.Pos(p.file.Base() + p.file.Size()),
+               Doc:     doc,
+               Package: pos,
+               Name:    ident,
+               Decls:   decls,
+               // File{Start,End} are set by the defer in the caller.
                Imports:   p.imports,
                Comments:  p.comments,
                GoVersion: p.goVersion,
index 244d43c663b06ab8bd1bfe4ab71b9abdc9ae8cea..869d803df67475cacd72231d020a1e7a7eb1a00e 100644 (file)
@@ -838,3 +838,23 @@ func TestParseTypeParamsAsParenExpr(t *testing.T) {
                t.Fatalf("typeParam is a %T; want: *ast.ParenExpr", typeParam)
        }
 }
+
+// TestEmptyFileHasValidStartEnd is a regression test for #70162.
+func TestEmptyFileHasValidStartEnd(t *testing.T) {
+       for _, test := range []struct {
+               src  string
+               want string // "Pos() FileStart FileEnd"
+       }{
+               {src: "", want: "0 1 1"},
+               {src: "package ", want: "0 1 9"},
+               {src: "package p", want: "1 1 10"},
+               {src: "type T int", want: "0 1 11"},
+       } {
+               fset := token.NewFileSet()
+               f, _ := ParseFile(fset, "a.go", test.src, 0)
+               got := fmt.Sprintf("%d %d %d", f.Pos(), f.FileStart, f.FileEnd)
+               if got != test.want {
+                       t.Fatalf("src = %q: got %s, want %s", test.src, got, test.want)
+               }
+       }
+}