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