[release-branch.0.24] internal/tokeninternal: move unsafe gopls code into gopls

In x/tools@v0.24.0, the tokeninternal package accessed the token.FileSet
unsafely, and used a trick to force the build to break if ever the size
of token.FileSet changed. This workaround was only necessary for gopls,
but was unfortunately implemented in a package that was reachable
through the public API of x/tools.

In go1.25, the size of the token.FileSet changed, breaking tools that
indirectly depended on this poisoned package. Fix this by moving the
gopls logic into gopls: the gopls build will still be broken, but that
doesn't matter as we only support the most recent version of gopls,
which is fixed.

For golang/go#74462

Change-Id: Ied3f7a615cab66d88ec6f4cfe8738f8429865993
Reviewed-on: https://go-review.googlesource.com/c/tools/+/697336
Reviewed-by: Peter Weinberger <pjw@google.com>
TryBot-Bypass: Robert Findley <rfindley@google.com>
diff --git a/gopls/internal/cache/check.go b/gopls/internal/cache/check.go
index ea91d28..57e2c1f 100644
--- a/gopls/internal/cache/check.go
+++ b/gopls/internal/cache/check.go
@@ -30,6 +30,7 @@
 	"golang.org/x/tools/gopls/internal/filecache"
 	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
+	"golang.org/x/tools/gopls/internal/tokeninternal"
 	"golang.org/x/tools/gopls/internal/util/bug"
 	"golang.org/x/tools/gopls/internal/util/safetoken"
 	"golang.org/x/tools/gopls/internal/util/slices"
@@ -37,7 +38,6 @@
 	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/internal/gcimporter"
 	"golang.org/x/tools/internal/packagesinternal"
-	"golang.org/x/tools/internal/tokeninternal"
 	"golang.org/x/tools/internal/typesinternal"
 	"golang.org/x/tools/internal/versions"
 )
diff --git a/gopls/internal/cache/parse_cache.go b/gopls/internal/cache/parse_cache.go
index 8586f65..2302fa0 100644
--- a/gopls/internal/cache/parse_cache.go
+++ b/gopls/internal/cache/parse_cache.go
@@ -20,8 +20,8 @@
 	"golang.org/x/tools/gopls/internal/cache/parsego"
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/protocol"
+	"golang.org/x/tools/gopls/internal/tokeninternal"
 	"golang.org/x/tools/internal/memoize"
-	"golang.org/x/tools/internal/tokeninternal"
 )
 
 // This file contains an implementation of an LRU parse cache, that offsets the
diff --git a/gopls/internal/cache/parsego/parse_test.go b/gopls/internal/cache/parsego/parse_test.go
index c641254..3799374 100644
--- a/gopls/internal/cache/parsego/parse_test.go
+++ b/gopls/internal/cache/parsego/parse_test.go
@@ -11,8 +11,8 @@
 	"testing"
 
 	"golang.org/x/tools/gopls/internal/cache/parsego"
+	"golang.org/x/tools/gopls/internal/tokeninternal"
 	"golang.org/x/tools/gopls/internal/util/safetoken"
-	"golang.org/x/tools/internal/tokeninternal"
 )
 
 // TODO(golang/go#64335): we should have many more tests for fixed syntax.
diff --git a/gopls/internal/golang/change_signature.go b/gopls/internal/golang/change_signature.go
index 72cbe4c..699e0b1 100644
--- a/gopls/internal/golang/change_signature.go
+++ b/gopls/internal/golang/change_signature.go
@@ -20,13 +20,13 @@
 	"golang.org/x/tools/gopls/internal/cache/parsego"
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/protocol"
+	"golang.org/x/tools/gopls/internal/tokeninternal"
 	"golang.org/x/tools/gopls/internal/util/bug"
 	"golang.org/x/tools/gopls/internal/util/safetoken"
 	"golang.org/x/tools/imports"
 	internalastutil "golang.org/x/tools/internal/astutil"
 	"golang.org/x/tools/internal/diff"
 	"golang.org/x/tools/internal/refactor/inline"
-	"golang.org/x/tools/internal/tokeninternal"
 	"golang.org/x/tools/internal/typesinternal"
 	"golang.org/x/tools/internal/versions"
 )
diff --git a/gopls/internal/golang/format.go b/gopls/internal/golang/format.go
index 5755f7a..0de1bc8 100644
--- a/gopls/internal/golang/format.go
+++ b/gopls/internal/golang/format.go
@@ -22,11 +22,11 @@
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/settings"
+	"golang.org/x/tools/gopls/internal/tokeninternal"
 	"golang.org/x/tools/gopls/internal/util/safetoken"
 	"golang.org/x/tools/internal/diff"
 	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/internal/imports"
-	"golang.org/x/tools/internal/tokeninternal"
 )
 
 // Format formats a file with a given range.
diff --git a/gopls/internal/golang/hover.go b/gopls/internal/golang/hover.go
index 2edb8a9..2fda396 100644
--- a/gopls/internal/golang/hover.go
+++ b/gopls/internal/golang/hover.go
@@ -33,6 +33,7 @@
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/settings"
+	"golang.org/x/tools/gopls/internal/tokeninternal"
 	gastutil "golang.org/x/tools/gopls/internal/util/astutil"
 	"golang.org/x/tools/gopls/internal/util/bug"
 	"golang.org/x/tools/gopls/internal/util/safetoken"
@@ -41,7 +42,6 @@
 	"golang.org/x/tools/internal/aliases"
 	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/internal/stdlib"
-	"golang.org/x/tools/internal/tokeninternal"
 	"golang.org/x/tools/internal/typeparams"
 	"golang.org/x/tools/internal/typesinternal"
 )
diff --git a/gopls/internal/golang/stub.go b/gopls/internal/golang/stub.go
index 47bcf3a..afc448d 100644
--- a/gopls/internal/golang/stub.go
+++ b/gopls/internal/golang/stub.go
@@ -22,10 +22,10 @@
 	"golang.org/x/tools/gopls/internal/cache"
 	"golang.org/x/tools/gopls/internal/cache/metadata"
 	"golang.org/x/tools/gopls/internal/cache/parsego"
+	"golang.org/x/tools/gopls/internal/tokeninternal"
 	"golang.org/x/tools/gopls/internal/util/bug"
 	"golang.org/x/tools/gopls/internal/util/safetoken"
 	"golang.org/x/tools/internal/diff"
-	"golang.org/x/tools/internal/tokeninternal"
 )
 
 // stubMethodsFixer returns a suggested fix to declare the missing
diff --git a/gopls/internal/golang/types_format.go b/gopls/internal/golang/types_format.go
index 51584bc..13730a0 100644
--- a/gopls/internal/golang/types_format.go
+++ b/gopls/internal/golang/types_format.go
@@ -18,9 +18,9 @@
 	"golang.org/x/tools/gopls/internal/cache"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/settings"
+	"golang.org/x/tools/gopls/internal/tokeninternal"
 	"golang.org/x/tools/gopls/internal/util/bug"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/tokeninternal"
 	"golang.org/x/tools/internal/typeparams"
 )
 
diff --git a/gopls/internal/golang/util.go b/gopls/internal/golang/util.go
index 18f7242..ec2e6a7 100644
--- a/gopls/internal/golang/util.go
+++ b/gopls/internal/golang/util.go
@@ -17,10 +17,10 @@
 	"golang.org/x/tools/gopls/internal/cache/metadata"
 	"golang.org/x/tools/gopls/internal/cache/parsego"
 	"golang.org/x/tools/gopls/internal/protocol"
+	"golang.org/x/tools/gopls/internal/tokeninternal"
 	"golang.org/x/tools/gopls/internal/util/astutil"
 	"golang.org/x/tools/gopls/internal/util/bug"
 	"golang.org/x/tools/gopls/internal/util/safetoken"
-	"golang.org/x/tools/internal/tokeninternal"
 )
 
 // IsGenerated gets and reads the file denoted by uri and reports
diff --git a/gopls/internal/server/command.go b/gopls/internal/server/command.go
index 25a7f33..c667df6 100644
--- a/gopls/internal/server/command.go
+++ b/gopls/internal/server/command.go
@@ -34,13 +34,13 @@
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/protocol/command"
 	"golang.org/x/tools/gopls/internal/settings"
+	"golang.org/x/tools/gopls/internal/tokeninternal"
 	"golang.org/x/tools/gopls/internal/util/bug"
 	"golang.org/x/tools/gopls/internal/vulncheck"
 	"golang.org/x/tools/gopls/internal/vulncheck/scan"
 	"golang.org/x/tools/internal/diff"
 	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/internal/gocommand"
-	"golang.org/x/tools/internal/tokeninternal"
 	"golang.org/x/tools/internal/xcontext"
 )
 
diff --git a/gopls/internal/tokeninternal/tokeninternal.go b/gopls/internal/tokeninternal/tokeninternal.go
new file mode 100644
index 0000000..a0b6c7f
--- /dev/null
+++ b/gopls/internal/tokeninternal/tokeninternal.go
@@ -0,0 +1,107 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package tokeninternal was split off from x/tools/internal/tokeninternal, to
+// avoid the build breakage described in golang/go#74462.
+package tokeninternal
+
+import (
+	"fmt"
+	"go/token"
+	"sort"
+	"sync"
+	"unsafe"
+
+	"golang.org/x/tools/internal/tokeninternal"
+)
+
+// AddExistingFiles adds the specified files to the FileSet if they
+// are not already present. It panics if any pair of files in the
+// resulting FileSet would overlap.
+func AddExistingFiles(fset *token.FileSet, files []*token.File) {
+	// Punch through the FileSet encapsulation.
+	type tokenFileSet struct {
+		// This type remained essentially consistent from go1.16 to go1.21.
+		mutex sync.RWMutex
+		base  int
+		files []*token.File
+		_     *token.File // changed to atomic.Pointer[token.File] in go1.19
+	}
+
+	// If the size of token.FileSet changes, this will fail to compile.
+	const delta = int64(unsafe.Sizeof(tokenFileSet{})) - int64(unsafe.Sizeof(token.FileSet{}))
+	var _ [-delta * delta]int
+
+	type uP = unsafe.Pointer
+	var ptr *tokenFileSet
+	*(*uP)(uP(&ptr)) = uP(fset)
+	ptr.mutex.Lock()
+	defer ptr.mutex.Unlock()
+
+	// Merge and sort.
+	newFiles := append(ptr.files, files...)
+	sort.Slice(newFiles, func(i, j int) bool {
+		return newFiles[i].Base() < newFiles[j].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
+
+	ptr.files = newFiles
+
+	// Advance FileSet.Base().
+	if len(newFiles) > 0 {
+		last := newFiles[len(newFiles)-1]
+		newBase := last.Base() + last.Size() + 1
+		if ptr.base < newBase {
+			ptr.base = newBase
+		}
+	}
+}
+
+// FileSetFor returns a new FileSet containing a sequence of new Files with
+// the same base, size, and line as the input files, for use in APIs that
+// require a FileSet.
+//
+// Precondition: the input files must be non-overlapping, and sorted in order
+// of their Base.
+func FileSetFor(files ...*token.File) *token.FileSet {
+	fset := token.NewFileSet()
+	for _, f := range files {
+		f2 := fset.AddFile(f.Name(), f.Base(), f.Size())
+		lines := tokeninternal.GetLines(f)
+		f2.SetLines(lines)
+	}
+	return fset
+}
+
+// CloneFileSet creates a new FileSet holding all files in fset. It does not
+// create copies of the token.Files in fset: they are added to the resulting
+// FileSet unmodified.
+func CloneFileSet(fset *token.FileSet) *token.FileSet {
+	var files []*token.File
+	fset.Iterate(func(f *token.File) bool {
+		files = append(files, f)
+		return true
+	})
+	newFileSet := token.NewFileSet()
+	AddExistingFiles(newFileSet, files)
+	return newFileSet
+}
diff --git a/internal/tokeninternal/tokeninternal_test.go b/gopls/internal/tokeninternal/tokeninternal_test.go
similarity index 96%
rename from internal/tokeninternal/tokeninternal_test.go
rename to gopls/internal/tokeninternal/tokeninternal_test.go
index 7fd14fe..9b13a2e 100644
--- a/internal/tokeninternal/tokeninternal_test.go
+++ b/gopls/internal/tokeninternal/tokeninternal_test.go
@@ -10,7 +10,7 @@
 	"strings"
 	"testing"
 
-	"golang.org/x/tools/internal/tokeninternal"
+	"golang.org/x/tools/gopls/internal/tokeninternal"
 )
 
 func TestAddExistingFiles(t *testing.T) {
diff --git a/internal/tokeninternal/tokeninternal.go b/internal/tokeninternal/tokeninternal.go
index ff9437a..2c8d70a 100644
--- a/internal/tokeninternal/tokeninternal.go
+++ b/internal/tokeninternal/tokeninternal.go
@@ -7,9 +7,7 @@
 package tokeninternal
 
 import (
-	"fmt"
 	"go/token"
-	"sort"
 	"sync"
 	"unsafe"
 )
@@ -45,93 +43,3 @@
 	defer ptr.mu.Unlock()
 	return ptr.lines
 }
-
-// AddExistingFiles adds the specified files to the FileSet if they
-// are not already present. It panics if any pair of files in the
-// resulting FileSet would overlap.
-func AddExistingFiles(fset *token.FileSet, files []*token.File) {
-	// Punch through the FileSet encapsulation.
-	type tokenFileSet struct {
-		// This type remained essentially consistent from go1.16 to go1.21.
-		mutex sync.RWMutex
-		base  int
-		files []*token.File
-		_     *token.File // changed to atomic.Pointer[token.File] in go1.19
-	}
-
-	// If the size of token.FileSet changes, this will fail to compile.
-	const delta = int64(unsafe.Sizeof(tokenFileSet{})) - int64(unsafe.Sizeof(token.FileSet{}))
-	var _ [-delta * delta]int
-
-	type uP = unsafe.Pointer
-	var ptr *tokenFileSet
-	*(*uP)(uP(&ptr)) = uP(fset)
-	ptr.mutex.Lock()
-	defer ptr.mutex.Unlock()
-
-	// Merge and sort.
-	newFiles := append(ptr.files, files...)
-	sort.Slice(newFiles, func(i, j int) bool {
-		return newFiles[i].Base() < newFiles[j].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
-
-	ptr.files = newFiles
-
-	// Advance FileSet.Base().
-	if len(newFiles) > 0 {
-		last := newFiles[len(newFiles)-1]
-		newBase := last.Base() + last.Size() + 1
-		if ptr.base < newBase {
-			ptr.base = newBase
-		}
-	}
-}
-
-// FileSetFor returns a new FileSet containing a sequence of new Files with
-// the same base, size, and line as the input files, for use in APIs that
-// require a FileSet.
-//
-// Precondition: the input files must be non-overlapping, and sorted in order
-// of their Base.
-func FileSetFor(files ...*token.File) *token.FileSet {
-	fset := token.NewFileSet()
-	for _, f := range files {
-		f2 := fset.AddFile(f.Name(), f.Base(), f.Size())
-		lines := GetLines(f)
-		f2.SetLines(lines)
-	}
-	return fset
-}
-
-// CloneFileSet creates a new FileSet holding all files in fset. It does not
-// create copies of the token.Files in fset: they are added to the resulting
-// FileSet unmodified.
-func CloneFileSet(fset *token.FileSet) *token.FileSet {
-	var files []*token.File
-	fset.Iterate(func(f *token.File) bool {
-		files = append(files, f)
-		return true
-	})
-	newFileSet := token.NewFileSet()
-	AddExistingFiles(newFileSet, files)
-	return newFileSet
-}