Skip to content

Commit 5a9d5c3

Browse files
odeke-emadg
authored andcommitted
encoding/gob: document Encode, EncodeValue nil pointer panics
Fixes #16258. Docs for Encode and EncodeValue do not mention that nil pointers are not permitted hence we panic, because Gobs encode values yet nil pointers have no value to encode. It moves a comment that was internal to EncodeValue to the top level to make it clearer to users what to expect when they pass in nil pointers. Supplements test TestTopLevelNilPointer. Change-Id: Ie54f609fde4b791605960e088456047eb9aa8738 Reviewed-on: https://go-review.googlesource.com/24740 Reviewed-by: Andrew Gerrand <[email protected]> Run-TryBot: Andrew Gerrand <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 003a68b commit 5a9d5c3

File tree

3 files changed

+66
-13
lines changed

3 files changed

+66
-13
lines changed

src/encoding/gob/doc.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ Basics
1717
A stream of gobs is self-describing. Each data item in the stream is preceded by
1818
a specification of its type, expressed in terms of a small set of predefined
1919
types. Pointers are not transmitted, but the things they point to are
20-
transmitted; that is, the values are flattened. Recursive types work fine, but
20+
transmitted; that is, the values are flattened. Nil pointers are not permitted,
21+
as they have no value. Recursive types work fine, but
2122
recursive values (data with cycles) are problematic. This may change.
2223
2324
To use gobs, create an Encoder and present it with a series of data items as

src/encoding/gob/encoder.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ func (enc *Encoder) sendType(w io.Writer, state *encoderState, origt reflect.Typ
170170

171171
// Encode transmits the data item represented by the empty interface value,
172172
// guaranteeing that all necessary type information has been transmitted first.
173+
// Passing a nil pointer to Encoder will panic, as they cannot be transmitted by gob.
173174
func (enc *Encoder) Encode(e interface{}) error {
174175
return enc.EncodeValue(reflect.ValueOf(e))
175176
}
@@ -212,9 +213,8 @@ func (enc *Encoder) sendTypeId(state *encoderState, ut *userTypeInfo) {
212213

213214
// EncodeValue transmits the data item represented by the reflection value,
214215
// guaranteeing that all necessary type information has been transmitted first.
216+
// Passing a nil pointer to EncodeValue will panic, as they cannot be transmitted by gob.
215217
func (enc *Encoder) EncodeValue(value reflect.Value) error {
216-
// Gobs contain values. They cannot represent nil pointers, which
217-
// have no value to encode.
218218
if value.Kind() == reflect.Ptr && value.IsNil() {
219219
panic("gob: cannot encode nil pointer of type " + value.Type().String())
220220
}

src/encoding/gob/encoder_test.go

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"bytes"
99
"encoding/hex"
1010
"fmt"
11+
"io/ioutil"
1112
"reflect"
1213
"strings"
1314
"testing"
@@ -831,30 +832,81 @@ func TestPtrToMapOfMap(t *testing.T) {
831832

832833
// A top-level nil pointer generates a panic with a helpful string-valued message.
833834
func TestTopLevelNilPointer(t *testing.T) {
834-
errMsg := topLevelNilPanic(t)
835-
if errMsg == "" {
835+
var ip *int
836+
encodeErr, panicErr := encodeAndRecover(ip)
837+
if encodeErr != nil {
838+
t.Fatal("error in encode:", encodeErr)
839+
}
840+
if panicErr == nil {
836841
t.Fatal("top-level nil pointer did not panic")
837842
}
843+
errMsg := panicErr.Error()
838844
if !strings.Contains(errMsg, "nil pointer") {
839845
t.Fatal("expected nil pointer error, got:", errMsg)
840846
}
841847
}
842848

843-
func topLevelNilPanic(t *testing.T) (panicErr string) {
849+
func encodeAndRecover(value interface{}) (encodeErr, panicErr error) {
844850
defer func() {
845851
e := recover()
846-
if err, ok := e.(string); ok {
847-
panicErr = err
852+
if e != nil {
853+
switch err := e.(type) {
854+
case error:
855+
panicErr = err
856+
default:
857+
panicErr = fmt.Errorf("%v", err)
858+
}
848859
}
849860
}()
850-
var ip *int
851-
buf := new(bytes.Buffer)
852-
if err := NewEncoder(buf).Encode(ip); err != nil {
853-
t.Fatal("error in encode:", err)
854-
}
861+
862+
encodeErr = NewEncoder(ioutil.Discard).Encode(value)
855863
return
856864
}
857865

866+
func TestNilPointerPanics(t *testing.T) {
867+
var (
868+
nilStringPtr *string
869+
intMap = make(map[int]int)
870+
intMapPtr = &intMap
871+
nilIntMapPtr *map[int]int
872+
zero int
873+
nilBoolChannel chan bool
874+
nilBoolChannelPtr *chan bool
875+
nilStringSlice []string
876+
stringSlice = make([]string, 1)
877+
nilStringSlicePtr *[]string
878+
)
879+
880+
testCases := []struct {
881+
value interface{}
882+
mustPanic bool
883+
}{
884+
{nilStringPtr, true},
885+
{intMap, false},
886+
{intMapPtr, false},
887+
{nilIntMapPtr, true},
888+
{zero, false},
889+
{nilStringSlice, false},
890+
{stringSlice, false},
891+
{nilStringSlicePtr, true},
892+
{nilBoolChannel, false},
893+
{nilBoolChannelPtr, true},
894+
}
895+
896+
for _, tt := range testCases {
897+
_, panicErr := encodeAndRecover(tt.value)
898+
if tt.mustPanic {
899+
if panicErr == nil {
900+
t.Errorf("expected panic with input %#v, did not panic", tt.value)
901+
}
902+
continue
903+
}
904+
if panicErr != nil {
905+
t.Fatalf("expected no panic with input %#v, got panic=%v", tt.value, panicErr)
906+
}
907+
}
908+
}
909+
858910
func TestNilPointerInsideInterface(t *testing.T) {
859911
var ip *int
860912
si := struct {

0 commit comments

Comments
 (0)