cuelang.org/go@v0.10.1/internal/golangorgx/tools/tokeninternal/tokeninternal.go (about) 1 // Copyright 2023 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // package tokeninternal provides access to some internal features of the token 6 // package. 7 package tokeninternal 8 9 import ( 10 "fmt" 11 "go/token" 12 "sort" 13 "sync" 14 "unsafe" 15 ) 16 17 // GetLines returns the table of line-start offsets from a token.File. 18 func GetLines(file *token.File) []int { 19 // token.File has a Lines method on Go 1.21 and later. 20 if file, ok := (interface{})(file).(interface{ Lines() []int }); ok { 21 return file.Lines() 22 } 23 24 // This declaration must match that of token.File. 25 // This creates a risk of dependency skew. 26 // For now we check that the size of the two 27 // declarations is the same, on the (fragile) assumption 28 // that future changes would add fields. 29 type tokenFile119 struct { 30 _ string 31 _ int 32 _ int 33 mu sync.Mutex // we're not complete monsters 34 lines []int 35 _ []struct{} 36 } 37 type tokenFile118 struct { 38 _ *token.FileSet // deleted in go1.19 39 tokenFile119 40 } 41 42 type uP = unsafe.Pointer 43 switch unsafe.Sizeof(*file) { 44 case unsafe.Sizeof(tokenFile118{}): 45 var ptr *tokenFile118 46 *(*uP)(uP(&ptr)) = uP(file) 47 ptr.mu.Lock() 48 defer ptr.mu.Unlock() 49 return ptr.lines 50 51 case unsafe.Sizeof(tokenFile119{}): 52 var ptr *tokenFile119 53 *(*uP)(uP(&ptr)) = uP(file) 54 ptr.mu.Lock() 55 defer ptr.mu.Unlock() 56 return ptr.lines 57 58 default: 59 panic("unexpected token.File size") 60 } 61 } 62 63 // AddExistingFiles adds the specified files to the FileSet if they 64 // are not already present. It panics if any pair of files in the 65 // resulting FileSet would overlap. 66 func AddExistingFiles(fset *token.FileSet, files []*token.File) { 67 // Punch through the FileSet encapsulation. 68 type tokenFileSet struct { 69 // This type remained essentially consistent from go1.16 to go1.21. 70 mutex sync.RWMutex 71 base int 72 files []*token.File 73 _ *token.File // changed to atomic.Pointer[token.File] in go1.19 74 } 75 76 // If the size of token.FileSet changes, this will fail to compile. 77 const delta = int64(unsafe.Sizeof(tokenFileSet{})) - int64(unsafe.Sizeof(token.FileSet{})) 78 var _ [-delta * delta]int 79 80 type uP = unsafe.Pointer 81 var ptr *tokenFileSet 82 *(*uP)(uP(&ptr)) = uP(fset) 83 ptr.mutex.Lock() 84 defer ptr.mutex.Unlock() 85 86 // Merge and sort. 87 newFiles := append(ptr.files, files...) 88 sort.Slice(newFiles, func(i, j int) bool { 89 return newFiles[i].Base() < newFiles[j].Base() 90 }) 91 92 // Reject overlapping files. 93 // Discard adjacent identical files. 94 out := newFiles[:0] 95 for i, file := range newFiles { 96 if i > 0 { 97 prev := newFiles[i-1] 98 if file == prev { 99 continue 100 } 101 if prev.Base()+prev.Size()+1 > file.Base() { 102 panic(fmt.Sprintf("file %s (%d-%d) overlaps with file %s (%d-%d)", 103 prev.Name(), prev.Base(), prev.Base()+prev.Size(), 104 file.Name(), file.Base(), file.Base()+file.Size())) 105 } 106 } 107 out = append(out, file) 108 } 109 newFiles = out 110 111 ptr.files = newFiles 112 113 // Advance FileSet.Base(). 114 if len(newFiles) > 0 { 115 last := newFiles[len(newFiles)-1] 116 newBase := last.Base() + last.Size() + 1 117 if ptr.base < newBase { 118 ptr.base = newBase 119 } 120 } 121 } 122 123 // FileSetFor returns a new FileSet containing a sequence of new Files with 124 // the same base, size, and line as the input files, for use in APIs that 125 // require a FileSet. 126 // 127 // Precondition: the input files must be non-overlapping, and sorted in order 128 // of their Base. 129 func FileSetFor(files ...*token.File) *token.FileSet { 130 fset := token.NewFileSet() 131 for _, f := range files { 132 f2 := fset.AddFile(f.Name(), f.Base(), f.Size()) 133 lines := GetLines(f) 134 f2.SetLines(lines) 135 } 136 return fset 137 } 138 139 // CloneFileSet creates a new FileSet holding all files in fset. It does not 140 // create copies of the token.Files in fset: they are added to the resulting 141 // FileSet unmodified. 142 func CloneFileSet(fset *token.FileSet) *token.FileSet { 143 var files []*token.File 144 fset.Iterate(func(f *token.File) bool { 145 files = append(files, f) 146 return true 147 }) 148 newFileSet := token.NewFileSet() 149 AddExistingFiles(newFileSet, files) 150 return newFileSet 151 }