]> Cypherpunks repositories - gostls13.git/commitdiff
go/ast: record start and end of file in File.File{Start,End}
authorAlan Donovan <adonovan@google.com>
Fri, 2 Sep 2022 15:13:43 +0000 (11:13 -0400)
committerGopher Robot <gobot@golang.org>
Wed, 28 Sep 2022 20:37:59 +0000 (20:37 +0000)
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 golang/go#53202

Change-Id: I250b19e69f41e3590292c3fe6dea1943ec98f629
Reviewed-on: https://go-review.googlesource.com/c/go/+/427955
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@google.com>
Run-TryBot: Alan Donovan <adonovan@google.com>
Auto-Submit: Alan Donovan <adonovan@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
api/next/53202.txt [new file with mode: 0644]
src/go/ast/ast.go
src/go/ast/example_test.go
src/go/ast/filter.go
src/go/parser/parser.go
src/go/parser/parser_test.go

diff --git a/api/next/53202.txt b/api/next/53202.txt
new file mode 100644 (file)
index 0000000..8dadbfb
--- /dev/null
@@ -0,0 +1,2 @@
+pkg go/ast, type File struct, FileEnd token.Pos #53202
+pkg go/ast, type File struct, FileStart token.Pos #53202
index 8d138fc72af6f282645d554eb783827ce2f41166..9baf72f40f785b109e92287bb1cac36df821bc2e 100644 (file)
@@ -1036,17 +1036,24 @@ func (*FuncDecl) declNode() {}
 // and Comment comments directly associated with nodes, the remaining comments
 // are "free-floating" (see also issues #18593, #20744).
 type File struct {
-       Doc        *CommentGroup   // associated documentation; or nil
-       Package    token.Pos       // position of "package" keyword
-       Name       *Ident          // package name
-       Decls      []Decl          // top-level declarations; or nil
-       Scope      *Scope          // package scope (this file only)
-       Imports    []*ImportSpec   // imports in this file
-       Unresolved []*Ident        // unresolved identifiers in this file
-       Comments   []*CommentGroup // list of all comments in the source file
+       Doc     *CommentGroup // associated documentation; or nil
+       Package token.Pos     // position of "package" keyword
+       Name    *Ident        // package name
+       Decls   []Decl        // top-level declarations; or nil
+
+       FileStart, FileEnd token.Pos       // start and end of entire file
+       Scope              *Scope          // package scope (this file only)
+       Imports            []*ImportSpec   // imports in this file
+       Unresolved         []*Ident        // unresolved identifiers in this file
+       Comments           []*CommentGroup // list of all comments in the source file
 }
 
+// Pos returns the position of the package declaration.
+// (Use FileStart for the start of the entire file.)
 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.)
 func (f *File) End() token.Pos {
        if n := len(f.Decls); n > 0 {
                return f.Decls[n-1].End()
index 67860ce922d2cbea025eb3ad9caa78aad68300e8..c6904be6e553e6f10be14646021484692c3d23f7 100644 (file)
@@ -126,15 +126,17 @@ func main() {
        //     47  .  .  .  }
        //     48  .  .  }
        //     49  .  }
-       //     50  .  Scope: *ast.Scope {
-       //     51  .  .  Objects: map[string]*ast.Object (len = 1) {
-       //     52  .  .  .  "main": *(obj @ 11)
-       //     53  .  .  }
-       //     54  .  }
-       //     55  .  Unresolved: []*ast.Ident (len = 1) {
-       //     56  .  .  0: *(obj @ 29)
-       //     57  .  }
-       //     58  }
+       //     50  .  FileStart: 1:1
+       //     51  .  FileEnd: 5:3
+       //     52  .  Scope: *ast.Scope {
+       //     53  .  .  Objects: map[string]*ast.Object (len = 1) {
+       //     54  .  .  .  "main": *(obj @ 11)
+       //     55  .  .  }
+       //     56  .  }
+       //     57  .  Unresolved: []*ast.Ident (len = 1) {
+       //     58  .  .  0: *(obj @ 29)
+       //     59  .  }
+       //     60  }
 }
 
 // This example illustrates how to remove a variable declaration
index 2fc73c4b99420bb7b88b49c8d7c5d43632c3c747..7d2a11e4750b487f893e64085d48237366acc607 100644 (file)
@@ -340,6 +340,7 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File {
        ncomments := 0
        ndecls := 0
        filenames := make([]string, len(pkg.Files))
+       var minPos, maxPos token.Pos
        i := 0
        for filename, f := range pkg.Files {
                filenames[i] = filename
@@ -349,6 +350,12 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File {
                }
                ncomments += len(f.Comments)
                ndecls += len(f.Decls)
+               if i == 0 || f.FileStart < minPos {
+                       minPos = f.FileStart
+               }
+               if i == 0 || f.FileEnd > maxPos {
+                       maxPos = f.FileEnd
+               }
        }
        sort.Strings(filenames)
 
@@ -484,5 +491,5 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File {
        }
 
        // TODO(gri) need to compute unresolved identifiers!
-       return &File{doc, pos, NewIdent(pkg.Name), decls, pkg.Scope, imports, nil, comments}
+       return &File{doc, pos, NewIdent(pkg.Name), decls, minPos, maxPos, pkg.Scope, imports, nil, comments}
 }
index 5ee53fc81ebf1a5ef4fe9794b1555bd65f51814d..89ed0e433fb8f2d143e9c522015f5dc4d69ba8ec 100644 (file)
@@ -2836,12 +2836,14 @@ func (p *parser) parseFile() *ast.File {
        }
 
        f := &ast.File{
-               Doc:      doc,
-               Package:  pos,
-               Name:     ident,
-               Decls:    decls,
-               Imports:  p.imports,
-               Comments: p.comments,
+               Doc:       doc,
+               Package:   pos,
+               Name:      ident,
+               Decls:     decls,
+               FileStart: token.Pos(p.file.Base()),
+               FileEnd:   token.Pos(p.file.Base() + p.file.Size()),
+               Imports:   p.imports,
+               Comments:  p.comments,
        }
        var declErr func(token.Pos, string)
        if p.mode&DeclarationErrors != 0 {
index 6d559e231cce1be7a8ef6cc8e793eb3110a41372..153562df75068de8f5ddee0a2f4fb946fded1510 100644 (file)
@@ -488,6 +488,34 @@ func TestIssue9979(t *testing.T) {
        }
 }
 
+func TestFileStartEndPos(t *testing.T) {
+       const src = `// Copyright
+
+//+build tag
+
+// Package p doc comment.
+package p
+
+var lastDecl int
+
+/* end of file */
+`
+       fset := token.NewFileSet()
+       f, err := ParseFile(fset, "file.go", src, 0)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       // File{Start,End} spans the entire file, not just the declarations.
+       if got, want := fset.Position(f.FileStart).String(), "file.go:1:1"; got != want {
+               t.Errorf("for File.FileStart, got %s, want %s", got, want)
+       }
+       // The end position is the newline at the end of the /* end of file */ line.
+       if got, want := fset.Position(f.FileEnd).String(), "file.go:10:19"; got != want {
+               t.Errorf("for File.FileEnd, got %s, want %s", got, want)
+       }
+}
+
 // TestIncompleteSelection ensures that an incomplete selector
 // expression is parsed as a (blank) *ast.SelectorExpr, not a
 // *ast.BadExpr.