github.com/jhump/protocompile@v0.0.0-20221021153901-4f6f732835e8/linker/resolve.go (about) 1 package linker 2 3 import ( 4 "fmt" 5 "strings" 6 7 "google.golang.org/protobuf/proto" 8 "google.golang.org/protobuf/reflect/protoreflect" 9 "google.golang.org/protobuf/types/descriptorpb" 10 "google.golang.org/protobuf/types/dynamicpb" 11 12 "github.com/jhump/protocompile/ast" 13 "github.com/jhump/protocompile/internal" 14 "github.com/jhump/protocompile/reporter" 15 "github.com/jhump/protocompile/walk" 16 ) 17 18 func (r *result) ResolveMessageType(name protoreflect.FullName) protoreflect.MessageDescriptor { 19 d := r.resolveElement(name) 20 if md, ok := d.(protoreflect.MessageDescriptor); ok { 21 return md 22 } 23 return nil 24 } 25 26 func (r *result) ResolveEnumType(name protoreflect.FullName) protoreflect.EnumDescriptor { 27 d := r.resolveElement(name) 28 if ed, ok := d.(protoreflect.EnumDescriptor); ok { 29 return ed 30 } 31 return nil 32 } 33 34 func (r *result) ResolveExtension(name protoreflect.FullName) protoreflect.ExtensionTypeDescriptor { 35 d := r.resolveElement(name) 36 if ed, ok := d.(protoreflect.ExtensionDescriptor); ok { 37 if !ed.IsExtension() { 38 return nil 39 } 40 if td, ok := ed.(protoreflect.ExtensionTypeDescriptor); ok { 41 return td 42 } 43 return dynamicpb.NewExtensionType(ed).TypeDescriptor() 44 } 45 return nil 46 } 47 48 func (r *result) resolveElement(name protoreflect.FullName) protoreflect.Descriptor { 49 if len(name) > 0 && name[0] == '.' { 50 name = name[1:] 51 } 52 importedFd, res := resolveElement(r, name, false, nil) 53 if importedFd != nil { 54 r.markUsed(importedFd.Path()) 55 } 56 return res 57 } 58 59 func (r *result) markUsed(importPath string) { 60 r.usedImports[importPath] = struct{}{} 61 } 62 63 func (r *result) CheckForUnusedImports(handler *reporter.Handler) { 64 fd := r.Proto() 65 file, _ := r.FileNode().(*ast.FileNode) 66 for i, dep := range fd.Dependency { 67 if _, ok := r.usedImports[dep]; !ok { 68 isPublic := false 69 // it's fine if it's a public import 70 for _, j := range fd.PublicDependency { 71 if i == int(j) { 72 isPublic = true 73 break 74 } 75 } 76 if isPublic { 77 continue 78 } 79 pos := ast.UnknownPos(fd.GetName()) 80 if file != nil { 81 for _, decl := range file.Decls { 82 imp, ok := decl.(*ast.ImportNode) 83 if ok && imp.Name.AsString() == dep { 84 pos = file.NodeInfo(imp).Start() 85 } 86 } 87 } 88 handler.HandleWarning(pos, errUnusedImport(dep)) 89 } 90 } 91 } 92 93 func resolveElement(f File, fqn protoreflect.FullName, publicImportsOnly bool, checked []string) (imported File, d protoreflect.Descriptor) { 94 path := f.Path() 95 for _, str := range checked { 96 if str == path { 97 // already checked 98 return nil, nil 99 } 100 } 101 checked = append(checked, path) 102 103 r := resolveElementInFile(fqn, f) 104 if r != nil { 105 // not imported, but present in f 106 return nil, r 107 } 108 109 // When publicImportsOnly = false, we are searching only directly imported symbols. But 110 // we also need to search transitive public imports due to semantics of public imports. 111 for i := 0; i < f.Imports().Len(); i++ { 112 dep := f.Imports().Get(i) 113 if dep.IsPublic || !publicImportsOnly { 114 depFile := f.FindImportByPath(dep.Path()) 115 _, d := resolveElement(depFile, fqn, true, checked) 116 if d != nil { 117 return depFile, d 118 } 119 } 120 } 121 122 return nil, nil 123 } 124 125 func (r *result) toDescriptor(fqn string, d proto.Message) protoreflect.Descriptor { 126 if ret := r.descriptors[d]; ret != nil { 127 // don't bother searching for parent if we don't need it... 128 return ret 129 } 130 131 parent, index := r.findParent(fqn) 132 switch d := d.(type) { 133 case *descriptorpb.DescriptorProto: 134 return r.asMessageDescriptor(d, r, parent, index, fqn) 135 case *descriptorpb.FieldDescriptorProto: 136 return r.asFieldDescriptor(d, r, parent, index, fqn) 137 case *descriptorpb.OneofDescriptorProto: 138 return r.asOneOfDescriptor(d, r, parent.(*msgDescriptor), index, fqn) 139 case *descriptorpb.EnumDescriptorProto: 140 return r.asEnumDescriptor(d, r, parent, index, fqn) 141 case *descriptorpb.EnumValueDescriptorProto: 142 return r.asEnumValueDescriptor(d, r, parent.(*enumDescriptor), index, fqn) 143 case *descriptorpb.ServiceDescriptorProto: 144 return r.asServiceDescriptor(d, r, index, fqn) 145 case *descriptorpb.MethodDescriptorProto: 146 return r.asMethodDescriptor(d, r, parent.(*svcDescriptor), index, fqn) 147 default: 148 // WTF? panic? 149 return nil 150 } 151 } 152 153 func (r *result) findParent(fqn string) (protoreflect.Descriptor, int) { 154 names := strings.Split(strings.TrimPrefix(fqn, r.prefix), ".") 155 if len(names) == 1 { 156 for i, en := range r.Proto().EnumType { 157 if en.GetName() == names[0] { 158 return r, i 159 } 160 for j, env := range en.Value { 161 if env.GetName() == names[0] { 162 return r.asEnumDescriptor(en, r, r, i, r.prefix+en.GetName()), j 163 } 164 } 165 } 166 for i, ext := range r.Proto().Extension { 167 if ext.GetName() == names[0] { 168 return r, i 169 } 170 } 171 } 172 for i, svc := range r.Proto().Service { 173 if svc.GetName() == names[0] { 174 if len(names) == 1 { 175 return r, i 176 } else { 177 if len(names) != 2 { 178 return nil, 0 179 } 180 sd := r.asServiceDescriptor(svc, r, i, r.prefix+svc.GetName()) 181 for j, mtd := range svc.Method { 182 if mtd.GetName() == names[1] { 183 return sd, j 184 } 185 } 186 } 187 } 188 } 189 for i, msg := range r.Proto().MessageType { 190 if msg.GetName() == names[0] { 191 if len(names) == 1 { 192 return r, i 193 } 194 md := r.asMessageDescriptor(msg, r, r, i, r.prefix+msg.GetName()) 195 return r.findParentInMessage(md, names[1:]) 196 } 197 } 198 return nil, 0 199 } 200 201 func (r *result) findParentInMessage(msg *msgDescriptor, names []string) (protoreflect.Descriptor, int) { 202 if len(names) == 1 { 203 for i, en := range msg.proto.EnumType { 204 if en.GetName() == names[0] { 205 return msg, i 206 } 207 for j, env := range en.Value { 208 if env.GetName() == names[0] { 209 return r.asEnumDescriptor(en, msg.file, msg, i, msg.fqn+"."+en.GetName()), j 210 } 211 } 212 } 213 for i, ext := range msg.proto.Extension { 214 if ext.GetName() == names[0] { 215 return msg, i 216 } 217 } 218 for i, fld := range msg.proto.Field { 219 if fld.GetName() == names[0] { 220 return msg, i 221 } 222 } 223 for i, ood := range msg.proto.OneofDecl { 224 if ood.GetName() == names[0] { 225 return msg, i 226 } 227 } 228 } 229 for i, nested := range msg.proto.NestedType { 230 if nested.GetName() == names[0] { 231 if len(names) == 1 { 232 return msg, i 233 } 234 md := r.asMessageDescriptor(nested, msg.file, msg, i, msg.fqn+"."+nested.GetName()) 235 return r.findParentInMessage(md, names[1:]) 236 } 237 } 238 return nil, 0 239 } 240 241 func descriptorType(d protoreflect.Descriptor) string { 242 switch d := d.(type) { 243 case protoreflect.MessageDescriptor: 244 return "message" 245 case protoreflect.FieldDescriptor: 246 if d.IsExtension() { 247 return "extension" 248 } 249 return "field" 250 case protoreflect.EnumDescriptor: 251 return "enum" 252 case protoreflect.EnumValueDescriptor: 253 return "enum value" 254 case protoreflect.ServiceDescriptor: 255 return "service" 256 case protoreflect.MethodDescriptor: 257 return "method" 258 case protoreflect.FileDescriptor: 259 return "file" 260 default: 261 // shouldn't be possible 262 return fmt.Sprintf("%T", d) 263 } 264 } 265 266 func (r *result) resolveReferences(handler *reporter.Handler, s *Symbols) error { 267 fd := r.Proto() 268 scopes := []scope{fileScope(r)} 269 if fd.Options != nil { 270 if err := r.resolveOptions(handler, "file", protoreflect.FullName(fd.GetName()), fd.Options.UninterpretedOption, scopes); err != nil { 271 return err 272 } 273 } 274 275 return walk.DescriptorProtosEnterAndExit(fd, 276 func(fqn protoreflect.FullName, d proto.Message) error { 277 switch d := d.(type) { 278 case *descriptorpb.DescriptorProto: 279 scopes = append(scopes, messageScope(r, fqn)) // push new scope on entry 280 if d.Options != nil { 281 if err := r.resolveOptions(handler, "message", fqn, d.Options.UninterpretedOption, scopes); err != nil { 282 return err 283 } 284 } 285 // walk only visits descriptors, so we need to loop over extension ranges ourselves 286 for _, er := range d.ExtensionRange { 287 if er.Options != nil { 288 erName := protoreflect.FullName(fmt.Sprintf("%s:%d-%d", fqn, er.GetStart(), er.GetEnd()-1)) 289 if err := r.resolveOptions(handler, "extension range", erName, er.Options.UninterpretedOption, scopes); err != nil { 290 return err 291 } 292 } 293 } 294 case *descriptorpb.FieldDescriptorProto: 295 elemType := "field" 296 if d.GetExtendee() != "" { 297 elemType = "extension" 298 } 299 if d.Options != nil { 300 if err := r.resolveOptions(handler, elemType, fqn, d.Options.UninterpretedOption, scopes); err != nil { 301 return err 302 } 303 } 304 if err := r.resolveFieldTypes(handler, s, fqn, d, scopes); err != nil { 305 return err 306 } 307 case *descriptorpb.OneofDescriptorProto: 308 if d.Options != nil { 309 if err := r.resolveOptions(handler, "one-of", fqn, d.Options.UninterpretedOption, scopes); err != nil { 310 return err 311 } 312 } 313 case *descriptorpb.EnumDescriptorProto: 314 if d.Options != nil { 315 if err := r.resolveOptions(handler, "enum", fqn, d.Options.UninterpretedOption, scopes); err != nil { 316 return err 317 } 318 } 319 case *descriptorpb.EnumValueDescriptorProto: 320 if d.Options != nil { 321 if err := r.resolveOptions(handler, "enum value", fqn, d.Options.UninterpretedOption, scopes); err != nil { 322 return err 323 } 324 } 325 case *descriptorpb.ServiceDescriptorProto: 326 // not a message, but same scoping rules for nested elements as if it were 327 scopes = append(scopes, messageScope(r, fqn)) // push new scope on entry 328 if d.Options != nil { 329 if err := r.resolveOptions(handler, "service", fqn, d.Options.UninterpretedOption, scopes); err != nil { 330 return err 331 } 332 } 333 case *descriptorpb.MethodDescriptorProto: 334 if d.Options != nil { 335 if err := r.resolveOptions(handler, "method", fqn, d.Options.UninterpretedOption, scopes); err != nil { 336 return err 337 } 338 } 339 if err := r.resolveMethodTypes(handler, fqn, d, scopes); err != nil { 340 return err 341 } 342 } 343 return nil 344 }, 345 func(fqn protoreflect.FullName, d proto.Message) error { 346 switch d.(type) { 347 case *descriptorpb.DescriptorProto, *descriptorpb.ServiceDescriptorProto: 348 // pop message scope on exit 349 scopes = scopes[:len(scopes)-1] 350 } 351 return nil 352 }) 353 } 354 355 func (r *result) resolveFieldTypes(handler *reporter.Handler, s *Symbols, fqn protoreflect.FullName, fld *descriptorpb.FieldDescriptorProto, scopes []scope) error { 356 scope := fmt.Sprintf("field %s", fqn) 357 file := r.FileNode() 358 node := r.FieldNode(fld) 359 elemType := "field" 360 if fld.GetExtendee() != "" { 361 elemType = "extension" 362 dsc := r.resolve(fld.GetExtendee(), true, scopes) 363 if dsc == nil { 364 return handler.HandleErrorf(file.NodeInfo(node.FieldExtendee()).Start(), "unknown extendee type %s", fld.GetExtendee()) 365 } 366 if isSentinelDescriptor(dsc) { 367 return handler.HandleErrorf(file.NodeInfo(node.FieldExtendee()).Start(), "unknown extendee type %s; resolved to %s which is not defined; consider using a leading dot", fld.GetExtendee(), dsc.FullName()) 368 } 369 extd, ok := dsc.(protoreflect.MessageDescriptor) 370 if !ok { 371 otherType := descriptorType(dsc) 372 return handler.HandleErrorf(file.NodeInfo(node.FieldExtendee()).Start(), "extendee is invalid: %s is a %s, not a message", dsc.FullName(), otherType) 373 } 374 fld.Extendee = proto.String("." + string(dsc.FullName())) 375 // make sure the tag number is in range 376 found := false 377 tag := protoreflect.FieldNumber(fld.GetNumber()) 378 for i := 0; i < extd.ExtensionRanges().Len(); i++ { 379 rng := extd.ExtensionRanges().Get(i) 380 if tag >= rng[0] && tag < rng[1] { 381 found = true 382 break 383 } 384 } 385 if !found { 386 if err := handler.HandleErrorf(file.NodeInfo(node.FieldTag()).Start(), "%s: tag %d is not in valid range for extended type %s", scope, tag, dsc.FullName()); err != nil { 387 return err 388 } 389 } else { 390 // make sure tag is not a duplicate 391 if err := s.addExtension(dsc.FullName(), tag, file.NodeInfo(node.FieldTag()).Start(), handler); err != nil { 392 return err 393 } 394 } 395 } 396 397 if fld.Options != nil { 398 if err := r.resolveOptions(handler, elemType, fqn, 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 dsc := r.resolve(fld.GetTypeName(), true, scopes) 409 if dsc == nil { 410 return handler.HandleErrorf(file.NodeInfo(node.FieldType()).Start(), "%s: unknown type %s", scope, fld.GetTypeName()) 411 } 412 if isSentinelDescriptor(dsc) { 413 return handler.HandleErrorf(file.NodeInfo(node.FieldType()).Start(), "%s: unknown type %s; resolved to %s which is not defined; consider using a leading dot", scope, fld.GetTypeName(), dsc.FullName()) 414 } 415 switch dsc := dsc.(type) { 416 case protoreflect.MessageDescriptor: 417 fld.TypeName = proto.String("." + string(dsc.FullName())) 418 // if type was tentatively unset, we now know it's actually a message 419 if fld.Type == nil { 420 fld.Type = descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum() 421 } 422 case protoreflect.EnumDescriptor: 423 proto3 := r.Syntax() == protoreflect.Proto3 424 enumIsProto3 := dsc.ParentFile().Syntax() == protoreflect.Proto3 425 if fld.GetExtendee() == "" && proto3 && !enumIsProto3 { 426 // fields in a proto3 message cannot refer to proto2 enums 427 return handler.HandleErrorf(file.NodeInfo(node.FieldType()).Start(), "%s: cannot use proto2 enum %s in a proto3 message", scope, fld.GetTypeName()) 428 } 429 fld.TypeName = proto.String("." + string(dsc.FullName())) 430 // the type was tentatively unset, but now we know it's actually an enum 431 fld.Type = descriptorpb.FieldDescriptorProto_TYPE_ENUM.Enum() 432 default: 433 otherType := descriptorType(dsc) 434 return handler.HandleErrorf(file.NodeInfo(node.FieldType()).Start(), "%s: invalid type: %s is a %s, not a message or enum", scope, dsc.FullName(), otherType) 435 } 436 return nil 437 } 438 439 func (r *result) resolveMethodTypes(handler *reporter.Handler, fqn protoreflect.FullName, mtd *descriptorpb.MethodDescriptorProto, scopes []scope) error { 440 scope := fmt.Sprintf("method %s", fqn) 441 file := r.FileNode() 442 node := r.MethodNode(mtd) 443 dsc := r.resolve(mtd.GetInputType(), false, scopes) 444 if dsc == nil { 445 if err := handler.HandleErrorf(file.NodeInfo(node.GetInputType()).Start(), "%s: unknown request type %s", scope, mtd.GetInputType()); err != nil { 446 return err 447 } 448 } else if isSentinelDescriptor(dsc) { 449 if err := handler.HandleErrorf(file.NodeInfo(node.GetInputType()).Start(), "%s: unknown request type %s; resolved to %s which is not defined; consider using a leading dot", scope, mtd.GetInputType(), dsc.FullName()); err != nil { 450 return err 451 } 452 } else if _, ok := dsc.(protoreflect.MessageDescriptor); !ok { 453 otherType := descriptorType(dsc) 454 if err := handler.HandleErrorf(file.NodeInfo(node.GetInputType()).Start(), "%s: invalid request type: %s is a %s, not a message", scope, dsc.FullName(), otherType); err != nil { 455 return err 456 } 457 } else { 458 mtd.InputType = proto.String("." + string(dsc.FullName())) 459 } 460 461 // TODO: make input and output type resolution more DRY 462 dsc = r.resolve(mtd.GetOutputType(), false, scopes) 463 if dsc == nil { 464 if err := handler.HandleErrorf(file.NodeInfo(node.GetOutputType()).Start(), "%s: unknown response type %s", scope, mtd.GetOutputType()); err != nil { 465 return err 466 } 467 } else if isSentinelDescriptor(dsc) { 468 if err := handler.HandleErrorf(file.NodeInfo(node.GetInputType()).Start(), "%s: unknown response type %s; resolved to %s which is not defined; consider using a leading dot", scope, mtd.GetOutputType(), dsc.FullName()); err != nil { 469 return err 470 } 471 } else if _, ok := dsc.(protoreflect.MessageDescriptor); !ok { 472 otherType := descriptorType(dsc) 473 if err := handler.HandleErrorf(file.NodeInfo(node.GetOutputType()).Start(), "%s: invalid response type: %s is a %s, not a message", scope, dsc.FullName(), otherType); err != nil { 474 return err 475 } 476 } else { 477 mtd.OutputType = proto.String("." + string(dsc.FullName())) 478 } 479 480 return nil 481 } 482 483 func (r *result) resolveOptions(handler *reporter.Handler, elemType string, elemName protoreflect.FullName, opts []*descriptorpb.UninterpretedOption, scopes []scope) error { 484 var scope string 485 if elemType != "file" { 486 scope = fmt.Sprintf("%s %s: ", elemType, elemName) 487 } 488 file := r.FileNode() 489 opts: 490 for _, opt := range opts { 491 for _, nm := range opt.Name { 492 if nm.GetIsExtension() { 493 node := r.OptionNamePartNode(nm) 494 dsc := r.resolve(nm.GetNamePart(), false, scopes) 495 if dsc == nil { 496 if err := handler.HandleErrorf(file.NodeInfo(node).Start(), "%sunknown extension %s", scope, nm.GetNamePart()); err != nil { 497 return err 498 } 499 continue opts 500 } 501 if isSentinelDescriptor(dsc) { 502 if err := handler.HandleErrorf(file.NodeInfo(node).Start(), "%sunknown extension %s; resolved to %s which is not defined; consider using a leading dot", scope, nm.GetNamePart(), dsc.FullName()); err != nil { 503 return err 504 } 505 continue opts 506 } 507 if ext, ok := dsc.(protoreflect.FieldDescriptor); !ok { 508 otherType := descriptorType(dsc) 509 if err := handler.HandleErrorf(file.NodeInfo(node).Start(), "%sinvalid extension: %s is a %s, not an extension", scope, nm.GetNamePart(), otherType); err != nil { 510 return err 511 } 512 continue opts 513 } else if !ext.IsExtension() { 514 if err := handler.HandleErrorf(file.NodeInfo(node).Start(), "%sinvalid extension: %s is a field but not an extension", scope, nm.GetNamePart()); err != nil { 515 return err 516 } 517 continue opts 518 } 519 nm.NamePart = proto.String("." + string(dsc.FullName())) 520 } 521 } 522 } 523 return nil 524 } 525 526 func (r *result) resolve(name string, onlyTypes bool, scopes []scope) protoreflect.Descriptor { 527 if strings.HasPrefix(name, ".") { 528 // already fully-qualified 529 return r.resolveElement(protoreflect.FullName(name[1:])) 530 } 531 // unqualified, so we look in the enclosing (last) scope first and move 532 // towards outermost (first) scope, trying to resolve the symbol 533 pos := strings.IndexByte(name, '.') 534 firstName := name 535 if pos > 0 { 536 firstName = name[:pos] 537 } 538 var bestGuess protoreflect.Descriptor 539 for i := len(scopes) - 1; i >= 0; i-- { 540 d := scopes[i](firstName, name) 541 if d != nil { 542 if !onlyTypes || isType(d) { 543 return d 544 } else if bestGuess == nil { 545 bestGuess = d 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 bestGuess 553 } 554 555 func isType(d protoreflect.Descriptor) bool { 556 switch d.(type) { 557 case protoreflect.MessageDescriptor, protoreflect.EnumDescriptor: 558 return true 559 } 560 return false 561 } 562 563 // scope represents a lexical scope in a proto file in which messages and enums 564 // can be declared. 565 type scope func(firstName, fullName string) protoreflect.Descriptor 566 567 func fileScope(r *result) scope { 568 // we search symbols in this file, but also symbols in other files that have 569 // the same package as this file or a "parent" package (in protobuf, 570 // packages are a hierarchy like C++ namespaces) 571 prefixes := internal.CreatePrefixList(r.Proto().GetPackage()) 572 querySymbol := func(n string) protoreflect.Descriptor { 573 return r.resolveElement(protoreflect.FullName(n)) 574 } 575 return func(firstName, fullName string) protoreflect.Descriptor { 576 for _, prefix := range prefixes { 577 var n1, n string 578 if prefix == "" { 579 // exhausted all prefixes, so it must be in this one 580 n1, n = fullName, fullName 581 } else { 582 n = prefix + "." + fullName 583 n1 = prefix + "." + firstName 584 } 585 d := resolveElementRelative(n1, n, querySymbol) 586 if d != nil { 587 return d 588 } 589 } 590 return nil 591 } 592 } 593 594 func messageScope(r *result, messageName protoreflect.FullName) scope { 595 querySymbol := func(n string) protoreflect.Descriptor { 596 return resolveElementInFile(protoreflect.FullName(n), r) 597 } 598 return func(firstName, fullName string) protoreflect.Descriptor { 599 n1 := string(messageName) + "." + firstName 600 n := string(messageName) + "." + fullName 601 return resolveElementRelative(n1, n, querySymbol) 602 } 603 } 604 605 func resolveElementRelative(firstName, fullName string, query func(name string) protoreflect.Descriptor) protoreflect.Descriptor { 606 d := query(firstName) 607 if d == nil { 608 return nil 609 } 610 if firstName == fullName { 611 return d 612 } 613 if !isAggregateDescriptor(d) { 614 // can't possibly find the rest of full name if 615 // the first name indicated a leaf descriptor 616 return nil 617 } 618 d = query(fullName) 619 if d == nil { 620 return newSentinelDescriptor(fullName) 621 } 622 return d 623 } 624 625 func resolveElementInFile(name protoreflect.FullName, f File) protoreflect.Descriptor { 626 d := f.FindDescriptorByName(name) 627 if d != nil { 628 return d 629 } 630 631 if matchesPkgNamespace(name, f.Package()) { 632 // this sentinel means the name is a valid namespace but 633 // does not refer to a descriptor 634 return newSentinelDescriptor(string(name)) 635 } 636 return nil 637 } 638 639 func matchesPkgNamespace(fqn, pkg protoreflect.FullName) bool { 640 if pkg == "" { 641 return false 642 } 643 if fqn == pkg { 644 return true 645 } 646 if len(pkg) > len(fqn) && strings.HasPrefix(string(pkg), string(fqn)) { 647 // if char after fqn is a dot, then fqn is a namespace 648 if pkg[len(fqn)] == '.' { 649 return true 650 } 651 } 652 return false 653 } 654 655 func isAggregateDescriptor(d protoreflect.Descriptor) bool { 656 if isSentinelDescriptor(d) { 657 // this indicates the name matched a package, not a 658 // descriptor, but a package is an aggregate so 659 // we return true 660 return true 661 } 662 switch d.(type) { 663 case protoreflect.MessageDescriptor, protoreflect.EnumDescriptor, protoreflect.ServiceDescriptor: 664 return true 665 default: 666 return false 667 } 668 } 669 670 func isSentinelDescriptor(d protoreflect.Descriptor) bool { 671 _, ok := d.(*sentinelDescriptor) 672 return ok 673 } 674 675 func newSentinelDescriptor(name string) protoreflect.Descriptor { 676 return &sentinelDescriptor{name: name} 677 } 678 679 // sentinelDescriptor is a placeholder descriptor. It is used instead of nil to 680 // distinguish between two situations: 681 // 1. The given name could not be found. 682 // 2. The given name *cannot* be a valid result so stop seraching. 683 // In these cases, attempts to resolve an element name will return nil for the 684 // first case and will return a sentinelDescriptor in the second. The sentinel 685 // contains the fully-qualified name which caused the search to stop (which may 686 // be a prefix of the actual name being resolved). 687 type sentinelDescriptor struct { 688 protoreflect.Descriptor 689 name string 690 } 691 692 func (p *sentinelDescriptor) ParentFile() protoreflect.FileDescriptor { 693 return nil 694 } 695 696 func (p *sentinelDescriptor) Parent() protoreflect.Descriptor { 697 return nil 698 } 699 700 func (p *sentinelDescriptor) Index() int { 701 return 0 702 } 703 704 func (p *sentinelDescriptor) Syntax() protoreflect.Syntax { 705 return 0 706 } 707 708 func (p *sentinelDescriptor) Name() protoreflect.Name { 709 return protoreflect.Name(p.name) 710 } 711 712 func (p *sentinelDescriptor) FullName() protoreflect.FullName { 713 return protoreflect.FullName(p.name) 714 } 715 716 func (p sentinelDescriptor) IsPlaceholder() bool { 717 return false 718 } 719 720 func (p sentinelDescriptor) Options() protoreflect.ProtoMessage { 721 return nil 722 } 723 724 var _ protoreflect.Descriptor = (*sentinelDescriptor)(nil)