go/token: add FileSet.AddExistingFiles
+ test, doc, relnote Fixes #73205 Change-Id: Id3a4cc6290c55ffa518ad174a02ccca85e8636f7 Reviewed-on: https://go-review.googlesource.com/c/go/+/672875 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
11c86ddcb8
commit
972639fc4c
4 changed files with 113 additions and 0 deletions
1
api/next/73205.txt
Normal file
1
api/next/73205.txt
Normal file
|
@ -0,0 +1 @@
|
|||
pkg go/token, method (*FileSet) AddExistingFiles(...*File) #73205
|
4
doc/next/6-stdlib/99-minor/go/token/73205.md
Normal file
4
doc/next/6-stdlib/99-minor/go/token/73205.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
The new [FileSet.AddExistingFiles] method enables existing Files to be
|
||||
added to a FileSet, or a FileSet to be constructed for an arbitrary
|
||||
set of Files, alleviating the problems associated with a single global
|
||||
FileSet in long-lived applications.
|
|
@ -98,6 +98,9 @@ func (p Pos) IsValid() bool {
|
|||
|
||||
// A File is a handle for a file belonging to a [FileSet].
|
||||
// A File has a name, size, and line offset table.
|
||||
//
|
||||
// Use [FileSet.AddFile] to create a File.
|
||||
// A File may belong to more than one FileSet; see [FileSet.AddExistingFiles].
|
||||
type File struct {
|
||||
name string // file name as provided to AddFile
|
||||
base int // Pos value range for this file is [base...base+size]
|
||||
|
@ -489,6 +492,69 @@ func (s *FileSet) AddFile(filename string, base, size int) *File {
|
|||
return f
|
||||
}
|
||||
|
||||
// AddExistingFiles adds the specified files to the
|
||||
// FileSet if they are not already present.
|
||||
// The caller must ensure that no pair of Files that
|
||||
// would appear in the resulting FileSet overlap.
|
||||
func (s *FileSet) AddExistingFiles(files ...*File) {
|
||||
// This function cannot be implemented as:
|
||||
//
|
||||
// for _, file := range files {
|
||||
// if prev := fset.File(token.Pos(file.Base())); prev != nil {
|
||||
// if prev != file {
|
||||
// panic("FileSet contains a different file at the same base")
|
||||
// }
|
||||
// continue
|
||||
// }
|
||||
// file2 := fset.AddFile(file.Name(), file.Base(), file.Size())
|
||||
// file2.SetLines(file.Lines())
|
||||
// }
|
||||
//
|
||||
// because all calls to AddFile must be in increasing order.
|
||||
// AddExistingFilesFiles lets us augment an existing FileSet
|
||||
// sequentially, so long as all sets of files have disjoint ranges.
|
||||
// This approach also does not preserve line directives.
|
||||
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
// Merge and sort.
|
||||
newFiles := append(s.files, files...)
|
||||
slices.SortFunc(newFiles, func(x, y *File) int {
|
||||
return cmp.Compare(x.Base(), y.Base())
|
||||
})
|
||||
|
||||
// Reject overlapping files.
|
||||
// Discard adjacent identical files.
|
||||
out := newFiles[:0]
|
||||
for i, file := range newFiles {
|
||||
if i > 0 {
|
||||
prev := newFiles[i-1]
|
||||
if file == prev {
|
||||
continue
|
||||
}
|
||||
if prev.Base()+prev.Size()+1 > file.Base() {
|
||||
panic(fmt.Sprintf("file %s (%d-%d) overlaps with file %s (%d-%d)",
|
||||
prev.Name(), prev.Base(), prev.Base()+prev.Size(),
|
||||
file.Name(), file.Base(), file.Base()+file.Size()))
|
||||
}
|
||||
}
|
||||
out = append(out, file)
|
||||
}
|
||||
newFiles = out
|
||||
|
||||
s.files = newFiles
|
||||
|
||||
// Advance base.
|
||||
if len(newFiles) > 0 {
|
||||
last := newFiles[len(newFiles)-1]
|
||||
newBase := last.Base() + last.Size() + 1
|
||||
if s.base < newBase {
|
||||
s.base = newBase
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveFile removes a file from the [FileSet] so that subsequent
|
||||
// queries for its [Pos] interval yield a negative result.
|
||||
// This reduces the memory usage of a long-lived [FileSet] that
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"fmt"
|
||||
"math/rand"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
@ -537,3 +538,44 @@ func TestIssue57490(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileSet_AddExistingFiles(t *testing.T) {
|
||||
fset := NewFileSet()
|
||||
|
||||
check := func(descr, want string) {
|
||||
t.Helper()
|
||||
if got := fsetString(fset); got != want {
|
||||
t.Errorf("%s: got %s, want %s", descr, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
fileA := fset.AddFile("A", -1, 3)
|
||||
fileB := fset.AddFile("B", -1, 5)
|
||||
_ = fileB
|
||||
check("after AddFile [AB]", "{A:1-4 B:5-10}")
|
||||
|
||||
fset.AddExistingFiles() // noop
|
||||
check("after AddExistingFiles []", "{A:1-4 B:5-10}")
|
||||
|
||||
fileC := NewFileSet().AddFile("C", 100, 5)
|
||||
fileD := NewFileSet().AddFile("D", 200, 5)
|
||||
fset.AddExistingFiles(fileC, fileA, fileD, fileC)
|
||||
check("after AddExistingFiles [CADC]", "{A:1-4 B:5-10 C:100-105 D:200-205}")
|
||||
|
||||
fileE := fset.AddFile("E", -1, 3)
|
||||
_ = fileE
|
||||
check("after AddFile [E]", "{A:1-4 B:5-10 C:100-105 D:200-205 E:206-209}")
|
||||
}
|
||||
|
||||
func fsetString(fset *FileSet) string {
|
||||
var buf strings.Builder
|
||||
buf.WriteRune('{')
|
||||
sep := ""
|
||||
fset.Iterate(func(f *File) bool {
|
||||
fmt.Fprintf(&buf, "%s%s:%d-%d", sep, f.Name(), f.Base(), f.Base()+f.Size())
|
||||
sep = " "
|
||||
return true
|
||||
})
|
||||
buf.WriteRune('}')
|
||||
return buf.String()
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue