• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2018 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
5// Package protogen provides support for writing protoc plugins.
6//
7// Plugins for protoc, the Protocol Buffer compiler,
8// are programs which read a CodeGeneratorRequest message from standard input
9// and write a CodeGeneratorResponse message to standard output.
10// This package provides support for writing plugins which generate Go code.
11package protogen
12
13import (
14	"bufio"
15	"bytes"
16	"fmt"
17	"go/ast"
18	"go/parser"
19	"go/printer"
20	"go/token"
21	"go/types"
22	"io/ioutil"
23	"os"
24	"path"
25	"path/filepath"
26	"sort"
27	"strconv"
28	"strings"
29
30	"google.golang.org/protobuf/encoding/prototext"
31	"google.golang.org/protobuf/internal/genid"
32	"google.golang.org/protobuf/internal/strs"
33	"google.golang.org/protobuf/proto"
34	"google.golang.org/protobuf/reflect/protodesc"
35	"google.golang.org/protobuf/reflect/protoreflect"
36	"google.golang.org/protobuf/reflect/protoregistry"
37
38	"google.golang.org/protobuf/types/descriptorpb"
39	"google.golang.org/protobuf/types/dynamicpb"
40	"google.golang.org/protobuf/types/pluginpb"
41)
42
43const goPackageDocURL = "https://protobuf.dev/reference/go/go-generated#package"
44
45// Run executes a function as a protoc plugin.
46//
47// It reads a CodeGeneratorRequest message from os.Stdin, invokes the plugin
48// function, and writes a CodeGeneratorResponse message to os.Stdout.
49//
50// If a failure occurs while reading or writing, Run prints an error to
51// os.Stderr and calls os.Exit(1).
52func (opts Options) Run(f func(*Plugin) error) {
53	if err := run(opts, f); err != nil {
54		fmt.Fprintf(os.Stderr, "%s: %v\n", filepath.Base(os.Args[0]), err)
55		os.Exit(1)
56	}
57}
58
59func run(opts Options, f func(*Plugin) error) error {
60	if len(os.Args) > 1 {
61		return fmt.Errorf("unknown argument %q (this program should be run by protoc, not directly)", os.Args[1])
62	}
63	in, err := ioutil.ReadAll(os.Stdin)
64	if err != nil {
65		return err
66	}
67	req := &pluginpb.CodeGeneratorRequest{}
68	if err := proto.Unmarshal(in, req); err != nil {
69		return err
70	}
71	gen, err := opts.New(req)
72	if err != nil {
73		return err
74	}
75	if err := f(gen); err != nil {
76		// Errors from the plugin function are reported by setting the
77		// error field in the CodeGeneratorResponse.
78		//
79		// In contrast, errors that indicate a problem in protoc
80		// itself (unparsable input, I/O errors, etc.) are reported
81		// to stderr.
82		gen.Error(err)
83	}
84	resp := gen.Response()
85	out, err := proto.Marshal(resp)
86	if err != nil {
87		return err
88	}
89	if _, err := os.Stdout.Write(out); err != nil {
90		return err
91	}
92	return nil
93}
94
95// A Plugin is a protoc plugin invocation.
96type Plugin struct {
97	// Request is the CodeGeneratorRequest provided by protoc.
98	Request *pluginpb.CodeGeneratorRequest
99
100	// Files is the set of files to generate and everything they import.
101	// Files appear in topological order, so each file appears before any
102	// file that imports it.
103	Files       []*File
104	FilesByPath map[string]*File
105
106	// SupportedFeatures is the set of protobuf language features supported by
107	// this generator plugin. See the documentation for
108	// google.protobuf.CodeGeneratorResponse.supported_features for details.
109	SupportedFeatures uint64
110
111	fileReg        *protoregistry.Files
112	enumsByName    map[protoreflect.FullName]*Enum
113	messagesByName map[protoreflect.FullName]*Message
114	annotateCode   bool
115	pathType       pathType
116	module         string
117	genFiles       []*GeneratedFile
118	opts           Options
119	err            error
120}
121
122type Options struct {
123	// If ParamFunc is non-nil, it will be called with each unknown
124	// generator parameter.
125	//
126	// Plugins for protoc can accept parameters from the command line,
127	// passed in the --<lang>_out protoc, separated from the output
128	// directory with a colon; e.g.,
129	//
130	//   --go_out=<param1>=<value1>,<param2>=<value2>:<output_directory>
131	//
132	// Parameters passed in this fashion as a comma-separated list of
133	// key=value pairs will be passed to the ParamFunc.
134	//
135	// The (flag.FlagSet).Set method matches this function signature,
136	// so parameters can be converted into flags as in the following:
137	//
138	//   var flags flag.FlagSet
139	//   value := flags.Bool("param", false, "")
140	//   opts := &protogen.Options{
141	//     ParamFunc: flags.Set,
142	//   }
143	//   protogen.Run(opts, func(p *protogen.Plugin) error {
144	//     if *value { ... }
145	//   })
146	ParamFunc func(name, value string) error
147
148	// ImportRewriteFunc is called with the import path of each package
149	// imported by a generated file. It returns the import path to use
150	// for this package.
151	ImportRewriteFunc func(GoImportPath) GoImportPath
152}
153
154// New returns a new Plugin.
155func (opts Options) New(req *pluginpb.CodeGeneratorRequest) (*Plugin, error) {
156	gen := &Plugin{
157		Request:        req,
158		FilesByPath:    make(map[string]*File),
159		fileReg:        new(protoregistry.Files),
160		enumsByName:    make(map[protoreflect.FullName]*Enum),
161		messagesByName: make(map[protoreflect.FullName]*Message),
162		opts:           opts,
163	}
164
165	packageNames := make(map[string]GoPackageName) // filename -> package name
166	importPaths := make(map[string]GoImportPath)   // filename -> import path
167	for _, param := range strings.Split(req.GetParameter(), ",") {
168		var value string
169		if i := strings.Index(param, "="); i >= 0 {
170			value = param[i+1:]
171			param = param[0:i]
172		}
173		switch param {
174		case "":
175			// Ignore.
176		case "module":
177			gen.module = value
178		case "paths":
179			switch value {
180			case "import":
181				gen.pathType = pathTypeImport
182			case "source_relative":
183				gen.pathType = pathTypeSourceRelative
184			default:
185				return nil, fmt.Errorf(`unknown path type %q: want "import" or "source_relative"`, value)
186			}
187		case "annotate_code":
188			switch value {
189			case "true", "":
190				gen.annotateCode = true
191			case "false":
192			default:
193				return nil, fmt.Errorf(`bad value for parameter %q: want "true" or "false"`, param)
194			}
195		default:
196			if param[0] == 'M' {
197				impPath, pkgName := splitImportPathAndPackageName(value)
198				if pkgName != "" {
199					packageNames[param[1:]] = pkgName
200				}
201				if impPath != "" {
202					importPaths[param[1:]] = impPath
203				}
204				continue
205			}
206			if opts.ParamFunc != nil {
207				if err := opts.ParamFunc(param, value); err != nil {
208					return nil, err
209				}
210			}
211		}
212	}
213
214	// When the module= option is provided, we strip the module name
215	// prefix from generated files. This only makes sense if generated
216	// filenames are based on the import path.
217	if gen.module != "" && gen.pathType == pathTypeSourceRelative {
218		return nil, fmt.Errorf("cannot use module= with paths=source_relative")
219	}
220
221	// Figure out the import path and package name for each file.
222	//
223	// The rules here are complicated and have grown organically over time.
224	// Interactions between different ways of specifying package information
225	// may be surprising.
226	//
227	// The recommended approach is to include a go_package option in every
228	// .proto source file specifying the full import path of the Go package
229	// associated with this file.
230	//
231	//     option go_package = "google.golang.org/protobuf/types/known/anypb";
232	//
233	// Alternatively, build systems which want to exert full control over
234	// import paths may specify M<filename>=<import_path> flags.
235	for _, fdesc := range gen.Request.ProtoFile {
236		// The "M" command-line flags take precedence over
237		// the "go_package" option in the .proto source file.
238		filename := fdesc.GetName()
239		impPath, pkgName := splitImportPathAndPackageName(fdesc.GetOptions().GetGoPackage())
240		if importPaths[filename] == "" && impPath != "" {
241			importPaths[filename] = impPath
242		}
243		if packageNames[filename] == "" && pkgName != "" {
244			packageNames[filename] = pkgName
245		}
246		switch {
247		case importPaths[filename] == "":
248			// The import path must be specified one way or another.
249			return nil, fmt.Errorf(
250				"unable to determine Go import path for %q\n\n"+
251					"Please specify either:\n"+
252					"\t• a \"go_package\" option in the .proto source file, or\n"+
253					"\t• a \"M\" argument on the command line.\n\n"+
254					"See %v for more information.\n",
255				fdesc.GetName(), goPackageDocURL)
256		case !strings.Contains(string(importPaths[filename]), ".") &&
257			!strings.Contains(string(importPaths[filename]), "/"):
258			// Check that import paths contain at least a dot or slash to avoid
259			// a common mistake where import path is confused with package name.
260			return nil, fmt.Errorf(
261				"invalid Go import path %q for %q\n\n"+
262					"The import path must contain at least one period ('.') or forward slash ('/') character.\n\n"+
263					"See %v for more information.\n",
264				string(importPaths[filename]), fdesc.GetName(), goPackageDocURL)
265		case packageNames[filename] == "":
266			// If the package name is not explicitly specified,
267			// then derive a reasonable package name from the import path.
268			//
269			// NOTE: The package name is derived first from the import path in
270			// the "go_package" option (if present) before trying the "M" flag.
271			// The inverted order for this is because the primary use of the "M"
272			// flag is by build systems that have full control over the
273			// import paths all packages, where it is generally expected that
274			// the Go package name still be identical for the Go toolchain and
275			// for custom build systems like Bazel.
276			if impPath == "" {
277				impPath = importPaths[filename]
278			}
279			packageNames[filename] = cleanPackageName(path.Base(string(impPath)))
280		}
281	}
282
283	// Consistency check: Every file with the same Go import path should have
284	// the same Go package name.
285	packageFiles := make(map[GoImportPath][]string)
286	for filename, importPath := range importPaths {
287		if _, ok := packageNames[filename]; !ok {
288			// Skip files mentioned in a M<file>=<import_path> parameter
289			// but which do not appear in the CodeGeneratorRequest.
290			continue
291		}
292		packageFiles[importPath] = append(packageFiles[importPath], filename)
293	}
294	for importPath, filenames := range packageFiles {
295		for i := 1; i < len(filenames); i++ {
296			if a, b := packageNames[filenames[0]], packageNames[filenames[i]]; a != b {
297				return nil, fmt.Errorf("Go package %v has inconsistent names %v (%v) and %v (%v)",
298					importPath, a, filenames[0], b, filenames[i])
299			}
300		}
301	}
302
303	// The extracted types from the full import set
304	typeRegistry := newExtensionRegistry()
305	for _, fdesc := range gen.Request.ProtoFile {
306		filename := fdesc.GetName()
307		if gen.FilesByPath[filename] != nil {
308			return nil, fmt.Errorf("duplicate file name: %q", filename)
309		}
310		f, err := newFile(gen, fdesc, packageNames[filename], importPaths[filename])
311		if err != nil {
312			return nil, err
313		}
314		gen.Files = append(gen.Files, f)
315		gen.FilesByPath[filename] = f
316		if err = typeRegistry.registerAllExtensionsFromFile(f.Desc); err != nil {
317			return nil, err
318		}
319	}
320	for _, filename := range gen.Request.FileToGenerate {
321		f, ok := gen.FilesByPath[filename]
322		if !ok {
323			return nil, fmt.Errorf("no descriptor for generated file: %v", filename)
324		}
325		f.Generate = true
326	}
327
328	// Create fully-linked descriptors if new extensions were found
329	if typeRegistry.hasNovelExtensions() {
330		for _, f := range gen.Files {
331			b, err := proto.Marshal(f.Proto.ProtoReflect().Interface())
332			if err != nil {
333				return nil, err
334			}
335			err = proto.UnmarshalOptions{Resolver: typeRegistry}.Unmarshal(b, f.Proto)
336			if err != nil {
337				return nil, err
338			}
339		}
340	}
341	return gen, nil
342}
343
344// Error records an error in code generation. The generator will report the
345// error back to protoc and will not produce output.
346func (gen *Plugin) Error(err error) {
347	if gen.err == nil {
348		gen.err = err
349	}
350}
351
352// Response returns the generator output.
353func (gen *Plugin) Response() *pluginpb.CodeGeneratorResponse {
354	resp := &pluginpb.CodeGeneratorResponse{}
355	if gen.err != nil {
356		resp.Error = proto.String(gen.err.Error())
357		return resp
358	}
359	for _, g := range gen.genFiles {
360		if g.skip {
361			continue
362		}
363		content, err := g.Content()
364		if err != nil {
365			return &pluginpb.CodeGeneratorResponse{
366				Error: proto.String(err.Error()),
367			}
368		}
369		filename := g.filename
370		if gen.module != "" {
371			trim := gen.module + "/"
372			if !strings.HasPrefix(filename, trim) {
373				return &pluginpb.CodeGeneratorResponse{
374					Error: proto.String(fmt.Sprintf("%v: generated file does not match prefix %q", filename, gen.module)),
375				}
376			}
377			filename = strings.TrimPrefix(filename, trim)
378		}
379		resp.File = append(resp.File, &pluginpb.CodeGeneratorResponse_File{
380			Name:    proto.String(filename),
381			Content: proto.String(string(content)),
382		})
383		if gen.annotateCode && strings.HasSuffix(g.filename, ".go") {
384			meta, err := g.metaFile(content)
385			if err != nil {
386				return &pluginpb.CodeGeneratorResponse{
387					Error: proto.String(err.Error()),
388				}
389			}
390			resp.File = append(resp.File, &pluginpb.CodeGeneratorResponse_File{
391				Name:    proto.String(filename + ".meta"),
392				Content: proto.String(meta),
393			})
394		}
395	}
396	if gen.SupportedFeatures > 0 {
397		resp.SupportedFeatures = proto.Uint64(gen.SupportedFeatures)
398	}
399	return resp
400}
401
402// A File describes a .proto source file.
403type File struct {
404	Desc  protoreflect.FileDescriptor
405	Proto *descriptorpb.FileDescriptorProto
406
407	GoDescriptorIdent GoIdent       // name of Go variable for the file descriptor
408	GoPackageName     GoPackageName // name of this file's Go package
409	GoImportPath      GoImportPath  // import path of this file's Go package
410
411	Enums      []*Enum      // top-level enum declarations
412	Messages   []*Message   // top-level message declarations
413	Extensions []*Extension // top-level extension declarations
414	Services   []*Service   // top-level service declarations
415
416	Generate bool // true if we should generate code for this file
417
418	// GeneratedFilenamePrefix is used to construct filenames for generated
419	// files associated with this source file.
420	//
421	// For example, the source file "dir/foo.proto" might have a filename prefix
422	// of "dir/foo". Appending ".pb.go" produces an output file of "dir/foo.pb.go".
423	GeneratedFilenamePrefix string
424
425	location Location
426}
427
428func newFile(gen *Plugin, p *descriptorpb.FileDescriptorProto, packageName GoPackageName, importPath GoImportPath) (*File, error) {
429	desc, err := protodesc.NewFile(p, gen.fileReg)
430	if err != nil {
431		return nil, fmt.Errorf("invalid FileDescriptorProto %q: %v", p.GetName(), err)
432	}
433	if err := gen.fileReg.RegisterFile(desc); err != nil {
434		return nil, fmt.Errorf("cannot register descriptor %q: %v", p.GetName(), err)
435	}
436	f := &File{
437		Desc:          desc,
438		Proto:         p,
439		GoPackageName: packageName,
440		GoImportPath:  importPath,
441		location:      Location{SourceFile: desc.Path()},
442	}
443
444	// Determine the prefix for generated Go files.
445	prefix := p.GetName()
446	if ext := path.Ext(prefix); ext == ".proto" || ext == ".protodevel" {
447		prefix = prefix[:len(prefix)-len(ext)]
448	}
449	switch gen.pathType {
450	case pathTypeImport:
451		// If paths=import, the output filename is derived from the Go import path.
452		prefix = path.Join(string(f.GoImportPath), path.Base(prefix))
453	case pathTypeSourceRelative:
454		// If paths=source_relative, the output filename is derived from
455		// the input filename.
456	}
457	f.GoDescriptorIdent = GoIdent{
458		GoName:       "File_" + strs.GoSanitized(p.GetName()),
459		GoImportPath: f.GoImportPath,
460	}
461	f.GeneratedFilenamePrefix = prefix
462
463	for i, eds := 0, desc.Enums(); i < eds.Len(); i++ {
464		f.Enums = append(f.Enums, newEnum(gen, f, nil, eds.Get(i)))
465	}
466	for i, mds := 0, desc.Messages(); i < mds.Len(); i++ {
467		f.Messages = append(f.Messages, newMessage(gen, f, nil, mds.Get(i)))
468	}
469	for i, xds := 0, desc.Extensions(); i < xds.Len(); i++ {
470		f.Extensions = append(f.Extensions, newField(gen, f, nil, xds.Get(i)))
471	}
472	for i, sds := 0, desc.Services(); i < sds.Len(); i++ {
473		f.Services = append(f.Services, newService(gen, f, sds.Get(i)))
474	}
475	for _, message := range f.Messages {
476		if err := message.resolveDependencies(gen); err != nil {
477			return nil, err
478		}
479	}
480	for _, extension := range f.Extensions {
481		if err := extension.resolveDependencies(gen); err != nil {
482			return nil, err
483		}
484	}
485	for _, service := range f.Services {
486		for _, method := range service.Methods {
487			if err := method.resolveDependencies(gen); err != nil {
488				return nil, err
489			}
490		}
491	}
492	return f, nil
493}
494
495// splitImportPathAndPackageName splits off the optional Go package name
496// from the Go import path when separated by a ';' delimiter.
497func splitImportPathAndPackageName(s string) (GoImportPath, GoPackageName) {
498	if i := strings.Index(s, ";"); i >= 0 {
499		return GoImportPath(s[:i]), GoPackageName(s[i+1:])
500	}
501	return GoImportPath(s), ""
502}
503
504// An Enum describes an enum.
505type Enum struct {
506	Desc protoreflect.EnumDescriptor
507
508	GoIdent GoIdent // name of the generated Go type
509
510	Values []*EnumValue // enum value declarations
511
512	Location Location   // location of this enum
513	Comments CommentSet // comments associated with this enum
514}
515
516func newEnum(gen *Plugin, f *File, parent *Message, desc protoreflect.EnumDescriptor) *Enum {
517	var loc Location
518	if parent != nil {
519		loc = parent.Location.appendPath(genid.DescriptorProto_EnumType_field_number, desc.Index())
520	} else {
521		loc = f.location.appendPath(genid.FileDescriptorProto_EnumType_field_number, desc.Index())
522	}
523	enum := &Enum{
524		Desc:     desc,
525		GoIdent:  newGoIdent(f, desc),
526		Location: loc,
527		Comments: makeCommentSet(f.Desc.SourceLocations().ByDescriptor(desc)),
528	}
529	gen.enumsByName[desc.FullName()] = enum
530	for i, vds := 0, enum.Desc.Values(); i < vds.Len(); i++ {
531		enum.Values = append(enum.Values, newEnumValue(gen, f, parent, enum, vds.Get(i)))
532	}
533	return enum
534}
535
536// An EnumValue describes an enum value.
537type EnumValue struct {
538	Desc protoreflect.EnumValueDescriptor
539
540	GoIdent GoIdent // name of the generated Go declaration
541
542	Parent *Enum // enum in which this value is declared
543
544	Location Location   // location of this enum value
545	Comments CommentSet // comments associated with this enum value
546}
547
548func newEnumValue(gen *Plugin, f *File, message *Message, enum *Enum, desc protoreflect.EnumValueDescriptor) *EnumValue {
549	// A top-level enum value's name is: EnumName_ValueName
550	// An enum value contained in a message is: MessageName_ValueName
551	//
552	// For historical reasons, enum value names are not camel-cased.
553	parentIdent := enum.GoIdent
554	if message != nil {
555		parentIdent = message.GoIdent
556	}
557	name := parentIdent.GoName + "_" + string(desc.Name())
558	loc := enum.Location.appendPath(genid.EnumDescriptorProto_Value_field_number, desc.Index())
559	return &EnumValue{
560		Desc:     desc,
561		GoIdent:  f.GoImportPath.Ident(name),
562		Parent:   enum,
563		Location: loc,
564		Comments: makeCommentSet(f.Desc.SourceLocations().ByDescriptor(desc)),
565	}
566}
567
568// A Message describes a message.
569type Message struct {
570	Desc protoreflect.MessageDescriptor
571
572	GoIdent GoIdent // name of the generated Go type
573
574	Fields []*Field // message field declarations
575	Oneofs []*Oneof // message oneof declarations
576
577	Enums      []*Enum      // nested enum declarations
578	Messages   []*Message   // nested message declarations
579	Extensions []*Extension // nested extension declarations
580
581	Location Location   // location of this message
582	Comments CommentSet // comments associated with this message
583}
584
585func newMessage(gen *Plugin, f *File, parent *Message, desc protoreflect.MessageDescriptor) *Message {
586	var loc Location
587	if parent != nil {
588		loc = parent.Location.appendPath(genid.DescriptorProto_NestedType_field_number, desc.Index())
589	} else {
590		loc = f.location.appendPath(genid.FileDescriptorProto_MessageType_field_number, desc.Index())
591	}
592	message := &Message{
593		Desc:     desc,
594		GoIdent:  newGoIdent(f, desc),
595		Location: loc,
596		Comments: makeCommentSet(f.Desc.SourceLocations().ByDescriptor(desc)),
597	}
598	gen.messagesByName[desc.FullName()] = message
599	for i, eds := 0, desc.Enums(); i < eds.Len(); i++ {
600		message.Enums = append(message.Enums, newEnum(gen, f, message, eds.Get(i)))
601	}
602	for i, mds := 0, desc.Messages(); i < mds.Len(); i++ {
603		message.Messages = append(message.Messages, newMessage(gen, f, message, mds.Get(i)))
604	}
605	for i, fds := 0, desc.Fields(); i < fds.Len(); i++ {
606		message.Fields = append(message.Fields, newField(gen, f, message, fds.Get(i)))
607	}
608	for i, ods := 0, desc.Oneofs(); i < ods.Len(); i++ {
609		message.Oneofs = append(message.Oneofs, newOneof(gen, f, message, ods.Get(i)))
610	}
611	for i, xds := 0, desc.Extensions(); i < xds.Len(); i++ {
612		message.Extensions = append(message.Extensions, newField(gen, f, message, xds.Get(i)))
613	}
614
615	// Resolve local references between fields and oneofs.
616	for _, field := range message.Fields {
617		if od := field.Desc.ContainingOneof(); od != nil {
618			oneof := message.Oneofs[od.Index()]
619			field.Oneof = oneof
620			oneof.Fields = append(oneof.Fields, field)
621		}
622	}
623
624	// Field name conflict resolution.
625	//
626	// We assume well-known method names that may be attached to a generated
627	// message type, as well as a 'Get*' method for each field. For each
628	// field in turn, we add _s to its name until there are no conflicts.
629	//
630	// Any change to the following set of method names is a potential
631	// incompatible API change because it may change generated field names.
632	//
633	// TODO: If we ever support a 'go_name' option to set the Go name of a
634	// field, we should consider dropping this entirely. The conflict
635	// resolution algorithm is subtle and surprising (changing the order
636	// in which fields appear in the .proto source file can change the
637	// names of fields in generated code), and does not adapt well to
638	// adding new per-field methods such as setters.
639	usedNames := map[string]bool{
640		"Reset":               true,
641		"String":              true,
642		"ProtoMessage":        true,
643		"Marshal":             true,
644		"Unmarshal":           true,
645		"ExtensionRangeArray": true,
646		"ExtensionMap":        true,
647		"Descriptor":          true,
648	}
649	makeNameUnique := func(name string, hasGetter bool) string {
650		for usedNames[name] || (hasGetter && usedNames["Get"+name]) {
651			name += "_"
652		}
653		usedNames[name] = true
654		usedNames["Get"+name] = hasGetter
655		return name
656	}
657	for _, field := range message.Fields {
658		field.GoName = makeNameUnique(field.GoName, true)
659		field.GoIdent.GoName = message.GoIdent.GoName + "_" + field.GoName
660		if field.Oneof != nil && field.Oneof.Fields[0] == field {
661			// Make the name for a oneof unique as well. For historical reasons,
662			// this assumes that a getter method is not generated for oneofs.
663			// This is incorrect, but fixing it breaks existing code.
664			field.Oneof.GoName = makeNameUnique(field.Oneof.GoName, false)
665			field.Oneof.GoIdent.GoName = message.GoIdent.GoName + "_" + field.Oneof.GoName
666		}
667	}
668
669	// Oneof field name conflict resolution.
670	//
671	// This conflict resolution is incomplete as it does not consider collisions
672	// with other oneof field types, but fixing it breaks existing code.
673	for _, field := range message.Fields {
674		if field.Oneof != nil {
675		Loop:
676			for {
677				for _, nestedMessage := range message.Messages {
678					if nestedMessage.GoIdent == field.GoIdent {
679						field.GoIdent.GoName += "_"
680						continue Loop
681					}
682				}
683				for _, nestedEnum := range message.Enums {
684					if nestedEnum.GoIdent == field.GoIdent {
685						field.GoIdent.GoName += "_"
686						continue Loop
687					}
688				}
689				break Loop
690			}
691		}
692	}
693
694	return message
695}
696
697func (message *Message) resolveDependencies(gen *Plugin) error {
698	for _, field := range message.Fields {
699		if err := field.resolveDependencies(gen); err != nil {
700			return err
701		}
702	}
703	for _, message := range message.Messages {
704		if err := message.resolveDependencies(gen); err != nil {
705			return err
706		}
707	}
708	for _, extension := range message.Extensions {
709		if err := extension.resolveDependencies(gen); err != nil {
710			return err
711		}
712	}
713	return nil
714}
715
716// A Field describes a message field.
717type Field struct {
718	Desc protoreflect.FieldDescriptor
719
720	// GoName is the base name of this field's Go field and methods.
721	// For code generated by protoc-gen-go, this means a field named
722	// '{{GoName}}' and a getter method named 'Get{{GoName}}'.
723	GoName string // e.g., "FieldName"
724
725	// GoIdent is the base name of a top-level declaration for this field.
726	// For code generated by protoc-gen-go, this means a wrapper type named
727	// '{{GoIdent}}' for members fields of a oneof, and a variable named
728	// 'E_{{GoIdent}}' for extension fields.
729	GoIdent GoIdent // e.g., "MessageName_FieldName"
730
731	Parent   *Message // message in which this field is declared; nil if top-level extension
732	Oneof    *Oneof   // containing oneof; nil if not part of a oneof
733	Extendee *Message // extended message for extension fields; nil otherwise
734
735	Enum    *Enum    // type for enum fields; nil otherwise
736	Message *Message // type for message or group fields; nil otherwise
737
738	Location Location   // location of this field
739	Comments CommentSet // comments associated with this field
740}
741
742func newField(gen *Plugin, f *File, message *Message, desc protoreflect.FieldDescriptor) *Field {
743	var loc Location
744	switch {
745	case desc.IsExtension() && message == nil:
746		loc = f.location.appendPath(genid.FileDescriptorProto_Extension_field_number, desc.Index())
747	case desc.IsExtension() && message != nil:
748		loc = message.Location.appendPath(genid.DescriptorProto_Extension_field_number, desc.Index())
749	default:
750		loc = message.Location.appendPath(genid.DescriptorProto_Field_field_number, desc.Index())
751	}
752	camelCased := strs.GoCamelCase(string(desc.Name()))
753	var parentPrefix string
754	if message != nil {
755		parentPrefix = message.GoIdent.GoName + "_"
756	}
757	field := &Field{
758		Desc:   desc,
759		GoName: camelCased,
760		GoIdent: GoIdent{
761			GoImportPath: f.GoImportPath,
762			GoName:       parentPrefix + camelCased,
763		},
764		Parent:   message,
765		Location: loc,
766		Comments: makeCommentSet(f.Desc.SourceLocations().ByDescriptor(desc)),
767	}
768	return field
769}
770
771func (field *Field) resolveDependencies(gen *Plugin) error {
772	desc := field.Desc
773	switch desc.Kind() {
774	case protoreflect.EnumKind:
775		name := field.Desc.Enum().FullName()
776		enum, ok := gen.enumsByName[name]
777		if !ok {
778			return fmt.Errorf("field %v: no descriptor for enum %v", desc.FullName(), name)
779		}
780		field.Enum = enum
781	case protoreflect.MessageKind, protoreflect.GroupKind:
782		name := desc.Message().FullName()
783		message, ok := gen.messagesByName[name]
784		if !ok {
785			return fmt.Errorf("field %v: no descriptor for type %v", desc.FullName(), name)
786		}
787		field.Message = message
788	}
789	if desc.IsExtension() {
790		name := desc.ContainingMessage().FullName()
791		message, ok := gen.messagesByName[name]
792		if !ok {
793			return fmt.Errorf("field %v: no descriptor for type %v", desc.FullName(), name)
794		}
795		field.Extendee = message
796	}
797	return nil
798}
799
800// A Oneof describes a message oneof.
801type Oneof struct {
802	Desc protoreflect.OneofDescriptor
803
804	// GoName is the base name of this oneof's Go field and methods.
805	// For code generated by protoc-gen-go, this means a field named
806	// '{{GoName}}' and a getter method named 'Get{{GoName}}'.
807	GoName string // e.g., "OneofName"
808
809	// GoIdent is the base name of a top-level declaration for this oneof.
810	GoIdent GoIdent // e.g., "MessageName_OneofName"
811
812	Parent *Message // message in which this oneof is declared
813
814	Fields []*Field // fields that are part of this oneof
815
816	Location Location   // location of this oneof
817	Comments CommentSet // comments associated with this oneof
818}
819
820func newOneof(gen *Plugin, f *File, message *Message, desc protoreflect.OneofDescriptor) *Oneof {
821	loc := message.Location.appendPath(genid.DescriptorProto_OneofDecl_field_number, desc.Index())
822	camelCased := strs.GoCamelCase(string(desc.Name()))
823	parentPrefix := message.GoIdent.GoName + "_"
824	return &Oneof{
825		Desc:   desc,
826		Parent: message,
827		GoName: camelCased,
828		GoIdent: GoIdent{
829			GoImportPath: f.GoImportPath,
830			GoName:       parentPrefix + camelCased,
831		},
832		Location: loc,
833		Comments: makeCommentSet(f.Desc.SourceLocations().ByDescriptor(desc)),
834	}
835}
836
837// Extension is an alias of Field for documentation.
838type Extension = Field
839
840// A Service describes a service.
841type Service struct {
842	Desc protoreflect.ServiceDescriptor
843
844	GoName string
845
846	Methods []*Method // service method declarations
847
848	Location Location   // location of this service
849	Comments CommentSet // comments associated with this service
850}
851
852func newService(gen *Plugin, f *File, desc protoreflect.ServiceDescriptor) *Service {
853	loc := f.location.appendPath(genid.FileDescriptorProto_Service_field_number, desc.Index())
854	service := &Service{
855		Desc:     desc,
856		GoName:   strs.GoCamelCase(string(desc.Name())),
857		Location: loc,
858		Comments: makeCommentSet(f.Desc.SourceLocations().ByDescriptor(desc)),
859	}
860	for i, mds := 0, desc.Methods(); i < mds.Len(); i++ {
861		service.Methods = append(service.Methods, newMethod(gen, f, service, mds.Get(i)))
862	}
863	return service
864}
865
866// A Method describes a method in a service.
867type Method struct {
868	Desc protoreflect.MethodDescriptor
869
870	GoName string
871
872	Parent *Service // service in which this method is declared
873
874	Input  *Message
875	Output *Message
876
877	Location Location   // location of this method
878	Comments CommentSet // comments associated with this method
879}
880
881func newMethod(gen *Plugin, f *File, service *Service, desc protoreflect.MethodDescriptor) *Method {
882	loc := service.Location.appendPath(genid.ServiceDescriptorProto_Method_field_number, desc.Index())
883	method := &Method{
884		Desc:     desc,
885		GoName:   strs.GoCamelCase(string(desc.Name())),
886		Parent:   service,
887		Location: loc,
888		Comments: makeCommentSet(f.Desc.SourceLocations().ByDescriptor(desc)),
889	}
890	return method
891}
892
893func (method *Method) resolveDependencies(gen *Plugin) error {
894	desc := method.Desc
895
896	inName := desc.Input().FullName()
897	in, ok := gen.messagesByName[inName]
898	if !ok {
899		return fmt.Errorf("method %v: no descriptor for type %v", desc.FullName(), inName)
900	}
901	method.Input = in
902
903	outName := desc.Output().FullName()
904	out, ok := gen.messagesByName[outName]
905	if !ok {
906		return fmt.Errorf("method %v: no descriptor for type %v", desc.FullName(), outName)
907	}
908	method.Output = out
909
910	return nil
911}
912
913// A GeneratedFile is a generated file.
914type GeneratedFile struct {
915	gen              *Plugin
916	skip             bool
917	filename         string
918	goImportPath     GoImportPath
919	buf              bytes.Buffer
920	packageNames     map[GoImportPath]GoPackageName
921	usedPackageNames map[GoPackageName]bool
922	manualImports    map[GoImportPath]bool
923	annotations      map[string][]Location
924}
925
926// NewGeneratedFile creates a new generated file with the given filename
927// and import path.
928func (gen *Plugin) NewGeneratedFile(filename string, goImportPath GoImportPath) *GeneratedFile {
929	g := &GeneratedFile{
930		gen:              gen,
931		filename:         filename,
932		goImportPath:     goImportPath,
933		packageNames:     make(map[GoImportPath]GoPackageName),
934		usedPackageNames: make(map[GoPackageName]bool),
935		manualImports:    make(map[GoImportPath]bool),
936		annotations:      make(map[string][]Location),
937	}
938
939	// All predeclared identifiers in Go are already used.
940	for _, s := range types.Universe.Names() {
941		g.usedPackageNames[GoPackageName(s)] = true
942	}
943
944	gen.genFiles = append(gen.genFiles, g)
945	return g
946}
947
948// P prints a line to the generated output. It converts each parameter to a
949// string following the same rules as fmt.Print. It never inserts spaces
950// between parameters.
951func (g *GeneratedFile) P(v ...interface{}) {
952	for _, x := range v {
953		switch x := x.(type) {
954		case GoIdent:
955			fmt.Fprint(&g.buf, g.QualifiedGoIdent(x))
956		default:
957			fmt.Fprint(&g.buf, x)
958		}
959	}
960	fmt.Fprintln(&g.buf)
961}
962
963// QualifiedGoIdent returns the string to use for a Go identifier.
964//
965// If the identifier is from a different Go package than the generated file,
966// the returned name will be qualified (package.name) and an import statement
967// for the identifier's package will be included in the file.
968func (g *GeneratedFile) QualifiedGoIdent(ident GoIdent) string {
969	if ident.GoImportPath == g.goImportPath {
970		return ident.GoName
971	}
972	if packageName, ok := g.packageNames[ident.GoImportPath]; ok {
973		return string(packageName) + "." + ident.GoName
974	}
975	packageName := cleanPackageName(path.Base(string(ident.GoImportPath)))
976	for i, orig := 1, packageName; g.usedPackageNames[packageName]; i++ {
977		packageName = orig + GoPackageName(strconv.Itoa(i))
978	}
979	g.packageNames[ident.GoImportPath] = packageName
980	g.usedPackageNames[packageName] = true
981	return string(packageName) + "." + ident.GoName
982}
983
984// Import ensures a package is imported by the generated file.
985//
986// Packages referenced by QualifiedGoIdent are automatically imported.
987// Explicitly importing a package with Import is generally only necessary
988// when the import will be blank (import _ "package").
989func (g *GeneratedFile) Import(importPath GoImportPath) {
990	g.manualImports[importPath] = true
991}
992
993// Write implements io.Writer.
994func (g *GeneratedFile) Write(p []byte) (n int, err error) {
995	return g.buf.Write(p)
996}
997
998// Skip removes the generated file from the plugin output.
999func (g *GeneratedFile) Skip() {
1000	g.skip = true
1001}
1002
1003// Unskip reverts a previous call to Skip, re-including the generated file in
1004// the plugin output.
1005func (g *GeneratedFile) Unskip() {
1006	g.skip = false
1007}
1008
1009// Annotate associates a symbol in a generated Go file with a location in a
1010// source .proto file.
1011//
1012// The symbol may refer to a type, constant, variable, function, method, or
1013// struct field.  The "T.sel" syntax is used to identify the method or field
1014// 'sel' on type 'T'.
1015func (g *GeneratedFile) Annotate(symbol string, loc Location) {
1016	g.annotations[symbol] = append(g.annotations[symbol], loc)
1017}
1018
1019// Content returns the contents of the generated file.
1020func (g *GeneratedFile) Content() ([]byte, error) {
1021	if !strings.HasSuffix(g.filename, ".go") {
1022		return g.buf.Bytes(), nil
1023	}
1024
1025	// Reformat generated code.
1026	original := g.buf.Bytes()
1027	fset := token.NewFileSet()
1028	file, err := parser.ParseFile(fset, "", original, parser.ParseComments)
1029	if err != nil {
1030		// Print out the bad code with line numbers.
1031		// This should never happen in practice, but it can while changing generated code
1032		// so consider this a debugging aid.
1033		var src bytes.Buffer
1034		s := bufio.NewScanner(bytes.NewReader(original))
1035		for line := 1; s.Scan(); line++ {
1036			fmt.Fprintf(&src, "%5d\t%s\n", line, s.Bytes())
1037		}
1038		return nil, fmt.Errorf("%v: unparsable Go source: %v\n%v", g.filename, err, src.String())
1039	}
1040
1041	// Collect a sorted list of all imports.
1042	var importPaths [][2]string
1043	rewriteImport := func(importPath string) string {
1044		if f := g.gen.opts.ImportRewriteFunc; f != nil {
1045			return string(f(GoImportPath(importPath)))
1046		}
1047		return importPath
1048	}
1049	for importPath := range g.packageNames {
1050		pkgName := string(g.packageNames[GoImportPath(importPath)])
1051		pkgPath := rewriteImport(string(importPath))
1052		importPaths = append(importPaths, [2]string{pkgName, pkgPath})
1053	}
1054	for importPath := range g.manualImports {
1055		if _, ok := g.packageNames[importPath]; !ok {
1056			pkgPath := rewriteImport(string(importPath))
1057			importPaths = append(importPaths, [2]string{"_", pkgPath})
1058		}
1059	}
1060	sort.Slice(importPaths, func(i, j int) bool {
1061		return importPaths[i][1] < importPaths[j][1]
1062	})
1063
1064	// Modify the AST to include a new import block.
1065	if len(importPaths) > 0 {
1066		// Insert block after package statement or
1067		// possible comment attached to the end of the package statement.
1068		pos := file.Package
1069		tokFile := fset.File(file.Package)
1070		pkgLine := tokFile.Line(file.Package)
1071		for _, c := range file.Comments {
1072			if tokFile.Line(c.Pos()) > pkgLine {
1073				break
1074			}
1075			pos = c.End()
1076		}
1077
1078		// Construct the import block.
1079		impDecl := &ast.GenDecl{
1080			Tok:    token.IMPORT,
1081			TokPos: pos,
1082			Lparen: pos,
1083			Rparen: pos,
1084		}
1085		for _, importPath := range importPaths {
1086			impDecl.Specs = append(impDecl.Specs, &ast.ImportSpec{
1087				Name: &ast.Ident{
1088					Name:    importPath[0],
1089					NamePos: pos,
1090				},
1091				Path: &ast.BasicLit{
1092					Kind:     token.STRING,
1093					Value:    strconv.Quote(importPath[1]),
1094					ValuePos: pos,
1095				},
1096				EndPos: pos,
1097			})
1098		}
1099		file.Decls = append([]ast.Decl{impDecl}, file.Decls...)
1100	}
1101
1102	var out bytes.Buffer
1103	if err = (&printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8}).Fprint(&out, fset, file); err != nil {
1104		return nil, fmt.Errorf("%v: can not reformat Go source: %v", g.filename, err)
1105	}
1106	return out.Bytes(), nil
1107}
1108
1109// metaFile returns the contents of the file's metadata file, which is a
1110// text formatted string of the google.protobuf.GeneratedCodeInfo.
1111func (g *GeneratedFile) metaFile(content []byte) (string, error) {
1112	fset := token.NewFileSet()
1113	astFile, err := parser.ParseFile(fset, "", content, 0)
1114	if err != nil {
1115		return "", err
1116	}
1117	info := &descriptorpb.GeneratedCodeInfo{}
1118
1119	seenAnnotations := make(map[string]bool)
1120	annotate := func(s string, ident *ast.Ident) {
1121		seenAnnotations[s] = true
1122		for _, loc := range g.annotations[s] {
1123			info.Annotation = append(info.Annotation, &descriptorpb.GeneratedCodeInfo_Annotation{
1124				SourceFile: proto.String(loc.SourceFile),
1125				Path:       loc.Path,
1126				Begin:      proto.Int32(int32(fset.Position(ident.Pos()).Offset)),
1127				End:        proto.Int32(int32(fset.Position(ident.End()).Offset)),
1128			})
1129		}
1130	}
1131	for _, decl := range astFile.Decls {
1132		switch decl := decl.(type) {
1133		case *ast.GenDecl:
1134			for _, spec := range decl.Specs {
1135				switch spec := spec.(type) {
1136				case *ast.TypeSpec:
1137					annotate(spec.Name.Name, spec.Name)
1138					switch st := spec.Type.(type) {
1139					case *ast.StructType:
1140						for _, field := range st.Fields.List {
1141							for _, name := range field.Names {
1142								annotate(spec.Name.Name+"."+name.Name, name)
1143							}
1144						}
1145					case *ast.InterfaceType:
1146						for _, field := range st.Methods.List {
1147							for _, name := range field.Names {
1148								annotate(spec.Name.Name+"."+name.Name, name)
1149							}
1150						}
1151					}
1152				case *ast.ValueSpec:
1153					for _, name := range spec.Names {
1154						annotate(name.Name, name)
1155					}
1156				}
1157			}
1158		case *ast.FuncDecl:
1159			if decl.Recv == nil {
1160				annotate(decl.Name.Name, decl.Name)
1161			} else {
1162				recv := decl.Recv.List[0].Type
1163				if s, ok := recv.(*ast.StarExpr); ok {
1164					recv = s.X
1165				}
1166				if id, ok := recv.(*ast.Ident); ok {
1167					annotate(id.Name+"."+decl.Name.Name, decl.Name)
1168				}
1169			}
1170		}
1171	}
1172	for a := range g.annotations {
1173		if !seenAnnotations[a] {
1174			return "", fmt.Errorf("%v: no symbol matching annotation %q", g.filename, a)
1175		}
1176	}
1177
1178	b, err := prototext.Marshal(info)
1179	if err != nil {
1180		return "", err
1181	}
1182	return string(b), nil
1183}
1184
1185// A GoIdent is a Go identifier, consisting of a name and import path.
1186// The name is a single identifier and may not be a dot-qualified selector.
1187type GoIdent struct {
1188	GoName       string
1189	GoImportPath GoImportPath
1190}
1191
1192func (id GoIdent) String() string { return fmt.Sprintf("%q.%v", id.GoImportPath, id.GoName) }
1193
1194// newGoIdent returns the Go identifier for a descriptor.
1195func newGoIdent(f *File, d protoreflect.Descriptor) GoIdent {
1196	name := strings.TrimPrefix(string(d.FullName()), string(f.Desc.Package())+".")
1197	return GoIdent{
1198		GoName:       strs.GoCamelCase(name),
1199		GoImportPath: f.GoImportPath,
1200	}
1201}
1202
1203// A GoImportPath is the import path of a Go package.
1204// For example: "google.golang.org/protobuf/compiler/protogen"
1205type GoImportPath string
1206
1207func (p GoImportPath) String() string { return strconv.Quote(string(p)) }
1208
1209// Ident returns a GoIdent with s as the GoName and p as the GoImportPath.
1210func (p GoImportPath) Ident(s string) GoIdent {
1211	return GoIdent{GoName: s, GoImportPath: p}
1212}
1213
1214// A GoPackageName is the name of a Go package. e.g., "protobuf".
1215type GoPackageName string
1216
1217// cleanPackageName converts a string to a valid Go package name.
1218func cleanPackageName(name string) GoPackageName {
1219	return GoPackageName(strs.GoSanitized(name))
1220}
1221
1222type pathType int
1223
1224const (
1225	pathTypeImport pathType = iota
1226	pathTypeSourceRelative
1227)
1228
1229// A Location is a location in a .proto source file.
1230//
1231// See the google.protobuf.SourceCodeInfo documentation in descriptor.proto
1232// for details.
1233type Location struct {
1234	SourceFile string
1235	Path       protoreflect.SourcePath
1236}
1237
1238// appendPath add elements to a Location's path, returning a new Location.
1239func (loc Location) appendPath(num protoreflect.FieldNumber, idx int) Location {
1240	loc.Path = append(protoreflect.SourcePath(nil), loc.Path...) // make copy
1241	loc.Path = append(loc.Path, int32(num), int32(idx))
1242	return loc
1243}
1244
1245// CommentSet is a set of leading and trailing comments associated
1246// with a .proto descriptor declaration.
1247type CommentSet struct {
1248	LeadingDetached []Comments
1249	Leading         Comments
1250	Trailing        Comments
1251}
1252
1253func makeCommentSet(loc protoreflect.SourceLocation) CommentSet {
1254	var leadingDetached []Comments
1255	for _, s := range loc.LeadingDetachedComments {
1256		leadingDetached = append(leadingDetached, Comments(s))
1257	}
1258	return CommentSet{
1259		LeadingDetached: leadingDetached,
1260		Leading:         Comments(loc.LeadingComments),
1261		Trailing:        Comments(loc.TrailingComments),
1262	}
1263}
1264
1265// Comments is a comments string as provided by protoc.
1266type Comments string
1267
1268// String formats the comments by inserting // to the start of each line,
1269// ensuring that there is a trailing newline.
1270// An empty comment is formatted as an empty string.
1271func (c Comments) String() string {
1272	if c == "" {
1273		return ""
1274	}
1275	var b []byte
1276	for _, line := range strings.Split(strings.TrimSuffix(string(c), "\n"), "\n") {
1277		b = append(b, "//"...)
1278		b = append(b, line...)
1279		b = append(b, "\n"...)
1280	}
1281	return string(b)
1282}
1283
1284// extensionRegistry allows registration of new extensions defined in the .proto
1285// file for which we are generating bindings.
1286//
1287// Lookups consult the local type registry first and fall back to the base type
1288// registry which defaults to protoregistry.GlobalTypes
1289type extensionRegistry struct {
1290	base  *protoregistry.Types
1291	local *protoregistry.Types
1292}
1293
1294func newExtensionRegistry() *extensionRegistry {
1295	return &extensionRegistry{
1296		base:  protoregistry.GlobalTypes,
1297		local: &protoregistry.Types{},
1298	}
1299}
1300
1301// FindExtensionByName implements proto.UnmarshalOptions.FindExtensionByName
1302func (e *extensionRegistry) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) {
1303	if xt, err := e.local.FindExtensionByName(field); err == nil {
1304		return xt, nil
1305	}
1306
1307	return e.base.FindExtensionByName(field)
1308}
1309
1310// FindExtensionByNumber implements proto.UnmarshalOptions.FindExtensionByNumber
1311func (e *extensionRegistry) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) {
1312	if xt, err := e.local.FindExtensionByNumber(message, field); err == nil {
1313		return xt, nil
1314	}
1315
1316	return e.base.FindExtensionByNumber(message, field)
1317}
1318
1319func (e *extensionRegistry) hasNovelExtensions() bool {
1320	return e.local.NumExtensions() > 0
1321}
1322
1323func (e *extensionRegistry) registerAllExtensionsFromFile(f protoreflect.FileDescriptor) error {
1324	if err := e.registerAllExtensions(f.Extensions()); err != nil {
1325		return err
1326	}
1327	return nil
1328}
1329
1330func (e *extensionRegistry) registerAllExtensionsFromMessage(ms protoreflect.MessageDescriptors) error {
1331	for i := 0; i < ms.Len(); i++ {
1332		m := ms.Get(i)
1333		if err := e.registerAllExtensions(m.Extensions()); err != nil {
1334			return err
1335		}
1336	}
1337	return nil
1338}
1339
1340func (e *extensionRegistry) registerAllExtensions(exts protoreflect.ExtensionDescriptors) error {
1341	for i := 0; i < exts.Len(); i++ {
1342		if err := e.registerExtension(exts.Get(i)); err != nil {
1343			return err
1344		}
1345	}
1346	return nil
1347}
1348
1349// registerExtension adds the given extension to the type registry if an
1350// extension with that full name does not exist yet.
1351func (e *extensionRegistry) registerExtension(xd protoreflect.ExtensionDescriptor) error {
1352	if _, err := e.FindExtensionByName(xd.FullName()); err != protoregistry.NotFound {
1353		// Either the extension already exists or there was an error, either way we're done.
1354		return err
1355	}
1356	return e.local.RegisterExtension(dynamicpb.NewExtensionType(xd))
1357}
1358