1package protoparse 2 3import ( 4 "bytes" 5 "fmt" 6 "sort" 7 "strings" 8 9 "github.com/golang/protobuf/proto" 10 dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" 11 12 "github.com/jhump/protoreflect/desc" 13 "github.com/jhump/protoreflect/desc/internal" 14 "github.com/jhump/protoreflect/desc/protoparse/ast" 15) 16 17type linker struct { 18 files map[string]*parseResult 19 filenames []string 20 errs *errorHandler 21 descriptorPool map[*dpb.FileDescriptorProto]map[string]proto.Message 22 extensions map[string]map[int32]string 23} 24 25func newLinker(files *parseResults, errs *errorHandler) *linker { 26 return &linker{files: files.resultsByFilename, filenames: files.filenames, errs: errs} 27} 28 29func (l *linker) linkFiles() (map[string]*desc.FileDescriptor, error) { 30 // First, we put all symbols into a single pool, which lets us ensure there 31 // are no duplicate symbols and will also let us resolve and revise all type 32 // references in next step. 33 if err := l.createDescriptorPool(); err != nil { 34 return nil, err 35 } 36 37 // After we've populated the pool, we can now try to resolve all type 38 // references. All references must be checked for correct type, any fields 39 // with enum types must be corrected (since we parse them as if they are 40 // message references since we don't actually know message or enum until 41 // link time), and references will be re-written to be fully-qualified 42 // references (e.g. start with a dot "."). 43 if err := l.resolveReferences(); err != nil { 44 return nil, err 45 } 46 47 if err := l.errs.getError(); err != nil { 48 // we won't be able to create real descriptors if we've encountered 49 // errors up to this point, so bail at this point 50 return nil, err 51 } 52 53 // Now we've validated the descriptors, so we can link them into rich 54 // descriptors. This is a little redundant since that step does similar 55 // checking of symbols. But, without breaking encapsulation (e.g. exporting 56 // a lot of fields from desc package that are currently unexported) or 57 // merging this into the same package, we can't really prevent it. 58 linked, err := l.createdLinkedDescriptors() 59 if err != nil { 60 return nil, err 61 } 62 63 // Now that we have linked descriptors, we can interpret any uninterpreted 64 // options that remain. 65 for _, r := range l.files { 66 fd := linked[r.fd.GetName()] 67 if err := interpretFileOptions(r, richFileDescriptorish{FileDescriptor: fd}); err != nil { 68 return nil, err 69 } 70 // we should now have any message_set_wire_format options parsed 71 // and can do further validation on tag ranges 72 if err := checkExtensionsInFile(fd, r); err != nil { 73 return nil, err 74 } 75 } 76 77 // When Parser calls linkFiles, it does not check errs again, and it expects that linkFiles 78 // will return all errors it should process. If the ErrorReporter handles all errors itself 79 // and always returns nil, we should get ErrInvalidSource here, and need to propagate this 80 if err := l.errs.getError(); err != nil { 81 return nil, err 82 } 83 return linked, nil 84} 85 86func (l *linker) createDescriptorPool() error { 87 l.descriptorPool = map[*dpb.FileDescriptorProto]map[string]proto.Message{} 88 for _, filename := range l.filenames { 89 r := l.files[filename] 90 fd := r.fd 91 pool := map[string]proto.Message{} 92 l.descriptorPool[fd] = pool 93 prefix := fd.GetPackage() 94 if prefix != "" { 95 prefix += "." 96 } 97 for _, md := range fd.MessageType { 98 if err := addMessageToPool(r, pool, l.errs, prefix, md); err != nil { 99 return err 100 } 101 } 102 for _, fld := range fd.Extension { 103 if err := addFieldToPool(r, pool, l.errs, prefix, fld); err != nil { 104 return err 105 } 106 } 107 for _, ed := range fd.EnumType { 108 if err := addEnumToPool(r, pool, l.errs, prefix, ed); err != nil { 109 return err 110 } 111 } 112 for _, sd := range fd.Service { 113 if err := addServiceToPool(r, pool, l.errs, prefix, sd); err != nil { 114 return err 115 } 116 } 117 } 118 // try putting everything into a single pool, to ensure there are no duplicates 119 // across files (e.g. same symbol, but declared in two different files) 120 type entry struct { 121 file string 122 msg proto.Message 123 } 124 pool := map[string]entry{} 125 for _, filename := range l.filenames { 126 f := l.files[filename].fd 127 p := l.descriptorPool[f] 128 keys := make([]string, 0, len(p)) 129 for k := range p { 130 keys = append(keys, k) 131 } 132 sort.Strings(keys) // for deterministic error reporting 133 for _, k := range keys { 134 v := p[k] 135 if e, ok := pool[k]; ok { 136 desc1 := e.msg 137 file1 := e.file 138 desc2 := v 139 file2 := f.GetName() 140 if file2 < file1 { 141 file1, file2 = file2, file1 142 desc1, desc2 = desc2, desc1 143 } 144 node := l.files[file2].nodes[desc2] 145 if err := l.errs.handleErrorWithPos(node.Start(), "duplicate symbol %s: already defined as %s in %q", k, descriptorType(desc1), file1); err != nil { 146 return err 147 } 148 } 149 pool[k] = entry{file: f.GetName(), msg: v} 150 } 151 } 152 153 return nil 154} 155 156func addMessageToPool(r *parseResult, pool map[string]proto.Message, errs *errorHandler, prefix string, md *dpb.DescriptorProto) error { 157 fqn := prefix + md.GetName() 158 if err := addToPool(r, pool, errs, fqn, md); err != nil { 159 return err 160 } 161 prefix = fqn + "." 162 for _, fld := range md.Field { 163 if err := addFieldToPool(r, pool, errs, prefix, fld); err != nil { 164 return err 165 } 166 } 167 for _, fld := range md.Extension { 168 if err := addFieldToPool(r, pool, errs, prefix, fld); err != nil { 169 return err 170 } 171 } 172 for _, nmd := range md.NestedType { 173 if err := addMessageToPool(r, pool, errs, prefix, nmd); err != nil { 174 return err 175 } 176 } 177 for _, ed := range md.EnumType { 178 if err := addEnumToPool(r, pool, errs, prefix, ed); err != nil { 179 return err 180 } 181 } 182 return nil 183} 184 185func addFieldToPool(r *parseResult, pool map[string]proto.Message, errs *errorHandler, prefix string, fld *dpb.FieldDescriptorProto) error { 186 fqn := prefix + fld.GetName() 187 return addToPool(r, pool, errs, fqn, fld) 188} 189 190func addEnumToPool(r *parseResult, pool map[string]proto.Message, errs *errorHandler, prefix string, ed *dpb.EnumDescriptorProto) error { 191 fqn := prefix + ed.GetName() 192 if err := addToPool(r, pool, errs, fqn, ed); err != nil { 193 return err 194 } 195 for _, evd := range ed.Value { 196 vfqn := fqn + "." + evd.GetName() 197 if err := addToPool(r, pool, errs, vfqn, evd); err != nil { 198 return err 199 } 200 } 201 return nil 202} 203 204func addServiceToPool(r *parseResult, pool map[string]proto.Message, errs *errorHandler, prefix string, sd *dpb.ServiceDescriptorProto) error { 205 fqn := prefix + sd.GetName() 206 if err := addToPool(r, pool, errs, fqn, sd); err != nil { 207 return err 208 } 209 for _, mtd := range sd.Method { 210 mfqn := fqn + "." + mtd.GetName() 211 if err := addToPool(r, pool, errs, mfqn, mtd); err != nil { 212 return err 213 } 214 } 215 return nil 216} 217 218func addToPool(r *parseResult, pool map[string]proto.Message, errs *errorHandler, fqn string, dsc proto.Message) error { 219 if d, ok := pool[fqn]; ok { 220 node := r.nodes[dsc] 221 if err := errs.handleErrorWithPos(node.Start(), "duplicate symbol %s: already defined as %s", fqn, descriptorType(d)); err != nil { 222 return err 223 } 224 } 225 pool[fqn] = dsc 226 return nil 227} 228 229func descriptorType(m proto.Message) string { 230 switch m := m.(type) { 231 case *dpb.DescriptorProto: 232 return "message" 233 case *dpb.DescriptorProto_ExtensionRange: 234 return "extension range" 235 case *dpb.FieldDescriptorProto: 236 if m.GetExtendee() == "" { 237 return "field" 238 } else { 239 return "extension" 240 } 241 case *dpb.EnumDescriptorProto: 242 return "enum" 243 case *dpb.EnumValueDescriptorProto: 244 return "enum value" 245 case *dpb.ServiceDescriptorProto: 246 return "service" 247 case *dpb.MethodDescriptorProto: 248 return "method" 249 case *dpb.FileDescriptorProto: 250 return "file" 251 default: 252 // shouldn't be possible 253 return fmt.Sprintf("%T", m) 254 } 255} 256 257func (l *linker) resolveReferences() error { 258 l.extensions = map[string]map[int32]string{} 259 for _, filename := range l.filenames { 260 r := l.files[filename] 261 fd := r.fd 262 prefix := fd.GetPackage() 263 scopes := []scope{fileScope(fd, l)} 264 if prefix != "" { 265 prefix += "." 266 } 267 if fd.Options != nil { 268 if err := l.resolveOptions(r, fd, "file", fd.GetName(), proto.MessageName(fd.Options), fd.Options.UninterpretedOption, scopes); err != nil { 269 return err 270 } 271 } 272 for _, md := range fd.MessageType { 273 if err := l.resolveMessageTypes(r, fd, prefix, md, scopes); err != nil { 274 return err 275 } 276 } 277 for _, fld := range fd.Extension { 278 if err := l.resolveFieldTypes(r, fd, prefix, fld, scopes); err != nil { 279 return err 280 } 281 } 282 for _, ed := range fd.EnumType { 283 if err := l.resolveEnumTypes(r, fd, prefix, ed, scopes); err != nil { 284 return err 285 } 286 } 287 for _, sd := range fd.Service { 288 if err := l.resolveServiceTypes(r, fd, prefix, sd, scopes); err != nil { 289 return err 290 } 291 } 292 } 293 return nil 294} 295 296func (l *linker) resolveEnumTypes(r *parseResult, fd *dpb.FileDescriptorProto, prefix string, ed *dpb.EnumDescriptorProto, scopes []scope) error { 297 enumFqn := prefix + ed.GetName() 298 if ed.Options != nil { 299 if err := l.resolveOptions(r, fd, "enum", enumFqn, proto.MessageName(ed.Options), ed.Options.UninterpretedOption, scopes); err != nil { 300 return err 301 } 302 } 303 for _, evd := range ed.Value { 304 if evd.Options != nil { 305 evFqn := enumFqn + "." + evd.GetName() 306 if err := l.resolveOptions(r, fd, "enum value", evFqn, proto.MessageName(evd.Options), evd.Options.UninterpretedOption, scopes); err != nil { 307 return err 308 } 309 } 310 } 311 return nil 312} 313 314func (l *linker) resolveMessageTypes(r *parseResult, fd *dpb.FileDescriptorProto, prefix string, md *dpb.DescriptorProto, scopes []scope) error { 315 fqn := prefix + md.GetName() 316 scope := messageScope(fqn, isProto3(fd), l.descriptorPool[fd]) 317 scopes = append(scopes, scope) 318 prefix = fqn + "." 319 320 if md.Options != nil { 321 if err := l.resolveOptions(r, fd, "message", fqn, proto.MessageName(md.Options), md.Options.UninterpretedOption, scopes); err != nil { 322 return err 323 } 324 } 325 326 for _, nmd := range md.NestedType { 327 if err := l.resolveMessageTypes(r, fd, prefix, nmd, scopes); err != nil { 328 return err 329 } 330 } 331 for _, ned := range md.EnumType { 332 if err := l.resolveEnumTypes(r, fd, prefix, ned, scopes); err != nil { 333 return err 334 } 335 } 336 for _, fld := range md.Field { 337 if err := l.resolveFieldTypes(r, fd, prefix, fld, scopes); err != nil { 338 return err 339 } 340 } 341 for _, fld := range md.Extension { 342 if err := l.resolveFieldTypes(r, fd, prefix, fld, scopes); err != nil { 343 return err 344 } 345 } 346 for _, er := range md.ExtensionRange { 347 if er.Options != nil { 348 erName := fmt.Sprintf("%s:%d-%d", fqn, er.GetStart(), er.GetEnd()-1) 349 if err := l.resolveOptions(r, fd, "extension range", erName, proto.MessageName(er.Options), er.Options.UninterpretedOption, scopes); err != nil { 350 return err 351 } 352 } 353 } 354 return nil 355} 356 357func (l *linker) resolveFieldTypes(r *parseResult, fd *dpb.FileDescriptorProto, prefix string, fld *dpb.FieldDescriptorProto, scopes []scope) error { 358 thisName := prefix + fld.GetName() 359 scope := fmt.Sprintf("field %s", thisName) 360 node := r.getFieldNode(fld) 361 elemType := "field" 362 if fld.GetExtendee() != "" { 363 elemType = "extension" 364 fqn, dsc, _ := l.resolve(fd, fld.GetExtendee(), isMessage, scopes) 365 if dsc == nil { 366 return l.errs.handleErrorWithPos(node.FieldExtendee().Start(), "unknown extendee type %s", fld.GetExtendee()) 367 } 368 extd, ok := dsc.(*dpb.DescriptorProto) 369 if !ok { 370 otherType := descriptorType(dsc) 371 return l.errs.handleErrorWithPos(node.FieldExtendee().Start(), "extendee is invalid: %s is a %s, not a message", fqn, otherType) 372 } 373 fld.Extendee = proto.String("." + fqn) 374 // make sure the tag number is in range 375 found := false 376 tag := fld.GetNumber() 377 for _, rng := range extd.ExtensionRange { 378 if tag >= rng.GetStart() && tag < rng.GetEnd() { 379 found = true 380 break 381 } 382 } 383 if !found { 384 if err := l.errs.handleErrorWithPos(node.FieldTag().Start(), "%s: tag %d is not in valid range for extended type %s", scope, tag, fqn); err != nil { 385 return err 386 } 387 } else { 388 // make sure tag is not a duplicate 389 usedExtTags := l.extensions[fqn] 390 if usedExtTags == nil { 391 usedExtTags = map[int32]string{} 392 l.extensions[fqn] = usedExtTags 393 } 394 if other := usedExtTags[fld.GetNumber()]; other != "" { 395 if err := l.errs.handleErrorWithPos(node.FieldTag().Start(), "%s: duplicate extension: %s and %s are both using tag %d", scope, other, thisName, fld.GetNumber()); err != nil { 396 return err 397 } 398 } else { 399 usedExtTags[fld.GetNumber()] = thisName 400 } 401 } 402 } 403 404 if fld.Options != nil { 405 if err := l.resolveOptions(r, fd, elemType, thisName, proto.MessageName(fld.Options), fld.Options.UninterpretedOption, scopes); err != nil { 406 return err 407 } 408 } 409 410 if fld.GetTypeName() == "" { 411 // scalar type; no further resolution required 412 return nil 413 } 414 415 fqn, dsc, proto3 := l.resolve(fd, fld.GetTypeName(), isType, scopes) 416 if dsc == nil { 417 return l.errs.handleErrorWithPos(node.FieldType().Start(), "%s: unknown type %s", scope, fld.GetTypeName()) 418 } 419 switch dsc := dsc.(type) { 420 case *dpb.DescriptorProto: 421 fld.TypeName = proto.String("." + fqn) 422 // if type was tentatively unset, we now know it's actually a message 423 if fld.Type == nil { 424 fld.Type = dpb.FieldDescriptorProto_TYPE_MESSAGE.Enum() 425 } 426 case *dpb.EnumDescriptorProto: 427 if fld.GetExtendee() == "" && isProto3(fd) && !proto3 { 428 // fields in a proto3 message cannot refer to proto2 enums 429 return l.errs.handleErrorWithPos(node.FieldType().Start(), "%s: cannot use proto2 enum %s in a proto3 message", scope, fld.GetTypeName()) 430 } 431 fld.TypeName = proto.String("." + fqn) 432 // the type was tentatively unset, but now we know it's actually an enum 433 fld.Type = dpb.FieldDescriptorProto_TYPE_ENUM.Enum() 434 default: 435 otherType := descriptorType(dsc) 436 return l.errs.handleErrorWithPos(node.FieldType().Start(), "%s: invalid type: %s is a %s, not a message or enum", scope, fqn, otherType) 437 } 438 return nil 439} 440 441func (l *linker) resolveServiceTypes(r *parseResult, fd *dpb.FileDescriptorProto, prefix string, sd *dpb.ServiceDescriptorProto, scopes []scope) error { 442 thisName := prefix + sd.GetName() 443 if sd.Options != nil { 444 if err := l.resolveOptions(r, fd, "service", thisName, proto.MessageName(sd.Options), sd.Options.UninterpretedOption, scopes); err != nil { 445 return err 446 } 447 } 448 449 for _, mtd := range sd.Method { 450 if mtd.Options != nil { 451 if err := l.resolveOptions(r, fd, "method", thisName+"."+mtd.GetName(), proto.MessageName(mtd.Options), mtd.Options.UninterpretedOption, scopes); err != nil { 452 return err 453 } 454 } 455 scope := fmt.Sprintf("method %s.%s", thisName, mtd.GetName()) 456 node := r.getMethodNode(mtd) 457 fqn, dsc, _ := l.resolve(fd, mtd.GetInputType(), isMessage, scopes) 458 if dsc == nil { 459 if err := l.errs.handleErrorWithPos(node.GetInputType().Start(), "%s: unknown request type %s", scope, mtd.GetInputType()); err != nil { 460 return err 461 } 462 } else if _, ok := dsc.(*dpb.DescriptorProto); !ok { 463 otherType := descriptorType(dsc) 464 if err := l.errs.handleErrorWithPos(node.GetInputType().Start(), "%s: invalid request type: %s is a %s, not a message", scope, fqn, otherType); err != nil { 465 return err 466 } 467 } else { 468 mtd.InputType = proto.String("." + fqn) 469 } 470 471 fqn, dsc, _ = l.resolve(fd, mtd.GetOutputType(), isMessage, scopes) 472 if dsc == nil { 473 if err := l.errs.handleErrorWithPos(node.GetOutputType().Start(), "%s: unknown response type %s", scope, mtd.GetOutputType()); err != nil { 474 return err 475 } 476 } else if _, ok := dsc.(*dpb.DescriptorProto); !ok { 477 otherType := descriptorType(dsc) 478 if err := l.errs.handleErrorWithPos(node.GetOutputType().Start(), "%s: invalid response type: %s is a %s, not a message", scope, fqn, otherType); err != nil { 479 return err 480 } 481 } else { 482 mtd.OutputType = proto.String("." + fqn) 483 } 484 } 485 return nil 486} 487 488func (l *linker) resolveOptions(r *parseResult, fd *dpb.FileDescriptorProto, elemType, elemName, optType string, opts []*dpb.UninterpretedOption, scopes []scope) error { 489 var scope string 490 if elemType != "file" { 491 scope = fmt.Sprintf("%s %s: ", elemType, elemName) 492 } 493opts: 494 for _, opt := range opts { 495 for _, nm := range opt.Name { 496 if nm.GetIsExtension() { 497 node := r.getOptionNamePartNode(nm) 498 fqn, dsc, _ := l.resolve(fd, nm.GetNamePart(), isField, scopes) 499 if dsc == nil { 500 if err := l.errs.handleErrorWithPos(node.Start(), "%sunknown extension %s", scope, nm.GetNamePart()); err != nil { 501 return err 502 } 503 continue opts 504 } 505 if ext, ok := dsc.(*dpb.FieldDescriptorProto); !ok { 506 otherType := descriptorType(dsc) 507 if err := l.errs.handleErrorWithPos(node.Start(), "%sinvalid extension: %s is a %s, not an extension", scope, nm.GetNamePart(), otherType); err != nil { 508 return err 509 } 510 continue opts 511 } else if ext.GetExtendee() == "" { 512 if err := l.errs.handleErrorWithPos(node.Start(), "%sinvalid extension: %s is a field but not an extension", scope, nm.GetNamePart()); err != nil { 513 return err 514 } 515 continue opts 516 } 517 nm.NamePart = proto.String("." + fqn) 518 } 519 } 520 } 521 return nil 522} 523 524func (l *linker) resolve(fd *dpb.FileDescriptorProto, name string, allowed func(proto.Message) bool, scopes []scope) (fqn string, element proto.Message, proto3 bool) { 525 if strings.HasPrefix(name, ".") { 526 // already fully-qualified 527 d, proto3 := l.findSymbol(fd, name[1:], false, map[*dpb.FileDescriptorProto]struct{}{}) 528 if d != nil { 529 return name[1:], d, proto3 530 } 531 } else { 532 // unqualified, so we look in the enclosing (last) scope first and move 533 // towards outermost (first) scope, trying to resolve the symbol 534 var bestGuess proto.Message 535 var bestGuessFqn string 536 var bestGuessProto3 bool 537 for i := len(scopes) - 1; i >= 0; i-- { 538 fqn, d, proto3 := scopes[i](name) 539 if d != nil { 540 if allowed(d) { 541 return fqn, d, proto3 542 } else if bestGuess == nil { 543 bestGuess = d 544 bestGuessFqn = fqn 545 bestGuessProto3 = proto3 546 } 547 } 548 } 549 // we return best guess, even though it was not an allowed kind of 550 // descriptor, so caller can print a better error message (e.g. 551 // indicating that the name was found but that it's the wrong type) 552 return bestGuessFqn, bestGuess, bestGuessProto3 553 } 554 return "", nil, false 555} 556 557func isField(m proto.Message) bool { 558 _, ok := m.(*dpb.FieldDescriptorProto) 559 return ok 560} 561 562func isMessage(m proto.Message) bool { 563 _, ok := m.(*dpb.DescriptorProto) 564 return ok 565} 566 567func isType(m proto.Message) bool { 568 switch m.(type) { 569 case *dpb.DescriptorProto, *dpb.EnumDescriptorProto: 570 return true 571 } 572 return false 573} 574 575// scope represents a lexical scope in a proto file in which messages and enums 576// can be declared. 577type scope func(symbol string) (fqn string, element proto.Message, proto3 bool) 578 579func fileScope(fd *dpb.FileDescriptorProto, l *linker) scope { 580 // we search symbols in this file, but also symbols in other files that have 581 // the same package as this file or a "parent" package (in protobuf, 582 // packages are a hierarchy like C++ namespaces) 583 prefixes := internal.CreatePrefixList(fd.GetPackage()) 584 return func(name string) (string, proto.Message, bool) { 585 for _, prefix := range prefixes { 586 var n string 587 if prefix == "" { 588 n = name 589 } else { 590 n = prefix + "." + name 591 } 592 d, proto3 := l.findSymbol(fd, n, false, map[*dpb.FileDescriptorProto]struct{}{}) 593 if d != nil { 594 return n, d, proto3 595 } 596 } 597 return "", nil, false 598 } 599} 600 601func messageScope(messageName string, proto3 bool, filePool map[string]proto.Message) scope { 602 return func(name string) (string, proto.Message, bool) { 603 n := messageName + "." + name 604 if d, ok := filePool[n]; ok { 605 return n, d, proto3 606 } 607 return "", nil, false 608 } 609} 610 611func (l *linker) findSymbol(fd *dpb.FileDescriptorProto, name string, public bool, checked map[*dpb.FileDescriptorProto]struct{}) (element proto.Message, proto3 bool) { 612 if _, ok := checked[fd]; ok { 613 // already checked this one 614 return nil, false 615 } 616 checked[fd] = struct{}{} 617 d := l.descriptorPool[fd][name] 618 if d != nil { 619 return d, isProto3(fd) 620 } 621 622 // When public = false, we are searching only directly imported symbols. But we 623 // also need to search transitive public imports due to semantics of public imports. 624 if public { 625 for _, depIndex := range fd.PublicDependency { 626 dep := fd.Dependency[depIndex] 627 depres := l.files[dep] 628 if depres == nil { 629 // we'll catch this error later 630 continue 631 } 632 if d, proto3 := l.findSymbol(depres.fd, name, true, checked); d != nil { 633 return d, proto3 634 } 635 } 636 } else { 637 for _, dep := range fd.Dependency { 638 depres := l.files[dep] 639 if depres == nil { 640 // we'll catch this error later 641 continue 642 } 643 if d, proto3 := l.findSymbol(depres.fd, name, true, checked); d != nil { 644 return d, proto3 645 } 646 } 647 } 648 649 return nil, false 650} 651 652func isProto3(fd *dpb.FileDescriptorProto) bool { 653 return fd.GetSyntax() == "proto3" 654} 655 656func (l *linker) createdLinkedDescriptors() (map[string]*desc.FileDescriptor, error) { 657 names := make([]string, 0, len(l.files)) 658 for name := range l.files { 659 names = append(names, name) 660 } 661 sort.Strings(names) 662 linked := map[string]*desc.FileDescriptor{} 663 for _, name := range names { 664 if _, err := l.linkFile(name, nil, nil, linked); err != nil { 665 return nil, err 666 } 667 } 668 return linked, nil 669} 670 671func (l *linker) linkFile(name string, rootImportLoc *SourcePos, seen []string, linked map[string]*desc.FileDescriptor) (*desc.FileDescriptor, error) { 672 // check for import cycle 673 for _, s := range seen { 674 if name == s { 675 var msg bytes.Buffer 676 first := true 677 for _, s := range seen { 678 if first { 679 first = false 680 } else { 681 msg.WriteString(" -> ") 682 } 683 _, _ = fmt.Fprintf(&msg, "%q", s) 684 } 685 _, _ = fmt.Fprintf(&msg, " -> %q", name) 686 return nil, ErrorWithSourcePos{ 687 Underlying: fmt.Errorf("cycle found in imports: %s", msg.String()), 688 Pos: rootImportLoc, 689 } 690 } 691 } 692 seen = append(seen, name) 693 694 if lfd, ok := linked[name]; ok { 695 // already linked 696 return lfd, nil 697 } 698 r := l.files[name] 699 if r == nil { 700 importer := seen[len(seen)-2] // len-1 is *this* file, before that is the one that imported it 701 return nil, fmt.Errorf("no descriptor found for %q, imported by %q", name, importer) 702 } 703 var deps []*desc.FileDescriptor 704 if rootImportLoc == nil { 705 // try to find a source location for this "root" import 706 decl := r.getFileNode(r.fd) 707 fnode, ok := decl.(*ast.FileNode) 708 if ok { 709 for _, decl := range fnode.Decls { 710 if dep, ok := decl.(*ast.ImportNode); ok { 711 ldep, err := l.linkFile(dep.Name.AsString(), dep.Name.Start(), seen, linked) 712 if err != nil { 713 return nil, err 714 } 715 deps = append(deps, ldep) 716 } 717 } 718 } else { 719 // no AST? just use the descriptor 720 for _, dep := range r.fd.Dependency { 721 ldep, err := l.linkFile(dep, decl.Start(), seen, linked) 722 if err != nil { 723 return nil, err 724 } 725 deps = append(deps, ldep) 726 } 727 } 728 } else { 729 // we can just use the descriptor since we don't need source location 730 // (we'll just attribute any import cycles found to the "root" import) 731 for _, dep := range r.fd.Dependency { 732 ldep, err := l.linkFile(dep, rootImportLoc, seen, linked) 733 if err != nil { 734 return nil, err 735 } 736 deps = append(deps, ldep) 737 } 738 } 739 lfd, err := desc.CreateFileDescriptor(r.fd, deps...) 740 if err != nil { 741 return nil, fmt.Errorf("error linking %q: %s", name, err) 742 } 743 linked[name] = lfd 744 return lfd, nil 745} 746