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