2018-08-15 11:24:18 -07:00
|
|
|
// Copyright 2018 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
2018-09-24 13:43:03 -07:00
|
|
|
// Package internal_gengo is internal to the protobuf module.
|
|
|
|
package internal_gengo
|
2018-08-15 11:24:18 -07:00
|
|
|
|
|
|
|
import (
|
2018-09-07 14:14:06 -07:00
|
|
|
"fmt"
|
2018-12-21 15:54:06 -08:00
|
|
|
"go/ast"
|
|
|
|
"go/parser"
|
|
|
|
"go/token"
|
2018-09-13 08:50:13 -07:00
|
|
|
"math"
|
2018-09-07 14:14:06 -07:00
|
|
|
"strconv"
|
2018-09-06 14:51:28 -07:00
|
|
|
"strings"
|
2018-12-21 15:54:06 -08:00
|
|
|
"unicode"
|
|
|
|
"unicode/utf8"
|
2018-09-07 14:14:06 -07:00
|
|
|
|
2018-11-01 13:52:16 -07:00
|
|
|
"github.com/golang/protobuf/v2/internal/encoding/tag"
|
2019-03-20 16:51:09 -07:00
|
|
|
"github.com/golang/protobuf/v2/internal/fieldnum"
|
2018-09-21 17:44:00 -07:00
|
|
|
"github.com/golang/protobuf/v2/protogen"
|
|
|
|
"github.com/golang/protobuf/v2/reflect/protoreflect"
|
2018-11-26 18:55:29 -08:00
|
|
|
|
|
|
|
descriptorpb "github.com/golang/protobuf/v2/types/descriptor"
|
2018-08-15 11:24:18 -07:00
|
|
|
)
|
|
|
|
|
2019-03-28 01:13:26 -07:00
|
|
|
// minimumVersion is minimum version of the v2 proto package that is required.
|
|
|
|
// This is incremented every time the generated code relies on some property
|
|
|
|
// in the proto package that was introduced in a later version.
|
|
|
|
const minimumVersion = 0
|
|
|
|
|
2019-04-16 15:23:29 -07:00
|
|
|
const (
|
|
|
|
// generateEnumMapVars specifies whether to generate enum maps,
|
|
|
|
// which provide a bi-directional mapping between enum numbers and names.
|
|
|
|
generateEnumMapVars = true
|
|
|
|
|
|
|
|
// generateRawDescMethods specifies whether to generate EnumDescriptor and
|
|
|
|
// Descriptor methods for enums and messages. These methods return the
|
|
|
|
// GZIP'd contents of the raw file descriptor and the path from the root
|
|
|
|
// to the given enum or message descriptor.
|
|
|
|
generateRawDescMethods = true
|
|
|
|
)
|
|
|
|
|
2018-11-16 11:14:14 -08:00
|
|
|
const (
|
2019-03-28 01:13:26 -07:00
|
|
|
syncPackage = protogen.GoImportPath("sync")
|
all: move v1 types over to the v2 repository
As a goal, v2 should not depend on v1. As another step towards that end,
we move all the types that used to be in the v1 protoapi package over to v2.
For now, we place MessageV1, ExtensionRangeV1, and ExtensionDescV1
in runtime/protoiface since these are types that generated messages will
probably have to reference forever. An alternative location could be
reflect/protoreflect, but it seems unfortunate to have to dirty the
namespace of that package with these types.
We move ExtensionFieldV1, ExtensionFieldsV1, and ExtensionFieldsOf
to internal/impl, since these are related to the implementation of a
generated message.
Since moving these types from v1 to v2 implies that the v1 protoapi
package is useless, we update all usages of v1 protoapi in the v2
repository to point to the relevant v2 type or functionality.
CL/168538 is the corresponding change to alter v1.
There will be a temporary build failure as it is not possible
to submit CL/168519 and CL/168538 atomically.
Change-Id: Ide4025c1b6af5b7f0696f4b65b988b4d10a50f0b
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/168519
Reviewed-by: Herbie Ong <herbie@google.com>
2019-03-20 18:29:32 -07:00
|
|
|
mathPackage = protogen.GoImportPath("math")
|
|
|
|
protoifacePackage = protogen.GoImportPath("github.com/golang/protobuf/v2/runtime/protoiface")
|
|
|
|
protoimplPackage = protogen.GoImportPath("github.com/golang/protobuf/v2/runtime/protoimpl")
|
|
|
|
protoreflectPackage = protogen.GoImportPath("github.com/golang/protobuf/v2/reflect/protoreflect")
|
|
|
|
protoregistryPackage = protogen.GoImportPath("github.com/golang/protobuf/v2/reflect/protoregistry")
|
2018-11-16 11:14:14 -08:00
|
|
|
)
|
2018-09-07 12:45:37 -07:00
|
|
|
|
2018-09-24 12:38:10 -07:00
|
|
|
type fileInfo struct {
|
2018-09-06 14:51:28 -07:00
|
|
|
*protogen.File
|
internal/fileinit: generate reflect data structures from raw descriptors
This CL takes a significantly different approach to generating support
for protobuf reflection. The previous approach involved generating a
large number of Go literals to represent the reflection information.
While that approach was correct, it resulted in too much binary bloat.
The approach taken here initializes the reflection information from
the raw descriptor proto, which is a relatively dense representation
of the protobuf reflection information. In order to keep initialization
cost low, several measures were taken:
* At program init, the bare minimum is parsed in order to initialize
naming information for enums, messages, extensions, and services declared
in the file. This is done because those top-level declarations are often
relevant for registration.
* Only upon first are most of the other data structures for protobuf
reflection actually initialized.
* Instead of using proto.Unmarshal, a hand-written unmarshaler is used.
This allows us to avoid a dependendency on the descriptor proto and also
because the API for the descriptor proto is fundamentally non-performant
since it requires an allocation for every primitive field.
At a high-level, the new implementation lives in internal/fileinit.
Several changes were made to other parts of the repository:
* cmd/protoc-gen-go:
* Stop compressing the raw descriptors. While compression does reduce
the size of the descriptors by approximately 2x, it is a pre-mature
optimization since the descriptors themselves are around 1% of the total
binary bloat that is due to generated protobufs.
* Seeding protobuf reflection from the raw descriptor significantly
simplifies the generator implementation since it is no longer responsible
for constructing a tree of Go literals to represent the same information.
* We remove the generation of the shadow types and instead call
protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation
for every call to ProtoReflect since we need to allocate a tuple that wraps
a pointer to the message value, and a pointer to message type.
* internal/impl:
* We add a MessageType.GoType field and make it required that it is
set prior to first use. This is done so that we can avoid calling
MessageType.init except for when it is actually needed. The allows code
to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the
init code will run, possibly triggering a recursive deadlock (where the
init code depends on getting the Type of some dependency which may be
declared within the same file).
* internal/cmd/generate-types:
* The code to generate reflect/prototype/protofile_list_gen.go was copied
and altered to generated internal/fileinit.desc_list_gen.go.
At a high-level this CL adds significant technical complexity.
However, this is offset by several possible future changes:
* The prototype package can be drastically simplified. We can probably
reimplement internal/legacy to use internal/fileinit instead, allowing us
to drop another dependency on the prototype package. As a result, we can
probably delete most of the constructor types in that package.
* With the prototype package significantly pruned, and the fact that generated
code no longer depend on depends on that package, we can consider merging
what's left of prototype into protodesc.
Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715
Reviewed-on: https://go-review.googlesource.com/c/158539
Reviewed-by: Herbie Ong <herbie@google.com>
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
|
|
|
|
2018-12-05 15:42:52 -08:00
|
|
|
allEnums []*protogen.Enum
|
|
|
|
allEnumsByPtr map[*protogen.Enum]int // value is index into allEnums
|
|
|
|
allMessages []*protogen.Message
|
|
|
|
allMessagesByPtr map[*protogen.Message]int // value is index into allMessages
|
|
|
|
allExtensions []*protogen.Extension
|
2018-09-06 14:51:28 -07:00
|
|
|
}
|
|
|
|
|
2018-09-27 15:26:33 -07:00
|
|
|
// GenerateFile generates the contents of a .pb.go file.
|
2019-02-27 21:46:29 -08:00
|
|
|
func GenerateFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile {
|
|
|
|
filename := file.GeneratedFilenamePrefix + ".pb.go"
|
|
|
|
g := gen.NewGeneratedFile(filename, file.GoImportPath)
|
2018-09-24 12:38:10 -07:00
|
|
|
f := &fileInfo{
|
2018-10-17 12:53:18 -07:00
|
|
|
File: file,
|
2018-09-06 14:51:28 -07:00
|
|
|
}
|
|
|
|
|
internal/fileinit: generate reflect data structures from raw descriptors
This CL takes a significantly different approach to generating support
for protobuf reflection. The previous approach involved generating a
large number of Go literals to represent the reflection information.
While that approach was correct, it resulted in too much binary bloat.
The approach taken here initializes the reflection information from
the raw descriptor proto, which is a relatively dense representation
of the protobuf reflection information. In order to keep initialization
cost low, several measures were taken:
* At program init, the bare minimum is parsed in order to initialize
naming information for enums, messages, extensions, and services declared
in the file. This is done because those top-level declarations are often
relevant for registration.
* Only upon first are most of the other data structures for protobuf
reflection actually initialized.
* Instead of using proto.Unmarshal, a hand-written unmarshaler is used.
This allows us to avoid a dependendency on the descriptor proto and also
because the API for the descriptor proto is fundamentally non-performant
since it requires an allocation for every primitive field.
At a high-level, the new implementation lives in internal/fileinit.
Several changes were made to other parts of the repository:
* cmd/protoc-gen-go:
* Stop compressing the raw descriptors. While compression does reduce
the size of the descriptors by approximately 2x, it is a pre-mature
optimization since the descriptors themselves are around 1% of the total
binary bloat that is due to generated protobufs.
* Seeding protobuf reflection from the raw descriptor significantly
simplifies the generator implementation since it is no longer responsible
for constructing a tree of Go literals to represent the same information.
* We remove the generation of the shadow types and instead call
protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation
for every call to ProtoReflect since we need to allocate a tuple that wraps
a pointer to the message value, and a pointer to message type.
* internal/impl:
* We add a MessageType.GoType field and make it required that it is
set prior to first use. This is done so that we can avoid calling
MessageType.init except for when it is actually needed. The allows code
to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the
init code will run, possibly triggering a recursive deadlock (where the
init code depends on getting the Type of some dependency which may be
declared within the same file).
* internal/cmd/generate-types:
* The code to generate reflect/prototype/protofile_list_gen.go was copied
and altered to generated internal/fileinit.desc_list_gen.go.
At a high-level this CL adds significant technical complexity.
However, this is offset by several possible future changes:
* The prototype package can be drastically simplified. We can probably
reimplement internal/legacy to use internal/fileinit instead, allowing us
to drop another dependency on the prototype package. As a result, we can
probably delete most of the constructor types in that package.
* With the prototype package significantly pruned, and the fact that generated
code no longer depend on depends on that package, we can consider merging
what's left of prototype into protodesc.
Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715
Reviewed-on: https://go-review.googlesource.com/c/158539
Reviewed-by: Herbie Ong <herbie@google.com>
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
|
|
|
// Collect all enums, messages, and extensions in "flattened ordering".
|
|
|
|
// See fileinit.FileBuilder.
|
2018-12-05 15:42:52 -08:00
|
|
|
f.allEnums = append(f.allEnums, f.Enums...)
|
|
|
|
f.allMessages = append(f.allMessages, f.Messages...)
|
|
|
|
f.allExtensions = append(f.allExtensions, f.Extensions...)
|
|
|
|
walkMessages(f.Messages, func(m *protogen.Message) {
|
|
|
|
f.allEnums = append(f.allEnums, m.Enums...)
|
|
|
|
f.allMessages = append(f.allMessages, m.Messages...)
|
|
|
|
f.allExtensions = append(f.allExtensions, m.Extensions...)
|
2018-09-17 15:11:24 -07:00
|
|
|
})
|
2018-09-13 15:19:08 -07:00
|
|
|
|
2018-12-05 15:42:52 -08:00
|
|
|
// Derive a reverse mapping of enum and message pointers to their index
|
|
|
|
// in allEnums and allMessages.
|
|
|
|
if len(f.allEnums) > 0 {
|
|
|
|
f.allEnumsByPtr = make(map[*protogen.Enum]int)
|
|
|
|
for i, e := range f.allEnums {
|
|
|
|
f.allEnumsByPtr[e] = i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(f.allMessages) > 0 {
|
|
|
|
f.allMessagesByPtr = make(map[*protogen.Message]int)
|
|
|
|
for i, m := range f.allMessages {
|
|
|
|
f.allMessagesByPtr[m] = i
|
|
|
|
}
|
|
|
|
}
|
cmd/protoc-gen-go: add support for protobuf reflection
Implement support in protoc-gen-go for generating messages and enums
that satisfy the v2 protobuf reflection interfaces. Specifically, the following
are added:
* top-level variable representing the file descriptor
* ProtoReflect method on enums (to implement protoV2.Enum)
* ProtoReflect method on messages (to implement protoV2.Message)
The following are not supported yet:
* resolving transitive dependencies for file imports
* Extension descriptors
* Service descriptors
The implementation approach creates a single array for all the message and enum
declarations and references sections of that array to complete dependencies.
Since protobuf declarations can form a graph (a message may depend on itself),
it is difficult to construct a graph as a single literal. One way is to use
placeholder descriptors, but that is not efficient as it requires encoding
the full name of each dependent enum and message and then later resolving it;
thus, both expanding the binary size and also increasing initialization cost.
Instead, we add a prototype.{Enum,Message}.Reference method to obtain a
descriptor reference for the purposes for satisfying dependencies.
As such, nested declarations and dependencies are populated in an init function.
Other changes to support the implementation:
* Added a protoimpl package to expose the MessageType type and also the
MessageTypeOf and EnumTypeOf helper functions.
* Added a protogen.File.GoIdent field to provide a suggested variable name
for the file descriptor.
* Added prototype.{Enum,Message}.Reference that provides a descriptor reference
for the purposes for satisfying cyclic dependencies.
* Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source
identifier that represents the given constant.
Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d
Reviewed-on: https://go-review.googlesource.com/c/150074
Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
|
|
|
|
2018-08-15 11:24:18 -07:00
|
|
|
g.P("// Code generated by protoc-gen-go. DO NOT EDIT.")
|
2018-09-17 15:11:24 -07:00
|
|
|
if f.Proto.GetOptions().GetDeprecated() {
|
|
|
|
g.P("// ", f.Desc.Path(), " is a deprecated file.")
|
|
|
|
} else {
|
|
|
|
g.P("// source: ", f.Desc.Path())
|
|
|
|
}
|
2018-08-15 11:24:18 -07:00
|
|
|
g.P()
|
2018-10-17 12:53:18 -07:00
|
|
|
g.PrintLeadingComments(protogen.Location{
|
|
|
|
SourceFile: f.Proto.GetName(),
|
2019-03-20 16:51:09 -07:00
|
|
|
Path: []int32{fieldnum.FileDescriptorProto_Package},
|
2018-10-17 12:53:18 -07:00
|
|
|
})
|
2018-09-06 14:51:28 -07:00
|
|
|
g.P()
|
2018-09-06 10:23:53 -07:00
|
|
|
g.P("package ", f.GoPackageName)
|
2018-08-22 13:46:02 -07:00
|
|
|
g.P()
|
2018-09-13 13:12:36 -07:00
|
|
|
|
2019-03-28 01:13:26 -07:00
|
|
|
// Emit a static check that enforces a minimum version of the proto package.
|
|
|
|
g.P("const _ = ", protoimplPackage.Ident("EnforceVersion"), "(", protoimplPackage.Ident("Version"), " - ", minimumVersion, ")")
|
|
|
|
g.P()
|
|
|
|
|
2018-09-17 15:11:24 -07:00
|
|
|
for i, imps := 0, f.Desc.Imports(); i < imps.Len(); i++ {
|
|
|
|
genImport(gen, g, f, imps.Get(i))
|
|
|
|
}
|
2018-09-13 15:19:08 -07:00
|
|
|
for _, enum := range f.allEnums {
|
2018-09-07 12:45:37 -07:00
|
|
|
genEnum(gen, g, f, enum)
|
|
|
|
}
|
2018-09-13 15:19:08 -07:00
|
|
|
for _, message := range f.allMessages {
|
2018-09-06 14:51:28 -07:00
|
|
|
genMessage(gen, g, f, message)
|
2018-08-22 13:46:02 -07:00
|
|
|
}
|
2019-03-14 16:08:22 -07:00
|
|
|
genExtensions(gen, g, f)
|
2018-08-15 11:24:18 -07:00
|
|
|
|
cmd/protoc-gen-go: add support for protobuf reflection
Implement support in protoc-gen-go for generating messages and enums
that satisfy the v2 protobuf reflection interfaces. Specifically, the following
are added:
* top-level variable representing the file descriptor
* ProtoReflect method on enums (to implement protoV2.Enum)
* ProtoReflect method on messages (to implement protoV2.Message)
The following are not supported yet:
* resolving transitive dependencies for file imports
* Extension descriptors
* Service descriptors
The implementation approach creates a single array for all the message and enum
declarations and references sections of that array to complete dependencies.
Since protobuf declarations can form a graph (a message may depend on itself),
it is difficult to construct a graph as a single literal. One way is to use
placeholder descriptors, but that is not efficient as it requires encoding
the full name of each dependent enum and message and then later resolving it;
thus, both expanding the binary size and also increasing initialization cost.
Instead, we add a prototype.{Enum,Message}.Reference method to obtain a
descriptor reference for the purposes for satisfying dependencies.
As such, nested declarations and dependencies are populated in an init function.
Other changes to support the implementation:
* Added a protoimpl package to expose the MessageType type and also the
MessageTypeOf and EnumTypeOf helper functions.
* Added a protogen.File.GoIdent field to provide a suggested variable name
for the file descriptor.
* Added prototype.{Enum,Message}.Reference that provides a descriptor reference
for the purposes for satisfying cyclic dependencies.
* Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source
identifier that represents the given constant.
Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d
Reviewed-on: https://go-review.googlesource.com/c/150074
Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
|
|
|
genReflectFileDescriptor(gen, g, f)
|
2019-02-27 21:46:29 -08:00
|
|
|
|
|
|
|
return g
|
2018-09-07 14:14:06 -07:00
|
|
|
}
|
|
|
|
|
2018-09-17 15:11:24 -07:00
|
|
|
// walkMessages calls f on each message and all of its descendants.
|
|
|
|
func walkMessages(messages []*protogen.Message, f func(*protogen.Message)) {
|
|
|
|
for _, m := range messages {
|
|
|
|
f(m)
|
|
|
|
walkMessages(m.Messages, f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-24 12:38:10 -07:00
|
|
|
func genImport(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, imp protoreflect.FileImport) {
|
2018-09-17 15:11:24 -07:00
|
|
|
impFile, ok := gen.FileByName(imp.Path())
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if impFile.GoImportPath == f.GoImportPath {
|
2018-09-19 12:51:36 -07:00
|
|
|
// Don't generate imports or aliases for types in the same Go package.
|
|
|
|
return
|
|
|
|
}
|
2018-10-29 09:07:41 -07:00
|
|
|
// Generate imports for all non-weak dependencies, even if they are not
|
2018-09-19 12:51:36 -07:00
|
|
|
// referenced, because other code and tools depend on having the
|
|
|
|
// full transitive closure of protocol buffer types in the binary.
|
2018-10-29 09:07:41 -07:00
|
|
|
if !imp.IsWeak {
|
|
|
|
g.Import(impFile.GoImportPath)
|
|
|
|
}
|
2018-09-19 12:51:36 -07:00
|
|
|
if !imp.IsPublic {
|
2018-09-17 15:11:24 -07:00
|
|
|
return
|
|
|
|
}
|
2018-12-21 15:54:06 -08:00
|
|
|
|
|
|
|
// Generate public imports by generating the imported file, parsing it,
|
|
|
|
// and extracting every symbol that should receive a forwarding declaration.
|
2019-02-27 21:46:29 -08:00
|
|
|
impGen := GenerateFile(gen, impFile)
|
2018-12-21 15:54:06 -08:00
|
|
|
impGen.Skip()
|
|
|
|
b, err := impGen.Content()
|
|
|
|
if err != nil {
|
|
|
|
gen.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
fset := token.NewFileSet()
|
|
|
|
astFile, err := parser.ParseFile(fset, "", b, parser.ParseComments)
|
|
|
|
if err != nil {
|
|
|
|
gen.Error(err)
|
|
|
|
return
|
|
|
|
}
|
2019-01-06 16:29:14 -08:00
|
|
|
genForward := func(tok token.Token, name string, expr ast.Expr) {
|
2018-12-21 15:54:06 -08:00
|
|
|
// Don't import unexported symbols.
|
|
|
|
r, _ := utf8.DecodeRuneInString(name)
|
|
|
|
if !unicode.IsUpper(r) {
|
2018-10-09 12:49:13 -07:00
|
|
|
return
|
|
|
|
}
|
2018-12-21 15:54:06 -08:00
|
|
|
// Don't import the FileDescriptor.
|
|
|
|
if name == impFile.GoDescriptorIdent.GoName {
|
|
|
|
return
|
2018-10-09 12:49:13 -07:00
|
|
|
}
|
2019-01-06 16:29:14 -08:00
|
|
|
// Don't import decls referencing a symbol defined in another package.
|
|
|
|
// i.e., don't import decls which are themselves public imports:
|
|
|
|
//
|
|
|
|
// type T = somepackage.T
|
|
|
|
if _, ok := expr.(*ast.SelectorExpr); ok {
|
|
|
|
return
|
|
|
|
}
|
2018-12-21 15:54:06 -08:00
|
|
|
g.P(tok, " ", name, " = ", impFile.GoImportPath.Ident(name))
|
|
|
|
}
|
|
|
|
g.P("// Symbols defined in public import of ", imp.Path())
|
|
|
|
g.P()
|
|
|
|
for _, decl := range astFile.Decls {
|
|
|
|
switch decl := decl.(type) {
|
|
|
|
case *ast.GenDecl:
|
|
|
|
for _, spec := range decl.Specs {
|
|
|
|
switch spec := spec.(type) {
|
|
|
|
case *ast.TypeSpec:
|
2019-01-06 16:29:14 -08:00
|
|
|
genForward(decl.Tok, spec.Name.Name, spec.Type)
|
2018-12-21 15:54:06 -08:00
|
|
|
case *ast.ValueSpec:
|
2019-01-06 16:29:14 -08:00
|
|
|
for i, name := range spec.Names {
|
|
|
|
var expr ast.Expr
|
|
|
|
if i < len(spec.Values) {
|
|
|
|
expr = spec.Values[i]
|
|
|
|
}
|
|
|
|
genForward(decl.Tok, name.Name, expr)
|
2018-12-21 15:54:06 -08:00
|
|
|
}
|
|
|
|
case *ast.ImportSpec:
|
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("can't generate forward for spec type %T", spec))
|
|
|
|
}
|
2018-09-17 15:11:24 -07:00
|
|
|
}
|
|
|
|
}
|
2018-10-29 09:14:14 -07:00
|
|
|
}
|
2018-10-09 12:49:13 -07:00
|
|
|
g.P()
|
2018-09-13 15:19:08 -07:00
|
|
|
}
|
|
|
|
|
2018-09-24 12:38:10 -07:00
|
|
|
func genEnum(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, enum *protogen.Enum) {
|
2019-04-01 12:59:24 -07:00
|
|
|
// Enum type declaration.
|
2018-10-17 12:53:18 -07:00
|
|
|
g.PrintLeadingComments(enum.Location)
|
2018-10-04 12:42:37 -07:00
|
|
|
g.Annotate(enum.GoIdent.GoName, enum.Location)
|
2018-09-17 15:11:24 -07:00
|
|
|
g.P("type ", enum.GoIdent, " int32",
|
2018-11-26 18:55:29 -08:00
|
|
|
deprecationComment(enum.Desc.Options().(*descriptorpb.EnumOptions).GetDeprecated()))
|
2019-04-01 12:59:24 -07:00
|
|
|
|
|
|
|
// Enum value constants.
|
2018-09-07 12:45:37 -07:00
|
|
|
g.P("const (")
|
|
|
|
for _, value := range enum.Values {
|
2018-10-17 12:53:18 -07:00
|
|
|
g.PrintLeadingComments(value.Location)
|
2018-10-04 12:42:37 -07:00
|
|
|
g.Annotate(value.GoIdent.GoName, value.Location)
|
2018-09-17 15:11:24 -07:00
|
|
|
g.P(value.GoIdent, " ", enum.GoIdent, " = ", value.Desc.Number(),
|
2018-11-26 18:55:29 -08:00
|
|
|
deprecationComment(value.Desc.Options().(*descriptorpb.EnumValueOptions).GetDeprecated()))
|
2018-09-07 12:45:37 -07:00
|
|
|
}
|
|
|
|
g.P(")")
|
|
|
|
g.P()
|
cmd/protoc-gen-go: add support for protobuf reflection
Implement support in protoc-gen-go for generating messages and enums
that satisfy the v2 protobuf reflection interfaces. Specifically, the following
are added:
* top-level variable representing the file descriptor
* ProtoReflect method on enums (to implement protoV2.Enum)
* ProtoReflect method on messages (to implement protoV2.Message)
The following are not supported yet:
* resolving transitive dependencies for file imports
* Extension descriptors
* Service descriptors
The implementation approach creates a single array for all the message and enum
declarations and references sections of that array to complete dependencies.
Since protobuf declarations can form a graph (a message may depend on itself),
it is difficult to construct a graph as a single literal. One way is to use
placeholder descriptors, but that is not efficient as it requires encoding
the full name of each dependent enum and message and then later resolving it;
thus, both expanding the binary size and also increasing initialization cost.
Instead, we add a prototype.{Enum,Message}.Reference method to obtain a
descriptor reference for the purposes for satisfying dependencies.
As such, nested declarations and dependencies are populated in an init function.
Other changes to support the implementation:
* Added a protoimpl package to expose the MessageType type and also the
MessageTypeOf and EnumTypeOf helper functions.
* Added a protogen.File.GoIdent field to provide a suggested variable name
for the file descriptor.
* Added prototype.{Enum,Message}.Reference that provides a descriptor reference
for the purposes for satisfying cyclic dependencies.
* Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source
identifier that represents the given constant.
Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d
Reviewed-on: https://go-review.googlesource.com/c/150074
Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
|
|
|
|
2019-04-01 12:59:24 -07:00
|
|
|
// Enum value mapping (number -> name).
|
2019-04-16 15:23:29 -07:00
|
|
|
if generateEnumMapVars {
|
|
|
|
nameMap := enum.GoIdent.GoName + "_name"
|
|
|
|
g.P("// Deprecated: Use ", enum.GoIdent.GoName, ".Type.Values instead.")
|
|
|
|
g.P("var ", nameMap, " = map[int32]string{")
|
|
|
|
generated := make(map[protoreflect.EnumNumber]bool)
|
|
|
|
for _, value := range enum.Values {
|
|
|
|
duplicate := ""
|
|
|
|
if _, present := generated[value.Desc.Number()]; present {
|
|
|
|
duplicate = "// Duplicate value: "
|
|
|
|
}
|
|
|
|
g.P(duplicate, value.Desc.Number(), ": ", strconv.Quote(string(value.Desc.Name())), ",")
|
|
|
|
generated[value.Desc.Number()] = true
|
2018-09-07 12:45:37 -07:00
|
|
|
}
|
2019-04-16 15:23:29 -07:00
|
|
|
g.P("}")
|
|
|
|
g.P()
|
2018-09-07 12:45:37 -07:00
|
|
|
}
|
2019-03-16 00:05:34 -07:00
|
|
|
|
2019-04-01 12:59:24 -07:00
|
|
|
// Enum value mapping (name -> number).
|
2019-04-16 15:23:29 -07:00
|
|
|
if generateEnumMapVars {
|
|
|
|
valueMap := enum.GoIdent.GoName + "_value"
|
|
|
|
g.P("// Deprecated: Use ", enum.GoIdent.GoName, ".Type.Values instead.")
|
|
|
|
g.P("var ", valueMap, " = map[string]int32{")
|
|
|
|
for _, value := range enum.Values {
|
|
|
|
g.P(strconv.Quote(string(value.Desc.Name())), ": ", value.Desc.Number(), ",")
|
|
|
|
}
|
|
|
|
g.P("}")
|
|
|
|
g.P()
|
2018-09-07 12:45:37 -07:00
|
|
|
}
|
2019-03-16 00:05:34 -07:00
|
|
|
|
2019-04-01 12:59:24 -07:00
|
|
|
// Enum method.
|
2018-09-07 12:45:37 -07:00
|
|
|
if enum.Desc.Syntax() != protoreflect.Proto3 {
|
|
|
|
g.P("func (x ", enum.GoIdent, ") Enum() *", enum.GoIdent, " {")
|
2019-04-10 15:29:01 -07:00
|
|
|
g.P("p := new(", enum.GoIdent, ")")
|
|
|
|
g.P("*p = x")
|
|
|
|
g.P("return p")
|
2018-09-07 12:45:37 -07:00
|
|
|
g.P("}")
|
|
|
|
g.P()
|
|
|
|
}
|
2019-04-01 12:59:24 -07:00
|
|
|
// String method.
|
2018-09-07 12:45:37 -07:00
|
|
|
g.P("func (x ", enum.GoIdent, ") String() string {")
|
2019-03-16 00:05:34 -07:00
|
|
|
g.P("return ", protoimplPackage.Ident("X"), ".EnumStringOf(x.Type(), ", protoreflectPackage.Ident("EnumNumber"), "(x))")
|
2018-09-07 12:45:37 -07:00
|
|
|
g.P("}")
|
|
|
|
g.P()
|
|
|
|
|
2019-04-01 12:59:24 -07:00
|
|
|
genReflectEnum(gen, g, f, enum)
|
|
|
|
|
|
|
|
// UnmarshalJSON method.
|
2018-12-14 12:22:41 -08:00
|
|
|
if enum.Desc.Syntax() == protoreflect.Proto2 {
|
2019-03-16 00:05:34 -07:00
|
|
|
g.P("// Deprecated: Do not use.")
|
|
|
|
g.P("func (x *", enum.GoIdent, ") UnmarshalJSON(b []byte) error {")
|
|
|
|
g.P("num, err := ", protoimplPackage.Ident("X"), ".UnmarshalJSONEnum(x.Type(), b)")
|
2018-09-07 12:45:37 -07:00
|
|
|
g.P("if err != nil {")
|
|
|
|
g.P("return err")
|
|
|
|
g.P("}")
|
2019-03-16 00:05:34 -07:00
|
|
|
g.P("*x = ", enum.GoIdent, "(num)")
|
2018-09-07 12:45:37 -07:00
|
|
|
g.P("return nil")
|
|
|
|
g.P("}")
|
|
|
|
g.P()
|
|
|
|
}
|
|
|
|
|
2019-04-01 12:59:24 -07:00
|
|
|
// EnumDescriptor method.
|
2019-04-16 15:23:29 -07:00
|
|
|
if generateRawDescMethods {
|
|
|
|
var indexes []string
|
|
|
|
for i := 1; i < len(enum.Location.Path); i += 2 {
|
|
|
|
indexes = append(indexes, strconv.Itoa(int(enum.Location.Path[i])))
|
|
|
|
}
|
|
|
|
g.P("// Deprecated: Use ", enum.GoIdent, ".Type instead.")
|
|
|
|
g.P("func (", enum.GoIdent, ") EnumDescriptor() ([]byte, []int) {")
|
|
|
|
g.P("return ", rawDescVarName(f), "GZIP(), []int{", strings.Join(indexes, ","), "}")
|
|
|
|
g.P("}")
|
|
|
|
g.P()
|
2018-09-07 12:45:37 -07:00
|
|
|
}
|
|
|
|
|
2018-09-28 14:23:44 -07:00
|
|
|
genWellKnownType(g, "", enum.GoIdent, enum.Desc)
|
2018-09-07 12:45:37 -07:00
|
|
|
}
|
|
|
|
|
2019-04-01 12:59:24 -07:00
|
|
|
// enumLegacyName returns the name used by the v1 proto package.
|
2018-09-10 12:26:21 -07:00
|
|
|
//
|
|
|
|
// Confusingly, this is <proto_package>.<go_ident>. This probably should have
|
|
|
|
// been the full name of the proto enum type instead, but changing it at this
|
|
|
|
// point would require thought.
|
2019-04-01 12:59:24 -07:00
|
|
|
func enumLegacyName(enum *protogen.Enum) string {
|
2018-09-10 12:26:21 -07:00
|
|
|
// Find the FileDescriptor for this enum.
|
|
|
|
var desc protoreflect.Descriptor = enum.Desc
|
|
|
|
for {
|
|
|
|
p, ok := desc.Parent()
|
|
|
|
if !ok {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
desc = p
|
|
|
|
}
|
|
|
|
fdesc := desc.(protoreflect.FileDescriptor)
|
2018-10-08 14:08:27 -07:00
|
|
|
if fdesc.Package() == "" {
|
|
|
|
return enum.GoIdent.GoName
|
|
|
|
}
|
2018-09-10 12:26:21 -07:00
|
|
|
return string(fdesc.Package()) + "." + enum.GoIdent.GoName
|
|
|
|
}
|
|
|
|
|
2018-09-24 12:38:10 -07:00
|
|
|
func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message) {
|
2018-09-13 15:07:10 -07:00
|
|
|
if message.Desc.IsMapEntry() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-04-01 12:59:24 -07:00
|
|
|
// Message type declaration.
|
2018-10-17 12:53:18 -07:00
|
|
|
hasComment := g.PrintLeadingComments(message.Location)
|
2018-11-26 18:55:29 -08:00
|
|
|
if message.Desc.Options().(*descriptorpb.MessageOptions).GetDeprecated() {
|
2018-09-17 15:11:24 -07:00
|
|
|
if hasComment {
|
|
|
|
g.P("//")
|
|
|
|
}
|
|
|
|
g.P(deprecationComment(true))
|
|
|
|
}
|
2018-10-04 12:42:37 -07:00
|
|
|
g.Annotate(message.GoIdent.GoName, message.Location)
|
2018-09-06 14:51:28 -07:00
|
|
|
g.P("type ", message.GoIdent, " struct {")
|
2018-09-10 12:26:21 -07:00
|
|
|
for _, field := range message.Fields {
|
2019-04-15 23:39:09 -07:00
|
|
|
if field.Oneof != nil {
|
2018-09-13 13:12:36 -07:00
|
|
|
// It would be a bit simpler to iterate over the oneofs below,
|
|
|
|
// but generating the field here keeps the contents of the Go
|
|
|
|
// struct in the same order as the contents of the source
|
|
|
|
// .proto file.
|
2019-04-15 23:39:09 -07:00
|
|
|
if field == field.Oneof.Fields[0] {
|
|
|
|
genOneofField(gen, g, f, message, field.Oneof)
|
2018-09-13 13:12:36 -07:00
|
|
|
}
|
2018-09-10 12:26:21 -07:00
|
|
|
continue
|
|
|
|
}
|
2018-10-17 12:53:18 -07:00
|
|
|
g.PrintLeadingComments(field.Location)
|
2018-09-13 10:59:17 -07:00
|
|
|
goType, pointer := fieldGoType(g, field)
|
|
|
|
if pointer {
|
|
|
|
goType = "*" + goType
|
|
|
|
}
|
2018-09-13 15:07:10 -07:00
|
|
|
tags := []string{
|
|
|
|
fmt.Sprintf("protobuf:%q", fieldProtobufTag(field)),
|
|
|
|
fmt.Sprintf("json:%q", fieldJSONTag(field)),
|
|
|
|
}
|
|
|
|
if field.Desc.IsMap() {
|
2019-04-15 23:39:09 -07:00
|
|
|
key := field.Message.Fields[0]
|
|
|
|
val := field.Message.Fields[1]
|
2018-09-13 15:07:10 -07:00
|
|
|
tags = append(tags,
|
|
|
|
fmt.Sprintf("protobuf_key:%q", fieldProtobufTag(key)),
|
|
|
|
fmt.Sprintf("protobuf_val:%q", fieldProtobufTag(val)),
|
|
|
|
)
|
|
|
|
}
|
2018-10-04 12:42:37 -07:00
|
|
|
g.Annotate(message.GoIdent.GoName+"."+field.GoName, field.Location)
|
2018-09-17 15:11:24 -07:00
|
|
|
g.P(field.GoName, " ", goType, " `", strings.Join(tags, " "), "`",
|
2018-11-26 18:55:29 -08:00
|
|
|
deprecationComment(field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated()))
|
2018-09-10 12:26:21 -07:00
|
|
|
}
|
|
|
|
g.P("XXX_NoUnkeyedLiteral struct{} `json:\"-\"`")
|
2018-09-14 15:41:11 -07:00
|
|
|
|
|
|
|
if message.Desc.ExtensionRanges().Len() > 0 {
|
|
|
|
var tags []string
|
2018-11-26 18:55:29 -08:00
|
|
|
if message.Desc.Options().(*descriptorpb.MessageOptions).GetMessageSetWireFormat() {
|
2018-09-14 15:41:11 -07:00
|
|
|
tags = append(tags, `protobuf_messageset:"1"`)
|
|
|
|
}
|
|
|
|
tags = append(tags, `json:"-"`)
|
2019-04-16 13:22:20 -07:00
|
|
|
g.P("XXX_InternalExtensions ", protoimplPackage.Ident("ExtensionFields"), " `", strings.Join(tags, " "), "`")
|
2018-09-14 15:41:11 -07:00
|
|
|
}
|
2019-04-16 13:22:20 -07:00
|
|
|
g.P("XXX_unrecognized ", protoimplPackage.Ident("UnknownFields"), " `json:\"-\"`")
|
|
|
|
g.P("XXX_sizecache ", protoimplPackage.Ident("SizeCache"), " `json:\"-\"`")
|
2018-08-22 13:46:02 -07:00
|
|
|
g.P("}")
|
|
|
|
g.P()
|
|
|
|
|
2019-04-01 12:59:24 -07:00
|
|
|
// Reset method.
|
|
|
|
g.P("func (x *", message.GoIdent, ") Reset() {")
|
|
|
|
g.P("*x = ", message.GoIdent, "{}")
|
|
|
|
g.P("}")
|
|
|
|
g.P()
|
|
|
|
// String method.
|
|
|
|
g.P("func (x *", message.GoIdent, ") String() string {")
|
|
|
|
g.P("return ", protoimplPackage.Ident("X"), ".MessageStringOf(x)")
|
|
|
|
g.P("}")
|
|
|
|
g.P()
|
|
|
|
// ProtoMessage method.
|
|
|
|
g.P("func (*", message.GoIdent, ") ProtoMessage() {}")
|
|
|
|
g.P()
|
|
|
|
|
cmd/protoc-gen-go: add support for protobuf reflection
Implement support in protoc-gen-go for generating messages and enums
that satisfy the v2 protobuf reflection interfaces. Specifically, the following
are added:
* top-level variable representing the file descriptor
* ProtoReflect method on enums (to implement protoV2.Enum)
* ProtoReflect method on messages (to implement protoV2.Message)
The following are not supported yet:
* resolving transitive dependencies for file imports
* Extension descriptors
* Service descriptors
The implementation approach creates a single array for all the message and enum
declarations and references sections of that array to complete dependencies.
Since protobuf declarations can form a graph (a message may depend on itself),
it is difficult to construct a graph as a single literal. One way is to use
placeholder descriptors, but that is not efficient as it requires encoding
the full name of each dependent enum and message and then later resolving it;
thus, both expanding the binary size and also increasing initialization cost.
Instead, we add a prototype.{Enum,Message}.Reference method to obtain a
descriptor reference for the purposes for satisfying dependencies.
As such, nested declarations and dependencies are populated in an init function.
Other changes to support the implementation:
* Added a protoimpl package to expose the MessageType type and also the
MessageTypeOf and EnumTypeOf helper functions.
* Added a protogen.File.GoIdent field to provide a suggested variable name
for the file descriptor.
* Added prototype.{Enum,Message}.Reference that provides a descriptor reference
for the purposes for satisfying cyclic dependencies.
* Added protoreflect.{Syntax,Cardinality,Kind}.GoString to obtain a Go source
identifier that represents the given constant.
Change-Id: I9455764882dee6ad10f251901e7d419091e8bf1d
Reviewed-on: https://go-review.googlesource.com/c/150074
Reviewed-by: Damien Neil <dneil@google.com>
2018-11-15 14:44:37 -08:00
|
|
|
genReflectMessage(gen, g, f, message)
|
|
|
|
|
2019-04-01 12:59:24 -07:00
|
|
|
// Descriptor method.
|
2019-04-16 15:23:29 -07:00
|
|
|
if generateRawDescMethods {
|
|
|
|
var indexes []string
|
|
|
|
for i := 1; i < len(message.Location.Path); i += 2 {
|
|
|
|
indexes = append(indexes, strconv.Itoa(int(message.Location.Path[i])))
|
|
|
|
}
|
|
|
|
g.P("// Deprecated: Use ", message.GoIdent, ".ProtoReflect.Type instead.")
|
|
|
|
g.P("func (*", message.GoIdent, ") Descriptor() ([]byte, []int) {")
|
|
|
|
g.P("return ", rawDescVarName(f), "GZIP(), []int{", strings.Join(indexes, ","), "}")
|
|
|
|
g.P("}")
|
|
|
|
g.P()
|
2018-09-12 13:36:34 -07:00
|
|
|
}
|
2018-09-14 15:41:11 -07:00
|
|
|
|
2019-04-01 12:59:24 -07:00
|
|
|
// ExtensionRangeArray method.
|
2018-09-14 15:41:11 -07:00
|
|
|
if extranges := message.Desc.ExtensionRanges(); extranges.Len() > 0 {
|
all: move v1 types over to the v2 repository
As a goal, v2 should not depend on v1. As another step towards that end,
we move all the types that used to be in the v1 protoapi package over to v2.
For now, we place MessageV1, ExtensionRangeV1, and ExtensionDescV1
in runtime/protoiface since these are types that generated messages will
probably have to reference forever. An alternative location could be
reflect/protoreflect, but it seems unfortunate to have to dirty the
namespace of that package with these types.
We move ExtensionFieldV1, ExtensionFieldsV1, and ExtensionFieldsOf
to internal/impl, since these are related to the implementation of a
generated message.
Since moving these types from v1 to v2 implies that the v1 protoapi
package is useless, we update all usages of v1 protoapi in the v2
repository to point to the relevant v2 type or functionality.
CL/168538 is the corresponding change to alter v1.
There will be a temporary build failure as it is not possible
to submit CL/168519 and CL/168538 atomically.
Change-Id: Ide4025c1b6af5b7f0696f4b65b988b4d10a50f0b
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/168519
Reviewed-by: Herbie Ong <herbie@google.com>
2019-03-20 18:29:32 -07:00
|
|
|
protoExtRange := protoifacePackage.Ident("ExtensionRangeV1")
|
2018-09-14 15:41:11 -07:00
|
|
|
extRangeVar := "extRange_" + message.GoIdent.GoName
|
|
|
|
g.P("var ", extRangeVar, " = []", protoExtRange, " {")
|
|
|
|
for i := 0; i < extranges.Len(); i++ {
|
|
|
|
r := extranges.Get(i)
|
|
|
|
g.P("{Start:", r[0], ", End:", r[1]-1 /* inclusive */, "},")
|
|
|
|
}
|
|
|
|
g.P("}")
|
|
|
|
g.P()
|
2019-03-16 00:05:34 -07:00
|
|
|
g.P("// Deprecated: Use ", message.GoIdent, ".ProtoReflect.Type.ExtensionRanges instead.")
|
2018-09-14 15:41:11 -07:00
|
|
|
g.P("func (*", message.GoIdent, ") ExtensionRangeArray() []", protoExtRange, " {")
|
|
|
|
g.P("return ", extRangeVar)
|
|
|
|
g.P("}")
|
|
|
|
g.P()
|
|
|
|
}
|
2018-09-12 13:36:34 -07:00
|
|
|
|
2018-09-28 14:23:44 -07:00
|
|
|
genWellKnownType(g, "*", message.GoIdent, message.Desc)
|
|
|
|
|
2018-09-13 08:50:13 -07:00
|
|
|
// Constants and vars holding the default values of fields.
|
|
|
|
for _, field := range message.Fields {
|
2018-12-05 15:42:52 -08:00
|
|
|
if !field.Desc.HasDefault() {
|
2018-09-13 08:50:13 -07:00
|
|
|
continue
|
|
|
|
}
|
2018-09-13 13:12:36 -07:00
|
|
|
defVarName := "Default_" + message.GoIdent.GoName + "_" + field.GoName
|
2018-09-13 08:50:13 -07:00
|
|
|
def := field.Desc.Default()
|
|
|
|
switch field.Desc.Kind() {
|
|
|
|
case protoreflect.StringKind:
|
|
|
|
g.P("const ", defVarName, " string = ", strconv.Quote(def.String()))
|
|
|
|
case protoreflect.BytesKind:
|
|
|
|
g.P("var ", defVarName, " []byte = []byte(", strconv.Quote(string(def.Bytes())), ")")
|
|
|
|
case protoreflect.EnumKind:
|
2018-10-26 13:28:37 -07:00
|
|
|
evalueDesc := field.Desc.DefaultEnumValue()
|
2019-04-15 23:39:09 -07:00
|
|
|
enum := field.Enum
|
2018-10-26 13:28:37 -07:00
|
|
|
evalue := enum.Values[evalueDesc.Index()]
|
2019-04-15 23:39:09 -07:00
|
|
|
g.P("const ", defVarName, " ", field.Enum.GoIdent, " = ", evalue.GoIdent)
|
2018-09-13 08:50:13 -07:00
|
|
|
case protoreflect.FloatKind, protoreflect.DoubleKind:
|
|
|
|
// Floating point numbers need extra handling for -Inf/Inf/NaN.
|
|
|
|
f := field.Desc.Default().Float()
|
|
|
|
goType := "float64"
|
|
|
|
if field.Desc.Kind() == protoreflect.FloatKind {
|
|
|
|
goType = "float32"
|
|
|
|
}
|
|
|
|
// funcCall returns a call to a function in the math package,
|
|
|
|
// possibly converting the result to float32.
|
|
|
|
funcCall := func(fn, param string) string {
|
2018-11-16 11:14:14 -08:00
|
|
|
s := g.QualifiedGoIdent(mathPackage.Ident(fn)) + param
|
2018-09-13 08:50:13 -07:00
|
|
|
if goType != "float64" {
|
|
|
|
s = goType + "(" + s + ")"
|
|
|
|
}
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
switch {
|
|
|
|
case math.IsInf(f, -1):
|
|
|
|
g.P("var ", defVarName, " ", goType, " = ", funcCall("Inf", "(-1)"))
|
|
|
|
case math.IsInf(f, 1):
|
|
|
|
g.P("var ", defVarName, " ", goType, " = ", funcCall("Inf", "(1)"))
|
|
|
|
case math.IsNaN(f):
|
|
|
|
g.P("var ", defVarName, " ", goType, " = ", funcCall("NaN", "()"))
|
|
|
|
default:
|
2018-09-28 14:12:41 -07:00
|
|
|
g.P("const ", defVarName, " ", goType, " = ", field.Desc.Default().Interface())
|
2018-09-13 08:50:13 -07:00
|
|
|
}
|
|
|
|
default:
|
2018-09-13 10:59:17 -07:00
|
|
|
goType, _ := fieldGoType(g, field)
|
2018-09-13 08:50:13 -07:00
|
|
|
g.P("const ", defVarName, " ", goType, " = ", def.Interface())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g.P()
|
|
|
|
|
2019-04-01 12:59:24 -07:00
|
|
|
// Getter methods.
|
2018-09-13 10:59:17 -07:00
|
|
|
for _, field := range message.Fields {
|
2019-04-08 14:03:15 -07:00
|
|
|
if isFirstOneofField(field) {
|
2019-04-15 23:39:09 -07:00
|
|
|
genOneofGetter(gen, g, f, message, field.Oneof)
|
2018-09-13 13:12:36 -07:00
|
|
|
}
|
2018-09-13 10:59:17 -07:00
|
|
|
goType, pointer := fieldGoType(g, field)
|
|
|
|
defaultValue := fieldDefaultValue(g, message, field)
|
2018-11-26 18:55:29 -08:00
|
|
|
if field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated() {
|
2018-09-17 15:11:24 -07:00
|
|
|
g.P(deprecationComment(true))
|
|
|
|
}
|
2018-10-04 12:42:37 -07:00
|
|
|
g.Annotate(message.GoIdent.GoName+".Get"+field.GoName, field.Location)
|
2019-04-01 12:59:24 -07:00
|
|
|
g.P("func (x *", message.GoIdent, ") Get", field.GoName, "() ", goType, " {")
|
2019-04-15 23:39:09 -07:00
|
|
|
if field.Oneof != nil {
|
|
|
|
g.P("if x, ok := x.Get", field.Oneof.GoName, "().(*", fieldOneofType(field), "); ok {")
|
2018-09-13 13:12:36 -07:00
|
|
|
g.P("return x.", field.GoName)
|
|
|
|
g.P("}")
|
2018-09-13 10:59:17 -07:00
|
|
|
} else {
|
2018-09-13 13:12:36 -07:00
|
|
|
if field.Desc.Syntax() == protoreflect.Proto3 || defaultValue == "nil" {
|
2019-04-01 12:59:24 -07:00
|
|
|
g.P("if x != nil {")
|
2018-09-13 13:12:36 -07:00
|
|
|
} else {
|
2019-04-01 12:59:24 -07:00
|
|
|
g.P("if x != nil && x.", field.GoName, " != nil {")
|
2018-09-13 13:12:36 -07:00
|
|
|
}
|
|
|
|
star := ""
|
|
|
|
if pointer {
|
|
|
|
star = "*"
|
|
|
|
}
|
2019-04-01 12:59:24 -07:00
|
|
|
g.P("return ", star, " x.", field.GoName)
|
2018-09-13 13:12:36 -07:00
|
|
|
g.P("}")
|
2018-09-13 10:59:17 -07:00
|
|
|
}
|
|
|
|
g.P("return ", defaultValue)
|
|
|
|
g.P("}")
|
|
|
|
g.P()
|
|
|
|
}
|
2018-09-12 13:36:34 -07:00
|
|
|
|
2019-04-08 14:03:15 -07:00
|
|
|
// XXX_OneofWrappers method.
|
2018-09-13 13:12:36 -07:00
|
|
|
if len(message.Oneofs) > 0 {
|
2018-11-26 12:57:27 -08:00
|
|
|
genOneofWrappers(gen, g, f, message)
|
2018-09-13 13:12:36 -07:00
|
|
|
}
|
2019-04-08 14:03:15 -07:00
|
|
|
|
|
|
|
// Oneof wrapper types.
|
|
|
|
for _, oneof := range message.Oneofs {
|
|
|
|
genOneofTypes(gen, g, f, message, oneof)
|
|
|
|
}
|
2018-09-06 14:51:28 -07:00
|
|
|
}
|
|
|
|
|
2018-09-13 10:59:17 -07:00
|
|
|
// fieldGoType returns the Go type used for a field.
|
|
|
|
//
|
|
|
|
// If it returns pointer=true, the struct field is a pointer to the type.
|
|
|
|
func fieldGoType(g *protogen.GeneratedFile, field *protogen.Field) (goType string, pointer bool) {
|
|
|
|
pointer = true
|
2018-09-10 12:26:21 -07:00
|
|
|
switch field.Desc.Kind() {
|
|
|
|
case protoreflect.BoolKind:
|
2018-09-13 10:59:17 -07:00
|
|
|
goType = "bool"
|
2018-09-10 12:26:21 -07:00
|
|
|
case protoreflect.EnumKind:
|
2019-04-15 23:39:09 -07:00
|
|
|
goType = g.QualifiedGoIdent(field.Enum.GoIdent)
|
2018-09-10 12:26:21 -07:00
|
|
|
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
|
2018-09-13 10:59:17 -07:00
|
|
|
goType = "int32"
|
2018-09-10 12:26:21 -07:00
|
|
|
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
|
2018-09-13 10:59:17 -07:00
|
|
|
goType = "uint32"
|
2018-09-10 12:26:21 -07:00
|
|
|
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
|
2018-09-13 10:59:17 -07:00
|
|
|
goType = "int64"
|
2018-09-10 12:26:21 -07:00
|
|
|
case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
|
2018-09-13 10:59:17 -07:00
|
|
|
goType = "uint64"
|
2018-09-10 12:26:21 -07:00
|
|
|
case protoreflect.FloatKind:
|
2018-09-13 10:59:17 -07:00
|
|
|
goType = "float32"
|
2018-09-10 12:26:21 -07:00
|
|
|
case protoreflect.DoubleKind:
|
2018-09-13 10:59:17 -07:00
|
|
|
goType = "float64"
|
2018-09-10 12:26:21 -07:00
|
|
|
case protoreflect.StringKind:
|
2018-09-13 10:59:17 -07:00
|
|
|
goType = "string"
|
2018-09-10 12:26:21 -07:00
|
|
|
case protoreflect.BytesKind:
|
2018-09-13 10:59:17 -07:00
|
|
|
goType = "[]byte"
|
|
|
|
pointer = false
|
2018-09-10 12:26:21 -07:00
|
|
|
case protoreflect.MessageKind, protoreflect.GroupKind:
|
2018-09-13 15:07:10 -07:00
|
|
|
if field.Desc.IsMap() {
|
2019-04-15 23:39:09 -07:00
|
|
|
keyType, _ := fieldGoType(g, field.Message.Fields[0])
|
|
|
|
valType, _ := fieldGoType(g, field.Message.Fields[1])
|
2018-09-13 15:07:10 -07:00
|
|
|
return fmt.Sprintf("map[%v]%v", keyType, valType), false
|
|
|
|
}
|
2019-04-15 23:39:09 -07:00
|
|
|
goType = "*" + g.QualifiedGoIdent(field.Message.GoIdent)
|
2018-09-13 10:59:17 -07:00
|
|
|
pointer = false
|
2018-09-10 12:26:21 -07:00
|
|
|
}
|
|
|
|
if field.Desc.Cardinality() == protoreflect.Repeated {
|
2018-09-13 10:59:17 -07:00
|
|
|
goType = "[]" + goType
|
|
|
|
pointer = false
|
2018-09-10 12:26:21 -07:00
|
|
|
}
|
2018-10-24 12:31:16 -07:00
|
|
|
// Extension fields always have pointer type, even when defined in a proto3 file.
|
2019-04-15 23:39:09 -07:00
|
|
|
if field.Desc.Syntax() == protoreflect.Proto3 && field.Desc.Extendee() == nil {
|
2018-09-13 10:59:17 -07:00
|
|
|
pointer = false
|
2018-09-10 12:26:21 -07:00
|
|
|
}
|
2018-09-13 10:59:17 -07:00
|
|
|
return goType, pointer
|
2018-09-10 12:26:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func fieldProtobufTag(field *protogen.Field) string {
|
2018-11-01 13:52:16 -07:00
|
|
|
var enumName string
|
2018-09-10 12:26:21 -07:00
|
|
|
if field.Desc.Kind() == protoreflect.EnumKind {
|
2019-04-15 23:39:09 -07:00
|
|
|
enumName = enumLegacyName(field.Enum)
|
2018-09-13 08:50:13 -07:00
|
|
|
}
|
2018-11-01 13:52:16 -07:00
|
|
|
return tag.Marshal(field.Desc, enumName)
|
2018-09-10 12:26:21 -07:00
|
|
|
}
|
|
|
|
|
2018-09-13 10:59:17 -07:00
|
|
|
func fieldDefaultValue(g *protogen.GeneratedFile, message *protogen.Message, field *protogen.Field) string {
|
|
|
|
if field.Desc.Cardinality() == protoreflect.Repeated {
|
|
|
|
return "nil"
|
|
|
|
}
|
2018-12-05 15:42:52 -08:00
|
|
|
if field.Desc.HasDefault() {
|
2018-09-13 13:12:36 -07:00
|
|
|
defVarName := "Default_" + message.GoIdent.GoName + "_" + field.GoName
|
2018-09-13 10:59:17 -07:00
|
|
|
if field.Desc.Kind() == protoreflect.BytesKind {
|
|
|
|
return "append([]byte(nil), " + defVarName + "...)"
|
|
|
|
}
|
|
|
|
return defVarName
|
|
|
|
}
|
|
|
|
switch field.Desc.Kind() {
|
|
|
|
case protoreflect.BoolKind:
|
|
|
|
return "false"
|
|
|
|
case protoreflect.StringKind:
|
|
|
|
return `""`
|
|
|
|
case protoreflect.MessageKind, protoreflect.GroupKind, protoreflect.BytesKind:
|
|
|
|
return "nil"
|
|
|
|
case protoreflect.EnumKind:
|
2019-04-15 23:39:09 -07:00
|
|
|
return g.QualifiedGoIdent(field.Enum.Values[0].GoIdent)
|
2018-09-13 10:59:17 -07:00
|
|
|
default:
|
|
|
|
return "0"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-10 12:26:21 -07:00
|
|
|
func fieldJSONTag(field *protogen.Field) string {
|
|
|
|
return string(field.Desc.Name()) + ",omitempty"
|
|
|
|
}
|
|
|
|
|
2019-03-14 16:08:22 -07:00
|
|
|
func genExtensions(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
|
|
|
|
if len(f.allExtensions) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
all: move v1 types over to the v2 repository
As a goal, v2 should not depend on v1. As another step towards that end,
we move all the types that used to be in the v1 protoapi package over to v2.
For now, we place MessageV1, ExtensionRangeV1, and ExtensionDescV1
in runtime/protoiface since these are types that generated messages will
probably have to reference forever. An alternative location could be
reflect/protoreflect, but it seems unfortunate to have to dirty the
namespace of that package with these types.
We move ExtensionFieldV1, ExtensionFieldsV1, and ExtensionFieldsOf
to internal/impl, since these are related to the implementation of a
generated message.
Since moving these types from v1 to v2 implies that the v1 protoapi
package is useless, we update all usages of v1 protoapi in the v2
repository to point to the relevant v2 type or functionality.
CL/168538 is the corresponding change to alter v1.
There will be a temporary build failure as it is not possible
to submit CL/168519 and CL/168538 atomically.
Change-Id: Ide4025c1b6af5b7f0696f4b65b988b4d10a50f0b
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/168519
Reviewed-by: Herbie Ong <herbie@google.com>
2019-03-20 18:29:32 -07:00
|
|
|
g.P("var ", extDecsVarName(f), " = []", protoifacePackage.Ident("ExtensionDescV1"), "{")
|
2019-03-14 16:08:22 -07:00
|
|
|
for _, extension := range f.allExtensions {
|
|
|
|
// Special case for proto2 message sets: If this extension is extending
|
|
|
|
// proto2.bridge.MessageSet, and its final name component is "message_set_extension",
|
|
|
|
// then drop that last component.
|
|
|
|
//
|
|
|
|
// TODO: This should be implemented in the text formatter rather than the generator.
|
|
|
|
// In addition, the situation for when to apply this special case is implemented
|
|
|
|
// differently in other languages:
|
|
|
|
// https://github.com/google/protobuf/blob/aff10976/src/google/protobuf/text_format.cc#L1560
|
|
|
|
name := extension.Desc.FullName()
|
|
|
|
if n, ok := isExtensionMessageSetElement(extension); ok {
|
|
|
|
name = n
|
|
|
|
}
|
|
|
|
|
|
|
|
g.P("{")
|
2019-04-15 23:39:09 -07:00
|
|
|
g.P("ExtendedType: (*", extension.Extendee.GoIdent, ")(nil),")
|
2019-03-14 16:08:22 -07:00
|
|
|
goType, pointer := fieldGoType(g, extension)
|
|
|
|
if pointer {
|
|
|
|
goType = "*" + goType
|
|
|
|
}
|
|
|
|
g.P("ExtensionType: (", goType, ")(nil),")
|
|
|
|
g.P("Field: ", extension.Desc.Number(), ",")
|
|
|
|
g.P("Name: ", strconv.Quote(string(name)), ",")
|
|
|
|
g.P("Tag: ", strconv.Quote(fieldProtobufTag(extension)), ",")
|
|
|
|
g.P("Filename: ", strconv.Quote(f.Desc.Path()), ",")
|
|
|
|
g.P("},")
|
|
|
|
}
|
2018-09-14 15:41:11 -07:00
|
|
|
g.P("}")
|
2019-03-14 16:08:22 -07:00
|
|
|
|
|
|
|
g.P("var (")
|
|
|
|
for i, extension := range f.allExtensions {
|
|
|
|
ed := extension.Desc
|
2019-04-15 23:39:09 -07:00
|
|
|
targetName := string(ed.Extendee().FullName())
|
2019-03-14 16:08:22 -07:00
|
|
|
typeName := ed.Kind().String()
|
|
|
|
switch ed.Kind() {
|
|
|
|
case protoreflect.EnumKind:
|
2019-04-15 23:39:09 -07:00
|
|
|
typeName = string(ed.Enum().FullName())
|
2019-03-14 16:08:22 -07:00
|
|
|
case protoreflect.MessageKind, protoreflect.GroupKind:
|
2019-04-15 23:39:09 -07:00
|
|
|
typeName = string(ed.Message().FullName())
|
2019-03-14 16:08:22 -07:00
|
|
|
}
|
|
|
|
fieldName := string(ed.Name())
|
|
|
|
g.P("// extend ", targetName, " { ", ed.Cardinality().String(), " ", typeName, " ", fieldName, " = ", ed.Number(), "; }")
|
|
|
|
g.P(extensionVar(f.File, extension), " = &", extDecsVarName(f), "[", i, "]")
|
|
|
|
g.P()
|
|
|
|
}
|
|
|
|
g.P(")")
|
2018-09-14 15:41:11 -07:00
|
|
|
}
|
|
|
|
|
2018-10-30 10:35:48 -07:00
|
|
|
// isExtensionMessageSetELement returns the adjusted name of an extension
|
|
|
|
// which extends proto2.bridge.MessageSet.
|
|
|
|
func isExtensionMessageSetElement(extension *protogen.Extension) (name protoreflect.FullName, ok bool) {
|
2019-04-15 23:39:09 -07:00
|
|
|
opts := extension.Extendee.Desc.Options().(*descriptorpb.MessageOptions)
|
2018-10-30 10:35:48 -07:00
|
|
|
if !opts.GetMessageSetWireFormat() || extension.Desc.Name() != "message_set_extension" {
|
|
|
|
return "", false
|
|
|
|
}
|
2019-04-15 23:39:09 -07:00
|
|
|
if extension.Parent == nil {
|
2018-10-30 10:35:48 -07:00
|
|
|
// This case shouldn't be given special handling at all--we're
|
|
|
|
// only supposed to drop the ".message_set_extension" for
|
|
|
|
// extensions defined within a message (i.e., the extension
|
|
|
|
// takes the message's name).
|
|
|
|
//
|
|
|
|
// This matches the behavior of the v1 generator, however.
|
|
|
|
//
|
|
|
|
// TODO: See if we can drop this case.
|
|
|
|
name = extension.Desc.FullName()
|
|
|
|
name = name[:len(name)-len("message_set_extension")]
|
|
|
|
return name, true
|
|
|
|
}
|
|
|
|
return extension.Desc.FullName().Parent(), true
|
2018-09-19 13:21:58 -07:00
|
|
|
}
|
|
|
|
|
2018-09-14 15:41:11 -07:00
|
|
|
// extensionVar returns the var holding the ExtensionDesc for an extension.
|
2018-10-29 09:14:14 -07:00
|
|
|
func extensionVar(f *protogen.File, extension *protogen.Extension) protogen.GoIdent {
|
2018-09-14 15:41:11 -07:00
|
|
|
name := "E_"
|
2019-04-15 23:39:09 -07:00
|
|
|
if extension.Parent != nil {
|
|
|
|
name += extension.Parent.GoIdent.GoName + "_"
|
2018-09-14 15:41:11 -07:00
|
|
|
}
|
|
|
|
name += extension.GoName
|
2018-11-16 11:14:14 -08:00
|
|
|
return f.GoImportPath.Ident(name)
|
2018-09-14 15:41:11 -07:00
|
|
|
}
|
|
|
|
|
2018-09-17 15:11:24 -07:00
|
|
|
// deprecationComment returns a standard deprecation comment if deprecated is true.
|
|
|
|
func deprecationComment(deprecated bool) string {
|
|
|
|
if !deprecated {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return "// Deprecated: Do not use."
|
|
|
|
}
|
|
|
|
|
2019-04-01 12:59:24 -07:00
|
|
|
// TODO: Remove this. This was added to aid jsonpb, but jsonpb does this work
|
|
|
|
// through the use of protobuf reflection now.
|
2018-09-28 14:23:44 -07:00
|
|
|
func genWellKnownType(g *protogen.GeneratedFile, ptr string, ident protogen.GoIdent, desc protoreflect.Descriptor) {
|
2018-09-07 12:45:37 -07:00
|
|
|
if wellKnownTypes[desc.FullName()] {
|
2018-09-28 14:23:44 -07:00
|
|
|
g.P("func (", ptr, ident, `) XXX_WellKnownType() string { return "`, desc.Name(), `" }`)
|
2018-09-07 12:45:37 -07:00
|
|
|
g.P()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Names of messages and enums for which we will generate XXX_WellKnownType methods.
|
|
|
|
var wellKnownTypes = map[protoreflect.FullName]bool{
|
2018-09-28 14:23:44 -07:00
|
|
|
"google.protobuf.Any": true,
|
|
|
|
"google.protobuf.Duration": true,
|
|
|
|
"google.protobuf.Empty": true,
|
|
|
|
"google.protobuf.Struct": true,
|
|
|
|
"google.protobuf.Timestamp": true,
|
|
|
|
|
|
|
|
"google.protobuf.BoolValue": true,
|
|
|
|
"google.protobuf.BytesValue": true,
|
|
|
|
"google.protobuf.DoubleValue": true,
|
|
|
|
"google.protobuf.FloatValue": true,
|
|
|
|
"google.protobuf.Int32Value": true,
|
|
|
|
"google.protobuf.Int64Value": true,
|
|
|
|
"google.protobuf.ListValue": true,
|
|
|
|
"google.protobuf.NullValue": true,
|
|
|
|
"google.protobuf.StringValue": true,
|
|
|
|
"google.protobuf.UInt32Value": true,
|
|
|
|
"google.protobuf.UInt64Value": true,
|
|
|
|
"google.protobuf.Value": true,
|
2018-09-07 12:45:37 -07:00
|
|
|
}
|
2018-11-26 12:57:27 -08:00
|
|
|
|
|
|
|
// genOneofField generates the struct field for a oneof.
|
|
|
|
func genOneofField(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message, oneof *protogen.Oneof) {
|
|
|
|
if g.PrintLeadingComments(oneof.Location) {
|
|
|
|
g.P("//")
|
|
|
|
}
|
|
|
|
g.P("// Types that are valid to be assigned to ", oneofFieldName(oneof), ":")
|
|
|
|
for _, field := range oneof.Fields {
|
|
|
|
g.PrintLeadingComments(field.Location)
|
|
|
|
g.P("//\t*", fieldOneofType(field))
|
|
|
|
}
|
|
|
|
g.Annotate(message.GoIdent.GoName+"."+oneofFieldName(oneof), oneof.Location)
|
|
|
|
g.P(oneofFieldName(oneof), " ", oneofInterfaceName(oneof), " `protobuf_oneof:\"", oneof.Desc.Name(), "\"`")
|
|
|
|
}
|
|
|
|
|
2019-04-08 14:03:15 -07:00
|
|
|
// genOneofGetter generate a Get method for a oneof.
|
|
|
|
func genOneofGetter(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message, oneof *protogen.Oneof) {
|
|
|
|
g.Annotate(message.GoIdent.GoName+".Get"+oneof.GoName, oneof.Location)
|
|
|
|
g.P("func (m *", message.GoIdent.GoName, ") Get", oneof.GoName, "() ", oneofInterfaceName(oneof), " {")
|
|
|
|
g.P("if m != nil {")
|
|
|
|
g.P("return m.", oneofFieldName(oneof))
|
|
|
|
g.P("}")
|
|
|
|
g.P("return nil")
|
|
|
|
g.P("}")
|
|
|
|
g.P()
|
|
|
|
}
|
|
|
|
|
|
|
|
// genOneofWrappers generates the XXX_OneofWrappers method for a message.
|
|
|
|
func genOneofWrappers(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message) {
|
|
|
|
g.P("// XXX_OneofWrappers is for the internal use of the proto package.")
|
|
|
|
g.P("func (*", message.GoIdent.GoName, ") XXX_OneofWrappers() []interface{} {")
|
|
|
|
g.P("return []interface{}{")
|
|
|
|
for _, oneof := range message.Oneofs {
|
|
|
|
for _, field := range oneof.Fields {
|
|
|
|
g.P("(*", fieldOneofType(field), ")(nil),")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g.P("}")
|
|
|
|
g.P("}")
|
|
|
|
g.P()
|
|
|
|
}
|
|
|
|
|
2018-11-26 12:57:27 -08:00
|
|
|
// genOneofTypes generates the interface type used for a oneof field,
|
|
|
|
// and the wrapper types that satisfy that interface.
|
|
|
|
func genOneofTypes(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message, oneof *protogen.Oneof) {
|
|
|
|
ifName := oneofInterfaceName(oneof)
|
|
|
|
g.P("type ", ifName, " interface {")
|
|
|
|
g.P(ifName, "()")
|
|
|
|
g.P("}")
|
|
|
|
g.P()
|
|
|
|
for _, field := range oneof.Fields {
|
|
|
|
name := fieldOneofType(field)
|
|
|
|
g.Annotate(name.GoName, field.Location)
|
|
|
|
g.Annotate(name.GoName+"."+field.GoName, field.Location)
|
|
|
|
g.P("type ", name, " struct {")
|
|
|
|
goType, _ := fieldGoType(g, field)
|
|
|
|
tags := []string{
|
|
|
|
fmt.Sprintf("protobuf:%q", fieldProtobufTag(field)),
|
|
|
|
}
|
|
|
|
g.P(field.GoName, " ", goType, " `", strings.Join(tags, " "), "`")
|
|
|
|
g.P("}")
|
|
|
|
g.P()
|
|
|
|
}
|
|
|
|
for _, field := range oneof.Fields {
|
|
|
|
g.P("func (*", fieldOneofType(field), ") ", ifName, "() {}")
|
|
|
|
g.P()
|
|
|
|
}
|
2019-04-08 14:03:15 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// isFirstOneofField reports whether this is the first field in a oneof.
|
|
|
|
func isFirstOneofField(field *protogen.Field) bool {
|
2019-04-15 23:39:09 -07:00
|
|
|
return field.Oneof != nil && field.Oneof.Fields[0] == field
|
2018-11-26 12:57:27 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// oneofFieldName returns the name of the struct field holding the oneof value.
|
|
|
|
//
|
|
|
|
// This function is trivial, but pulling out the name like this makes it easier
|
|
|
|
// to experiment with alternative oneof implementations.
|
|
|
|
func oneofFieldName(oneof *protogen.Oneof) string {
|
|
|
|
return oneof.GoName
|
|
|
|
}
|
|
|
|
|
|
|
|
// oneofInterfaceName returns the name of the interface type implemented by
|
|
|
|
// the oneof field value types.
|
|
|
|
func oneofInterfaceName(oneof *protogen.Oneof) string {
|
2019-04-15 23:39:09 -07:00
|
|
|
return fmt.Sprintf("is%s_%s", oneof.Parent.GoIdent.GoName, oneof.GoName)
|
2018-11-26 12:57:27 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// fieldOneofType returns the wrapper type used to represent a field in a oneof.
|
|
|
|
func fieldOneofType(field *protogen.Field) protogen.GoIdent {
|
|
|
|
ident := protogen.GoIdent{
|
2019-04-15 23:39:09 -07:00
|
|
|
GoImportPath: field.Parent.GoIdent.GoImportPath,
|
|
|
|
GoName: field.Parent.GoIdent.GoName + "_" + field.GoName,
|
2018-11-26 12:57:27 -08:00
|
|
|
}
|
|
|
|
// Check for collisions with nested messages or enums.
|
|
|
|
//
|
|
|
|
// This conflict resolution is incomplete: Among other things, it
|
|
|
|
// does not consider collisions with other oneof field types.
|
|
|
|
//
|
|
|
|
// TODO: Consider dropping this entirely. Detecting conflicts and
|
|
|
|
// producing an error is almost certainly better than permuting
|
|
|
|
// field and type names in mostly unpredictable ways.
|
|
|
|
Loop:
|
|
|
|
for {
|
2019-04-15 23:39:09 -07:00
|
|
|
for _, message := range field.Parent.Messages {
|
2018-11-26 12:57:27 -08:00
|
|
|
if message.GoIdent == ident {
|
|
|
|
ident.GoName += "_"
|
|
|
|
continue Loop
|
|
|
|
}
|
|
|
|
}
|
2019-04-15 23:39:09 -07:00
|
|
|
for _, enum := range field.Parent.Enums {
|
2018-11-26 12:57:27 -08:00
|
|
|
if enum.GoIdent == ident {
|
|
|
|
ident.GoName += "_"
|
|
|
|
continue Loop
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ident
|
|
|
|
}
|
|
|
|
}
|