• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2021 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package reflect_test
6
7import (
8	. "reflect"
9	"strings"
10	"testing"
11)
12
13type structField struct {
14	name  string
15	index []int
16}
17
18var fieldsTests = []struct {
19	testName string
20	val      any
21	expect   []structField
22}{{
23	testName: "SimpleStruct",
24	val: struct {
25		A int
26		B string
27		C bool
28	}{},
29	expect: []structField{{
30		name:  "A",
31		index: []int{0},
32	}, {
33		name:  "B",
34		index: []int{1},
35	}, {
36		name:  "C",
37		index: []int{2},
38	}},
39}, {
40	testName: "NonEmbeddedStructMember",
41	val: struct {
42		A struct {
43			X int
44		}
45	}{},
46	expect: []structField{{
47		name:  "A",
48		index: []int{0},
49	}},
50}, {
51	testName: "EmbeddedExportedStruct",
52	val: struct {
53		SFG
54	}{},
55	expect: []structField{{
56		name:  "SFG",
57		index: []int{0},
58	}, {
59		name:  "F",
60		index: []int{0, 0},
61	}, {
62		name:  "G",
63		index: []int{0, 1},
64	}},
65}, {
66	testName: "EmbeddedUnexportedStruct",
67	val: struct {
68		sFG
69	}{},
70	expect: []structField{{
71		name:  "sFG",
72		index: []int{0},
73	}, {
74		name:  "F",
75		index: []int{0, 0},
76	}, {
77		name:  "G",
78		index: []int{0, 1},
79	}},
80}, {
81	testName: "TwoEmbeddedStructsWithCancelingMembers",
82	val: struct {
83		SFG
84		SF
85	}{},
86	expect: []structField{{
87		name:  "SFG",
88		index: []int{0},
89	}, {
90		name:  "G",
91		index: []int{0, 1},
92	}, {
93		name:  "SF",
94		index: []int{1},
95	}},
96}, {
97	testName: "EmbeddedStructsWithSameFieldsAtDifferentDepths",
98	val: struct {
99		SFGH3
100		SG1
101		SFG2
102		SF2
103		L int
104	}{},
105	expect: []structField{{
106		name:  "SFGH3",
107		index: []int{0},
108	}, {
109		name:  "SFGH2",
110		index: []int{0, 0},
111	}, {
112		name:  "SFGH1",
113		index: []int{0, 0, 0},
114	}, {
115		name:  "SFGH",
116		index: []int{0, 0, 0, 0},
117	}, {
118		name:  "H",
119		index: []int{0, 0, 0, 0, 2},
120	}, {
121		name:  "SG1",
122		index: []int{1},
123	}, {
124		name:  "SG",
125		index: []int{1, 0},
126	}, {
127		name:  "G",
128		index: []int{1, 0, 0},
129	}, {
130		name:  "SFG2",
131		index: []int{2},
132	}, {
133		name:  "SFG1",
134		index: []int{2, 0},
135	}, {
136		name:  "SFG",
137		index: []int{2, 0, 0},
138	}, {
139		name:  "SF2",
140		index: []int{3},
141	}, {
142		name:  "SF1",
143		index: []int{3, 0},
144	}, {
145		name:  "SF",
146		index: []int{3, 0, 0},
147	}, {
148		name:  "L",
149		index: []int{4},
150	}},
151}, {
152	testName: "EmbeddedPointerStruct",
153	val: struct {
154		*SF
155	}{},
156	expect: []structField{{
157		name:  "SF",
158		index: []int{0},
159	}, {
160		name:  "F",
161		index: []int{0, 0},
162	}},
163}, {
164	testName: "EmbeddedNotAPointer",
165	val: struct {
166		M
167	}{},
168	expect: []structField{{
169		name:  "M",
170		index: []int{0},
171	}},
172}, {
173	testName: "RecursiveEmbedding",
174	val:      Rec1{},
175	expect: []structField{{
176		name:  "Rec2",
177		index: []int{0},
178	}, {
179		name:  "F",
180		index: []int{0, 0},
181	}, {
182		name:  "Rec1",
183		index: []int{0, 1},
184	}},
185}, {
186	testName: "RecursiveEmbedding2",
187	val:      Rec2{},
188	expect: []structField{{
189		name:  "F",
190		index: []int{0},
191	}, {
192		name:  "Rec1",
193		index: []int{1},
194	}, {
195		name:  "Rec2",
196		index: []int{1, 0},
197	}},
198}, {
199	testName: "RecursiveEmbedding3",
200	val:      RS3{},
201	expect: []structField{{
202		name:  "RS2",
203		index: []int{0},
204	}, {
205		name:  "RS1",
206		index: []int{1},
207	}, {
208		name:  "i",
209		index: []int{1, 0},
210	}},
211}}
212
213type SFG struct {
214	F int
215	G int
216}
217
218type SFG1 struct {
219	SFG
220}
221
222type SFG2 struct {
223	SFG1
224}
225
226type SFGH struct {
227	F int
228	G int
229	H int
230}
231
232type SFGH1 struct {
233	SFGH
234}
235
236type SFGH2 struct {
237	SFGH1
238}
239
240type SFGH3 struct {
241	SFGH2
242}
243
244type SF struct {
245	F int
246}
247
248type SF1 struct {
249	SF
250}
251
252type SF2 struct {
253	SF1
254}
255
256type SG struct {
257	G int
258}
259
260type SG1 struct {
261	SG
262}
263
264type sFG struct {
265	F int
266	G int
267}
268
269type RS1 struct {
270	i int
271}
272
273type RS2 struct {
274	RS1
275}
276
277type RS3 struct {
278	RS2
279	RS1
280}
281
282type M map[string]any
283
284type Rec1 struct {
285	*Rec2
286}
287
288type Rec2 struct {
289	F string
290	*Rec1
291}
292
293func TestFields(t *testing.T) {
294	for _, test := range fieldsTests {
295		test := test
296		t.Run(test.testName, func(t *testing.T) {
297			typ := TypeOf(test.val)
298			fields := VisibleFields(typ)
299			if got, want := len(fields), len(test.expect); got != want {
300				t.Fatalf("unexpected field count; got %d want %d", got, want)
301			}
302
303			for j, field := range fields {
304				expect := test.expect[j]
305				t.Logf("field %d: %s", j, expect.name)
306				gotField := typ.FieldByIndex(field.Index)
307				// Unfortunately, FieldByIndex does not return
308				// a field with the same index that we passed in,
309				// so we set it to the expected value so that
310				// it can be compared later with the result of FieldByName.
311				gotField.Index = field.Index
312				expectField := typ.FieldByIndex(expect.index)
313				// ditto.
314				expectField.Index = expect.index
315				if !DeepEqual(gotField, expectField) {
316					t.Fatalf("unexpected field result\ngot %#v\nwant %#v", gotField, expectField)
317				}
318
319				// Sanity check that we can actually access the field by the
320				// expected name.
321				gotField1, ok := typ.FieldByName(expect.name)
322				if !ok {
323					t.Fatalf("field %q not accessible by name", expect.name)
324				}
325				if !DeepEqual(gotField1, expectField) {
326					t.Fatalf("unexpected FieldByName result; got %#v want %#v", gotField1, expectField)
327				}
328			}
329		})
330	}
331}
332
333// Must not panic with nil embedded pointer.
334func 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}
350