reflect/protoreflect: improve source information usability
Added API:
SourceLocations.ByPath
SourceLocations.ByDescriptor
SourceLocation.Next
SourcePath.String
SourcePath.Equal
We modify compiler/protogen to use SourceLocations.ByDescriptor.
In retrospect, if this had existed during the development of protogen,
we would not have exposed protogen.Location and related fields.
Change-Id: I58f17e59f90b9ba16f0982c4b71c2542e4ff6e75
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/238000
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/cmd/protoc-gen-go/internal_gengo/main.go b/cmd/protoc-gen-go/internal_gengo/main.go
index ea5d2e6..5f55976 100644
--- a/cmd/protoc-gen-go/internal_gengo/main.go
+++ b/cmd/protoc-gen-go/internal_gengo/main.go
@@ -115,17 +115,14 @@
// genStandaloneComments prints all leading comments for a FileDescriptorProto
// location identified by the field number n.
func genStandaloneComments(g *protogen.GeneratedFile, f *fileInfo, n int32) {
- for _, loc := range f.Proto.GetSourceCodeInfo().GetLocation() {
- if len(loc.Path) == 1 && loc.Path[0] == n {
- for _, s := range loc.GetLeadingDetachedComments() {
- g.P(protogen.Comments(s))
- g.P()
- }
- if s := loc.GetLeadingComments(); s != "" {
- g.P(protogen.Comments(s))
- g.P()
- }
- }
+ loc := f.Desc.SourceLocations().ByPath(protoreflect.SourcePath{n})
+ for _, s := range loc.LeadingDetachedComments {
+ g.P(protogen.Comments(s))
+ g.P()
+ }
+ if s := loc.LeadingComments; s != "" {
+ g.P(protogen.Comments(s))
+ g.P()
}
}
diff --git a/compiler/protogen/protogen.go b/compiler/protogen/protogen.go
index 3892d05..c380a03 100644
--- a/compiler/protogen/protogen.go
+++ b/compiler/protogen/protogen.go
@@ -13,7 +13,6 @@
import (
"bufio"
"bytes"
- "encoding/binary"
"fmt"
"go/ast"
"go/parser"
@@ -482,7 +481,7 @@
// of "dir/foo". Appending ".pb.go" produces an output file of "dir/foo.pb.go".
GeneratedFilenamePrefix string
- comments map[pathKey]CommentSet
+ location Location
}
func newFile(gen *Plugin, p *descriptorpb.FileDescriptorProto, packageName GoPackageName, importPath GoImportPath) (*File, error) {
@@ -498,7 +497,7 @@
Proto: p,
GoPackageName: packageName,
GoImportPath: importPath,
- comments: make(map[pathKey]CommentSet),
+ location: Location{SourceFile: desc.Path()},
}
// Determine the prefix for generated Go files.
@@ -526,19 +525,6 @@
}
f.GeneratedFilenamePrefix = prefix
- for _, loc := range p.GetSourceCodeInfo().GetLocation() {
- // Descriptors declarations are guaranteed to have unique comment sets.
- // Other locations may not be unique, but we don't use them.
- var leadingDetached []Comments
- for _, s := range loc.GetLeadingDetachedComments() {
- leadingDetached = append(leadingDetached, Comments(s))
- }
- f.comments[newPathKey(loc.Path)] = CommentSet{
- LeadingDetached: leadingDetached,
- Leading: Comments(loc.GetLeadingComments()),
- Trailing: Comments(loc.GetTrailingComments()),
- }
- }
for i, eds := 0, desc.Enums(); i < eds.Len(); i++ {
f.Enums = append(f.Enums, newEnum(gen, f, nil, eds.Get(i)))
}
@@ -571,13 +557,6 @@
return f, nil
}
-func (f *File) location(idxPath ...int32) Location {
- return Location{
- SourceFile: f.Desc.Path(),
- Path: idxPath,
- }
-}
-
// goPackageOption interprets a file's go_package option.
// If there is no go_package, it returns ("", "").
// If there's a simple name, it returns (pkg, "").
@@ -625,15 +604,15 @@
func newEnum(gen *Plugin, f *File, parent *Message, desc protoreflect.EnumDescriptor) *Enum {
var loc Location
if parent != nil {
- loc = parent.Location.appendPath(int32(genid.DescriptorProto_EnumType_field_number), int32(desc.Index()))
+ loc = parent.Location.appendPath(genid.DescriptorProto_EnumType_field_number, desc.Index())
} else {
- loc = f.location(int32(genid.FileDescriptorProto_EnumType_field_number), int32(desc.Index()))
+ loc = f.location.appendPath(genid.FileDescriptorProto_EnumType_field_number, desc.Index())
}
enum := &Enum{
Desc: desc,
GoIdent: newGoIdent(f, desc),
Location: loc,
- Comments: f.comments[newPathKey(loc.Path)],
+ Comments: makeCommentSet(f.Desc.SourceLocations().ByDescriptor(desc)),
}
gen.enumsByName[desc.FullName()] = enum
for i, vds := 0, enum.Desc.Values(); i < vds.Len(); i++ {
@@ -664,13 +643,13 @@
parentIdent = message.GoIdent
}
name := parentIdent.GoName + "_" + string(desc.Name())
- loc := enum.Location.appendPath(int32(genid.EnumDescriptorProto_Value_field_number), int32(desc.Index()))
+ loc := enum.Location.appendPath(genid.EnumDescriptorProto_Value_field_number, desc.Index())
return &EnumValue{
Desc: desc,
GoIdent: f.GoImportPath.Ident(name),
Parent: enum,
Location: loc,
- Comments: f.comments[newPathKey(loc.Path)],
+ Comments: makeCommentSet(f.Desc.SourceLocations().ByDescriptor(desc)),
}
}
@@ -694,15 +673,15 @@
func newMessage(gen *Plugin, f *File, parent *Message, desc protoreflect.MessageDescriptor) *Message {
var loc Location
if parent != nil {
- loc = parent.Location.appendPath(int32(genid.DescriptorProto_NestedType_field_number), int32(desc.Index()))
+ loc = parent.Location.appendPath(genid.DescriptorProto_NestedType_field_number, desc.Index())
} else {
- loc = f.location(int32(genid.FileDescriptorProto_MessageType_field_number), int32(desc.Index()))
+ loc = f.location.appendPath(genid.FileDescriptorProto_MessageType_field_number, desc.Index())
}
message := &Message{
Desc: desc,
GoIdent: newGoIdent(f, desc),
Location: loc,
- Comments: f.comments[newPathKey(loc.Path)],
+ Comments: makeCommentSet(f.Desc.SourceLocations().ByDescriptor(desc)),
}
gen.messagesByName[desc.FullName()] = message
for i, eds := 0, desc.Enums(); i < eds.Len(); i++ {
@@ -852,11 +831,11 @@
var loc Location
switch {
case desc.IsExtension() && message == nil:
- loc = f.location(int32(genid.FileDescriptorProto_Extension_field_number), int32(desc.Index()))
+ loc = f.location.appendPath(genid.FileDescriptorProto_Extension_field_number, desc.Index())
case desc.IsExtension() && message != nil:
- loc = message.Location.appendPath(int32(genid.DescriptorProto_Extension_field_number), int32(desc.Index()))
+ loc = message.Location.appendPath(genid.DescriptorProto_Extension_field_number, desc.Index())
default:
- loc = message.Location.appendPath(int32(genid.DescriptorProto_Field_field_number), int32(desc.Index()))
+ loc = message.Location.appendPath(genid.DescriptorProto_Field_field_number, desc.Index())
}
camelCased := strs.GoCamelCase(string(desc.Name()))
var parentPrefix string
@@ -872,7 +851,7 @@
},
Parent: message,
Location: loc,
- Comments: f.comments[newPathKey(loc.Path)],
+ Comments: makeCommentSet(f.Desc.SourceLocations().ByDescriptor(desc)),
}
return field
}
@@ -927,7 +906,7 @@
}
func newOneof(gen *Plugin, f *File, message *Message, desc protoreflect.OneofDescriptor) *Oneof {
- loc := message.Location.appendPath(int32(genid.DescriptorProto_OneofDecl_field_number), int32(desc.Index()))
+ loc := message.Location.appendPath(genid.DescriptorProto_OneofDecl_field_number, desc.Index())
camelCased := strs.GoCamelCase(string(desc.Name()))
parentPrefix := message.GoIdent.GoName + "_"
return &Oneof{
@@ -939,7 +918,7 @@
GoName: parentPrefix + camelCased,
},
Location: loc,
- Comments: f.comments[newPathKey(loc.Path)],
+ Comments: makeCommentSet(f.Desc.SourceLocations().ByDescriptor(desc)),
}
}
@@ -959,12 +938,12 @@
}
func newService(gen *Plugin, f *File, desc protoreflect.ServiceDescriptor) *Service {
- loc := f.location(int32(genid.FileDescriptorProto_Service_field_number), int32(desc.Index()))
+ loc := f.location.appendPath(genid.FileDescriptorProto_Service_field_number, desc.Index())
service := &Service{
Desc: desc,
GoName: strs.GoCamelCase(string(desc.Name())),
Location: loc,
- Comments: f.comments[newPathKey(loc.Path)],
+ Comments: makeCommentSet(f.Desc.SourceLocations().ByDescriptor(desc)),
}
for i, mds := 0, desc.Methods(); i < mds.Len(); i++ {
service.Methods = append(service.Methods, newMethod(gen, f, service, mds.Get(i)))
@@ -988,13 +967,13 @@
}
func newMethod(gen *Plugin, f *File, service *Service, desc protoreflect.MethodDescriptor) *Method {
- loc := service.Location.appendPath(int32(genid.ServiceDescriptorProto_Method_field_number), int32(desc.Index()))
+ loc := service.Location.appendPath(genid.ServiceDescriptorProto_Method_field_number, desc.Index())
method := &Method{
Desc: desc,
GoName: strs.GoCamelCase(string(desc.Name())),
Parent: service,
Location: loc,
- Comments: f.comments[newPathKey(loc.Path)],
+ Comments: makeCommentSet(f.Desc.SourceLocations().ByDescriptor(desc)),
}
return method
}
@@ -1359,28 +1338,10 @@
}
// appendPath add elements to a Location's path, returning a new Location.
-func (loc Location) appendPath(a ...int32) Location {
- var n protoreflect.SourcePath
- n = append(n, loc.Path...)
- n = append(n, a...)
- return Location{
- SourceFile: loc.SourceFile,
- Path: n,
- }
-}
-
-// A pathKey is a representation of a location path suitable for use as a map key.
-type pathKey struct {
- s string
-}
-
-// newPathKey converts a location path to a pathKey.
-func newPathKey(idxPath []int32) pathKey {
- buf := make([]byte, 4*len(idxPath))
- for i, x := range idxPath {
- binary.LittleEndian.PutUint32(buf[i*4:], uint32(x))
- }
- return pathKey{string(buf)}
+func (loc Location) appendPath(num protoreflect.FieldNumber, idx int) Location {
+ loc.Path = append(protoreflect.SourcePath(nil), loc.Path...) // make copy
+ loc.Path = append(loc.Path, int32(num), int32(idx))
+ return loc
}
// CommentSet is a set of leading and trailing comments associated
@@ -1391,6 +1352,18 @@
Trailing Comments
}
+func makeCommentSet(loc protoreflect.SourceLocation) CommentSet {
+ var leadingDetached []Comments
+ for _, s := range loc.LeadingDetachedComments {
+ leadingDetached = append(leadingDetached, Comments(s))
+ }
+ return CommentSet{
+ LeadingDetached: leadingDetached,
+ Leading: Comments(loc.LeadingComments),
+ Trailing: Comments(loc.TrailingComments),
+ }
+}
+
// Comments is a comments string as provided by protoc.
type Comments string
diff --git a/internal/cmd/generate-protos/main.go b/internal/cmd/generate-protos/main.go
index 48dda63..f8778d7 100644
--- a/internal/cmd/generate-protos/main.go
+++ b/internal/cmd/generate-protos/main.go
@@ -93,6 +93,7 @@
gengo.GenerateVersionMarkers = false
gengo.GenerateFile(gen, file)
generateIdentifiers(gen, file)
+ generateSouceContextStringer(gen, file)
}
}
gen.SupportedFeatures = gengo.SupportedFeatures
@@ -361,6 +362,66 @@
processMessages(file.Messages)
}
+// generateSouceContextStringer generates the implementation for the
+// protoreflect.SourcePath.String method by using information present
+// in the descriptor.proto.
+func generateSouceContextStringer(gen *protogen.Plugin, file *protogen.File) {
+ if file.Desc.Path() != "google/protobuf/descriptor.proto" {
+ return
+ }
+
+ importPath := modulePath + "/reflect/protoreflect"
+ g := gen.NewGeneratedFile(importPath+"/source_gen.go", protogen.GoImportPath(importPath))
+ for _, s := range generatedPreamble {
+ g.P(s)
+ }
+ g.P("package ", path.Base(importPath))
+ g.P()
+
+ var messages []*protogen.Message
+ for _, message := range file.Messages {
+ if message.Desc.Name() == "FileDescriptorProto" {
+ messages = append(messages, message)
+ }
+ }
+ seen := make(map[*protogen.Message]bool)
+
+ for len(messages) > 0 {
+ m := messages[0]
+ messages = messages[1:]
+ if seen[m] {
+ continue
+ }
+ seen[m] = true
+
+ g.P("func (p *SourcePath) append", m.GoIdent.GoName, "(b []byte) []byte {")
+ g.P("if len(*p) == 0 { return b }")
+ g.P("switch (*p)[0] {")
+ for _, f := range m.Fields {
+ g.P("case ", f.Desc.Number(), ":")
+ var cardinality string
+ switch {
+ case f.Desc.IsMap():
+ panic("maps are not supported")
+ case f.Desc.IsList():
+ cardinality = "Repeated"
+ default:
+ cardinality = "Singular"
+ }
+ nextAppender := "nil"
+ if f.Message != nil {
+ nextAppender = "(*SourcePath).append" + f.Message.GoIdent.GoName
+ messages = append(messages, f.Message)
+ }
+ g.P("b = p.append", cardinality, "Field(b, ", strconv.Quote(string(f.Desc.Name())), ", ", nextAppender, ")")
+ }
+ g.P("}")
+ g.P("return b")
+ g.P("}")
+ g.P()
+ }
+}
+
func syncOutput(dstDir, srcDir string) {
filepath.Walk(srcDir, func(srcPath string, _ os.FileInfo, _ error) error {
if !strings.HasSuffix(srcPath, ".go") && !strings.HasSuffix(srcPath, ".meta") {
diff --git a/internal/filedesc/build.go b/internal/filedesc/build.go
index d02d770..b293b69 100644
--- a/internal/filedesc/build.go
+++ b/internal/filedesc/build.go
@@ -3,6 +3,9 @@
// license that can be found in the LICENSE file.
// Package filedesc provides functionality for constructing descriptors.
+//
+// The types in this package implement interfaces in the protoreflect package
+// related to protobuf descripriptors.
package filedesc
import (
diff --git a/internal/filedesc/desc_list.go b/internal/filedesc/desc_list.go
index 4187d66..aa294ff 100644
--- a/internal/filedesc/desc_list.go
+++ b/internal/filedesc/desc_list.go
@@ -6,9 +6,12 @@
import (
"fmt"
+ "math"
"sort"
"sync"
+ "google.golang.org/protobuf/internal/genid"
+
"google.golang.org/protobuf/encoding/protowire"
"google.golang.org/protobuf/internal/descfmt"
"google.golang.org/protobuf/internal/errors"
@@ -278,9 +281,170 @@
}
type SourceLocations struct {
+ // List is a list of SourceLocations.
+ // The SourceLocation.Next field does not need to be populated
+ // as it will be lazily populated upon first need.
List []pref.SourceLocation
+
+ // File is the parent file descriptor that these locations are relative to.
+ // If non-nil, ByDescriptor verifies that the provided descriptor
+ // is a child of this file descriptor.
+ File pref.FileDescriptor
+
+ once sync.Once
+ byPath map[pathKey]int
}
-func (p *SourceLocations) Len() int { return len(p.List) }
-func (p *SourceLocations) Get(i int) pref.SourceLocation { return p.List[i] }
+func (p *SourceLocations) Len() int { return len(p.List) }
+func (p *SourceLocations) Get(i int) pref.SourceLocation { return p.lazyInit().List[i] }
+func (p *SourceLocations) byKey(k pathKey) pref.SourceLocation {
+ if i, ok := p.lazyInit().byPath[k]; ok {
+ return p.List[i]
+ }
+ return pref.SourceLocation{}
+}
+func (p *SourceLocations) ByPath(path pref.SourcePath) pref.SourceLocation {
+ return p.byKey(newPathKey(path))
+}
+func (p *SourceLocations) ByDescriptor(desc pref.Descriptor) pref.SourceLocation {
+ if p.File != nil && desc != nil && p.File != desc.ParentFile() {
+ return pref.SourceLocation{} // mismatching parent files
+ }
+ var pathArr [16]int32
+ path := pathArr[:0]
+ for {
+ switch desc.(type) {
+ case pref.FileDescriptor:
+ // Reverse the path since it was constructed in reverse.
+ for i, j := 0, len(path)-1; i < j; i, j = i+1, j-1 {
+ path[i], path[j] = path[j], path[i]
+ }
+ return p.byKey(newPathKey(path))
+ case pref.MessageDescriptor:
+ path = append(path, int32(desc.Index()))
+ desc = desc.Parent()
+ switch desc.(type) {
+ case pref.FileDescriptor:
+ path = append(path, int32(genid.FileDescriptorProto_MessageType_field_number))
+ case pref.MessageDescriptor:
+ path = append(path, int32(genid.DescriptorProto_NestedType_field_number))
+ default:
+ return pref.SourceLocation{}
+ }
+ case pref.FieldDescriptor:
+ isExtension := desc.(pref.FieldDescriptor).IsExtension()
+ path = append(path, int32(desc.Index()))
+ desc = desc.Parent()
+ if isExtension {
+ switch desc.(type) {
+ case pref.FileDescriptor:
+ path = append(path, int32(genid.FileDescriptorProto_Extension_field_number))
+ case pref.MessageDescriptor:
+ path = append(path, int32(genid.DescriptorProto_Extension_field_number))
+ default:
+ return pref.SourceLocation{}
+ }
+ } else {
+ switch desc.(type) {
+ case pref.MessageDescriptor:
+ path = append(path, int32(genid.DescriptorProto_Field_field_number))
+ default:
+ return pref.SourceLocation{}
+ }
+ }
+ case pref.OneofDescriptor:
+ path = append(path, int32(desc.Index()))
+ desc = desc.Parent()
+ switch desc.(type) {
+ case pref.MessageDescriptor:
+ path = append(path, int32(genid.DescriptorProto_OneofDecl_field_number))
+ default:
+ return pref.SourceLocation{}
+ }
+ case pref.EnumDescriptor:
+ path = append(path, int32(desc.Index()))
+ desc = desc.Parent()
+ switch desc.(type) {
+ case pref.FileDescriptor:
+ path = append(path, int32(genid.FileDescriptorProto_EnumType_field_number))
+ case pref.MessageDescriptor:
+ path = append(path, int32(genid.DescriptorProto_EnumType_field_number))
+ default:
+ return pref.SourceLocation{}
+ }
+ case pref.EnumValueDescriptor:
+ path = append(path, int32(desc.Index()))
+ desc = desc.Parent()
+ switch desc.(type) {
+ case pref.EnumDescriptor:
+ path = append(path, int32(genid.EnumDescriptorProto_Value_field_number))
+ default:
+ return pref.SourceLocation{}
+ }
+ case pref.ServiceDescriptor:
+ path = append(path, int32(desc.Index()))
+ desc = desc.Parent()
+ switch desc.(type) {
+ case pref.FileDescriptor:
+ path = append(path, int32(genid.FileDescriptorProto_Service_field_number))
+ default:
+ return pref.SourceLocation{}
+ }
+ case pref.MethodDescriptor:
+ path = append(path, int32(desc.Index()))
+ desc = desc.Parent()
+ switch desc.(type) {
+ case pref.ServiceDescriptor:
+ path = append(path, int32(genid.ServiceDescriptorProto_Method_field_number))
+ default:
+ return pref.SourceLocation{}
+ }
+ default:
+ return pref.SourceLocation{}
+ }
+ }
+}
+func (p *SourceLocations) lazyInit() *SourceLocations {
+ p.once.Do(func() {
+ if len(p.List) > 0 {
+ // Collect all the indexes for a given path.
+ pathIdxs := make(map[pathKey][]int, len(p.List))
+ for i, l := range p.List {
+ k := newPathKey(l.Path)
+ pathIdxs[k] = append(pathIdxs[k], i)
+ }
+
+ // Update the next index for all locations.
+ p.byPath = make(map[pathKey]int, len(p.List))
+ for k, idxs := range pathIdxs {
+ for i := 0; i < len(idxs)-1; i++ {
+ p.List[idxs[i]].Next = idxs[i+1]
+ }
+ p.List[idxs[len(idxs)-1]].Next = 0
+ p.byPath[k] = idxs[0] // record the first location for this path
+ }
+ }
+ })
+ return p
+}
func (p *SourceLocations) ProtoInternal(pragma.DoNotImplement) {}
+
+// pathKey is a comparable representation of protoreflect.SourcePath.
+type pathKey struct {
+ arr [16]uint8 // first n-1 path segments; last element is the length
+ str string // used if the path does not fit in arr
+}
+
+func newPathKey(p pref.SourcePath) (k pathKey) {
+ if len(p) < len(k.arr) {
+ for i, ps := range p {
+ if ps < 0 || math.MaxUint8 <= ps {
+ return pathKey{str: p.String()}
+ }
+ k.arr[i] = uint8(ps)
+ }
+ k.arr[len(k.arr)-1] = uint8(len(p))
+ return k
+ }
+ return pathKey{str: p.String()}
+}
diff --git a/reflect/protodesc/desc.go b/reflect/protodesc/desc.go
index 37f254d..e4dfb12 100644
--- a/reflect/protodesc/desc.go
+++ b/reflect/protodesc/desc.go
@@ -144,6 +144,7 @@
}
// Handle source locations.
+ f.L2.Locations.File = f
for _, loc := range fd.GetSourceCodeInfo().GetLocation() {
var l protoreflect.SourceLocation
// TODO: Validate that the path points to an actual declaration?
diff --git a/reflect/protodesc/file_test.go b/reflect/protodesc/file_test.go
index 9b7e8dd..50a0ed9 100644
--- a/reflect/protodesc/file_test.go
+++ b/reflect/protodesc/file_test.go
@@ -994,3 +994,241 @@
t.Fatal("NewFiles with import cycle: success, want error")
}
}
+
+func TestSourceLocations(t *testing.T) {
+ fd := mustParseFile(`
+ name: "comments.proto"
+ message_type: [{
+ name: "Message1"
+ field: [
+ {name:"field1" number:1 label:LABEL_OPTIONAL type:TYPE_STRING},
+ {name:"field2" number:2 label:LABEL_OPTIONAL type:TYPE_STRING},
+ {name:"field3" number:3 label:LABEL_OPTIONAL type:TYPE_STRING oneof_index:0},
+ {name:"field4" number:4 label:LABEL_OPTIONAL type:TYPE_STRING oneof_index:0},
+ {name:"field5" number:5 label:LABEL_OPTIONAL type:TYPE_STRING oneof_index:1},
+ {name:"field6" number:6 label:LABEL_OPTIONAL type:TYPE_STRING oneof_index:1}
+ ]
+ extension: [
+ {name:"extension1" number:100 label:LABEL_OPTIONAL type:TYPE_STRING extendee:".Message1"},
+ {name:"extension2" number:101 label:LABEL_OPTIONAL type:TYPE_STRING extendee:".Message1"}
+ ]
+ nested_type: [{name:"Message1"}, {name:"Message2"}]
+ extension_range: {start:100 end:536870912}
+ oneof_decl: [{name:"oneof1"}, {name:"oneof2"}]
+ }, {
+ name: "Message2"
+ enum_type: {
+ name: "Enum1"
+ value: [
+ {name: "FOO", number: 0},
+ {name: "BAR", number: 1}
+ ]
+ }
+ }]
+ enum_type: {
+ name: "Enum1"
+ value: [
+ {name: "FOO", number: 0},
+ {name: "BAR", number: 1}
+ ]
+ }
+ service: {
+ name: "Service1"
+ method: [
+ {name:"Method1" input_type:".Message1" output_type:".Message1"},
+ {name:"Method2" input_type:".Message2" output_type:".Message2"}
+ ]
+ }
+ extension: [
+ {name:"extension1" number:102 label:LABEL_OPTIONAL type:TYPE_STRING extendee:".Message1"},
+ {name:"extension2" number:103 label:LABEL_OPTIONAL type:TYPE_STRING extendee:".Message1"}
+ ]
+ source_code_info: {
+ location: [
+ {span:[0,0,69,1]},
+ {path:[12] span:[0,0,18]},
+ {path:[5,0] span:[3,0,8,1] leading_comments:" Enum1\r\n"},
+ {path:[5,0,1] span:[3,5,10]},
+ {path:[5,0,2,0] span:[5,2,10] leading_comments:" FOO\r\n"},
+ {path:[5,0,2,0,1] span:[5,2,5]},
+ {path:[5,0,2,0,2] span:[5,8,9]},
+ {path:[5,0,2,1] span:[7,2,10] leading_comments:" BAR\r\n"},
+ {path:[5,0,2,1,1] span:[7,2,5]},
+ {path:[5,0,2,1,2] span:[7,8,9]},
+ {path:[4,0] span:[11,0,43,1] leading_comments:" Message1\r\n"},
+ {path:[4,0,1] span:[11,8,16]},
+ {path:[4,0,3,0] span:[13,2,21] leading_comments:" Message1.Message1\r\n"},
+ {path:[4,0,3,0,1] span:[13,10,18]},
+ {path:[4,0,3,1] span:[15,2,21] leading_comments:" Message1.Message2\r\n"},
+ {path:[4,0,3,1,1] span:[15,10,18]},
+ {path:[4,0,2,0] span:[18,2,29] leading_comments:" Message1.field1\r\n"},
+ {path:[4,0,2,0,4] span:[18,2,10]},
+ {path:[4,0,2,0,5] span:[18,11,17]},
+ {path:[4,0,2,0,1] span:[18,18,24]},
+ {path:[4,0,2,0,3] span:[18,27,28]},
+ {path:[4,0,2,1] span:[20,2,29] leading_comments:" Message1.field2\r\n"},
+ {path:[4,0,2,1,4] span:[20,2,10]},
+ {path:[4,0,2,1,5] span:[20,11,17]},
+ {path:[4,0,2,1,1] span:[20,18,24]},
+ {path:[4,0,2,1,3] span:[20,27,28]},
+ {path:[4,0,8,0] span:[22,2,27,3] leading_comments:" Message1.oneof1\r\n"},
+ {path:[4,0,8,0,1] span:[22,8,14]},
+ {path:[4,0,2,2] span:[24,4,22] leading_comments:" Message1.field3\r\n"},
+ {path:[4,0,2,2,5] span:[24,4,10]},
+ {path:[4,0,2,2,1] span:[24,11,17]},
+ {path:[4,0,2,2,3] span:[24,20,21]},
+ {path:[4,0,2,3] span:[26,4,22] leading_comments:" Message1.field4\r\n"},
+ {path:[4,0,2,3,5] span:[26,4,10]},
+ {path:[4,0,2,3,1] span:[26,11,17]},
+ {path:[4,0,2,3,3] span:[26,20,21]},
+ {path:[4,0,8,1] span:[29,2,34,3] leading_comments:" Message1.oneof2\r\n"},
+ {path:[4,0,8,1,1] span:[29,8,14]},
+ {path:[4,0,2,4] span:[31,4,22] leading_comments:" Message1.field5\r\n"},
+ {path:[4,0,2,4,5] span:[31,4,10]},
+ {path:[4,0,2,4,1] span:[31,11,17]},
+ {path:[4,0,2,4,3] span:[31,20,21]},
+ {path:[4,0,2,5] span:[33,4,22] leading_comments:" Message1.field6\r\n"},
+ {path:[4,0,2,5,5] span:[33,4,10]},
+ {path:[4,0,2,5,1] span:[33,11,17]},
+ {path:[4,0,2,5,3] span:[33,20,21]},
+ {path:[4,0,5] span:[36,2,24]},
+ {path:[4,0,5,0] span:[36,13,23]},
+ {path:[4,0,5,0,1] span:[36,13,16]},
+ {path:[4,0,5,0,2] span:[36,20,23]},
+ {path:[4,0,6] span:[37,2,42,3]},
+ {path:[4,0,6,0] span:[39,4,37] leading_comments:" Message1.extension1\r\n"},
+ {path:[4,0,6,0,2] span:[37,9,18]},
+ {path:[4,0,6,0,4] span:[39,4,12]},
+ {path:[4,0,6,0,5] span:[39,13,19]},
+ {path:[4,0,6,0,1] span:[39,20,30]},
+ {path:[4,0,6,0,3] span:[39,33,36]},
+ {path:[4,0,6,1] span:[41,4,37] leading_comments:" Message1.extension2\r\n"},
+ {path:[4,0,6,1,2] span:[37,9,18]},
+ {path:[4,0,6,1,4] span:[41,4,12]},
+ {path:[4,0,6,1,5] span:[41,13,19]},
+ {path:[4,0,6,1,1] span:[41,20,30]},
+ {path:[4,0,6,1,3] span:[41,33,36]},
+ {path:[7] span:[45,0,50,1]},
+ {path:[7,0] span:[47,2,35] leading_comments:" extension1\r\n"},
+ {path:[7,0,2] span:[45,7,15]},
+ {path:[7,0,4] span:[47,2,10]},
+ {path:[7,0,5] span:[47,11,17]},
+ {path:[7,0,1] span:[47,18,28]},
+ {path:[7,0,3] span:[47,31,34]},
+ {path:[7,1] span:[49,2,35] leading_comments:" extension2\r\n"},
+ {path:[7,1,2] span:[45,7,15]},
+ {path:[7,1,4] span:[49,2,10]},
+ {path:[7,1,5] span:[49,11,17]},
+ {path:[7,1,1] span:[49,18,28]},
+ {path:[7,1,3] span:[49,31,34]},
+ {path:[4,1] span:[53,0,61,1] leading_comments:" Message2\r\n"},
+ {path:[4,1,1] span:[53,8,16]},
+ {path:[4,1,4,0] span:[55,2,60,3] leading_comments:" Message2.Enum1\r\n"},
+ {path:[4,1,4,0,1] span:[55,7,12]},
+ {path:[4,1,4,0,2,0] span:[57,4,12] leading_comments:" Message2.FOO\r\n"},
+ {path:[4,1,4,0,2,0,1] span:[57,4,7]},
+ {path:[4,1,4,0,2,0,2] span:[57,10,11]},
+ {path:[4,1,4,0,2,1] span:[59,4,12] leading_comments:" Message2.BAR\r\n"},
+ {path:[4,1,4,0,2,1,1] span:[59,4,7]},
+ {path:[4,1,4,0,2,1,2] span:[59,10,11]},
+ {path:[6,0] span:[64,0,69,1] leading_comments:" Service1\r\n"},
+ {path:[6,0,1] span:[64,8,16]},
+ {path:[6,0,2,0] span:[66,2,43] leading_comments:" Service1.Method1\r\n"},
+ {path:[6,0,2,0,1] span:[66,6,13]},
+ {path:[6,0,2,0,2] span:[66,14,22]},
+ {path:[6,0,2,0,3] span:[66,33,41]},
+ {path:[6,0,2,1] span:[68,2,43] leading_comments:" Service1.Method2\r\n"},
+ {path:[6,0,2,1,1] span:[68,6,13]},
+ {path:[6,0,2,1,2] span:[68,14,22]},
+ {path:[6,0,2,1,3] span:[68,33,41]}
+ ]
+ }
+ `)
+ fileDesc, err := NewFile(fd, nil)
+ if err != nil {
+ t.Fatalf("NewFile error: %v", err)
+ }
+
+ var walkDescs func(protoreflect.Descriptor, func(protoreflect.Descriptor))
+ walkDescs = func(d protoreflect.Descriptor, f func(protoreflect.Descriptor)) {
+ f(d)
+ if d, ok := d.(interface {
+ Enums() protoreflect.EnumDescriptors
+ }); ok {
+ eds := d.Enums()
+ for i := 0; i < eds.Len(); i++ {
+ walkDescs(eds.Get(i), f)
+ }
+ }
+ if d, ok := d.(interface {
+ Values() protoreflect.EnumValueDescriptors
+ }); ok {
+ vds := d.Values()
+ for i := 0; i < vds.Len(); i++ {
+ walkDescs(vds.Get(i), f)
+ }
+ }
+ if d, ok := d.(interface {
+ Messages() protoreflect.MessageDescriptors
+ }); ok {
+ mds := d.Messages()
+ for i := 0; i < mds.Len(); i++ {
+ walkDescs(mds.Get(i), f)
+ }
+ }
+ if d, ok := d.(interface {
+ Fields() protoreflect.FieldDescriptors
+ }); ok {
+ fds := d.Fields()
+ for i := 0; i < fds.Len(); i++ {
+ walkDescs(fds.Get(i), f)
+ }
+ }
+ if d, ok := d.(interface {
+ Oneofs() protoreflect.OneofDescriptors
+ }); ok {
+ ods := d.Oneofs()
+ for i := 0; i < ods.Len(); i++ {
+ walkDescs(ods.Get(i), f)
+ }
+ }
+ if d, ok := d.(interface {
+ Extensions() protoreflect.ExtensionDescriptors
+ }); ok {
+ xds := d.Extensions()
+ for i := 0; i < xds.Len(); i++ {
+ walkDescs(xds.Get(i), f)
+ }
+ }
+ if d, ok := d.(interface {
+ Services() protoreflect.ServiceDescriptors
+ }); ok {
+ sds := d.Services()
+ for i := 0; i < sds.Len(); i++ {
+ walkDescs(sds.Get(i), f)
+ }
+ }
+ if d, ok := d.(interface {
+ Methods() protoreflect.MethodDescriptors
+ }); ok {
+ mds := d.Methods()
+ for i := 0; i < mds.Len(); i++ {
+ walkDescs(mds.Get(i), f)
+ }
+ }
+ }
+
+ var numDescs int
+ walkDescs(fileDesc, func(d protoreflect.Descriptor) {
+ // The comment for every descriptor should be the full name itself.
+ got := strings.TrimSpace(fileDesc.SourceLocations().ByDescriptor(d).LeadingComments)
+ want := string(d.FullName())
+ if got != want {
+ t.Errorf("comment mismatch: got %v, want %v", got, want)
+ }
+ numDescs++
+ })
+ if numDescs != 30 {
+ t.Errorf("visited %d descriptor, expected 30", numDescs)
+ }
+}
diff --git a/reflect/protoreflect/source.go b/reflect/protoreflect/source.go
index 32ea3d9..121ba3a 100644
--- a/reflect/protoreflect/source.go
+++ b/reflect/protoreflect/source.go
@@ -4,6 +4,10 @@
package protoreflect
+import (
+ "strconv"
+)
+
// SourceLocations is a list of source locations.
type SourceLocations interface {
// Len reports the number of source locations in the proto file.
@@ -11,9 +15,20 @@
// Get returns the ith SourceLocation. It panics if out of bounds.
Get(int) SourceLocation
- doNotImplement
+ // ByPath returns the SourceLocation for the given path,
+ // returning the first location if multiple exist for the same path.
+ // If multiple locations exist for the same path,
+ // then SourceLocation.Next index can be used to identify the
+ // index of the next SourceLocation.
+ // If no location exists for this path, it returns the zero value.
+ ByPath(path SourcePath) SourceLocation
- // TODO: Add ByPath and ByDescriptor helper methods.
+ // ByDescriptor returns the SourceLocation for the given descriptor,
+ // returning the first location if multiple exist for the same path.
+ // If no location exists for this descriptor, it returns the zero value.
+ ByDescriptor(desc Descriptor) SourceLocation
+
+ doNotImplement
}
// SourceLocation describes a source location and
@@ -39,6 +54,10 @@
LeadingComments string
// TrailingComments is the trailing attached comment for the declaration.
TrailingComments string
+
+ // Next is an index into SourceLocations for the next source location that
+ // has the same Path. It is zero if there is no next location.
+ Next int
}
// SourcePath identifies part of a file descriptor for a source location.
@@ -48,5 +67,62 @@
// See google.protobuf.SourceCodeInfo.Location.path.
type SourcePath []int32
-// TODO: Add SourcePath.String method to pretty-print the path. For example:
-// ".message_type[6].nested_type[15].field[3]"
+// Equal reports whether p1 equals p2.
+func (p1 SourcePath) Equal(p2 SourcePath) bool {
+ if len(p1) != len(p2) {
+ return false
+ }
+ for i := range p1 {
+ if p1[i] != p2[i] {
+ return false
+ }
+ }
+ return true
+}
+
+// String formats the path in a humanly readable manner.
+// The output is guaranteed to be deterministic,
+// making it suitable for use as a key into a Go map.
+// It is not guaranteed to be stable as the exact output could change
+// in a future version of this module.
+//
+// Example output:
+// .message_type[6].nested_type[15].field[3]
+func (p SourcePath) String() string {
+ b := p.appendFileDescriptorProto(nil)
+ for _, i := range p {
+ b = append(b, '.')
+ b = strconv.AppendInt(b, int64(i), 10)
+ }
+ return string(b)
+}
+
+type appendFunc func(*SourcePath, []byte) []byte
+
+func (p *SourcePath) appendSingularField(b []byte, name string, f appendFunc) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ b = append(b, '.')
+ b = append(b, name...)
+ *p = (*p)[1:]
+ if f != nil {
+ b = f(p, b)
+ }
+ return b
+}
+
+func (p *SourcePath) appendRepeatedField(b []byte, name string, f appendFunc) []byte {
+ b = p.appendSingularField(b, name, nil)
+ if len(*p) == 0 || (*p)[0] < 0 {
+ return b
+ }
+ b = append(b, '[')
+ b = strconv.AppendUint(b, uint64((*p)[0]), 10)
+ b = append(b, ']')
+ *p = (*p)[1:]
+ if f != nil {
+ b = f(p, b)
+ }
+ return b
+}
diff --git a/reflect/protoreflect/source_gen.go b/reflect/protoreflect/source_gen.go
new file mode 100644
index 0000000..b03c122
--- /dev/null
+++ b/reflect/protoreflect/source_gen.go
@@ -0,0 +1,461 @@
+// Copyright 2019 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.
+
+// Code generated by generate-protos. DO NOT EDIT.
+
+package protoreflect
+
+func (p *SourcePath) appendFileDescriptorProto(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 1:
+ b = p.appendSingularField(b, "name", nil)
+ case 2:
+ b = p.appendSingularField(b, "package", nil)
+ case 3:
+ b = p.appendRepeatedField(b, "dependency", nil)
+ case 10:
+ b = p.appendRepeatedField(b, "public_dependency", nil)
+ case 11:
+ b = p.appendRepeatedField(b, "weak_dependency", nil)
+ case 4:
+ b = p.appendRepeatedField(b, "message_type", (*SourcePath).appendDescriptorProto)
+ case 5:
+ b = p.appendRepeatedField(b, "enum_type", (*SourcePath).appendEnumDescriptorProto)
+ case 6:
+ b = p.appendRepeatedField(b, "service", (*SourcePath).appendServiceDescriptorProto)
+ case 7:
+ b = p.appendRepeatedField(b, "extension", (*SourcePath).appendFieldDescriptorProto)
+ case 8:
+ b = p.appendSingularField(b, "options", (*SourcePath).appendFileOptions)
+ case 9:
+ b = p.appendSingularField(b, "source_code_info", (*SourcePath).appendSourceCodeInfo)
+ case 12:
+ b = p.appendSingularField(b, "syntax", nil)
+ }
+ return b
+}
+
+func (p *SourcePath) appendDescriptorProto(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 1:
+ b = p.appendSingularField(b, "name", nil)
+ case 2:
+ b = p.appendRepeatedField(b, "field", (*SourcePath).appendFieldDescriptorProto)
+ case 6:
+ b = p.appendRepeatedField(b, "extension", (*SourcePath).appendFieldDescriptorProto)
+ case 3:
+ b = p.appendRepeatedField(b, "nested_type", (*SourcePath).appendDescriptorProto)
+ case 4:
+ b = p.appendRepeatedField(b, "enum_type", (*SourcePath).appendEnumDescriptorProto)
+ case 5:
+ b = p.appendRepeatedField(b, "extension_range", (*SourcePath).appendDescriptorProto_ExtensionRange)
+ case 8:
+ b = p.appendRepeatedField(b, "oneof_decl", (*SourcePath).appendOneofDescriptorProto)
+ case 7:
+ b = p.appendSingularField(b, "options", (*SourcePath).appendMessageOptions)
+ case 9:
+ b = p.appendRepeatedField(b, "reserved_range", (*SourcePath).appendDescriptorProto_ReservedRange)
+ case 10:
+ b = p.appendRepeatedField(b, "reserved_name", nil)
+ }
+ return b
+}
+
+func (p *SourcePath) appendEnumDescriptorProto(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 1:
+ b = p.appendSingularField(b, "name", nil)
+ case 2:
+ b = p.appendRepeatedField(b, "value", (*SourcePath).appendEnumValueDescriptorProto)
+ case 3:
+ b = p.appendSingularField(b, "options", (*SourcePath).appendEnumOptions)
+ case 4:
+ b = p.appendRepeatedField(b, "reserved_range", (*SourcePath).appendEnumDescriptorProto_EnumReservedRange)
+ case 5:
+ b = p.appendRepeatedField(b, "reserved_name", nil)
+ }
+ return b
+}
+
+func (p *SourcePath) appendServiceDescriptorProto(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 1:
+ b = p.appendSingularField(b, "name", nil)
+ case 2:
+ b = p.appendRepeatedField(b, "method", (*SourcePath).appendMethodDescriptorProto)
+ case 3:
+ b = p.appendSingularField(b, "options", (*SourcePath).appendServiceOptions)
+ }
+ return b
+}
+
+func (p *SourcePath) appendFieldDescriptorProto(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 1:
+ b = p.appendSingularField(b, "name", nil)
+ case 3:
+ b = p.appendSingularField(b, "number", nil)
+ case 4:
+ b = p.appendSingularField(b, "label", nil)
+ case 5:
+ b = p.appendSingularField(b, "type", nil)
+ case 6:
+ b = p.appendSingularField(b, "type_name", nil)
+ case 2:
+ b = p.appendSingularField(b, "extendee", nil)
+ case 7:
+ b = p.appendSingularField(b, "default_value", nil)
+ case 9:
+ b = p.appendSingularField(b, "oneof_index", nil)
+ case 10:
+ b = p.appendSingularField(b, "json_name", nil)
+ case 8:
+ b = p.appendSingularField(b, "options", (*SourcePath).appendFieldOptions)
+ case 17:
+ b = p.appendSingularField(b, "proto3_optional", nil)
+ }
+ return b
+}
+
+func (p *SourcePath) appendFileOptions(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 1:
+ b = p.appendSingularField(b, "java_package", nil)
+ case 8:
+ b = p.appendSingularField(b, "java_outer_classname", nil)
+ case 10:
+ b = p.appendSingularField(b, "java_multiple_files", nil)
+ case 20:
+ b = p.appendSingularField(b, "java_generate_equals_and_hash", nil)
+ case 27:
+ b = p.appendSingularField(b, "java_string_check_utf8", nil)
+ case 9:
+ b = p.appendSingularField(b, "optimize_for", nil)
+ case 11:
+ b = p.appendSingularField(b, "go_package", nil)
+ case 16:
+ b = p.appendSingularField(b, "cc_generic_services", nil)
+ case 17:
+ b = p.appendSingularField(b, "java_generic_services", nil)
+ case 18:
+ b = p.appendSingularField(b, "py_generic_services", nil)
+ case 42:
+ b = p.appendSingularField(b, "php_generic_services", nil)
+ case 23:
+ b = p.appendSingularField(b, "deprecated", nil)
+ case 31:
+ b = p.appendSingularField(b, "cc_enable_arenas", nil)
+ case 36:
+ b = p.appendSingularField(b, "objc_class_prefix", nil)
+ case 37:
+ b = p.appendSingularField(b, "csharp_namespace", nil)
+ case 39:
+ b = p.appendSingularField(b, "swift_prefix", nil)
+ case 40:
+ b = p.appendSingularField(b, "php_class_prefix", nil)
+ case 41:
+ b = p.appendSingularField(b, "php_namespace", nil)
+ case 44:
+ b = p.appendSingularField(b, "php_metadata_namespace", nil)
+ case 45:
+ b = p.appendSingularField(b, "ruby_package", nil)
+ case 999:
+ b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption)
+ }
+ return b
+}
+
+func (p *SourcePath) appendSourceCodeInfo(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 1:
+ b = p.appendRepeatedField(b, "location", (*SourcePath).appendSourceCodeInfo_Location)
+ }
+ return b
+}
+
+func (p *SourcePath) appendDescriptorProto_ExtensionRange(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 1:
+ b = p.appendSingularField(b, "start", nil)
+ case 2:
+ b = p.appendSingularField(b, "end", nil)
+ case 3:
+ b = p.appendSingularField(b, "options", (*SourcePath).appendExtensionRangeOptions)
+ }
+ return b
+}
+
+func (p *SourcePath) appendOneofDescriptorProto(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 1:
+ b = p.appendSingularField(b, "name", nil)
+ case 2:
+ b = p.appendSingularField(b, "options", (*SourcePath).appendOneofOptions)
+ }
+ return b
+}
+
+func (p *SourcePath) appendMessageOptions(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 1:
+ b = p.appendSingularField(b, "message_set_wire_format", nil)
+ case 2:
+ b = p.appendSingularField(b, "no_standard_descriptor_accessor", nil)
+ case 3:
+ b = p.appendSingularField(b, "deprecated", nil)
+ case 7:
+ b = p.appendSingularField(b, "map_entry", nil)
+ case 999:
+ b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption)
+ }
+ return b
+}
+
+func (p *SourcePath) appendDescriptorProto_ReservedRange(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 1:
+ b = p.appendSingularField(b, "start", nil)
+ case 2:
+ b = p.appendSingularField(b, "end", nil)
+ }
+ return b
+}
+
+func (p *SourcePath) appendEnumValueDescriptorProto(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 1:
+ b = p.appendSingularField(b, "name", nil)
+ case 2:
+ b = p.appendSingularField(b, "number", nil)
+ case 3:
+ b = p.appendSingularField(b, "options", (*SourcePath).appendEnumValueOptions)
+ }
+ return b
+}
+
+func (p *SourcePath) appendEnumOptions(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 2:
+ b = p.appendSingularField(b, "allow_alias", nil)
+ case 3:
+ b = p.appendSingularField(b, "deprecated", nil)
+ case 999:
+ b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption)
+ }
+ return b
+}
+
+func (p *SourcePath) appendEnumDescriptorProto_EnumReservedRange(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 1:
+ b = p.appendSingularField(b, "start", nil)
+ case 2:
+ b = p.appendSingularField(b, "end", nil)
+ }
+ return b
+}
+
+func (p *SourcePath) appendMethodDescriptorProto(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 1:
+ b = p.appendSingularField(b, "name", nil)
+ case 2:
+ b = p.appendSingularField(b, "input_type", nil)
+ case 3:
+ b = p.appendSingularField(b, "output_type", nil)
+ case 4:
+ b = p.appendSingularField(b, "options", (*SourcePath).appendMethodOptions)
+ case 5:
+ b = p.appendSingularField(b, "client_streaming", nil)
+ case 6:
+ b = p.appendSingularField(b, "server_streaming", nil)
+ }
+ return b
+}
+
+func (p *SourcePath) appendServiceOptions(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 33:
+ b = p.appendSingularField(b, "deprecated", nil)
+ case 999:
+ b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption)
+ }
+ return b
+}
+
+func (p *SourcePath) appendFieldOptions(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 1:
+ b = p.appendSingularField(b, "ctype", nil)
+ case 2:
+ b = p.appendSingularField(b, "packed", nil)
+ case 6:
+ b = p.appendSingularField(b, "jstype", nil)
+ case 5:
+ b = p.appendSingularField(b, "lazy", nil)
+ case 3:
+ b = p.appendSingularField(b, "deprecated", nil)
+ case 10:
+ b = p.appendSingularField(b, "weak", nil)
+ case 999:
+ b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption)
+ }
+ return b
+}
+
+func (p *SourcePath) appendUninterpretedOption(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 2:
+ b = p.appendRepeatedField(b, "name", (*SourcePath).appendUninterpretedOption_NamePart)
+ case 3:
+ b = p.appendSingularField(b, "identifier_value", nil)
+ case 4:
+ b = p.appendSingularField(b, "positive_int_value", nil)
+ case 5:
+ b = p.appendSingularField(b, "negative_int_value", nil)
+ case 6:
+ b = p.appendSingularField(b, "double_value", nil)
+ case 7:
+ b = p.appendSingularField(b, "string_value", nil)
+ case 8:
+ b = p.appendSingularField(b, "aggregate_value", nil)
+ }
+ return b
+}
+
+func (p *SourcePath) appendSourceCodeInfo_Location(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 1:
+ b = p.appendRepeatedField(b, "path", nil)
+ case 2:
+ b = p.appendRepeatedField(b, "span", nil)
+ case 3:
+ b = p.appendSingularField(b, "leading_comments", nil)
+ case 4:
+ b = p.appendSingularField(b, "trailing_comments", nil)
+ case 6:
+ b = p.appendRepeatedField(b, "leading_detached_comments", nil)
+ }
+ return b
+}
+
+func (p *SourcePath) appendExtensionRangeOptions(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 999:
+ b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption)
+ }
+ return b
+}
+
+func (p *SourcePath) appendOneofOptions(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 999:
+ b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption)
+ }
+ return b
+}
+
+func (p *SourcePath) appendEnumValueOptions(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 1:
+ b = p.appendSingularField(b, "deprecated", nil)
+ case 999:
+ b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption)
+ }
+ return b
+}
+
+func (p *SourcePath) appendMethodOptions(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 33:
+ b = p.appendSingularField(b, "deprecated", nil)
+ case 34:
+ b = p.appendSingularField(b, "idempotency_level", nil)
+ case 999:
+ b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption)
+ }
+ return b
+}
+
+func (p *SourcePath) appendUninterpretedOption_NamePart(b []byte) []byte {
+ if len(*p) == 0 {
+ return b
+ }
+ switch (*p)[0] {
+ case 1:
+ b = p.appendSingularField(b, "name_part", nil)
+ case 2:
+ b = p.appendSingularField(b, "is_extension", nil)
+ }
+ return b
+}
diff --git a/reflect/protoreflect/source_test.go b/reflect/protoreflect/source_test.go
new file mode 100644
index 0000000..877ede5
--- /dev/null
+++ b/reflect/protoreflect/source_test.go
@@ -0,0 +1,35 @@
+// Copyright 2020 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.
+
+package protoreflect
+
+import "testing"
+
+func TestSourcePathString(t *testing.T) {
+ tests := []struct {
+ in SourcePath
+ want string
+ }{
+ {nil, ""},
+ {SourcePath{}, ""},
+ {SourcePath{0}, ".0"},
+ {SourcePath{1}, ".name"},
+ {SourcePath{1, 1}, ".name.1"},
+ {SourcePath{1, 1, -2, 3}, ".name.1.-2.3"},
+ {SourcePath{3}, ".dependency"},
+ {SourcePath{3, 0}, ".dependency[0]"},
+ {SourcePath{3, -1}, ".dependency.-1"},
+ {SourcePath{3, 1, 2}, ".dependency[1].2"},
+ {SourcePath{4}, ".message_type"},
+ {SourcePath{4, 0}, ".message_type[0]"},
+ {SourcePath{4, -1}, ".message_type.-1"},
+ {SourcePath{4, 1, 0}, ".message_type[1].0"},
+ {SourcePath{4, 1, 1}, ".message_type[1].name"},
+ }
+ for _, tt := range tests {
+ if got := tt.in.String(); got != tt.want {
+ t.Errorf("SourcePath(%d).String() = %v, want %v", tt.in, got, tt.want)
+ }
+ }
+}