Skip to content

Commit f1d281f

Browse files
adonovangopherbot
authored andcommitted
go/ast: record start and end of file in File.File{Start,End}
This change causes the parser to record the positions of the first and last character in the file in new ast.File fields FileStart and FileEnd. The behavior of the existing Pos() and End() methods, which record the span of declarations, must remain unchanged for compatibility. Fixes #53202 Change-Id: I250b19e69f41e3590292c3fe6dea1943ec98f629 Reviewed-on: https://go-review.googlesource.com/c/go/+/427955 TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]> Run-TryBot: Alan Donovan <[email protected]> Auto-Submit: Alan Donovan <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent dbf174d commit f1d281f

File tree

6 files changed

+72
-24
lines changed

6 files changed

+72
-24
lines changed

api/next/53202.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pkg go/ast, type File struct, FileEnd token.Pos #53202
2+
pkg go/ast, type File struct, FileStart token.Pos #53202

src/go/ast/ast.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,17 +1036,24 @@ func (*FuncDecl) declNode() {}
10361036
// and Comment comments directly associated with nodes, the remaining comments
10371037
// are "free-floating" (see also issues #18593, #20744).
10381038
type File struct {
1039-
Doc *CommentGroup // associated documentation; or nil
1040-
Package token.Pos // position of "package" keyword
1041-
Name *Ident // package name
1042-
Decls []Decl // top-level declarations; or nil
1043-
Scope *Scope // package scope (this file only)
1044-
Imports []*ImportSpec // imports in this file
1045-
Unresolved []*Ident // unresolved identifiers in this file
1046-
Comments []*CommentGroup // list of all comments in the source file
1039+
Doc *CommentGroup // associated documentation; or nil
1040+
Package token.Pos // position of "package" keyword
1041+
Name *Ident // package name
1042+
Decls []Decl // top-level declarations; or nil
1043+
1044+
FileStart, FileEnd token.Pos // start and end of entire file
1045+
Scope *Scope // package scope (this file only)
1046+
Imports []*ImportSpec // imports in this file
1047+
Unresolved []*Ident // unresolved identifiers in this file
1048+
Comments []*CommentGroup // list of all comments in the source file
10471049
}
10481050

1051+
// Pos returns the position of the package declaration.
1052+
// (Use FileStart for the start of the entire file.)
10491053
func (f *File) Pos() token.Pos { return f.Package }
1054+
1055+
// End returns the end of the last declaration in the file.
1056+
// (Use FileEnd for the end of the entire file.)
10501057
func (f *File) End() token.Pos {
10511058
if n := len(f.Decls); n > 0 {
10521059
return f.Decls[n-1].End()

src/go/ast/example_test.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -126,15 +126,17 @@ func main() {
126126
// 47 . . . }
127127
// 48 . . }
128128
// 49 . }
129-
// 50 . Scope: *ast.Scope {
130-
// 51 . . Objects: map[string]*ast.Object (len = 1) {
131-
// 52 . . . "main": *(obj @ 11)
132-
// 53 . . }
133-
// 54 . }
134-
// 55 . Unresolved: []*ast.Ident (len = 1) {
135-
// 56 . . 0: *(obj @ 29)
136-
// 57 . }
137-
// 58 }
129+
// 50 . FileStart: 1:1
130+
// 51 . FileEnd: 5:3
131+
// 52 . Scope: *ast.Scope {
132+
// 53 . . Objects: map[string]*ast.Object (len = 1) {
133+
// 54 . . . "main": *(obj @ 11)
134+
// 55 . . }
135+
// 56 . }
136+
// 57 . Unresolved: []*ast.Ident (len = 1) {
137+
// 58 . . 0: *(obj @ 29)
138+
// 59 . }
139+
// 60 }
138140
}
139141

140142
// This example illustrates how to remove a variable declaration

src/go/ast/filter.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File {
340340
ncomments := 0
341341
ndecls := 0
342342
filenames := make([]string, len(pkg.Files))
343+
var minPos, maxPos token.Pos
343344
i := 0
344345
for filename, f := range pkg.Files {
345346
filenames[i] = filename
@@ -349,6 +350,12 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File {
349350
}
350351
ncomments += len(f.Comments)
351352
ndecls += len(f.Decls)
353+
if i == 0 || f.FileStart < minPos {
354+
minPos = f.FileStart
355+
}
356+
if i == 0 || f.FileEnd > maxPos {
357+
maxPos = f.FileEnd
358+
}
352359
}
353360
sort.Strings(filenames)
354361

@@ -484,5 +491,5 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File {
484491
}
485492

486493
// TODO(gri) need to compute unresolved identifiers!
487-
return &File{doc, pos, NewIdent(pkg.Name), decls, pkg.Scope, imports, nil, comments}
494+
return &File{doc, pos, NewIdent(pkg.Name), decls, minPos, maxPos, pkg.Scope, imports, nil, comments}
488495
}

src/go/parser/parser.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2836,12 +2836,14 @@ func (p *parser) parseFile() *ast.File {
28362836
}
28372837

28382838
f := &ast.File{
2839-
Doc: doc,
2840-
Package: pos,
2841-
Name: ident,
2842-
Decls: decls,
2843-
Imports: p.imports,
2844-
Comments: p.comments,
2839+
Doc: doc,
2840+
Package: pos,
2841+
Name: ident,
2842+
Decls: decls,
2843+
FileStart: token.Pos(p.file.Base()),
2844+
FileEnd: token.Pos(p.file.Base() + p.file.Size()),
2845+
Imports: p.imports,
2846+
Comments: p.comments,
28452847
}
28462848
var declErr func(token.Pos, string)
28472849
if p.mode&DeclarationErrors != 0 {

src/go/parser/parser_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,34 @@ func TestIssue9979(t *testing.T) {
488488
}
489489
}
490490

491+
func TestFileStartEndPos(t *testing.T) {
492+
const src = `// Copyright
493+
494+
//+build tag
495+
496+
// Package p doc comment.
497+
package p
498+
499+
var lastDecl int
500+
501+
/* end of file */
502+
`
503+
fset := token.NewFileSet()
504+
f, err := ParseFile(fset, "file.go", src, 0)
505+
if err != nil {
506+
t.Fatal(err)
507+
}
508+
509+
// File{Start,End} spans the entire file, not just the declarations.
510+
if got, want := fset.Position(f.FileStart).String(), "file.go:1:1"; got != want {
511+
t.Errorf("for File.FileStart, got %s, want %s", got, want)
512+
}
513+
// The end position is the newline at the end of the /* end of file */ line.
514+
if got, want := fset.Position(f.FileEnd).String(), "file.go:10:19"; got != want {
515+
t.Errorf("for File.FileEnd, got %s, want %s", got, want)
516+
}
517+
}
518+
491519
// TestIncompleteSelection ensures that an incomplete selector
492520
// expression is parsed as a (blank) *ast.SelectorExpr, not a
493521
// *ast.BadExpr.

0 commit comments

Comments
 (0)