...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>
}
// 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()
return nil, err
}
+ file := fset.AddFile(filename, -1, len(text))
+
var p parser
defer func() {
if e := recover(); e != nil {
}
}
+ // 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
}()
// 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;
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)
}
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,
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)
+ }
+ }
+}