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