tokeninternal.go 4.0 KB

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