Import of https://github.com/ciena/voltctl at commit 40d61fbf3f910ed4017cf67c9c79e8e1f82a33a5
Change-Id: I8464c59e60d76cb8612891db3303878975b5416c
diff --git a/vendor/github.com/jhump/protoreflect/desc/protoparse/source_code_info.go b/vendor/github.com/jhump/protoreflect/desc/protoparse/source_code_info.go
new file mode 100644
index 0000000..d0a61c2
--- /dev/null
+++ b/vendor/github.com/jhump/protoreflect/desc/protoparse/source_code_info.go
@@ -0,0 +1,612 @@
+package protoparse
+
+import (
+ "bytes"
+ "reflect"
+ "sort"
+ "strings"
+
+ "github.com/golang/protobuf/proto"
+ dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
+
+ "github.com/jhump/protoreflect/desc/internal"
+)
+
+func (r *parseResult) generateSourceCodeInfo() *dpb.SourceCodeInfo {
+ if r.nodes == nil {
+ // skip files that do not have AST info (these will be files
+ // that came from well-known descriptors, instead of from source)
+ return nil
+ }
+
+ sci := sourceCodeInfo{commentsUsed: map[*comment]struct{}{}}
+ path := make([]int32, 0, 10)
+
+ fn := r.getFileNode(r.fd).(*fileNode)
+ if fn.syntax != nil {
+ sci.newLoc(fn.syntax, append(path, internal.File_syntaxTag))
+ }
+ if fn.pkg != nil {
+ sci.newLoc(fn.pkg, append(path, internal.File_packageTag))
+ }
+ for i, imp := range fn.imports {
+ sci.newLoc(imp, append(path, internal.File_dependencyTag, int32(i)))
+ }
+
+ // file options
+ r.generateSourceCodeInfoForOptions(&sci, fn.decls, func(n interface{}) *optionNode {
+ return n.(*fileElement).option
+ }, r.fd.Options.GetUninterpretedOption(), append(path, internal.File_optionsTag))
+
+ // message types
+ for i, msg := range r.fd.GetMessageType() {
+ r.generateSourceCodeInfoForMessage(&sci, msg, append(path, internal.File_messagesTag, int32(i)))
+ }
+
+ // enum types
+ for i, enum := range r.fd.GetEnumType() {
+ r.generateSourceCodeInfoForEnum(&sci, enum, append(path, internal.File_enumsTag, int32(i)))
+ }
+
+ // extension fields
+ for i, ext := range r.fd.GetExtension() {
+ r.generateSourceCodeInfoForField(&sci, ext, append(path, internal.File_extensionsTag, int32(i)))
+ }
+
+ // services and methods
+ for i, svc := range r.fd.GetService() {
+ n := r.getServiceNode(svc).(*serviceNode)
+ svcPath := append(path, internal.File_servicesTag, int32(i))
+ sci.newLoc(n, svcPath)
+ sci.newLoc(n.name, append(svcPath, internal.Service_nameTag))
+
+ // service options
+ r.generateSourceCodeInfoForOptions(&sci, n.decls, func(n interface{}) *optionNode {
+ return n.(*serviceElement).option
+ }, svc.Options.GetUninterpretedOption(), append(svcPath, internal.Service_optionsTag))
+
+ // methods
+ for j, mtd := range svc.GetMethod() {
+ mn := r.getMethodNode(mtd).(*methodNode)
+ mtdPath := append(svcPath, internal.Service_methodsTag, int32(j))
+ sci.newLoc(mn, mtdPath)
+ sci.newLoc(mn.name, append(mtdPath, internal.Method_nameTag))
+
+ sci.newLoc(mn.input.msgType, append(mtdPath, internal.Method_inputTag))
+ if mn.input.streamKeyword != nil {
+ sci.newLoc(mn.input.streamKeyword, append(mtdPath, internal.Method_inputStreamTag))
+ }
+ sci.newLoc(mn.output.msgType, append(mtdPath, internal.Method_outputTag))
+ if mn.output.streamKeyword != nil {
+ sci.newLoc(mn.output.streamKeyword, append(mtdPath, internal.Method_outputStreamTag))
+ }
+
+ // method options
+ r.generateSourceCodeInfoForOptions(&sci, mn.options, func(n interface{}) *optionNode {
+ return n.(*optionNode)
+ }, mtd.Options.GetUninterpretedOption(), append(mtdPath, internal.Method_optionsTag))
+ }
+ }
+ return &dpb.SourceCodeInfo{Location: sci.generateLocs()}
+}
+
+func (r *parseResult) generateSourceCodeInfoForOptions(sci *sourceCodeInfo, elements interface{}, extractor func(interface{}) *optionNode, uninterp []*dpb.UninterpretedOption, path []int32) {
+ // Known options are option node elements that have a corresponding
+ // path in r.interpretedOptions. We'll do those first.
+ rv := reflect.ValueOf(elements)
+ for i := 0; i < rv.Len(); i++ {
+ on := extractor(rv.Index(i).Interface())
+ if on == nil {
+ continue
+ }
+ optPath := r.interpretedOptions[on]
+ if len(optPath) > 0 {
+ p := path
+ if optPath[0] == -1 {
+ // used by "default" and "json_name" field pseudo-options
+ // to attribute path to parent element (since those are
+ // stored directly on the descriptor, not its options)
+ p = make([]int32, len(path)-1)
+ copy(p, path)
+ optPath = optPath[1:]
+ }
+ sci.newLoc(on, append(p, optPath...))
+ }
+ }
+
+ // Now uninterpreted options
+ for i, uo := range uninterp {
+ optPath := append(path, internal.UninterpretedOptionsTag, int32(i))
+ on := r.getOptionNode(uo).(*optionNode)
+ sci.newLoc(on, optPath)
+
+ var valTag int32
+ switch {
+ case uo.IdentifierValue != nil:
+ valTag = internal.Uninterpreted_identTag
+ case uo.PositiveIntValue != nil:
+ valTag = internal.Uninterpreted_posIntTag
+ case uo.NegativeIntValue != nil:
+ valTag = internal.Uninterpreted_negIntTag
+ case uo.DoubleValue != nil:
+ valTag = internal.Uninterpreted_doubleTag
+ case uo.StringValue != nil:
+ valTag = internal.Uninterpreted_stringTag
+ case uo.AggregateValue != nil:
+ valTag = internal.Uninterpreted_aggregateTag
+ }
+ if valTag != 0 {
+ sci.newLoc(on.val, append(optPath, valTag))
+ }
+
+ for j, n := range uo.Name {
+ optNmPath := append(optPath, internal.Uninterpreted_nameTag, int32(j))
+ nn := r.getOptionNamePartNode(n).(*optionNamePartNode)
+ sci.newLoc(nn, optNmPath)
+ sci.newLoc(nn.text, append(optNmPath, internal.UninterpretedName_nameTag))
+ }
+ }
+}
+
+func (r *parseResult) generateSourceCodeInfoForMessage(sci *sourceCodeInfo, msg *dpb.DescriptorProto, path []int32) {
+ n := r.getMessageNode(msg)
+ sci.newLoc(n, path)
+
+ var decls []*messageElement
+ var resvdNames []*stringLiteralNode
+ switch n := n.(type) {
+ case *messageNode:
+ decls = n.decls
+ resvdNames = n.reserved
+ case *groupNode:
+ decls = n.decls
+ resvdNames = n.reserved
+ }
+ if decls == nil {
+ // map entry so nothing else to do
+ return
+ }
+
+ sci.newLoc(n.messageName(), append(path, internal.Message_nameTag))
+
+ // message options
+ r.generateSourceCodeInfoForOptions(sci, decls, func(n interface{}) *optionNode {
+ return n.(*messageElement).option
+ }, msg.Options.GetUninterpretedOption(), append(path, internal.Message_optionsTag))
+
+ // fields
+ for i, fld := range msg.GetField() {
+ r.generateSourceCodeInfoForField(sci, fld, append(path, internal.Message_fieldsTag, int32(i)))
+ }
+
+ // one-ofs
+ for i, ood := range msg.GetOneofDecl() {
+ oon := r.getOneOfNode(ood).(*oneOfNode)
+ ooPath := append(path, internal.Message_oneOfsTag, int32(i))
+ sci.newLoc(oon, ooPath)
+ sci.newLoc(oon.name, append(ooPath, internal.OneOf_nameTag))
+
+ // one-of options
+ r.generateSourceCodeInfoForOptions(sci, oon.decls, func(n interface{}) *optionNode {
+ return n.(*oneOfElement).option
+ }, ood.Options.GetUninterpretedOption(), append(ooPath, internal.OneOf_optionsTag))
+ }
+
+ // nested messages
+ for i, nm := range msg.GetNestedType() {
+ r.generateSourceCodeInfoForMessage(sci, nm, append(path, internal.Message_nestedMessagesTag, int32(i)))
+ }
+
+ // nested enums
+ for i, enum := range msg.GetEnumType() {
+ r.generateSourceCodeInfoForEnum(sci, enum, append(path, internal.Message_enumsTag, int32(i)))
+ }
+
+ // nested extensions
+ for i, ext := range msg.GetExtension() {
+ r.generateSourceCodeInfoForField(sci, ext, append(path, internal.Message_extensionsTag, int32(i)))
+ }
+
+ // extension ranges
+ for i, er := range msg.ExtensionRange {
+ rangePath := append(path, internal.Message_extensionRangeTag, int32(i))
+ rn := r.getExtensionRangeNode(er).(*rangeNode)
+ sci.newLoc(rn, rangePath)
+ sci.newLoc(rn.stNode, append(rangePath, internal.ExtensionRange_startTag))
+ if rn.stNode != rn.enNode {
+ sci.newLoc(rn.enNode, append(rangePath, internal.ExtensionRange_endTag))
+ }
+ // now we have to find the extension decl and options that correspond to this range :(
+ for _, d := range decls {
+ found := false
+ if d.extensionRange != nil {
+ for _, r := range d.extensionRange.ranges {
+ if rn == r {
+ found = true
+ break
+ }
+ }
+ }
+ if found {
+ r.generateSourceCodeInfoForOptions(sci, d.extensionRange.options, func(n interface{}) *optionNode {
+ return n.(*optionNode)
+ }, er.Options.GetUninterpretedOption(), append(rangePath, internal.ExtensionRange_optionsTag))
+ break
+ }
+ }
+ }
+
+ // reserved ranges
+ for i, rr := range msg.ReservedRange {
+ rangePath := append(path, internal.Message_reservedRangeTag, int32(i))
+ rn := r.getMessageReservedRangeNode(rr).(*rangeNode)
+ sci.newLoc(rn, rangePath)
+ sci.newLoc(rn.stNode, append(rangePath, internal.ReservedRange_startTag))
+ if rn.stNode != rn.enNode {
+ sci.newLoc(rn.enNode, append(rangePath, internal.ReservedRange_endTag))
+ }
+ }
+
+ // reserved names
+ for i, n := range resvdNames {
+ sci.newLoc(n, append(path, internal.Message_reservedNameTag, int32(i)))
+ }
+}
+
+func (r *parseResult) generateSourceCodeInfoForEnum(sci *sourceCodeInfo, enum *dpb.EnumDescriptorProto, path []int32) {
+ n := r.getEnumNode(enum).(*enumNode)
+ sci.newLoc(n, path)
+ sci.newLoc(n.name, append(path, internal.Enum_nameTag))
+
+ // enum options
+ r.generateSourceCodeInfoForOptions(sci, n.decls, func(n interface{}) *optionNode {
+ return n.(*enumElement).option
+ }, enum.Options.GetUninterpretedOption(), append(path, internal.Enum_optionsTag))
+
+ // enum values
+ for j, ev := range enum.GetValue() {
+ evn := r.getEnumValueNode(ev).(*enumValueNode)
+ evPath := append(path, internal.Enum_valuesTag, int32(j))
+ sci.newLoc(evn, evPath)
+ sci.newLoc(evn.name, append(evPath, internal.EnumVal_nameTag))
+ sci.newLoc(evn.getNumber(), append(evPath, internal.EnumVal_numberTag))
+
+ // enum value options
+ r.generateSourceCodeInfoForOptions(sci, evn.options, func(n interface{}) *optionNode {
+ return n.(*optionNode)
+ }, ev.Options.GetUninterpretedOption(), append(evPath, internal.EnumVal_optionsTag))
+ }
+
+ // reserved ranges
+ for i, rr := range enum.GetReservedRange() {
+ rangePath := append(path, internal.Enum_reservedRangeTag, int32(i))
+ rn := r.getEnumReservedRangeNode(rr).(*rangeNode)
+ sci.newLoc(rn, rangePath)
+ sci.newLoc(rn.stNode, append(rangePath, internal.ReservedRange_startTag))
+ if rn.stNode != rn.enNode {
+ sci.newLoc(rn.enNode, append(rangePath, internal.ReservedRange_endTag))
+ }
+ }
+
+ // reserved names
+ for i, rn := range n.reserved {
+ sci.newLoc(rn, append(path, internal.Enum_reservedNameTag, int32(i)))
+ }
+}
+
+func (r *parseResult) generateSourceCodeInfoForField(sci *sourceCodeInfo, fld *dpb.FieldDescriptorProto, path []int32) {
+ n := r.getFieldNode(fld)
+
+ isGroup := false
+ var opts []*optionNode
+ var extendee *extendNode
+ switch n := n.(type) {
+ case *fieldNode:
+ opts = n.options
+ extendee = n.extendee
+ case *mapFieldNode:
+ opts = n.options
+ case *groupNode:
+ isGroup = true
+ extendee = n.extendee
+ case *syntheticMapField:
+ // shouldn't get here since we don't recurse into fields from a mapNode
+ // in generateSourceCodeInfoForMessage... but just in case
+ return
+ }
+
+ sci.newLoc(n, path)
+ if !isGroup {
+ sci.newLoc(n.fieldName(), append(path, internal.Field_nameTag))
+ sci.newLoc(n.fieldType(), append(path, internal.Field_typeTag))
+ }
+ if n.fieldLabel() != nil {
+ sci.newLoc(n.fieldLabel(), append(path, internal.Field_labelTag))
+ }
+ sci.newLoc(n.fieldTag(), append(path, internal.Field_numberTag))
+ if extendee != nil {
+ sci.newLoc(extendee.extendee, append(path, internal.Field_extendeeTag))
+ }
+
+ r.generateSourceCodeInfoForOptions(sci, opts, func(n interface{}) *optionNode {
+ return n.(*optionNode)
+ }, fld.Options.GetUninterpretedOption(), append(path, internal.Field_optionsTag))
+}
+
+type sourceCodeInfo struct {
+ locs []*dpb.SourceCodeInfo_Location
+ commentsUsed map[*comment]struct{}
+}
+
+func (sci *sourceCodeInfo) newLoc(n node, path []int32) {
+ leadingComments := n.leadingComments()
+ trailingComments := n.trailingComments()
+ if sci.commentUsed(leadingComments) {
+ leadingComments = nil
+ }
+ if sci.commentUsed(trailingComments) {
+ trailingComments = nil
+ }
+ detached := groupComments(leadingComments)
+ trail := combineComments(trailingComments)
+ var lead *string
+ if len(leadingComments) > 0 && leadingComments[len(leadingComments)-1].end.Line >= n.start().Line-1 {
+ lead = proto.String(detached[len(detached)-1])
+ detached = detached[:len(detached)-1]
+ }
+ dup := make([]int32, len(path))
+ copy(dup, path)
+ var span []int32
+ if n.start().Line == n.end().Line {
+ span = []int32{int32(n.start().Line) - 1, int32(n.start().Col) - 1, int32(n.end().Col) - 1}
+ } else {
+ span = []int32{int32(n.start().Line) - 1, int32(n.start().Col) - 1, int32(n.end().Line) - 1, int32(n.end().Col) - 1}
+ }
+ sci.locs = append(sci.locs, &dpb.SourceCodeInfo_Location{
+ LeadingDetachedComments: detached,
+ LeadingComments: lead,
+ TrailingComments: trail,
+ Path: dup,
+ Span: span,
+ })
+}
+
+func (sci *sourceCodeInfo) commentUsed(c []*comment) bool {
+ if len(c) == 0 {
+ return false
+ }
+ if _, ok := sci.commentsUsed[c[0]]; ok {
+ return true
+ }
+
+ sci.commentsUsed[c[0]] = struct{}{}
+ return false
+}
+
+func groupComments(comments []*comment) []string {
+ if len(comments) == 0 {
+ return nil
+ }
+
+ var groups []string
+ singleLineStyle := comments[0].text[:2] == "//"
+ line := comments[0].end.Line
+ start := 0
+ for i := 1; i < len(comments); i++ {
+ c := comments[i]
+ prevSingleLine := singleLineStyle
+ singleLineStyle = strings.HasPrefix(comments[i].text, "//")
+ if !singleLineStyle || prevSingleLine != singleLineStyle || c.start.Line > line+1 {
+ // new group!
+ groups = append(groups, *combineComments(comments[start:i]))
+ start = i
+ }
+ line = c.end.Line
+ }
+ // don't forget last group
+ groups = append(groups, *combineComments(comments[start:]))
+
+ return groups
+}
+
+func combineComments(comments []*comment) *string {
+ if len(comments) == 0 {
+ return nil
+ }
+ first := true
+ var buf bytes.Buffer
+ for _, c := range comments {
+ if first {
+ first = false
+ } else {
+ buf.WriteByte('\n')
+ }
+ if c.text[:2] == "//" {
+ buf.WriteString(c.text[2:])
+ } else {
+ lines := strings.Split(c.text[2:len(c.text)-2], "\n")
+ first := true
+ for _, l := range lines {
+ if first {
+ first = false
+ } else {
+ buf.WriteByte('\n')
+ }
+
+ // strip a prefix of whitespace followed by '*'
+ j := 0
+ for j < len(l) {
+ if l[j] != ' ' && l[j] != '\t' {
+ break
+ }
+ j++
+ }
+ if j == len(l) {
+ l = ""
+ } else if l[j] == '*' {
+ l = l[j+1:]
+ } else if j > 0 {
+ l = " " + l[j:]
+ }
+
+ buf.WriteString(l)
+ }
+ }
+ }
+ return proto.String(buf.String())
+}
+
+func (sci *sourceCodeInfo) generateLocs() []*dpb.SourceCodeInfo_Location {
+ // generate intermediate locations: paths between root (inclusive) and the
+ // leaf locations already created, these will not have comments but will
+ // have aggregate span, than runs from min(start pos) to max(end pos) for
+ // all descendent paths.
+
+ if len(sci.locs) == 0 {
+ // nothing to generate
+ return nil
+ }
+
+ var root locTrie
+ for _, loc := range sci.locs {
+ root.add(loc.Path, loc)
+ }
+ root.fillIn()
+ locs := make([]*dpb.SourceCodeInfo_Location, 0, root.countLocs())
+ root.aggregate(&locs)
+ // finally, sort the resulting slice by location
+ sort.Slice(locs, func(i, j int) bool {
+ startI, endI := getSpanPositions(locs[i].Span)
+ startJ, endJ := getSpanPositions(locs[j].Span)
+ cmp := compareSlice(startI, startJ)
+ if cmp == 0 {
+ // if start position is the same, sort by end position _decreasing_
+ // (so enclosing locations will appear before leaves)
+ cmp = -compareSlice(endI, endJ)
+ if cmp == 0 {
+ // start and end position are the same? so break ties using path
+ cmp = compareSlice(locs[i].Path, locs[j].Path)
+ }
+ }
+ return cmp < 0
+ })
+ return locs
+}
+
+type locTrie struct {
+ children map[int32]*locTrie
+ loc *dpb.SourceCodeInfo_Location
+}
+
+func (t *locTrie) add(path []int32, loc *dpb.SourceCodeInfo_Location) {
+ if len(path) == 0 {
+ t.loc = loc
+ return
+ }
+ child := t.children[path[0]]
+ if child == nil {
+ if t.children == nil {
+ t.children = map[int32]*locTrie{}
+ }
+ child = &locTrie{}
+ t.children[path[0]] = child
+ }
+ child.add(path[1:], loc)
+}
+
+func (t *locTrie) fillIn() {
+ var path []int32
+ var start, end []int32
+ for _, child := range t.children {
+ // recurse
+ child.fillIn()
+ if t.loc == nil {
+ // maintain min(start) and max(end) so we can
+ // populate t.loc below
+ childStart, childEnd := getSpanPositions(child.loc.Span)
+
+ if start == nil {
+ if path == nil {
+ path = child.loc.Path[:len(child.loc.Path)-1]
+ }
+ start = childStart
+ end = childEnd
+ } else {
+ if compareSlice(childStart, start) < 0 {
+ start = childStart
+ }
+ if compareSlice(childEnd, end) > 0 {
+ end = childEnd
+ }
+ }
+ }
+ }
+
+ if t.loc == nil {
+ var span []int32
+ // we don't use append below because we want a new slice
+ // that doesn't share underlying buffer with spans from
+ // any other location
+ if start[0] == end[0] {
+ span = []int32{start[0], start[1], end[1]}
+ } else {
+ span = []int32{start[0], start[1], end[0], end[1]}
+ }
+ t.loc = &dpb.SourceCodeInfo_Location{
+ Path: path,
+ Span: span,
+ }
+ }
+}
+
+func (t *locTrie) countLocs() int {
+ count := 0
+ if t.loc != nil {
+ count = 1
+ }
+ for _, ch := range t.children {
+ count += ch.countLocs()
+ }
+ return count
+}
+
+func (t *locTrie) aggregate(dest *[]*dpb.SourceCodeInfo_Location) {
+ if t.loc != nil {
+ *dest = append(*dest, t.loc)
+ }
+ for _, child := range t.children {
+ child.aggregate(dest)
+ }
+}
+
+func getSpanPositions(span []int32) (start, end []int32) {
+ start = span[:2]
+ if len(span) == 3 {
+ end = []int32{span[0], span[2]}
+ } else {
+ end = span[2:]
+ }
+ return
+}
+
+func compareSlice(a, b []int32) int {
+ end := len(a)
+ if len(b) < end {
+ end = len(b)
+ }
+ for i := 0; i < end; i++ {
+ if a[i] < b[i] {
+ return -1
+ }
+ if a[i] > b[i] {
+ return 1
+ }
+ }
+ if len(a) < len(b) {
+ return -1
+ }
+ if len(a) > len(b) {
+ return 1
+ }
+ return 0
+}