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