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)