]> Cypherpunks repositories - gostls13.git/commitdiff
reflect: add FieldByIndexErr
authorRob Pike <r@golang.org>
Sun, 24 Oct 2021 03:11:07 +0000 (14:11 +1100)
committerRob Pike <r@golang.org>
Fri, 29 Oct 2021 02:11:42 +0000 (02:11 +0000)
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>
src/reflect/value.go
src/reflect/visiblefields_test.go

index 618d38893e8456744dfd7f7ef12ec721a395f984..90edf8e31ddf6d76ee2b48ce6e395a58462f35c4 100644 (file)
@@ -5,6 +5,7 @@
 package reflect
 
 import (
+       "errors"
        "internal/abi"
        "internal/goarch"
        "internal/itoa"
@@ -1232,7 +1233,8 @@ func (v Value) Field(i int) Value {
 }
 
 // FieldByIndex returns the nested field corresponding to index.
-// It panics if v's Kind is not struct.
+// It panics if evaluation requires stepping through a nil
+// pointer or a field that is not a struct.
 func (v Value) FieldByIndex(index []int) Value {
        if len(index) == 1 {
                return v.Field(index[0])
@@ -1252,6 +1254,29 @@ func (v Value) FieldByIndex(index []int) Value {
        return v
 }
 
+// FieldByIndexErr returns the nested field corresponding to index.
+// It returns an error if evaluation requires stepping through a nil
+// pointer, but panics if it must step through a field that
+// is not a struct.
+func (v Value) FieldByIndexErr(index []int) (Value, error) {
+       if len(index) == 1 {
+               return v.Field(index[0]), nil
+       }
+       v.mustBe(Struct)
+       for i, x := range index {
+               if i > 0 {
+                       if v.Kind() == Ptr && v.typ.Elem().Kind() == Struct {
+                               if v.IsNil() {
+                                       return Value{}, errors.New("reflect: indirection through nil pointer to embedded struct field " + v.typ.Elem().Name())
+                               }
+                               v = v.Elem()
+                       }
+               }
+               v = v.Field(x)
+       }
+       return v, nil
+}
+
 // FieldByName returns the struct field with the given name.
 // It returns the zero Value if no field was found.
 // It panics if v's Kind is not struct.
index 915bbee867c60db0c491a97d822def430fa16213..5ae322321b2de8365522ec503ca43799709e4fe9 100644 (file)
@@ -6,6 +6,7 @@ package reflect_test
 
 import (
        . "reflect"
+       "strings"
        "testing"
 )
 
@@ -328,3 +329,21 @@ func TestFields(t *testing.T) {
                })
        }
 }
+
+// Must not panic with nil embedded pointer.
+func TestFieldByIndexErr(t *testing.T) {
+       type A struct {
+               S string
+       }
+       type B struct {
+               *A
+       }
+       v := ValueOf(B{})
+       _, err := v.FieldByIndexErr([]int{0, 0})
+       if err == nil {
+               t.Fatal("expected error")
+       }
+       if !strings.Contains(err.Error(), "embedded struct field A") {
+               t.Fatal(err)
+       }
+}