Skip to content

Commit 33c392f

Browse files
committed
reflect: add FieldByIndexErr
This new function, although different in signature from other reflect functions, allows the caller to avoid the panic caused by nil embedded fields in calls to FieldByIndex. Fixes #48218 Change-Id: I447f135bb789148c27ae3f2f23dcf43094f4c1de Reviewed-on: https://go-review.googlesource.com/c/go/+/357962 Trust: Rob Pike <r@golang.org> Run-TryBot: Rob Pike <r@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
1 parent af05d8b commit 33c392f

File tree

2 files changed

+45
-1
lines changed

2 files changed

+45
-1
lines changed

src/reflect/value.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package reflect
66

77
import (
8+
"errors"
89
"internal/abi"
910
"internal/goarch"
1011
"internal/itoa"
@@ -1232,7 +1233,8 @@ func (v Value) Field(i int) Value {
12321233
}
12331234

12341235
// FieldByIndex returns the nested field corresponding to index.
1235-
// It panics if v's Kind is not struct.
1236+
// It panics if evaluation requires stepping through a nil
1237+
// pointer or a field that is not a struct.
12361238
func (v Value) FieldByIndex(index []int) Value {
12371239
if len(index) == 1 {
12381240
return v.Field(index[0])
@@ -1252,6 +1254,29 @@ func (v Value) FieldByIndex(index []int) Value {
12521254
return v
12531255
}
12541256

1257+
// FieldByIndexErr returns the nested field corresponding to index.
1258+
// It returns an error if evaluation requires stepping through a nil
1259+
// pointer, but panics if it must step through a field that
1260+
// is not a struct.
1261+
func (v Value) FieldByIndexErr(index []int) (Value, error) {
1262+
if len(index) == 1 {
1263+
return v.Field(index[0]), nil
1264+
}
1265+
v.mustBe(Struct)
1266+
for i, x := range index {
1267+
if i > 0 {
1268+
if v.Kind() == Ptr && v.typ.Elem().Kind() == Struct {
1269+
if v.IsNil() {
1270+
return Value{}, errors.New("reflect: indirection through nil pointer to embedded struct field " + v.typ.Elem().Name())
1271+
}
1272+
v = v.Elem()
1273+
}
1274+
}
1275+
v = v.Field(x)
1276+
}
1277+
return v, nil
1278+
}
1279+
12551280
// FieldByName returns the struct field with the given name.
12561281
// It returns the zero Value if no field was found.
12571282
// It panics if v's Kind is not struct.

src/reflect/visiblefields_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package reflect_test
66

77
import (
88
. "reflect"
9+
"strings"
910
"testing"
1011
)
1112

@@ -328,3 +329,21 @@ func TestFields(t *testing.T) {
328329
})
329330
}
330331
}
332+
333+
// Must not panic with nil embedded pointer.
334+
func TestFieldByIndexErr(t *testing.T) {
335+
type A struct {
336+
S string
337+
}
338+
type B struct {
339+
*A
340+
}
341+
v := ValueOf(B{})
342+
_, err := v.FieldByIndexErr([]int{0, 0})
343+
if err == nil {
344+
t.Fatal("expected error")
345+
}
346+
if !strings.Contains(err.Error(), "embedded struct field A") {
347+
t.Fatal(err)
348+
}
349+
}

0 commit comments

Comments
 (0)