xref: /aosp_15_r20/external/golang-protobuf/reflect/protodesc/file_test.go (revision 1c12ee1efe575feb122dbf939ff15148a3b3e8f2)
1*1c12ee1eSDan Willemsen// Copyright 2019 The Go Authors. All rights reserved.
2*1c12ee1eSDan Willemsen// Use of this source code is governed by a BSD-style
3*1c12ee1eSDan Willemsen// license that can be found in the LICENSE file.
4*1c12ee1eSDan Willemsen
5*1c12ee1eSDan Willemsenpackage protodesc
6*1c12ee1eSDan Willemsen
7*1c12ee1eSDan Willemsenimport (
8*1c12ee1eSDan Willemsen	"fmt"
9*1c12ee1eSDan Willemsen	"strings"
10*1c12ee1eSDan Willemsen	"testing"
11*1c12ee1eSDan Willemsen
12*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/encoding/prototext"
13*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/internal/flags"
14*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/proto"
15*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/reflect/protoreflect"
16*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/reflect/protoregistry"
17*1c12ee1eSDan Willemsen
18*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/types/descriptorpb"
19*1c12ee1eSDan Willemsen)
20*1c12ee1eSDan Willemsen
21*1c12ee1eSDan Willemsenfunc mustParseFile(s string) *descriptorpb.FileDescriptorProto {
22*1c12ee1eSDan Willemsen	pb := new(descriptorpb.FileDescriptorProto)
23*1c12ee1eSDan Willemsen	if err := prototext.Unmarshal([]byte(s), pb); err != nil {
24*1c12ee1eSDan Willemsen		panic(err)
25*1c12ee1eSDan Willemsen	}
26*1c12ee1eSDan Willemsen	return pb
27*1c12ee1eSDan Willemsen}
28*1c12ee1eSDan Willemsen
29*1c12ee1eSDan Willemsenfunc cloneFile(in *descriptorpb.FileDescriptorProto) *descriptorpb.FileDescriptorProto {
30*1c12ee1eSDan Willemsen	return proto.Clone(in).(*descriptorpb.FileDescriptorProto)
31*1c12ee1eSDan Willemsen}
32*1c12ee1eSDan Willemsen
33*1c12ee1eSDan Willemsenvar (
34*1c12ee1eSDan Willemsen	proto2Enum = mustParseFile(`
35*1c12ee1eSDan Willemsen		syntax:    "proto2"
36*1c12ee1eSDan Willemsen		name:      "proto2_enum.proto"
37*1c12ee1eSDan Willemsen		package:   "test.proto2"
38*1c12ee1eSDan Willemsen		enum_type: [{name:"Enum" value:[{name:"ONE" number:1}]}]
39*1c12ee1eSDan Willemsen	`)
40*1c12ee1eSDan Willemsen	proto3Message = mustParseFile(`
41*1c12ee1eSDan Willemsen		syntax:    "proto3"
42*1c12ee1eSDan Willemsen		name:      "proto3_message.proto"
43*1c12ee1eSDan Willemsen		package:   "test.proto3"
44*1c12ee1eSDan Willemsen		message_type: [{
45*1c12ee1eSDan Willemsen			name:  "Message"
46*1c12ee1eSDan Willemsen			field: [
47*1c12ee1eSDan Willemsen				{name:"foo" number:1 label:LABEL_OPTIONAL type:TYPE_STRING},
48*1c12ee1eSDan Willemsen				{name:"bar" number:2 label:LABEL_OPTIONAL type:TYPE_STRING}
49*1c12ee1eSDan Willemsen			]
50*1c12ee1eSDan Willemsen		}]
51*1c12ee1eSDan Willemsen	`)
52*1c12ee1eSDan Willemsen	extendableMessage = mustParseFile(`
53*1c12ee1eSDan Willemsen		syntax:       "proto2"
54*1c12ee1eSDan Willemsen		name:         "extendable_message.proto"
55*1c12ee1eSDan Willemsen		package:      "test.proto2"
56*1c12ee1eSDan Willemsen		message_type: [{name:"Message" extension_range:[{start:1 end:1000}]}]
57*1c12ee1eSDan Willemsen	`)
58*1c12ee1eSDan Willemsen	importPublicFile1 = mustParseFile(`
59*1c12ee1eSDan Willemsen		syntax:            "proto3"
60*1c12ee1eSDan Willemsen		name:              "import_public1.proto"
61*1c12ee1eSDan Willemsen		dependency:        ["proto2_enum.proto", "proto3_message.proto", "extendable_message.proto"]
62*1c12ee1eSDan Willemsen		message_type:      [{name:"Public1"}]
63*1c12ee1eSDan Willemsen	`)
64*1c12ee1eSDan Willemsen	importPublicFile2 = mustParseFile(`
65*1c12ee1eSDan Willemsen		syntax:            "proto3"
66*1c12ee1eSDan Willemsen		name:              "import_public2.proto"
67*1c12ee1eSDan Willemsen		dependency:        ["import_public1.proto"]
68*1c12ee1eSDan Willemsen		public_dependency: [0]
69*1c12ee1eSDan Willemsen		message_type:      [{name:"Public2"}]
70*1c12ee1eSDan Willemsen	`)
71*1c12ee1eSDan Willemsen	importPublicFile3 = mustParseFile(`
72*1c12ee1eSDan Willemsen		syntax:            "proto3"
73*1c12ee1eSDan Willemsen		name:              "import_public3.proto"
74*1c12ee1eSDan Willemsen		dependency:        ["import_public2.proto", "extendable_message.proto"]
75*1c12ee1eSDan Willemsen		public_dependency: [0]
76*1c12ee1eSDan Willemsen		message_type:      [{name:"Public3"}]
77*1c12ee1eSDan Willemsen	`)
78*1c12ee1eSDan Willemsen	importPublicFile4 = mustParseFile(`
79*1c12ee1eSDan Willemsen		syntax:            "proto3"
80*1c12ee1eSDan Willemsen		name:              "import_public4.proto"
81*1c12ee1eSDan Willemsen		dependency:        ["import_public2.proto", "import_public3.proto", "proto2_enum.proto"]
82*1c12ee1eSDan Willemsen		public_dependency: [0, 1]
83*1c12ee1eSDan Willemsen		message_type:      [{name:"Public4"}]
84*1c12ee1eSDan Willemsen	`)
85*1c12ee1eSDan Willemsen)
86*1c12ee1eSDan Willemsen
87*1c12ee1eSDan Willemsenfunc TestNewFile(t *testing.T) {
88*1c12ee1eSDan Willemsen	tests := []struct {
89*1c12ee1eSDan Willemsen		label    string
90*1c12ee1eSDan Willemsen		inDeps   []*descriptorpb.FileDescriptorProto
91*1c12ee1eSDan Willemsen		inDesc   *descriptorpb.FileDescriptorProto
92*1c12ee1eSDan Willemsen		inOpts   FileOptions
93*1c12ee1eSDan Willemsen		wantDesc *descriptorpb.FileDescriptorProto
94*1c12ee1eSDan Willemsen		wantErr  string
95*1c12ee1eSDan Willemsen	}{{
96*1c12ee1eSDan Willemsen		label:   "empty path",
97*1c12ee1eSDan Willemsen		inDesc:  mustParseFile(``),
98*1c12ee1eSDan Willemsen		wantErr: `path must be populated`,
99*1c12ee1eSDan Willemsen	}, {
100*1c12ee1eSDan Willemsen		label:  "empty package and syntax",
101*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`name:"weird"`),
102*1c12ee1eSDan Willemsen	}, {
103*1c12ee1eSDan Willemsen		label:   "invalid syntax",
104*1c12ee1eSDan Willemsen		inDesc:  mustParseFile(`name:"weird" syntax:"proto9"`),
105*1c12ee1eSDan Willemsen		wantErr: `invalid syntax: "proto9"`,
106*1c12ee1eSDan Willemsen	}, {
107*1c12ee1eSDan Willemsen		label:   "bad package",
108*1c12ee1eSDan Willemsen		inDesc:  mustParseFile(`name:"weird" package:"$"`),
109*1c12ee1eSDan Willemsen		wantErr: `invalid package: "$"`,
110*1c12ee1eSDan Willemsen	}, {
111*1c12ee1eSDan Willemsen		label: "unresolvable import",
112*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
113*1c12ee1eSDan Willemsen			name:       "test.proto"
114*1c12ee1eSDan Willemsen			dependency: "dep.proto"
115*1c12ee1eSDan Willemsen		`),
116*1c12ee1eSDan Willemsen		wantErr: `could not resolve import "dep.proto": not found`,
117*1c12ee1eSDan Willemsen	}, {
118*1c12ee1eSDan Willemsen		label: "unresolvable import but allowed",
119*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
120*1c12ee1eSDan Willemsen			name:       "test.proto"
121*1c12ee1eSDan Willemsen			dependency: "dep.proto"
122*1c12ee1eSDan Willemsen		`),
123*1c12ee1eSDan Willemsen		inOpts: FileOptions{AllowUnresolvable: true},
124*1c12ee1eSDan Willemsen	}, {
125*1c12ee1eSDan Willemsen		label: "duplicate import",
126*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
127*1c12ee1eSDan Willemsen			name:       "test.proto"
128*1c12ee1eSDan Willemsen			dependency: ["dep.proto", "dep.proto"]
129*1c12ee1eSDan Willemsen		`),
130*1c12ee1eSDan Willemsen		inOpts:  FileOptions{AllowUnresolvable: true},
131*1c12ee1eSDan Willemsen		wantErr: `already imported "dep.proto"`,
132*1c12ee1eSDan Willemsen	}, {
133*1c12ee1eSDan Willemsen		label: "invalid weak import",
134*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
135*1c12ee1eSDan Willemsen			name:            "test.proto"
136*1c12ee1eSDan Willemsen			dependency:      "dep.proto"
137*1c12ee1eSDan Willemsen			weak_dependency: [-23]
138*1c12ee1eSDan Willemsen		`),
139*1c12ee1eSDan Willemsen		inOpts:  FileOptions{AllowUnresolvable: true},
140*1c12ee1eSDan Willemsen		wantErr: `invalid or duplicate weak import index: -23`,
141*1c12ee1eSDan Willemsen	}, {
142*1c12ee1eSDan Willemsen		label: "normal weak and public import",
143*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
144*1c12ee1eSDan Willemsen			name:              "test.proto"
145*1c12ee1eSDan Willemsen			dependency:        "dep.proto"
146*1c12ee1eSDan Willemsen			weak_dependency:   [0]
147*1c12ee1eSDan Willemsen			public_dependency: [0]
148*1c12ee1eSDan Willemsen		`),
149*1c12ee1eSDan Willemsen		inOpts: FileOptions{AllowUnresolvable: true},
150*1c12ee1eSDan Willemsen	}, {
151*1c12ee1eSDan Willemsen		label: "import public indirect dependency duplicate",
152*1c12ee1eSDan Willemsen		inDeps: []*descriptorpb.FileDescriptorProto{
153*1c12ee1eSDan Willemsen			mustParseFile(`name:"leaf.proto"`),
154*1c12ee1eSDan Willemsen			mustParseFile(`name:"public.proto" dependency:"leaf.proto" public_dependency:0`),
155*1c12ee1eSDan Willemsen		},
156*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
157*1c12ee1eSDan Willemsen			name: "test.proto"
158*1c12ee1eSDan Willemsen			dependency: ["public.proto", "leaf.proto"]
159*1c12ee1eSDan Willemsen		`),
160*1c12ee1eSDan Willemsen	}, {
161*1c12ee1eSDan Willemsen		label: "import public graph",
162*1c12ee1eSDan Willemsen		inDeps: []*descriptorpb.FileDescriptorProto{
163*1c12ee1eSDan Willemsen			cloneFile(proto2Enum),
164*1c12ee1eSDan Willemsen			cloneFile(proto3Message),
165*1c12ee1eSDan Willemsen			cloneFile(extendableMessage),
166*1c12ee1eSDan Willemsen			cloneFile(importPublicFile1),
167*1c12ee1eSDan Willemsen			cloneFile(importPublicFile2),
168*1c12ee1eSDan Willemsen			cloneFile(importPublicFile3),
169*1c12ee1eSDan Willemsen			cloneFile(importPublicFile4),
170*1c12ee1eSDan Willemsen		},
171*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
172*1c12ee1eSDan Willemsen			name:       "test.proto"
173*1c12ee1eSDan Willemsen			package:    "test.graph"
174*1c12ee1eSDan Willemsen			dependency: ["import_public4.proto"],
175*1c12ee1eSDan Willemsen		`),
176*1c12ee1eSDan Willemsen		// TODO: Test import public
177*1c12ee1eSDan Willemsen	}, {
178*1c12ee1eSDan Willemsen		label: "preserve source code locations",
179*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
180*1c12ee1eSDan Willemsen			name: "test.proto"
181*1c12ee1eSDan Willemsen			package: "fizz.buzz"
182*1c12ee1eSDan Willemsen			source_code_info: {location: [{
183*1c12ee1eSDan Willemsen				span: [39,0,882,1]
184*1c12ee1eSDan Willemsen			}, {
185*1c12ee1eSDan Willemsen				path: [12]
186*1c12ee1eSDan Willemsen				span: [39,0,18]
187*1c12ee1eSDan Willemsen				leading_detached_comments: [" foo\n"," bar\n"]
188*1c12ee1eSDan Willemsen			}, {
189*1c12ee1eSDan Willemsen				path: [8,9]
190*1c12ee1eSDan Willemsen				span: [51,0,28]
191*1c12ee1eSDan Willemsen				leading_comments: " Comment\n"
192*1c12ee1eSDan Willemsen			}]}
193*1c12ee1eSDan Willemsen		`),
194*1c12ee1eSDan Willemsen	}, {
195*1c12ee1eSDan Willemsen		label: "invalid source code span",
196*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
197*1c12ee1eSDan Willemsen			name: "test.proto"
198*1c12ee1eSDan Willemsen			package: "fizz.buzz"
199*1c12ee1eSDan Willemsen			source_code_info: {location: [{
200*1c12ee1eSDan Willemsen				span: [39]
201*1c12ee1eSDan Willemsen			}]}
202*1c12ee1eSDan Willemsen		`),
203*1c12ee1eSDan Willemsen		wantErr: `invalid span: [39]`,
204*1c12ee1eSDan Willemsen	}, {
205*1c12ee1eSDan Willemsen		label: "resolve relative reference",
206*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
207*1c12ee1eSDan Willemsen			name: "test.proto"
208*1c12ee1eSDan Willemsen			package: "fizz.buzz"
209*1c12ee1eSDan Willemsen			message_type: [{
210*1c12ee1eSDan Willemsen				name: "A"
211*1c12ee1eSDan Willemsen				field: [{name:"F" number:1 label:LABEL_OPTIONAL type:TYPE_MESSAGE type_name:"B.C"}]
212*1c12ee1eSDan Willemsen				nested_type: [{name: "B"}]
213*1c12ee1eSDan Willemsen			}, {
214*1c12ee1eSDan Willemsen				name: "B"
215*1c12ee1eSDan Willemsen				nested_type: [{name: "C"}]
216*1c12ee1eSDan Willemsen			}]
217*1c12ee1eSDan Willemsen		`),
218*1c12ee1eSDan Willemsen		wantDesc: mustParseFile(`
219*1c12ee1eSDan Willemsen			name: "test.proto"
220*1c12ee1eSDan Willemsen			package: "fizz.buzz"
221*1c12ee1eSDan Willemsen			message_type: [{
222*1c12ee1eSDan Willemsen				name: "A"
223*1c12ee1eSDan Willemsen				field: [{name:"F" number:1 label:LABEL_OPTIONAL type:TYPE_MESSAGE type_name:".fizz.buzz.B.C"}]
224*1c12ee1eSDan Willemsen				nested_type: [{name: "B"}]
225*1c12ee1eSDan Willemsen			}, {
226*1c12ee1eSDan Willemsen				name: "B"
227*1c12ee1eSDan Willemsen				nested_type: [{name: "C"}]
228*1c12ee1eSDan Willemsen			}]
229*1c12ee1eSDan Willemsen		`),
230*1c12ee1eSDan Willemsen	}, {
231*1c12ee1eSDan Willemsen		label: "resolve the wrong type",
232*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
233*1c12ee1eSDan Willemsen			name: "test.proto"
234*1c12ee1eSDan Willemsen			message_type: [{
235*1c12ee1eSDan Willemsen				name: "M"
236*1c12ee1eSDan Willemsen				field: [{name:"F" number:1 label:LABEL_OPTIONAL type:TYPE_MESSAGE type_name:"E"}]
237*1c12ee1eSDan Willemsen				enum_type: [{name: "E" value: [{name:"V0" number:0}, {name:"V1" number:1}]}]
238*1c12ee1eSDan Willemsen			}]
239*1c12ee1eSDan Willemsen		`),
240*1c12ee1eSDan Willemsen		wantErr: `message field "M.F" cannot resolve type: resolved "M.E", but it is not an message`,
241*1c12ee1eSDan Willemsen	}, {
242*1c12ee1eSDan Willemsen		label: "auto-resolve unknown kind",
243*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
244*1c12ee1eSDan Willemsen			name: "test.proto"
245*1c12ee1eSDan Willemsen			message_type: [{
246*1c12ee1eSDan Willemsen				name: "M"
247*1c12ee1eSDan Willemsen				field: [{name:"F" number:1 label:LABEL_OPTIONAL type_name:"E"}]
248*1c12ee1eSDan Willemsen				enum_type: [{name: "E" value: [{name:"V0" number:0}, {name:"V1" number:1}]}]
249*1c12ee1eSDan Willemsen			}]
250*1c12ee1eSDan Willemsen		`),
251*1c12ee1eSDan Willemsen		wantDesc: mustParseFile(`
252*1c12ee1eSDan Willemsen			name: "test.proto"
253*1c12ee1eSDan Willemsen			message_type: [{
254*1c12ee1eSDan Willemsen				name: "M"
255*1c12ee1eSDan Willemsen				field: [{name:"F" number:1 label:LABEL_OPTIONAL type:TYPE_ENUM type_name:".M.E"}]
256*1c12ee1eSDan Willemsen				enum_type: [{name: "E" value: [{name:"V0" number:0}, {name:"V1" number:1}]}]
257*1c12ee1eSDan Willemsen			}]
258*1c12ee1eSDan Willemsen		`),
259*1c12ee1eSDan Willemsen	}, {
260*1c12ee1eSDan Willemsen		label: "unresolved import",
261*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
262*1c12ee1eSDan Willemsen			name: "test.proto"
263*1c12ee1eSDan Willemsen			package: "fizz.buzz"
264*1c12ee1eSDan Willemsen			dependency: "remote.proto"
265*1c12ee1eSDan Willemsen		`),
266*1c12ee1eSDan Willemsen		wantErr: `could not resolve import "remote.proto": not found`,
267*1c12ee1eSDan Willemsen	}, {
268*1c12ee1eSDan Willemsen		label: "unresolved message field",
269*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
270*1c12ee1eSDan Willemsen			name: "test.proto"
271*1c12ee1eSDan Willemsen			package: "fizz.buzz"
272*1c12ee1eSDan Willemsen			message_type: [{
273*1c12ee1eSDan Willemsen				name: "M"
274*1c12ee1eSDan Willemsen				field: [{name:"F1" number:1 label:LABEL_OPTIONAL type:TYPE_ENUM type_name:"some.other.enum" default_value:"UNKNOWN"}]
275*1c12ee1eSDan Willemsen			}]
276*1c12ee1eSDan Willemsen		`),
277*1c12ee1eSDan Willemsen		wantErr: `message field "fizz.buzz.M.F1" cannot resolve type: "*.some.other.enum" not found`,
278*1c12ee1eSDan Willemsen	}, {
279*1c12ee1eSDan Willemsen		label: "unresolved default enum value",
280*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
281*1c12ee1eSDan Willemsen			name: "test.proto"
282*1c12ee1eSDan Willemsen			package: "fizz.buzz"
283*1c12ee1eSDan Willemsen			message_type: [{
284*1c12ee1eSDan Willemsen				name: "M"
285*1c12ee1eSDan Willemsen				field: [{name:"F1" number:1 label:LABEL_OPTIONAL type:TYPE_ENUM type_name:"E" default_value:"UNKNOWN"}]
286*1c12ee1eSDan Willemsen				enum_type: [{name:"E" value:[{name:"V0" number:0}]}]
287*1c12ee1eSDan Willemsen			}]
288*1c12ee1eSDan Willemsen		`),
289*1c12ee1eSDan Willemsen		wantErr: `message field "fizz.buzz.M.F1" has invalid default: could not parse value for enum: "UNKNOWN"`,
290*1c12ee1eSDan Willemsen	}, {
291*1c12ee1eSDan Willemsen		label: "allowed unresolved default enum value",
292*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
293*1c12ee1eSDan Willemsen			name: "test.proto"
294*1c12ee1eSDan Willemsen			package: "fizz.buzz"
295*1c12ee1eSDan Willemsen			message_type: [{
296*1c12ee1eSDan Willemsen				name: "M"
297*1c12ee1eSDan Willemsen				field: [{name:"F1" number:1 label:LABEL_OPTIONAL type:TYPE_ENUM type_name:".fizz.buzz.M.E" default_value:"UNKNOWN"}]
298*1c12ee1eSDan Willemsen				enum_type: [{name:"E" value:[{name:"V0" number:0}]}]
299*1c12ee1eSDan Willemsen			}]
300*1c12ee1eSDan Willemsen		`),
301*1c12ee1eSDan Willemsen		inOpts: FileOptions{AllowUnresolvable: true},
302*1c12ee1eSDan Willemsen	}, {
303*1c12ee1eSDan Willemsen		label: "unresolved extendee",
304*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
305*1c12ee1eSDan Willemsen			name: "test.proto"
306*1c12ee1eSDan Willemsen			package: "fizz.buzz"
307*1c12ee1eSDan Willemsen			extension: [{name:"X" number:1 label:LABEL_OPTIONAL extendee:"some.extended.message" type:TYPE_MESSAGE type_name:"some.other.message"}]
308*1c12ee1eSDan Willemsen		`),
309*1c12ee1eSDan Willemsen		wantErr: `extension field "fizz.buzz.X" cannot resolve extendee: "*.some.extended.message" not found`,
310*1c12ee1eSDan Willemsen	}, {
311*1c12ee1eSDan Willemsen		label: "unresolved method input",
312*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
313*1c12ee1eSDan Willemsen			name: "test.proto"
314*1c12ee1eSDan Willemsen			package: "fizz.buzz"
315*1c12ee1eSDan Willemsen			service: [{
316*1c12ee1eSDan Willemsen				name: "S"
317*1c12ee1eSDan Willemsen				method: [{name:"M" input_type:"foo.bar.input" output_type:".absolute.foo.bar.output"}]
318*1c12ee1eSDan Willemsen			}]
319*1c12ee1eSDan Willemsen		`),
320*1c12ee1eSDan Willemsen		wantErr: `service method "fizz.buzz.S.M" cannot resolve input: "*.foo.bar.input" not found`,
321*1c12ee1eSDan Willemsen	}, {
322*1c12ee1eSDan Willemsen		label: "allowed unresolved references",
323*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
324*1c12ee1eSDan Willemsen			name: "test.proto"
325*1c12ee1eSDan Willemsen			package: "fizz.buzz"
326*1c12ee1eSDan Willemsen			dependency: "remote.proto"
327*1c12ee1eSDan Willemsen			message_type: [{
328*1c12ee1eSDan Willemsen				name: "M"
329*1c12ee1eSDan Willemsen				field: [{name:"F1" number:1 label:LABEL_OPTIONAL type_name:"some.other.enum" default_value:"UNKNOWN"}]
330*1c12ee1eSDan Willemsen			}]
331*1c12ee1eSDan Willemsen			extension: [{name:"X" number:1 label:LABEL_OPTIONAL extendee:"some.extended.message" type:TYPE_MESSAGE type_name:"some.other.message"}]
332*1c12ee1eSDan Willemsen			service: [{
333*1c12ee1eSDan Willemsen				name: "S"
334*1c12ee1eSDan Willemsen				method: [{name:"M" input_type:"foo.bar.input" output_type:".absolute.foo.bar.output"}]
335*1c12ee1eSDan Willemsen			}]
336*1c12ee1eSDan Willemsen		`),
337*1c12ee1eSDan Willemsen		inOpts: FileOptions{AllowUnresolvable: true},
338*1c12ee1eSDan Willemsen	}, {
339*1c12ee1eSDan Willemsen		label: "resolved but not imported",
340*1c12ee1eSDan Willemsen		inDeps: []*descriptorpb.FileDescriptorProto{mustParseFile(`
341*1c12ee1eSDan Willemsen			name: "dep.proto"
342*1c12ee1eSDan Willemsen			package: "fizz"
343*1c12ee1eSDan Willemsen			message_type: [{name:"M" nested_type:[{name:"M"}]}]
344*1c12ee1eSDan Willemsen		`)},
345*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
346*1c12ee1eSDan Willemsen			name: "test.proto"
347*1c12ee1eSDan Willemsen			package: "fizz.buzz"
348*1c12ee1eSDan Willemsen			message_type: [{
349*1c12ee1eSDan Willemsen				name: "M"
350*1c12ee1eSDan Willemsen				field: [{name:"F" number:1 label:LABEL_OPTIONAL type:TYPE_MESSAGE type_name:"M.M"}]
351*1c12ee1eSDan Willemsen			}]
352*1c12ee1eSDan Willemsen		`),
353*1c12ee1eSDan Willemsen		wantErr: `message field "fizz.buzz.M.F" cannot resolve type: resolved "fizz.M.M", but "dep.proto" is not imported`,
354*1c12ee1eSDan Willemsen	}, {
355*1c12ee1eSDan Willemsen		label: "resolved from remote import",
356*1c12ee1eSDan Willemsen		inDeps: []*descriptorpb.FileDescriptorProto{mustParseFile(`
357*1c12ee1eSDan Willemsen			name: "dep.proto"
358*1c12ee1eSDan Willemsen			package: "fizz"
359*1c12ee1eSDan Willemsen			message_type: [{name:"M" nested_type:[{name:"M"}]}]
360*1c12ee1eSDan Willemsen		`)},
361*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
362*1c12ee1eSDan Willemsen			name: "test.proto"
363*1c12ee1eSDan Willemsen			package: "fizz.buzz"
364*1c12ee1eSDan Willemsen			dependency: "dep.proto"
365*1c12ee1eSDan Willemsen			message_type: [{
366*1c12ee1eSDan Willemsen				name: "M"
367*1c12ee1eSDan Willemsen				field: [{name:"F" number:1 label:LABEL_OPTIONAL type:TYPE_MESSAGE type_name:"M.M"}]
368*1c12ee1eSDan Willemsen			}]
369*1c12ee1eSDan Willemsen		`),
370*1c12ee1eSDan Willemsen		wantDesc: mustParseFile(`
371*1c12ee1eSDan Willemsen			name: "test.proto"
372*1c12ee1eSDan Willemsen			package: "fizz.buzz"
373*1c12ee1eSDan Willemsen			dependency: "dep.proto"
374*1c12ee1eSDan Willemsen			message_type: [{
375*1c12ee1eSDan Willemsen				name: "M"
376*1c12ee1eSDan Willemsen				field: [{name:"F" number:1 label:LABEL_OPTIONAL type:TYPE_MESSAGE type_name:".fizz.M.M"}]
377*1c12ee1eSDan Willemsen			}]
378*1c12ee1eSDan Willemsen		`),
379*1c12ee1eSDan Willemsen	}, {
380*1c12ee1eSDan Willemsen		label: "namespace conflict on enum value",
381*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
382*1c12ee1eSDan Willemsen			name:    "test.proto"
383*1c12ee1eSDan Willemsen			enum_type: [{
384*1c12ee1eSDan Willemsen				name: "foo"
385*1c12ee1eSDan Willemsen				value: [{name:"foo" number:0}]
386*1c12ee1eSDan Willemsen			}]
387*1c12ee1eSDan Willemsen		`),
388*1c12ee1eSDan Willemsen		wantErr: `descriptor "foo" already declared`,
389*1c12ee1eSDan Willemsen	}, {
390*1c12ee1eSDan Willemsen		label: "no namespace conflict on message field",
391*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
392*1c12ee1eSDan Willemsen			name:    "test.proto"
393*1c12ee1eSDan Willemsen			message_type: [{
394*1c12ee1eSDan Willemsen				name: "foo"
395*1c12ee1eSDan Willemsen				field: [{name:"foo" number:1 label:LABEL_OPTIONAL type:TYPE_STRING}]
396*1c12ee1eSDan Willemsen			}]
397*1c12ee1eSDan Willemsen		`),
398*1c12ee1eSDan Willemsen	}, {
399*1c12ee1eSDan Willemsen		label: "invalid name",
400*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
401*1c12ee1eSDan Willemsen			name:    "test.proto"
402*1c12ee1eSDan Willemsen			message_type: [{name: "$"}]
403*1c12ee1eSDan Willemsen		`),
404*1c12ee1eSDan Willemsen		wantErr: `descriptor "" has an invalid nested name: "$"`,
405*1c12ee1eSDan Willemsen	}, {
406*1c12ee1eSDan Willemsen		label: "invalid empty enum",
407*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
408*1c12ee1eSDan Willemsen			name:    "test.proto"
409*1c12ee1eSDan Willemsen			message_type: [{name:"M" enum_type:[{name:"E"}]}]
410*1c12ee1eSDan Willemsen		`),
411*1c12ee1eSDan Willemsen		wantErr: `enum "M.E" must contain at least one value declaration`,
412*1c12ee1eSDan Willemsen	}, {
413*1c12ee1eSDan Willemsen		label: "invalid enum value without number",
414*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
415*1c12ee1eSDan Willemsen			name:    "test.proto"
416*1c12ee1eSDan Willemsen			message_type: [{name:"M" enum_type:[{name:"E" value:[{name:"one"}]}]}]
417*1c12ee1eSDan Willemsen		`),
418*1c12ee1eSDan Willemsen		wantErr: `enum value "M.one" must have a specified number`,
419*1c12ee1eSDan Willemsen	}, {
420*1c12ee1eSDan Willemsen		label: "valid enum",
421*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
422*1c12ee1eSDan Willemsen			name:    "test.proto"
423*1c12ee1eSDan Willemsen			message_type: [{name:"M" enum_type:[{name:"E" value:[{name:"one" number:1}]}]}]
424*1c12ee1eSDan Willemsen		`),
425*1c12ee1eSDan Willemsen	}, {
426*1c12ee1eSDan Willemsen		label: "invalid enum reserved names",
427*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
428*1c12ee1eSDan Willemsen			name:    "test.proto"
429*1c12ee1eSDan Willemsen			message_type: [{name:"M" enum_type:[{
430*1c12ee1eSDan Willemsen				name:          "E"
431*1c12ee1eSDan Willemsen				reserved_name: [""]
432*1c12ee1eSDan Willemsen				value: [{name:"V" number:0}]
433*1c12ee1eSDan Willemsen			}]}]
434*1c12ee1eSDan Willemsen		`),
435*1c12ee1eSDan Willemsen		// NOTE: In theory this should be an error.
436*1c12ee1eSDan Willemsen		// See https://github.com/protocolbuffers/protobuf/issues/6335.
437*1c12ee1eSDan Willemsen		/*wantErr: `enum "M.E" reserved names has invalid name: ""`,*/
438*1c12ee1eSDan Willemsen	}, {
439*1c12ee1eSDan Willemsen		label: "duplicate enum reserved names",
440*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
441*1c12ee1eSDan Willemsen			name:    "test.proto"
442*1c12ee1eSDan Willemsen			message_type: [{name:"M" enum_type:[{
443*1c12ee1eSDan Willemsen				name:          "E"
444*1c12ee1eSDan Willemsen				reserved_name: ["foo", "foo"]
445*1c12ee1eSDan Willemsen			}]}]
446*1c12ee1eSDan Willemsen		`),
447*1c12ee1eSDan Willemsen		wantErr: `enum "M.E" reserved names has duplicate name: "foo"`,
448*1c12ee1eSDan Willemsen	}, {
449*1c12ee1eSDan Willemsen		label: "valid enum reserved names",
450*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
451*1c12ee1eSDan Willemsen			name:    "test.proto"
452*1c12ee1eSDan Willemsen			message_type: [{name:"M" enum_type:[{
453*1c12ee1eSDan Willemsen				name:          "E"
454*1c12ee1eSDan Willemsen				reserved_name: ["foo", "bar"]
455*1c12ee1eSDan Willemsen				value:         [{name:"baz" number:1}]
456*1c12ee1eSDan Willemsen			}]}]
457*1c12ee1eSDan Willemsen		`),
458*1c12ee1eSDan Willemsen	}, {
459*1c12ee1eSDan Willemsen		label: "use of enum reserved names",
460*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
461*1c12ee1eSDan Willemsen			name:    "test.proto"
462*1c12ee1eSDan Willemsen			message_type: [{name:"M" enum_type:[{
463*1c12ee1eSDan Willemsen				name:          "E"
464*1c12ee1eSDan Willemsen				reserved_name: ["foo", "bar"]
465*1c12ee1eSDan Willemsen				value:         [{name:"foo" number:1}]
466*1c12ee1eSDan Willemsen			}]}]
467*1c12ee1eSDan Willemsen		`),
468*1c12ee1eSDan Willemsen		wantErr: `enum value "M.foo" must not use reserved name`,
469*1c12ee1eSDan Willemsen	}, {
470*1c12ee1eSDan Willemsen		label: "invalid enum reserved ranges",
471*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
472*1c12ee1eSDan Willemsen			name:    "test.proto"
473*1c12ee1eSDan Willemsen			message_type: [{name:"M" enum_type:[{
474*1c12ee1eSDan Willemsen				name:           "E"
475*1c12ee1eSDan Willemsen				reserved_range: [{start:5 end:4}]
476*1c12ee1eSDan Willemsen			}]}]
477*1c12ee1eSDan Willemsen		`),
478*1c12ee1eSDan Willemsen		wantErr: `enum "M.E" reserved ranges has invalid range: 5 to 4`,
479*1c12ee1eSDan Willemsen	}, {
480*1c12ee1eSDan Willemsen		label: "overlapping enum reserved ranges",
481*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
482*1c12ee1eSDan Willemsen			name:    "test.proto"
483*1c12ee1eSDan Willemsen			message_type: [{name:"M" enum_type:[{
484*1c12ee1eSDan Willemsen				name:           "E"
485*1c12ee1eSDan Willemsen				reserved_range: [{start:1 end:1000}, {start:10 end:100}]
486*1c12ee1eSDan Willemsen			}]}]
487*1c12ee1eSDan Willemsen		`),
488*1c12ee1eSDan Willemsen		wantErr: `enum "M.E" reserved ranges has overlapping ranges: 1 to 1000 with 10 to 100`,
489*1c12ee1eSDan Willemsen	}, {
490*1c12ee1eSDan Willemsen		label: "valid enum reserved names",
491*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
492*1c12ee1eSDan Willemsen			name:    "test.proto"
493*1c12ee1eSDan Willemsen			message_type: [{name:"M" enum_type:[{
494*1c12ee1eSDan Willemsen				name:           "E"
495*1c12ee1eSDan Willemsen				reserved_range: [{start:1 end:10}, {start:100 end:1000}]
496*1c12ee1eSDan Willemsen				value:          [{name:"baz" number:50}]
497*1c12ee1eSDan Willemsen			}]}]
498*1c12ee1eSDan Willemsen		`),
499*1c12ee1eSDan Willemsen	}, {
500*1c12ee1eSDan Willemsen		label: "use of enum reserved range",
501*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
502*1c12ee1eSDan Willemsen			name:    "test.proto"
503*1c12ee1eSDan Willemsen			message_type: [{name:"M" enum_type:[{
504*1c12ee1eSDan Willemsen				name:           "E"
505*1c12ee1eSDan Willemsen				reserved_range: [{start:1 end:10}, {start:100 end:1000}]
506*1c12ee1eSDan Willemsen				value:          [{name:"baz" number:500}]
507*1c12ee1eSDan Willemsen			}]}]
508*1c12ee1eSDan Willemsen		`),
509*1c12ee1eSDan Willemsen		wantErr: `enum value "M.baz" must not use reserved number 500`,
510*1c12ee1eSDan Willemsen	}, {
511*1c12ee1eSDan Willemsen		label: "unused enum alias feature",
512*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
513*1c12ee1eSDan Willemsen			name:    "test.proto"
514*1c12ee1eSDan Willemsen			message_type: [{name:"M" enum_type:[{
515*1c12ee1eSDan Willemsen				name:    "E"
516*1c12ee1eSDan Willemsen				value:   [{name:"baz" number:500}]
517*1c12ee1eSDan Willemsen				options: {allow_alias:true}
518*1c12ee1eSDan Willemsen			}]}]
519*1c12ee1eSDan Willemsen		`),
520*1c12ee1eSDan Willemsen		wantErr: `enum "M.E" allows aliases, but none were found`,
521*1c12ee1eSDan Willemsen	}, {
522*1c12ee1eSDan Willemsen		label: "enum number conflicts",
523*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
524*1c12ee1eSDan Willemsen			name:    "test.proto"
525*1c12ee1eSDan Willemsen			message_type: [{name:"M" enum_type:[{
526*1c12ee1eSDan Willemsen				name:  "E"
527*1c12ee1eSDan Willemsen				value: [{name:"foo" number:0}, {name:"bar" number:1}, {name:"baz" number:1}]
528*1c12ee1eSDan Willemsen			}]}]
529*1c12ee1eSDan Willemsen		`),
530*1c12ee1eSDan Willemsen		wantErr: `enum "M.E" has conflicting non-aliased values on number 1: "baz" with "bar"`,
531*1c12ee1eSDan Willemsen	}, {
532*1c12ee1eSDan Willemsen		label: "aliased enum numbers",
533*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
534*1c12ee1eSDan Willemsen			name:    "test.proto"
535*1c12ee1eSDan Willemsen			message_type: [{name:"M" enum_type:[{
536*1c12ee1eSDan Willemsen				name:    "E"
537*1c12ee1eSDan Willemsen				value:   [{name:"foo" number:0}, {name:"bar" number:1}, {name:"baz" number:1}]
538*1c12ee1eSDan Willemsen				options: {allow_alias:true}
539*1c12ee1eSDan Willemsen			}]}]
540*1c12ee1eSDan Willemsen		`),
541*1c12ee1eSDan Willemsen	}, {
542*1c12ee1eSDan Willemsen		label: "invalid proto3 enum",
543*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
544*1c12ee1eSDan Willemsen			syntax:  "proto3"
545*1c12ee1eSDan Willemsen			name:    "test.proto"
546*1c12ee1eSDan Willemsen			message_type: [{name:"M" enum_type:[{
547*1c12ee1eSDan Willemsen				name:  "E"
548*1c12ee1eSDan Willemsen				value: [{name:"baz" number:500}]
549*1c12ee1eSDan Willemsen			}]}]
550*1c12ee1eSDan Willemsen		`),
551*1c12ee1eSDan Willemsen		wantErr: `enum "M.baz" using proto3 semantics must have zero number for the first value`,
552*1c12ee1eSDan Willemsen	}, {
553*1c12ee1eSDan Willemsen		label: "valid proto3 enum",
554*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
555*1c12ee1eSDan Willemsen			syntax:  "proto3"
556*1c12ee1eSDan Willemsen			name:    "test.proto"
557*1c12ee1eSDan Willemsen			message_type: [{name:"M" enum_type:[{
558*1c12ee1eSDan Willemsen				name:  "E"
559*1c12ee1eSDan Willemsen				value: [{name:"baz" number:0}]
560*1c12ee1eSDan Willemsen			}]}]
561*1c12ee1eSDan Willemsen		`),
562*1c12ee1eSDan Willemsen	}, {
563*1c12ee1eSDan Willemsen		label: "proto3 enum name prefix conflict",
564*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
565*1c12ee1eSDan Willemsen			syntax:  "proto3"
566*1c12ee1eSDan Willemsen			name:    "test.proto"
567*1c12ee1eSDan Willemsen			message_type: [{name:"M" enum_type:[{
568*1c12ee1eSDan Willemsen				name:  "E"
569*1c12ee1eSDan Willemsen				value: [{name:"e_Foo" number:0}, {name:"fOo" number:1}]
570*1c12ee1eSDan Willemsen			}]}]
571*1c12ee1eSDan Willemsen		`),
572*1c12ee1eSDan Willemsen		wantErr: `enum "M.E" using proto3 semantics has conflict: "fOo" with "e_Foo"`,
573*1c12ee1eSDan Willemsen	}, {
574*1c12ee1eSDan Willemsen		label: "proto2 enum has name prefix check",
575*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
576*1c12ee1eSDan Willemsen			name:    "test.proto"
577*1c12ee1eSDan Willemsen			message_type: [{name:"M" enum_type:[{
578*1c12ee1eSDan Willemsen				name:  "E"
579*1c12ee1eSDan Willemsen				value: [{name:"e_Foo" number:0}, {name:"fOo" number:1}]
580*1c12ee1eSDan Willemsen			}]}]
581*1c12ee1eSDan Willemsen		`),
582*1c12ee1eSDan Willemsen	}, {
583*1c12ee1eSDan Willemsen		label: "proto3 enum same name prefix with number conflict",
584*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
585*1c12ee1eSDan Willemsen			syntax:  "proto3"
586*1c12ee1eSDan Willemsen			name:    "test.proto"
587*1c12ee1eSDan Willemsen			message_type: [{name:"M" enum_type:[{
588*1c12ee1eSDan Willemsen				name:  "E"
589*1c12ee1eSDan Willemsen				value: [{name:"e_Foo" number:0}, {name:"fOo" number:0}]
590*1c12ee1eSDan Willemsen			}]}]
591*1c12ee1eSDan Willemsen		`),
592*1c12ee1eSDan Willemsen		wantErr: `enum "M.E" has conflicting non-aliased values on number 0: "fOo" with "e_Foo"`,
593*1c12ee1eSDan Willemsen	}, {
594*1c12ee1eSDan Willemsen		label: "proto3 enum same name prefix with alias numbers",
595*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
596*1c12ee1eSDan Willemsen			syntax:  "proto3"
597*1c12ee1eSDan Willemsen			name:    "test.proto"
598*1c12ee1eSDan Willemsen			message_type: [{name:"M" enum_type:[{
599*1c12ee1eSDan Willemsen				name:    "E"
600*1c12ee1eSDan Willemsen				value:   [{name:"e_Foo" number:0}, {name:"fOo" number:0}]
601*1c12ee1eSDan Willemsen				options: {allow_alias: true}
602*1c12ee1eSDan Willemsen			}]}]
603*1c12ee1eSDan Willemsen		`),
604*1c12ee1eSDan Willemsen	}, {
605*1c12ee1eSDan Willemsen		label: "invalid message reserved names",
606*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
607*1c12ee1eSDan Willemsen			name:    "test.proto"
608*1c12ee1eSDan Willemsen			message_type: [{name:"M" nested_type:[{
609*1c12ee1eSDan Willemsen				name:          "M"
610*1c12ee1eSDan Willemsen				reserved_name: ["$"]
611*1c12ee1eSDan Willemsen			}]}]
612*1c12ee1eSDan Willemsen		`),
613*1c12ee1eSDan Willemsen		// NOTE: In theory this should be an error.
614*1c12ee1eSDan Willemsen		// See https://github.com/protocolbuffers/protobuf/issues/6335.
615*1c12ee1eSDan Willemsen		/*wantErr: `message "M.M" reserved names has invalid name: "$"`,*/
616*1c12ee1eSDan Willemsen	}, {
617*1c12ee1eSDan Willemsen		label: "valid message reserved names",
618*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
619*1c12ee1eSDan Willemsen			name:    "test.proto"
620*1c12ee1eSDan Willemsen			message_type: [{name:"M" nested_type:[{
621*1c12ee1eSDan Willemsen				name:          "M"
622*1c12ee1eSDan Willemsen				reserved_name: ["foo", "bar"]
623*1c12ee1eSDan Willemsen				field:         [{name:"foo" number:1 label:LABEL_OPTIONAL type:TYPE_STRING}]
624*1c12ee1eSDan Willemsen			}]}]
625*1c12ee1eSDan Willemsen		`),
626*1c12ee1eSDan Willemsen		wantErr: `message field "M.M.foo" must not use reserved name`,
627*1c12ee1eSDan Willemsen	}, {
628*1c12ee1eSDan Willemsen		label: "valid message reserved names",
629*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
630*1c12ee1eSDan Willemsen			name:    "test.proto"
631*1c12ee1eSDan Willemsen			message_type: [{name:"M" nested_type:[{
632*1c12ee1eSDan Willemsen				name:          "M"
633*1c12ee1eSDan Willemsen				reserved_name: ["foo", "bar"]
634*1c12ee1eSDan Willemsen				field:         [{name:"baz" number:1 label:LABEL_OPTIONAL type:TYPE_STRING oneof_index:0}]
635*1c12ee1eSDan Willemsen				oneof_decl:    [{name:"foo"}] # not affected by reserved_name
636*1c12ee1eSDan Willemsen			}]}]
637*1c12ee1eSDan Willemsen		`),
638*1c12ee1eSDan Willemsen	}, {
639*1c12ee1eSDan Willemsen		label: "invalid reserved number",
640*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
641*1c12ee1eSDan Willemsen			name:    "test.proto"
642*1c12ee1eSDan Willemsen			message_type: [{name:"M" nested_type:[{
643*1c12ee1eSDan Willemsen				name:           "M"
644*1c12ee1eSDan Willemsen				reserved_range: [{start:1 end:1}]
645*1c12ee1eSDan Willemsen				field:          [{name:"baz" number:1 label:LABEL_OPTIONAL type:TYPE_STRING}]
646*1c12ee1eSDan Willemsen			}]}]
647*1c12ee1eSDan Willemsen		`),
648*1c12ee1eSDan Willemsen		wantErr: `message "M.M" reserved ranges has invalid field number: 0`,
649*1c12ee1eSDan Willemsen	}, {
650*1c12ee1eSDan Willemsen		label: "invalid reserved ranges",
651*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
652*1c12ee1eSDan Willemsen			name:    "test.proto"
653*1c12ee1eSDan Willemsen			message_type: [{name:"M" nested_type:[{
654*1c12ee1eSDan Willemsen				name:           "M"
655*1c12ee1eSDan Willemsen				reserved_range: [{start:2 end:2}]
656*1c12ee1eSDan Willemsen				field:          [{name:"baz" number:1 label:LABEL_OPTIONAL type:TYPE_STRING}]
657*1c12ee1eSDan Willemsen			}]}]
658*1c12ee1eSDan Willemsen		`),
659*1c12ee1eSDan Willemsen		wantErr: `message "M.M" reserved ranges has invalid range: 2 to 1`,
660*1c12ee1eSDan Willemsen	}, {
661*1c12ee1eSDan Willemsen		label: "overlapping reserved ranges",
662*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
663*1c12ee1eSDan Willemsen			name:    "test.proto"
664*1c12ee1eSDan Willemsen			message_type: [{name:"M" nested_type:[{
665*1c12ee1eSDan Willemsen				name:           "M"
666*1c12ee1eSDan Willemsen				reserved_range: [{start:1 end:10}, {start:2 end:9}]
667*1c12ee1eSDan Willemsen				field:          [{name:"baz" number:1 label:LABEL_OPTIONAL type:TYPE_STRING}]
668*1c12ee1eSDan Willemsen			}]}]
669*1c12ee1eSDan Willemsen		`),
670*1c12ee1eSDan Willemsen		wantErr: `message "M.M" reserved ranges has overlapping ranges: 1 to 9 with 2 to 8`,
671*1c12ee1eSDan Willemsen	}, {
672*1c12ee1eSDan Willemsen		label: "use of reserved message field number",
673*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
674*1c12ee1eSDan Willemsen			name:    "test.proto"
675*1c12ee1eSDan Willemsen			message_type: [{name:"M" nested_type:[{
676*1c12ee1eSDan Willemsen				name:           "M"
677*1c12ee1eSDan Willemsen				reserved_range: [{start:10 end:20}, {start:20 end:30}, {start:30 end:31}]
678*1c12ee1eSDan Willemsen				field:          [{name:"baz" number:30 label:LABEL_OPTIONAL type:TYPE_STRING}]
679*1c12ee1eSDan Willemsen			}]}]
680*1c12ee1eSDan Willemsen		`),
681*1c12ee1eSDan Willemsen		wantErr: `message field "M.M.baz" must not use reserved number 30`,
682*1c12ee1eSDan Willemsen	}, {
683*1c12ee1eSDan Willemsen		label: "invalid extension ranges",
684*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
685*1c12ee1eSDan Willemsen			name:    "test.proto"
686*1c12ee1eSDan Willemsen			message_type: [{name:"M" nested_type:[{
687*1c12ee1eSDan Willemsen				name:            "M"
688*1c12ee1eSDan Willemsen				extension_range: [{start:-500 end:2}]
689*1c12ee1eSDan Willemsen				field:           [{name:"baz" number:1 label:LABEL_OPTIONAL type:TYPE_STRING}]
690*1c12ee1eSDan Willemsen			}]}]
691*1c12ee1eSDan Willemsen		`),
692*1c12ee1eSDan Willemsen		wantErr: `message "M.M" extension ranges has invalid field number: -500`,
693*1c12ee1eSDan Willemsen	}, {
694*1c12ee1eSDan Willemsen		label: "overlapping reserved and extension ranges",
695*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
696*1c12ee1eSDan Willemsen			name:    "test.proto"
697*1c12ee1eSDan Willemsen			message_type: [{name:"M" nested_type:[{
698*1c12ee1eSDan Willemsen				name:            "M"
699*1c12ee1eSDan Willemsen				reserved_range:  [{start:15 end:20}, {start:1 end:3}, {start:7 end:10}]
700*1c12ee1eSDan Willemsen				extension_range: [{start:8 end:9}, {start:3 end:5}]
701*1c12ee1eSDan Willemsen			}]}]
702*1c12ee1eSDan Willemsen		`),
703*1c12ee1eSDan Willemsen		wantErr: `message "M.M" reserved and extension ranges has overlapping ranges: 7 to 9 with 8`,
704*1c12ee1eSDan Willemsen	}, {
705*1c12ee1eSDan Willemsen		label: "message field conflicting number",
706*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
707*1c12ee1eSDan Willemsen			name:    "test.proto"
708*1c12ee1eSDan Willemsen			message_type: [{name:"M" nested_type:[{
709*1c12ee1eSDan Willemsen				name:            "M"
710*1c12ee1eSDan Willemsen				field: [
711*1c12ee1eSDan Willemsen					{name:"one" number:1 label:LABEL_OPTIONAL type:TYPE_STRING},
712*1c12ee1eSDan Willemsen					{name:"One" number:1 label:LABEL_OPTIONAL type:TYPE_STRING}
713*1c12ee1eSDan Willemsen				]
714*1c12ee1eSDan Willemsen			}]}]
715*1c12ee1eSDan Willemsen		`),
716*1c12ee1eSDan Willemsen		wantErr: `message "M.M" has conflicting fields: "One" with "one"`,
717*1c12ee1eSDan Willemsen	}, {
718*1c12ee1eSDan Willemsen		label: "invalid MessageSet",
719*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
720*1c12ee1eSDan Willemsen			syntax:  "proto3"
721*1c12ee1eSDan Willemsen			name:    "test.proto"
722*1c12ee1eSDan Willemsen			message_type: [{name:"M" nested_type:[{
723*1c12ee1eSDan Willemsen				name:    "M"
724*1c12ee1eSDan Willemsen				options: {message_set_wire_format:true}
725*1c12ee1eSDan Willemsen			}]}]
726*1c12ee1eSDan Willemsen		`),
727*1c12ee1eSDan Willemsen		wantErr: func() string {
728*1c12ee1eSDan Willemsen			if flags.ProtoLegacy {
729*1c12ee1eSDan Willemsen				return `message "M.M" is an invalid proto1 MessageSet`
730*1c12ee1eSDan Willemsen			} else {
731*1c12ee1eSDan Willemsen				return `message "M.M" is a MessageSet, which is a legacy proto1 feature that is no longer supported`
732*1c12ee1eSDan Willemsen			}
733*1c12ee1eSDan Willemsen		}(),
734*1c12ee1eSDan Willemsen	}, {
735*1c12ee1eSDan Willemsen		label: "valid MessageSet",
736*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
737*1c12ee1eSDan Willemsen			name:    "test.proto"
738*1c12ee1eSDan Willemsen			message_type: [{name:"M" nested_type:[{
739*1c12ee1eSDan Willemsen				name:            "M"
740*1c12ee1eSDan Willemsen				extension_range: [{start:1 end:100000}]
741*1c12ee1eSDan Willemsen				options:         {message_set_wire_format:true}
742*1c12ee1eSDan Willemsen			}]}]
743*1c12ee1eSDan Willemsen		`),
744*1c12ee1eSDan Willemsen		wantErr: func() string {
745*1c12ee1eSDan Willemsen			if flags.ProtoLegacy {
746*1c12ee1eSDan Willemsen				return ""
747*1c12ee1eSDan Willemsen			} else {
748*1c12ee1eSDan Willemsen				return `message "M.M" is a MessageSet, which is a legacy proto1 feature that is no longer supported`
749*1c12ee1eSDan Willemsen			}
750*1c12ee1eSDan Willemsen		}(),
751*1c12ee1eSDan Willemsen	}, {
752*1c12ee1eSDan Willemsen		label: "invalid extension ranges in proto3",
753*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
754*1c12ee1eSDan Willemsen			syntax:  "proto3"
755*1c12ee1eSDan Willemsen			name:    "test.proto"
756*1c12ee1eSDan Willemsen			message_type: [{name:"M" nested_type:[{
757*1c12ee1eSDan Willemsen				name:            "M"
758*1c12ee1eSDan Willemsen				extension_range: [{start:1 end:100000}]
759*1c12ee1eSDan Willemsen			}]}]
760*1c12ee1eSDan Willemsen		`),
761*1c12ee1eSDan Willemsen		wantErr: `message "M.M" using proto3 semantics cannot have extension ranges`,
762*1c12ee1eSDan Willemsen	}, {
763*1c12ee1eSDan Willemsen		label: "proto3 message fields conflict",
764*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
765*1c12ee1eSDan Willemsen			syntax:  "proto3"
766*1c12ee1eSDan Willemsen			name:    "test.proto"
767*1c12ee1eSDan Willemsen			message_type: [{name:"M" nested_type:[{
768*1c12ee1eSDan Willemsen				name: "M"
769*1c12ee1eSDan Willemsen				field: [
770*1c12ee1eSDan Willemsen					{name:"_b_a_z_" number:1 label:LABEL_OPTIONAL type:TYPE_STRING},
771*1c12ee1eSDan Willemsen					{name:"baz" number:2 label:LABEL_OPTIONAL type:TYPE_STRING}
772*1c12ee1eSDan Willemsen				]
773*1c12ee1eSDan Willemsen			}]}]
774*1c12ee1eSDan Willemsen		`),
775*1c12ee1eSDan Willemsen		wantErr: `message "M.M" using proto3 semantics has conflict: "baz" with "_b_a_z_"`,
776*1c12ee1eSDan Willemsen	}, {
777*1c12ee1eSDan Willemsen		label: "proto3 message fields",
778*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
779*1c12ee1eSDan Willemsen			syntax:  "proto3"
780*1c12ee1eSDan Willemsen			name:    "test.proto"
781*1c12ee1eSDan Willemsen			message_type: [{name:"M" nested_type:[{
782*1c12ee1eSDan Willemsen				name:       "M"
783*1c12ee1eSDan Willemsen				field:      [{name:"_b_a_z_" number:1 label:LABEL_OPTIONAL type:TYPE_STRING oneof_index:0}]
784*1c12ee1eSDan Willemsen				oneof_decl: [{name:"baz"}] # proto3 name conflict logic does not include oneof
785*1c12ee1eSDan Willemsen			}]}]
786*1c12ee1eSDan Willemsen		`),
787*1c12ee1eSDan Willemsen	}, {
788*1c12ee1eSDan Willemsen		label: "proto2 message fields with no conflict",
789*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
790*1c12ee1eSDan Willemsen			name:    "test.proto"
791*1c12ee1eSDan Willemsen			message_type: [{name:"M" nested_type:[{
792*1c12ee1eSDan Willemsen				name: "M"
793*1c12ee1eSDan Willemsen				field: [
794*1c12ee1eSDan Willemsen					{name:"_b_a_z_" number:1 label:LABEL_OPTIONAL type:TYPE_STRING},
795*1c12ee1eSDan Willemsen					{name:"baz" number:2 label:LABEL_OPTIONAL type:TYPE_STRING}
796*1c12ee1eSDan Willemsen				]
797*1c12ee1eSDan Willemsen			}]}]
798*1c12ee1eSDan Willemsen		`),
799*1c12ee1eSDan Willemsen	}, {
800*1c12ee1eSDan Willemsen		label: "proto3 message with unresolved enum",
801*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
802*1c12ee1eSDan Willemsen			name:    "test.proto"
803*1c12ee1eSDan Willemsen			syntax:  "proto3"
804*1c12ee1eSDan Willemsen			message_type: [{
805*1c12ee1eSDan Willemsen				name: "M"
806*1c12ee1eSDan Willemsen				field: [
807*1c12ee1eSDan Willemsen					{name:"enum" number:1 label:LABEL_OPTIONAL type:TYPE_ENUM type_name:".fizz.buzz.Enum"}
808*1c12ee1eSDan Willemsen				]
809*1c12ee1eSDan Willemsen			}]
810*1c12ee1eSDan Willemsen		`),
811*1c12ee1eSDan Willemsen		inOpts: FileOptions{AllowUnresolvable: true},
812*1c12ee1eSDan Willemsen		// TODO: Test field and oneof handling in validateMessageDeclarations
813*1c12ee1eSDan Willemsen		// TODO: Test unmarshalDefault
814*1c12ee1eSDan Willemsen		// TODO: Test validateExtensionDeclarations
815*1c12ee1eSDan Willemsen		// TODO: Test checkValidGroup
816*1c12ee1eSDan Willemsen		// TODO: Test checkValidMap
817*1c12ee1eSDan Willemsen	}, {
818*1c12ee1eSDan Willemsen		label: "empty service",
819*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
820*1c12ee1eSDan Willemsen			name:    "test.proto"
821*1c12ee1eSDan Willemsen			service: [{name:"service"}]
822*1c12ee1eSDan Willemsen		`),
823*1c12ee1eSDan Willemsen	}, {
824*1c12ee1eSDan Willemsen		label: "service with method with unresolved",
825*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
826*1c12ee1eSDan Willemsen			name:    "test.proto"
827*1c12ee1eSDan Willemsen			service: [{
828*1c12ee1eSDan Willemsen				name: "service"
829*1c12ee1eSDan Willemsen				method: [{
830*1c12ee1eSDan Willemsen					name:"method"
831*1c12ee1eSDan Willemsen					input_type:"foo"
832*1c12ee1eSDan Willemsen					output_type:".foo.bar.baz"
833*1c12ee1eSDan Willemsen				}]
834*1c12ee1eSDan Willemsen			}]
835*1c12ee1eSDan Willemsen		`),
836*1c12ee1eSDan Willemsen		inOpts: FileOptions{AllowUnresolvable: true},
837*1c12ee1eSDan Willemsen	}, {
838*1c12ee1eSDan Willemsen		label: "service with wrong reference type",
839*1c12ee1eSDan Willemsen		inDeps: []*descriptorpb.FileDescriptorProto{
840*1c12ee1eSDan Willemsen			cloneFile(proto3Message),
841*1c12ee1eSDan Willemsen			cloneFile(proto2Enum),
842*1c12ee1eSDan Willemsen		},
843*1c12ee1eSDan Willemsen		inDesc: mustParseFile(`
844*1c12ee1eSDan Willemsen			name:    "test.proto"
845*1c12ee1eSDan Willemsen			dependency: ["proto2_enum.proto", "proto3_message.proto"]
846*1c12ee1eSDan Willemsen			service: [{
847*1c12ee1eSDan Willemsen				name: "service"
848*1c12ee1eSDan Willemsen				method: [{
849*1c12ee1eSDan Willemsen					name:        "method"
850*1c12ee1eSDan Willemsen					input_type:  ".test.proto2.Enum",
851*1c12ee1eSDan Willemsen					output_type: ".test.proto3.Message"
852*1c12ee1eSDan Willemsen				}]
853*1c12ee1eSDan Willemsen			}]
854*1c12ee1eSDan Willemsen		`),
855*1c12ee1eSDan Willemsen		wantErr: `service method "service.method" cannot resolve input: resolved "test.proto2.Enum", but it is not an message`,
856*1c12ee1eSDan Willemsen	}}
857*1c12ee1eSDan Willemsen
858*1c12ee1eSDan Willemsen	for _, tt := range tests {
859*1c12ee1eSDan Willemsen		t.Run(tt.label, func(t *testing.T) {
860*1c12ee1eSDan Willemsen			r := new(protoregistry.Files)
861*1c12ee1eSDan Willemsen			for i, dep := range tt.inDeps {
862*1c12ee1eSDan Willemsen				f, err := tt.inOpts.New(dep, r)
863*1c12ee1eSDan Willemsen				if err != nil {
864*1c12ee1eSDan Willemsen					t.Fatalf("dependency %d: unexpected NewFile() error: %v", i, err)
865*1c12ee1eSDan Willemsen				}
866*1c12ee1eSDan Willemsen				if err := r.RegisterFile(f); err != nil {
867*1c12ee1eSDan Willemsen					t.Fatalf("dependency %d: unexpected Register() error: %v", i, err)
868*1c12ee1eSDan Willemsen				}
869*1c12ee1eSDan Willemsen			}
870*1c12ee1eSDan Willemsen			var gotDesc *descriptorpb.FileDescriptorProto
871*1c12ee1eSDan Willemsen			if tt.wantErr == "" && tt.wantDesc == nil {
872*1c12ee1eSDan Willemsen				tt.wantDesc = cloneFile(tt.inDesc)
873*1c12ee1eSDan Willemsen			}
874*1c12ee1eSDan Willemsen			gotFile, err := tt.inOpts.New(tt.inDesc, r)
875*1c12ee1eSDan Willemsen			if gotFile != nil {
876*1c12ee1eSDan Willemsen				gotDesc = ToFileDescriptorProto(gotFile)
877*1c12ee1eSDan Willemsen			}
878*1c12ee1eSDan Willemsen			if !proto.Equal(gotDesc, tt.wantDesc) {
879*1c12ee1eSDan Willemsen				t.Errorf("NewFile() mismatch:\ngot  %v\nwant %v", gotDesc, tt.wantDesc)
880*1c12ee1eSDan Willemsen			}
881*1c12ee1eSDan Willemsen			if ((err == nil) != (tt.wantErr == "")) || !strings.Contains(fmt.Sprint(err), tt.wantErr) {
882*1c12ee1eSDan Willemsen				t.Errorf("NewFile() error:\ngot:  %v\nwant: %v", err, tt.wantErr)
883*1c12ee1eSDan Willemsen			}
884*1c12ee1eSDan Willemsen		})
885*1c12ee1eSDan Willemsen	}
886*1c12ee1eSDan Willemsen}
887*1c12ee1eSDan Willemsen
888*1c12ee1eSDan Willemsenfunc TestNewFiles(t *testing.T) {
889*1c12ee1eSDan Willemsen	fdset := &descriptorpb.FileDescriptorSet{
890*1c12ee1eSDan Willemsen		File: []*descriptorpb.FileDescriptorProto{
891*1c12ee1eSDan Willemsen			mustParseFile(`
892*1c12ee1eSDan Willemsen				name: "test.proto"
893*1c12ee1eSDan Willemsen				package: "fizz"
894*1c12ee1eSDan Willemsen				dependency: "dep.proto"
895*1c12ee1eSDan Willemsen				message_type: [{
896*1c12ee1eSDan Willemsen					name: "M2"
897*1c12ee1eSDan Willemsen					field: [{name:"F" number:1 label:LABEL_OPTIONAL type:TYPE_MESSAGE type_name:"M1"}]
898*1c12ee1eSDan Willemsen				}]
899*1c12ee1eSDan Willemsen			`),
900*1c12ee1eSDan Willemsen			// Inputs deliberately out of order.
901*1c12ee1eSDan Willemsen			mustParseFile(`
902*1c12ee1eSDan Willemsen				name: "dep.proto"
903*1c12ee1eSDan Willemsen				package: "fizz"
904*1c12ee1eSDan Willemsen				message_type: [{name:"M1"}]
905*1c12ee1eSDan Willemsen			`),
906*1c12ee1eSDan Willemsen		},
907*1c12ee1eSDan Willemsen	}
908*1c12ee1eSDan Willemsen	f, err := NewFiles(fdset)
909*1c12ee1eSDan Willemsen	if err != nil {
910*1c12ee1eSDan Willemsen		t.Fatal(err)
911*1c12ee1eSDan Willemsen	}
912*1c12ee1eSDan Willemsen	m1, err := f.FindDescriptorByName("fizz.M1")
913*1c12ee1eSDan Willemsen	if err != nil {
914*1c12ee1eSDan Willemsen		t.Fatalf(`f.FindDescriptorByName("fizz.M1") = %v`, err)
915*1c12ee1eSDan Willemsen	}
916*1c12ee1eSDan Willemsen	m2, err := f.FindDescriptorByName("fizz.M2")
917*1c12ee1eSDan Willemsen	if err != nil {
918*1c12ee1eSDan Willemsen		t.Fatalf(`f.FindDescriptorByName("fizz.M2") = %v`, err)
919*1c12ee1eSDan Willemsen	}
920*1c12ee1eSDan Willemsen	if m2.(protoreflect.MessageDescriptor).Fields().ByName("F").Message() != m1 {
921*1c12ee1eSDan Willemsen		t.Fatalf(`m1.Fields().ByName("F").Message() != m2`)
922*1c12ee1eSDan Willemsen	}
923*1c12ee1eSDan Willemsen}
924*1c12ee1eSDan Willemsen
925*1c12ee1eSDan Willemsenfunc TestNewFilesImportCycle(t *testing.T) {
926*1c12ee1eSDan Willemsen	fdset := &descriptorpb.FileDescriptorSet{
927*1c12ee1eSDan Willemsen		File: []*descriptorpb.FileDescriptorProto{
928*1c12ee1eSDan Willemsen			mustParseFile(`
929*1c12ee1eSDan Willemsen				name: "test.proto"
930*1c12ee1eSDan Willemsen				package: "fizz"
931*1c12ee1eSDan Willemsen				dependency: "dep.proto"
932*1c12ee1eSDan Willemsen			`),
933*1c12ee1eSDan Willemsen			mustParseFile(`
934*1c12ee1eSDan Willemsen				name: "dep.proto"
935*1c12ee1eSDan Willemsen				package: "fizz"
936*1c12ee1eSDan Willemsen				dependency: "test.proto"
937*1c12ee1eSDan Willemsen			`),
938*1c12ee1eSDan Willemsen		},
939*1c12ee1eSDan Willemsen	}
940*1c12ee1eSDan Willemsen	_, err := NewFiles(fdset)
941*1c12ee1eSDan Willemsen	if err == nil {
942*1c12ee1eSDan Willemsen		t.Fatal("NewFiles with import cycle: success, want error")
943*1c12ee1eSDan Willemsen	}
944*1c12ee1eSDan Willemsen}
945*1c12ee1eSDan Willemsen
946*1c12ee1eSDan Willemsenfunc TestSourceLocations(t *testing.T) {
947*1c12ee1eSDan Willemsen	fd := mustParseFile(`
948*1c12ee1eSDan Willemsen		name: "comments.proto"
949*1c12ee1eSDan Willemsen		message_type: [{
950*1c12ee1eSDan Willemsen			name: "Message1"
951*1c12ee1eSDan Willemsen			field: [
952*1c12ee1eSDan Willemsen				{name:"field1" number:1 label:LABEL_OPTIONAL type:TYPE_STRING},
953*1c12ee1eSDan Willemsen				{name:"field2" number:2 label:LABEL_OPTIONAL type:TYPE_STRING},
954*1c12ee1eSDan Willemsen				{name:"field3" number:3 label:LABEL_OPTIONAL type:TYPE_STRING oneof_index:0},
955*1c12ee1eSDan Willemsen				{name:"field4" number:4 label:LABEL_OPTIONAL type:TYPE_STRING oneof_index:0},
956*1c12ee1eSDan Willemsen				{name:"field5" number:5 label:LABEL_OPTIONAL type:TYPE_STRING oneof_index:1},
957*1c12ee1eSDan Willemsen				{name:"field6" number:6 label:LABEL_OPTIONAL type:TYPE_STRING oneof_index:1}
958*1c12ee1eSDan Willemsen			]
959*1c12ee1eSDan Willemsen			extension: [
960*1c12ee1eSDan Willemsen				{name:"extension1" number:100 label:LABEL_OPTIONAL type:TYPE_STRING extendee:".Message1"},
961*1c12ee1eSDan Willemsen				{name:"extension2" number:101 label:LABEL_OPTIONAL type:TYPE_STRING extendee:".Message1"}
962*1c12ee1eSDan Willemsen			]
963*1c12ee1eSDan Willemsen			nested_type: [{name:"Message1"}, {name:"Message2"}]
964*1c12ee1eSDan Willemsen			extension_range: {start:100 end:536870912}
965*1c12ee1eSDan Willemsen			oneof_decl: [{name:"oneof1"}, {name:"oneof2"}]
966*1c12ee1eSDan Willemsen		}, {
967*1c12ee1eSDan Willemsen			name: "Message2"
968*1c12ee1eSDan Willemsen			enum_type: {
969*1c12ee1eSDan Willemsen				name: "Enum1"
970*1c12ee1eSDan Willemsen				value: [
971*1c12ee1eSDan Willemsen					{name: "FOO", number: 0},
972*1c12ee1eSDan Willemsen					{name: "BAR", number: 1}
973*1c12ee1eSDan Willemsen				]
974*1c12ee1eSDan Willemsen			}
975*1c12ee1eSDan Willemsen		}]
976*1c12ee1eSDan Willemsen		enum_type: {
977*1c12ee1eSDan Willemsen			name: "Enum1"
978*1c12ee1eSDan Willemsen			value: [
979*1c12ee1eSDan Willemsen				{name: "FOO", number: 0},
980*1c12ee1eSDan Willemsen				{name: "BAR", number: 1}
981*1c12ee1eSDan Willemsen			]
982*1c12ee1eSDan Willemsen		}
983*1c12ee1eSDan Willemsen		service: {
984*1c12ee1eSDan Willemsen			name: "Service1"
985*1c12ee1eSDan Willemsen			method: [
986*1c12ee1eSDan Willemsen				{name:"Method1" input_type:".Message1" output_type:".Message1"},
987*1c12ee1eSDan Willemsen				{name:"Method2" input_type:".Message2" output_type:".Message2"}
988*1c12ee1eSDan Willemsen			]
989*1c12ee1eSDan Willemsen		}
990*1c12ee1eSDan Willemsen		extension: [
991*1c12ee1eSDan Willemsen			{name:"extension1" number:102 label:LABEL_OPTIONAL type:TYPE_STRING extendee:".Message1"},
992*1c12ee1eSDan Willemsen			{name:"extension2" number:103 label:LABEL_OPTIONAL type:TYPE_STRING extendee:".Message1"}
993*1c12ee1eSDan Willemsen		]
994*1c12ee1eSDan Willemsen		source_code_info: {
995*1c12ee1eSDan Willemsen			location: [
996*1c12ee1eSDan Willemsen				{span:[0,0,69,1]},
997*1c12ee1eSDan Willemsen				{path:[12] span:[0,0,18]},
998*1c12ee1eSDan Willemsen				{path:[5,0] span:[3,0,8,1] leading_comments:" Enum1\r\n"},
999*1c12ee1eSDan Willemsen				{path:[5,0,1] span:[3,5,10]},
1000*1c12ee1eSDan Willemsen				{path:[5,0,2,0] span:[5,2,10] leading_comments:" FOO\r\n"},
1001*1c12ee1eSDan Willemsen				{path:[5,0,2,0,1] span:[5,2,5]},
1002*1c12ee1eSDan Willemsen				{path:[5,0,2,0,2] span:[5,8,9]},
1003*1c12ee1eSDan Willemsen				{path:[5,0,2,1] span:[7,2,10] leading_comments:" BAR\r\n"},
1004*1c12ee1eSDan Willemsen				{path:[5,0,2,1,1] span:[7,2,5]},
1005*1c12ee1eSDan Willemsen				{path:[5,0,2,1,2] span:[7,8,9]},
1006*1c12ee1eSDan Willemsen				{path:[4,0] span:[11,0,43,1] leading_comments:" Message1\r\n"},
1007*1c12ee1eSDan Willemsen				{path:[4,0,1] span:[11,8,16]},
1008*1c12ee1eSDan Willemsen				{path:[4,0,3,0] span:[13,2,21] leading_comments:" Message1.Message1\r\n"},
1009*1c12ee1eSDan Willemsen				{path:[4,0,3,0,1] span:[13,10,18]},
1010*1c12ee1eSDan Willemsen				{path:[4,0,3,1] span:[15,2,21] leading_comments:" Message1.Message2\r\n"},
1011*1c12ee1eSDan Willemsen				{path:[4,0,3,1,1] span:[15,10,18]},
1012*1c12ee1eSDan Willemsen				{path:[4,0,2,0] span:[18,2,29] leading_comments:" Message1.field1\r\n"},
1013*1c12ee1eSDan Willemsen				{path:[4,0,2,0,4] span:[18,2,10]},
1014*1c12ee1eSDan Willemsen				{path:[4,0,2,0,5] span:[18,11,17]},
1015*1c12ee1eSDan Willemsen				{path:[4,0,2,0,1] span:[18,18,24]},
1016*1c12ee1eSDan Willemsen				{path:[4,0,2,0,3] span:[18,27,28]},
1017*1c12ee1eSDan Willemsen				{path:[4,0,2,1] span:[20,2,29] leading_comments:" Message1.field2\r\n"},
1018*1c12ee1eSDan Willemsen				{path:[4,0,2,1,4] span:[20,2,10]},
1019*1c12ee1eSDan Willemsen				{path:[4,0,2,1,5] span:[20,11,17]},
1020*1c12ee1eSDan Willemsen				{path:[4,0,2,1,1] span:[20,18,24]},
1021*1c12ee1eSDan Willemsen				{path:[4,0,2,1,3] span:[20,27,28]},
1022*1c12ee1eSDan Willemsen				{path:[4,0,8,0] span:[22,2,27,3] leading_comments:" Message1.oneof1\r\n"},
1023*1c12ee1eSDan Willemsen				{path:[4,0,8,0,1] span:[22,8,14]},
1024*1c12ee1eSDan Willemsen				{path:[4,0,2,2] span:[24,4,22] leading_comments:" Message1.field3\r\n"},
1025*1c12ee1eSDan Willemsen				{path:[4,0,2,2,5] span:[24,4,10]},
1026*1c12ee1eSDan Willemsen				{path:[4,0,2,2,1] span:[24,11,17]},
1027*1c12ee1eSDan Willemsen				{path:[4,0,2,2,3] span:[24,20,21]},
1028*1c12ee1eSDan Willemsen				{path:[4,0,2,3] span:[26,4,22] leading_comments:" Message1.field4\r\n"},
1029*1c12ee1eSDan Willemsen				{path:[4,0,2,3,5] span:[26,4,10]},
1030*1c12ee1eSDan Willemsen				{path:[4,0,2,3,1] span:[26,11,17]},
1031*1c12ee1eSDan Willemsen				{path:[4,0,2,3,3] span:[26,20,21]},
1032*1c12ee1eSDan Willemsen				{path:[4,0,8,1] span:[29,2,34,3] leading_comments:" Message1.oneof2\r\n"},
1033*1c12ee1eSDan Willemsen				{path:[4,0,8,1,1] span:[29,8,14]},
1034*1c12ee1eSDan Willemsen				{path:[4,0,2,4] span:[31,4,22] leading_comments:" Message1.field5\r\n"},
1035*1c12ee1eSDan Willemsen				{path:[4,0,2,4,5] span:[31,4,10]},
1036*1c12ee1eSDan Willemsen				{path:[4,0,2,4,1] span:[31,11,17]},
1037*1c12ee1eSDan Willemsen				{path:[4,0,2,4,3] span:[31,20,21]},
1038*1c12ee1eSDan Willemsen				{path:[4,0,2,5] span:[33,4,22] leading_comments:" Message1.field6\r\n"},
1039*1c12ee1eSDan Willemsen				{path:[4,0,2,5,5] span:[33,4,10]},
1040*1c12ee1eSDan Willemsen				{path:[4,0,2,5,1] span:[33,11,17]},
1041*1c12ee1eSDan Willemsen				{path:[4,0,2,5,3] span:[33,20,21]},
1042*1c12ee1eSDan Willemsen				{path:[4,0,5] span:[36,2,24]},
1043*1c12ee1eSDan Willemsen				{path:[4,0,5,0] span:[36,13,23]},
1044*1c12ee1eSDan Willemsen				{path:[4,0,5,0,1] span:[36,13,16]},
1045*1c12ee1eSDan Willemsen				{path:[4,0,5,0,2] span:[36,20,23]},
1046*1c12ee1eSDan Willemsen				{path:[4,0,6] span:[37,2,42,3]},
1047*1c12ee1eSDan Willemsen				{path:[4,0,6,0] span:[39,4,37] leading_comments:" Message1.extension1\r\n"},
1048*1c12ee1eSDan Willemsen				{path:[4,0,6,0,2] span:[37,9,18]},
1049*1c12ee1eSDan Willemsen				{path:[4,0,6,0,4] span:[39,4,12]},
1050*1c12ee1eSDan Willemsen				{path:[4,0,6,0,5] span:[39,13,19]},
1051*1c12ee1eSDan Willemsen				{path:[4,0,6,0,1] span:[39,20,30]},
1052*1c12ee1eSDan Willemsen				{path:[4,0,6,0,3] span:[39,33,36]},
1053*1c12ee1eSDan Willemsen				{path:[4,0,6,1] span:[41,4,37] leading_comments:" Message1.extension2\r\n"},
1054*1c12ee1eSDan Willemsen				{path:[4,0,6,1,2] span:[37,9,18]},
1055*1c12ee1eSDan Willemsen				{path:[4,0,6,1,4] span:[41,4,12]},
1056*1c12ee1eSDan Willemsen				{path:[4,0,6,1,5] span:[41,13,19]},
1057*1c12ee1eSDan Willemsen				{path:[4,0,6,1,1] span:[41,20,30]},
1058*1c12ee1eSDan Willemsen				{path:[4,0,6,1,3] span:[41,33,36]},
1059*1c12ee1eSDan Willemsen				{path:[7] span:[45,0,50,1]},
1060*1c12ee1eSDan Willemsen				{path:[7,0] span:[47,2,35] leading_comments:" extension1\r\n"},
1061*1c12ee1eSDan Willemsen				{path:[7,0,2] span:[45,7,15]},
1062*1c12ee1eSDan Willemsen				{path:[7,0,4] span:[47,2,10]},
1063*1c12ee1eSDan Willemsen				{path:[7,0,5] span:[47,11,17]},
1064*1c12ee1eSDan Willemsen				{path:[7,0,1] span:[47,18,28]},
1065*1c12ee1eSDan Willemsen				{path:[7,0,3] span:[47,31,34]},
1066*1c12ee1eSDan Willemsen				{path:[7,1] span:[49,2,35] leading_comments:" extension2\r\n"},
1067*1c12ee1eSDan Willemsen				{path:[7,1,2] span:[45,7,15]},
1068*1c12ee1eSDan Willemsen				{path:[7,1,4] span:[49,2,10]},
1069*1c12ee1eSDan Willemsen				{path:[7,1,5] span:[49,11,17]},
1070*1c12ee1eSDan Willemsen				{path:[7,1,1] span:[49,18,28]},
1071*1c12ee1eSDan Willemsen				{path:[7,1,3] span:[49,31,34]},
1072*1c12ee1eSDan Willemsen				{path:[4,1] span:[53,0,61,1] leading_comments:" Message2\r\n"},
1073*1c12ee1eSDan Willemsen				{path:[4,1,1] span:[53,8,16]},
1074*1c12ee1eSDan Willemsen				{path:[4,1,4,0] span:[55,2,60,3] leading_comments:" Message2.Enum1\r\n"},
1075*1c12ee1eSDan Willemsen				{path:[4,1,4,0,1] span:[55,7,12]},
1076*1c12ee1eSDan Willemsen				{path:[4,1,4,0,2,0] span:[57,4,12] leading_comments:" Message2.FOO\r\n"},
1077*1c12ee1eSDan Willemsen				{path:[4,1,4,0,2,0,1] span:[57,4,7]},
1078*1c12ee1eSDan Willemsen				{path:[4,1,4,0,2,0,2] span:[57,10,11]},
1079*1c12ee1eSDan Willemsen				{path:[4,1,4,0,2,1] span:[59,4,12] leading_comments:" Message2.BAR\r\n"},
1080*1c12ee1eSDan Willemsen				{path:[4,1,4,0,2,1,1] span:[59,4,7]},
1081*1c12ee1eSDan Willemsen				{path:[4,1,4,0,2,1,2] span:[59,10,11]},
1082*1c12ee1eSDan Willemsen				{path:[6,0] span:[64,0,69,1] leading_comments:" Service1\r\n"},
1083*1c12ee1eSDan Willemsen				{path:[6,0,1] span:[64,8,16]},
1084*1c12ee1eSDan Willemsen				{path:[6,0,2,0] span:[66,2,43] leading_comments:" Service1.Method1\r\n"},
1085*1c12ee1eSDan Willemsen				{path:[6,0,2,0,1] span:[66,6,13]},
1086*1c12ee1eSDan Willemsen				{path:[6,0,2,0,2] span:[66,14,22]},
1087*1c12ee1eSDan Willemsen				{path:[6,0,2,0,3] span:[66,33,41]},
1088*1c12ee1eSDan Willemsen				{path:[6,0,2,1] span:[68,2,43] leading_comments:" Service1.Method2\r\n"},
1089*1c12ee1eSDan Willemsen				{path:[6,0,2,1,1] span:[68,6,13]},
1090*1c12ee1eSDan Willemsen				{path:[6,0,2,1,2] span:[68,14,22]},
1091*1c12ee1eSDan Willemsen				{path:[6,0,2,1,3] span:[68,33,41]}
1092*1c12ee1eSDan Willemsen			]
1093*1c12ee1eSDan Willemsen		}
1094*1c12ee1eSDan Willemsen	`)
1095*1c12ee1eSDan Willemsen	fileDesc, err := NewFile(fd, nil)
1096*1c12ee1eSDan Willemsen	if err != nil {
1097*1c12ee1eSDan Willemsen		t.Fatalf("NewFile error: %v", err)
1098*1c12ee1eSDan Willemsen	}
1099*1c12ee1eSDan Willemsen
1100*1c12ee1eSDan Willemsen	var walkDescs func(protoreflect.Descriptor, func(protoreflect.Descriptor))
1101*1c12ee1eSDan Willemsen	walkDescs = func(d protoreflect.Descriptor, f func(protoreflect.Descriptor)) {
1102*1c12ee1eSDan Willemsen		f(d)
1103*1c12ee1eSDan Willemsen		if d, ok := d.(interface {
1104*1c12ee1eSDan Willemsen			Enums() protoreflect.EnumDescriptors
1105*1c12ee1eSDan Willemsen		}); ok {
1106*1c12ee1eSDan Willemsen			eds := d.Enums()
1107*1c12ee1eSDan Willemsen			for i := 0; i < eds.Len(); i++ {
1108*1c12ee1eSDan Willemsen				walkDescs(eds.Get(i), f)
1109*1c12ee1eSDan Willemsen			}
1110*1c12ee1eSDan Willemsen		}
1111*1c12ee1eSDan Willemsen		if d, ok := d.(interface {
1112*1c12ee1eSDan Willemsen			Values() protoreflect.EnumValueDescriptors
1113*1c12ee1eSDan Willemsen		}); ok {
1114*1c12ee1eSDan Willemsen			vds := d.Values()
1115*1c12ee1eSDan Willemsen			for i := 0; i < vds.Len(); i++ {
1116*1c12ee1eSDan Willemsen				walkDescs(vds.Get(i), f)
1117*1c12ee1eSDan Willemsen			}
1118*1c12ee1eSDan Willemsen		}
1119*1c12ee1eSDan Willemsen		if d, ok := d.(interface {
1120*1c12ee1eSDan Willemsen			Messages() protoreflect.MessageDescriptors
1121*1c12ee1eSDan Willemsen		}); ok {
1122*1c12ee1eSDan Willemsen			mds := d.Messages()
1123*1c12ee1eSDan Willemsen			for i := 0; i < mds.Len(); i++ {
1124*1c12ee1eSDan Willemsen				walkDescs(mds.Get(i), f)
1125*1c12ee1eSDan Willemsen			}
1126*1c12ee1eSDan Willemsen		}
1127*1c12ee1eSDan Willemsen		if d, ok := d.(interface {
1128*1c12ee1eSDan Willemsen			Fields() protoreflect.FieldDescriptors
1129*1c12ee1eSDan Willemsen		}); ok {
1130*1c12ee1eSDan Willemsen			fds := d.Fields()
1131*1c12ee1eSDan Willemsen			for i := 0; i < fds.Len(); i++ {
1132*1c12ee1eSDan Willemsen				walkDescs(fds.Get(i), f)
1133*1c12ee1eSDan Willemsen			}
1134*1c12ee1eSDan Willemsen		}
1135*1c12ee1eSDan Willemsen		if d, ok := d.(interface {
1136*1c12ee1eSDan Willemsen			Oneofs() protoreflect.OneofDescriptors
1137*1c12ee1eSDan Willemsen		}); ok {
1138*1c12ee1eSDan Willemsen			ods := d.Oneofs()
1139*1c12ee1eSDan Willemsen			for i := 0; i < ods.Len(); i++ {
1140*1c12ee1eSDan Willemsen				walkDescs(ods.Get(i), f)
1141*1c12ee1eSDan Willemsen			}
1142*1c12ee1eSDan Willemsen		}
1143*1c12ee1eSDan Willemsen		if d, ok := d.(interface {
1144*1c12ee1eSDan Willemsen			Extensions() protoreflect.ExtensionDescriptors
1145*1c12ee1eSDan Willemsen		}); ok {
1146*1c12ee1eSDan Willemsen			xds := d.Extensions()
1147*1c12ee1eSDan Willemsen			for i := 0; i < xds.Len(); i++ {
1148*1c12ee1eSDan Willemsen				walkDescs(xds.Get(i), f)
1149*1c12ee1eSDan Willemsen			}
1150*1c12ee1eSDan Willemsen		}
1151*1c12ee1eSDan Willemsen		if d, ok := d.(interface {
1152*1c12ee1eSDan Willemsen			Services() protoreflect.ServiceDescriptors
1153*1c12ee1eSDan Willemsen		}); ok {
1154*1c12ee1eSDan Willemsen			sds := d.Services()
1155*1c12ee1eSDan Willemsen			for i := 0; i < sds.Len(); i++ {
1156*1c12ee1eSDan Willemsen				walkDescs(sds.Get(i), f)
1157*1c12ee1eSDan Willemsen			}
1158*1c12ee1eSDan Willemsen		}
1159*1c12ee1eSDan Willemsen		if d, ok := d.(interface {
1160*1c12ee1eSDan Willemsen			Methods() protoreflect.MethodDescriptors
1161*1c12ee1eSDan Willemsen		}); ok {
1162*1c12ee1eSDan Willemsen			mds := d.Methods()
1163*1c12ee1eSDan Willemsen			for i := 0; i < mds.Len(); i++ {
1164*1c12ee1eSDan Willemsen				walkDescs(mds.Get(i), f)
1165*1c12ee1eSDan Willemsen			}
1166*1c12ee1eSDan Willemsen		}
1167*1c12ee1eSDan Willemsen	}
1168*1c12ee1eSDan Willemsen
1169*1c12ee1eSDan Willemsen	var numDescs int
1170*1c12ee1eSDan Willemsen	walkDescs(fileDesc, func(d protoreflect.Descriptor) {
1171*1c12ee1eSDan Willemsen		// The comment for every descriptor should be the full name itself.
1172*1c12ee1eSDan Willemsen		got := strings.TrimSpace(fileDesc.SourceLocations().ByDescriptor(d).LeadingComments)
1173*1c12ee1eSDan Willemsen		want := string(d.FullName())
1174*1c12ee1eSDan Willemsen		if got != want {
1175*1c12ee1eSDan Willemsen			t.Errorf("comment mismatch: got %v, want %v", got, want)
1176*1c12ee1eSDan Willemsen		}
1177*1c12ee1eSDan Willemsen		numDescs++
1178*1c12ee1eSDan Willemsen	})
1179*1c12ee1eSDan Willemsen	if numDescs != 30 {
1180*1c12ee1eSDan Willemsen		t.Errorf("visited %d descriptor, expected 30", numDescs)
1181*1c12ee1eSDan Willemsen	}
1182*1c12ee1eSDan Willemsen}
1183