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