Skip to content

Commit 9c5f5bd

Browse files
committed
go/parser: set File{Start,End} correctly in all cases
...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 <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent 9c93d99 commit 9c5f5bd

File tree

4 files changed

+43
-12
lines changed

4 files changed

+43
-12
lines changed

src/go/ast/ast.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,11 +1065,15 @@ type File struct {
10651065
}
10661066

10671067
// Pos returns the position of the package declaration.
1068-
// (Use FileStart for the start of the entire file.)
1068+
// It may be invalid, for example in an empty file.
1069+
//
1070+
// (Use FileStart for the start of the entire file. It is always valid.)
10691071
func (f *File) Pos() token.Pos { return f.Package }
10701072

10711073
// End returns the end of the last declaration in the file.
1072-
// (Use FileEnd for the end of the entire file.)
1074+
// It may be invalid, for example in an empty file.
1075+
//
1076+
// (Use FileEnd for the end of the entire file. It is always valid.)
10731077
func (f *File) End() token.Pos {
10741078
if n := len(f.Decls); n > 0 {
10751079
return f.Decls[n-1].End()

src/go/parser/interface.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ func ParseFile(fset *token.FileSet, filename string, src any, mode Mode) (f *ast
9292
return nil, err
9393
}
9494

95+
file := fset.AddFile(filename, -1, len(text))
96+
9597
var p parser
9698
defer func() {
9799
if e := recover(); e != nil {
@@ -115,12 +117,17 @@ func ParseFile(fset *token.FileSet, filename string, src any, mode Mode) (f *ast
115117
}
116118
}
117119

120+
// Ensure the start/end are consistent,
121+
// whether parsing succeeded or not.
122+
f.FileStart = token.Pos(file.Base())
123+
f.FileEnd = token.Pos(file.Base() + file.Size())
124+
118125
p.errors.Sort()
119126
err = p.errors.Err()
120127
}()
121128

122129
// parse source
123-
p.init(fset, filename, text, mode)
130+
p.init(file, text, mode)
124131
f = p.parseFile()
125132

126133
return
@@ -215,7 +222,8 @@ func ParseExprFrom(fset *token.FileSet, filename string, src any, mode Mode) (ex
215222
}()
216223

217224
// parse expr
218-
p.init(fset, filename, text, mode)
225+
file := fset.AddFile(filename, -1, len(text))
226+
p.init(file, text, mode)
219227
expr = p.parseRhs()
220228

221229
// If a semicolon was inserted, consume it;

src/go/parser/parser.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ type parser struct {
6565
nestLev int
6666
}
6767

68-
func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode Mode) {
69-
p.file = fset.AddFile(filename, -1, len(src))
68+
func (p *parser) init(file *token.File, src []byte, mode Mode) {
69+
p.file = file
7070
eh := func(pos token.Position, msg string) { p.errors.Add(pos, msg) }
7171
p.scanner.Init(p.file, src, eh, scanner.ScanComments)
7272

@@ -2900,12 +2900,11 @@ func (p *parser) parseFile() *ast.File {
29002900
}
29012901

29022902
f := &ast.File{
2903-
Doc: doc,
2904-
Package: pos,
2905-
Name: ident,
2906-
Decls: decls,
2907-
FileStart: token.Pos(p.file.Base()),
2908-
FileEnd: token.Pos(p.file.Base() + p.file.Size()),
2903+
Doc: doc,
2904+
Package: pos,
2905+
Name: ident,
2906+
Decls: decls,
2907+
// File{Start,End} are set by the defer in the caller.
29092908
Imports: p.imports,
29102909
Comments: p.comments,
29112910
GoVersion: p.goVersion,

src/go/parser/parser_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,3 +838,23 @@ func TestParseTypeParamsAsParenExpr(t *testing.T) {
838838
t.Fatalf("typeParam is a %T; want: *ast.ParenExpr", typeParam)
839839
}
840840
}
841+
842+
// TestEmptyFileHasValidStartEnd is a regression test for #70162.
843+
func TestEmptyFileHasValidStartEnd(t *testing.T) {
844+
for _, test := range []struct {
845+
src string
846+
want string // "Pos() FileStart FileEnd"
847+
}{
848+
{src: "", want: "0 1 1"},
849+
{src: "package ", want: "0 1 9"},
850+
{src: "package p", want: "1 1 10"},
851+
{src: "type T int", want: "0 1 11"},
852+
} {
853+
fset := token.NewFileSet()
854+
f, _ := ParseFile(fset, "a.go", test.src, 0)
855+
got := fmt.Sprintf("%d %d %d", f.Pos(), f.FileStart, f.FileEnd)
856+
if got != test.want {
857+
t.Fatalf("src = %q: got %s, want %s", test.src, got, test.want)
858+
}
859+
}
860+
}

0 commit comments

Comments
 (0)