reflect: import visiblefields code and tests from upstream
Этот коммит содержится в:
родитель
fa4f361ca7
коммит
d9c6f7c11f
2 изменённых файлов: 454 добавлений и 0 удалений
105
src/reflect/visiblefields.go
Обычный файл
105
src/reflect/visiblefields.go
Обычный файл
|
@ -0,0 +1,105 @@
|
||||||
|
// Copyright 2021 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 reflect
|
||||||
|
|
||||||
|
// VisibleFields returns all the visible fields in t, which must be a
|
||||||
|
// struct type. A field is defined as visible if it's accessible
|
||||||
|
// directly with a FieldByName call. The returned fields include fields
|
||||||
|
// inside anonymous struct members and unexported fields. They follow
|
||||||
|
// the same order found in the struct, with anonymous fields followed
|
||||||
|
// immediately by their promoted fields.
|
||||||
|
//
|
||||||
|
// For each element e of the returned slice, the corresponding field
|
||||||
|
// can be retrieved from a value v of type t by calling v.FieldByIndex(e.Index).
|
||||||
|
func VisibleFields(t Type) []StructField {
|
||||||
|
if t == nil {
|
||||||
|
panic("reflect: VisibleFields(nil)")
|
||||||
|
}
|
||||||
|
if t.Kind() != Struct {
|
||||||
|
panic("reflect.VisibleFields of non-struct type")
|
||||||
|
}
|
||||||
|
w := &visibleFieldsWalker{
|
||||||
|
byName: make(map[string]int),
|
||||||
|
visiting: make(map[Type]bool),
|
||||||
|
fields: make([]StructField, 0, t.NumField()),
|
||||||
|
index: make([]int, 0, 2),
|
||||||
|
}
|
||||||
|
w.walk(t)
|
||||||
|
// Remove all the fields that have been hidden.
|
||||||
|
// Use an in-place removal that avoids copying in
|
||||||
|
// the common case that there are no hidden fields.
|
||||||
|
j := 0
|
||||||
|
for i := range w.fields {
|
||||||
|
f := &w.fields[i]
|
||||||
|
if f.Name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if i != j {
|
||||||
|
// A field has been removed. We need to shuffle
|
||||||
|
// all the subsequent elements up.
|
||||||
|
w.fields[j] = *f
|
||||||
|
}
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
return w.fields[:j]
|
||||||
|
}
|
||||||
|
|
||||||
|
type visibleFieldsWalker struct {
|
||||||
|
byName map[string]int
|
||||||
|
visiting map[Type]bool
|
||||||
|
fields []StructField
|
||||||
|
index []int
|
||||||
|
}
|
||||||
|
|
||||||
|
// walk walks all the fields in the struct type t, visiting
|
||||||
|
// fields in index preorder and appending them to w.fields
|
||||||
|
// (this maintains the required ordering).
|
||||||
|
// Fields that have been overridden have their
|
||||||
|
// Name field cleared.
|
||||||
|
func (w *visibleFieldsWalker) walk(t Type) {
|
||||||
|
if w.visiting[t] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.visiting[t] = true
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
f := t.Field(i)
|
||||||
|
w.index = append(w.index, i)
|
||||||
|
add := true
|
||||||
|
if oldIndex, ok := w.byName[f.Name]; ok {
|
||||||
|
old := &w.fields[oldIndex]
|
||||||
|
if len(w.index) == len(old.Index) {
|
||||||
|
// Fields with the same name at the same depth
|
||||||
|
// cancel one another out. Set the field name
|
||||||
|
// to empty to signify that has happened, and
|
||||||
|
// there's no need to add this field.
|
||||||
|
old.Name = ""
|
||||||
|
add = false
|
||||||
|
} else if len(w.index) < len(old.Index) {
|
||||||
|
// The old field loses because it's deeper than the new one.
|
||||||
|
old.Name = ""
|
||||||
|
} else {
|
||||||
|
// The old field wins because it's shallower than the new one.
|
||||||
|
add = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if add {
|
||||||
|
// Copy the index so that it's not overwritten
|
||||||
|
// by the other appends.
|
||||||
|
f.Index = append([]int(nil), w.index...)
|
||||||
|
w.byName[f.Name] = len(w.fields)
|
||||||
|
w.fields = append(w.fields, f)
|
||||||
|
}
|
||||||
|
if f.Anonymous {
|
||||||
|
if f.Type.Kind() == Pointer {
|
||||||
|
f.Type = f.Type.Elem()
|
||||||
|
}
|
||||||
|
if f.Type.Kind() == Struct {
|
||||||
|
w.walk(f.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.index = w.index[:len(w.index)-1]
|
||||||
|
}
|
||||||
|
delete(w.visiting, t)
|
||||||
|
}
|
349
src/reflect/visiblefields_test.go
Обычный файл
349
src/reflect/visiblefields_test.go
Обычный файл
|
@ -0,0 +1,349 @@
|
||||||
|
// Copyright 2021 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 reflect_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type structField struct {
|
||||||
|
name string
|
||||||
|
index []int
|
||||||
|
}
|
||||||
|
|
||||||
|
var fieldsTests = []struct {
|
||||||
|
testName string
|
||||||
|
val any
|
||||||
|
expect []structField
|
||||||
|
}{{
|
||||||
|
testName: "SimpleStruct",
|
||||||
|
val: struct {
|
||||||
|
A int
|
||||||
|
B string
|
||||||
|
C bool
|
||||||
|
}{},
|
||||||
|
expect: []structField{{
|
||||||
|
name: "A",
|
||||||
|
index: []int{0},
|
||||||
|
}, {
|
||||||
|
name: "B",
|
||||||
|
index: []int{1},
|
||||||
|
}, {
|
||||||
|
name: "C",
|
||||||
|
index: []int{2},
|
||||||
|
}},
|
||||||
|
}, {
|
||||||
|
testName: "NonEmbeddedStructMember",
|
||||||
|
val: struct {
|
||||||
|
A struct {
|
||||||
|
X int
|
||||||
|
}
|
||||||
|
}{},
|
||||||
|
expect: []structField{{
|
||||||
|
name: "A",
|
||||||
|
index: []int{0},
|
||||||
|
}},
|
||||||
|
}, {
|
||||||
|
testName: "EmbeddedExportedStruct",
|
||||||
|
val: struct {
|
||||||
|
SFG
|
||||||
|
}{},
|
||||||
|
expect: []structField{{
|
||||||
|
name: "SFG",
|
||||||
|
index: []int{0},
|
||||||
|
}, {
|
||||||
|
name: "F",
|
||||||
|
index: []int{0, 0},
|
||||||
|
}, {
|
||||||
|
name: "G",
|
||||||
|
index: []int{0, 1},
|
||||||
|
}},
|
||||||
|
}, {
|
||||||
|
testName: "EmbeddedUnexportedStruct",
|
||||||
|
val: struct {
|
||||||
|
sFG
|
||||||
|
}{},
|
||||||
|
expect: []structField{{
|
||||||
|
name: "sFG",
|
||||||
|
index: []int{0},
|
||||||
|
}, {
|
||||||
|
name: "F",
|
||||||
|
index: []int{0, 0},
|
||||||
|
}, {
|
||||||
|
name: "G",
|
||||||
|
index: []int{0, 1},
|
||||||
|
}},
|
||||||
|
}, {
|
||||||
|
testName: "TwoEmbeddedStructsWithCancelingMembers",
|
||||||
|
val: struct {
|
||||||
|
SFG
|
||||||
|
SF
|
||||||
|
}{},
|
||||||
|
expect: []structField{{
|
||||||
|
name: "SFG",
|
||||||
|
index: []int{0},
|
||||||
|
}, {
|
||||||
|
name: "G",
|
||||||
|
index: []int{0, 1},
|
||||||
|
}, {
|
||||||
|
name: "SF",
|
||||||
|
index: []int{1},
|
||||||
|
}},
|
||||||
|
}, {
|
||||||
|
testName: "EmbeddedStructsWithSameFieldsAtDifferentDepths",
|
||||||
|
val: struct {
|
||||||
|
SFGH3
|
||||||
|
SG1
|
||||||
|
SFG2
|
||||||
|
SF2
|
||||||
|
L int
|
||||||
|
}{},
|
||||||
|
expect: []structField{{
|
||||||
|
name: "SFGH3",
|
||||||
|
index: []int{0},
|
||||||
|
}, {
|
||||||
|
name: "SFGH2",
|
||||||
|
index: []int{0, 0},
|
||||||
|
}, {
|
||||||
|
name: "SFGH1",
|
||||||
|
index: []int{0, 0, 0},
|
||||||
|
}, {
|
||||||
|
name: "SFGH",
|
||||||
|
index: []int{0, 0, 0, 0},
|
||||||
|
}, {
|
||||||
|
name: "H",
|
||||||
|
index: []int{0, 0, 0, 0, 2},
|
||||||
|
}, {
|
||||||
|
name: "SG1",
|
||||||
|
index: []int{1},
|
||||||
|
}, {
|
||||||
|
name: "SG",
|
||||||
|
index: []int{1, 0},
|
||||||
|
}, {
|
||||||
|
name: "G",
|
||||||
|
index: []int{1, 0, 0},
|
||||||
|
}, {
|
||||||
|
name: "SFG2",
|
||||||
|
index: []int{2},
|
||||||
|
}, {
|
||||||
|
name: "SFG1",
|
||||||
|
index: []int{2, 0},
|
||||||
|
}, {
|
||||||
|
name: "SFG",
|
||||||
|
index: []int{2, 0, 0},
|
||||||
|
}, {
|
||||||
|
name: "SF2",
|
||||||
|
index: []int{3},
|
||||||
|
}, {
|
||||||
|
name: "SF1",
|
||||||
|
index: []int{3, 0},
|
||||||
|
}, {
|
||||||
|
name: "SF",
|
||||||
|
index: []int{3, 0, 0},
|
||||||
|
}, {
|
||||||
|
name: "L",
|
||||||
|
index: []int{4},
|
||||||
|
}},
|
||||||
|
}, {
|
||||||
|
testName: "EmbeddedPointerStruct",
|
||||||
|
val: struct {
|
||||||
|
*SF
|
||||||
|
}{},
|
||||||
|
expect: []structField{{
|
||||||
|
name: "SF",
|
||||||
|
index: []int{0},
|
||||||
|
}, {
|
||||||
|
name: "F",
|
||||||
|
index: []int{0, 0},
|
||||||
|
}},
|
||||||
|
}, {
|
||||||
|
testName: "EmbeddedNotAPointer",
|
||||||
|
val: struct {
|
||||||
|
M
|
||||||
|
}{},
|
||||||
|
expect: []structField{{
|
||||||
|
name: "M",
|
||||||
|
index: []int{0},
|
||||||
|
}},
|
||||||
|
}, {
|
||||||
|
testName: "RecursiveEmbedding",
|
||||||
|
val: Rec1{},
|
||||||
|
expect: []structField{{
|
||||||
|
name: "Rec2",
|
||||||
|
index: []int{0},
|
||||||
|
}, {
|
||||||
|
name: "F",
|
||||||
|
index: []int{0, 0},
|
||||||
|
}, {
|
||||||
|
name: "Rec1",
|
||||||
|
index: []int{0, 1},
|
||||||
|
}},
|
||||||
|
}, {
|
||||||
|
testName: "RecursiveEmbedding2",
|
||||||
|
val: Rec2{},
|
||||||
|
expect: []structField{{
|
||||||
|
name: "F",
|
||||||
|
index: []int{0},
|
||||||
|
}, {
|
||||||
|
name: "Rec1",
|
||||||
|
index: []int{1},
|
||||||
|
}, {
|
||||||
|
name: "Rec2",
|
||||||
|
index: []int{1, 0},
|
||||||
|
}},
|
||||||
|
}, {
|
||||||
|
testName: "RecursiveEmbedding3",
|
||||||
|
val: RS3{},
|
||||||
|
expect: []structField{{
|
||||||
|
name: "RS2",
|
||||||
|
index: []int{0},
|
||||||
|
}, {
|
||||||
|
name: "RS1",
|
||||||
|
index: []int{1},
|
||||||
|
}, {
|
||||||
|
name: "i",
|
||||||
|
index: []int{1, 0},
|
||||||
|
}},
|
||||||
|
}}
|
||||||
|
|
||||||
|
type SFG struct {
|
||||||
|
F int
|
||||||
|
G int
|
||||||
|
}
|
||||||
|
|
||||||
|
type SFG1 struct {
|
||||||
|
SFG
|
||||||
|
}
|
||||||
|
|
||||||
|
type SFG2 struct {
|
||||||
|
SFG1
|
||||||
|
}
|
||||||
|
|
||||||
|
type SFGH struct {
|
||||||
|
F int
|
||||||
|
G int
|
||||||
|
H int
|
||||||
|
}
|
||||||
|
|
||||||
|
type SFGH1 struct {
|
||||||
|
SFGH
|
||||||
|
}
|
||||||
|
|
||||||
|
type SFGH2 struct {
|
||||||
|
SFGH1
|
||||||
|
}
|
||||||
|
|
||||||
|
type SFGH3 struct {
|
||||||
|
SFGH2
|
||||||
|
}
|
||||||
|
|
||||||
|
type SF struct {
|
||||||
|
F int
|
||||||
|
}
|
||||||
|
|
||||||
|
type SF1 struct {
|
||||||
|
SF
|
||||||
|
}
|
||||||
|
|
||||||
|
type SF2 struct {
|
||||||
|
SF1
|
||||||
|
}
|
||||||
|
|
||||||
|
type SG struct {
|
||||||
|
G int
|
||||||
|
}
|
||||||
|
|
||||||
|
type SG1 struct {
|
||||||
|
SG
|
||||||
|
}
|
||||||
|
|
||||||
|
type sFG struct {
|
||||||
|
F int
|
||||||
|
G int
|
||||||
|
}
|
||||||
|
|
||||||
|
type RS1 struct {
|
||||||
|
i int
|
||||||
|
}
|
||||||
|
|
||||||
|
type RS2 struct {
|
||||||
|
RS1
|
||||||
|
}
|
||||||
|
|
||||||
|
type RS3 struct {
|
||||||
|
RS2
|
||||||
|
RS1
|
||||||
|
}
|
||||||
|
|
||||||
|
type M map[string]any
|
||||||
|
|
||||||
|
type Rec1 struct {
|
||||||
|
*Rec2
|
||||||
|
}
|
||||||
|
|
||||||
|
type Rec2 struct {
|
||||||
|
F string
|
||||||
|
*Rec1
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFields(t *testing.T) {
|
||||||
|
for _, test := range fieldsTests {
|
||||||
|
test := test
|
||||||
|
t.Run(test.testName, func(t *testing.T) {
|
||||||
|
typ := TypeOf(test.val)
|
||||||
|
fields := VisibleFields(typ)
|
||||||
|
if got, want := len(fields), len(test.expect); got != want {
|
||||||
|
t.Fatalf("unexpected field count; got %d want %d", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
for j, field := range fields {
|
||||||
|
expect := test.expect[j]
|
||||||
|
t.Logf("field %d: %s", j, expect.name)
|
||||||
|
gotField := typ.FieldByIndex(field.Index)
|
||||||
|
// Unfortunately, FieldByIndex does not return
|
||||||
|
// a field with the same index that we passed in,
|
||||||
|
// so we set it to the expected value so that
|
||||||
|
// it can be compared later with the result of FieldByName.
|
||||||
|
gotField.Index = field.Index
|
||||||
|
expectField := typ.FieldByIndex(expect.index)
|
||||||
|
// ditto.
|
||||||
|
expectField.Index = expect.index
|
||||||
|
if !DeepEqual(gotField, expectField) {
|
||||||
|
t.Fatalf("unexpected field result\ngot %#v\nwant %#v", gotField, expectField)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check that we can actually access the field by the
|
||||||
|
// expected name.
|
||||||
|
gotField1, ok := typ.FieldByName(expect.name)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("field %q not accessible by name", expect.name)
|
||||||
|
}
|
||||||
|
if !DeepEqual(gotField1, expectField) {
|
||||||
|
t.Fatalf("unexpected FieldByName result; got %#v want %#v", gotField1, expectField)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
Загрузка…
Создание таблицы
Сослаться в новой задаче