| // 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: "TwoEmbeddedStructsWithCancellingMembers", | 
 | 	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) | 
 | 	} | 
 | } |