gopls/internal/lsp/frob: make API generic

Change-Id: Ia3f2c68cec6588fa23f35d1a125d9f602767f375
Reviewed-on: https://go-review.googlesource.com/c/tools/+/510378
Run-TryBot: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
Auto-Submit: Alan Donovan <adonovan@google.com>
diff --git a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/analysis.go
index c5f6f72..1cfa84d 100644
--- a/gopls/internal/lsp/cache/analysis.go
+++ b/gopls/internal/lsp/cache/analysis.go
@@ -1403,14 +1403,12 @@
 	return buf.Bytes()
 }
 
-// var analyzeSummaryCodec = frob.For[*analyzeSummary]()
-var analyzeSummaryCodec = frob.CodecFor117(new(*analyzeSummary))
+var analyzeSummaryCodec = frob.CodecFor[*analyzeSummary]()
 
 // -- data types for serialization of analysis.Diagnostic and source.Diagnostic --
 
 // (The name says gob but we use frob.)
-// var diagnosticsCodec = frob.For[[]gobDiagnostic]()
-var diagnosticsCodec = frob.CodecFor117(new([]gobDiagnostic))
+var diagnosticsCodec = frob.CodecFor[[]gobDiagnostic]()
 
 type gobDiagnostic struct {
 	Location       protocol.Location
diff --git a/gopls/internal/lsp/frob/frob.go b/gopls/internal/lsp/frob/frob.go
index 5582ebe..e3abcfe 100644
--- a/gopls/internal/lsp/frob/frob.go
+++ b/gopls/internal/lsp/frob/frob.go
@@ -3,11 +3,18 @@
 // license that can be found in the LICENSE file.
 
 // Package frob is a fast restricted object encoder/decoder in the
-// spirit of gob. Restrictions include:
+// spirit of encoding/gob.
 //
-//   - Interface values are not supported. This avoids the need for
+// As with gob, types that recursively contain functions,
+// channels, and unsafe.Pointers cannot encoded, but frob has these
+// additional restrictions:
+//
+//   - Interface values are not supported; this avoids the need for
 //     the encoding to describe types.
 //
+//   - Types that recursively contain private struct fields are not
+//     permitted.
+//
 //   - The encoding is unspecified and subject to change, so the encoder
 //     and decoder must exactly agree on their implementation and on the
 //     definitions of the target types.
@@ -33,38 +40,20 @@
 	"sync"
 )
 
-// Use CodecFor117(new(T)) to create a codec for values of type T.
-// Then call Encode(T) and Decode(data, *T).
-// This is a placeholder for the forthcoming generic API -- see below.
-// CodecFor117 panics if type T is unsuitable.
-func CodecFor117(x any) Codec {
-	frobsMu.Lock()
-	defer frobsMu.Unlock()
-	return Codec{frobFor(reflect.TypeOf(x).Elem())}
-}
-
-type any = interface{}
-
-// A Codec is an immutable encoder and decoder for values of a particular type.
-type Codec struct{ *frob }
-
-// TODO(adonovan): after go1.18, enable this generic interface.
-/*
-
-// CodecFor[T] returns a codec for values of type T.
-//
-// For panics if the type recursively contains members of unsupported
-// types: functions, channels, interfaces, unsafe.Pointer.
-func CodecFor[T any]() Codec[T] { return For117((*T)(nil)) }
-
 // A Codec[T] is an immutable encoder and decoder for values of type T.
 type Codec[T any] struct{ frob *frob }
 
+// CodecFor[T] returns a codec for values of type T.
+// It panics if type T is unsuitable.
+func CodecFor[T any]() Codec[T] {
+	frobsMu.Lock()
+	defer frobsMu.Unlock()
+	return Codec[T]{frobFor(reflect.TypeOf((*T)(nil)).Elem())}
+}
+
 func (codec Codec[T]) Encode(v T) []byte          { return codec.frob.Encode(v) }
 func (codec Codec[T]) Decode(data []byte, ptr *T) { codec.frob.Decode(data, ptr) }
 
-*/
-
 var (
 	frobsMu sync.Mutex
 	frobs   = make(map[reflect.Type]*frob)
@@ -106,7 +95,7 @@
 
 		case reflect.Array,
 			reflect.Slice,
-			reflect.Ptr: // TODO(adonovan): after go1.18, use Pointer
+			reflect.Pointer:
 			fr.addElem(fr.t.Elem())
 
 		case reflect.Map:
@@ -224,7 +213,7 @@
 			}
 		}
 
-	case reflect.Ptr: // TODO(adonovan): after go1.18, use Pointer
+	case reflect.Pointer:
 		if v.IsNil() {
 			out.uint8(0)
 		} else {
@@ -337,7 +326,7 @@
 			kzero := reflect.Zero(kfrob.t)
 			vzero := reflect.Zero(vfrob.t)
 			for i := 0; i < len; i++ {
-				// TODO(adonovan): after go1.18, use SetZero.
+				// TODO(adonovan): use SetZero from go1.20.
 				// k.SetZero()
 				// v.SetZero()
 				k.Set(kzero)
@@ -348,7 +337,7 @@
 			}
 		}
 
-	case reflect.Ptr: // TODO(adonovan): after go1.18, use Pointer
+	case reflect.Pointer:
 		isNil := in.uint8() == 0
 		if !isNil {
 			ptr := reflect.New(fr.elems[0].t)
@@ -409,38 +398,7 @@
 type writer struct{ data []byte }
 
 func (w *writer) uint8(v uint8)   { w.data = append(w.data, v) }
-func (w *writer) uint16(v uint16) { w.data = appendUint16(w.data, v) }
-func (w *writer) uint32(v uint32) { w.data = appendUint32(w.data, v) }
-func (w *writer) uint64(v uint64) { w.data = appendUint64(w.data, v) }
+func (w *writer) uint16(v uint16) { w.data = le.AppendUint16(w.data, v) }
+func (w *writer) uint32(v uint32) { w.data = le.AppendUint32(w.data, v) }
+func (w *writer) uint64(v uint64) { w.data = le.AppendUint64(w.data, v) }
 func (w *writer) bytes(v []byte)  { w.data = append(w.data, v...) }
-
-// TODO(adonovan): delete these as in go1.18 they are methods on LittleEndian:
-
-func appendUint16(b []byte, v uint16) []byte {
-	return append(b,
-		byte(v),
-		byte(v>>8),
-	)
-}
-
-func appendUint32(b []byte, v uint32) []byte {
-	return append(b,
-		byte(v),
-		byte(v>>8),
-		byte(v>>16),
-		byte(v>>24),
-	)
-}
-
-func appendUint64(b []byte, v uint64) []byte {
-	return append(b,
-		byte(v),
-		byte(v>>8),
-		byte(v>>16),
-		byte(v>>24),
-		byte(v>>32),
-		byte(v>>40),
-		byte(v>>48),
-		byte(v>>56),
-	)
-}
diff --git a/gopls/internal/lsp/frob/frob_test.go b/gopls/internal/lsp/frob/frob_test.go
index d2a9f2a..892f18b 100644
--- a/gopls/internal/lsp/frob/frob_test.go
+++ b/gopls/internal/lsp/frob/frob_test.go
@@ -19,7 +19,7 @@
 		C *Basics
 		D map[string]int
 	}
-	codec := frob.CodecFor117(new(Basics))
+	codec := frob.CodecFor[Basics]()
 
 	s1, s2 := "hello", "world"
 	x := Basics{
@@ -55,7 +55,7 @@
 		C64  complex64
 		C128 complex128
 	}
-	codec := frob.CodecFor117(new(Ints))
+	codec := frob.CodecFor[Ints]()
 
 	// maxima
 	max1 := Ints{
diff --git a/gopls/internal/lsp/source/methodsets/methodsets.go b/gopls/internal/lsp/source/methodsets/methodsets.go
index 1ade740..d934c3c 100644
--- a/gopls/internal/lsp/source/methodsets/methodsets.go
+++ b/gopls/internal/lsp/source/methodsets/methodsets.go
@@ -455,8 +455,7 @@
 // -- serial format of index --
 
 // (The name says gob but in fact we use frob.)
-// var packageCodec = frob.For[gobPackage]()
-var packageCodec = frob.CodecFor117(new(gobPackage))
+var packageCodec = frob.CodecFor[gobPackage]()
 
 // A gobPackage records the method set of each package-level type for a single package.
 type gobPackage struct {
diff --git a/gopls/internal/lsp/source/typerefs/refs.go b/gopls/internal/lsp/source/typerefs/refs.go
index 2f6b1d9..9adbb88 100644
--- a/gopls/internal/lsp/source/typerefs/refs.go
+++ b/gopls/internal/lsp/source/typerefs/refs.go
@@ -738,8 +738,7 @@
 // -- serialization --
 
 // (The name says gob but in fact we use frob.)
-// var classesCodec = frob.For[gobClasses]()
-var classesCodec = frob.CodecFor117(new(gobClasses))
+var classesCodec = frob.CodecFor[gobClasses]()
 
 type gobClasses struct {
 	Strings []string // table of strings (PackageIDs and names)
diff --git a/gopls/internal/lsp/source/xrefs/xrefs.go b/gopls/internal/lsp/source/xrefs/xrefs.go
index 0a8d574..88f76b1 100644
--- a/gopls/internal/lsp/source/xrefs/xrefs.go
+++ b/gopls/internal/lsp/source/xrefs/xrefs.go
@@ -172,8 +172,7 @@
 // The gobRef.Range field is the obvious place to begin.
 
 // (The name says gob but in fact we use frob.)
-// var packageCodec = frob.For[[]*gobPackage]()
-var packageCodec = frob.CodecFor117(new([]*gobPackage))
+var packageCodec = frob.CodecFor[[]*gobPackage]()
 
 // A gobPackage records the set of outgoing references from the index
 // package to symbols defined in a dependency package.