From 4a7f91eaa93fb4b7b4f3b3968b3923570035d658 Mon Sep 17 00:00:00 2001 From: Josh Humphries <2035234+jhump@users.noreply.github.com> Date: Sun, 21 Apr 2024 14:39:47 -0400 Subject: [PATCH 1/3] more optimizations: 1. ast.NoSourceNode has a pointer receiver, so wrapping one in an ast.Node interface value doesn't incur an allocation. Also updates parser.ParseResult to refer to a single *ast.NoSourceNode, instead of allocating one in each call to get a node value. The NoSourceNode's underlying type is now FileInfo so that it can be allocation-free, even for the NodeInfo method (which previously was allocating a new FileInfo each time). 2. Don't allocate a slice to hold the set of checked files for each element being resolved. Instead, we allocate a single slice up front, and use that throughout. 3. Don't pro-actively allocate strings that only are used for error messages; instead defer construction of the change to the construction of the error. --- ast/enum.go | 2 +- ast/field.go | 4 +- ast/file.go | 2 +- ast/message.go | 2 +- ast/no_source.go | 58 ++++++++++++------------- ast/options.go | 4 +- ast/ranges.go | 2 +- ast/service.go | 2 +- ast/values.go | 2 +- linker/resolve.go | 108 +++++++++++++++++++++++++--------------------- parser/result.go | 35 +++++++-------- 11 files changed, 115 insertions(+), 106 deletions(-) diff --git a/ast/enum.go b/ast/enum.go index 1afd0905..55a62292 100644 --- a/ast/enum.go +++ b/ast/enum.go @@ -108,7 +108,7 @@ type EnumValueDeclNode interface { } var _ EnumValueDeclNode = (*EnumValueNode)(nil) -var _ EnumValueDeclNode = NoSourceNode{} +var _ EnumValueDeclNode = (*NoSourceNode)(nil) // EnumValueNode represents an enum declaration. Example: // diff --git a/ast/field.go b/ast/field.go index f08a92c5..63d65b3a 100644 --- a/ast/field.go +++ b/ast/field.go @@ -41,7 +41,7 @@ var _ FieldDeclNode = (*FieldNode)(nil) var _ FieldDeclNode = (*GroupNode)(nil) var _ FieldDeclNode = (*MapFieldNode)(nil) var _ FieldDeclNode = (*SyntheticMapField)(nil) -var _ FieldDeclNode = NoSourceNode{} +var _ FieldDeclNode = (*NoSourceNode)(nil) // FieldNode represents a normal field declaration (not groups or maps). It // can represent extension fields as well as non-extension fields (both inside @@ -394,7 +394,7 @@ type OneofDeclNode interface { var _ OneofDeclNode = (*OneofNode)(nil) var _ OneofDeclNode = (*SyntheticOneof)(nil) -var _ OneofDeclNode = NoSourceNode{} +var _ OneofDeclNode = (*NoSourceNode)(nil) // OneofNode represents a one-of declaration. Example: // diff --git a/ast/file.go b/ast/file.go index 8e94234f..50d4ca92 100644 --- a/ast/file.go +++ b/ast/file.go @@ -25,7 +25,7 @@ type FileDeclNode interface { } var _ FileDeclNode = (*FileNode)(nil) -var _ FileDeclNode = NoSourceNode{} +var _ FileDeclNode = (*NoSourceNode)(nil) // FileNode is the root of the AST hierarchy. It represents an entire // protobuf source file. diff --git a/ast/message.go b/ast/message.go index 4d21af11..eede28ed 100644 --- a/ast/message.go +++ b/ast/message.go @@ -32,7 +32,7 @@ type MessageDeclNode interface { var _ MessageDeclNode = (*MessageNode)(nil) var _ MessageDeclNode = (*SyntheticGroupMessageNode)(nil) var _ MessageDeclNode = (*SyntheticMapEntryNode)(nil) -var _ MessageDeclNode = NoSourceNode{} +var _ MessageDeclNode = (*NoSourceNode)(nil) // MessageNode represents a message declaration. Example: // diff --git a/ast/no_source.go b/ast/no_source.go index 8b160911..44dbb714 100644 --- a/ast/no_source.go +++ b/ast/no_source.go @@ -41,104 +41,102 @@ func (s unknownSpan) End() SourcePos { // NoSourceNode is a placeholder AST node that implements numerous // interfaces in this package. It can be used to represent an AST // element for a file whose source is not available. -type NoSourceNode struct { - filename string -} +type NoSourceNode FileInfo // NewNoSourceNode creates a new NoSourceNode for the given filename. -func NewNoSourceNode(filename string) NoSourceNode { - return NoSourceNode{filename: filename} +func NewNoSourceNode(filename string) *NoSourceNode { + return &NoSourceNode{name: filename} } -func (n NoSourceNode) Name() string { - return n.filename +func (n *NoSourceNode) Name() string { + return n.name } -func (n NoSourceNode) Start() Token { +func (n *NoSourceNode) Start() Token { return 0 } -func (n NoSourceNode) End() Token { +func (n *NoSourceNode) End() Token { return 0 } -func (n NoSourceNode) NodeInfo(Node) NodeInfo { +func (n *NoSourceNode) NodeInfo(Node) NodeInfo { return NodeInfo{ - fileInfo: &FileInfo{name: n.filename}, + fileInfo: (*FileInfo)(n), } } -func (n NoSourceNode) GetSyntax() Node { +func (n *NoSourceNode) GetSyntax() Node { return n } -func (n NoSourceNode) GetName() Node { +func (n *NoSourceNode) GetName() Node { return n } -func (n NoSourceNode) GetValue() ValueNode { +func (n *NoSourceNode) GetValue() ValueNode { return n } -func (n NoSourceNode) FieldLabel() Node { +func (n *NoSourceNode) FieldLabel() Node { return n } -func (n NoSourceNode) FieldName() Node { +func (n *NoSourceNode) FieldName() Node { return n } -func (n NoSourceNode) FieldType() Node { +func (n *NoSourceNode) FieldType() Node { return n } -func (n NoSourceNode) FieldTag() Node { +func (n *NoSourceNode) FieldTag() Node { return n } -func (n NoSourceNode) FieldExtendee() Node { +func (n *NoSourceNode) FieldExtendee() Node { return n } -func (n NoSourceNode) GetGroupKeyword() Node { +func (n *NoSourceNode) GetGroupKeyword() Node { return n } -func (n NoSourceNode) GetOptions() *CompactOptionsNode { +func (n *NoSourceNode) GetOptions() *CompactOptionsNode { return nil } -func (n NoSourceNode) RangeStart() Node { +func (n *NoSourceNode) RangeStart() Node { return n } -func (n NoSourceNode) RangeEnd() Node { +func (n *NoSourceNode) RangeEnd() Node { return n } -func (n NoSourceNode) GetNumber() Node { +func (n *NoSourceNode) GetNumber() Node { return n } -func (n NoSourceNode) MessageName() Node { +func (n *NoSourceNode) MessageName() Node { return n } -func (n NoSourceNode) OneofName() Node { +func (n *NoSourceNode) OneofName() Node { return n } -func (n NoSourceNode) GetInputType() Node { +func (n *NoSourceNode) GetInputType() Node { return n } -func (n NoSourceNode) GetOutputType() Node { +func (n *NoSourceNode) GetOutputType() Node { return n } -func (n NoSourceNode) Value() interface{} { +func (n *NoSourceNode) Value() interface{} { return nil } -func (n NoSourceNode) RangeOptions(func(*OptionNode) bool) { +func (n *NoSourceNode) RangeOptions(func(*OptionNode) bool) { } diff --git a/ast/options.go b/ast/options.go index 224c8704..be31f0b4 100644 --- a/ast/options.go +++ b/ast/options.go @@ -26,7 +26,7 @@ type OptionDeclNode interface { } var _ OptionDeclNode = (*OptionNode)(nil) -var _ OptionDeclNode = NoSourceNode{} +var _ OptionDeclNode = (*NoSourceNode)(nil) // OptionNode represents the declaration of a single option for an element. // It is used both for normal option declarations (start with "option" keyword @@ -410,4 +410,4 @@ var _ NodeWithOptions = RPCDeclNode(nil) var _ NodeWithOptions = FieldDeclNode(nil) var _ NodeWithOptions = EnumValueDeclNode(nil) var _ NodeWithOptions = (*ExtensionRangeNode)(nil) -var _ NodeWithOptions = NoSourceNode{} +var _ NodeWithOptions = (*NoSourceNode)(nil) diff --git a/ast/ranges.go b/ast/ranges.go index 7e467589..b365786a 100644 --- a/ast/ranges.go +++ b/ast/ranges.go @@ -108,7 +108,7 @@ type RangeDeclNode interface { } var _ RangeDeclNode = (*RangeNode)(nil) -var _ RangeDeclNode = NoSourceNode{} +var _ RangeDeclNode = (*NoSourceNode)(nil) // RangeNode represents a range expression, used in both extension ranges and // reserved ranges. Example: diff --git a/ast/service.go b/ast/service.go index 9a17f93e..eba22fd2 100644 --- a/ast/service.go +++ b/ast/service.go @@ -108,7 +108,7 @@ type RPCDeclNode interface { } var _ RPCDeclNode = (*RPCNode)(nil) -var _ RPCDeclNode = NoSourceNode{} +var _ RPCDeclNode = (*NoSourceNode)(nil) // RPCNode represents an RPC declaration. Example: // diff --git a/ast/values.go b/ast/values.go index 4ca79eb5..ffb9900c 100644 --- a/ast/values.go +++ b/ast/values.go @@ -54,7 +54,7 @@ var _ ValueNode = (*SpecialFloatLiteralNode)(nil) var _ ValueNode = (*SignedFloatLiteralNode)(nil) var _ ValueNode = (*ArrayLiteralNode)(nil) var _ ValueNode = (*MessageLiteralNode)(nil) -var _ ValueNode = NoSourceNode{} +var _ ValueNode = (*NoSourceNode)(nil) // StringValueNode is an AST node that represents a string literal. // Such a node can be a single literal (*StringLiteralNode) or a diff --git a/linker/resolve.go b/linker/resolve.go index 6f56a856..5882c5cc 100644 --- a/linker/resolve.go +++ b/linker/resolve.go @@ -34,11 +34,11 @@ func (r *result) ResolveMessageLiteralExtensionName(node ast.IdentValueNode) str return r.optionQualifiedNames[node] } -func (r *result) resolveElement(name protoreflect.FullName) protoreflect.Descriptor { +func (r *result) resolveElement(name protoreflect.FullName, checkedCache []string) protoreflect.Descriptor { if len(name) > 0 && name[0] == '.' { name = name[1:] } - res, _ := resolveInFile(r, false, nil, func(f File) (protoreflect.Descriptor, error) { + res, _ := resolveInFile(r, false, checkedCache[:0], func(f File) (protoreflect.Descriptor, error) { d := resolveElementInFile(name, f) if d != nil { return d, nil @@ -166,9 +166,10 @@ func (r *result) resolveReferences(handler *reporter.Handler, s *Symbols) error r.services = r.createServices(prefix, fd.Service) // then resolve symbol references - scopes := []scope{fileScope(r)} + checkedCache := make([]string, 0, 16) + scopes := []scope{fileScope(r, checkedCache)} if fd.Options != nil { - if err := r.resolveOptions(handler, "file", protoreflect.FullName(fd.GetName()), fd.Options.UninterpretedOption, scopes); err != nil { + if err := r.resolveOptions(handler, "file", protoreflect.FullName(fd.GetName()), fd.Options.UninterpretedOption, scopes, checkedCache); err != nil { return err } } @@ -188,7 +189,7 @@ func (r *result) resolveReferences(handler *reporter.Handler, s *Symbols) error // an option cannot refer to it as simply "i" but must qualify it (at a minimum "Msg.i"). // So we don't add this messages scope to our scopes slice until *after* we do options. if d.proto.Options != nil { - if err := r.resolveOptions(handler, "message", fqn, d.proto.Options.UninterpretedOption, scopes); err != nil { + if err := r.resolveOptions(handler, "message", fqn, d.proto.Options.UninterpretedOption, scopes, checkedCache); err != nil { return err } } @@ -197,21 +198,21 @@ func (r *result) resolveReferences(handler *reporter.Handler, s *Symbols) error for _, er := range d.proto.ExtensionRange { if er.Options != nil { erName := protoreflect.FullName(fmt.Sprintf("%s:%d-%d", fqn, er.GetStart(), er.GetEnd()-1)) - if err := r.resolveOptions(handler, "extension range", erName, er.Options.UninterpretedOption, scopes); err != nil { + if err := r.resolveOptions(handler, "extension range", erName, er.Options.UninterpretedOption, scopes, checkedCache); err != nil { return err } } } case *extTypeDescriptor: if d.field.proto.Options != nil { - if err := r.resolveOptions(handler, "extension", fqn, d.field.proto.Options.UninterpretedOption, scopes); err != nil { + if err := r.resolveOptions(handler, "extension", fqn, d.field.proto.Options.UninterpretedOption, scopes, checkedCache); err != nil { return err } } if extendeeNodes == nil && r.AST() != nil { extendeeNodes = map[ast.Node]struct{}{} } - if err := resolveFieldTypes(d.field, handler, extendeeNodes, s, scopes); err != nil { + if err := resolveFieldTypes(d.field, handler, extendeeNodes, s, scopes, checkedCache); err != nil { return err } if r.Syntax() == protoreflect.Proto3 && !allowedProto3Extendee(d.field.proto.GetExtendee()) { @@ -223,34 +224,34 @@ func (r *result) resolveReferences(handler *reporter.Handler, s *Symbols) error } case *fldDescriptor: if d.proto.Options != nil { - if err := r.resolveOptions(handler, "field", fqn, d.proto.Options.UninterpretedOption, scopes); err != nil { + if err := r.resolveOptions(handler, "field", fqn, d.proto.Options.UninterpretedOption, scopes, checkedCache); err != nil { return err } } - if err := resolveFieldTypes(d, handler, nil, s, scopes); err != nil { + if err := resolveFieldTypes(d, handler, nil, s, scopes, checkedCache); err != nil { return err } case *oneofDescriptor: if d.proto.Options != nil { - if err := r.resolveOptions(handler, "oneof", fqn, d.proto.Options.UninterpretedOption, scopes); err != nil { + if err := r.resolveOptions(handler, "oneof", fqn, d.proto.Options.UninterpretedOption, scopes, checkedCache); err != nil { return err } } case *enumDescriptor: if d.proto.Options != nil { - if err := r.resolveOptions(handler, "enum", fqn, d.proto.Options.UninterpretedOption, scopes); err != nil { + if err := r.resolveOptions(handler, "enum", fqn, d.proto.Options.UninterpretedOption, scopes, checkedCache); err != nil { return err } } case *enValDescriptor: if d.proto.Options != nil { - if err := r.resolveOptions(handler, "enum value", fqn, d.proto.Options.UninterpretedOption, scopes); err != nil { + if err := r.resolveOptions(handler, "enum value", fqn, d.proto.Options.UninterpretedOption, scopes, checkedCache); err != nil { return err } } case *svcDescriptor: if d.proto.Options != nil { - if err := r.resolveOptions(handler, "service", fqn, d.proto.Options.UninterpretedOption, scopes); err != nil { + if err := r.resolveOptions(handler, "service", fqn, d.proto.Options.UninterpretedOption, scopes, checkedCache); err != nil { return err } } @@ -258,11 +259,11 @@ func (r *result) resolveReferences(handler *reporter.Handler, s *Symbols) error scopes = append(scopes, messageScope(r, fqn)) // push new scope on entry case *mtdDescriptor: if d.proto.Options != nil { - if err := r.resolveOptions(handler, "method", fqn, d.proto.Options.UninterpretedOption, scopes); err != nil { + if err := r.resolveOptions(handler, "method", fqn, d.proto.Options.UninterpretedOption, scopes, checkedCache); err != nil { return err } } - if err := resolveMethodTypes(d, handler, scopes); err != nil { + if err := resolveMethodTypes(d, handler, scopes, checkedCache); err != nil { return err } } @@ -299,35 +300,40 @@ func allowedProto3Extendee(n string) bool { return ok } -func resolveFieldTypes(f *fldDescriptor, handler *reporter.Handler, extendees map[ast.Node]struct{}, s *Symbols, scopes []scope) error { +func resolveFieldTypes(f *fldDescriptor, handler *reporter.Handler, extendees map[ast.Node]struct{}, s *Symbols, scopes []scope, checkedCache []string) error { r := f.file fld := f.proto file := r.FileNode() node := r.FieldNode(fld) - scope := fmt.Sprintf("field %s", f.fqn) + kind := "field" if fld.GetExtendee() != "" { - scope := fmt.Sprintf("extension %s", f.fqn) + kind = "extension" var alreadyReported bool - var extendeePrefix string - if extendees == nil { - extendeePrefix = scope + ": " - } else { + if extendees != nil { _, alreadyReported = extendees[node.FieldExtendee()] if !alreadyReported { extendees[node.FieldExtendee()] = struct{}{} } } - dsc := r.resolve(fld.GetExtendee(), false, scopes) + dsc := r.resolve(fld.GetExtendee(), false, scopes, checkedCache) if dsc == nil { if alreadyReported { return nil } + var extendeePrefix string + if extendees == nil { + extendeePrefix = kind + " " + f.fqn + ": " + } return handler.HandleErrorf(file.NodeInfo(node.FieldExtendee()), "%sunknown extendee type %s", extendeePrefix, fld.GetExtendee()) } if isSentinelDescriptor(dsc) { if alreadyReported { return nil } + var extendeePrefix string + if extendees == nil { + extendeePrefix = kind + " " + f.fqn + ": " + } return handler.HandleErrorf(file.NodeInfo(node.FieldExtendee()), "%sunknown extendee type %s; resolved to %s which is not defined; consider using a leading dot", extendeePrefix, fld.GetExtendee(), dsc.FullName()) } extd, ok := dsc.(protoreflect.MessageDescriptor) @@ -335,6 +341,10 @@ func resolveFieldTypes(f *fldDescriptor, handler *reporter.Handler, extendees ma if alreadyReported { return nil } + var extendeePrefix string + if extendees == nil { + extendeePrefix = kind + " " + f.fqn + ": " + } return handler.HandleErrorf(file.NodeInfo(node.FieldExtendee()), "%sextendee is invalid: %s is %s, not a message", extendeePrefix, dsc.FullName(), descriptorTypeWithArticle(dsc)) } @@ -354,7 +364,7 @@ func resolveFieldTypes(f *fldDescriptor, handler *reporter.Handler, extendees ma } } if !found { - if err := handler.HandleErrorf(file.NodeInfo(node.FieldTag()), "%s: tag %d is not in valid range for extended type %s", scope, tag, dsc.FullName()); err != nil { + if err := handler.HandleErrorf(file.NodeInfo(node.FieldTag()), "%s %s: tag %d is not in valid range for extended type %s", kind, f.fqn, tag, dsc.FullName()); err != nil { return err } } else { @@ -374,12 +384,12 @@ func resolveFieldTypes(f *fldDescriptor, handler *reporter.Handler, extendees ma return nil } - dsc := r.resolve(fld.GetTypeName(), true, scopes) + dsc := r.resolve(fld.GetTypeName(), true, scopes, checkedCache) if dsc == nil { - return handler.HandleErrorf(file.NodeInfo(node.FieldType()), "%s: unknown type %s", scope, fld.GetTypeName()) + return handler.HandleErrorf(file.NodeInfo(node.FieldType()), "%s %s: unknown type %s", kind, f.fqn, fld.GetTypeName()) } if isSentinelDescriptor(dsc) { - return handler.HandleErrorf(file.NodeInfo(node.FieldType()), "%s: unknown type %s; resolved to %s which is not defined; consider using a leading dot", scope, fld.GetTypeName(), dsc.FullName()) + return handler.HandleErrorf(file.NodeInfo(node.FieldType()), "%s %s: unknown type %s; resolved to %s which is not defined; consider using a leading dot", kind, f.fqn, fld.GetTypeName(), dsc.FullName()) } switch dsc := dsc.(type) { case protoreflect.MessageDescriptor: @@ -389,7 +399,7 @@ func resolveFieldTypes(f *fldDescriptor, handler *reporter.Handler, extendees ma case *ast.MapFieldNode: // We have an AST for this file and can see this field is from a map declaration isValid = true - case ast.NoSourceNode: + case *ast.NoSourceNode: // We don't have an AST for the file (it came from a provided descriptor). So we // need to validate that it's not an illegal reference. To be valid, the field // must be repeated and the entry type must be nested in the same enclosing @@ -407,7 +417,7 @@ func resolveFieldTypes(f *fldDescriptor, handler *reporter.Handler, extendees ma } } if !isValid { - return handler.HandleErrorf(file.NodeInfo(node.FieldType()), "%s: %s is a synthetic map entry and may not be referenced explicitly", scope, dsc.FullName()) + return handler.HandleErrorf(file.NodeInfo(node.FieldType()), "%s %s: %s is a synthetic map entry and may not be referenced explicitly", kind, f.fqn, dsc.FullName()) } } typeName := "." + string(dsc.FullName()) @@ -418,7 +428,7 @@ func resolveFieldTypes(f *fldDescriptor, handler *reporter.Handler, extendees ma // if type was tentatively unset, we now know it's actually a message fld.Type = descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum() } else if fld.GetType() != descriptorpb.FieldDescriptorProto_TYPE_MESSAGE && fld.GetType() != descriptorpb.FieldDescriptorProto_TYPE_GROUP { - return handler.HandleErrorf(file.NodeInfo(node.FieldType()), "%s: descriptor proto indicates type %v but should be %v", scope, fld.GetType(), descriptorpb.FieldDescriptorProto_TYPE_MESSAGE) + return handler.HandleErrorf(file.NodeInfo(node.FieldType()), "%s %s: descriptor proto indicates type %v but should be %v", kind, f.fqn, fld.GetType(), descriptorpb.FieldDescriptorProto_TYPE_MESSAGE) } f.msgType = dsc case protoreflect.EnumDescriptor: @@ -430,11 +440,11 @@ func resolveFieldTypes(f *fldDescriptor, handler *reporter.Handler, extendees ma // the type was tentatively unset, but now we know it's actually an enum fld.Type = descriptorpb.FieldDescriptorProto_TYPE_ENUM.Enum() } else if fld.GetType() != descriptorpb.FieldDescriptorProto_TYPE_ENUM { - return handler.HandleErrorf(file.NodeInfo(node.FieldType()), "%s: descriptor proto indicates type %v but should be %v", scope, fld.GetType(), descriptorpb.FieldDescriptorProto_TYPE_ENUM) + return handler.HandleErrorf(file.NodeInfo(node.FieldType()), "%s %s: descriptor proto indicates type %v but should be %v", kind, f.fqn, fld.GetType(), descriptorpb.FieldDescriptorProto_TYPE_ENUM) } f.enumType = dsc default: - return handler.HandleErrorf(file.NodeInfo(node.FieldType()), "%s: invalid type: %s is %s, not a message or enum", scope, dsc.FullName(), descriptorTypeWithArticle(dsc)) + return handler.HandleErrorf(file.NodeInfo(node.FieldType()), "%s %s: invalid type: %s is %s, not a message or enum", kind, f.fqn, dsc.FullName(), descriptorTypeWithArticle(dsc)) } return nil } @@ -454,13 +464,13 @@ func isValidMap(mapField protoreflect.FieldDescriptor, mapEntry protoreflect.Mes string(mapEntry.Name()) == internal.InitCap(internal.JSONName(string(mapField.Name())))+"Entry" } -func resolveMethodTypes(m *mtdDescriptor, handler *reporter.Handler, scopes []scope) error { +func resolveMethodTypes(m *mtdDescriptor, handler *reporter.Handler, scopes []scope, checkedCache []string) error { scope := fmt.Sprintf("method %s", m.fqn) r := m.file mtd := m.proto file := r.FileNode() node := r.MethodNode(mtd) - dsc := r.resolve(mtd.GetInputType(), false, scopes) + dsc := r.resolve(mtd.GetInputType(), false, scopes, checkedCache) if dsc == nil { if err := handler.HandleErrorf(file.NodeInfo(node.GetInputType()), "%s: unknown request type %s", scope, mtd.GetInputType()); err != nil { return err @@ -482,7 +492,7 @@ func resolveMethodTypes(m *mtdDescriptor, handler *reporter.Handler, scopes []sc } // TODO: make input and output type resolution more DRY - dsc = r.resolve(mtd.GetOutputType(), false, scopes) + dsc = r.resolve(mtd.GetOutputType(), false, scopes, checkedCache) if dsc == nil { if err := handler.HandleErrorf(file.NodeInfo(node.GetOutputType()), "%s: unknown response type %s", scope, mtd.GetOutputType()); err != nil { return err @@ -506,7 +516,7 @@ func resolveMethodTypes(m *mtdDescriptor, handler *reporter.Handler, scopes []sc return nil } -func (r *result) resolveOptions(handler *reporter.Handler, elemType string, elemName protoreflect.FullName, opts []*descriptorpb.UninterpretedOption, scopes []scope) error { +func (r *result) resolveOptions(handler *reporter.Handler, elemType string, elemName protoreflect.FullName, opts []*descriptorpb.UninterpretedOption, scopes []scope, checkedCache []string) error { mc := &internal.MessageContext{ File: r, ElementName: string(elemName), @@ -519,7 +529,7 @@ opts: for _, nm := range opt.Name { if nm.GetIsExtension() { node := r.OptionNamePartNode(nm) - fqn, err := r.resolveExtensionName(nm.GetNamePart(), scopes) + fqn, err := r.resolveExtensionName(nm.GetNamePart(), scopes, checkedCache) if err != nil { if err := handler.HandleErrorf(file.NodeInfo(node), "%v%v", mc, err); err != nil { return err @@ -532,7 +542,7 @@ opts: // also resolve any extension names found inside message literals in option values mc.Option = opt optVal := r.OptionNode(opt).GetValue() - if err := r.resolveOptionValue(handler, mc, optVal, scopes); err != nil { + if err := r.resolveOptionValue(handler, mc, optVal, scopes, checkedCache); err != nil { return err } mc.Option = nil @@ -540,7 +550,7 @@ opts: return nil } -func (r *result) resolveOptionValue(handler *reporter.Handler, mc *internal.MessageContext, val ast.ValueNode, scopes []scope) error { +func (r *result) resolveOptionValue(handler *reporter.Handler, mc *internal.MessageContext, val ast.ValueNode, scopes []scope, checkedCache []string) error { optVal := val.Value() switch optVal := optVal.(type) { case []ast.ValueNode: @@ -550,7 +560,7 @@ func (r *result) resolveOptionValue(handler *reporter.Handler, mc *internal.Mess }() for i, v := range optVal { mc.OptAggPath = fmt.Sprintf("%s[%d]", origPath, i) - if err := r.resolveOptionValue(handler, mc, v, scopes); err != nil { + if err := r.resolveOptionValue(handler, mc, v, scopes, checkedCache); err != nil { return err } } @@ -569,7 +579,7 @@ func (r *result) resolveOptionValue(handler *reporter.Handler, mc *internal.Mess // likely due to how it re-uses C++ text format implementation, and normal text // format doesn't expect that kind of relative reference.) scopes := scopes[:1] // first scope is file, the rest are enclosing messages - fqn, err := r.resolveExtensionName(string(fld.Name.Name.AsIdentifier()), scopes) + fqn, err := r.resolveExtensionName(string(fld.Name.Name.AsIdentifier()), scopes, checkedCache) if err != nil { if err := handler.HandleErrorf(r.FileNode().NodeInfo(fld.Name.Name), "%v%v", mc, err); err != nil { return err @@ -590,7 +600,7 @@ func (r *result) resolveOptionValue(handler *reporter.Handler, mc *internal.Mess mc.OptAggPath = fmt.Sprintf("%s%s", mc.OptAggPath, string(fld.Name.Name.AsIdentifier())) } - if err := r.resolveOptionValue(handler, mc, fld.Val, scopes); err != nil { + if err := r.resolveOptionValue(handler, mc, fld.Val, scopes, checkedCache); err != nil { return err } } @@ -598,8 +608,8 @@ func (r *result) resolveOptionValue(handler *reporter.Handler, mc *internal.Mess return nil } -func (r *result) resolveExtensionName(name string, scopes []scope) (string, error) { - dsc := r.resolve(name, false, scopes) +func (r *result) resolveExtensionName(name string, scopes []scope, checkedCache []string) (string, error) { + dsc := r.resolve(name, false, scopes, checkedCache) if dsc == nil { return "", fmt.Errorf("unknown extension %s", name) } @@ -614,10 +624,10 @@ func (r *result) resolveExtensionName(name string, scopes []scope) (string, erro return string("." + dsc.FullName()), nil } -func (r *result) resolve(name string, onlyTypes bool, scopes []scope) protoreflect.Descriptor { +func (r *result) resolve(name string, onlyTypes bool, scopes []scope, checkedCache []string) protoreflect.Descriptor { if strings.HasPrefix(name, ".") { // already fully-qualified - return r.resolveElement(protoreflect.FullName(name[1:])) + return r.resolveElement(protoreflect.FullName(name[1:]), checkedCache) } // unqualified, so we look in the enclosing (last) scope first and move // towards outermost (first) scope, trying to resolve the symbol @@ -662,13 +672,13 @@ func isType(d protoreflect.Descriptor) bool { // can be declared. type scope func(firstName, fullName string) protoreflect.Descriptor -func fileScope(r *result) scope { +func fileScope(r *result, checkedCache []string) scope { // we search symbols in this file, but also symbols in other files that have // the same package as this file or a "parent" package (in protobuf, // packages are a hierarchy like C++ namespaces) prefixes := internal.CreatePrefixList(r.FileDescriptorProto().GetPackage()) querySymbol := func(n string) protoreflect.Descriptor { - return r.resolveElement(protoreflect.FullName(n)) + return r.resolveElement(protoreflect.FullName(n), checkedCache) } return func(firstName, fullName string) protoreflect.Descriptor { for _, prefix := range prefixes { diff --git a/parser/result.go b/parser/result.go index 4d19dd2d..fac2bd6f 100644 --- a/parser/result.go +++ b/parser/result.go @@ -36,14 +36,15 @@ type result struct { file *ast.FileNode proto *descriptorpb.FileDescriptorProto - nodes map[proto.Message]ast.Node + nodes map[proto.Message]ast.Node + ifNoAST *ast.NoSourceNode } // ResultWithoutAST returns a parse result that has no AST. All methods for // looking up AST nodes return a placeholder node that contains only the filename // in position information. func ResultWithoutAST(proto *descriptorpb.FileDescriptorProto) Result { - return &result{proto: proto} + return &result{proto: proto, ifNoAST: ast.NewNoSourceNode(proto.GetName())} } // ResultFromAST constructs a descriptor proto from the given AST. The returned @@ -846,105 +847,105 @@ func (r *result) processProto3OptionalFields(msgd *descriptorpb.DescriptorProto) func (r *result) Node(m proto.Message) ast.Node { if r.nodes == nil { - return ast.NewNoSourceNode(r.proto.GetName()) + return r.ifNoAST } return r.nodes[m] } func (r *result) FileNode() ast.FileDeclNode { if r.nodes == nil { - return ast.NewNoSourceNode(r.proto.GetName()) + return r.ifNoAST } return r.nodes[r.proto].(ast.FileDeclNode) } func (r *result) OptionNode(o *descriptorpb.UninterpretedOption) ast.OptionDeclNode { if r.nodes == nil { - return ast.NewNoSourceNode(r.proto.GetName()) + return r.ifNoAST } return r.nodes[o].(ast.OptionDeclNode) } func (r *result) OptionNamePartNode(o *descriptorpb.UninterpretedOption_NamePart) ast.Node { if r.nodes == nil { - return ast.NewNoSourceNode(r.proto.GetName()) + return r.ifNoAST } return r.nodes[o] } func (r *result) MessageNode(m *descriptorpb.DescriptorProto) ast.MessageDeclNode { if r.nodes == nil { - return ast.NewNoSourceNode(r.proto.GetName()) + return r.ifNoAST } return r.nodes[m].(ast.MessageDeclNode) } func (r *result) FieldNode(f *descriptorpb.FieldDescriptorProto) ast.FieldDeclNode { if r.nodes == nil { - return ast.NewNoSourceNode(r.proto.GetName()) + return r.ifNoAST } return r.nodes[f].(ast.FieldDeclNode) } func (r *result) OneofNode(o *descriptorpb.OneofDescriptorProto) ast.OneofDeclNode { if r.nodes == nil { - return ast.NewNoSourceNode(r.proto.GetName()) + return r.ifNoAST } return r.nodes[o].(ast.OneofDeclNode) } func (r *result) ExtensionsNode(e *descriptorpb.DescriptorProto_ExtensionRange) ast.NodeWithOptions { if r.nodes == nil { - return ast.NewNoSourceNode(r.proto.GetName()) + return r.ifNoAST } return r.nodes[asExtsNode(e)].(ast.NodeWithOptions) } func (r *result) ExtensionRangeNode(e *descriptorpb.DescriptorProto_ExtensionRange) ast.RangeDeclNode { if r.nodes == nil { - return ast.NewNoSourceNode(r.proto.GetName()) + return r.ifNoAST } return r.nodes[e].(ast.RangeDeclNode) } func (r *result) MessageReservedRangeNode(rr *descriptorpb.DescriptorProto_ReservedRange) ast.RangeDeclNode { if r.nodes == nil { - return ast.NewNoSourceNode(r.proto.GetName()) + return r.ifNoAST } return r.nodes[rr].(ast.RangeDeclNode) } func (r *result) EnumNode(e *descriptorpb.EnumDescriptorProto) ast.NodeWithOptions { if r.nodes == nil { - return ast.NewNoSourceNode(r.proto.GetName()) + return r.ifNoAST } return r.nodes[e].(ast.NodeWithOptions) } func (r *result) EnumValueNode(e *descriptorpb.EnumValueDescriptorProto) ast.EnumValueDeclNode { if r.nodes == nil { - return ast.NewNoSourceNode(r.proto.GetName()) + return r.ifNoAST } return r.nodes[e].(ast.EnumValueDeclNode) } func (r *result) EnumReservedRangeNode(rr *descriptorpb.EnumDescriptorProto_EnumReservedRange) ast.RangeDeclNode { if r.nodes == nil { - return ast.NewNoSourceNode(r.proto.GetName()) + return r.ifNoAST } return r.nodes[rr].(ast.RangeDeclNode) } func (r *result) ServiceNode(s *descriptorpb.ServiceDescriptorProto) ast.NodeWithOptions { if r.nodes == nil { - return ast.NewNoSourceNode(r.proto.GetName()) + return r.ifNoAST } return r.nodes[s].(ast.NodeWithOptions) } func (r *result) MethodNode(m *descriptorpb.MethodDescriptorProto) ast.RPCDeclNode { if r.nodes == nil { - return ast.NewNoSourceNode(r.proto.GetName()) + return r.ifNoAST } return r.nodes[m].(ast.RPCDeclNode) } From 89cc7b10b5c2aaad49b369bed551c551a533e532 Mon Sep 17 00:00:00 2001 From: Josh Humphries <2035234+jhump@users.noreply.github.com> Date: Mon, 22 Apr 2024 15:16:50 -0400 Subject: [PATCH 2/3] try out map for tracking the checked set, instead of slice --- linker/files.go | 10 +++++----- linker/resolve.go | 36 +++++++++++++++++++----------------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/linker/files.go b/linker/files.go index 51ce3a8b..69e91152 100644 --- a/linker/files.go +++ b/linker/files.go @@ -201,7 +201,7 @@ type fileResolver struct { } func (r fileResolver) FindFileByPath(path string) (protoreflect.FileDescriptor, error) { - return resolveInFile(r.f, false, nil, func(f File) (protoreflect.FileDescriptor, error) { + return resolveInFile(r.f, false, map[string]struct{}{}, func(f File) (protoreflect.FileDescriptor, error) { if f.Path() == path { return f, nil } @@ -210,7 +210,7 @@ func (r fileResolver) FindFileByPath(path string) (protoreflect.FileDescriptor, } func (r fileResolver) FindDescriptorByName(name protoreflect.FullName) (protoreflect.Descriptor, error) { - return resolveInFile(r.f, false, nil, func(f File) (protoreflect.Descriptor, error) { + return resolveInFile(r.f, false, map[string]struct{}{}, func(f File) (protoreflect.Descriptor, error) { if d := f.FindDescriptorByName(name); d != nil { return d, nil } @@ -219,7 +219,7 @@ func (r fileResolver) FindDescriptorByName(name protoreflect.FullName) (protoref } func (r fileResolver) FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error) { - return resolveInFile(r.f, false, nil, func(f File) (protoreflect.MessageType, error) { + return resolveInFile(r.f, false, map[string]struct{}{}, func(f File) (protoreflect.MessageType, error) { d := f.FindDescriptorByName(message) if d != nil { md, ok := d.(protoreflect.MessageDescriptor) @@ -243,7 +243,7 @@ func messageNameFromURL(url string) string { } func (r fileResolver) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) { - return resolveInFile(r.f, false, nil, func(f File) (protoreflect.ExtensionType, error) { + return resolveInFile(r.f, false, map[string]struct{}{}, func(f File) (protoreflect.ExtensionType, error) { d := f.FindDescriptorByName(field) if d != nil { fld, ok := d.(protoreflect.FieldDescriptor) @@ -260,7 +260,7 @@ func (r fileResolver) FindExtensionByName(field protoreflect.FullName) (protoref } func (r fileResolver) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) { - return resolveInFile(r.f, false, nil, func(f File) (protoreflect.ExtensionType, error) { + return resolveInFile(r.f, false, map[string]struct{}{}, func(f File) (protoreflect.ExtensionType, error) { ext := findExtension(f, message, field) if ext != nil { return ext.Type(), nil diff --git a/linker/resolve.go b/linker/resolve.go index 02edcdb1..38891fcc 100644 --- a/linker/resolve.go +++ b/linker/resolve.go @@ -34,11 +34,15 @@ func (r *result) ResolveMessageLiteralExtensionName(node ast.IdentValueNode) str return r.optionQualifiedNames[node] } -func (r *result) resolveElement(name protoreflect.FullName, checkedCache []string) protoreflect.Descriptor { +func (r *result) resolveElement(name protoreflect.FullName, checkedCache map[string]struct{}) protoreflect.Descriptor { if len(name) > 0 && name[0] == '.' { name = name[1:] } - res, _ := resolveInFile(r, false, checkedCache[:0], func(f File) (protoreflect.Descriptor, error) { + // clear cached map instance before use + for k := range checkedCache { + delete(checkedCache, k) + } + res, _ := resolveInFile(r, false, checkedCache, func(f File) (protoreflect.Descriptor, error) { d := resolveElementInFile(name, f) if d != nil { return d, nil @@ -48,16 +52,14 @@ func (r *result) resolveElement(name protoreflect.FullName, checkedCache []strin return res } -func resolveInFile[T any](f File, publicImportsOnly bool, checked []string, fn func(File) (T, error)) (T, error) { +func resolveInFile[T any](f File, publicImportsOnly bool, checked map[string]struct{}, fn func(File) (T, error)) (T, error) { var zero T path := f.Path() - for _, str := range checked { - if str == path { - // already checked - return zero, protoregistry.NotFound - } + if _, ok := checked[path]; ok { + // already checked + return zero, protoregistry.NotFound } - checked = append(checked, path) + checked[path] = struct{}{} res, err := fn(f) if err == nil { @@ -168,7 +170,7 @@ func (r *result) createDescendants() { func (r *result) resolveReferences(handler *reporter.Handler, s *Symbols) error { fd := r.FileDescriptorProto() - checkedCache := make([]string, 0, 16) + checkedCache := make(map[string]struct{}, 16) scopes := []scope{fileScope(r, checkedCache)} if fd.Options != nil { if err := r.resolveOptions(handler, "file", protoreflect.FullName(fd.GetName()), fd.Options.UninterpretedOption, scopes, checkedCache); err != nil { @@ -302,7 +304,7 @@ func allowedProto3Extendee(n string) bool { return ok } -func resolveFieldTypes(f *fldDescriptor, handler *reporter.Handler, extendees map[ast.Node]struct{}, s *Symbols, scopes []scope, checkedCache []string) error { +func resolveFieldTypes(f *fldDescriptor, handler *reporter.Handler, extendees map[ast.Node]struct{}, s *Symbols, scopes []scope, checkedCache map[string]struct{}) error { r := f.file fld := f.proto file := r.FileNode() @@ -466,7 +468,7 @@ func isValidMap(mapField protoreflect.FieldDescriptor, mapEntry protoreflect.Mes string(mapEntry.Name()) == internal.InitCap(internal.JSONName(string(mapField.Name())))+"Entry" } -func resolveMethodTypes(m *mtdDescriptor, handler *reporter.Handler, scopes []scope, checkedCache []string) error { +func resolveMethodTypes(m *mtdDescriptor, handler *reporter.Handler, scopes []scope, checkedCache map[string]struct{}) error { scope := fmt.Sprintf("method %s", m.fqn) r := m.file mtd := m.proto @@ -518,7 +520,7 @@ func resolveMethodTypes(m *mtdDescriptor, handler *reporter.Handler, scopes []sc return nil } -func (r *result) resolveOptions(handler *reporter.Handler, elemType string, elemName protoreflect.FullName, opts []*descriptorpb.UninterpretedOption, scopes []scope, checkedCache []string) error { +func (r *result) resolveOptions(handler *reporter.Handler, elemType string, elemName protoreflect.FullName, opts []*descriptorpb.UninterpretedOption, scopes []scope, checkedCache map[string]struct{}) error { mc := &internal.MessageContext{ File: r, ElementName: string(elemName), @@ -552,7 +554,7 @@ opts: return nil } -func (r *result) resolveOptionValue(handler *reporter.Handler, mc *internal.MessageContext, val ast.ValueNode, scopes []scope, checkedCache []string) error { +func (r *result) resolveOptionValue(handler *reporter.Handler, mc *internal.MessageContext, val ast.ValueNode, scopes []scope, checkedCache map[string]struct{}) error { optVal := val.Value() switch optVal := optVal.(type) { case []ast.ValueNode: @@ -610,7 +612,7 @@ func (r *result) resolveOptionValue(handler *reporter.Handler, mc *internal.Mess return nil } -func (r *result) resolveExtensionName(name string, scopes []scope, checkedCache []string) (string, error) { +func (r *result) resolveExtensionName(name string, scopes []scope, checkedCache map[string]struct{}) (string, error) { dsc := r.resolve(name, false, scopes, checkedCache) if dsc == nil { return "", fmt.Errorf("unknown extension %s", name) @@ -626,7 +628,7 @@ func (r *result) resolveExtensionName(name string, scopes []scope, checkedCache return string("." + dsc.FullName()), nil } -func (r *result) resolve(name string, onlyTypes bool, scopes []scope, checkedCache []string) protoreflect.Descriptor { +func (r *result) resolve(name string, onlyTypes bool, scopes []scope, checkedCache map[string]struct{}) protoreflect.Descriptor { if strings.HasPrefix(name, ".") { // already fully-qualified return r.resolveElement(protoreflect.FullName(name[1:]), checkedCache) @@ -674,7 +676,7 @@ func isType(d protoreflect.Descriptor) bool { // can be declared. type scope func(firstName, fullName string) protoreflect.Descriptor -func fileScope(r *result, checkedCache []string) scope { +func fileScope(r *result, checkedCache map[string]struct{}) scope { // we search symbols in this file, but also symbols in other files that have // the same package as this file or a "parent" package (in protobuf, // packages are a hierarchy like C++ namespaces) From 742e6083e14b8e6f40bdb07c8fdf22a893092f49 Mon Sep 17 00:00:00 2001 From: Josh Humphries <2035234+jhump@users.noreply.github.com> Date: Mon, 22 Apr 2024 15:20:31 -0400 Subject: [PATCH 3/3] Slices are faster. Revert "try out map for tracking the checked set, instead of slice" This reverts commit 89cc7b10b5c2aaad49b369bed551c551a533e532. --- linker/files.go | 10 +++++----- linker/resolve.go | 36 +++++++++++++++++------------------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/linker/files.go b/linker/files.go index 69e91152..51ce3a8b 100644 --- a/linker/files.go +++ b/linker/files.go @@ -201,7 +201,7 @@ type fileResolver struct { } func (r fileResolver) FindFileByPath(path string) (protoreflect.FileDescriptor, error) { - return resolveInFile(r.f, false, map[string]struct{}{}, func(f File) (protoreflect.FileDescriptor, error) { + return resolveInFile(r.f, false, nil, func(f File) (protoreflect.FileDescriptor, error) { if f.Path() == path { return f, nil } @@ -210,7 +210,7 @@ func (r fileResolver) FindFileByPath(path string) (protoreflect.FileDescriptor, } func (r fileResolver) FindDescriptorByName(name protoreflect.FullName) (protoreflect.Descriptor, error) { - return resolveInFile(r.f, false, map[string]struct{}{}, func(f File) (protoreflect.Descriptor, error) { + return resolveInFile(r.f, false, nil, func(f File) (protoreflect.Descriptor, error) { if d := f.FindDescriptorByName(name); d != nil { return d, nil } @@ -219,7 +219,7 @@ func (r fileResolver) FindDescriptorByName(name protoreflect.FullName) (protoref } func (r fileResolver) FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error) { - return resolveInFile(r.f, false, map[string]struct{}{}, func(f File) (protoreflect.MessageType, error) { + return resolveInFile(r.f, false, nil, func(f File) (protoreflect.MessageType, error) { d := f.FindDescriptorByName(message) if d != nil { md, ok := d.(protoreflect.MessageDescriptor) @@ -243,7 +243,7 @@ func messageNameFromURL(url string) string { } func (r fileResolver) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) { - return resolveInFile(r.f, false, map[string]struct{}{}, func(f File) (protoreflect.ExtensionType, error) { + return resolveInFile(r.f, false, nil, func(f File) (protoreflect.ExtensionType, error) { d := f.FindDescriptorByName(field) if d != nil { fld, ok := d.(protoreflect.FieldDescriptor) @@ -260,7 +260,7 @@ func (r fileResolver) FindExtensionByName(field protoreflect.FullName) (protoref } func (r fileResolver) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) { - return resolveInFile(r.f, false, map[string]struct{}{}, func(f File) (protoreflect.ExtensionType, error) { + return resolveInFile(r.f, false, nil, func(f File) (protoreflect.ExtensionType, error) { ext := findExtension(f, message, field) if ext != nil { return ext.Type(), nil diff --git a/linker/resolve.go b/linker/resolve.go index 38891fcc..02edcdb1 100644 --- a/linker/resolve.go +++ b/linker/resolve.go @@ -34,15 +34,11 @@ func (r *result) ResolveMessageLiteralExtensionName(node ast.IdentValueNode) str return r.optionQualifiedNames[node] } -func (r *result) resolveElement(name protoreflect.FullName, checkedCache map[string]struct{}) protoreflect.Descriptor { +func (r *result) resolveElement(name protoreflect.FullName, checkedCache []string) protoreflect.Descriptor { if len(name) > 0 && name[0] == '.' { name = name[1:] } - // clear cached map instance before use - for k := range checkedCache { - delete(checkedCache, k) - } - res, _ := resolveInFile(r, false, checkedCache, func(f File) (protoreflect.Descriptor, error) { + res, _ := resolveInFile(r, false, checkedCache[:0], func(f File) (protoreflect.Descriptor, error) { d := resolveElementInFile(name, f) if d != nil { return d, nil @@ -52,14 +48,16 @@ func (r *result) resolveElement(name protoreflect.FullName, checkedCache map[str return res } -func resolveInFile[T any](f File, publicImportsOnly bool, checked map[string]struct{}, fn func(File) (T, error)) (T, error) { +func resolveInFile[T any](f File, publicImportsOnly bool, checked []string, fn func(File) (T, error)) (T, error) { var zero T path := f.Path() - if _, ok := checked[path]; ok { - // already checked - return zero, protoregistry.NotFound + for _, str := range checked { + if str == path { + // already checked + return zero, protoregistry.NotFound + } } - checked[path] = struct{}{} + checked = append(checked, path) res, err := fn(f) if err == nil { @@ -170,7 +168,7 @@ func (r *result) createDescendants() { func (r *result) resolveReferences(handler *reporter.Handler, s *Symbols) error { fd := r.FileDescriptorProto() - checkedCache := make(map[string]struct{}, 16) + checkedCache := make([]string, 0, 16) scopes := []scope{fileScope(r, checkedCache)} if fd.Options != nil { if err := r.resolveOptions(handler, "file", protoreflect.FullName(fd.GetName()), fd.Options.UninterpretedOption, scopes, checkedCache); err != nil { @@ -304,7 +302,7 @@ func allowedProto3Extendee(n string) bool { return ok } -func resolveFieldTypes(f *fldDescriptor, handler *reporter.Handler, extendees map[ast.Node]struct{}, s *Symbols, scopes []scope, checkedCache map[string]struct{}) error { +func resolveFieldTypes(f *fldDescriptor, handler *reporter.Handler, extendees map[ast.Node]struct{}, s *Symbols, scopes []scope, checkedCache []string) error { r := f.file fld := f.proto file := r.FileNode() @@ -468,7 +466,7 @@ func isValidMap(mapField protoreflect.FieldDescriptor, mapEntry protoreflect.Mes string(mapEntry.Name()) == internal.InitCap(internal.JSONName(string(mapField.Name())))+"Entry" } -func resolveMethodTypes(m *mtdDescriptor, handler *reporter.Handler, scopes []scope, checkedCache map[string]struct{}) error { +func resolveMethodTypes(m *mtdDescriptor, handler *reporter.Handler, scopes []scope, checkedCache []string) error { scope := fmt.Sprintf("method %s", m.fqn) r := m.file mtd := m.proto @@ -520,7 +518,7 @@ func resolveMethodTypes(m *mtdDescriptor, handler *reporter.Handler, scopes []sc return nil } -func (r *result) resolveOptions(handler *reporter.Handler, elemType string, elemName protoreflect.FullName, opts []*descriptorpb.UninterpretedOption, scopes []scope, checkedCache map[string]struct{}) error { +func (r *result) resolveOptions(handler *reporter.Handler, elemType string, elemName protoreflect.FullName, opts []*descriptorpb.UninterpretedOption, scopes []scope, checkedCache []string) error { mc := &internal.MessageContext{ File: r, ElementName: string(elemName), @@ -554,7 +552,7 @@ opts: return nil } -func (r *result) resolveOptionValue(handler *reporter.Handler, mc *internal.MessageContext, val ast.ValueNode, scopes []scope, checkedCache map[string]struct{}) error { +func (r *result) resolveOptionValue(handler *reporter.Handler, mc *internal.MessageContext, val ast.ValueNode, scopes []scope, checkedCache []string) error { optVal := val.Value() switch optVal := optVal.(type) { case []ast.ValueNode: @@ -612,7 +610,7 @@ func (r *result) resolveOptionValue(handler *reporter.Handler, mc *internal.Mess return nil } -func (r *result) resolveExtensionName(name string, scopes []scope, checkedCache map[string]struct{}) (string, error) { +func (r *result) resolveExtensionName(name string, scopes []scope, checkedCache []string) (string, error) { dsc := r.resolve(name, false, scopes, checkedCache) if dsc == nil { return "", fmt.Errorf("unknown extension %s", name) @@ -628,7 +626,7 @@ func (r *result) resolveExtensionName(name string, scopes []scope, checkedCache return string("." + dsc.FullName()), nil } -func (r *result) resolve(name string, onlyTypes bool, scopes []scope, checkedCache map[string]struct{}) protoreflect.Descriptor { +func (r *result) resolve(name string, onlyTypes bool, scopes []scope, checkedCache []string) protoreflect.Descriptor { if strings.HasPrefix(name, ".") { // already fully-qualified return r.resolveElement(protoreflect.FullName(name[1:]), checkedCache) @@ -676,7 +674,7 @@ func isType(d protoreflect.Descriptor) bool { // can be declared. type scope func(firstName, fullName string) protoreflect.Descriptor -func fileScope(r *result, checkedCache map[string]struct{}) scope { +func fileScope(r *result, checkedCache []string) scope { // we search symbols in this file, but also symbols in other files that have // the same package as this file or a "parent" package (in protobuf, // packages are a hierarchy like C++ namespaces)