Import of https://github.com/ciena/voltctl at commit 40d61fbf3f910ed4017cf67c9c79e8e1f82a33a5
Change-Id: I8464c59e60d76cb8612891db3303878975b5416c
diff --git a/vendor/github.com/jhump/protoreflect/desc/protoparse/linker.go b/vendor/github.com/jhump/protoreflect/desc/protoparse/linker.go
new file mode 100644
index 0000000..c150936
--- /dev/null
+++ b/vendor/github.com/jhump/protoreflect/desc/protoparse/linker.go
@@ -0,0 +1,652 @@
+package protoparse
+
+import (
+ "bytes"
+ "fmt"
+ "sort"
+ "strings"
+
+ "github.com/golang/protobuf/proto"
+ dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
+
+ "github.com/jhump/protoreflect/desc"
+ "github.com/jhump/protoreflect/desc/internal"
+)
+
+type linker struct {
+ files map[string]*parseResult
+ descriptorPool map[*dpb.FileDescriptorProto]map[string]proto.Message
+ extensions map[string]map[int32]string
+}
+
+func newLinker(files map[string]*parseResult) *linker {
+ return &linker{files: files}
+}
+
+func (l *linker) linkFiles() (map[string]*desc.FileDescriptor, error) {
+ // First, we put all symbols into a single pool, which lets us ensure there
+ // are no duplicate symbols and will also let us resolve and revise all type
+ // references in next step.
+ if err := l.createDescriptorPool(); err != nil {
+ return nil, err
+ }
+
+ // After we've populated the pool, we can now try to resolve all type
+ // references. All references must be checked for correct type, any fields
+ // with enum types must be corrected (since we parse them as if they are
+ // message references since we don't actually know message or enum until
+ // link time), and references will be re-written to be fully-qualified
+ // references (e.g. start with a dot ".").
+ if err := l.resolveReferences(); err != nil {
+ return nil, err
+ }
+
+ // Now we've validated the descriptors, so we can link them into rich
+ // descriptors. This is a little redundant since that step does similar
+ // checking of symbols. But, without breaking encapsulation (e.g. exporting
+ // a lot of fields from desc package that are currently unexported) or
+ // merging this into the same package, we can't really prevent it.
+ linked, err := l.createdLinkedDescriptors()
+ if err != nil {
+ return nil, err
+ }
+
+ // Now that we have linked descriptors, we can interpret any uninterpreted
+ // options that remain.
+ for _, r := range l.files {
+ fd := linked[r.fd.GetName()]
+ if err := interpretFileOptions(r, richFileDescriptorish{FileDescriptor: fd}); err != nil {
+ return nil, err
+ }
+ }
+
+ return linked, nil
+}
+
+func (l *linker) createDescriptorPool() error {
+ l.descriptorPool = map[*dpb.FileDescriptorProto]map[string]proto.Message{}
+ for _, r := range l.files {
+ fd := r.fd
+ pool := map[string]proto.Message{}
+ l.descriptorPool[fd] = pool
+ prefix := fd.GetPackage()
+ if prefix != "" {
+ prefix += "."
+ }
+ for _, md := range fd.MessageType {
+ if err := addMessageToPool(r, pool, prefix, md); err != nil {
+ return err
+ }
+ }
+ for _, fld := range fd.Extension {
+ if err := addFieldToPool(r, pool, prefix, fld); err != nil {
+ return err
+ }
+ }
+ for _, ed := range fd.EnumType {
+ if err := addEnumToPool(r, pool, prefix, ed); err != nil {
+ return err
+ }
+ }
+ for _, sd := range fd.Service {
+ if err := addServiceToPool(r, pool, prefix, sd); err != nil {
+ return err
+ }
+ }
+ }
+ // try putting everything into a single pool, to ensure there are no duplicates
+ // across files (e.g. same symbol, but declared in two different files)
+ type entry struct {
+ file string
+ msg proto.Message
+ }
+ pool := map[string]entry{}
+ for f, p := range l.descriptorPool {
+ for k, v := range p {
+ if e, ok := pool[k]; ok {
+ desc1 := e.msg
+ file1 := e.file
+ desc2 := v
+ file2 := f.GetName()
+ if file2 < file1 {
+ file1, file2 = file2, file1
+ desc1, desc2 = desc2, desc1
+ }
+ node := l.files[file2].nodes[desc2]
+ return ErrorWithSourcePos{Pos: node.start(), Underlying: fmt.Errorf("duplicate symbol %s: already defined as %s in %q", k, descriptorType(desc1), file1)}
+ }
+ pool[k] = entry{file: f.GetName(), msg: v}
+ }
+ }
+
+ return nil
+}
+
+func addMessageToPool(r *parseResult, pool map[string]proto.Message, prefix string, md *dpb.DescriptorProto) error {
+ fqn := prefix + md.GetName()
+ if err := addToPool(r, pool, fqn, md); err != nil {
+ return err
+ }
+ prefix = fqn + "."
+ for _, fld := range md.Field {
+ if err := addFieldToPool(r, pool, prefix, fld); err != nil {
+ return err
+ }
+ }
+ for _, fld := range md.Extension {
+ if err := addFieldToPool(r, pool, prefix, fld); err != nil {
+ return err
+ }
+ }
+ for _, nmd := range md.NestedType {
+ if err := addMessageToPool(r, pool, prefix, nmd); err != nil {
+ return err
+ }
+ }
+ for _, ed := range md.EnumType {
+ if err := addEnumToPool(r, pool, prefix, ed); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func addFieldToPool(r *parseResult, pool map[string]proto.Message, prefix string, fld *dpb.FieldDescriptorProto) error {
+ fqn := prefix + fld.GetName()
+ return addToPool(r, pool, fqn, fld)
+}
+
+func addEnumToPool(r *parseResult, pool map[string]proto.Message, prefix string, ed *dpb.EnumDescriptorProto) error {
+ fqn := prefix + ed.GetName()
+ if err := addToPool(r, pool, fqn, ed); err != nil {
+ return err
+ }
+ for _, evd := range ed.Value {
+ vfqn := fqn + "." + evd.GetName()
+ if err := addToPool(r, pool, vfqn, evd); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func addServiceToPool(r *parseResult, pool map[string]proto.Message, prefix string, sd *dpb.ServiceDescriptorProto) error {
+ fqn := prefix + sd.GetName()
+ if err := addToPool(r, pool, fqn, sd); err != nil {
+ return err
+ }
+ for _, mtd := range sd.Method {
+ mfqn := fqn + "." + mtd.GetName()
+ if err := addToPool(r, pool, mfqn, mtd); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func addToPool(r *parseResult, pool map[string]proto.Message, fqn string, dsc proto.Message) error {
+ if d, ok := pool[fqn]; ok {
+ node := r.nodes[dsc]
+ return ErrorWithSourcePos{Pos: node.start(), Underlying: fmt.Errorf("duplicate symbol %s: already defined as %s", fqn, descriptorType(d))}
+ }
+ pool[fqn] = dsc
+ return nil
+}
+
+func descriptorType(m proto.Message) string {
+ switch m := m.(type) {
+ case *dpb.DescriptorProto:
+ return "message"
+ case *dpb.DescriptorProto_ExtensionRange:
+ return "extension range"
+ case *dpb.FieldDescriptorProto:
+ if m.GetExtendee() == "" {
+ return "field"
+ } else {
+ return "extension"
+ }
+ case *dpb.EnumDescriptorProto:
+ return "enum"
+ case *dpb.EnumValueDescriptorProto:
+ return "enum value"
+ case *dpb.ServiceDescriptorProto:
+ return "service"
+ case *dpb.MethodDescriptorProto:
+ return "method"
+ case *dpb.FileDescriptorProto:
+ return "file"
+ default:
+ // shouldn't be possible
+ return fmt.Sprintf("%T", m)
+ }
+}
+
+func (l *linker) resolveReferences() error {
+ l.extensions = map[string]map[int32]string{}
+ for _, r := range l.files {
+ fd := r.fd
+ prefix := fd.GetPackage()
+ scopes := []scope{fileScope(fd, l)}
+ if prefix != "" {
+ prefix += "."
+ }
+ if fd.Options != nil {
+ if err := l.resolveOptions(r, fd, "file", fd.GetName(), proto.MessageName(fd.Options), fd.Options.UninterpretedOption, scopes); err != nil {
+ return err
+ }
+ }
+ for _, md := range fd.MessageType {
+ if err := l.resolveMessageTypes(r, fd, prefix, md, scopes); err != nil {
+ return err
+ }
+ }
+ for _, fld := range fd.Extension {
+ if err := l.resolveFieldTypes(r, fd, prefix, fld, scopes); err != nil {
+ return err
+ }
+ }
+ for _, ed := range fd.EnumType {
+ if err := l.resolveEnumTypes(r, fd, prefix, ed, scopes); err != nil {
+ return err
+ }
+ }
+ for _, sd := range fd.Service {
+ if err := l.resolveServiceTypes(r, fd, prefix, sd, scopes); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+func (l *linker) resolveEnumTypes(r *parseResult, fd *dpb.FileDescriptorProto, prefix string, ed *dpb.EnumDescriptorProto, scopes []scope) error {
+ enumFqn := prefix + ed.GetName()
+ if ed.Options != nil {
+ if err := l.resolveOptions(r, fd, "enum", enumFqn, proto.MessageName(ed.Options), ed.Options.UninterpretedOption, scopes); err != nil {
+ return err
+ }
+ }
+ for _, evd := range ed.Value {
+ if evd.Options != nil {
+ evFqn := enumFqn + "." + evd.GetName()
+ if err := l.resolveOptions(r, fd, "enum value", evFqn, proto.MessageName(evd.Options), evd.Options.UninterpretedOption, scopes); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+func (l *linker) resolveMessageTypes(r *parseResult, fd *dpb.FileDescriptorProto, prefix string, md *dpb.DescriptorProto, scopes []scope) error {
+ fqn := prefix + md.GetName()
+ scope := messageScope(fqn, isProto3(fd), l.descriptorPool[fd])
+ scopes = append(scopes, scope)
+ prefix = fqn + "."
+
+ if md.Options != nil {
+ if err := l.resolveOptions(r, fd, "message", fqn, proto.MessageName(md.Options), md.Options.UninterpretedOption, scopes); err != nil {
+ return err
+ }
+ }
+
+ for _, nmd := range md.NestedType {
+ if err := l.resolveMessageTypes(r, fd, prefix, nmd, scopes); err != nil {
+ return err
+ }
+ }
+ for _, ned := range md.EnumType {
+ if err := l.resolveEnumTypes(r, fd, prefix, ned, scopes); err != nil {
+ return err
+ }
+ }
+ for _, fld := range md.Field {
+ if err := l.resolveFieldTypes(r, fd, prefix, fld, scopes); err != nil {
+ return err
+ }
+ }
+ for _, fld := range md.Extension {
+ if err := l.resolveFieldTypes(r, fd, prefix, fld, scopes); err != nil {
+ return err
+ }
+ }
+ for _, er := range md.ExtensionRange {
+ if er.Options != nil {
+ erName := fmt.Sprintf("%s:%d-%d", fqn, er.GetStart(), er.GetEnd()-1)
+ if err := l.resolveOptions(r, fd, "extension range", erName, proto.MessageName(er.Options), er.Options.UninterpretedOption, scopes); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+func (l *linker) resolveFieldTypes(r *parseResult, fd *dpb.FileDescriptorProto, prefix string, fld *dpb.FieldDescriptorProto, scopes []scope) error {
+ thisName := prefix + fld.GetName()
+ scope := fmt.Sprintf("field %s", thisName)
+ node := r.getFieldNode(fld)
+ elemType := "field"
+ if fld.GetExtendee() != "" {
+ fqn, dsc, _ := l.resolve(fd, fld.GetExtendee(), isMessage, scopes)
+ if dsc == nil {
+ return ErrorWithSourcePos{Pos: node.fieldExtendee().start(), Underlying: fmt.Errorf("unknown extendee type %s", fld.GetExtendee())}
+ }
+ extd, ok := dsc.(*dpb.DescriptorProto)
+ if !ok {
+ otherType := descriptorType(dsc)
+ return ErrorWithSourcePos{Pos: node.fieldExtendee().start(), Underlying: fmt.Errorf("extendee is invalid: %s is a %s, not a message", fqn, otherType)}
+ }
+ fld.Extendee = proto.String("." + fqn)
+ // make sure the tag number is in range
+ found := false
+ tag := fld.GetNumber()
+ for _, rng := range extd.ExtensionRange {
+ if tag >= rng.GetStart() && tag < rng.GetEnd() {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return ErrorWithSourcePos{Pos: node.fieldTag().start(), Underlying: fmt.Errorf("%s: tag %d is not in valid range for extended type %s", scope, tag, fqn)}
+ }
+ // make sure tag is not a duplicate
+ usedExtTags := l.extensions[fqn]
+ if usedExtTags == nil {
+ usedExtTags = map[int32]string{}
+ l.extensions[fqn] = usedExtTags
+ }
+ if other := usedExtTags[fld.GetNumber()]; other != "" {
+ return ErrorWithSourcePos{Pos: node.fieldTag().start(), Underlying: fmt.Errorf("%s: duplicate extension: %s and %s are both using tag %d", scope, other, thisName, fld.GetNumber())}
+ }
+ usedExtTags[fld.GetNumber()] = thisName
+ elemType = "extension"
+ }
+
+ if fld.Options != nil {
+ if err := l.resolveOptions(r, fd, elemType, thisName, proto.MessageName(fld.Options), fld.Options.UninterpretedOption, scopes); err != nil {
+ return err
+ }
+ }
+
+ if fld.GetTypeName() == "" {
+ // scalar type; no further resolution required
+ return nil
+ }
+
+ fqn, dsc, proto3 := l.resolve(fd, fld.GetTypeName(), isType, scopes)
+ if dsc == nil {
+ return ErrorWithSourcePos{Pos: node.fieldType().start(), Underlying: fmt.Errorf("%s: unknown type %s", scope, fld.GetTypeName())}
+ }
+ switch dsc := dsc.(type) {
+ case *dpb.DescriptorProto:
+ fld.TypeName = proto.String("." + fqn)
+ case *dpb.EnumDescriptorProto:
+ if fld.GetExtendee() == "" && isProto3(fd) && !proto3 {
+ // fields in a proto3 message cannot refer to proto2 enums
+ return ErrorWithSourcePos{Pos: node.fieldType().start(), Underlying: fmt.Errorf("%s: cannot use proto2 enum %s in a proto3 message", scope, fld.GetTypeName())}
+ }
+ fld.TypeName = proto.String("." + fqn)
+ // the type was tentatively set to message, but now we know it's actually an enum
+ fld.Type = dpb.FieldDescriptorProto_TYPE_ENUM.Enum()
+ default:
+ otherType := descriptorType(dsc)
+ return ErrorWithSourcePos{Pos: node.fieldType().start(), Underlying: fmt.Errorf("%s: invalid type: %s is a %s, not a message or enum", scope, fqn, otherType)}
+ }
+ return nil
+}
+
+func (l *linker) resolveServiceTypes(r *parseResult, fd *dpb.FileDescriptorProto, prefix string, sd *dpb.ServiceDescriptorProto, scopes []scope) error {
+ thisName := prefix + sd.GetName()
+ if sd.Options != nil {
+ if err := l.resolveOptions(r, fd, "service", thisName, proto.MessageName(sd.Options), sd.Options.UninterpretedOption, scopes); err != nil {
+ return err
+ }
+ }
+
+ for _, mtd := range sd.Method {
+ if mtd.Options != nil {
+ if err := l.resolveOptions(r, fd, "method", thisName+"."+mtd.GetName(), proto.MessageName(mtd.Options), mtd.Options.UninterpretedOption, scopes); err != nil {
+ return err
+ }
+ }
+ scope := fmt.Sprintf("method %s.%s", thisName, mtd.GetName())
+ node := r.getMethodNode(mtd)
+ fqn, dsc, _ := l.resolve(fd, mtd.GetInputType(), isMessage, scopes)
+ if dsc == nil {
+ return ErrorWithSourcePos{Pos: node.getInputType().start(), Underlying: fmt.Errorf("%s: unknown request type %s", scope, mtd.GetInputType())}
+ }
+ if _, ok := dsc.(*dpb.DescriptorProto); !ok {
+ otherType := descriptorType(dsc)
+ return ErrorWithSourcePos{Pos: node.getInputType().start(), Underlying: fmt.Errorf("%s: invalid request type: %s is a %s, not a message", scope, fqn, otherType)}
+ }
+ mtd.InputType = proto.String("." + fqn)
+
+ fqn, dsc, _ = l.resolve(fd, mtd.GetOutputType(), isMessage, scopes)
+ if dsc == nil {
+ return ErrorWithSourcePos{Pos: node.getOutputType().start(), Underlying: fmt.Errorf("%s: unknown response type %s", scope, mtd.GetOutputType())}
+ }
+ if _, ok := dsc.(*dpb.DescriptorProto); !ok {
+ otherType := descriptorType(dsc)
+ return ErrorWithSourcePos{Pos: node.getOutputType().start(), Underlying: fmt.Errorf("%s: invalid response type: %s is a %s, not a message", scope, fqn, otherType)}
+ }
+ mtd.OutputType = proto.String("." + fqn)
+ }
+ return nil
+}
+
+func (l *linker) resolveOptions(r *parseResult, fd *dpb.FileDescriptorProto, elemType, elemName, optType string, opts []*dpb.UninterpretedOption, scopes []scope) error {
+ var scope string
+ if elemType != "file" {
+ scope = fmt.Sprintf("%s %s: ", elemType, elemName)
+ }
+ for _, opt := range opts {
+ for _, nm := range opt.Name {
+ if nm.GetIsExtension() {
+ node := r.getOptionNamePartNode(nm)
+ fqn, dsc, _ := l.resolve(fd, nm.GetNamePart(), isField, scopes)
+ if dsc == nil {
+ return ErrorWithSourcePos{Pos: node.start(), Underlying: fmt.Errorf("%sunknown extension %s", scope, nm.GetNamePart())}
+ }
+ if ext, ok := dsc.(*dpb.FieldDescriptorProto); !ok {
+ otherType := descriptorType(dsc)
+ return ErrorWithSourcePos{Pos: node.start(), Underlying: fmt.Errorf("%sinvalid extension: %s is a %s, not an extension", scope, nm.GetNamePart(), otherType)}
+ } else if ext.GetExtendee() == "" {
+ return ErrorWithSourcePos{Pos: node.start(), Underlying: fmt.Errorf("%sinvalid extension: %s is a field but not an extension", scope, nm.GetNamePart())}
+ }
+ nm.NamePart = proto.String("." + fqn)
+ }
+ }
+ }
+ return nil
+}
+
+func (l *linker) resolve(fd *dpb.FileDescriptorProto, name string, allowed func(proto.Message) bool, scopes []scope) (fqn string, element proto.Message, proto3 bool) {
+ if strings.HasPrefix(name, ".") {
+ // already fully-qualified
+ d, proto3 := l.findSymbol(fd, name[1:], false, map[*dpb.FileDescriptorProto]struct{}{})
+ if d != nil {
+ return name[1:], d, proto3
+ }
+ } else {
+ // unqualified, so we look in the enclosing (last) scope first and move
+ // towards outermost (first) scope, trying to resolve the symbol
+ var bestGuess proto.Message
+ var bestGuessFqn string
+ var bestGuessProto3 bool
+ for i := len(scopes) - 1; i >= 0; i-- {
+ fqn, d, proto3 := scopes[i](name)
+ if d != nil {
+ if allowed(d) {
+ return fqn, d, proto3
+ } else if bestGuess == nil {
+ bestGuess = d
+ bestGuessFqn = fqn
+ bestGuessProto3 = proto3
+ }
+ }
+ }
+ // we return best guess, even though it was not an allowed kind of
+ // descriptor, so caller can print a better error message (e.g.
+ // indicating that the name was found but that it's the wrong type)
+ return bestGuessFqn, bestGuess, bestGuessProto3
+ }
+ return "", nil, false
+}
+
+func isField(m proto.Message) bool {
+ _, ok := m.(*dpb.FieldDescriptorProto)
+ return ok
+}
+
+func isMessage(m proto.Message) bool {
+ _, ok := m.(*dpb.DescriptorProto)
+ return ok
+}
+
+func isType(m proto.Message) bool {
+ switch m.(type) {
+ case *dpb.DescriptorProto, *dpb.EnumDescriptorProto:
+ return true
+ }
+ return false
+}
+
+// scope represents a lexical scope in a proto file in which messages and enums
+// can be declared.
+type scope func(symbol string) (fqn string, element proto.Message, proto3 bool)
+
+func fileScope(fd *dpb.FileDescriptorProto, l *linker) scope {
+ // we search symbols in this file, but also symbols in other files that have
+ // the same package as this file or a "parent" package (in protobuf,
+ // packages are a hierarchy like C++ namespaces)
+ prefixes := internal.CreatePrefixList(fd.GetPackage())
+ return func(name string) (string, proto.Message, bool) {
+ for _, prefix := range prefixes {
+ var n string
+ if prefix == "" {
+ n = name
+ } else {
+ n = prefix + "." + name
+ }
+ d, proto3 := l.findSymbol(fd, n, false, map[*dpb.FileDescriptorProto]struct{}{})
+ if d != nil {
+ return n, d, proto3
+ }
+ }
+ return "", nil, false
+ }
+}
+
+func messageScope(messageName string, proto3 bool, filePool map[string]proto.Message) scope {
+ return func(name string) (string, proto.Message, bool) {
+ n := messageName + "." + name
+ if d, ok := filePool[n]; ok {
+ return n, d, proto3
+ }
+ return "", nil, false
+ }
+}
+
+func (l *linker) findSymbol(fd *dpb.FileDescriptorProto, name string, public bool, checked map[*dpb.FileDescriptorProto]struct{}) (element proto.Message, proto3 bool) {
+ if _, ok := checked[fd]; ok {
+ // already checked this one
+ return nil, false
+ }
+ checked[fd] = struct{}{}
+ d := l.descriptorPool[fd][name]
+ if d != nil {
+ return d, isProto3(fd)
+ }
+
+ // When public = false, we are searching only directly imported symbols. But we
+ // also need to search transitive public imports due to semantics of public imports.
+ if public {
+ for _, depIndex := range fd.PublicDependency {
+ dep := fd.Dependency[depIndex]
+ depres := l.files[dep]
+ if depres == nil {
+ // we'll catch this error later
+ continue
+ }
+ if d, proto3 := l.findSymbol(depres.fd, name, true, checked); d != nil {
+ return d, proto3
+ }
+ }
+ } else {
+ for _, dep := range fd.Dependency {
+ depres := l.files[dep]
+ if depres == nil {
+ // we'll catch this error later
+ continue
+ }
+ if d, proto3 := l.findSymbol(depres.fd, name, true, checked); d != nil {
+ return d, proto3
+ }
+ }
+ }
+
+ return nil, false
+}
+
+func isProto3(fd *dpb.FileDescriptorProto) bool {
+ return fd.GetSyntax() == "proto3"
+}
+
+func (l *linker) createdLinkedDescriptors() (map[string]*desc.FileDescriptor, error) {
+ names := make([]string, 0, len(l.files))
+ for name := range l.files {
+ names = append(names, name)
+ }
+ sort.Strings(names)
+ linked := map[string]*desc.FileDescriptor{}
+ for _, name := range names {
+ if _, err := l.linkFile(name, nil, linked); err != nil {
+ return nil, err
+ }
+ }
+ return linked, nil
+}
+
+func (l *linker) linkFile(name string, seen []string, linked map[string]*desc.FileDescriptor) (*desc.FileDescriptor, error) {
+ // check for import cycle
+ for _, s := range seen {
+ if name == s {
+ var msg bytes.Buffer
+ first := true
+ for _, s := range seen {
+ if first {
+ first = false
+ } else {
+ msg.WriteString(" -> ")
+ }
+ fmt.Fprintf(&msg, "%q", s)
+ }
+ fmt.Fprintf(&msg, " -> %q", name)
+ return nil, fmt.Errorf("cycle found in imports: %s", msg.String())
+ }
+ }
+ seen = append(seen, name)
+
+ if lfd, ok := linked[name]; ok {
+ // already linked
+ return lfd, nil
+ }
+ r := l.files[name]
+ if r == nil {
+ importer := seen[len(seen)-2] // len-1 is *this* file, before that is the one that imported it
+ return nil, fmt.Errorf("no descriptor found for %q, imported by %q", name, importer)
+ }
+ var deps []*desc.FileDescriptor
+ for _, dep := range r.fd.Dependency {
+ ldep, err := l.linkFile(dep, seen, linked)
+ if err != nil {
+ return nil, err
+ }
+ deps = append(deps, ldep)
+ }
+ lfd, err := desc.CreateFileDescriptor(r.fd, deps...)
+ if err != nil {
+ return nil, fmt.Errorf("error linking %q: %s", name, err)
+ }
+ linked[name] = lfd
+ return lfd, nil
+}