github.com/Khushbukela/protoreflect@v1.0.1/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/jhump/protoreflect/desc" 13 "github.com/jhump/protoreflect/desc/internal" 14 "github.com/jhump/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 _, ood := range md.OneofDecl { 184 if err := addOneofToPool(r, pool, errs, prefix, ood); err != nil { 185 return err 186 } 187 } 188 for _, fld := range md.Field { 189 if err := addFieldToPool(r, pool, errs, prefix, fld); err != nil { 190 return err 191 } 192 } 193 for _, fld := range md.Extension { 194 if err := addFieldToPool(r, pool, errs, prefix, fld); err != nil { 195 return err 196 } 197 } 198 for _, nmd := range md.NestedType { 199 if err := addMessageToPool(r, pool, errs, prefix, nmd); err != nil { 200 return err 201 } 202 } 203 for _, ed := range md.EnumType { 204 if err := addEnumToPool(r, pool, errs, prefix, ed); err != nil { 205 return err 206 } 207 } 208 return nil 209 } 210 211 func addFieldToPool(r *parseResult, pool map[string]proto.Message, errs *errorHandler, prefix string, fld *dpb.FieldDescriptorProto) error { 212 fqn := prefix + fld.GetName() 213 return addToPool(r, pool, errs, fqn, fld) 214 } 215 216 func addOneofToPool(r *parseResult, pool map[string]proto.Message, errs *errorHandler, prefix string, ood *dpb.OneofDescriptorProto) error { 217 fqn := prefix + ood.GetName() 218 return addToPool(r, pool, errs, fqn, ood) 219 } 220 221 func addEnumToPool(r *parseResult, pool map[string]proto.Message, errs *errorHandler, prefix string, ed *dpb.EnumDescriptorProto) error { 222 fqn := prefix + ed.GetName() 223 if err := addToPool(r, pool, errs, fqn, ed); err != nil { 224 return err 225 } 226 for _, evd := range ed.Value { 227 // protobuf name-scoping rules for enum values follow C++ scoping rules: 228 // the enum value name is a symbol in the *parent* scope (the one 229 // enclosing the enum). 230 vfqn := prefix + evd.GetName() 231 if err := addToPool(r, pool, errs, vfqn, evd); err != nil { 232 return err 233 } 234 } 235 return nil 236 } 237 238 func addServiceToPool(r *parseResult, pool map[string]proto.Message, errs *errorHandler, prefix string, sd *dpb.ServiceDescriptorProto) error { 239 fqn := prefix + sd.GetName() 240 if err := addToPool(r, pool, errs, fqn, sd); err != nil { 241 return err 242 } 243 for _, mtd := range sd.Method { 244 mfqn := fqn + "." + mtd.GetName() 245 if err := addToPool(r, pool, errs, mfqn, mtd); err != nil { 246 return err 247 } 248 } 249 return nil 250 } 251 252 func addToPool(r *parseResult, pool map[string]proto.Message, errs *errorHandler, fqn string, dsc proto.Message) error { 253 if d, ok := pool[fqn]; ok { 254 node := r.nodes[dsc] 255 _, additionIsEnumVal := dsc.(*dpb.EnumValueDescriptorProto) 256 _, existingIsEnumVal := d.(*dpb.EnumValueDescriptorProto) 257 // because of weird scoping for enum values, provide more context in error message 258 // if this conflict is with an enum value 259 var suffix string 260 if additionIsEnumVal || existingIsEnumVal { 261 suffix = "; protobuf uses C++ scoping rules for enum values, so they exist in the scope enclosing the enum" 262 } 263 // TODO: also include the source location for the conflicting symbol 264 if err := errs.handleErrorWithPos(node.Start(), "duplicate symbol %s: already defined as %s%s", fqn, descriptorType(d), suffix); err != nil { 265 return err 266 } 267 } 268 pool[fqn] = dsc 269 return nil 270 } 271 272 func descriptorType(m proto.Message) string { 273 switch m := m.(type) { 274 case *dpb.DescriptorProto: 275 return "message" 276 case *dpb.DescriptorProto_ExtensionRange: 277 return "extension range" 278 case *dpb.FieldDescriptorProto: 279 if m.GetExtendee() == "" { 280 return "field" 281 } else { 282 return "extension" 283 } 284 case *dpb.EnumDescriptorProto: 285 return "enum" 286 case *dpb.EnumValueDescriptorProto: 287 return "enum value" 288 case *dpb.ServiceDescriptorProto: 289 return "service" 290 case *dpb.MethodDescriptorProto: 291 return "method" 292 case *dpb.FileDescriptorProto: 293 return "file" 294 case *dpb.OneofDescriptorProto: 295 return "oneof" 296 default: 297 // shouldn't be possible 298 return fmt.Sprintf("%T", m) 299 } 300 } 301 302 func (l *linker) resolveReferences() error { 303 l.extensions = map[string]map[int32]string{} 304 l.usedImports = map[*dpb.FileDescriptorProto]map[string]struct{}{} 305 for _, filename := range l.filenames { 306 r := l.files[filename] 307 fd := r.fd 308 prefix := fd.GetPackage() 309 scopes := []scope{fileScope(fd, l)} 310 if prefix != "" { 311 prefix += "." 312 } 313 if fd.Options != nil { 314 if err := l.resolveOptions(r, fd, "file", fd.GetName(), proto.MessageName(fd.Options), fd.Options.UninterpretedOption, scopes); err != nil { 315 return err 316 } 317 } 318 for _, md := range fd.MessageType { 319 if err := l.resolveMessageTypes(r, fd, prefix, md, scopes); err != nil { 320 return err 321 } 322 } 323 for _, fld := range fd.Extension { 324 if err := l.resolveFieldTypes(r, fd, prefix, fld, scopes); err != nil { 325 return err 326 } 327 } 328 for _, ed := range fd.EnumType { 329 if err := l.resolveEnumTypes(r, fd, prefix, ed, scopes); err != nil { 330 return err 331 } 332 } 333 for _, sd := range fd.Service { 334 if err := l.resolveServiceTypes(r, fd, prefix, sd, scopes); err != nil { 335 return err 336 } 337 } 338 } 339 return nil 340 } 341 342 func (l *linker) resolveEnumTypes(r *parseResult, fd *dpb.FileDescriptorProto, prefix string, ed *dpb.EnumDescriptorProto, scopes []scope) error { 343 enumFqn := prefix + ed.GetName() 344 if ed.Options != nil { 345 if err := l.resolveOptions(r, fd, "enum", enumFqn, proto.MessageName(ed.Options), ed.Options.UninterpretedOption, scopes); err != nil { 346 return err 347 } 348 } 349 for _, evd := range ed.Value { 350 if evd.Options != nil { 351 evFqn := enumFqn + "." + evd.GetName() 352 if err := l.resolveOptions(r, fd, "enum value", evFqn, proto.MessageName(evd.Options), evd.Options.UninterpretedOption, scopes); err != nil { 353 return err 354 } 355 } 356 } 357 return nil 358 } 359 360 func (l *linker) resolveMessageTypes(r *parseResult, fd *dpb.FileDescriptorProto, prefix string, md *dpb.DescriptorProto, scopes []scope) error { 361 fqn := prefix + md.GetName() 362 scope := messageScope(fqn, isProto3(fd), l, fd) 363 scopes = append(scopes, scope) 364 prefix = fqn + "." 365 366 if md.Options != nil { 367 if err := l.resolveOptions(r, fd, "message", fqn, proto.MessageName(md.Options), md.Options.UninterpretedOption, scopes); err != nil { 368 return err 369 } 370 } 371 372 for _, nmd := range md.NestedType { 373 if err := l.resolveMessageTypes(r, fd, prefix, nmd, scopes); err != nil { 374 return err 375 } 376 } 377 for _, ned := range md.EnumType { 378 if err := l.resolveEnumTypes(r, fd, prefix, ned, scopes); err != nil { 379 return err 380 } 381 } 382 for _, fld := range md.Field { 383 if err := l.resolveFieldTypes(r, fd, prefix, fld, scopes); err != nil { 384 return err 385 } 386 } 387 for _, ood := range md.OneofDecl { 388 if ood.Options != nil { 389 ooName := fmt.Sprintf("%s.%s", fqn, ood.GetName()) 390 if err := l.resolveOptions(r, fd, "oneof", ooName, proto.MessageName(ood.Options), ood.Options.UninterpretedOption, scopes); err != nil { 391 return err 392 } 393 } 394 } 395 for _, fld := range md.Extension { 396 if err := l.resolveFieldTypes(r, fd, prefix, fld, scopes); err != nil { 397 return err 398 } 399 } 400 for _, er := range md.ExtensionRange { 401 if er.Options != nil { 402 erName := fmt.Sprintf("%s:%d-%d", fqn, er.GetStart(), er.GetEnd()-1) 403 if err := l.resolveOptions(r, fd, "extension range", erName, proto.MessageName(er.Options), er.Options.UninterpretedOption, scopes); err != nil { 404 return err 405 } 406 } 407 } 408 return nil 409 } 410 411 func (l *linker) resolveFieldTypes(r *parseResult, fd *dpb.FileDescriptorProto, prefix string, fld *dpb.FieldDescriptorProto, scopes []scope) error { 412 thisName := prefix + fld.GetName() 413 scope := fmt.Sprintf("field %s", thisName) 414 node := r.getFieldNode(fld) 415 elemType := "field" 416 if fld.GetExtendee() != "" { 417 elemType = "extension" 418 fqn, dsc, _ := l.resolve(fd, fld.GetExtendee(), true, scopes) 419 if dsc == nil { 420 return l.errs.handleErrorWithPos(node.FieldExtendee().Start(), "unknown extendee type %s", fld.GetExtendee()) 421 } 422 if dsc == sentinelMissingSymbol { 423 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) 424 } 425 extd, ok := dsc.(*dpb.DescriptorProto) 426 if !ok { 427 otherType := descriptorType(dsc) 428 return l.errs.handleErrorWithPos(node.FieldExtendee().Start(), "extendee is invalid: %s is a %s, not a message", fqn, otherType) 429 } 430 fld.Extendee = proto.String("." + fqn) 431 // make sure the tag number is in range 432 found := false 433 tag := fld.GetNumber() 434 for _, rng := range extd.ExtensionRange { 435 if tag >= rng.GetStart() && tag < rng.GetEnd() { 436 found = true 437 break 438 } 439 } 440 if !found { 441 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 { 442 return err 443 } 444 } else { 445 // make sure tag is not a duplicate 446 usedExtTags := l.extensions[fqn] 447 if usedExtTags == nil { 448 usedExtTags = map[int32]string{} 449 l.extensions[fqn] = usedExtTags 450 } 451 if other := usedExtTags[fld.GetNumber()]; other != "" { 452 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 { 453 return err 454 } 455 } else { 456 usedExtTags[fld.GetNumber()] = thisName 457 } 458 } 459 } 460 461 if fld.Options != nil { 462 if err := l.resolveOptions(r, fd, elemType, thisName, proto.MessageName(fld.Options), fld.Options.UninterpretedOption, scopes); err != nil { 463 return err 464 } 465 } 466 467 if fld.GetTypeName() == "" { 468 // scalar type; no further resolution required 469 return nil 470 } 471 472 fqn, dsc, proto3 := l.resolve(fd, fld.GetTypeName(), true, scopes) 473 if dsc == nil { 474 return l.errs.handleErrorWithPos(node.FieldType().Start(), "%s: unknown type %s", scope, fld.GetTypeName()) 475 } 476 if dsc == sentinelMissingSymbol { 477 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) 478 } 479 switch dsc := dsc.(type) { 480 case *dpb.DescriptorProto: 481 fld.TypeName = proto.String("." + fqn) 482 // if type was tentatively unset, we now know it's actually a message 483 if fld.Type == nil { 484 fld.Type = dpb.FieldDescriptorProto_TYPE_MESSAGE.Enum() 485 } 486 case *dpb.EnumDescriptorProto: 487 if fld.GetExtendee() == "" && isProto3(fd) && !proto3 { 488 // fields in a proto3 message cannot refer to proto2 enums 489 return l.errs.handleErrorWithPos(node.FieldType().Start(), "%s: cannot use proto2 enum %s in a proto3 message", scope, fld.GetTypeName()) 490 } 491 fld.TypeName = proto.String("." + fqn) 492 // the type was tentatively unset, but now we know it's actually an enum 493 fld.Type = dpb.FieldDescriptorProto_TYPE_ENUM.Enum() 494 default: 495 otherType := descriptorType(dsc) 496 return l.errs.handleErrorWithPos(node.FieldType().Start(), "%s: invalid type: %s is a %s, not a message or enum", scope, fqn, otherType) 497 } 498 return nil 499 } 500 501 func (l *linker) resolveServiceTypes(r *parseResult, fd *dpb.FileDescriptorProto, prefix string, sd *dpb.ServiceDescriptorProto, scopes []scope) error { 502 svcFqn := prefix + sd.GetName() 503 if sd.Options != nil { 504 if err := l.resolveOptions(r, fd, "service", svcFqn, proto.MessageName(sd.Options), sd.Options.UninterpretedOption, scopes); err != nil { 505 return err 506 } 507 } 508 509 // not a message, but same scoping rules for nested elements as if it were 510 scope := messageScope(svcFqn, isProto3(fd), l, fd) 511 scopes = append(scopes, scope) 512 513 for _, mtd := range sd.Method { 514 if mtd.Options != nil { 515 if err := l.resolveOptions(r, fd, "method", svcFqn+"."+mtd.GetName(), proto.MessageName(mtd.Options), mtd.Options.UninterpretedOption, scopes); err != nil { 516 return err 517 } 518 } 519 scope := fmt.Sprintf("method %s.%s", svcFqn, mtd.GetName()) 520 node := r.getMethodNode(mtd) 521 fqn, dsc, _ := l.resolve(fd, mtd.GetInputType(), false, scopes) 522 if dsc == nil { 523 if err := l.errs.handleErrorWithPos(node.GetInputType().Start(), "%s: unknown request type %s", scope, mtd.GetInputType()); err != nil { 524 return err 525 } 526 } else if dsc == sentinelMissingSymbol { 527 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 { 528 return err 529 } 530 } else if _, ok := dsc.(*dpb.DescriptorProto); !ok { 531 otherType := descriptorType(dsc) 532 if err := l.errs.handleErrorWithPos(node.GetInputType().Start(), "%s: invalid request type: %s is a %s, not a message", scope, fqn, otherType); err != nil { 533 return err 534 } 535 } else { 536 mtd.InputType = proto.String("." + fqn) 537 } 538 539 // TODO: make input and output type resolution more DRY 540 fqn, dsc, _ = l.resolve(fd, mtd.GetOutputType(), false, scopes) 541 if dsc == nil { 542 if err := l.errs.handleErrorWithPos(node.GetOutputType().Start(), "%s: unknown response type %s", scope, mtd.GetOutputType()); err != nil { 543 return err 544 } 545 } else if dsc == sentinelMissingSymbol { 546 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 { 547 return err 548 } 549 } else if _, ok := dsc.(*dpb.DescriptorProto); !ok { 550 otherType := descriptorType(dsc) 551 if err := l.errs.handleErrorWithPos(node.GetOutputType().Start(), "%s: invalid response type: %s is a %s, not a message", scope, fqn, otherType); err != nil { 552 return err 553 } 554 } else { 555 mtd.OutputType = proto.String("." + fqn) 556 } 557 } 558 return nil 559 } 560 561 func (l *linker) resolveOptions(r *parseResult, fd *dpb.FileDescriptorProto, elemType, elemName, optType string, opts []*dpb.UninterpretedOption, scopes []scope) error { 562 var scope string 563 if elemType != "file" { 564 scope = fmt.Sprintf("%s %s: ", elemType, elemName) 565 } 566 opts: 567 for _, opt := range opts { 568 for _, nm := range opt.Name { 569 if nm.GetIsExtension() { 570 node := r.getOptionNamePartNode(nm) 571 fqn, dsc, _ := l.resolve(fd, nm.GetNamePart(), false, scopes) 572 if dsc == nil { 573 if err := l.errs.handleErrorWithPos(node.Start(), "%sunknown extension %s", scope, nm.GetNamePart()); err != nil { 574 return err 575 } 576 continue opts 577 } 578 if dsc == sentinelMissingSymbol { 579 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 { 580 return err 581 } 582 continue opts 583 } 584 if ext, ok := dsc.(*dpb.FieldDescriptorProto); !ok { 585 otherType := descriptorType(dsc) 586 if err := l.errs.handleErrorWithPos(node.Start(), "%sinvalid extension: %s is a %s, not an extension", scope, nm.GetNamePart(), otherType); err != nil { 587 return err 588 } 589 continue opts 590 } else if ext.GetExtendee() == "" { 591 if err := l.errs.handleErrorWithPos(node.Start(), "%sinvalid extension: %s is a field but not an extension", scope, nm.GetNamePart()); err != nil { 592 return err 593 } 594 continue opts 595 } 596 nm.NamePart = proto.String("." + fqn) 597 } 598 } 599 } 600 return nil 601 } 602 603 func (l *linker) resolve(fd *dpb.FileDescriptorProto, name string, onlyTypes bool, scopes []scope) (fqn string, element proto.Message, proto3 bool) { 604 if strings.HasPrefix(name, ".") { 605 // already fully-qualified 606 d, proto3 := l.findSymbol(fd, name[1:]) 607 if d != nil { 608 return name[1:], d, proto3 609 } 610 return "", nil, false 611 } 612 // unqualified, so we look in the enclosing (last) scope first and move 613 // towards outermost (first) scope, trying to resolve the symbol 614 pos := strings.IndexByte(name, '.') 615 firstName := name 616 if pos > 0 { 617 firstName = name[:pos] 618 } 619 var bestGuess proto.Message 620 var bestGuessFqn string 621 var bestGuessProto3 bool 622 for i := len(scopes) - 1; i >= 0; i-- { 623 fqn, d, proto3 := scopes[i](firstName, name) 624 if d != nil { 625 if !onlyTypes || isType(d) { 626 return fqn, d, proto3 627 } else if bestGuess == nil { 628 bestGuess = d 629 bestGuessFqn = fqn 630 bestGuessProto3 = proto3 631 } 632 } 633 } 634 // we return best guess, even though it was not an allowed kind of 635 // descriptor, so caller can print a better error message (e.g. 636 // indicating that the name was found but that it's the wrong type) 637 return bestGuessFqn, bestGuess, bestGuessProto3 638 } 639 640 func isType(m proto.Message) bool { 641 switch m.(type) { 642 case *dpb.DescriptorProto, *dpb.EnumDescriptorProto: 643 return true 644 } 645 return false 646 } 647 648 // scope represents a lexical scope in a proto file in which messages and enums 649 // can be declared. 650 type scope func(firstName, fullName string) (fqn string, element proto.Message, proto3 bool) 651 652 func fileScope(fd *dpb.FileDescriptorProto, l *linker) scope { 653 // we search symbols in this file, but also symbols in other files that have 654 // the same package as this file or a "parent" package (in protobuf, 655 // packages are a hierarchy like C++ namespaces) 656 prefixes := internal.CreatePrefixList(fd.GetPackage()) 657 querySymbol := func(n string) (d proto.Message, isProto3 bool) { 658 return l.findSymbol(fd, n) 659 } 660 return func(firstName, fullName string) (string, proto.Message, bool) { 661 for _, prefix := range prefixes { 662 var n1, n string 663 if prefix == "" { 664 // exhausted all prefixes, so it must be in this one 665 n1, n = fullName, fullName 666 } else { 667 n = prefix + "." + fullName 668 n1 = prefix + "." + firstName 669 } 670 d, proto3 := findSymbolRelative(n1, n, querySymbol) 671 if d != nil { 672 return n, d, proto3 673 } 674 } 675 return "", nil, false 676 } 677 } 678 679 func messageScope(messageName string, proto3 bool, l *linker, fd *dpb.FileDescriptorProto) scope { 680 querySymbol := func(n string) (d proto.Message, isProto3 bool) { 681 return l.findSymbolInFile(n, fd), false 682 } 683 return func(firstName, fullName string) (string, proto.Message, bool) { 684 n1 := messageName + "." + firstName 685 n := messageName + "." + fullName 686 d, _ := findSymbolRelative(n1, n, querySymbol) 687 if d != nil { 688 return n, d, proto3 689 } 690 return "", nil, false 691 } 692 } 693 694 func findSymbolRelative(firstName, fullName string, query func(name string) (d proto.Message, isProto3 bool)) (d proto.Message, isProto3 bool) { 695 d, proto3 := query(firstName) 696 if d == nil { 697 return nil, false 698 } 699 if firstName == fullName { 700 return d, proto3 701 } 702 if !isAggregateDescriptor(d) { 703 // can't possibly find the rest of full name if 704 // the first name indicated a leaf descriptor 705 return nil, false 706 } 707 d, proto3 = query(fullName) 708 if d == nil { 709 return sentinelMissingSymbol, false 710 } 711 return d, proto3 712 } 713 714 func (l *linker) findSymbolInFile(name string, fd *dpb.FileDescriptorProto) proto.Message { 715 d, ok := l.descriptorPool[fd][name] 716 if ok { 717 return d 718 } 719 _, ok = l.packageNamespaces[fd][name] 720 if ok { 721 // this sentinel means the name is a valid namespace but 722 // does not refer to a descriptor 723 return sentinelMissingSymbol 724 } 725 return nil 726 } 727 728 func (l *linker) markUsed(entryPoint, used *dpb.FileDescriptorProto) { 729 importsForFile := l.usedImports[entryPoint] 730 if importsForFile == nil { 731 importsForFile = map[string]struct{}{} 732 l.usedImports[entryPoint] = importsForFile 733 } 734 importsForFile[used.GetName()] = struct{}{} 735 } 736 737 func isAggregateDescriptor(m proto.Message) bool { 738 if m == sentinelMissingSymbol { 739 // this indicates the name matched a package, not a 740 // descriptor, but a package is an aggregate so 741 // we return true 742 return true 743 } 744 switch m.(type) { 745 case *dpb.DescriptorProto, *dpb.EnumDescriptorProto, *dpb.ServiceDescriptorProto: 746 return true 747 default: 748 return false 749 } 750 } 751 752 // This value is a bogus/nil value, but results in a non-nil 753 // proto.Message interface value. So we use it as a sentinel 754 // to indicate "stop searching for symbol... because it 755 // definitively does not exist". 756 var sentinelMissingSymbol = (*dpb.DescriptorProto)(nil) 757 758 func (l *linker) findSymbol(fd *dpb.FileDescriptorProto, name string) (element proto.Message, proto3 bool) { 759 return l.findSymbolRecursive(fd, fd, name, false, map[*dpb.FileDescriptorProto]struct{}{}) 760 } 761 762 func (l *linker) findSymbolRecursive(entryPoint, fd *dpb.FileDescriptorProto, name string, public bool, checked map[*dpb.FileDescriptorProto]struct{}) (element proto.Message, proto3 bool) { 763 if _, ok := checked[fd]; ok { 764 // already checked this one 765 return nil, false 766 } 767 checked[fd] = struct{}{} 768 d := l.findSymbolInFile(name, fd) 769 if d != nil { 770 return d, isProto3(fd) 771 } 772 773 // When public = false, we are searching only directly imported symbols. But we 774 // also need to search transitive public imports due to semantics of public imports. 775 if public { 776 for _, depIndex := range fd.PublicDependency { 777 dep := fd.Dependency[depIndex] 778 depres := l.files[dep] 779 if depres == nil { 780 // we'll catch this error later 781 continue 782 } 783 if d, proto3 := l.findSymbolRecursive(entryPoint, depres.fd, name, true, checked); d != nil { 784 l.markUsed(entryPoint, depres.fd) 785 return d, proto3 786 } 787 } 788 } else { 789 for _, dep := range fd.Dependency { 790 depres := l.files[dep] 791 if depres == nil { 792 // we'll catch this error later 793 continue 794 } 795 if d, proto3 := l.findSymbolRecursive(entryPoint, depres.fd, name, true, checked); d != nil { 796 l.markUsed(entryPoint, depres.fd) 797 return d, proto3 798 } 799 } 800 } 801 802 return nil, false 803 } 804 805 func isProto3(fd *dpb.FileDescriptorProto) bool { 806 return fd.GetSyntax() == "proto3" 807 } 808 809 func (l *linker) createdLinkedDescriptors() (map[string]*desc.FileDescriptor, error) { 810 names := make([]string, 0, len(l.files)) 811 for name := range l.files { 812 names = append(names, name) 813 } 814 sort.Strings(names) 815 linked := map[string]*desc.FileDescriptor{} 816 for _, name := range names { 817 if _, err := l.linkFile(name, nil, nil, linked); err != nil { 818 return nil, err 819 } 820 } 821 return linked, nil 822 } 823 824 func (l *linker) linkFile(name string, rootImportLoc *SourcePos, seen []string, linked map[string]*desc.FileDescriptor) (*desc.FileDescriptor, error) { 825 // check for import cycle 826 for _, s := range seen { 827 if name == s { 828 var msg bytes.Buffer 829 first := true 830 for _, s := range seen { 831 if first { 832 first = false 833 } else { 834 msg.WriteString(" -> ") 835 } 836 _, _ = fmt.Fprintf(&msg, "%q", s) 837 } 838 _, _ = fmt.Fprintf(&msg, " -> %q", name) 839 return nil, ErrorWithSourcePos{ 840 Underlying: fmt.Errorf("cycle found in imports: %s", msg.String()), 841 Pos: rootImportLoc, 842 } 843 } 844 } 845 seen = append(seen, name) 846 847 if lfd, ok := linked[name]; ok { 848 // already linked 849 return lfd, nil 850 } 851 r := l.files[name] 852 if r == nil { 853 importer := seen[len(seen)-2] // len-1 is *this* file, before that is the one that imported it 854 return nil, fmt.Errorf("no descriptor found for %q, imported by %q", name, importer) 855 } 856 var deps []*desc.FileDescriptor 857 if rootImportLoc == nil { 858 // try to find a source location for this "root" import 859 decl := r.getFileNode(r.fd) 860 fnode, ok := decl.(*ast.FileNode) 861 if ok { 862 for _, decl := range fnode.Decls { 863 if dep, ok := decl.(*ast.ImportNode); ok { 864 ldep, err := l.linkFile(dep.Name.AsString(), dep.Name.Start(), seen, linked) 865 if err != nil { 866 return nil, err 867 } 868 deps = append(deps, ldep) 869 } 870 } 871 } else { 872 // no AST? just use the descriptor 873 for _, dep := range r.fd.Dependency { 874 ldep, err := l.linkFile(dep, decl.Start(), seen, linked) 875 if err != nil { 876 return nil, err 877 } 878 deps = append(deps, ldep) 879 } 880 } 881 } else { 882 // we can just use the descriptor since we don't need source location 883 // (we'll just attribute any import cycles found to the "root" import) 884 for _, dep := range r.fd.Dependency { 885 ldep, err := l.linkFile(dep, rootImportLoc, seen, linked) 886 if err != nil { 887 return nil, err 888 } 889 deps = append(deps, ldep) 890 } 891 } 892 lfd, err := desc.CreateFileDescriptor(r.fd, deps...) 893 if err != nil { 894 return nil, fmt.Errorf("error linking %q: %s", name, err) 895 } 896 linked[name] = lfd 897 return lfd, nil 898 } 899 900 func (l *linker) checkForUnusedImports(filename string) { 901 r := l.files[filename] 902 usedImports := l.usedImports[r.fd] 903 node := r.nodes[r.fd] 904 fileNode, _ := node.(*ast.FileNode) 905 for i, dep := range r.fd.Dependency { 906 if _, ok := usedImports[dep]; !ok { 907 isPublic := false 908 // it's fine if it's a public import 909 for _, j := range r.fd.PublicDependency { 910 if i == int(j) { 911 isPublic = true 912 break 913 } 914 } 915 if isPublic { 916 break 917 } 918 var pos *SourcePos 919 if fileNode != nil { 920 for _, decl := range fileNode.Decls { 921 imp, ok := decl.(*ast.ImportNode) 922 if !ok { 923 continue 924 } 925 if imp.Name.AsString() == dep { 926 pos = imp.Start() 927 } 928 } 929 } 930 if pos == nil { 931 pos = ast.UnknownPos(r.fd.GetName()) 932 } 933 r.errs.warn(pos, errUnusedImport(dep)) 934 } 935 } 936 }