diff --git a/go.mod b/go.mod index d074888f4cb..285de5a173c 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/grafana/xk6-websockets v0.7.2 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/influxdata/influxdb1-client v0.0.0-20190402204710-8ff2fc3824fc - github.com/jhump/protoreflect v1.16.0 + github.com/jhump/protoreflect v1.17.0 github.com/klauspost/compress v1.17.11 github.com/mailru/easyjson v0.7.7 github.com/mattn/go-colorable v0.1.13 @@ -63,7 +63,7 @@ require ( buf.build/gen/go/prometheus/prometheus/protocolbuffers/go v1.31.0-20230627135113-9a12bc2590d2.1 // indirect github.com/andybalholm/cascadia v1.3.2 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bufbuild/protocompile v0.10.0 // indirect + github.com/bufbuild/protocompile v0.14.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chromedp/cdproto v0.0.0-20240919203636-12af5e8a671f // indirect diff --git a/go.sum b/go.sum index d4a3355f7ca..26981722dfa 100644 --- a/go.sum +++ b/go.sum @@ -23,8 +23,8 @@ github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao= github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= -github.com/bufbuild/protocompile v0.10.0 h1:+jW/wnLMLxaCEG8AX9lD0bQ5v9h1RUiMKOBOT5ll9dM= -github.com/bufbuild/protocompile v0.10.0/go.mod h1:G9qQIQo0xZ6Uyj6CMNz0saGmx2so+KONo8/KrELABiY= +github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= +github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -111,8 +111,8 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20190402204710-8ff2fc3824fc h1:KpMgaYJRieDkHZJWY3LMafvtqS/U8xX6+lUN+OKpl/Y= github.com/influxdata/influxdb1-client v0.0.0-20190402204710-8ff2fc3824fc/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/jhump/protoreflect v1.16.0 h1:54fZg+49widqXYQ0b+usAFHbMkBGR4PpXrsHc8+TBDg= -github.com/jhump/protoreflect v1.16.0/go.mod h1:oYPd7nPvcBw/5wlDfm/AVmU9zH9BgqGCI469pGxfj/8= +github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= +github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= diff --git a/js/modules/k6/grpc/client.go b/js/modules/k6/grpc/client.go index 836fb76daf5..ff4836a379e 100644 --- a/js/modules/k6/grpc/client.go +++ b/js/modules/k6/grpc/client.go @@ -16,8 +16,8 @@ import ( "go.k6.io/k6/lib/netext/grpcext" "github.com/grafana/sobek" - "github.com/jhump/protoreflect/desc" - "github.com/jhump/protoreflect/desc/protoparse" + "github.com/jhump/protoreflect/desc" //nolint:staticcheck // FIXME: #4035 + "github.com/jhump/protoreflect/desc/protoparse" //nolint:staticcheck // FIXME: #4035 "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" diff --git a/lib/netext/grpcext/conn_test.go b/lib/netext/grpcext/conn_test.go index 6d14df0b163..1134e23f7f7 100644 --- a/lib/netext/grpcext/conn_test.go +++ b/lib/netext/grpcext/conn_test.go @@ -7,7 +7,7 @@ import ( "io" "testing" - "github.com/jhump/protoreflect/desc/protoparse" + "github.com/jhump/protoreflect/desc/protoparse" //nolint:staticcheck // FIXME: #4035 "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc" diff --git a/lib/netext/grpcext/reflect.go b/lib/netext/grpcext/reflect.go index 25c4f097a41..108a16ca4d5 100644 --- a/lib/netext/grpcext/reflect.go +++ b/lib/netext/grpcext/reflect.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/jhump/protoreflect/desc" + "github.com/jhump/protoreflect/desc" //nolint:staticcheck // FIXME: #4035 "github.com/jhump/protoreflect/grpcreflect" "google.golang.org/grpc" "google.golang.org/protobuf/types/descriptorpb" diff --git a/vendor/github.com/bufbuild/protocompile/.protoc_version b/vendor/github.com/bufbuild/protocompile/.protoc_version index facd73a3322..a0d6856dbee 100644 --- a/vendor/github.com/bufbuild/protocompile/.protoc_version +++ b/vendor/github.com/bufbuild/protocompile/.protoc_version @@ -1 +1 @@ -26.0 +27.0 diff --git a/vendor/github.com/bufbuild/protocompile/ast/enum.go b/vendor/github.com/bufbuild/protocompile/ast/enum.go index 1afd090576f..55a62292779 100644 --- a/vendor/github.com/bufbuild/protocompile/ast/enum.go +++ b/vendor/github.com/bufbuild/protocompile/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/vendor/github.com/bufbuild/protocompile/ast/field.go b/vendor/github.com/bufbuild/protocompile/ast/field.go index f08a92c576e..63d65b3a3ca 100644 --- a/vendor/github.com/bufbuild/protocompile/ast/field.go +++ b/vendor/github.com/bufbuild/protocompile/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/vendor/github.com/bufbuild/protocompile/ast/file.go b/vendor/github.com/bufbuild/protocompile/ast/file.go index 8e94234fb47..50d4ca920dc 100644 --- a/vendor/github.com/bufbuild/protocompile/ast/file.go +++ b/vendor/github.com/bufbuild/protocompile/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/vendor/github.com/bufbuild/protocompile/ast/message.go b/vendor/github.com/bufbuild/protocompile/ast/message.go index 4d21af11dae..eede28ed02d 100644 --- a/vendor/github.com/bufbuild/protocompile/ast/message.go +++ b/vendor/github.com/bufbuild/protocompile/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/vendor/github.com/bufbuild/protocompile/ast/no_source.go b/vendor/github.com/bufbuild/protocompile/ast/no_source.go index 8b16091114f..44dbb714153 100644 --- a/vendor/github.com/bufbuild/protocompile/ast/no_source.go +++ b/vendor/github.com/bufbuild/protocompile/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/vendor/github.com/bufbuild/protocompile/ast/options.go b/vendor/github.com/bufbuild/protocompile/ast/options.go index 224c8704264..be31f0b4129 100644 --- a/vendor/github.com/bufbuild/protocompile/ast/options.go +++ b/vendor/github.com/bufbuild/protocompile/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/vendor/github.com/bufbuild/protocompile/ast/ranges.go b/vendor/github.com/bufbuild/protocompile/ast/ranges.go index 7e46758935b..c42908e1037 100644 --- a/vendor/github.com/bufbuild/protocompile/ast/ranges.go +++ b/vendor/github.com/bufbuild/protocompile/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: @@ -129,16 +129,16 @@ type RangeNode struct { // then so must be exactly one of end or max. If max is non-nil, it indicates a // "100 to max" style range. But if end is non-nil, the end of the range is a // literal, such as "100 to 200". -func NewRangeNode(start IntValueNode, to *KeywordNode, end IntValueNode, max *KeywordNode) *RangeNode { +func NewRangeNode(start IntValueNode, to *KeywordNode, end IntValueNode, maxEnd *KeywordNode) *RangeNode { if start == nil { panic("start is nil") } numChildren := 1 if to != nil { - if end == nil && max == nil { + if end == nil && maxEnd == nil { panic("to is not nil, but end and max both are") } - if end != nil && max != nil { + if end != nil && maxEnd != nil { panic("end and max cannot be both non-nil") } numChildren = 3 @@ -146,7 +146,7 @@ func NewRangeNode(start IntValueNode, to *KeywordNode, end IntValueNode, max *Ke if end != nil { panic("to is nil, but end is not") } - if max != nil { + if maxEnd != nil { panic("to is nil, but max is not") } } @@ -157,7 +157,7 @@ func NewRangeNode(start IntValueNode, to *KeywordNode, end IntValueNode, max *Ke if end != nil { children = append(children, end) } else { - children = append(children, max) + children = append(children, maxEnd) } } return &RangeNode{ @@ -167,7 +167,7 @@ func NewRangeNode(start IntValueNode, to *KeywordNode, end IntValueNode, max *Ke StartVal: start, To: to, EndVal: end, - Max: max, + Max: maxEnd, } } @@ -189,8 +189,8 @@ func (n *RangeNode) StartValue() interface{} { return n.StartVal.Value() } -func (n *RangeNode) StartValueAsInt32(min, max int32) (int32, bool) { - return AsInt32(n.StartVal, min, max) +func (n *RangeNode) StartValueAsInt32(minVal, maxVal int32) (int32, bool) { + return AsInt32(n.StartVal, minVal, maxVal) } func (n *RangeNode) EndValue() interface{} { @@ -200,14 +200,14 @@ func (n *RangeNode) EndValue() interface{} { return n.EndVal.Value() } -func (n *RangeNode) EndValueAsInt32(min, max int32) (int32, bool) { +func (n *RangeNode) EndValueAsInt32(minVal, maxVal int32) (int32, bool) { if n.Max != nil { - return max, true + return maxVal, true } if n.EndVal == nil { - return n.StartValueAsInt32(min, max) + return n.StartValueAsInt32(minVal, maxVal) } - return AsInt32(n.EndVal, min, max) + return AsInt32(n.EndVal, minVal, maxVal) } // ReservedNode represents reserved declaration, which can be used to reserve diff --git a/vendor/github.com/bufbuild/protocompile/ast/service.go b/vendor/github.com/bufbuild/protocompile/ast/service.go index 9a17f93e773..eba22fd21d7 100644 --- a/vendor/github.com/bufbuild/protocompile/ast/service.go +++ b/vendor/github.com/bufbuild/protocompile/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/vendor/github.com/bufbuild/protocompile/ast/values.go b/vendor/github.com/bufbuild/protocompile/ast/values.go index 4ca79eb53fd..22bd208d757 100644 --- a/vendor/github.com/bufbuild/protocompile/ast/values.go +++ b/vendor/github.com/bufbuild/protocompile/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 @@ -141,12 +141,12 @@ type IntValueNode interface { // AsInt32 range checks the given int value and returns its value is // in the range or 0, false if it is outside the range. -func AsInt32(n IntValueNode, min, max int32) (int32, bool) { +func AsInt32(n IntValueNode, minVal, maxVal int32) (int32, bool) { i, ok := n.AsInt64() if !ok { return 0, false } - if i < int64(min) || i > int64(max) { + if i < int64(minVal) || i > int64(maxVal) { return 0, false } return int32(i), true diff --git a/vendor/github.com/bufbuild/protocompile/ast/walk.go b/vendor/github.com/bufbuild/protocompile/ast/walk.go index 29125b84b19..00e71ab76b4 100644 --- a/vendor/github.com/bufbuild/protocompile/ast/walk.go +++ b/vendor/github.com/bufbuild/protocompile/ast/walk.go @@ -197,7 +197,7 @@ func (t *AncestorTracker) AsWalkOptions() []WalkOption { t.ancestors = append(t.ancestors, n) return nil }), - WithAfter(func(n Node) error { + WithAfter(func(_ Node) error { t.ancestors = t.ancestors[:len(t.ancestors)-1] return nil }), diff --git a/vendor/github.com/bufbuild/protocompile/compiler.go b/vendor/github.com/bufbuild/protocompile/compiler.go index b95f733ec63..b9a6d15e5ec 100644 --- a/vendor/github.com/bufbuild/protocompile/compiler.go +++ b/vendor/github.com/bufbuild/protocompile/compiler.go @@ -17,6 +17,7 @@ package protocompile import ( "bytes" "context" + "errors" "fmt" "io" "runtime" @@ -89,6 +90,15 @@ type Compiler struct { // will be removed as soon as it's no longer needed. This can help reduce // total memory usage for operations involving a large number of files. RetainASTs bool + + // If non-nil, the set of symbols already known. Any symbols in the current + // compilation will be added to it. If the compilation tries to redefine any + // of these symbols, it will be reported as a collision. + // + // This allows a large compilation to be split up into multiple, smaller + // operations and still be able to identify naming collisions and extension + // number collisions across all operations. + Symbols *linker.Symbols } // SourceInfoMode indicates how source code info is generated by a Compiler. @@ -140,12 +150,16 @@ func (c *Compiler) Compile(ctx context.Context, files ...string) (linker.Files, h := reporter.NewHandler(c.Reporter) + sym := c.Symbols + if sym == nil { + sym = &linker.Symbols{} + } e := executor{ c: c, h: h, s: semaphore.NewWeighted(int64(par)), cancel: cancel, - sym: &linker.Symbols{}, + sym: sym, results: map[string]*result{}, } @@ -550,7 +564,7 @@ func handleImportCycle(h *reporter.Handler, span ast.SourceSpan, importSequence } _, _ = fmt.Fprintf(&buf, "%q", dep) // error is saved and returned in caller - _ = h.HandleErrorf(span, buf.String()) + _ = h.HandleErrorWithPos(span, errors.New(buf.String())) } func findImportSpan(res parser.Result, dep string) ast.SourceSpan { diff --git a/vendor/github.com/bufbuild/protocompile/go.work b/vendor/github.com/bufbuild/protocompile/go.work index 48dfa6f8ed2..ba2d9c0d7a1 100644 --- a/vendor/github.com/bufbuild/protocompile/go.work +++ b/vendor/github.com/bufbuild/protocompile/go.work @@ -1,4 +1,4 @@ -go 1.20 +go 1.21 use ( . diff --git a/vendor/github.com/bufbuild/protocompile/go.work.sum b/vendor/github.com/bufbuild/protocompile/go.work.sum index eb88ec6984e..d977cf0580c 100644 --- a/vendor/github.com/bufbuild/protocompile/go.work.sum +++ b/vendor/github.com/bufbuild/protocompile/go.work.sum @@ -20,6 +20,7 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/bufbuild/protocompile v0.2.1-0.20230123224550-da57cd758c2f/go.mod h1:tleDrpPTlLUVmgnEoN6qBliKWqJaZFJXqZdFjTd+ocU= +github.com/bufbuild/protocompile v0.13.0/go.mod h1:dr++fGGeMPWHv7jPeT06ZKukm45NJscd7rUxQVzEKRk= github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -70,6 +71,8 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -77,6 +80,8 @@ go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 h1:LQmS1nU0twXLA96Kt7U9qtHJEbBk3z6Q0V4UXjZkpr4= @@ -124,15 +129,19 @@ golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -219,6 +228,7 @@ google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBt google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/vendor/github.com/bufbuild/protocompile/internal/editions.go b/vendor/github.com/bufbuild/protocompile/internal/editions.go deleted file mode 100644 index aa0d0adc891..00000000000 --- a/vendor/github.com/bufbuild/protocompile/internal/editions.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2020-2024 Buf Technologies, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package internal - -import "google.golang.org/protobuf/types/descriptorpb" - -// AllowEditions is set to true in tests to enable editions syntax for testing. -// This will be removed and editions will be allowed by non-test code once the -// implementation is complete. -var AllowEditions = false - -// SupportedEditions is the exhaustive set of editions that protocompile -// can support. We don't allow it to compile future/unknown editions, to -// make sure we don't generate incorrect descriptors, in the event that -// a future edition introduces a change or new feature that requires -// new logic in the compiler. -var SupportedEditions = map[string]descriptorpb.Edition{ - "2023": descriptorpb.Edition_EDITION_2023, -} diff --git a/vendor/github.com/bufbuild/protocompile/internal/editions/editions.go b/vendor/github.com/bufbuild/protocompile/internal/editions/editions.go new file mode 100644 index 00000000000..ee054fa7d22 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/internal/editions/editions.go @@ -0,0 +1,420 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package editions contains helpers related to resolving features for +// Protobuf editions. These are lower-level helpers. Higher-level helpers +// (which use this package under the hood) can be found in the exported +// protoutil package. +package editions + +import ( + "fmt" + "strings" + "sync" + + "google.golang.org/protobuf/encoding/prototext" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" + "google.golang.org/protobuf/types/descriptorpb" + "google.golang.org/protobuf/types/dynamicpb" +) + +const ( + // MinSupportedEdition is the earliest edition supported by this module. + // It should be 2023 (the first edition) for the indefinite future. + MinSupportedEdition = descriptorpb.Edition_EDITION_2023 + + // MaxSupportedEdition is the most recent edition supported by this module. + MaxSupportedEdition = descriptorpb.Edition_EDITION_2023 +) + +var ( + // SupportedEditions is the exhaustive set of editions that protocompile + // can support. We don't allow it to compile future/unknown editions, to + // make sure we don't generate incorrect descriptors, in the event that + // a future edition introduces a change or new feature that requires + // new logic in the compiler. + SupportedEditions = computeSupportedEditions(MinSupportedEdition, MaxSupportedEdition) + + // FeatureSetDescriptor is the message descriptor for the compiled-in + // version (in the descriptorpb package) of the google.protobuf.FeatureSet + // message type. + FeatureSetDescriptor = (*descriptorpb.FeatureSet)(nil).ProtoReflect().Descriptor() + // FeatureSetType is the message type for the compiled-in version (in + // the descriptorpb package) of google.protobuf.FeatureSet. + FeatureSetType = (*descriptorpb.FeatureSet)(nil).ProtoReflect().Type() + + editionDefaults map[descriptorpb.Edition]*descriptorpb.FeatureSet + editionDefaultsInit sync.Once +) + +// HasFeatures is implemented by all options messages and provides a +// nil-receiver-safe way of accessing the features explicitly configured +// in those options. +type HasFeatures interface { + GetFeatures() *descriptorpb.FeatureSet +} + +var _ HasFeatures = (*descriptorpb.FileOptions)(nil) +var _ HasFeatures = (*descriptorpb.MessageOptions)(nil) +var _ HasFeatures = (*descriptorpb.FieldOptions)(nil) +var _ HasFeatures = (*descriptorpb.OneofOptions)(nil) +var _ HasFeatures = (*descriptorpb.ExtensionRangeOptions)(nil) +var _ HasFeatures = (*descriptorpb.EnumOptions)(nil) +var _ HasFeatures = (*descriptorpb.EnumValueOptions)(nil) +var _ HasFeatures = (*descriptorpb.ServiceOptions)(nil) +var _ HasFeatures = (*descriptorpb.MethodOptions)(nil) + +// ResolveFeature resolves a feature for the given descriptor. This simple +// helper examines the given element and its ancestors, searching for an +// override. If there is no overridden value, it returns a zero value. +func ResolveFeature( + element protoreflect.Descriptor, + fields ...protoreflect.FieldDescriptor, +) (protoreflect.Value, error) { + for { + var features *descriptorpb.FeatureSet + if withFeatures, ok := element.Options().(HasFeatures); ok { + // It should not really be possible for 'ok' to ever be false... + features = withFeatures.GetFeatures() + } + + // TODO: adaptFeatureSet is only looking at the first field. But if we needed to + // support an extension field inside a custom feature, we'd really need + // to check all fields. That gets particularly complicated if the traversal + // path of fields includes list and map values. Luckily, features are not + // supposed to be repeated and not supposed to themselves have extensions. + // So this should be fine, at least for now. + msgRef, err := adaptFeatureSet(features, fields[0]) + if err != nil { + return protoreflect.Value{}, err + } + // Navigate the fields to find the value + var val protoreflect.Value + for i, field := range fields { + if i > 0 { + msgRef = val.Message() + } + if !msgRef.Has(field) { + val = protoreflect.Value{} + break + } + val = msgRef.Get(field) + } + if val.IsValid() { + // All fields were set! + return val, nil + } + + parent := element.Parent() + if parent == nil { + // We've reached the end of the inheritance chain. + return protoreflect.Value{}, nil + } + element = parent + } +} + +// HasEdition should be implemented by values that implement +// [protoreflect.FileDescriptor], to provide access to the file's +// edition when its syntax is [protoreflect.Editions]. +type HasEdition interface { + // Edition returns the numeric value of a google.protobuf.Edition enum + // value that corresponds to the edition of this file. If the file does + // not use editions, it should return the enum value that corresponds + // to the syntax level, EDITION_PROTO2 or EDITION_PROTO3. + Edition() int32 +} + +// GetEdition returns the edition for a given element. It returns +// EDITION_PROTO2 or EDITION_PROTO3 if the element is in a file that +// uses proto2 or proto3 syntax, respectively. It returns EDITION_UNKNOWN +// if the syntax of the given element is not recognized or if the edition +// cannot be ascertained from the element's [protoreflect.FileDescriptor]. +func GetEdition(d protoreflect.Descriptor) descriptorpb.Edition { + switch d.ParentFile().Syntax() { + case protoreflect.Proto2: + return descriptorpb.Edition_EDITION_PROTO2 + case protoreflect.Proto3: + return descriptorpb.Edition_EDITION_PROTO3 + case protoreflect.Editions: + withEdition, ok := d.ParentFile().(HasEdition) + if !ok { + // The parent file should always be a *result, so we should + // never be able to actually get in here. If we somehow did + // have another implementation of protoreflect.FileDescriptor, + // it doesn't provide a way to get the edition, other than the + // potentially expensive step of generating a FileDescriptorProto + // and then querying for the edition from that. :/ + return descriptorpb.Edition_EDITION_UNKNOWN + } + return descriptorpb.Edition(withEdition.Edition()) + default: + return descriptorpb.Edition_EDITION_UNKNOWN + } +} + +// GetEditionDefaults returns the default feature values for the given edition. +// It returns nil if the given edition is not known. +// +// This only populates known features, those that are fields of [*descriptorpb.FeatureSet]. +// It does not populate any extension fields. +// +// The returned value must not be mutated as it references shared package state. +func GetEditionDefaults(edition descriptorpb.Edition) *descriptorpb.FeatureSet { + editionDefaultsInit.Do(func() { + editionDefaults = make(map[descriptorpb.Edition]*descriptorpb.FeatureSet, len(descriptorpb.Edition_name)) + // Compute default for all known editions in descriptorpb. + for editionInt := range descriptorpb.Edition_name { + edition := descriptorpb.Edition(editionInt) + defaults := &descriptorpb.FeatureSet{} + defaultsRef := defaults.ProtoReflect() + fields := defaultsRef.Descriptor().Fields() + // Note: we are not computing defaults for extensions. Those are not needed + // by anything in the compiler, so we can get away with just computing + // defaults for these static, non-extension fields. + for i, length := 0, fields.Len(); i < length; i++ { + field := fields.Get(i) + val, err := GetFeatureDefault(edition, FeatureSetType, field) + if err != nil { + // should we fail somehow?? + continue + } + defaultsRef.Set(field, val) + } + editionDefaults[edition] = defaults + } + }) + return editionDefaults[edition] +} + +// GetFeatureDefault computes the default value for a feature. The given container +// is the message type that contains the field. This should usually be the descriptor +// for google.protobuf.FeatureSet, but can be a different message for computing the +// default value of custom features. +// +// Note that this always re-computes the default. For known fields of FeatureSet, +// it is more efficient to query from the statically computed default messages, +// like so: +// +// editions.GetEditionDefaults(edition).ProtoReflect().Get(feature) +func GetFeatureDefault(edition descriptorpb.Edition, container protoreflect.MessageType, feature protoreflect.FieldDescriptor) (protoreflect.Value, error) { + opts, ok := feature.Options().(*descriptorpb.FieldOptions) + if !ok { + // this is most likely impossible except for contrived use cases... + return protoreflect.Value{}, fmt.Errorf("options is %T instead of *descriptorpb.FieldOptions", feature.Options()) + } + maxEdition := descriptorpb.Edition(-1) + var maxVal string + for _, def := range opts.EditionDefaults { + if def.GetEdition() <= edition && def.GetEdition() > maxEdition { + maxEdition = def.GetEdition() + maxVal = def.GetValue() + } + } + if maxEdition == -1 { + // no matching default found + return protoreflect.Value{}, fmt.Errorf("no relevant default for edition %s", edition) + } + // We use a typed nil so that it won't fall back to the global registry. Features + // should not use extensions or google.protobuf.Any, so a nil *Types is fine. + unmarshaler := prototext.UnmarshalOptions{Resolver: (*protoregistry.Types)(nil)} + // The string value is in the text format: either a field value literal or a + // message literal. (Repeated and map features aren't supported, so there's no + // array or map literal syntax to worry about.) + if feature.Kind() == protoreflect.MessageKind || feature.Kind() == protoreflect.GroupKind { + fldVal := container.Zero().NewField(feature) + err := unmarshaler.Unmarshal([]byte(maxVal), fldVal.Message().Interface()) + if err != nil { + return protoreflect.Value{}, err + } + return fldVal, nil + } + // The value is the textformat for the field. But prototext doesn't provide a way + // to unmarshal a single field value. To work around, we unmarshal into an enclosing + // message, which means we must prefix the value with the field name. + if feature.IsExtension() { + maxVal = fmt.Sprintf("[%s]: %s", feature.FullName(), maxVal) + } else { + maxVal = fmt.Sprintf("%s: %s", feature.Name(), maxVal) + } + empty := container.New() + err := unmarshaler.Unmarshal([]byte(maxVal), empty.Interface()) + if err != nil { + return protoreflect.Value{}, err + } + return empty.Get(feature), nil +} + +func adaptFeatureSet(msg *descriptorpb.FeatureSet, field protoreflect.FieldDescriptor) (protoreflect.Message, error) { + msgRef := msg.ProtoReflect() + var actualField protoreflect.FieldDescriptor + switch { + case field.IsExtension(): + // Extensions can be used directly with the feature set, even if + // field.ContainingMessage() != FeatureSetDescriptor. But only if + // the value is either not a message or is a message with the + // right descriptor, i.e. val.Descriptor() == field.Message(). + if actualField = actualDescriptor(msgRef, field); actualField == nil || actualField == field { + if msgRef.Has(field) || len(msgRef.GetUnknown()) == 0 { + return msgRef, nil + } + // The field is not present, but the message has unrecognized values. So + // let's try to parse the unrecognized bytes, just in case they contain + // this extension. + temp := &descriptorpb.FeatureSet{} + unmarshaler := proto.UnmarshalOptions{ + AllowPartial: true, + Resolver: resolverForExtension{field}, + } + if err := unmarshaler.Unmarshal(msgRef.GetUnknown(), temp); err != nil { + return nil, fmt.Errorf("failed to parse unrecognized fields of FeatureSet: %w", err) + } + return temp.ProtoReflect(), nil + } + case field.ContainingMessage() == FeatureSetDescriptor: + // Known field, not dynamically generated. Can directly use with the feature set. + return msgRef, nil + default: + actualField = FeatureSetDescriptor.Fields().ByNumber(field.Number()) + } + + // If we get here, we have a dynamic field descriptor or an extension + // descriptor whose message type does not match the descriptor of the + // stored value. We need to copy its value into a dynamic message, + // which requires marshalling/unmarshalling. + // We only need to copy over the unrecognized bytes (if any) + // and the same field (if present). + data := msgRef.GetUnknown() + if actualField != nil && msgRef.Has(actualField) { + subset := &descriptorpb.FeatureSet{} + subset.ProtoReflect().Set(actualField, msgRef.Get(actualField)) + var err error + data, err = proto.MarshalOptions{AllowPartial: true}.MarshalAppend(data, subset) + if err != nil { + return nil, fmt.Errorf("failed to marshal FeatureSet field %s to bytes: %w", field.Name(), err) + } + } + if len(data) == 0 { + // No relevant data to copy over, so we can just return + // a zero value message + return dynamicpb.NewMessageType(field.ContainingMessage()).Zero(), nil + } + + other := dynamicpb.NewMessage(field.ContainingMessage()) + // We don't need to use a resolver for this step because we know that + // field is not an extension. And features are not allowed to themselves + // have extensions. + if err := (proto.UnmarshalOptions{AllowPartial: true}).Unmarshal(data, other); err != nil { + return nil, fmt.Errorf("failed to marshal FeatureSet field %s to bytes: %w", field.Name(), err) + } + return other, nil +} + +type resolverForExtension struct { + ext protoreflect.ExtensionDescriptor +} + +func (r resolverForExtension) FindMessageByName(_ protoreflect.FullName) (protoreflect.MessageType, error) { + return nil, protoregistry.NotFound +} + +func (r resolverForExtension) FindMessageByURL(_ string) (protoreflect.MessageType, error) { + return nil, protoregistry.NotFound +} + +func (r resolverForExtension) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) { + if field == r.ext.FullName() { + return asExtensionType(r.ext), nil + } + return nil, protoregistry.NotFound +} + +func (r resolverForExtension) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) { + if message == r.ext.ContainingMessage().FullName() && field == r.ext.Number() { + return asExtensionType(r.ext), nil + } + return nil, protoregistry.NotFound +} + +func asExtensionType(ext protoreflect.ExtensionDescriptor) protoreflect.ExtensionType { + if xtd, ok := ext.(protoreflect.ExtensionTypeDescriptor); ok { + return xtd.Type() + } + return dynamicpb.NewExtensionType(ext) +} + +func computeSupportedEditions(minEdition, maxEdition descriptorpb.Edition) map[string]descriptorpb.Edition { + supportedEditions := map[string]descriptorpb.Edition{} + for editionNum := range descriptorpb.Edition_name { + edition := descriptorpb.Edition(editionNum) + if edition >= minEdition && edition <= maxEdition { + name := strings.TrimPrefix(edition.String(), "EDITION_") + supportedEditions[name] = edition + } + } + return supportedEditions +} + +// actualDescriptor returns the actual field descriptor referenced by msg that +// corresponds to the given ext (i.e. same number). It returns nil if msg has +// no reference, if the actual descriptor is the same as ext, or if ext is +// otherwise safe to use as is. +func actualDescriptor(msg protoreflect.Message, ext protoreflect.ExtensionDescriptor) protoreflect.FieldDescriptor { + if !msg.Has(ext) || ext.Message() == nil { + // nothing to match; safe as is + return nil + } + val := msg.Get(ext) + switch { + case ext.IsMap(): // should not actually be possible + expectedDescriptor := ext.MapValue().Message() + if expectedDescriptor == nil { + return nil // nothing to match + } + // We know msg.Has(field) is true, from above, so there's at least one entry. + var matches bool + val.Map().Range(func(_ protoreflect.MapKey, val protoreflect.Value) bool { + matches = val.Message().Descriptor() == expectedDescriptor + return false + }) + if matches { + return nil + } + case ext.IsList(): + // We know msg.Has(field) is true, from above, so there's at least one entry. + if val.List().Get(0).Message().Descriptor() == ext.Message() { + return nil + } + case !ext.IsMap(): + if val.Message().Descriptor() == ext.Message() { + return nil + } + } + // The underlying message descriptors do not match. So we need to return + // the actual field descriptor. Sadly, protoreflect.Message provides no way + // to query the field descriptor in a message by number. For non-extensions, + // one can query the associated message descriptor. But for extensions, we + // have to do the slow thing, and range through all fields looking for it. + var actualField protoreflect.FieldDescriptor + msg.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool { + if fd.Number() == ext.Number() { + actualField = fd + return false + } + return true + }) + return actualField +} diff --git a/vendor/github.com/bufbuild/protocompile/internal/featuresext/cpp_features.protoset b/vendor/github.com/bufbuild/protocompile/internal/featuresext/cpp_features.protoset new file mode 100644 index 00000000000..106ad8e4ae5 Binary files /dev/null and b/vendor/github.com/bufbuild/protocompile/internal/featuresext/cpp_features.protoset differ diff --git a/vendor/github.com/bufbuild/protocompile/internal/featuresext/featuresext.go b/vendor/github.com/bufbuild/protocompile/internal/featuresext/featuresext.go new file mode 100644 index 00000000000..892524e6411 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/internal/featuresext/featuresext.go @@ -0,0 +1,84 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package featuresext provides file descriptors for the +// "google/protobuf/cpp_features.proto" and "google/protobuf/java_features.proto" +// standard import files. Unlike the other standard/well-known +// imports, these files have no standard Go package in their +// runtime with generated code. So in order to make them available +// as "standard imports" to compiler users, we must embed these +// descriptors into a Go package. +package featuresext + +import ( + _ "embed" + "fmt" + "sync" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protodesc" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" + "google.golang.org/protobuf/types/descriptorpb" +) + +var ( + //go:embed cpp_features.protoset + cppFeatures []byte + + //go:embed java_features.protoset + javaFeatures []byte + + initOnce sync.Once + initCppFeatures protoreflect.FileDescriptor + initCppErr error + initJavaFeatures protoreflect.FileDescriptor + initJavaErr error +) + +func initDescriptors() { + initOnce.Do(func() { + initCppFeatures, initCppErr = buildDescriptor("google/protobuf/cpp_features.proto", cppFeatures) + initJavaFeatures, initJavaErr = buildDescriptor("google/protobuf/java_features.proto", javaFeatures) + }) +} + +func CppFeaturesDescriptor() (protoreflect.FileDescriptor, error) { + initDescriptors() + return initCppFeatures, initCppErr +} + +func JavaFeaturesDescriptor() (protoreflect.FileDescriptor, error) { + initDescriptors() + return initJavaFeatures, initJavaErr +} + +func buildDescriptor(name string, data []byte) (protoreflect.FileDescriptor, error) { + var files descriptorpb.FileDescriptorSet + err := proto.Unmarshal(data, &files) + if err != nil { + return nil, fmt.Errorf("failed to load descriptor for %q: %w", name, err) + } + if len(files.File) != 1 { + return nil, fmt.Errorf("failed to load descriptor for %q: expected embedded descriptor set to contain exactly one file but it instead has %d", name, len(files.File)) + } + if files.File[0].GetName() != name { + return nil, fmt.Errorf("failed to load descriptor for %q: embedded descriptor contains wrong file %q", name, files.File[0].GetName()) + } + descriptor, err := protodesc.NewFile(files.File[0], protoregistry.GlobalFiles) + if err != nil { + return nil, fmt.Errorf("failed to load descriptor for %q: %w", name, err) + } + return descriptor, nil +} diff --git a/vendor/github.com/bufbuild/protocompile/internal/featuresext/java_features.protoset b/vendor/github.com/bufbuild/protocompile/internal/featuresext/java_features.protoset new file mode 100644 index 00000000000..60de3eb758e Binary files /dev/null and b/vendor/github.com/bufbuild/protocompile/internal/featuresext/java_features.protoset differ diff --git a/vendor/github.com/bufbuild/protocompile/internal/messageset/messageset.go b/vendor/github.com/bufbuild/protocompile/internal/messageset/messageset.go new file mode 100644 index 00000000000..850a0c663a4 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/internal/messageset/messageset.go @@ -0,0 +1,62 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package messageset + +import ( + "math" + "sync" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protodesc" + "google.golang.org/protobuf/types/descriptorpb" +) + +var ( + messageSetSupport bool + messageSetSupportInit sync.Once +) + +// CanSupportMessageSets returns true if the protobuf-go runtime supports +// serializing messages with the message set wire format. +func CanSupportMessageSets() bool { + messageSetSupportInit.Do(func() { + // We check using the protodesc package, instead of just relying + // on protolegacy build tag, in case someone links in a fork of + // the protobuf-go runtime that supports legacy proto1 features + // or in case the protobuf-go runtime adds another mechanism to + // enable or disable it (such as environment variable). + _, err := protodesc.NewFile(&descriptorpb.FileDescriptorProto{ + Name: proto.String("test.proto"), + MessageType: []*descriptorpb.DescriptorProto{ + { + Name: proto.String("MessageSet"), + Options: &descriptorpb.MessageOptions{ + MessageSetWireFormat: proto.Bool(true), + }, + ExtensionRange: []*descriptorpb.DescriptorProto_ExtensionRange{ + { + Start: proto.Int32(1), + End: proto.Int32(math.MaxInt32), + }, + }, + }, + }, + }, nil) + // When message sets are not supported, the above returns an error: + // message "MessageSet" is a MessageSet, which is a legacy proto1 feature that is no longer supported + messageSetSupport = err == nil + }) + return messageSetSupport +} diff --git a/vendor/github.com/bufbuild/protocompile/internal/options.go b/vendor/github.com/bufbuild/protocompile/internal/options.go index d9671f2b8bf..4eaa0f6a207 100644 --- a/vendor/github.com/bufbuild/protocompile/internal/options.go +++ b/vendor/github.com/bufbuild/protocompile/internal/options.go @@ -18,7 +18,6 @@ import ( "google.golang.org/protobuf/types/descriptorpb" "github.com/bufbuild/protocompile/ast" - "github.com/bufbuild/protocompile/reporter" ) type hasOptionNode interface { @@ -26,15 +25,17 @@ type hasOptionNode interface { FileNode() ast.FileDeclNode // needed in order to query for NodeInfo } -func FindFirstOption(res hasOptionNode, handler *reporter.Handler, scope string, opts []*descriptorpb.UninterpretedOption, name string) (int, error) { +type errorHandler func(span ast.SourceSpan, format string, args ...interface{}) error + +func FindFirstOption(res hasOptionNode, handler errorHandler, scope string, opts []*descriptorpb.UninterpretedOption, name string) (int, error) { return findOption(res, handler, scope, opts, name, false, true) } -func FindOption(res hasOptionNode, handler *reporter.Handler, scope string, opts []*descriptorpb.UninterpretedOption, name string) (int, error) { +func FindOption(res hasOptionNode, handler errorHandler, scope string, opts []*descriptorpb.UninterpretedOption, name string) (int, error) { return findOption(res, handler, scope, opts, name, true, false) } -func findOption(res hasOptionNode, handler *reporter.Handler, scope string, opts []*descriptorpb.UninterpretedOption, name string, exact, first bool) (int, error) { +func findOption(res hasOptionNode, handler errorHandler, scope string, opts []*descriptorpb.UninterpretedOption, name string, exact, first bool) (int, error) { found := -1 for i, opt := range opts { if exact && len(opt.Name) != 1 { @@ -51,7 +52,7 @@ func findOption(res hasOptionNode, handler *reporter.Handler, scope string, opts fn := res.FileNode() node := optNode.GetName() nodeInfo := fn.NodeInfo(node) - return -1, handler.HandleErrorf(nodeInfo, "%s: option %s cannot be defined more than once", scope, name) + return -1, handler(nodeInfo, "%s: option %s cannot be defined more than once", scope, name) } found = i } diff --git a/vendor/github.com/bufbuild/protocompile/linker/descriptors.go b/vendor/github.com/bufbuild/protocompile/linker/descriptors.go index b15d5715137..cd43dcce929 100644 --- a/vendor/github.com/bufbuild/protocompile/linker/descriptors.go +++ b/vendor/github.com/bufbuild/protocompile/linker/descriptors.go @@ -29,6 +29,7 @@ import ( "github.com/bufbuild/protocompile/ast" "github.com/bufbuild/protocompile/internal" + "github.com/bufbuild/protocompile/internal/editions" "github.com/bufbuild/protocompile/parser" "github.com/bufbuild/protocompile/protoutil" ) @@ -59,6 +60,14 @@ var ( noOpMethod protoreflect.MethodDescriptor ) +var ( + fieldPresenceField = editions.FeatureSetDescriptor.Fields().ByName("field_presence") + repeatedFieldEncodingField = editions.FeatureSetDescriptor.Fields().ByName("repeated_field_encoding") + messageEncodingField = editions.FeatureSetDescriptor.Fields().ByName("message_encoding") + enumTypeField = editions.FeatureSetDescriptor.Fields().ByName("enum_type") + jsonFormatField = editions.FeatureSetDescriptor.Fields().ByName("json_format") +) + func init() { noOpFile, _ = protodesc.NewFile( &descriptorpb.FileDescriptorProto{ @@ -168,6 +177,7 @@ type result struct { var _ protoreflect.FileDescriptor = (*result)(nil) var _ Result = (*result)(nil) var _ protoutil.DescriptorProtoWrapper = (*result)(nil) +var _ editions.HasEdition = (*result)(nil) func (r *result) RemoveAST() { r.Result = parser.ResultWithoutAST(r.FileDescriptorProto()) @@ -321,7 +331,7 @@ func (r *result) createImports() fileImports { imps := make([]protoreflect.FileImport, len(fd.Dependency)) for i, dep := range fd.Dependency { desc := r.deps.FindFileByPath(dep) - imps[i] = protoreflect.FileImport{FileDescriptor: desc} + imps[i] = protoreflect.FileImport{FileDescriptor: unwrap(desc)} } for _, publicIndex := range fd.PublicDependency { imps[int(publicIndex)].IsPublic = true @@ -332,6 +342,20 @@ func (r *result) createImports() fileImports { return fileImports{files: imps} } +func unwrap(descriptor protoreflect.FileDescriptor) protoreflect.FileDescriptor { + wrapped, ok := descriptor.(interface { + Unwrap() protoreflect.FileDescriptor + }) + if !ok { + return descriptor + } + unwrapped := wrapped.Unwrap() + if unwrapped == nil { + return descriptor // shouldn't ever happen + } + return unwrapped +} + func (f *fileImports) Len() int { return len(f.files) } @@ -376,13 +400,13 @@ func (s *srcLocs) ByDescriptor(d protoreflect.Descriptor) protoreflect.SourceLoc type msgDescriptors struct { protoreflect.MessageDescriptors - msgs []*msgDescriptor + msgs []msgDescriptor } -func (r *result) createMessages(prefix string, parent protoreflect.Descriptor, msgProtos []*descriptorpb.DescriptorProto) msgDescriptors { - msgs := make([]*msgDescriptor, len(msgProtos)) +func (r *result) createMessages(prefix string, parent protoreflect.Descriptor, msgProtos []*descriptorpb.DescriptorProto, pool *allocPool) msgDescriptors { + msgs := pool.getMessages(len(msgProtos)) for i, msgProto := range msgProtos { - msgs[i] = r.createMessageDescriptor(msgProto, parent, i, prefix+msgProto.GetName()) + r.createMessageDescriptor(&msgs[i], msgProto, parent, i, prefix+msgProto.GetName(), pool) } return msgDescriptors{msgs: msgs} } @@ -392,11 +416,12 @@ func (m *msgDescriptors) Len() int { } func (m *msgDescriptors) Get(i int) protoreflect.MessageDescriptor { - return m.msgs[i] + return &m.msgs[i] } func (m *msgDescriptors) ByName(s protoreflect.Name) protoreflect.MessageDescriptor { - for _, msg := range m.msgs { + for i := range m.msgs { + msg := &m.msgs[i] if msg.Name() == s { return msg } @@ -426,23 +451,27 @@ type msgDescriptor struct { var _ protoreflect.MessageDescriptor = (*msgDescriptor)(nil) var _ protoutil.DescriptorProtoWrapper = (*msgDescriptor)(nil) -func (r *result) createMessageDescriptor(md *descriptorpb.DescriptorProto, parent protoreflect.Descriptor, index int, fqn string) *msgDescriptor { - ret := &msgDescriptor{MessageDescriptor: noOpMessage, file: r, parent: parent, index: index, proto: md, fqn: fqn} +func (r *result) createMessageDescriptor(ret *msgDescriptor, md *descriptorpb.DescriptorProto, parent protoreflect.Descriptor, index int, fqn string, pool *allocPool) { r.descriptors[fqn] = ret + ret.MessageDescriptor = noOpMessage + ret.file = r + ret.parent = parent + ret.index = index + ret.proto = md + ret.fqn = fqn + prefix := fqn + "." // NB: We MUST create fields before oneofs so that we can populate the // set of fields that belong to the oneof - ret.fields = r.createFields(prefix, ret, md.Field) - ret.oneofs = r.createOneofs(prefix, ret, md.OneofDecl) - ret.nestedMessages = r.createMessages(prefix, ret, md.NestedType) - ret.nestedEnums = r.createEnums(prefix, ret, md.EnumType) - ret.nestedExtensions = r.createExtensions(prefix, ret, md.Extension) + ret.fields = r.createFields(prefix, ret, md.Field, pool) + ret.oneofs = r.createOneofs(prefix, ret, md.OneofDecl, pool) + ret.nestedMessages = r.createMessages(prefix, ret, md.NestedType, pool) + ret.nestedEnums = r.createEnums(prefix, ret, md.EnumType, pool) + ret.nestedExtensions = r.createExtensions(prefix, ret, md.Extension, pool) ret.extRanges = createFieldRanges(md.ExtensionRange) ret.rsvdRanges = createFieldRanges(md.ReservedRange) ret.rsvdNames = names{s: md.ReservedName} - - return ret } func (m *msgDescriptor) MessageDescriptorProto() *descriptorpb.DescriptorProto { @@ -619,13 +648,13 @@ func (f fieldRanges) Has(n protoreflect.FieldNumber) bool { type enumDescriptors struct { protoreflect.EnumDescriptors - enums []*enumDescriptor + enums []enumDescriptor } -func (r *result) createEnums(prefix string, parent protoreflect.Descriptor, enumProtos []*descriptorpb.EnumDescriptorProto) enumDescriptors { - enums := make([]*enumDescriptor, len(enumProtos)) +func (r *result) createEnums(prefix string, parent protoreflect.Descriptor, enumProtos []*descriptorpb.EnumDescriptorProto, pool *allocPool) enumDescriptors { + enums := pool.getEnums(len(enumProtos)) for i, enumProto := range enumProtos { - enums[i] = r.createEnumDescriptor(enumProto, parent, i, prefix+enumProto.GetName()) + r.createEnumDescriptor(&enums[i], enumProto, parent, i, prefix+enumProto.GetName(), pool) } return enumDescriptors{enums: enums} } @@ -635,13 +664,14 @@ func (e *enumDescriptors) Len() int { } func (e *enumDescriptors) Get(i int) protoreflect.EnumDescriptor { - return e.enums[i] + return &e.enums[i] } func (e *enumDescriptors) ByName(s protoreflect.Name) protoreflect.EnumDescriptor { - for _, en := range e.enums { - if en.Name() == s { - return en + for i := range e.enums { + enum := &e.enums[i] + if enum.Name() == s { + return enum } } return nil @@ -664,19 +694,24 @@ type enumDescriptor struct { var _ protoreflect.EnumDescriptor = (*enumDescriptor)(nil) var _ protoutil.DescriptorProtoWrapper = (*enumDescriptor)(nil) -func (r *result) createEnumDescriptor(ed *descriptorpb.EnumDescriptorProto, parent protoreflect.Descriptor, index int, fqn string) *enumDescriptor { - ret := &enumDescriptor{EnumDescriptor: noOpEnum, file: r, parent: parent, index: index, proto: ed, fqn: fqn} +func (r *result) createEnumDescriptor(ret *enumDescriptor, ed *descriptorpb.EnumDescriptorProto, parent protoreflect.Descriptor, index int, fqn string, pool *allocPool) { r.descriptors[fqn] = ret - // Unlike all other elements, the fully-qualified name of enum values - // is NOT scoped to their parent element (the enum), but rather to + ret.EnumDescriptor = noOpEnum + ret.file = r + ret.parent = parent + ret.index = index + ret.proto = ed + ret.fqn = fqn + + // Unlike all other elements, the fully-qualified names of enum values + // are NOT scoped to their parent element (the enum), but rather to // the enum's parent element. This follows C++ scoping rules for // enum values. prefix := strings.TrimSuffix(fqn, ed.GetName()) - ret.values = r.createEnumValues(prefix, ret, ed.Value) + ret.values = r.createEnumValues(prefix, ret, ed.Value, pool) ret.rsvdRanges = createEnumRanges(ed.ReservedRange) ret.rsvdNames = names{s: ed.ReservedName} - return ret } func (e *enumDescriptor) EnumDescriptorProto() *descriptorpb.EnumDescriptorProto { @@ -771,13 +806,13 @@ func (e enumRanges) Has(n protoreflect.EnumNumber) bool { type enValDescriptors struct { protoreflect.EnumValueDescriptors - vals []*enValDescriptor + vals []enValDescriptor } -func (r *result) createEnumValues(prefix string, parent *enumDescriptor, enValProtos []*descriptorpb.EnumValueDescriptorProto) enValDescriptors { - vals := make([]*enValDescriptor, len(enValProtos)) +func (r *result) createEnumValues(prefix string, parent *enumDescriptor, enValProtos []*descriptorpb.EnumValueDescriptorProto, pool *allocPool) enValDescriptors { + vals := pool.getEnumValues(len(enValProtos)) for i, enValProto := range enValProtos { - vals[i] = r.createEnumValueDescriptor(enValProto, parent, i, prefix+enValProto.GetName()) + r.createEnumValueDescriptor(&vals[i], enValProto, parent, i, prefix+enValProto.GetName()) } return enValDescriptors{vals: vals} } @@ -787,11 +822,12 @@ func (e *enValDescriptors) Len() int { } func (e *enValDescriptors) Get(i int) protoreflect.EnumValueDescriptor { - return e.vals[i] + return &e.vals[i] } func (e *enValDescriptors) ByName(s protoreflect.Name) protoreflect.EnumValueDescriptor { - for _, val := range e.vals { + for i := range e.vals { + val := &e.vals[i] if val.Name() == s { return val } @@ -800,7 +836,8 @@ func (e *enValDescriptors) ByName(s protoreflect.Name) protoreflect.EnumValueDes } func (e *enValDescriptors) ByNumber(n protoreflect.EnumNumber) protoreflect.EnumValueDescriptor { - for _, val := range e.vals { + for i := range e.vals { + val := &e.vals[i] if val.Number() == n { return val } @@ -820,10 +857,14 @@ type enValDescriptor struct { var _ protoreflect.EnumValueDescriptor = (*enValDescriptor)(nil) var _ protoutil.DescriptorProtoWrapper = (*enValDescriptor)(nil) -func (r *result) createEnumValueDescriptor(ed *descriptorpb.EnumValueDescriptorProto, parent *enumDescriptor, index int, fqn string) *enValDescriptor { - ret := &enValDescriptor{EnumValueDescriptor: noOpEnumValue, file: r, parent: parent, index: index, proto: ed, fqn: fqn} +func (r *result) createEnumValueDescriptor(ret *enValDescriptor, ed *descriptorpb.EnumValueDescriptorProto, parent *enumDescriptor, index int, fqn string) { r.descriptors[fqn] = ret - return ret + ret.EnumValueDescriptor = noOpEnumValue + ret.file = r + ret.parent = parent + ret.index = index + ret.proto = ed + ret.fqn = fqn } func (e *enValDescriptor) EnumValueDescriptorProto() *descriptorpb.EnumValueDescriptorProto { @@ -872,13 +913,13 @@ func (e *enValDescriptor) Number() protoreflect.EnumNumber { type extDescriptors struct { protoreflect.ExtensionDescriptors - exts []*extTypeDescriptor + exts []extTypeDescriptor } -func (r *result) createExtensions(prefix string, parent protoreflect.Descriptor, extProtos []*descriptorpb.FieldDescriptorProto) extDescriptors { - exts := make([]*extTypeDescriptor, len(extProtos)) +func (r *result) createExtensions(prefix string, parent protoreflect.Descriptor, extProtos []*descriptorpb.FieldDescriptorProto, pool *allocPool) extDescriptors { + exts := pool.getExtensions(len(extProtos)) for i, extProto := range extProtos { - exts[i] = r.createExtTypeDescriptor(extProto, parent, i, prefix+extProto.GetName()) + r.createExtTypeDescriptor(&exts[i], extProto, parent, i, prefix+extProto.GetName()) } return extDescriptors{exts: exts} } @@ -888,11 +929,12 @@ func (e *extDescriptors) Len() int { } func (e *extDescriptors) Get(i int) protoreflect.ExtensionDescriptor { - return e.exts[i] + return &e.exts[i] } func (e *extDescriptors) ByName(s protoreflect.Name) protoreflect.ExtensionDescriptor { - for _, ext := range e.exts { + for i := range e.exts { + ext := &e.exts[i] if ext.Name() == s { return ext } @@ -902,15 +944,15 @@ func (e *extDescriptors) ByName(s protoreflect.Name) protoreflect.ExtensionDescr type extTypeDescriptor struct { protoreflect.ExtensionTypeDescriptor - field *fldDescriptor + field fldDescriptor } var _ protoutil.DescriptorProtoWrapper = &extTypeDescriptor{} -func (r *result) createExtTypeDescriptor(fd *descriptorpb.FieldDescriptorProto, parent protoreflect.Descriptor, index int, fqn string) *extTypeDescriptor { - ret := &fldDescriptor{FieldDescriptor: noOpExtension, file: r, parent: parent, index: index, proto: fd, fqn: fqn} +func (r *result) createExtTypeDescriptor(ret *extTypeDescriptor, fd *descriptorpb.FieldDescriptorProto, parent protoreflect.Descriptor, index int, fqn string) { r.descriptors[fqn] = ret - return &extTypeDescriptor{ExtensionTypeDescriptor: dynamicpb.NewExtensionType(ret).TypeDescriptor(), field: ret} + ret.field = fldDescriptor{FieldDescriptor: noOpExtension, file: r, parent: parent, index: index, proto: fd, fqn: fqn} + ret.ExtensionTypeDescriptor = dynamicpb.NewExtensionType(&ret.field).TypeDescriptor() } func (e *extTypeDescriptor) FieldDescriptorProto() *descriptorpb.FieldDescriptorProto { @@ -923,15 +965,23 @@ func (e *extTypeDescriptor) AsProto() proto.Message { type fldDescriptors struct { protoreflect.FieldDescriptors + // We use pointers here, instead of flattened slice, because oneofs + // also have fields, but need to point to values in the parent + // message's fields. Even though they are pointers, in the containing + // message, we always allocate a flattened slice and then point into + // that, so we're still doing fewer allocations (2 per set of fields + // instead of 1 per each field). fields []*fldDescriptor } -func (r *result) createFields(prefix string, parent *msgDescriptor, fldProtos []*descriptorpb.FieldDescriptorProto) fldDescriptors { - fields := make([]*fldDescriptor, len(fldProtos)) +func (r *result) createFields(prefix string, parent *msgDescriptor, fldProtos []*descriptorpb.FieldDescriptorProto, pool *allocPool) fldDescriptors { + fields := pool.getFields(len(fldProtos)) + fieldPtrs := make([]*fldDescriptor, len(fldProtos)) for i, fldProto := range fldProtos { - fields[i] = r.createFieldDescriptor(fldProto, parent, i, prefix+fldProto.GetName()) + r.createFieldDescriptor(&fields[i], fldProto, parent, i, prefix+fldProto.GetName()) + fieldPtrs[i] = &fields[i] } - return fldDescriptors{fields: fields} + return fldDescriptors{fields: fieldPtrs} } func (f *fldDescriptors) Len() int { @@ -961,7 +1011,17 @@ func (f *fldDescriptors) ByJSONName(s string) protoreflect.FieldDescriptor { } func (f *fldDescriptors) ByTextName(s string) protoreflect.FieldDescriptor { - return f.ByName(protoreflect.Name(s)) + fld := f.ByName(protoreflect.Name(s)) + if fld != nil { + return fld + } + // Groups use type name instead, so we fallback to slow search + for _, fld := range f.fields { + if fld.TextName() == s { + return fld + } + } + return nil } func (f *fldDescriptors) ByNumber(n protoreflect.FieldNumber) protoreflect.FieldDescriptor { @@ -990,10 +1050,14 @@ type fldDescriptor struct { var _ protoreflect.FieldDescriptor = (*fldDescriptor)(nil) var _ protoutil.DescriptorProtoWrapper = (*fldDescriptor)(nil) -func (r *result) createFieldDescriptor(fd *descriptorpb.FieldDescriptorProto, parent *msgDescriptor, index int, fqn string) *fldDescriptor { - ret := &fldDescriptor{FieldDescriptor: noOpField, file: r, parent: parent, index: index, proto: fd, fqn: fqn} +func (r *result) createFieldDescriptor(ret *fldDescriptor, fd *descriptorpb.FieldDescriptorProto, parent *msgDescriptor, index int, fqn string) { r.descriptors[fqn] = ret - return ret + ret.FieldDescriptor = noOpField + ret.file = r + ret.parent = parent + ret.index = index + ret.proto = fd + ret.fqn = fqn } func (f *fldDescriptor) FieldDescriptorProto() *descriptorpb.FieldDescriptorProto { @@ -1062,7 +1126,8 @@ func (f *fldDescriptor) Cardinality() protoreflect.Cardinality { } func (f *fldDescriptor) Kind() protoreflect.Kind { - if f.proto.GetType() == descriptorpb.FieldDescriptorProto_TYPE_MESSAGE && f.Syntax() == protoreflect.Editions { + if f.proto.GetType() == descriptorpb.FieldDescriptorProto_TYPE_MESSAGE && f.Syntax() == protoreflect.Editions && + !f.IsMap() && !f.parentIsMap() { // In editions, "group encoding" (aka "delimited encoding") is toggled // via a feature. So we report group kind when that feature is enabled. messageEncoding := resolveFeature(f, messageEncodingField) @@ -1088,9 +1153,21 @@ func (f *fldDescriptor) TextName() string { if f.IsExtension() { return fmt.Sprintf("[%s]", f.FullName()) } + if f.looksLikeGroup() { + // groups use the type name + return string(protoreflect.FullName(f.proto.GetTypeName()).Name()) + } return string(f.Name()) } +func (f *fldDescriptor) looksLikeGroup() bool { + // It looks like a group if it uses group/delimited encoding (checked via f.Kind) + // and the message type is a sibling whose name is a mixed-case version of the field name. + return f.Kind() == protoreflect.GroupKind && + f.Message().FullName().Parent() == f.FullName().Parent() && + string(f.Name()) == strings.ToLower(string(f.Message().Name())) +} + func (f *fldDescriptor) HasPresence() bool { if f.proto.GetLabel() == descriptorpb.FieldDescriptorProto_LABEL_REPEATED { return false @@ -1164,6 +1241,11 @@ func (f *fldDescriptor) isMapEntry() bool { return f.Message().IsMapEntry() } +func (f *fldDescriptor) parentIsMap() bool { + parent, ok := f.parent.(protoreflect.MessageDescriptor) + return ok && parent.IsMapEntry() +} + func (f *fldDescriptor) MapKey() protoreflect.FieldDescriptor { if !f.IsMap() { return nil @@ -1430,13 +1512,13 @@ func (f *fldDescriptor) Message() protoreflect.MessageDescriptor { type oneofDescriptors struct { protoreflect.OneofDescriptors - oneofs []*oneofDescriptor + oneofs []oneofDescriptor } -func (r *result) createOneofs(prefix string, parent *msgDescriptor, ooProtos []*descriptorpb.OneofDescriptorProto) oneofDescriptors { - oos := make([]*oneofDescriptor, len(ooProtos)) +func (r *result) createOneofs(prefix string, parent *msgDescriptor, ooProtos []*descriptorpb.OneofDescriptorProto, pool *allocPool) oneofDescriptors { + oos := pool.getOneofs(len(ooProtos)) for i, fldProto := range ooProtos { - oos[i] = r.createOneofDescriptor(fldProto, parent, i, prefix+fldProto.GetName()) + r.createOneofDescriptor(&oos[i], fldProto, parent, i, prefix+fldProto.GetName()) } return oneofDescriptors{oneofs: oos} } @@ -1446,11 +1528,12 @@ func (o *oneofDescriptors) Len() int { } func (o *oneofDescriptors) Get(i int) protoreflect.OneofDescriptor { - return o.oneofs[i] + return &o.oneofs[i] } func (o *oneofDescriptors) ByName(s protoreflect.Name) protoreflect.OneofDescriptor { - for _, oo := range o.oneofs { + for i := range o.oneofs { + oo := &o.oneofs[i] if oo.Name() == s { return oo } @@ -1472,9 +1555,14 @@ type oneofDescriptor struct { var _ protoreflect.OneofDescriptor = (*oneofDescriptor)(nil) var _ protoutil.DescriptorProtoWrapper = (*oneofDescriptor)(nil) -func (r *result) createOneofDescriptor(ood *descriptorpb.OneofDescriptorProto, parent *msgDescriptor, index int, fqn string) *oneofDescriptor { - ret := &oneofDescriptor{OneofDescriptor: noOpOneof, file: r, parent: parent, index: index, proto: ood, fqn: fqn} +func (r *result) createOneofDescriptor(ret *oneofDescriptor, ood *descriptorpb.OneofDescriptorProto, parent *msgDescriptor, index int, fqn string) { r.descriptors[fqn] = ret + ret.OneofDescriptor = noOpOneof + ret.file = r + ret.parent = parent + ret.index = index + ret.proto = ood + ret.fqn = fqn var fields []*fldDescriptor for _, fld := range parent.fields.fields { @@ -1483,8 +1571,6 @@ func (r *result) createOneofDescriptor(ood *descriptorpb.OneofDescriptorProto, p } } ret.fields = fldDescriptors{fields: fields} - - return ret } func (o *oneofDescriptor) OneofDescriptorProto() *descriptorpb.OneofDescriptorProto { @@ -1542,13 +1628,13 @@ func (o *oneofDescriptor) Fields() protoreflect.FieldDescriptors { type svcDescriptors struct { protoreflect.ServiceDescriptors - svcs []*svcDescriptor + svcs []svcDescriptor } -func (r *result) createServices(prefix string, svcProtos []*descriptorpb.ServiceDescriptorProto) svcDescriptors { - svcs := make([]*svcDescriptor, len(svcProtos)) +func (r *result) createServices(prefix string, svcProtos []*descriptorpb.ServiceDescriptorProto, pool *allocPool) svcDescriptors { + svcs := pool.getServices(len(svcProtos)) for i, svcProto := range svcProtos { - svcs[i] = r.createServiceDescriptor(svcProto, i, prefix+svcProto.GetName()) + r.createServiceDescriptor(&svcs[i], svcProto, i, prefix+svcProto.GetName(), pool) } return svcDescriptors{svcs: svcs} } @@ -1558,11 +1644,12 @@ func (s *svcDescriptors) Len() int { } func (s *svcDescriptors) Get(i int) protoreflect.ServiceDescriptor { - return s.svcs[i] + return &s.svcs[i] } func (s *svcDescriptors) ByName(n protoreflect.Name) protoreflect.ServiceDescriptor { - for _, svc := range s.svcs { + for i := range s.svcs { + svc := &s.svcs[i] if svc.Name() == n { return svc } @@ -1583,14 +1670,16 @@ type svcDescriptor struct { var _ protoreflect.ServiceDescriptor = (*svcDescriptor)(nil) var _ protoutil.DescriptorProtoWrapper = (*svcDescriptor)(nil) -func (r *result) createServiceDescriptor(sd *descriptorpb.ServiceDescriptorProto, index int, fqn string) *svcDescriptor { - ret := &svcDescriptor{ServiceDescriptor: noOpService, file: r, index: index, proto: sd, fqn: fqn} +func (r *result) createServiceDescriptor(ret *svcDescriptor, sd *descriptorpb.ServiceDescriptorProto, index int, fqn string, pool *allocPool) { r.descriptors[fqn] = ret + ret.ServiceDescriptor = noOpService + ret.file = r + ret.index = index + ret.proto = sd + ret.fqn = fqn prefix := fqn + "." - ret.methods = r.createMethods(prefix, ret, sd.Method) - - return ret + ret.methods = r.createMethods(prefix, ret, sd.Method, pool) } func (s *svcDescriptor) ServiceDescriptorProto() *descriptorpb.ServiceDescriptorProto { @@ -1639,13 +1728,13 @@ func (s *svcDescriptor) Methods() protoreflect.MethodDescriptors { type mtdDescriptors struct { protoreflect.MethodDescriptors - mtds []*mtdDescriptor + mtds []mtdDescriptor } -func (r *result) createMethods(prefix string, parent *svcDescriptor, mtdProtos []*descriptorpb.MethodDescriptorProto) mtdDescriptors { - mtds := make([]*mtdDescriptor, len(mtdProtos)) +func (r *result) createMethods(prefix string, parent *svcDescriptor, mtdProtos []*descriptorpb.MethodDescriptorProto, pool *allocPool) mtdDescriptors { + mtds := pool.getMethods(len(mtdProtos)) for i, mtdProto := range mtdProtos { - mtds[i] = r.createMethodDescriptor(mtdProto, parent, i, prefix+mtdProto.GetName()) + r.createMethodDescriptor(&mtds[i], mtdProto, parent, i, prefix+mtdProto.GetName()) } return mtdDescriptors{mtds: mtds} } @@ -1655,11 +1744,12 @@ func (m *mtdDescriptors) Len() int { } func (m *mtdDescriptors) Get(i int) protoreflect.MethodDescriptor { - return m.mtds[i] + return &m.mtds[i] } func (m *mtdDescriptors) ByName(n protoreflect.Name) protoreflect.MethodDescriptor { - for _, mtd := range m.mtds { + for i := range m.mtds { + mtd := &m.mtds[i] if mtd.Name() == n { return mtd } @@ -1681,10 +1771,14 @@ type mtdDescriptor struct { var _ protoreflect.MethodDescriptor = (*mtdDescriptor)(nil) var _ protoutil.DescriptorProtoWrapper = (*mtdDescriptor)(nil) -func (r *result) createMethodDescriptor(mtd *descriptorpb.MethodDescriptorProto, parent *svcDescriptor, index int, fqn string) *mtdDescriptor { - ret := &mtdDescriptor{MethodDescriptor: noOpMethod, file: r, parent: parent, index: index, proto: mtd, fqn: fqn} +func (r *result) createMethodDescriptor(ret *mtdDescriptor, mtd *descriptorpb.MethodDescriptorProto, parent *svcDescriptor, index int, fqn string) { r.descriptors[fqn] = ret - return ret + ret.MethodDescriptor = noOpMethod + ret.file = r + ret.parent = parent + ret.index = index + ret.proto = mtd + ret.fqn = fqn } func (m *mtdDescriptor) MethodDescriptorProto() *descriptorpb.MethodDescriptorProto { @@ -1761,3 +1855,30 @@ func (r *result) hasSource() bool { _, ok := n.(*ast.FileNode) return ok } + +// resolveFeature resolves a feature for the given descriptor. If the given element +// is in a proto2 or proto3 syntax file, this skips resolution and just returns the +// relevant default (since such files are not allowed to override features). +// +// If neither the given element nor any of its ancestors override the given feature, +// the relevant default is returned. +func resolveFeature(element protoreflect.Descriptor, feature protoreflect.FieldDescriptor) protoreflect.Value { + edition := editions.GetEdition(element) + if edition == descriptorpb.Edition_EDITION_PROTO2 || edition == descriptorpb.Edition_EDITION_PROTO3 { + // these syntax levels can't specify features, so we can short-circuit the search + // through the descriptor hierarchy for feature overrides + defaults := editions.GetEditionDefaults(edition) + return defaults.ProtoReflect().Get(feature) // returns default value if field is not present + } + val, err := editions.ResolveFeature(element, feature) + if err == nil && val.IsValid() { + return val + } + defaults := editions.GetEditionDefaults(edition) + return defaults.ProtoReflect().Get(feature) +} + +func isJSONCompliant(d protoreflect.Descriptor) bool { + jsonFormat := resolveFeature(d, jsonFormatField) + return descriptorpb.FeatureSet_JsonFormat(jsonFormat.Enum()) == descriptorpb.FeatureSet_ALLOW +} diff --git a/vendor/github.com/bufbuild/protocompile/linker/editions.go b/vendor/github.com/bufbuild/protocompile/linker/editions.go deleted file mode 100644 index 341a38a41dc..00000000000 --- a/vendor/github.com/bufbuild/protocompile/linker/editions.go +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2020-2024 Buf Technologies, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package linker - -import ( - "fmt" - "sync" - - "google.golang.org/protobuf/encoding/prototext" - "google.golang.org/protobuf/reflect/protoreflect" - "google.golang.org/protobuf/reflect/protoregistry" - "google.golang.org/protobuf/types/descriptorpb" -) - -var ( - editionDefaults map[descriptorpb.Edition]*descriptorpb.FeatureSet - editionDefaultsInit sync.Once - - featureSetDescriptor = (*descriptorpb.FeatureSet)(nil).ProtoReflect().Descriptor() - fieldPresenceField = featureSetDescriptor.Fields().ByName("field_presence") - repeatedFieldEncodingField = featureSetDescriptor.Fields().ByName("repeated_field_encoding") - messageEncodingField = featureSetDescriptor.Fields().ByName("message_encoding") - enumTypeField = featureSetDescriptor.Fields().ByName("enum_type") - jsonFormatField = featureSetDescriptor.Fields().ByName("json_format") -) - -type hasFeatures interface { - GetFeatures() *descriptorpb.FeatureSet -} - -var _ hasFeatures = (*descriptorpb.FileOptions)(nil) -var _ hasFeatures = (*descriptorpb.MessageOptions)(nil) -var _ hasFeatures = (*descriptorpb.FieldOptions)(nil) -var _ hasFeatures = (*descriptorpb.OneofOptions)(nil) -var _ hasFeatures = (*descriptorpb.ExtensionRangeOptions)(nil) -var _ hasFeatures = (*descriptorpb.EnumOptions)(nil) -var _ hasFeatures = (*descriptorpb.EnumValueOptions)(nil) -var _ hasFeatures = (*descriptorpb.ServiceOptions)(nil) -var _ hasFeatures = (*descriptorpb.MethodOptions)(nil) - -// resolveFeature resolves a feature for the given descriptor. It uses the function -// to extract a value from a feature set. The function should return false as the -// second argument if the first value it returns was not actually present in the -// feature set. -func resolveFeature(d protoreflect.Descriptor, field protoreflect.FieldDescriptor) protoreflect.Value { - edition := getEdition(d) - if edition == descriptorpb.Edition_EDITION_PROTO2 || edition == descriptorpb.Edition_EDITION_PROTO3 { - // these syntax levels can't specify features, so we can short-circuit the search - // through the descriptor hierarchy for feature overrides - defaults := getEditionDefaults(edition) - return defaults.ProtoReflect().Get(field) // returns default value if field is not present - } - for { - var features *descriptorpb.FeatureSet - if withFeatures, ok := d.Options().(hasFeatures); ok { - // It should not be possible for 'ok' to ever be false... - features = withFeatures.GetFeatures() - } - featuresRef := features.ProtoReflect() - if featuresRef.Has(field) { - return featuresRef.Get(field) - } - parent := d.Parent() - if parent == nil { - // We've reached the end of the inheritance chain. So we fall back to a default. - defaults := getEditionDefaults(edition) - return defaults.ProtoReflect().Get(field) - } - d = parent - } -} - -type hasEdition interface { - // Edition returns the numeric value of a google.protobuf.Edition enum - // value that corresponds to the edition of this file. If the file does - // not use editions, it should return the enum value that corresponds - // to the syntax level, EDITION_PROTO2 or EDITION_PROTO3. - Edition() int32 -} - -var _ hasEdition = (*result)(nil) - -func getEdition(d protoreflect.Descriptor) descriptorpb.Edition { - withEdition, ok := d.ParentFile().(hasEdition) - if !ok { - // The parent file should always be a *result, so we should - // never be able to actually get in here. If we somehow did - // have another implementation of protoreflect.FileDescriptor, - // it doesn't provide a way to get the edition, other than the - // potentially expensive step of generating a FileDescriptorProto - // and then querying for the edition from that. :/ - return descriptorpb.Edition_EDITION_UNKNOWN - } - return descriptorpb.Edition(withEdition.Edition()) -} - -func getEditionDefaults(edition descriptorpb.Edition) *descriptorpb.FeatureSet { - editionDefaultsInit.Do(func() { - editionDefaults = make(map[descriptorpb.Edition]*descriptorpb.FeatureSet, len(descriptorpb.Edition_name)) - // Compute default for all known editions in descriptorpb. - for editionInt := range descriptorpb.Edition_name { - edition := descriptorpb.Edition(editionInt) - defaults := &descriptorpb.FeatureSet{} - defaultsRef := defaults.ProtoReflect() - fields := defaultsRef.Descriptor().Fields() - // Note: we are not computing defaults for extensions. Those are not needed - // by anything in the compiler, so we can get away with just computing - // defaults for these static, non-extension fields. - for i, length := 0, fields.Len(); i < length; i++ { - field := fields.Get(i) - opts, ok := field.Options().(*descriptorpb.FieldOptions) - if !ok { - continue // this is most likely impossible - } - maxEdition := descriptorpb.Edition(-1) - var maxVal string - for _, def := range opts.EditionDefaults { - if def.GetEdition() <= edition && def.GetEdition() > maxEdition { - maxEdition = def.GetEdition() - maxVal = def.GetValue() - } - } - if maxEdition == -1 { - // no matching default found - continue - } - // We use a typed nil so that it won't fall back to the global registry. Features - // should not use extensions or google.protobuf.Any, so a nil *Types is fine. - unmarshaler := prototext.UnmarshalOptions{Resolver: (*protoregistry.Types)(nil)} - // The string value is in the text format: either a field value literal or a - // message literal. (Repeated and map features aren't supported, so there's no - // array or map literal syntax to worry about.) - if field.Kind() == protoreflect.MessageKind || field.Kind() == protoreflect.GroupKind { - fldVal := defaultsRef.NewField(field) - err := unmarshaler.Unmarshal([]byte(maxVal), fldVal.Message().Interface()) - if err != nil { - continue // should we fail somehow?? - } - defaultsRef.Set(field, fldVal) - continue - } - // The value is the textformat for the field. But prototext doesn't provide a way - // to unmarshal a single field value. To work around, we unmarshal into the enclosing - // message, so we prefix the value with the field name. - maxVal = fmt.Sprintf("%s: %s", field.Name(), maxVal) - // Sadly, prototext doesn't support a Merge option. So we can't merge the value - // directly into the supplied msg. We have to instead unmarshal into an empty - // message and then use that to set the field in the supplied msg. :( - empty := defaultsRef.Type().New() - err := unmarshaler.Unmarshal([]byte(maxVal), empty.Interface()) - if err != nil { - continue // should we fail somehow?? - } - defaultsRef.Set(field, empty.Get(field)) - } - editionDefaults[edition] = defaults - } - }) - return editionDefaults[edition] -} - -func isJSONCompliant(d protoreflect.Descriptor) bool { - jsonFormat := resolveFeature(d, jsonFormatField) - return descriptorpb.FeatureSet_JsonFormat(jsonFormat.Enum()) == descriptorpb.FeatureSet_ALLOW -} diff --git a/vendor/github.com/bufbuild/protocompile/linker/files.go b/vendor/github.com/bufbuild/protocompile/linker/files.go index 6b2d32467c8..51ce3a8b3c0 100644 --- a/vendor/github.com/bufbuild/protocompile/linker/files.go +++ b/vendor/github.com/bufbuild/protocompile/linker/files.go @@ -28,7 +28,7 @@ import ( // File is like a super-powered protoreflect.FileDescriptor. It includes helpful // methods for looking up elements in the descriptor and can be used to create a -// resolver for all the file's transitive closure of dependencies. (See +// resolver for the entire transitive closure of the file's dependencies. (See // ResolverFromFile.) type File interface { protoreflect.FileDescriptor @@ -131,6 +131,8 @@ type file struct { deps Files } +var _ File = (*file)(nil) + func (f *file) FindDescriptorByName(name protoreflect.FullName) protoreflect.Descriptor { return f.descs[name] } @@ -143,7 +145,9 @@ func (f *file) FindExtensionByNumber(msg protoreflect.FullName, tag protoreflect return findExtension(f, msg, tag) } -var _ File = (*file)(nil) +func (f *file) Unwrap() protoreflect.FileDescriptor { + return f.FileDescriptor +} // Files represents a set of protobuf files. It is a slice of File values, but // also provides a method for easily looking up files by path and name. @@ -186,8 +190,8 @@ type Resolver interface { // Note that this function does not compute any additional indexes for efficient // search, so queries generally take linear time, O(n) where n is the number of // files whose elements are visible to the given file. Queries for an extension -// by number are linear with the number of messages and extensions defined across -// those files. +// by number have runtime complexity that is linear with the number of messages +// and extensions defined across those files. func ResolverFromFile(f File) Resolver { return fileResolver{f: f} } diff --git a/vendor/github.com/bufbuild/protocompile/linker/linker.go b/vendor/github.com/bufbuild/protocompile/linker/linker.go index a4da97e76f6..6d8788381ce 100644 --- a/vendor/github.com/bufbuild/protocompile/linker/linker.go +++ b/vendor/github.com/bufbuild/protocompile/linker/linker.go @@ -67,8 +67,10 @@ func Link(parsed parser.Result, dependencies Files, symbols *Symbols, handler *r prefix: prefix, optionQualifiedNames: map[ast.IdentValueNode]string{}, } + // First, we create the hierarchy of descendant descriptors. + r.createDescendants() - // First, we put all symbols into a single pool, which lets us ensure there + // Then we can put all symbols into a single pool, which lets us ensure there // are no duplicate symbols and will also let us resolve and revise all type // references in next step. if err := symbols.importResult(r, handler); err != nil { diff --git a/vendor/github.com/bufbuild/protocompile/linker/pool.go b/vendor/github.com/bufbuild/protocompile/linker/pool.go new file mode 100644 index 00000000000..3609edcb5b6 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/linker/pool.go @@ -0,0 +1,131 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package linker + +import "google.golang.org/protobuf/types/descriptorpb" + +// allocPool helps allocate descriptor instances. Instead of allocating +// them one at a time, we allocate a pool -- a large, flat slice to hold +// all descriptors of a particular kind for a file. We then use capacity +// in the pool when we need space for individual descriptors. +type allocPool struct { + numMessages int + numFields int + numOneofs int + numEnums int + numEnumValues int + numExtensions int + numServices int + numMethods int + + messages []msgDescriptor + fields []fldDescriptor + oneofs []oneofDescriptor + enums []enumDescriptor + enumVals []enValDescriptor + extensions []extTypeDescriptor + services []svcDescriptor + methods []mtdDescriptor +} + +func newAllocPool(file *descriptorpb.FileDescriptorProto) *allocPool { + var pool allocPool + pool.countElements(file) + pool.messages = make([]msgDescriptor, pool.numMessages) + pool.fields = make([]fldDescriptor, pool.numFields) + pool.oneofs = make([]oneofDescriptor, pool.numOneofs) + pool.enums = make([]enumDescriptor, pool.numEnums) + pool.enumVals = make([]enValDescriptor, pool.numEnumValues) + pool.extensions = make([]extTypeDescriptor, pool.numExtensions) + pool.services = make([]svcDescriptor, pool.numServices) + pool.methods = make([]mtdDescriptor, pool.numMethods) + return &pool +} + +func (p *allocPool) getMessages(count int) []msgDescriptor { + allocated := p.messages[:count] + p.messages = p.messages[count:] + return allocated +} + +func (p *allocPool) getFields(count int) []fldDescriptor { + allocated := p.fields[:count] + p.fields = p.fields[count:] + return allocated +} + +func (p *allocPool) getOneofs(count int) []oneofDescriptor { + allocated := p.oneofs[:count] + p.oneofs = p.oneofs[count:] + return allocated +} + +func (p *allocPool) getEnums(count int) []enumDescriptor { + allocated := p.enums[:count] + p.enums = p.enums[count:] + return allocated +} + +func (p *allocPool) getEnumValues(count int) []enValDescriptor { + allocated := p.enumVals[:count] + p.enumVals = p.enumVals[count:] + return allocated +} + +func (p *allocPool) getExtensions(count int) []extTypeDescriptor { + allocated := p.extensions[:count] + p.extensions = p.extensions[count:] + return allocated +} + +func (p *allocPool) getServices(count int) []svcDescriptor { + allocated := p.services[:count] + p.services = p.services[count:] + return allocated +} + +func (p *allocPool) getMethods(count int) []mtdDescriptor { + allocated := p.methods[:count] + p.methods = p.methods[count:] + return allocated +} + +func (p *allocPool) countElements(file *descriptorpb.FileDescriptorProto) { + p.countElementsInMessages(file.MessageType) + p.countElementsInEnums(file.EnumType) + p.numExtensions += len(file.Extension) + p.numServices += len(file.Service) + for _, svc := range file.Service { + p.numMethods += len(svc.Method) + } +} + +func (p *allocPool) countElementsInMessages(msgs []*descriptorpb.DescriptorProto) { + p.numMessages += len(msgs) + for _, msg := range msgs { + p.numFields += len(msg.Field) + p.numOneofs += len(msg.OneofDecl) + p.countElementsInMessages(msg.NestedType) + p.countElementsInEnums(msg.EnumType) + p.numExtensions += len(msg.Extension) + } +} + +func (p *allocPool) countElementsInEnums(enums []*descriptorpb.EnumDescriptorProto) { + p.numEnums += len(enums) + for _, enum := range enums { + p.numEnumValues += len(enum.Value) + } +} diff --git a/vendor/github.com/bufbuild/protocompile/linker/resolve.go b/vendor/github.com/bufbuild/protocompile/linker/resolve.go index 7d25cbdbdc0..cf30148cd46 100644 --- a/vendor/github.com/bufbuild/protocompile/linker/resolve.go +++ b/vendor/github.com/bufbuild/protocompile/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 @@ -152,27 +152,35 @@ func descriptorTypeWithArticle(d protoreflect.Descriptor) string { } } -func (r *result) resolveReferences(handler *reporter.Handler, s *Symbols) error { - // first create the full descriptor hierarchy +func (r *result) createDescendants() { fd := r.FileDescriptorProto() + pool := newAllocPool(fd) prefix := "" if fd.GetPackage() != "" { prefix = fd.GetPackage() + "." } r.imports = r.createImports() - r.messages = r.createMessages(prefix, r, fd.MessageType) - r.enums = r.createEnums(prefix, r, fd.EnumType) - r.extensions = r.createExtensions(prefix, r, fd.Extension) - r.services = r.createServices(prefix, fd.Service) + r.messages = r.createMessages(prefix, r, fd.MessageType, pool) + r.enums = r.createEnums(prefix, r, fd.EnumType, pool) + r.extensions = r.createExtensions(prefix, r, fd.Extension, pool) + r.services = r.createServices(prefix, fd.Service, pool) +} - // then resolve symbol references - scopes := []scope{fileScope(r)} +func (r *result) resolveReferences(handler *reporter.Handler, s *Symbols) error { + fd := r.FileDescriptorProto() + 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 } } + // This is to de-dupe extendee-releated error messages when the same + // extendee is referenced from multiple extension field definitions. + // We leave it nil if there's no AST. + var extendeeNodes map[ast.Node]struct{} + return walk.DescriptorsEnterAndExit(r, func(d protoreflect.Descriptor) error { fqn := d.FullName() @@ -183,7 +191,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 } } @@ -192,18 +200,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 err := resolveFieldTypes(d.field, handler, s, scopes); err != nil { + if extendeeNodes == nil && r.AST() != nil { + extendeeNodes = map[ast.Node]struct{}{} + } + if err := resolveFieldTypes(&d.field, handler, extendeeNodes, s, scopes, checkedCache); err != nil { return err } if r.Syntax() == protoreflect.Proto3 && !allowedProto3Extendee(d.field.proto.GetExtendee()) { @@ -215,34 +226,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, 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 } } @@ -250,11 +261,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 } } @@ -291,25 +302,54 @@ func allowedProto3Extendee(n string) bool { return ok } -func resolveFieldTypes(f *fldDescriptor, handler *reporter.Handler, 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) - dsc := r.resolve(fld.GetExtendee(), false, scopes) + kind = "extension" + var alreadyReported bool + if extendees != nil { + _, alreadyReported = extendees[node.FieldExtendee()] + if !alreadyReported { + extendees[node.FieldExtendee()] = struct{}{} + } + } + dsc := r.resolve(fld.GetExtendee(), false, scopes, checkedCache) if dsc == nil { - return handler.HandleErrorf(file.NodeInfo(node.FieldExtendee()), "unknown extendee type %s", fld.GetExtendee()) + 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) { - return handler.HandleErrorf(file.NodeInfo(node.FieldExtendee()), "unknown extendee type %s; resolved to %s which is not defined; consider using a leading dot", fld.GetExtendee(), dsc.FullName()) + 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) if !ok { - return handler.HandleErrorf(file.NodeInfo(node.FieldExtendee()), "extendee is invalid: %s is %s, not a message", dsc.FullName(), descriptorTypeWithArticle(dsc)) + 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)) } + f.extendee = extd extendeeName := "." + string(dsc.FullName()) if fld.GetExtendee() != extendeeName { @@ -326,7 +366,7 @@ func resolveFieldTypes(f *fldDescriptor, handler *reporter.Handler, s *Symbols, } } 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 { @@ -346,12 +386,12 @@ func resolveFieldTypes(f *fldDescriptor, handler *reporter.Handler, s *Symbols, 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: @@ -361,7 +401,7 @@ func resolveFieldTypes(f *fldDescriptor, handler *reporter.Handler, s *Symbols, 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 @@ -379,7 +419,7 @@ func resolveFieldTypes(f *fldDescriptor, handler *reporter.Handler, s *Symbols, } } 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()) @@ -390,7 +430,7 @@ func resolveFieldTypes(f *fldDescriptor, handler *reporter.Handler, s *Symbols, // 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: @@ -402,11 +442,11 @@ func resolveFieldTypes(f *fldDescriptor, handler *reporter.Handler, s *Symbols, // 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 } @@ -426,13 +466,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 { - scope := fmt.Sprintf("method %s", m.fqn) +func resolveMethodTypes(m *mtdDescriptor, handler *reporter.Handler, scopes []scope, checkedCache []string) error { + scope := "method " + 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 @@ -454,7 +494,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 @@ -478,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) 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), @@ -491,7 +531,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 @@ -504,7 +544,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 @@ -512,7 +552,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: @@ -522,7 +562,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 } } @@ -541,7 +581,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 @@ -562,7 +602,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 } } @@ -570,8 +610,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) } @@ -586,10 +626,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 @@ -634,13 +674,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/vendor/github.com/bufbuild/protocompile/linker/symbols.go b/vendor/github.com/bufbuild/protocompile/linker/symbols.go index 8a66af06e45..c8db762b36a 100644 --- a/vendor/github.com/bufbuild/protocompile/linker/symbols.go +++ b/vendor/github.com/bufbuild/protocompile/linker/symbols.go @@ -18,12 +18,11 @@ import ( "strings" "sync" - "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" - "google.golang.org/protobuf/types/descriptorpb" "github.com/bufbuild/protocompile/ast" "github.com/bufbuild/protocompile/internal" + "github.com/bufbuild/protocompile/protoutil" "github.com/bufbuild/protocompile/reporter" "github.com/bufbuild/protocompile/walk" ) @@ -53,7 +52,7 @@ type packageSymbols struct { children map[protoreflect.FullName]*packageSymbols files map[protoreflect.FileDescriptor]struct{} symbols map[protoreflect.FullName]symbolEntry - exts map[extNumber]ast.SourcePos + exts map[extNumber]ast.SourceSpan } type extNumber struct { @@ -68,7 +67,7 @@ type symbolEntry struct { } type extDecl struct { - pos ast.SourcePos + span ast.SourceSpan extendee protoreflect.FullName tag protoreflect.FieldNumber } @@ -174,15 +173,15 @@ func (s *Symbols) importPackages(pkgSpan ast.SourceSpan, pkg protoreflect.FullNa return &s.pkgTrie, nil } - parts := strings.Split(string(pkg), ".") - for i := 1; i < len(parts); i++ { - parts[i] = parts[i-1] + "." + parts[i] - } - cur := &s.pkgTrie - for _, p := range parts { + enumerator := nameEnumerator{name: pkg} + for { + p, ok := enumerator.next() + if !ok { + return cur, nil + } var err error - cur, err = cur.importPackage(pkgSpan, protoreflect.FullName(p), handler) + cur, err = cur.importPackage(pkgSpan, p, handler) if err != nil { return nil, err } @@ -190,8 +189,6 @@ func (s *Symbols) importPackages(pkgSpan ast.SourceSpan, pkg protoreflect.FullNa return nil, nil } } - - return cur, nil } func (s *packageSymbols) importPackage(pkgSpan ast.SourceSpan, pkg protoreflect.FullName, handler *reporter.Handler) (*packageSymbols, error) { @@ -232,29 +229,29 @@ func (s *packageSymbols) importPackage(pkgSpan ast.SourceSpan, pkg protoreflect. return child, nil } -func (s *Symbols) getPackage(pkg protoreflect.FullName) *packageSymbols { +func (s *Symbols) getPackage(pkg protoreflect.FullName, exact bool) *packageSymbols { if pkg == "" { return &s.pkgTrie } - - parts := strings.Split(string(pkg), ".") - for i := 1; i < len(parts); i++ { - parts[i] = parts[i-1] + "." + parts[i] - } - cur := &s.pkgTrie - for _, p := range parts { + enumerator := nameEnumerator{name: pkg} + for { + p, ok := enumerator.next() + if !ok { + return cur + } cur.mu.RLock() - next := cur.children[protoreflect.FullName(p)] + next := cur.children[p] cur.mu.RUnlock() if next == nil { - return nil + if exact { + return nil + } + return cur } cur = next } - - return cur } func reportSymbolCollision(span ast.SourceSpan, fqn protoreflect.FullName, additionIsEnumVal bool, existing symbolEntry, handler *reporter.Handler) error { @@ -322,6 +319,9 @@ func sourceSpanFor(d protoreflect.Descriptor) ast.SourceSpan { if file == nil { return ast.UnknownSpan(unknownFilePath) } + if result, ok := file.(*result); ok { + return nameSpan(result.FileNode(), result.Node(protoutil.ProtoFromDescriptor(d))) + } path, ok := internal.ComputePath(d) if !ok { return ast.UnknownSpan(file.Path()) @@ -405,7 +405,7 @@ func (s *packageSymbols) commitFileLocked(f protoreflect.FileDescriptor) { s.symbols = map[protoreflect.FullName]symbolEntry{} } if s.exts == nil { - s.exts = map[extNumber]ast.SourcePos{} + s.exts = map[extNumber]ast.SourceSpan{} } _ = walk.Descriptors(f, func(d protoreflect.Descriptor) error { span := sourceSpanFor(d) @@ -471,32 +471,33 @@ func (s *packageSymbols) importResult(r *result, handler *reporter.Handler) (boo } // second pass: commit all symbols - s.commitResultLocked(r) + s.commitFileLocked(r) return true, nil } func (s *packageSymbols) checkResultLocked(r *result, handler *reporter.Handler) error { resultSyms := map[protoreflect.FullName]symbolEntry{} - return walk.DescriptorProtos(r.FileDescriptorProto(), func(fqn protoreflect.FullName, d proto.Message) error { - _, isEnumVal := d.(*descriptorpb.EnumValueDescriptorProto) + return walk.Descriptors(r, func(d protoreflect.Descriptor) error { + _, isEnumVal := d.(protoreflect.EnumValueDescriptor) file := r.FileNode() - node := r.Node(d) + name := d.FullName() + node := r.Node(protoutil.ProtoFromDescriptor(d)) span := nameSpan(file, node) // check symbols already in this symbol table - if existing, ok := s.symbols[fqn]; ok { - if err := reportSymbolCollision(span, fqn, isEnumVal, existing, handler); err != nil { + if existing, ok := s.symbols[name]; ok { + if err := reportSymbolCollision(span, name, isEnumVal, existing, handler); err != nil { return err } } // also check symbols from this result (that are not yet in symbol table) - if existing, ok := resultSyms[fqn]; ok { - if err := reportSymbolCollision(span, fqn, isEnumVal, existing, handler); err != nil { + if existing, ok := resultSyms[name]; ok { + if err := reportSymbolCollision(span, name, isEnumVal, existing, handler); err != nil { return err } } - resultSyms[fqn] = symbolEntry{ + resultSyms[name] = symbolEntry{ span: span, isEnumValue: isEnumVal, } @@ -538,26 +539,6 @@ func nameSpan(file ast.FileDeclNode, n ast.Node) ast.SourceSpan { } } -func (s *packageSymbols) commitResultLocked(r *result) { - if s.symbols == nil { - s.symbols = map[protoreflect.FullName]symbolEntry{} - } - if s.exts == nil { - s.exts = map[extNumber]ast.SourcePos{} - } - _ = walk.DescriptorProtos(r.FileDescriptorProto(), func(fqn protoreflect.FullName, d proto.Message) error { - span := nameSpan(r.FileNode(), r.Node(d)) - _, isEnumValue := d.(protoreflect.EnumValueDescriptor) - s.symbols[fqn] = symbolEntry{span: span, isEnumValue: isEnumValue} - return nil - }) - - if s.files == nil { - s.files = map[protoreflect.FileDescriptor]struct{}{} - } - s.files[r] = struct{}{} -} - // AddExtension records the given extension, which is used to ensure that no two files // attempt to extend the same message using the same tag. The given pkg should be the // package that defines extendee. @@ -567,7 +548,7 @@ func (s *Symbols) AddExtension(pkg, extendee protoreflect.FullName, tag protoref return handler.HandleErrorf(span, "could not register extension: extendee %q does not match package %q", extendee, pkg) } } - pkgSyms := s.getPackage(pkg) + pkgSyms := s.getPackage(pkg, true) if pkgSyms == nil { // should never happen return handler.HandleErrorf(span, "could not register extension: missing package symbols for %q", pkg) @@ -581,13 +562,13 @@ func (s *packageSymbols) addExtension(extendee protoreflect.FullName, tag protor extNum := extNumber{extendee: extendee, tag: tag} if existing, ok := s.exts[extNum]; ok { - return handler.HandleErrorf(span, "extension with tag %d for message %s already defined at %v", tag, extendee, existing) + return handler.HandleErrorf(span, "extension with tag %d for message %s already defined at %v", tag, extendee, existing.Start()) } if s.exts == nil { - s.exts = map[extNumber]ast.SourcePos{} + s.exts = map[extNumber]ast.SourceSpan{} } - s.exts[extNum] = span.Start() + s.exts[extNum] = span return nil } @@ -602,15 +583,53 @@ func (s *Symbols) AddExtensionDeclaration(extension, extendee protoreflect.FullN // This is a declaration that has already been added. Ignore. return nil } - return handler.HandleErrorf(span, "extension %s already declared as extending %s with tag %d at %v", extension, existing.extendee, existing.tag, existing.pos) + return handler.HandleErrorf(span, "extension %s already declared as extending %s with tag %d at %v", extension, existing.extendee, existing.tag, existing.span.Start()) } if s.extDecls == nil { s.extDecls = map[protoreflect.FullName]extDecl{} } s.extDecls[extension] = extDecl{ - pos: span.Start(), + span: span, extendee: extendee, tag: tag, } return nil } + +// Lookup finds the registered location of the given name. If the given name has +// not been seen/registered, nil is returned. +func (s *Symbols) Lookup(name protoreflect.FullName) ast.SourceSpan { + // note: getPackage never returns nil when exact=false + pkgSyms := s.getPackage(name, false) + if entry, ok := pkgSyms.symbols[name]; ok { + return entry.span + } + return nil +} + +// LookupExtension finds the registered location of the given extension. If the given +// extension has not been seen/registered, nil is returned. +func (s *Symbols) LookupExtension(messageName protoreflect.FullName, extensionNumber protoreflect.FieldNumber) ast.SourceSpan { + // note: getPackage never returns nil when exact=false + pkgSyms := s.getPackage(messageName, false) + return pkgSyms.exts[extNumber{messageName, extensionNumber}] +} + +type nameEnumerator struct { + name protoreflect.FullName + start int +} + +func (e *nameEnumerator) next() (protoreflect.FullName, bool) { + if e.start < 0 { + return "", false + } + pos := strings.IndexByte(string(e.name[e.start:]), '.') + if pos == -1 { + e.start = -1 + return e.name, true + } + pos += e.start + e.start = pos + 1 + return e.name[:pos], true +} diff --git a/vendor/github.com/bufbuild/protocompile/linker/validate.go b/vendor/github.com/bufbuild/protocompile/linker/validate.go index f9dad2edf8d..6633a9f3520 100644 --- a/vendor/github.com/bufbuild/protocompile/linker/validate.go +++ b/vendor/github.com/bufbuild/protocompile/linker/validate.go @@ -267,7 +267,7 @@ func (r *result) validateExtension(fd *fldDescriptor, handler *reporter.Handler) if extRangeOpts == nil { break } - if extRangeOpts.GetVerification() == descriptorpb.ExtensionRangeOptions_UNVERIFIED { + if len(extRangeOpts.Declaration) == 0 && extRangeOpts.GetVerification() != descriptorpb.ExtensionRangeOptions_DECLARATION { break } var found bool @@ -294,7 +294,7 @@ func (r *result) validateExtension(fd *fldDescriptor, handler *reporter.Handler) span, _ := findExtensionRangeOptionSpan(msg.ParentFile(), msg, i, extRange, internal.ExtensionRangeOptionsDeclarationTag, int32(j), internal.ExtensionRangeOptionsDeclarationFullNameTag) err := handler.HandleErrorf(info, "expected extension with number %d to be named %s, not %s, per declaration at %v", - fd.Number(), extDecl.GetFullName(), fd.FullName(), span.Start()) + fd.Number(), strings.TrimPrefix(extDecl.GetFullName(), "."), fd.FullName(), span.Start()) if err != nil { return err } @@ -305,7 +305,7 @@ func (r *result) validateExtension(fd *fldDescriptor, handler *reporter.Handler) span, _ := findExtensionRangeOptionSpan(msg.ParentFile(), msg, i, extRange, internal.ExtensionRangeOptionsDeclarationTag, int32(j), internal.ExtensionRangeOptionsDeclarationTypeTag) err := handler.HandleErrorf(info, "expected extension with number %d to have type %s, not %s, per declaration at %v", - fd.Number(), extDecl.GetType(), getTypeName(fd), span.Start()) + fd.Number(), strings.TrimPrefix(extDecl.GetType(), "."), getTypeName(fd), span.Start()) if err != nil { return err } @@ -520,7 +520,7 @@ func (r *result) validateJSONNamesInEnum(ed *enumDescriptor, handler *reporter.H // With editions, not fully supporting JSON is allowed via feature: json_format == BEST_EFFORT if !isJSONCompliant(ed) { handler.HandleWarningWithPos(r.FileNode().NodeInfo(fldNode), conflictErr) - } else if err := handler.HandleErrorf(r.FileNode().NodeInfo(fldNode), conflictErr.Error()); err != nil { + } else if err := handler.HandleErrorWithPos(r.FileNode().NodeInfo(fldNode), conflictErr); err != nil { return err } } else { @@ -590,12 +590,15 @@ func (r *result) validateExtensionDeclarations(md *msgDescriptor, handler *repor // nothing to check continue } - if len(opts.GetDeclaration()) > 0 && opts.GetVerification() == descriptorpb.ExtensionRangeOptions_UNVERIFIED { + // If any declarations are present, verification is assumed to be + // DECLARATION. It's an error for declarations to be present but the + // verification field explicitly set to something other than that. + if opts.Verification != nil && opts.GetVerification() != descriptorpb.ExtensionRangeOptions_DECLARATION { span, ok := findExtensionRangeOptionSpan(r, md, i, extRange, internal.ExtensionRangeOptionsVerificationTag) if !ok { span, _ = findExtensionRangeOptionSpan(r, md, i, extRange, internal.ExtensionRangeOptionsDeclarationTag, 0) } - if err := handler.HandleErrorf(span, "extension range cannot have declarations and have verification of UNVERIFIED"); err != nil { + if err := handler.HandleErrorf(span, "extension range cannot have declarations and have verification of %s", opts.GetVerification()); err != nil { return err } } @@ -652,68 +655,66 @@ func (r *result) validateExtensionDeclarations(md *msgDescriptor, handler *repor } } - if extDecl.GetReserved() { - if extDecl.FullName != nil { - span, _ := findExtensionRangeOptionSpan(r, md, i, extRange, - internal.ExtensionRangeOptionsDeclarationTag, int32(i), internal.ExtensionRangeOptionsDeclarationFullNameTag) - if err := handler.HandleErrorf(span, "extension declaration is marked reserved so full_name should not be present"); err != nil { + if extDecl.FullName == nil && !extDecl.GetReserved() { + span, _ := findExtensionRangeOptionSpan(r, md, i, extRange, internal.ExtensionRangeOptionsDeclarationTag, int32(i)) + if err := handler.HandleErrorf(span, "extension declaration that is not marked reserved must have a full_name"); err != nil { + return err + } + } else if extDecl.FullName != nil { + var extensionFullName protoreflect.FullName + extensionNameSpan, _ := findExtensionRangeOptionSpan(r, md, i, extRange, + internal.ExtensionRangeOptionsDeclarationTag, int32(i), internal.ExtensionRangeOptionsDeclarationFullNameTag) + if !strings.HasPrefix(extDecl.GetFullName(), ".") { + if err := handler.HandleErrorf(extensionNameSpan, "extension declaration full name %q should start with a leading dot (.)", extDecl.GetFullName()); err != nil { return err } + extensionFullName = protoreflect.FullName(extDecl.GetFullName()) + } else { + extensionFullName = protoreflect.FullName(extDecl.GetFullName()[1:]) } - if extDecl.Type != nil { - span, _ := findExtensionRangeOptionSpan(r, md, i, extRange, - internal.ExtensionRangeOptionsDeclarationTag, int32(i), internal.ExtensionRangeOptionsDeclarationTypeTag) - if err := handler.HandleErrorf(span, "extension declaration is marked reserved so type should not be present"); err != nil { + if !extensionFullName.IsValid() { + if err := handler.HandleErrorf(extensionNameSpan, "extension declaration full name %q is not a valid qualified name", extDecl.GetFullName()); err != nil { return err } } - continue - } - - if extDecl.FullName == nil { - span, _ := findExtensionRangeOptionSpan(r, md, i, extRange, internal.ExtensionRangeOptionsDeclarationTag, int32(i)) - if err := handler.HandleErrorf(span, "extension declaration that is not marked reserved must have a full_name"); err != nil { + if err := symbols.AddExtensionDeclaration(extensionFullName, md.FullName(), protoreflect.FieldNumber(extDecl.GetNumber()), extensionNameSpan, handler); err != nil { return err } } - var extensionFullName protoreflect.FullName - extensionNameSpan, _ := findExtensionRangeOptionSpan(r, md, i, extRange, - internal.ExtensionRangeOptionsDeclarationTag, int32(i), internal.ExtensionRangeOptionsDeclarationFullNameTag) - if !strings.HasPrefix(extDecl.GetFullName(), ".") { - if err := handler.HandleErrorf(extensionNameSpan, "extension declaration full name %q should start with a leading dot (.)", extDecl.GetFullName()); err != nil { - return err - } - extensionFullName = protoreflect.FullName(extDecl.GetFullName()) - } else { - extensionFullName = protoreflect.FullName(extDecl.GetFullName()[1:]) - } - if !extensionFullName.IsValid() { - if err := handler.HandleErrorf(extensionNameSpan, "extension declaration full name %q is not a valid qualified name", extDecl.GetFullName()); err != nil { - return err - } - } - if err := symbols.AddExtensionDeclaration(extensionFullName, md.FullName(), protoreflect.FieldNumber(extDecl.GetNumber()), extensionNameSpan, handler); err != nil { - return err - } - if extDecl.Type == nil { + if extDecl.Type == nil && !extDecl.GetReserved() { span, _ := findExtensionRangeOptionSpan(r, md, i, extRange, internal.ExtensionRangeOptionsDeclarationTag, int32(i)) if err := handler.HandleErrorf(span, "extension declaration that is not marked reserved must have a type"); err != nil { return err } - } - if strings.HasPrefix(extDecl.GetType(), ".") { - if !protoreflect.FullName(extDecl.GetType()[1:]).IsValid() { + } else if extDecl.Type != nil { + if strings.HasPrefix(extDecl.GetType(), ".") { + if !protoreflect.FullName(extDecl.GetType()[1:]).IsValid() { + span, _ := findExtensionRangeOptionSpan(r, md, i, extRange, + internal.ExtensionRangeOptionsDeclarationTag, int32(i), internal.ExtensionRangeOptionsDeclarationTypeTag) + if err := handler.HandleErrorf(span, "extension declaration type %q is not a valid qualified name", extDecl.GetType()); err != nil { + return err + } + } + } else if !isBuiltinTypeName(extDecl.GetType()) { span, _ := findExtensionRangeOptionSpan(r, md, i, extRange, internal.ExtensionRangeOptionsDeclarationTag, int32(i), internal.ExtensionRangeOptionsDeclarationTypeTag) - if err := handler.HandleErrorf(span, "extension declaration type %q is not a valid qualified name", extDecl.GetType()); err != nil { + if err := handler.HandleErrorf(span, "extension declaration type %q must be a builtin type or start with a leading dot (.)", extDecl.GetType()); err != nil { return err } } - } else if !isBuiltinTypeName(extDecl.GetType()) { + } + + if extDecl.GetReserved() && (extDecl.FullName == nil) != (extDecl.Type == nil) { + var fieldTag int32 + if extDecl.FullName != nil { + fieldTag = internal.ExtensionRangeOptionsDeclarationFullNameTag + } else { + fieldTag = internal.ExtensionRangeOptionsDeclarationTypeTag + } span, _ := findExtensionRangeOptionSpan(r, md, i, extRange, - internal.ExtensionRangeOptionsDeclarationTag, int32(i), internal.ExtensionRangeOptionsDeclarationTypeTag) - if err := handler.HandleErrorf(span, "extension declaration type %q must be a builtin type or start with a leading dot (.)", extDecl.GetType()); err != nil { + internal.ExtensionRangeOptionsDeclarationTag, int32(i), fieldTag) + if err := handler.HandleErrorf(span, "extension declarations that are reserved should specify both full_name and type or neither"); err != nil { return err } } diff --git a/vendor/github.com/bufbuild/protocompile/options/options.go b/vendor/github.com/bufbuild/protocompile/options/options.go index 103531af2c5..e22bb2af49c 100644 --- a/vendor/github.com/bufbuild/protocompile/options/options.go +++ b/vendor/github.com/bufbuild/protocompile/options/options.go @@ -41,30 +41,28 @@ import ( "github.com/bufbuild/protocompile/ast" "github.com/bufbuild/protocompile/internal" + "github.com/bufbuild/protocompile/internal/messageset" "github.com/bufbuild/protocompile/linker" "github.com/bufbuild/protocompile/parser" "github.com/bufbuild/protocompile/reporter" "github.com/bufbuild/protocompile/sourceinfo" ) -const ( - // featuresFieldName is the name of a field in every options message. - featuresFieldName = "features" -) - -var ( - featureSetType = (*descriptorpb.FeatureSet)(nil).ProtoReflect().Type() - featureSetName = featureSetType.Descriptor().FullName() -) - type interpreter struct { file file resolver linker.Resolver overrideDescriptorProto linker.File - lenient bool - reporter *reporter.Handler - index sourceinfo.OptionIndex - pathBuffer []int32 + + index sourceinfo.OptionIndex + pathBuffer []int32 + + reporter *reporter.Handler + lenient bool + + // lenienceEnabled is set to true when errors reported to reporter + // should be lenient + lenienceEnabled bool + lenientErrReported bool } type file interface { @@ -135,7 +133,7 @@ func InterpretUnlinkedOptions(parsed parser.Result, opts ...InterpreterOption) ( } func interpretOptions(lenient bool, file file, res linker.Resolver, handler *reporter.Handler, interpOpts []InterpreterOption) (sourceinfo.OptionIndex, error) { - interp := interpreter{ + interp := &interpreter{ file: file, resolver: res, lenient: lenient, @@ -144,7 +142,7 @@ func interpretOptions(lenient bool, file file, res linker.Resolver, handler *rep pathBuffer: make([]int32, 0, 16), } for _, opt := range interpOpts { - opt(&interp) + opt(interp) } // We have to do this in two phases. First we interpret non-custom options. // This allows us to handle standard options and features that may needed to @@ -159,6 +157,30 @@ func interpretOptions(lenient bool, file file, res linker.Resolver, handler *rep return interp.index, nil } +func (interp *interpreter) handleErrorf(span ast.SourceSpan, msg string, args ...interface{}) error { + if interp.lenienceEnabled { + interp.lenientErrReported = true + return nil + } + return interp.reporter.HandleErrorf(span, msg, args...) +} + +func (interp *interpreter) handleErrorWithPos(span ast.SourceSpan, err error) error { + if interp.lenienceEnabled { + interp.lenientErrReported = true + return nil + } + return interp.reporter.HandleErrorWithPos(span, err) +} + +func (interp *interpreter) handleError(err error) error { + if interp.lenienceEnabled { + interp.lenientErrReported = true + return nil + } + return interp.reporter.HandleError(err) +} + func (interp *interpreter) interpretFileOptions(file file, customOpts bool) error { fd := file.FileDescriptorProto() prefix := fd.GetPackage() @@ -345,7 +367,10 @@ func (interp *interpreter) interpretFieldOptions(fqn string, fld *descriptorpb.F // For non-custom phase, first process pseudo-options if len(opts.GetUninterpretedOption()) > 0 && !customOpts { - if err := interp.interpretFieldPseudoOptions(fqn, fld, opts); err != nil { + interp.enableLenience(true) + err := interp.interpretFieldPseudoOptions(fqn, fld, opts) + interp.enableLenience(false) + if err != nil { return err } } @@ -366,25 +391,23 @@ func (interp *interpreter) interpretFieldOptions(fqn string, fld *descriptorpb.F } func (interp *interpreter) interpretFieldPseudoOptions(fqn string, fld *descriptorpb.FieldDescriptorProto, opts *descriptorpb.FieldOptions) error { - scope := fmt.Sprintf("field %s", fqn) + scope := "field " + fqn uo := opts.UninterpretedOption // process json_name pseudo-option - index, err := internal.FindOption(interp.file, interp.reporter, scope, uo, "json_name") - if err != nil && !interp.lenient { + if index, err := internal.FindOption(interp.file, interp.handleErrorf, scope, uo, "json_name"); err != nil { return err - } - if index >= 0 { + } else if index >= 0 { opt := uo[index] optNode := interp.file.OptionNode(opt) if opt.StringValue == nil { - return interp.reporter.HandleErrorf(interp.nodeInfo(optNode.GetValue()), "%s: expecting string value for json_name option", scope) + return interp.handleErrorf(interp.nodeInfo(optNode.GetValue()), "%s: expecting string value for json_name option", scope) } jsonName := string(opt.StringValue) // Extensions don't support custom json_name values. // If the value is already set (via the descriptor) and doesn't match the default value, return an error. if fld.GetExtendee() != "" && jsonName != "" && jsonName != internal.JSONName(fld.GetName()) { - return interp.reporter.HandleErrorf(interp.nodeInfo(optNode.GetName()), "%s: option json_name is not allowed on extensions", scope) + return interp.handleErrorf(interp.nodeInfo(optNode.GetName()), "%s: option json_name is not allowed on extensions", scope) } // attribute source code info if on, ok := optNode.(*ast.OptionNode); ok { @@ -392,13 +415,13 @@ func (interp *interpreter) interpretFieldPseudoOptions(fqn string, fld *descript } uo = internal.RemoveOption(uo, index) if strings.HasPrefix(jsonName, "[") && strings.HasSuffix(jsonName, "]") { - return interp.reporter.HandleErrorf(interp.nodeInfo(optNode.GetValue()), "%s: option json_name value cannot start with '[' and end with ']'; that is reserved for representing extensions", scope) + return interp.handleErrorf(interp.nodeInfo(optNode.GetValue()), "%s: option json_name value cannot start with '[' and end with ']'; that is reserved for representing extensions", scope) } fld.JsonName = proto.String(jsonName) } // and process default pseudo-option - if index, err := interp.processDefaultOption(scope, fqn, fld, uo); err != nil && !interp.lenient { + if index, err := interp.processDefaultOption(scope, fqn, fld, uo); err != nil { return err } else if index >= 0 { // attribute source code info @@ -414,17 +437,17 @@ func (interp *interpreter) interpretFieldPseudoOptions(fqn string, fld *descript } func (interp *interpreter) processDefaultOption(scope string, fqn string, fld *descriptorpb.FieldDescriptorProto, uos []*descriptorpb.UninterpretedOption) (defaultIndex int, err error) { - found, err := internal.FindOption(interp.file, interp.reporter, scope, uos, "default") + found, err := internal.FindOption(interp.file, interp.handleErrorf, scope, uos, "default") if err != nil || found == -1 { return -1, err } opt := uos[found] optNode := interp.file.OptionNode(opt) if fld.GetLabel() == descriptorpb.FieldDescriptorProto_LABEL_REPEATED { - return -1, interp.reporter.HandleErrorf(interp.nodeInfo(optNode.GetName()), "%s: default value cannot be set because field is repeated", scope) + return -1, interp.handleErrorf(interp.nodeInfo(optNode.GetName()), "%s: default value cannot be set because field is repeated", scope) } if fld.GetType() == descriptorpb.FieldDescriptorProto_TYPE_GROUP || fld.GetType() == descriptorpb.FieldDescriptorProto_TYPE_MESSAGE { - return -1, interp.reporter.HandleErrorf(interp.nodeInfo(optNode.GetName()), "%s: default value cannot be set because field is a message", scope) + return -1, interp.handleErrorf(interp.nodeInfo(optNode.GetName()), "%s: default value cannot be set because field is a message", scope) } mc := &internal.MessageContext{ File: interp.file, @@ -443,7 +466,7 @@ func (interp *interpreter) processDefaultOption(scope string, fqn string, fld *d v, err = interp.defaultValue(mc, fld, val) } if err != nil { - return -1, interp.reporter.HandleError(err) + return -1, interp.handleError(err) } if str, ok := v.(string); ok { @@ -542,13 +565,40 @@ func interpretElementOptions[Elem elementType[OptsStruct, Opts], OptsStruct any, customOpts bool, ) error { opts := elem.GetOptions() - uo := opts.GetUninterpretedOption() - if len(uo) > 0 { - remain, err := interp.interpretOptions(fqn, target.t, elem, opts, uo, customOpts) + uninterpreted := opts.GetUninterpretedOption() + if len(uninterpreted) > 0 { + remain, err := interp.interpretOptions(fqn, target.t, elem, opts, uninterpreted, customOpts) if err != nil { return err } target.setUninterpretedOptions(opts, remain) + } else if customOpts { + // If customOpts is true, we are in second pass of interpreting. + // For second pass, even if there are no options to interpret, we still + // need to verify feature usage. + features := opts.GetFeatures() + var msg protoreflect.Message + if len(features.ProtoReflect().GetUnknown()) > 0 { + // We need to first convert to a message that uses the sources' definition + // of FeatureSet. + optsDesc := opts.ProtoReflect().Descriptor() + optsFqn := string(optsDesc.FullName()) + if md := interp.resolveOptionsType(optsFqn); md != nil { + dm := dynamicpb.NewMessage(md) + if err := cloneInto(dm, opts, interp.resolver); err != nil { + node := interp.file.Node(elem) + return interp.handleError(reporter.Error(interp.nodeInfo(node), err)) + } + msg = dm + } + } + if msg == nil { + msg = opts.ProtoReflect() + } + err := interp.validateRecursive(false, msg, "", elem, nil, false, false, false) + if err != nil { + return err + } } return nil } @@ -571,9 +621,9 @@ func (interp *interpreter) interpretOptions( // see if the parse included an override copy for these options if md := interp.resolveOptionsType(optsFqn); md != nil { dm := dynamicpb.NewMessage(md) - if err := cloneInto(dm, opts, nil); err != nil { + if err := cloneInto(dm, opts, interp.resolver); err != nil { node := interp.file.Node(element) - return nil, interp.reporter.HandleError(reporter.Error(interp.nodeInfo(node), err)) + return nil, interp.handleError(reporter.Error(interp.nodeInfo(node), err)) } msg = dm } else { @@ -586,45 +636,58 @@ func (interp *interpreter) interpretOptions( ElementType: descriptorType(element), } var remain []*descriptorpb.UninterpretedOption - var features []*ast.OptionNode for _, uo := range uninterpreted { - if uo.Name[0].GetIsExtension() != customOpts { + isCustom := uo.Name[0].GetIsExtension() + if isCustom != customOpts { // We're not looking at these this phase. remain = append(remain, uo) continue } + firstName := uo.Name[0].GetNamePart() + if targetType == descriptorpb.FieldOptions_TARGET_TYPE_FIELD && + !isCustom && (firstName == "default" || firstName == "json_name") { + // Field pseudo-option that we can skip and is handled elsewhere. + remain = append(remain, uo) + continue + } node := interp.file.OptionNode(uo) - if !uo.Name[0].GetIsExtension() && uo.Name[0].GetNamePart() == "uninterpreted_option" { + if !isCustom && firstName == "uninterpreted_option" { if interp.lenient { remain = append(remain, uo) continue } // uninterpreted_option might be found reflectively, but is not actually valid for use - if err := interp.reporter.HandleErrorf(interp.nodeInfo(node.GetName()), "%vinvalid option 'uninterpreted_option'", mc); err != nil { + if err := interp.handleErrorf(interp.nodeInfo(node.GetName()), "%vinvalid option 'uninterpreted_option'", mc); err != nil { return nil, err } } mc.Option = uo - srcInfo, err := interp.interpretField(mc, msg, uo, 0, interp.pathBuffer) + interp.enableLenience(true) + srcInfo, err := interp.interpretField(targetType, mc, msg, uo, 0, interp.pathBuffer) + interp.enableLenience(false) if err != nil { - if interp.lenient { - remain = append(remain, uo) - continue - } return nil, err } - if optn, ok := node.(*ast.OptionNode); ok { - if !uo.Name[0].GetIsExtension() && uo.Name[0].GetNamePart() == featuresFieldName { - features = append(features, optn) - } - if srcInfo != nil { + if interp.lenientErrReported { + remain = append(remain, uo) + continue + } + + if srcInfo != nil { + if optn, ok := node.(*ast.OptionNode); ok { interp.index[optn] = srcInfo } } } - if err := interp.validateFeatures(targetType, msg, features); err != nil && !interp.lenient { - return nil, err + // customOpts is true for the second pass, which is also when we want to validate feature usage. + doValidation := customOpts + if doValidation { + validateRequiredFields := !interp.lenient + err := interp.validateRecursive(validateRequiredFields, msg, "", element, nil, false, false, false) + if err != nil { + return nil, err + } } if interp.lenient { @@ -637,6 +700,13 @@ func (interp *interpreter) interpretOptions( // the work we've done so far. return uninterpreted, nil } + if doValidation { + if err := proto.CheckInitialized(optsClone); err != nil { + // Conversion from dynamic message failed to set some required fields. + // TODO above applies here as well... + return uninterpreted, nil + } + } // conversion from dynamic message above worked, so now // it is safe to overwrite the passed in message proto.Reset(opts) @@ -645,202 +715,450 @@ func (interp *interpreter) interpretOptions( return remain, nil } - if err := validateRecursive(msg, ""); err != nil { - node := interp.file.Node(element) - if err := interp.reporter.HandleErrorf(interp.nodeInfo(node), "error in %s options: %v", descriptorType(element), err); err != nil { - return nil, err - } - } - // now try to convert into the passed in message and fail if not successful if err := cloneInto(opts, msg.Interface(), interp.resolver); err != nil { node := interp.file.Node(element) - return nil, interp.reporter.HandleError(reporter.Error(interp.nodeInfo(node), err)) + return nil, interp.handleError(reporter.Error(interp.nodeInfo(node), err)) } return remain, nil } -func (interp *interpreter) validateFeatures( +// checkFieldUsage verifies that the given option field can be used +// for the given target type. It reports an error if not and returns +// a non-nil error if the handler returned a non-nil error. +func (interp *interpreter) checkFieldUsage( targetType descriptorpb.FieldOptions_OptionTargetType, - opts protoreflect.Message, - features []*ast.OptionNode, + fld protoreflect.FieldDescriptor, + node ast.Node, ) error { - fld := opts.Descriptor().Fields().ByName(featuresFieldName) - if fld == nil { - // no features to resolve - return nil - } - if fld.IsList() || fld.Message() == nil || fld.Message().FullName() != featureSetName { - // features field doesn't look right... abort - // TODO: should this return an error? - return nil - } - featureSet := opts.Get(fld).Message() - var err error - featureSet.Range(func(featureField protoreflect.FieldDescriptor, _ protoreflect.Value) bool { - opts, ok := featureField.Options().(*descriptorpb.FieldOptions) - if !ok { - return true - } - targetTypes := opts.GetTargets() - var allowed bool - for _, allowedType := range targetTypes { - if allowedType == targetType { - allowed = true - break - } - } - if !allowed { - allowedTypes := make([]string, len(targetTypes)) - for i, t := range opts.Targets { - allowedTypes[i] = targetTypeString(t) - } - pos := interp.positionOfFeature(features, featuresFieldName, featureField.Name()) - if len(opts.Targets) == 1 && opts.Targets[0] == descriptorpb.FieldOptions_TARGET_TYPE_UNKNOWN { - err = interp.reporter.HandleErrorf(pos, "feature field %q may not be used explicitly", featureField.Name()) - } else { - err = interp.reporter.HandleErrorf(pos, "feature %q is allowed on [%s], not on %s", featureField.Name(), strings.Join(allowedTypes, ","), targetTypeString(targetType)) - } - } - return err == nil - }) - return err -} - -func (interp *interpreter) positionOfFeature(features []*ast.OptionNode, fieldNames ...protoreflect.Name) ast.SourceSpan { - if interp.file.AST() == nil { - return ast.UnknownSpan(interp.file.FileDescriptorProto().GetName()) - } - for _, feature := range features { - matched, remainingNames, nodePos, nodeValue := matchInterpretedOption(feature, fieldNames) - if !matched { - continue - } - if len(remainingNames) > 0 { - nodePos = findInterpretedFieldForFeature(nodePos, nodeValue, remainingNames) - } - if nodePos != nil { - return interp.file.FileNode().NodeInfo(nodePos) - } - } - return ast.UnknownSpan(interp.file.FileDescriptorProto().GetName()) -} - -func matchInterpretedOption(node *ast.OptionNode, path []protoreflect.Name) (bool, []protoreflect.Name, ast.Node, ast.ValueNode) { - for i := 0; i < len(path) && i < len(node.Name.Parts); i++ { - part := node.Name.Parts[i] - if !part.IsExtension() && protoreflect.Name(part.Name.AsIdentifier()) != path[i] { - return false, nil, nil, nil + msgOpts, _ := fld.ContainingMessage().Options().(*descriptorpb.MessageOptions) + if msgOpts.GetMessageSetWireFormat() && !messageset.CanSupportMessageSets() { + err := interp.handleErrorf(interp.nodeInfo(node), "field %q may not be used in an option: it uses 'message set wire format' legacy proto1 feature which is not supported", fld.FullName()) + if err != nil { + return err } } - if len(path) <= len(node.Name.Parts) { - // No more path elements to match. Report location - // of the final element of path inside option name. - return true, nil, node.Name.Parts[len(path)-1], node.Val - } - return true, path[len(node.Name.Parts):], node.Name.Parts[len(node.Name.Parts)-1], node.Val -} -func findInterpretedFieldForFeature(nodePos ast.Node, nodeValue ast.ValueNode, path []protoreflect.Name) ast.Node { - if len(path) == 0 { - return nodePos - } - msgNode, ok := nodeValue.(*ast.MessageLiteralNode) + opts, ok := fld.Options().(*descriptorpb.FieldOptions) if !ok { return nil } - for _, fldNode := range msgNode.Elements { - if fldNode.Name.Open == nil && protoreflect.Name(fldNode.Name.Name.AsIdentifier()) == path[0] { - if res := findInterpretedFieldForFeature(fldNode.Name, fldNode.Val, path[1:]); res != nil { - return res - } + targetTypes := opts.GetTargets() + if len(targetTypes) == 0 { + return nil + } + for _, allowedType := range targetTypes { + if allowedType == targetType { + return nil } } - return nil + allowedTypes := make([]string, len(targetTypes)) + for i, t := range targetTypes { + allowedTypes[i] = targetTypeString(t) + } + if len(targetTypes) == 1 && targetTypes[0] == descriptorpb.FieldOptions_TARGET_TYPE_UNKNOWN { + return interp.handleErrorf(interp.nodeInfo(node), "field %q may not be used in an option (it declares no allowed target types)", fld.FullName()) + } + return interp.handleErrorf(interp.nodeInfo(node), "field %q is allowed on [%s], not on %s", fld.FullName(), strings.Join(allowedTypes, ","), targetTypeString(targetType)) } func targetTypeString(t descriptorpb.FieldOptions_OptionTargetType) string { return strings.ToLower(strings.ReplaceAll(strings.TrimPrefix(t.String(), "TARGET_TYPE_"), "_", " ")) } +func editionString(t descriptorpb.Edition) string { + return strings.ToLower(strings.ReplaceAll(strings.TrimPrefix(t.String(), "EDITION_"), "_", "-")) +} + func cloneInto(dest proto.Message, src proto.Message, res linker.Resolver) error { if dest.ProtoReflect().Descriptor() == src.ProtoReflect().Descriptor() { proto.Reset(dest) proto.Merge(dest, src) - return proto.CheckInitialized(dest) + return nil } // If descriptors are not the same, we could have field descriptors in src that // don't match the ones in dest. There's no easy/sane way to handle that. So we // just marshal to bytes and back to do this - data, err := proto.Marshal(src) + marshaler := proto.MarshalOptions{ + // We've already validated required fields before this point, + // so we can allow partial here. + AllowPartial: true, + } + data, err := marshaler.Marshal(src) if err != nil { return err } - return proto.UnmarshalOptions{Resolver: res}.Unmarshal(data, dest) + unmarshaler := proto.UnmarshalOptions{AllowPartial: true} + if res != nil { + unmarshaler.Resolver = res + } else { + // Use a typed nil, which returns "not found" to all queries + // and prevents fallback to protoregistry.GlobalTypes. + unmarshaler.Resolver = (*protoregistry.Types)(nil) + } + return unmarshaler.Unmarshal(data, dest) } -func validateRecursive(msg protoreflect.Message, prefix string) error { - flds := msg.Descriptor().Fields() - var missingFields []string - for i := 0; i < flds.Len(); i++ { - fld := flds.Get(i) - if fld.Cardinality() == protoreflect.Required && !msg.Has(fld) { - missingFields = append(missingFields, fmt.Sprintf("%s%s", prefix, fld.Name())) +func (interp *interpreter) validateRecursive( + validateRequiredFields bool, + msg protoreflect.Message, + prefix string, + element proto.Message, + path []int32, + isFeatures bool, + inFeatures bool, + inMap bool, +) error { + if validateRequiredFields { + flds := msg.Descriptor().Fields() + var missingFields []string + for i := 0; i < flds.Len(); i++ { + fld := flds.Get(i) + if fld.Cardinality() == protoreflect.Required && !msg.Has(fld) { + missingFields = append(missingFields, fmt.Sprintf("%s%s", prefix, fld.Name())) + } + } + if len(missingFields) > 0 { + node := interp.findOptionNode(path, element) + err := interp.handleErrorf(interp.nodeInfo(node), "error in %s options: some required fields missing: %v", descriptorType(element), strings.Join(missingFields, ", ")) + if err != nil { + return err + } } - } - if len(missingFields) > 0 { - return fmt.Errorf("some required fields missing: %v", strings.Join(missingFields, ", ")) } var err error msg.Range(func(fld protoreflect.FieldDescriptor, val protoreflect.Value) bool { - if fld.IsMap() { - md := fld.MapValue().Message() - if md != nil { - val.Map().Range(func(k protoreflect.MapKey, v protoreflect.Value) bool { - chprefix := fmt.Sprintf("%s%s[%v].", prefix, fieldName(fld), k) - err = validateRecursive(v.Message(), chprefix) - return err == nil - }) + chpath := path + if !inMap { + chpath = append(chpath, int32(fld.Number())) + } + chInFeatures := isFeatures || inFeatures + chIsFeatures := !chInFeatures && len(path) == 0 && fld.Name() == "features" + + if (isFeatures || (inFeatures && fld.IsExtension())) && + interp.file.FileNode().Name() == fld.ParentFile().Path() { + var what, name string + if fld.IsExtension() { + what = "custom feature" + name = "(" + string(fld.FullName()) + ")" + } else { + what = "feature" + name = string(fld.Name()) + } + node := interp.findOptionNode(path, element) + err = interp.handleErrorf(interp.nodeInfo(node), "%s %s cannot be used from the same file in which it is defined", what, name) + if err != nil { + return false + } + } + + if chInFeatures { + // Validate feature usage against feature settings. + + // First, check the feature support settings of the field. + opts, _ := fld.Options().(*descriptorpb.FieldOptions) + edition := interp.file.FileDescriptorProto().GetEdition() + if opts != nil && opts.FeatureSupport != nil { + err = interp.validateFeatureSupport(edition, opts.FeatureSupport, "field", string(fld.FullName()), chpath, element) if err != nil { return false } } - } else { - md := fld.Message() - if md != nil { - if fld.IsList() { + // Then, if it's an enum or has an enum, check the feature support settings of the enum values. + var enum protoreflect.EnumDescriptor + if fld.Enum() != nil { + enum = fld.Enum() + } else if fld.IsMap() && fld.MapValue().Enum() != nil { + enum = fld.MapValue().Enum() + } + if enum != nil { + switch { + case fld.IsMap(): + val.Map().Range(func(_ protoreflect.MapKey, v protoreflect.Value) bool { + // Can't construct path to particular map entry since we don't this entry's index. + // So we leave chpath alone, and it will have to point to the whole map value (or + // the first entry if the map is de-structured across multiple option statements). + err = interp.validateEnumValueFeatureSupport(edition, enum, v.Enum(), chpath, element) + return err == nil + }) + if err != nil { + return false + } + case fld.IsList(): sl := val.List() for i := 0; i < sl.Len(); i++ { v := sl.Get(i) - chprefix := fmt.Sprintf("%s%s[%d].", prefix, fieldName(fld), i) - err = validateRecursive(v.Message(), chprefix) + err = interp.validateEnumValueFeatureSupport(edition, enum, v.Enum(), append(chpath, int32(i)), element) if err != nil { return false } } - } else { - chprefix := fmt.Sprintf("%s%s.", prefix, fieldName(fld)) - err = validateRecursive(val.Message(), chprefix) + default: + err = interp.validateEnumValueFeatureSupport(edition, enum, val.Enum(), chpath, element) if err != nil { return false } } } } + + // If it's a message or contains a message, recursively validate fields in those messages. + switch { + case fld.IsMap() && fld.MapValue().Message() != nil: + val.Map().Range(func(k protoreflect.MapKey, v protoreflect.Value) bool { + chprefix := fmt.Sprintf("%s%s[%v].", prefix, fieldName(fld), k) + err = interp.validateRecursive(validateRequiredFields, v.Message(), chprefix, element, chpath, chIsFeatures, chInFeatures, true) + return err == nil + }) + if err != nil { + return false + } + case fld.IsList() && fld.Message() != nil: + sl := val.List() + for i := 0; i < sl.Len(); i++ { + v := sl.Get(i) + chprefix := fmt.Sprintf("%s%s[%d].", prefix, fieldName(fld), i) + if !inMap { + chpath = append(chpath, int32(i)) + } + err = interp.validateRecursive(validateRequiredFields, v.Message(), chprefix, element, chpath, chIsFeatures, chInFeatures, inMap) + if err != nil { + return false + } + } + case !fld.IsMap() && fld.Message() != nil: + chprefix := fmt.Sprintf("%s%s.", prefix, fieldName(fld)) + err = interp.validateRecursive(validateRequiredFields, val.Message(), chprefix, element, chpath, chIsFeatures, chInFeatures, inMap) + if err != nil { + return false + } + } return true }) return err } +func (interp *interpreter) validateEnumValueFeatureSupport( + edition descriptorpb.Edition, + enum protoreflect.EnumDescriptor, + number protoreflect.EnumNumber, + path []int32, + element proto.Message, +) error { + enumVal := enum.Values().ByNumber(number) + if enumVal == nil { + return nil + } + enumValOpts, _ := enumVal.Options().(*descriptorpb.EnumValueOptions) + if enumValOpts == nil || enumValOpts.FeatureSupport == nil { + return nil + } + return interp.validateFeatureSupport(edition, enumValOpts.FeatureSupport, "enum value", string(enumVal.Name()), path, element) +} + +func (interp *interpreter) validateFeatureSupport( + edition descriptorpb.Edition, + featureSupport *descriptorpb.FieldOptions_FeatureSupport, + what string, + name string, + path []int32, + element proto.Message, +) error { + if featureSupport.EditionIntroduced != nil && edition < featureSupport.GetEditionIntroduced() { + node := interp.findOptionNode(path, element) + err := interp.handleErrorf(interp.nodeInfo(node), "%s %q was not introduced until edition %s", what, name, editionString(featureSupport.GetEditionIntroduced())) + if err != nil { + return err + } + } + if featureSupport.EditionRemoved != nil && edition >= featureSupport.GetEditionRemoved() { + node := interp.findOptionNode(path, element) + err := interp.handleErrorf(interp.nodeInfo(node), "%s %q was removed in edition %s", what, name, editionString(featureSupport.GetEditionRemoved())) + if err != nil { + return err + } + } + if featureSupport.EditionDeprecated != nil && edition >= featureSupport.GetEditionDeprecated() { + node := interp.findOptionNode(path, element) + var suffix string + if featureSupport.GetDeprecationWarning() != "" { + suffix = ": " + featureSupport.GetDeprecationWarning() + } + interp.reporter.HandleWarningf(interp.nodeInfo(node), "%s %q is deprecated as of edition %s%s", what, name, editionString(featureSupport.GetEditionDeprecated()), suffix) + } + return nil +} + +func (interp *interpreter) findOptionNode( + path []int32, + element proto.Message, +) ast.Node { + elementNode := interp.file.Node(element) + nodeWithOpts, _ := elementNode.(ast.NodeWithOptions) + if nodeWithOpts == nil { + return elementNode + } + node, _ := findOptionNode[*ast.OptionNode]( + path, + optionsRanger{nodeWithOpts}, + func(n *ast.OptionNode) *sourceinfo.OptionSourceInfo { + return interp.index[n] + }, + ) + if node != nil { + return node + } + return elementNode +} + +func findOptionNode[N ast.Node]( + path []int32, + nodes interface { + Range(func(N, ast.ValueNode) bool) + }, + srcInfoAccessor func(N) *sourceinfo.OptionSourceInfo, +) (ast.Node, int) { + var bestMatch ast.Node + var bestMatchLen int + nodes.Range(func(node N, val ast.ValueNode) bool { + srcInfo := srcInfoAccessor(node) + if srcInfo == nil { + // can happen if we are lenient when interpreting -- this node + // could not be interpreted and thus has no source info; skip + return true + } + if srcInfo.Path[0] < 0 { + // negative first value means it's a field pseudo-option; skip + return true + } + match, matchLen := findOptionValueNode(path, node, val, srcInfo) + if matchLen > bestMatchLen { + bestMatch = match + bestMatchLen = matchLen + if matchLen >= len(path) { + // not going to find a better one + return false + } + } + return true + }) + return bestMatch, bestMatchLen +} + +type optionsRanger struct { + node ast.NodeWithOptions +} + +func (r optionsRanger) Range(f func(*ast.OptionNode, ast.ValueNode) bool) { + r.node.RangeOptions(func(optNode *ast.OptionNode) bool { + return f(optNode, optNode.Val) + }) +} + +type valueRanger []ast.ValueNode + +func (r valueRanger) Range(f func(ast.ValueNode, ast.ValueNode) bool) { + for _, elem := range r { + if !f(elem, elem) { + return + } + } +} + +type fieldRanger map[*ast.MessageFieldNode]*sourceinfo.OptionSourceInfo + +func (r fieldRanger) Range(f func(*ast.MessageFieldNode, ast.ValueNode) bool) { + for elem := range r { + if !f(elem, elem.Val) { + return + } + } +} + +func isPathMatch(a, b []int32) bool { + length := len(a) + if len(b) < length { + length = len(b) + } + for i := 0; i < length; i++ { + if a[i] != b[i] { + return false + } + } + return true +} + +func findOptionValueNode( + path []int32, + node ast.Node, + value ast.ValueNode, + srcInfo *sourceinfo.OptionSourceInfo, +) (ast.Node, int) { + srcInfoPath := srcInfo.Path + if _, ok := srcInfo.Children.(*sourceinfo.ArrayLiteralSourceInfo); ok { + // Last path element for array source info is the index of the + // first element. So exclude in the comparison, since path could + // indicate a later index, which is present in the array. + srcInfoPath = srcInfo.Path[:len(srcInfo.Path)-1] + } + + if !isPathMatch(path, srcInfoPath) { + return nil, 0 + } + if len(srcInfoPath) >= len(path) { + return node, len(path) + } + + switch children := srcInfo.Children.(type) { + case *sourceinfo.ArrayLiteralSourceInfo: + array, ok := value.(*ast.ArrayLiteralNode) + if !ok { + break // should never happen + } + var i int + match, matchLen := findOptionNode[ast.ValueNode]( + path, + valueRanger(array.Elements), + func(_ ast.ValueNode) *sourceinfo.OptionSourceInfo { + val := &children.Elements[i] + i++ + return val + }, + ) + if match != nil { + return match, matchLen + } + + case *sourceinfo.MessageLiteralSourceInfo: + match, matchLen := findOptionNode[*ast.MessageFieldNode]( + path, + fieldRanger(children.Fields), + func(n *ast.MessageFieldNode) *sourceinfo.OptionSourceInfo { + return children.Fields[n] + }, + ) + if match != nil { + return match, matchLen + } + } + + return node, len(srcInfoPath) +} + // interpretField interprets the option described by opt, as a field inside the given msg. This // interprets components of the option name starting at nameIndex. When nameIndex == 0, then // msg must be an options message. For nameIndex > 0, msg is a nested message inside of the // options message. The given pathPrefix is the path (sequence of field numbers and indices // with a FileDescriptorProto as the start) up to but not including the given nameIndex. +// +// Any errors encountered will be handled, so the returned error will only be non-nil if +// the handler returned non-nil. Callers must check that the source info is non-nil before +// using it since it can be nil (in the event of a problem) even if the error is nil. func (interp *interpreter) interpretField( + targetType descriptorpb.FieldOptions_OptionTargetType, mc *internal.MessageContext, msg protoreflect.Message, opt *descriptorpb.UninterpretedOption, @@ -858,38 +1176,42 @@ func (interp *interpreter) interpretField( var err error fld, err = interp.resolveExtensionType(extName) if errors.Is(err, protoregistry.NotFound) { - return nil, interp.reporter.HandleErrorf(interp.nodeInfo(node), + return nil, interp.handleErrorf(interp.nodeInfo(node), "%vunrecognized extension %s of %s", mc, extName, msg.Descriptor().FullName()) } else if err != nil { - return nil, interp.reporter.HandleErrorWithPos(interp.nodeInfo(node), err) + return nil, interp.handleErrorWithPos(interp.nodeInfo(node), err) } if fld.ContainingMessage().FullName() != msg.Descriptor().FullName() { - return nil, interp.reporter.HandleErrorf(interp.nodeInfo(node), + return nil, interp.handleErrorf(interp.nodeInfo(node), "%vextension %s should extend %s but instead extends %s", mc, extName, msg.Descriptor().FullName(), fld.ContainingMessage().FullName()) } } else { fld = msg.Descriptor().Fields().ByName(protoreflect.Name(nm.GetNamePart())) if fld == nil { - return nil, interp.reporter.HandleErrorf(interp.nodeInfo(node), + return nil, interp.handleErrorf(interp.nodeInfo(node), "%vfield %s of %s does not exist", mc, nm.GetNamePart(), msg.Descriptor().FullName()) } } pathPrefix = append(pathPrefix, int32(fld.Number())) + if err := interp.checkFieldUsage(targetType, fld, node); err != nil { + return nil, err + } + if len(opt.GetName()) > nameIndex+1 { nextnm := opt.GetName()[nameIndex+1] nextnode := interp.file.OptionNamePartNode(nextnm) k := fld.Kind() if k != protoreflect.MessageKind && k != protoreflect.GroupKind { - return nil, interp.reporter.HandleErrorf(interp.nodeInfo(nextnode), + return nil, interp.handleErrorf(interp.nodeInfo(nextnode), "%vcannot set field %s because %s is not a message", mc, nextnm.GetNamePart(), nm.GetNamePart()) } if fld.Cardinality() == protoreflect.Repeated { - return nil, interp.reporter.HandleErrorf(interp.nodeInfo(nextnode), + return nil, interp.handleErrorf(interp.nodeInfo(nextnode), "%vcannot set field %s because %s is repeated (must use an aggregate)", mc, nextnm.GetNamePart(), nm.GetNamePart()) } @@ -901,7 +1223,7 @@ func (interp *interpreter) interpretField( if ood := fld.ContainingOneof(); ood != nil { existingFld := msg.WhichOneof(ood) if existingFld != nil && existingFld.Number() != fld.Number() { - return nil, interp.reporter.HandleErrorf(interp.nodeInfo(node), + return nil, interp.handleErrorf(interp.nodeInfo(node), "%voneof %q already has field %q set", mc, ood.Name(), fieldName(existingFld)) } @@ -911,7 +1233,7 @@ func (interp *interpreter) interpretField( msg.Set(fld, fldVal) } // recurse to set next part of name - return interp.interpretField(mc, fdm, opt, nameIndex+1, pathPrefix) + return interp.interpretField(targetType, mc, fdm, opt, nameIndex+1, pathPrefix) } optNode := interp.file.OptionNode(opt) @@ -919,14 +1241,14 @@ func (interp *interpreter) interpretField( var srcInfo *sourceinfo.OptionSourceInfo var err error if optValNode.Value() == nil { - err = interp.setOptionFieldFromProto(mc, msg, fld, node, opt, optValNode) + err = interp.setOptionFieldFromProto(targetType, mc, msg, fld, node, opt, optValNode) srcInfoVal := newSrcInfo(pathPrefix, nil) srcInfo = &srcInfoVal } else { - srcInfo, err = interp.setOptionField(mc, msg, fld, node, optValNode, false, pathPrefix) + srcInfo, err = interp.setOptionField(targetType, mc, msg, fld, node, optValNode, false, pathPrefix) } if err != nil { - return nil, interp.reporter.HandleError(err) + return nil, err } return srcInfo, nil @@ -936,6 +1258,7 @@ func (interp *interpreter) interpretField( // by AST node val. The given name is the AST node that corresponds to the name of fld. On success, // it returns additional metadata about the field that was set. func (interp *interpreter) setOptionField( + targetType descriptorpb.FieldOptions_OptionTargetType, mc *internal.MessageContext, msg protoreflect.Message, fld protoreflect.FieldDescriptor, @@ -948,7 +1271,7 @@ func (interp *interpreter) setOptionField( if sl, ok := v.([]ast.ValueNode); ok { // handle slices a little differently than the others if fld.Cardinality() != protoreflect.Repeated { - return nil, reporter.Errorf(interp.nodeInfo(val), "%vvalue is an array but field is not repeated", mc) + return nil, interp.handleErrorf(interp.nodeInfo(val), "%vvalue is an array but field is not repeated", mc) } origPath := mc.OptAggPath defer func() { @@ -963,8 +1286,8 @@ func (interp *interpreter) setOptionField( } for index, item := range sl { mc.OptAggPath = fmt.Sprintf("%s[%d]", origPath, index) - value, srcInfo, err := interp.fieldValue(mc, msg, fld, item, insideMsgLiteral, append(pathPrefix, int32(firstIndex+index))) - if err != nil { + value, srcInfo, err := interp.fieldValue(targetType, mc, msg, fld, item, insideMsgLiteral, append(pathPrefix, int32(firstIndex+index))) + if err != nil || !value.IsValid() { return nil, err } if fld.IsMap() { @@ -986,15 +1309,15 @@ func (interp *interpreter) setOptionField( pathPrefix = append(pathPrefix, int32(msg.Get(fld).List().Len())) } - value, srcInfo, err := interp.fieldValue(mc, msg, fld, val, insideMsgLiteral, pathPrefix) - if err != nil { + value, srcInfo, err := interp.fieldValue(targetType, mc, msg, fld, val, insideMsgLiteral, pathPrefix) + if err != nil || !value.IsValid() { return nil, err } if ood := fld.ContainingOneof(); ood != nil { existingFld := msg.WhichOneof(ood) if existingFld != nil && existingFld.Number() != fld.Number() { - return nil, reporter.Errorf(interp.nodeInfo(name), "%voneof %q already has field %q set", mc, ood.Name(), fieldName(existingFld)) + return nil, interp.handleErrorf(interp.nodeInfo(name), "%voneof %q already has field %q set", mc, ood.Name(), fieldName(existingFld)) } } @@ -1007,7 +1330,7 @@ func (interp *interpreter) setOptionField( lv.Append(value) default: if msg.Has(fld) { - return nil, reporter.Errorf(interp.nodeInfo(name), "%vnon-repeated option field %s already set", mc, fieldName(fld)) + return nil, interp.handleErrorf(interp.nodeInfo(name), "%vnon-repeated option field %s already set", mc, fieldName(fld)) } msg.Set(fld, value) } @@ -1019,6 +1342,7 @@ func (interp *interpreter) setOptionField( // to report source positions in error messages. On success, it returns additional metadata // about the field that was set. func (interp *interpreter) setOptionFieldFromProto( + targetType descriptorpb.FieldOptions_OptionTargetType, mc *internal.MessageContext, msg protoreflect.Message, fld protoreflect.FieldDescriptor, @@ -1032,13 +1356,13 @@ func (interp *interpreter) setOptionFieldFromProto( case protoreflect.EnumKind: num, _, err := interp.enumFieldValueFromProto(mc, fld.Enum(), opt, node) if err != nil { - return err + return interp.handleError(err) } value = protoreflect.ValueOfEnum(num) case protoreflect.MessageKind, protoreflect.GroupKind: if opt.AggregateValue == nil { - return reporter.Errorf(interp.nodeInfo(node), "%vexpecting message, got %s", mc, optionValueKind(opt)) + return interp.handleErrorf(interp.nodeInfo(node), "%vexpecting message, got %s", mc, optionValueKind(opt)) } // We must parse the text format from the aggregate value string var elem protoreflect.Message @@ -1055,14 +1379,17 @@ func (interp *interpreter) setOptionFieldFromProto( AllowPartial: true, }.Unmarshal([]byte(opt.GetAggregateValue()), elem.Interface()) if err != nil { - return reporter.Errorf(interp.nodeInfo(node), "%vfailed to parse message literal %w", mc, err) + return interp.handleErrorf(interp.nodeInfo(node), "%vfailed to parse message literal %w", mc, err) + } + if err := interp.checkFieldUsagesInMessage(targetType, elem, node); err != nil { + return err } value = protoreflect.ValueOfMessage(elem) default: v, err := interp.scalarFieldValueFromProto(mc, descriptorpb.FieldDescriptorProto_Type(k), opt, node) if err != nil { - return err + return interp.handleError(err) } value = protoreflect.ValueOf(v) } @@ -1070,7 +1397,7 @@ func (interp *interpreter) setOptionFieldFromProto( if ood := fld.ContainingOneof(); ood != nil { existingFld := msg.WhichOneof(ood) if existingFld != nil && existingFld.Number() != fld.Number() { - return reporter.Errorf(interp.nodeInfo(name), "%voneof %q already has field %q set", mc, ood.Name(), fieldName(existingFld)) + return interp.handleErrorf(interp.nodeInfo(name), "%voneof %q already has field %q set", mc, ood.Name(), fieldName(existingFld)) } } @@ -1082,13 +1409,66 @@ func (interp *interpreter) setOptionFieldFromProto( msg.Mutable(fld).List().Append(value) default: if msg.Has(fld) { - return reporter.Errorf(interp.nodeInfo(name), "%vnon-repeated option field %s already set", mc, fieldName(fld)) + return interp.handleErrorf(interp.nodeInfo(name), "%vnon-repeated option field %s already set", mc, fieldName(fld)) } msg.Set(fld, value) } return nil } +// checkFieldUsagesInMessage verifies that all fields present in the given +// message can be used for the given target type. When an AST is +// present, we validate each field as it is processed. But without +// an AST, we unmarshal a message from an uninterpreted option's +// aggregate value string, and then must make sure that all fields +// set in that message are valid. This reports an error for each +// invalid field it encounters and returns a non-nil error if/when +// the handler returns a non-nil error. +func (interp *interpreter) checkFieldUsagesInMessage( + targetType descriptorpb.FieldOptions_OptionTargetType, + msg protoreflect.Message, + node ast.Node, +) error { + var err error + msg.Range(func(fld protoreflect.FieldDescriptor, val protoreflect.Value) bool { + err = interp.checkFieldUsage(targetType, fld, node) + if err != nil { + return false + } + switch { + case fld.IsList() && fld.Message() != nil: + listVal := val.List() + for i, length := 0, listVal.Len(); i < length; i++ { + err = interp.checkFieldUsagesInMessage(targetType, listVal.Get(i).Message(), node) + if err != nil { + return false + } + } + case fld.IsMap() && fld.MapValue().Message() != nil: + mapVal := val.Map() + mapVal.Range(func(_ protoreflect.MapKey, val protoreflect.Value) bool { + err = interp.checkFieldUsagesInMessage(targetType, val.Message(), node) + return err == nil + }) + case !fld.IsMap() && fld.Message() != nil: + err = interp.checkFieldUsagesInMessage(targetType, val.Message(), node) + } + return err == nil + }) + return err +} + +func (interp *interpreter) enableLenience(enable bool) { + if !interp.lenient { + return // nothing to do + } + if enable { + // reset the flag that tracks if an error has been reported + interp.lenientErrReported = false + } + interp.lenienceEnabled = enable +} + func setMapEntry( fld protoreflect.FieldDescriptor, msg protoreflect.Message, @@ -1130,6 +1510,9 @@ type msgLiteralResolver struct { } func (r *msgLiteralResolver) FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error) { + if r.interp.resolver == nil { + return nil, protoregistry.NotFound + } return r.interp.resolver.FindMessageByName(message) } @@ -1147,6 +1530,9 @@ func (r *msgLiteralResolver) FindMessageByURL(url string) (protoreflect.MessageT } func (r *msgLiteralResolver) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) { + if r.interp.resolver == nil { + return nil, protoregistry.NotFound + } // In a message literal, extension name may be partially qualified, relative to package. // So we have to search through package scopes. pkg := r.pkg @@ -1168,6 +1554,9 @@ func (r *msgLiteralResolver) FindExtensionByName(field protoreflect.FullName) (p } func (r *msgLiteralResolver) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) { + if r.interp.resolver == nil { + return nil, protoregistry.NotFound + } return r.interp.resolver.FindExtensionByNumber(message, field) } @@ -1226,7 +1615,12 @@ func optionValueKind(opt *descriptorpb.UninterpretedOption) string { // fieldValue computes a compile-time value (constant or list or message literal) for the given // AST node val. The value in val must be assignable to the field fld. +// +// If the returned value is not valid, then an error occurred during processing. +// The returned err may be nil, however, as any errors will already have been +// handled (so the resulting error could be nil if the handler returned nil). func (interp *interpreter) fieldValue( + targetType descriptorpb.FieldOptions_OptionTargetType, mc *internal.MessageContext, msg protoreflect.Message, fld protoreflect.FieldDescriptor, @@ -1239,7 +1633,7 @@ func (interp *interpreter) fieldValue( case protoreflect.EnumKind: num, _, err := interp.enumFieldValue(mc, fld.Enum(), val, insideMsgLiteral) if err != nil { - return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, interp.handleError(err) } return protoreflect.ValueOfEnum(num), newSrcInfo(pathPrefix, nil), nil @@ -1259,15 +1653,15 @@ func (interp *interpreter) fieldValue( // Normal message field childMsg = msg.NewField(fld).Message() } - return interp.messageLiteralValue(mc, aggs, childMsg, pathPrefix) + return interp.messageLiteralValue(targetType, mc, aggs, childMsg, pathPrefix) } return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, - reporter.Errorf(interp.nodeInfo(val), "%vexpecting message, got %s", mc, valueKind(v)) + interp.handleErrorf(interp.nodeInfo(val), "%vexpecting message, got %s", mc, valueKind(v)) default: v, err := interp.scalarFieldValue(mc, descriptorpb.FieldDescriptorProto_Type(k), val, insideMsgLiteral) if err != nil { - return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, interp.handleError(err) } return protoreflect.ValueOf(v), newSrcInfo(pathPrefix, nil), nil } @@ -1639,7 +2033,13 @@ func descriptorType(m proto.Message) string { } } +// messageLiteralValue processes a message literal value. +// +// If the returned value is not valid, then an error occurred during processing. +// The returned err may be nil, however, as any errors will already have been +// handled (so the resulting error could be nil if the handler returned nil). func (interp *interpreter) messageLiteralValue( + targetType descriptorpb.FieldOptions_OptionTargetType, mc *internal.MessageContext, fieldNodes []*ast.MessageFieldNode, msg protoreflect.Message, @@ -1651,6 +2051,7 @@ func (interp *interpreter) messageLiteralValue( mc.OptAggPath = origPath }() flds := make(map[*ast.MessageFieldNode]*sourceinfo.OptionSourceInfo, len(fieldNodes)) + var hadError bool for _, fieldNode := range fieldNodes { if origPath == "" { mc.OptAggPath = fieldNode.Name.Value() @@ -1659,13 +2060,57 @@ func (interp *interpreter) messageLiteralValue( } if fieldNode.Name.IsAnyTypeReference() { if len(fieldNodes) > 1 { - return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, - reporter.Errorf(interp.nodeInfo(fieldNode.Name.URLPrefix), "%vany type references cannot be repeated or mixed with other fields", mc) + err := interp.handleErrorf(interp.nodeInfo(fieldNode.Name.URLPrefix), "%vany type references cannot be repeated or mixed with other fields", mc) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + hadError = true } + if fmd.FullName() != "google.protobuf.Any" { - return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, - reporter.Errorf(interp.nodeInfo(fieldNode.Name.URLPrefix), "%vtype references are only allowed for google.protobuf.Any, but this type is %s", mc, fmd.FullName()) + err := interp.handleErrorf(interp.nodeInfo(fieldNode.Name.URLPrefix), "%vtype references are only allowed for google.protobuf.Any, but this type is %s", mc, fmd.FullName()) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + hadError = true + continue + } + typeURLDescriptor := fmd.Fields().ByNumber(internal.AnyTypeURLTag) + var err error + switch { + case typeURLDescriptor == nil: + err = fmt.Errorf("message schema is missing type_url field (number %d)", internal.AnyTypeURLTag) + case typeURLDescriptor.IsList(): + err = fmt.Errorf("message schema has type_url field (number %d) that is a list but should be singular", internal.AnyTypeURLTag) + case typeURLDescriptor.Kind() != protoreflect.StringKind: + err = fmt.Errorf("message schema has type_url field (number %d) that is %s but should be string", internal.AnyTypeURLTag, typeURLDescriptor.Kind()) + } + if err != nil { + err := interp.handleErrorf(interp.nodeInfo(fieldNode.Name), "%v%w", mc, err) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + hadError = true + continue + } + valueDescriptor := fmd.Fields().ByNumber(internal.AnyValueTag) + switch { + case valueDescriptor == nil: + err = fmt.Errorf("message schema is missing value field (number %d)", internal.AnyValueTag) + case valueDescriptor.IsList(): + err = fmt.Errorf("message schema has value field (number %d) that is a list but should be singular", internal.AnyValueTag) + case valueDescriptor.Kind() != protoreflect.BytesKind: + err = fmt.Errorf("message schema has value field (number %d) that is %s but should be bytes", internal.AnyValueTag, valueDescriptor.Kind()) + } + if err != nil { + err := interp.handleErrorf(interp.nodeInfo(fieldNode.Name), "%v%w", mc, err) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + hadError = true + continue } + urlPrefix := fieldNode.Name.URLPrefix.AsIdentifier() msgName := fieldNode.Name.Name.AsIdentifier() fullURL := fmt.Sprintf("%s/%s", urlPrefix, msgName) @@ -1677,112 +2122,138 @@ func (interp *interpreter) messageLiteralValue( // file's transitive closure to find the named message, since that // is what protoc does. if urlPrefix != "type.googleapis.com" && urlPrefix != "type.googleprod.com" { - return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, - reporter.Errorf(interp.nodeInfo(fieldNode.Name.URLPrefix), "%vcould not resolve type reference %s", mc, fullURL) + err := interp.handleErrorf(interp.nodeInfo(fieldNode.Name.URLPrefix), "%vcould not resolve type reference %s", mc, fullURL) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + hadError = true + continue } anyFields, ok := fieldNode.Val.Value().([]*ast.MessageFieldNode) if !ok { - return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, - reporter.Errorf(interp.nodeInfo(fieldNode.Val), "%vtype references for google.protobuf.Any must have message literal value", mc) + err := interp.handleErrorf(interp.nodeInfo(fieldNode.Val), "%vtype references for google.protobuf.Any must have message literal value", mc) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + hadError = true + continue } anyMd := resolveDescriptor[protoreflect.MessageDescriptor](interp.resolver, string(msgName)) if anyMd == nil { - return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, - reporter.Errorf(interp.nodeInfo(fieldNode.Name.URLPrefix), "%vcould not resolve type reference %s", mc, fullURL) + err := interp.handleErrorf(interp.nodeInfo(fieldNode.Name.URLPrefix), "%vcould not resolve type reference %s", mc, fullURL) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + hadError = true + continue } // parse the message value - msgVal, valueSrcInfo, err := interp.messageLiteralValue(mc, anyFields, dynamicpb.NewMessage(anyMd), append(pathPrefix, internal.AnyValueTag)) + msgVal, valueSrcInfo, err := interp.messageLiteralValue(targetType, mc, anyFields, dynamicpb.NewMessage(anyMd), append(pathPrefix, internal.AnyValueTag)) if err != nil { return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } else if !msgVal.IsValid() { + hadError = true + continue } - typeURLDescriptor := fmd.Fields().ByNumber(internal.AnyTypeURLTag) - if typeURLDescriptor == nil || typeURLDescriptor.Kind() != protoreflect.StringKind { - return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, - reporter.Errorf(interp.nodeInfo(fieldNode.Name), "%vfailed to set type_url string field on Any: %w", mc, err) + b, err := (proto.MarshalOptions{Deterministic: true}).Marshal(msgVal.Message().Interface()) + if err != nil { + err := interp.handleErrorf(interp.nodeInfo(fieldNode.Val), "%vfailed to serialize message value: %w", mc, err) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + hadError = true + continue } - typeURLVal := protoreflect.ValueOfString(fullURL) - msg.Set(typeURLDescriptor, typeURLVal) - valueDescriptor := fmd.Fields().ByNumber(internal.AnyValueTag) - if valueDescriptor == nil || valueDescriptor.Kind() != protoreflect.BytesKind { - return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, - reporter.Errorf(interp.nodeInfo(fieldNode.Name), "%vfailed to set value bytes field on Any: %w", mc, err) + + // Success! + if !hadError { + msg.Set(typeURLDescriptor, protoreflect.ValueOfString(fullURL)) + msg.Set(valueDescriptor, protoreflect.ValueOfBytes(b)) + flds[fieldNode] = &valueSrcInfo } + continue + } - b, err := (proto.MarshalOptions{Deterministic: true}).Marshal(msgVal.Message().Interface()) - if err != nil { - return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, - reporter.Errorf(interp.nodeInfo(fieldNode.Val), "%vfailed to serialize message value: %w", mc, err) + // Not expanded Any syntax; handle normal field. + var ffld protoreflect.FieldDescriptor + var err error + if fieldNode.Name.IsExtension() { + n := interp.file.ResolveMessageLiteralExtensionName(fieldNode.Name.Name) + if n == "" { + // this should not be possible! + n = string(fieldNode.Name.Name.AsIdentifier()) } - msg.Set(valueDescriptor, protoreflect.ValueOfBytes(b)) - flds[fieldNode] = &valueSrcInfo - } else { - var ffld protoreflect.FieldDescriptor - var err error - if fieldNode.Name.IsExtension() { - n := interp.file.ResolveMessageLiteralExtensionName(fieldNode.Name.Name) - if n == "" { - // this should not be possible! - n = string(fieldNode.Name.Name.AsIdentifier()) - } - ffld, err = interp.resolveExtensionType(n) - if errors.Is(err, protoregistry.NotFound) { - // may need to qualify with package name - // (this should not be necessary!) - pkg := mc.File.FileDescriptorProto().GetPackage() - if pkg != "" { - ffld, err = interp.resolveExtensionType(pkg + "." + n) - } - } - } else { - ffld = fmd.Fields().ByName(protoreflect.Name(fieldNode.Name.Value())) - // Groups are indicated in the text format by the group name (which is - // camel-case), NOT the field name (which is lower-case). - // ...but only regular fields, not extensions that are groups... - if ffld != nil && ffld.Kind() == protoreflect.GroupKind && - string(ffld.Name()) == strings.ToLower(string(ffld.Message().Name())) && - ffld.Message().Name() != protoreflect.Name(fieldNode.Name.Value()) { - // This is kind of silly to fail here, but this mimics protoc behavior. - // We only fail when this really looks like a group since we need to be - // able to use the field name for fields in editions files that use the - // delimited message encoding and don't use proto2 group naming. - return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, - reporter.Errorf(interp.nodeInfo(fieldNode.Name), "%vfield %s not found (did you mean the group named %s?)", mc, fieldNode.Name.Value(), ffld.Message().Name()) + ffld, err = interp.resolveExtensionType(n) + if errors.Is(err, protoregistry.NotFound) { + // may need to qualify with package name + // (this should not be necessary!) + pkg := mc.File.FileDescriptorProto().GetPackage() + if pkg != "" { + ffld, err = interp.resolveExtensionType(pkg + "." + n) } - if ffld == nil { - err = protoregistry.NotFound - // could be a group name - for i := 0; i < fmd.Fields().Len(); i++ { - fd := fmd.Fields().Get(i) - if fd.Kind() == protoreflect.GroupKind && fd.Message().Name() == protoreflect.Name(fieldNode.Name.Value()) { - // found it! - ffld = fd - err = nil - break - } + } + } else { + ffld = fmd.Fields().ByName(protoreflect.Name(fieldNode.Name.Value())) + if ffld == nil { + err = protoregistry.NotFound + // It could be a proto2 group, where the text format refers to the group type + // name, and the field name is the lower-cased form of that. + ffld = fmd.Fields().ByName(protoreflect.Name(strings.ToLower(fieldNode.Name.Value()))) + if ffld != nil { + // In editions, we support using the group type name only for fields that + // "look like" proto2 groups. + if protoreflect.Name(fieldNode.Name.Value()) == ffld.Message().Name() && // text format uses type name + ffld.Message().FullName().Parent() == ffld.FullName().Parent() && // message and field declared in same scope + ffld.Kind() == protoreflect.GroupKind /* uses delimited encoding */ { + // This one looks like a proto2 group, so it's a keeper. + err = nil + } else { + // It doesn't look like a proto2 group, so this is not a match. + ffld = nil } } } - if errors.Is(err, protoregistry.NotFound) { - return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, - reporter.Errorf(interp.nodeInfo(fieldNode.Name), "%vfield %s not found", mc, string(fieldNode.Name.Name.AsIdentifier())) - } else if err != nil { - return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, - reporter.Error(interp.nodeInfo(fieldNode.Name), err) + } + if errors.Is(err, protoregistry.NotFound) { + err := interp.handleErrorf(interp.nodeInfo(fieldNode.Name), "%vfield %s not found", mc, string(fieldNode.Name.Name.AsIdentifier())) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err } - if fieldNode.Sep == nil && ffld.Message() == nil { - // If there is no separator, the field type should be a message. - // Otherwise, it is an error in the text format. - return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, - reporter.Errorf(interp.nodeInfo(fieldNode.Val), "syntax error: unexpected value, expecting ':'") + hadError = true + continue + } else if err != nil { + err := interp.handleErrorWithPos(interp.nodeInfo(fieldNode.Name), err) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err } - srcInfo, err := interp.setOptionField(mc, msg, ffld, fieldNode.Name, fieldNode.Val, true, append(pathPrefix, int32(ffld.Number()))) + hadError = true + continue + } + if err := interp.checkFieldUsage(targetType, ffld, fieldNode.Name); err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + if fieldNode.Sep == nil && ffld.Message() == nil { + // If there is no separator, the field type should be a message. + // Otherwise, it is an error in the text format. + err := interp.handleErrorf(interp.nodeInfo(fieldNode.Val), "syntax error: unexpected value, expecting ':'") if err != nil { return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err } + hadError = true + continue + } + srcInfo, err := interp.setOptionField(targetType, mc, msg, ffld, fieldNode.Name, fieldNode.Val, true, append(pathPrefix, int32(ffld.Number()))) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + if srcInfo != nil { flds[fieldNode] = srcInfo } } + if hadError { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, nil + } return protoreflect.ValueOfMessage(msg), newSrcInfo(pathPrefix, &sourceinfo.MessageLiteralSourceInfo{Fields: flds}), nil diff --git a/vendor/github.com/bufbuild/protocompile/options/source_retention_options.go b/vendor/github.com/bufbuild/protocompile/options/source_retention_options.go index f73f8c73aa6..05c3e292997 100644 --- a/vendor/github.com/bufbuild/protocompile/options/source_retention_options.go +++ b/vendor/github.com/bufbuild/protocompile/options/source_retention_options.go @@ -158,7 +158,7 @@ func stripSourceRetentionOptions[M proto.Message]( var hasFieldToStrip bool var numFieldsToKeep int var err error - optionsRef.Range(func(field protoreflect.FieldDescriptor, val protoreflect.Value) bool { + optionsRef.Range(func(field protoreflect.FieldDescriptor, _ protoreflect.Value) bool { fieldOpts, ok := field.Options().(*descriptorpb.FieldOptions) if !ok { err = fmt.Errorf("field options is unexpected type: got %T, want %T", field.Options(), fieldOpts) diff --git a/vendor/github.com/bufbuild/protocompile/parser/result.go b/vendor/github.com/bufbuild/protocompile/parser/result.go index 60c0c462342..4aa83e7d408 100644 --- a/vendor/github.com/bufbuild/protocompile/parser/result.go +++ b/vendor/github.com/bufbuild/protocompile/parser/result.go @@ -28,6 +28,7 @@ import ( "github.com/bufbuild/protocompile/ast" "github.com/bufbuild/protocompile/internal" + "github.com/bufbuild/protocompile/internal/editions" "github.com/bufbuild/protocompile/reporter" ) @@ -35,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 @@ -107,21 +109,15 @@ func (r *result) createFileDescriptor(filename string, file *ast.FileNode, handl fd.Syntax = proto.String(file.Syntax.Syntax.AsString()) } case file.Edition != nil: - if !internal.AllowEditions { - nodeInfo := file.NodeInfo(file.Edition.Edition) - if handler.HandleErrorf(nodeInfo, `editions are not yet supported; use syntax proto2 or proto3 instead`) != nil { - return - } - } edition := file.Edition.Edition.AsString() syntax = protoreflect.Editions fd.Syntax = proto.String("editions") - editionEnum, ok := internal.SupportedEditions[edition] + editionEnum, ok := editions.SupportedEditions[edition] if !ok { nodeInfo := file.NodeInfo(file.Edition.Edition) - editionStrs := make([]string, 0, len(internal.SupportedEditions)) - for supportedEdition := range internal.SupportedEditions { + editionStrs := make([]string, 0, len(editions.SupportedEditions)) + for supportedEdition := range editions.SupportedEditions { editionStrs = append(editionStrs, fmt.Sprintf("%q", supportedEdition)) } sort.Strings(editionStrs) @@ -690,7 +686,7 @@ func (r *result) addMessageBody(msgd *descriptorpb.DescriptorProto, body *ast.Me func (r *result) isMessageSetWireFormat(scope string, md *descriptorpb.DescriptorProto, handler *reporter.Handler) (*descriptorpb.UninterpretedOption, error) { uo := md.GetOptions().GetUninterpretedOption() - index, err := internal.FindOption(r, handler, scope, uo, "message_set_wire_format") + index, err := internal.FindOption(r, handler.HandleErrorf, scope, uo, "message_set_wire_format") if err != nil { return nil, err } @@ -845,105 +841,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) } diff --git a/vendor/github.com/bufbuild/protocompile/parser/validate.go b/vendor/github.com/bufbuild/protocompile/parser/validate.go index 9596242050c..64ebdaa3326 100644 --- a/vendor/github.com/bufbuild/protocompile/parser/validate.go +++ b/vendor/github.com/bufbuild/protocompile/parser/validate.go @@ -112,7 +112,7 @@ func validateNoFeatures(res *result, syntax protoreflect.Syntax, scope string, o // Editions is allowed to use features return nil } - if index, err := internal.FindFirstOption(res, handler, scope, opts, "features"); err != nil { + if index, err := internal.FindFirstOption(res, handler.HandleErrorf, scope, opts, "features"); err != nil { return err } else if index >= 0 { optNode := res.OptionNode(opts[index]) @@ -135,7 +135,7 @@ func validateMessage(res *result, syntax protoreflect.Syntax, name protoreflect. } } - if index, err := internal.FindOption(res, handler, scope, md.Options.GetUninterpretedOption(), "map_entry"); err != nil { + if index, err := internal.FindOption(res, handler.HandleErrorf, scope, md.Options.GetUninterpretedOption(), "map_entry"); err != nil { return err } else if index >= 0 { optNode := res.OptionNode(md.Options.GetUninterpretedOption()[index]) @@ -331,7 +331,7 @@ func validateEnum(res *result, syntax protoreflect.Syntax, name protoreflect.Ful allowAlias := false var allowAliasOpt *descriptorpb.UninterpretedOption - if index, err := internal.FindOption(res, handler, scope, ed.Options.GetUninterpretedOption(), "allow_alias"); err != nil { + if index, err := internal.FindOption(res, handler.HandleErrorf, scope, ed.Options.GetUninterpretedOption(), "allow_alias"); err != nil { return err } else if index >= 0 { allowAliasOpt = ed.Options.UninterpretedOption[index] @@ -481,7 +481,7 @@ func validateField(res *result, syntax protoreflect.Syntax, name protoreflect.Fu return err } } - if index, err := internal.FindOption(res, handler, scope, fld.Options.GetUninterpretedOption(), "packed"); err != nil { + if index, err := internal.FindOption(res, handler.HandleErrorf, scope, fld.Options.GetUninterpretedOption(), "packed"); err != nil { return err } else if index >= 0 { optNode := res.OptionNode(fld.Options.GetUninterpretedOption()[index]) @@ -491,7 +491,7 @@ func validateField(res *result, syntax protoreflect.Syntax, name protoreflect.Fu } } } else if syntax == protoreflect.Proto3 { - if index, err := internal.FindOption(res, handler, scope, fld.Options.GetUninterpretedOption(), "default"); err != nil { + if index, err := internal.FindOption(res, handler.HandleErrorf, scope, fld.Options.GetUninterpretedOption(), "default"); err != nil { return err } else if index >= 0 { optNode := res.OptionNode(fld.Options.GetUninterpretedOption()[index]) diff --git a/vendor/github.com/bufbuild/protocompile/protoutil/editions.go b/vendor/github.com/bufbuild/protocompile/protoutil/editions.go new file mode 100644 index 00000000000..fb21dff63b9 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/protoutil/editions.go @@ -0,0 +1,140 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package protoutil + +import ( + "fmt" + + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/descriptorpb" + "google.golang.org/protobuf/types/dynamicpb" + + "github.com/bufbuild/protocompile/internal/editions" +) + +// GetFeatureDefault gets the default value for the given feature and the given +// edition. The given feature must represent a field of the google.protobuf.FeatureSet +// message and must not be an extension. +// +// If the given field is from a dynamically built descriptor (i.e. it's containing +// message descriptor is different from the linked-in descriptor for +// [*descriptorpb.FeatureSet]), the returned value may be a dynamic value. In such +// cases, the value may not be directly usable using [protoreflect.Message.Set] with +// an instance of [*descriptorpb.FeatureSet] and must instead be used with a +// [*dynamicpb.Message]. +// +// To get the default value of a custom feature, use [GetCustomFeatureDefault] +// instead. +func GetFeatureDefault(edition descriptorpb.Edition, feature protoreflect.FieldDescriptor) (protoreflect.Value, error) { + if feature.ContainingMessage().FullName() != editions.FeatureSetDescriptor.FullName() { + return protoreflect.Value{}, fmt.Errorf("feature %s is a field of %s but should be a field of %s", + feature.Name(), feature.ContainingMessage().FullName(), editions.FeatureSetDescriptor.FullName()) + } + var msgType protoreflect.MessageType + if feature.ContainingMessage() == editions.FeatureSetDescriptor { + msgType = editions.FeatureSetType + } else { + msgType = dynamicpb.NewMessageType(feature.ContainingMessage()) + } + return editions.GetFeatureDefault(edition, msgType, feature) +} + +// GetCustomFeatureDefault gets the default value for the given custom feature +// and given edition. A custom feature is a field whose containing message is the +// type of an extension field of google.protobuf.FeatureSet. The given extension +// describes that extension field and message type. The given feature must be a +// field of that extension's message type. +func GetCustomFeatureDefault(edition descriptorpb.Edition, extension protoreflect.ExtensionType, feature protoreflect.FieldDescriptor) (protoreflect.Value, error) { + extDesc := extension.TypeDescriptor() + if extDesc.ContainingMessage().FullName() != editions.FeatureSetDescriptor.FullName() { + return protoreflect.Value{}, fmt.Errorf("extension %s does not extend %s", extDesc.FullName(), editions.FeatureSetDescriptor.FullName()) + } + if extDesc.Message() == nil { + return protoreflect.Value{}, fmt.Errorf("extensions of %s should be messages; %s is instead %s", + editions.FeatureSetDescriptor.FullName(), extDesc.FullName(), extDesc.Kind().String()) + } + if feature.IsExtension() { + return protoreflect.Value{}, fmt.Errorf("feature %s is an extension, but feature extension %s may not itself have extensions", + feature.FullName(), extDesc.FullName()) + } + if feature.ContainingMessage().FullName() != extDesc.Message().FullName() { + return protoreflect.Value{}, fmt.Errorf("feature %s is a field of %s but should be a field of %s", + feature.Name(), feature.ContainingMessage().FullName(), extDesc.Message().FullName()) + } + if feature.ContainingMessage() != extDesc.Message() { + return protoreflect.Value{}, fmt.Errorf("feature %s has a different message descriptor from the given extension type for %s", + feature.Name(), extDesc.Message().FullName()) + } + return editions.GetFeatureDefault(edition, extension.Zero().Message().Type(), feature) +} + +// ResolveFeature resolves a feature for the given descriptor. +// +// If the given element is in a proto2 or proto3 syntax file, this skips +// resolution and just returns the relevant default (since such files are not +// allowed to override features). If neither the given element nor any of its +// ancestors override the given feature, the relevant default is returned. +// +// This has the same caveat as GetFeatureDefault if the given feature is from a +// dynamically built descriptor. +func ResolveFeature(element protoreflect.Descriptor, feature protoreflect.FieldDescriptor) (protoreflect.Value, error) { + edition := editions.GetEdition(element) + defaultVal, err := GetFeatureDefault(edition, feature) + if err != nil { + return protoreflect.Value{}, err + } + return resolveFeature(edition, defaultVal, element, feature) +} + +// ResolveCustomFeature resolves a custom feature for the given extension and +// field descriptor. +// +// The given extension must be an extension of google.protobuf.FeatureSet that +// represents a non-repeated message value. The given feature is a field in +// that extension's message type. +// +// If the given element is in a proto2 or proto3 syntax file, this skips +// resolution and just returns the relevant default (since such files are not +// allowed to override features). If neither the given element nor any of its +// ancestors override the given feature, the relevant default is returned. +func ResolveCustomFeature(element protoreflect.Descriptor, extension protoreflect.ExtensionType, feature protoreflect.FieldDescriptor) (protoreflect.Value, error) { + edition := editions.GetEdition(element) + defaultVal, err := GetCustomFeatureDefault(edition, extension, feature) + if err != nil { + return protoreflect.Value{}, err + } + return resolveFeature(edition, defaultVal, element, extension.TypeDescriptor(), feature) +} + +func resolveFeature( + edition descriptorpb.Edition, + defaultVal protoreflect.Value, + element protoreflect.Descriptor, + fields ...protoreflect.FieldDescriptor, +) (protoreflect.Value, error) { + if edition == descriptorpb.Edition_EDITION_PROTO2 || edition == descriptorpb.Edition_EDITION_PROTO3 { + // these syntax levels can't specify features, so we can short-circuit the search + // through the descriptor hierarchy for feature overrides + return defaultVal, nil + } + val, err := editions.ResolveFeature(element, fields...) + if err != nil { + return protoreflect.Value{}, err + } + if val.IsValid() { + return val, nil + } + return defaultVal, nil +} diff --git a/vendor/github.com/bufbuild/protocompile/protoutil/protos.go b/vendor/github.com/bufbuild/protocompile/protoutil/protos.go index 4f0f362962e..9c5599938a7 100644 --- a/vendor/github.com/bufbuild/protocompile/protoutil/protos.go +++ b/vendor/github.com/bufbuild/protocompile/protoutil/protos.go @@ -14,11 +14,12 @@ // Package protoutil contains useful functions for interacting with descriptors. // For now these include only functions for efficiently converting descriptors -// produced by the compiler to descriptor protos. +// produced by the compiler to descriptor protos and functions for resolving +// "features" (a core concept of Protobuf Editions). // // Despite the fact that descriptor protos are mutable, calling code should NOT // mutate any of the protos returned from this package. For efficiency, some -// protos returned from this package may be part of internal state of a compiler +// values returned from this package may reference internal state of a compiler // result, and mutating the proto could corrupt or invalidate parts of that // result. package protoutil diff --git a/vendor/github.com/bufbuild/protocompile/reporter/errors.go b/vendor/github.com/bufbuild/protocompile/reporter/errors.go index d8634273c17..3a70a43e038 100644 --- a/vendor/github.com/bufbuild/protocompile/reporter/errors.go +++ b/vendor/github.com/bufbuild/protocompile/reporter/errors.go @@ -39,13 +39,18 @@ type ErrorWithPos interface { // Error creates a new ErrorWithPos from the given error and source position. func Error(span ast.SourceSpan, err error) ErrorWithPos { - return errorWithSpan{SourceSpan: span, underlying: err} + var ewp ErrorWithPos + if errors.As(err, &ewp) { + // replace existing position with given one + return &errorWithSpan{SourceSpan: span, underlying: ewp.Unwrap()} + } + return &errorWithSpan{SourceSpan: span, underlying: err} } // Errorf creates a new ErrorWithPos whose underlying error is created using the // given message format and arguments (via fmt.Errorf). func Errorf(span ast.SourceSpan, format string, args ...interface{}) ErrorWithPos { - return errorWithSpan{SourceSpan: span, underlying: fmt.Errorf(format, args...)} + return Error(span, fmt.Errorf(format, args...)) } type errorWithSpan struct { @@ -53,17 +58,17 @@ type errorWithSpan struct { underlying error } -func (e errorWithSpan) Error() string { +func (e *errorWithSpan) Error() string { sourcePos := e.GetPosition() return fmt.Sprintf("%s: %v", sourcePos, e.underlying) } -func (e errorWithSpan) GetPosition() ast.SourcePos { +func (e *errorWithSpan) GetPosition() ast.SourcePos { return e.Start() } -func (e errorWithSpan) Unwrap() error { +func (e *errorWithSpan) Unwrap() error { return e.underlying } -var _ ErrorWithPos = errorWithSpan{} +var _ ErrorWithPos = (*errorWithSpan)(nil) diff --git a/vendor/github.com/bufbuild/protocompile/reporter/reporter.go b/vendor/github.com/bufbuild/protocompile/reporter/reporter.go index cfd188fc245..8e90640634f 100644 --- a/vendor/github.com/bufbuild/protocompile/reporter/reporter.go +++ b/vendor/github.com/bufbuild/protocompile/reporter/reporter.go @@ -153,13 +153,7 @@ func (h *Handler) HandleError(err error) error { // call to HandleError or HandleErrorf), that same error is returned and the // given error is not reported. func (h *Handler) HandleErrorWithPos(span ast.SourceSpan, err error) error { - if ewp, ok := err.(ErrorWithPos); ok { - // replace existing position with given one - err = errorWithSpan{SourceSpan: span, underlying: ewp.Unwrap()} - } else { - err = errorWithSpan{SourceSpan: span, underlying: err} - } - return h.HandleError(err) + return h.HandleError(Error(span, err)) } // HandleErrorf handles an error with the given source position, creating the @@ -191,14 +185,7 @@ func (h *Handler) HandleWarning(err ErrorWithPos) { // HandleWarningWithPos handles a warning with the given source position. This will // delegate to the handler's configured reporter. func (h *Handler) HandleWarningWithPos(span ast.SourceSpan, err error) { - ewp, ok := err.(ErrorWithPos) - if ok { - // replace existing position with given one - ewp = errorWithSpan{SourceSpan: span, underlying: ewp.Unwrap()} - } else { - ewp = errorWithSpan{SourceSpan: span, underlying: err} - } - h.HandleWarning(ewp) + h.HandleWarning(Error(span, err)) } // HandleWarningf handles a warning with the given source position, creating the diff --git a/vendor/github.com/bufbuild/protocompile/resolver.go b/vendor/github.com/bufbuild/protocompile/resolver.go index 7ff40eb0312..400d554bfde 100644 --- a/vendor/github.com/bufbuild/protocompile/resolver.go +++ b/vendor/github.com/bufbuild/protocompile/resolver.go @@ -180,6 +180,27 @@ func SourceAccessorFromMap(srcs map[string]string) func(string) (io.ReadCloser, // WithStandardImports returns a new resolver that knows about the same standard // imports that are included with protoc. +// +// Note that this uses the descriptors embedded in generated code in the packages +// of the Protobuf Go module, except for "google/protobuf/cpp_features.proto" and +// "google/protobuf/java_features.proto". For those two files, compiled descriptors +// are embedded in this module because there is no package in the Protobuf Go module +// that contains generated code for those files. This resolver also provides results +// for the "google/protobuf/go_features.proto", which is technically not a standard +// file (it is not included with protoc) but is included in generated code in the +// Protobuf Go module. +// +// As of v0.14.0 of this module (and v1.34.2 of the Protobuf Go module and v27.0 of +// Protobuf), the contents of the standard import "google/protobuf/descriptor.proto" +// contain extension declarations which are *absent* from the descriptors that this +// resolver returns. That is because extension declarations are only retained in +// source, not at runtime, which means they are not available in the embedded +// descriptors in generated code. +// +// To use versions of the standard imports that *do* include these extension +// declarations, see wellknownimports.WithStandardImports instead. As of this +// writing, the declarations are only needed to prevent source files from +// illegally re-defining the custom features for C++, Java, and Go. func WithStandardImports(r Resolver) Resolver { return ResolverFunc(func(name string) (SearchResult, error) { res, err := r.FindFileByPath(name) diff --git a/vendor/github.com/bufbuild/protocompile/sourceinfo/source_code_info.go b/vendor/github.com/bufbuild/protocompile/sourceinfo/source_code_info.go index 4cbbadfa47d..3b0ae6573a4 100644 --- a/vendor/github.com/bufbuild/protocompile/sourceinfo/source_code_info.go +++ b/vendor/github.com/bufbuild/protocompile/sourceinfo/source_code_info.go @@ -45,6 +45,10 @@ type OptionSourceInfo struct { // The source info path to this element. If this element represents a // declaration with an array-literal value, the last element of the // path is the index of the first item in the array. + // + // This path is relative to the options message. So the first element + // is a field number of the options message. + // // If the first element is negative, it indicates the number of path // components to remove from the path to the relevant options. This is // used for field pseudo-options, so that the path indicates a field on @@ -53,8 +57,8 @@ type OptionSourceInfo struct { Path []int32 // Children can be an *ArrayLiteralSourceInfo, a *MessageLiteralSourceInfo, // or nil, depending on whether the option's value is an - // *ast.ArrayLiteralNode, an *ast.MessageLiteralNode, or neither. - // For *ast.ArrayLiteralNode values, this is only populated if the + // [*ast.ArrayLiteralNode], an [*ast.MessageLiteralNode], or neither. + // For [*ast.ArrayLiteralNode] values, this is only populated if the // value is a non-empty array of messages. (Empty arrays and arrays // of scalar values do not need any additional info.) Children OptionChildrenSourceInfo @@ -67,7 +71,7 @@ type OptionChildrenSourceInfo interface { } // ArrayLiteralSourceInfo represents source info paths for the child -// elements of an *ast.ArrayLiteralNode. This value is only useful for +// elements of an [*ast.ArrayLiteralNode]. This value is only useful for // non-empty array literals that contain messages. type ArrayLiteralSourceInfo struct { Elements []OptionSourceInfo @@ -76,7 +80,7 @@ type ArrayLiteralSourceInfo struct { func (*ArrayLiteralSourceInfo) isChildSourceInfo() {} // MessageLiteralSourceInfo represents source info paths for the child -// elements of an *ast.MessageLiteralNode. +// elements of an [*ast.MessageLiteralNode]. type MessageLiteralSourceInfo struct { Fields map[*ast.MessageFieldNode]*OptionSourceInfo } diff --git a/vendor/github.com/bufbuild/protocompile/std_imports.go b/vendor/github.com/bufbuild/protocompile/std_imports.go index 58c61dac775..a31232ac6b8 100644 --- a/vendor/github.com/bufbuild/protocompile/std_imports.go +++ b/vendor/github.com/bufbuild/protocompile/std_imports.go @@ -17,7 +17,8 @@ package protocompile import ( "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/reflect/protoregistry" - _ "google.golang.org/protobuf/types/known/anypb" // link in packages that include the standard protos included with protoc. + _ "google.golang.org/protobuf/types/gofeaturespb" // link in packages that include the standard protos included with protoc. + _ "google.golang.org/protobuf/types/known/anypb" _ "google.golang.org/protobuf/types/known/apipb" _ "google.golang.org/protobuf/types/known/durationpb" _ "google.golang.org/protobuf/types/known/emptypb" @@ -28,6 +29,8 @@ import ( _ "google.golang.org/protobuf/types/known/typepb" _ "google.golang.org/protobuf/types/known/wrapperspb" _ "google.golang.org/protobuf/types/pluginpb" + + "github.com/bufbuild/protocompile/internal/featuresext" ) // All files that are included with protoc are also included with this package @@ -44,6 +47,7 @@ func init() { "google/protobuf/duration.proto", "google/protobuf/empty.proto", "google/protobuf/field_mask.proto", + "google/protobuf/go_features.proto", "google/protobuf/source_context.proto", "google/protobuf/struct.proto", "google/protobuf/timestamp.proto", @@ -59,4 +63,34 @@ func init() { } standardImports[fn] = fd } + + otherFeatures := []struct { + Name string + GetDescriptor func() (protoreflect.FileDescriptor, error) + }{ + { + Name: "google/protobuf/cpp_features.proto", + GetDescriptor: featuresext.CppFeaturesDescriptor, + }, + { + Name: "google/protobuf/java_features.proto", + GetDescriptor: featuresext.JavaFeaturesDescriptor, + }, + } + for _, feature := range otherFeatures { + // First see if the program has generated Go code for this + // file linked in: + fd, err := protoregistry.GlobalFiles.FindFileByPath(feature.Name) + if err == nil { + standardImports[feature.Name] = fd + continue + } + fd, err = feature.GetDescriptor() + if err != nil { + // For these extensions to FeatureSet, we are lenient. If + // we can't load them, just ignore them. + continue + } + standardImports[feature.Name] = fd + } } diff --git a/vendor/github.com/bufbuild/protocompile/supported_editions.go b/vendor/github.com/bufbuild/protocompile/supported_editions.go new file mode 100644 index 00000000000..72bd51f1913 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/supported_editions.go @@ -0,0 +1,30 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package protocompile + +import ( + "google.golang.org/protobuf/types/descriptorpb" + + "github.com/bufbuild/protocompile/internal/editions" +) + +// IsEditionSupported returns true if this module can compile sources for +// the given edition. This returns true for the special EDITION_PROTO2 and +// EDITION_PROTO3 as well as all actual editions supported. +func IsEditionSupported(edition descriptorpb.Edition) bool { + return edition == descriptorpb.Edition_EDITION_PROTO2 || + edition == descriptorpb.Edition_EDITION_PROTO3 || + (edition >= editions.MinSupportedEdition && edition <= editions.MaxSupportedEdition) +} diff --git a/vendor/github.com/bufbuild/protocompile/walk/walk.go b/vendor/github.com/bufbuild/protocompile/walk/walk.go index 11fceeb6c8a..244fa720bb2 100644 --- a/vendor/github.com/bufbuild/protocompile/walk/walk.go +++ b/vendor/github.com/bufbuild/protocompile/walk/walk.go @@ -65,36 +65,18 @@ func Descriptors(file protoreflect.FileDescriptor, fn func(protoreflect.Descript // The exit function is called using a post-order traversal, where the function // is called for a descriptor only after it is called for any descendants. func DescriptorsEnterAndExit(file protoreflect.FileDescriptor, enter, exit func(protoreflect.Descriptor) error) error { - for i := 0; i < file.Messages().Len(); i++ { - msg := file.Messages().Get(i) - if err := messageDescriptor(msg, enter, exit); err != nil { - return err - } - } - for i := 0; i < file.Enums().Len(); i++ { - en := file.Enums().Get(i) - if err := enumDescriptor(en, enter, exit); err != nil { - return err - } - } - for i := 0; i < file.Extensions().Len(); i++ { - ext := file.Extensions().Get(i) - if err := enter(ext); err != nil { - return err - } - if exit != nil { - if err := exit(ext); err != nil { - return err - } - } + if err := walkContainer(file, enter, exit); err != nil { + return err } - for i := 0; i < file.Services().Len(); i++ { - svc := file.Services().Get(i) + services := file.Services() + for i, length := 0, services.Len(); i < length; i++ { + svc := services.Get(i) if err := enter(svc); err != nil { return err } - for i := 0; i < svc.Methods().Len(); i++ { - mtd := svc.Methods().Get(i) + methods := svc.Methods() + for i, length := 0, methods.Len(); i < length; i++ { + mtd := methods.Get(i) if err := enter(mtd); err != nil { return err } @@ -113,12 +95,49 @@ func DescriptorsEnterAndExit(file protoreflect.FileDescriptor, enter, exit func( return nil } +type container interface { + Messages() protoreflect.MessageDescriptors + Enums() protoreflect.EnumDescriptors + Extensions() protoreflect.ExtensionDescriptors +} + +func walkContainer(container container, enter, exit func(protoreflect.Descriptor) error) error { + messages := container.Messages() + for i, length := 0, messages.Len(); i < length; i++ { + msg := messages.Get(i) + if err := messageDescriptor(msg, enter, exit); err != nil { + return err + } + } + enums := container.Enums() + for i, length := 0, enums.Len(); i < length; i++ { + en := enums.Get(i) + if err := enumDescriptor(en, enter, exit); err != nil { + return err + } + } + exts := container.Extensions() + for i, length := 0, exts.Len(); i < length; i++ { + ext := exts.Get(i) + if err := enter(ext); err != nil { + return err + } + if exit != nil { + if err := exit(ext); err != nil { + return err + } + } + } + return nil +} + func messageDescriptor(msg protoreflect.MessageDescriptor, enter, exit func(protoreflect.Descriptor) error) error { if err := enter(msg); err != nil { return err } - for i := 0; i < msg.Fields().Len(); i++ { - fld := msg.Fields().Get(i) + fields := msg.Fields() + for i, length := 0, fields.Len(); i < length; i++ { + fld := fields.Get(i) if err := enter(fld); err != nil { return err } @@ -128,8 +147,9 @@ func messageDescriptor(msg protoreflect.MessageDescriptor, enter, exit func(prot } } } - for i := 0; i < msg.Oneofs().Len(); i++ { - oo := msg.Oneofs().Get(i) + oneofs := msg.Oneofs() + for i, length := 0, oneofs.Len(); i < length; i++ { + oo := oneofs.Get(i) if err := enter(oo); err != nil { return err } @@ -139,28 +159,8 @@ func messageDescriptor(msg protoreflect.MessageDescriptor, enter, exit func(prot } } } - for i := 0; i < msg.Messages().Len(); i++ { - nested := msg.Messages().Get(i) - if err := messageDescriptor(nested, enter, exit); err != nil { - return err - } - } - for i := 0; i < msg.Enums().Len(); i++ { - en := msg.Enums().Get(i) - if err := enumDescriptor(en, enter, exit); err != nil { - return err - } - } - for i := 0; i < msg.Extensions().Len(); i++ { - ext := msg.Extensions().Get(i) - if err := enter(ext); err != nil { - return err - } - if exit != nil { - if err := exit(ext); err != nil { - return err - } - } + if err := walkContainer(msg, enter, exit); err != nil { + return err } if exit != nil { if err := exit(msg); err != nil { @@ -174,8 +174,9 @@ func enumDescriptor(en protoreflect.EnumDescriptor, enter, exit func(protoreflec if err := enter(en); err != nil { return err } - for i := 0; i < en.Values().Len(); i++ { - enVal := en.Values().Get(i) + vals := en.Values() + for i, length := 0, vals.Len(); i < length; i++ { + enVal := vals.Get(i) if err := enter(enVal); err != nil { return err } @@ -235,12 +236,12 @@ func DescriptorProtos(file *descriptorpb.FileDescriptorProto, fn func(protorefle // the function is called for a descriptor proto only after it is called for any // descendants. func DescriptorProtosEnterAndExit(file *descriptorpb.FileDescriptorProto, enter, exit func(protoreflect.FullName, proto.Message) error) error { - enterWithPath := func(n protoreflect.FullName, p protoreflect.SourcePath, m proto.Message) error { + enterWithPath := func(n protoreflect.FullName, _ protoreflect.SourcePath, m proto.Message) error { return enter(n, m) } - var exitWithPath func(n protoreflect.FullName, p protoreflect.SourcePath, m proto.Message) error + var exitWithPath func(protoreflect.FullName, protoreflect.SourcePath, proto.Message) error if exit != nil { - exitWithPath = func(n protoreflect.FullName, p protoreflect.SourcePath, m proto.Message) error { + exitWithPath = func(n protoreflect.FullName, _ protoreflect.SourcePath, m proto.Message) error { return exit(n, m) } } diff --git a/vendor/github.com/jhump/protoreflect/desc/descriptor.go b/vendor/github.com/jhump/protoreflect/desc/descriptor.go index 38b8f51ba99..68eb252b8fc 100644 --- a/vendor/github.com/jhump/protoreflect/desc/descriptor.go +++ b/vendor/github.com/jhump/protoreflect/desc/descriptor.go @@ -543,7 +543,7 @@ func (er extRanges) Swap(i, j int) { // FindFieldByName finds the field with the given name. If no such field exists // then nil is returned. Only regular fields are returned, not extensions. func (md *MessageDescriptor) FindFieldByName(fieldName string) *FieldDescriptor { - fqn := fmt.Sprintf("%s.%s", md.GetFullyQualifiedName(), fieldName) + fqn := md.GetFullyQualifiedName() + "." + fieldName if fd, ok := md.file.symbols[fqn].(*FieldDescriptor); ok && !fd.IsExtension() { return fd } else { diff --git a/vendor/github.com/jhump/protoreflect/desc/doc.go b/vendor/github.com/jhump/protoreflect/desc/doc.go index dfac5c72b37..07bcbf34ee6 100644 --- a/vendor/github.com/jhump/protoreflect/desc/doc.go +++ b/vendor/github.com/jhump/protoreflect/desc/doc.go @@ -59,4 +59,12 @@ // // Also see the desc/builder sub-package, for another API that makes it easier // to synthesize descriptors programmatically. +// +// Deprecated: This module was created for use with the older "v1" Protobuf API +// in github.com/golang/protobuf. However, much of this module is no longer +// necessary as the newer "v2" API in google.golang.org/protobuf provides similar +// capabilities. Instead of using this github.com/jhump/protoreflect/desc package, +// see [google.golang.org/protobuf/reflect/protoreflect]. +// +// [google.golang.org/protobuf/reflect/protoreflect]: https://pkg.go.dev/google.golang.org/protobuf/reflect/protoreflect package desc diff --git a/vendor/github.com/jhump/protoreflect/desc/imports.go b/vendor/github.com/jhump/protoreflect/desc/imports.go index 8e6a0d6e011..dc6b7358c05 100644 --- a/vendor/github.com/jhump/protoreflect/desc/imports.go +++ b/vendor/github.com/jhump/protoreflect/desc/imports.go @@ -34,10 +34,6 @@ var ( // package or when the alternate path is only used from one file (so you don't // want the alternate path used when loading every other file), use an // ImportResolver instead. -// -// Deprecated: the new protobuf runtime (v1.4+) verifies that import paths are -// correct and that descriptors can be linked during package initialization. So -// registering alternate paths is no longer useful or necessary. func RegisterImportPath(registerPath, importPath string) { if len(importPath) == 0 { panic("import path cannot be empty") @@ -60,10 +56,6 @@ func RegisterImportPath(registerPath, importPath string) { // ResolveImport resolves the given import path. If it has been registered as an // alternate via RegisterImportPath, the registered path is returned. Otherwise, // the given import path is returned unchanged. -// -// Deprecated: the new protobuf runtime (v1.4+) verifies that import paths are -// correct and that descriptors can be linked during package initialization. So -// registering alternate paths is no longer useful or necessary. func ResolveImport(importPath string) string { importPath = clean(importPath) globalImportPathMu.RLock() @@ -244,10 +236,6 @@ func (r *ImportResolver) registerImportPathFrom(registerPath, importPath, source // LoadFileDescriptor is the same as the package function of the same name, but // any alternate paths configured in this resolver are used when linking the // given descriptor proto. -// -// Deprecated: the new protobuf runtime (v1.4+) verifies that import paths are -// correct and that descriptors can be linked during package initialization. So -// registering alternate paths is no longer useful or necessary. func (r *ImportResolver) LoadFileDescriptor(filePath string) (*FileDescriptor, error) { return LoadFileDescriptor(filePath) } @@ -255,10 +243,6 @@ func (r *ImportResolver) LoadFileDescriptor(filePath string) (*FileDescriptor, e // LoadMessageDescriptor is the same as the package function of the same name, // but any alternate paths configured in this resolver are used when linking // files for the returned descriptor. -// -// Deprecated: the new protobuf runtime (v1.4+) verifies that import paths are -// correct and that descriptors can be linked during package initialization. So -// registering alternate paths is no longer useful or necessary. func (r *ImportResolver) LoadMessageDescriptor(msgName string) (*MessageDescriptor, error) { return LoadMessageDescriptor(msgName) } @@ -266,10 +250,6 @@ func (r *ImportResolver) LoadMessageDescriptor(msgName string) (*MessageDescript // LoadMessageDescriptorForMessage is the same as the package function of the // same name, but any alternate paths configured in this resolver are used when // linking files for the returned descriptor. -// -// Deprecated: the new protobuf runtime (v1.4+) verifies that import paths are -// correct and that descriptors can be linked during package initialization. So -// registering alternate paths is no longer useful or necessary. func (r *ImportResolver) LoadMessageDescriptorForMessage(msg proto.Message) (*MessageDescriptor, error) { return LoadMessageDescriptorForMessage(msg) } @@ -277,10 +257,6 @@ func (r *ImportResolver) LoadMessageDescriptorForMessage(msg proto.Message) (*Me // LoadMessageDescriptorForType is the same as the package function of the same // name, but any alternate paths configured in this resolver are used when // linking files for the returned descriptor. -// -// Deprecated: the new protobuf runtime (v1.4+) verifies that import paths are -// correct and that descriptors can be linked during package initialization. So -// registering alternate paths is no longer useful or necessary. func (r *ImportResolver) LoadMessageDescriptorForType(msgType reflect.Type) (*MessageDescriptor, error) { return LoadMessageDescriptorForType(msgType) } @@ -288,10 +264,6 @@ func (r *ImportResolver) LoadMessageDescriptorForType(msgType reflect.Type) (*Me // LoadEnumDescriptorForEnum is the same as the package function of the same // name, but any alternate paths configured in this resolver are used when // linking files for the returned descriptor. -// -// Deprecated: the new protobuf runtime (v1.4+) verifies that import paths are -// correct and that descriptors can be linked during package initialization. So -// registering alternate paths is no longer useful or necessary. func (r *ImportResolver) LoadEnumDescriptorForEnum(enum protoEnum) (*EnumDescriptor, error) { return LoadEnumDescriptorForEnum(enum) } @@ -299,10 +271,6 @@ func (r *ImportResolver) LoadEnumDescriptorForEnum(enum protoEnum) (*EnumDescrip // LoadEnumDescriptorForType is the same as the package function of the same // name, but any alternate paths configured in this resolver are used when // linking files for the returned descriptor. -// -// Deprecated: the new protobuf runtime (v1.4+) verifies that import paths are -// correct and that descriptors can be linked during package initialization. So -// registering alternate paths is no longer useful or necessary. func (r *ImportResolver) LoadEnumDescriptorForType(enumType reflect.Type) (*EnumDescriptor, error) { return LoadEnumDescriptorForType(enumType) } @@ -310,10 +278,6 @@ func (r *ImportResolver) LoadEnumDescriptorForType(enumType reflect.Type) (*Enum // LoadFieldDescriptorForExtension is the same as the package function of the // same name, but any alternate paths configured in this resolver are used when // linking files for the returned descriptor. -// -// Deprecated: the new protobuf runtime (v1.4+) verifies that import paths are -// correct and that descriptors can be linked during package initialization. So -// registering alternate paths is no longer useful or necessary. func (r *ImportResolver) LoadFieldDescriptorForExtension(ext *proto.ExtensionDesc) (*FieldDescriptor, error) { return LoadFieldDescriptorForExtension(ext) } diff --git a/vendor/github.com/jhump/protoreflect/desc/load.go b/vendor/github.com/jhump/protoreflect/desc/load.go index 8776ab0b9b8..8fd09ac18f4 100644 --- a/vendor/github.com/jhump/protoreflect/desc/load.go +++ b/vendor/github.com/jhump/protoreflect/desc/load.go @@ -1,6 +1,7 @@ package desc import ( + "errors" "fmt" "reflect" "sync" @@ -33,7 +34,7 @@ var ( // re-processed if the same file is fetched again later. func LoadFileDescriptor(file string) (*FileDescriptor, error) { d, err := sourceinfo.GlobalFiles.FindFileByPath(file) - if err == protoregistry.NotFound { + if errors.Is(err, protoregistry.NotFound) { // for backwards compatibility, see if this matches a known old // alias for the file (older versions of libraries that registered // the files using incorrect/non-canonical paths) @@ -42,7 +43,7 @@ func LoadFileDescriptor(file string) (*FileDescriptor, error) { } } if err != nil { - if err != protoregistry.NotFound { + if !errors.Is(err, protoregistry.NotFound) { return nil, internal.ErrNoSuchFile(file) } return nil, err @@ -64,7 +65,7 @@ func LoadFileDescriptor(file string) (*FileDescriptor, error) { func LoadMessageDescriptor(message string) (*MessageDescriptor, error) { mt, err := sourceinfo.GlobalTypes.FindMessageByName(protoreflect.FullName(message)) if err != nil { - if err == protoregistry.NotFound { + if errors.Is(err, protoregistry.NotFound) { return nil, nil } return nil, err diff --git a/vendor/github.com/jhump/protoreflect/desc/protoparse/doc.go b/vendor/github.com/jhump/protoreflect/desc/protoparse/doc.go index c6446d34680..6642f6a4c6e 100644 --- a/vendor/github.com/jhump/protoreflect/desc/protoparse/doc.go +++ b/vendor/github.com/jhump/protoreflect/desc/protoparse/doc.go @@ -7,4 +7,10 @@ // That way, like when invoking protoc, programs need not supply copies of these // "builtin" files. Though if copies of the files are provided, they will be // used instead of the builtin descriptors. +// +// Deprecated: This protoparse package is now just a thin veneer around a newer +// replacement parser/compiler: [github.com/bufbuild/protocompile]. Users are +// highly encouraged to directly use protocompile instead of this package. +// +// [github.com/bufbuild/protocompile]: https://pkg.go.dev/github.com/bufbuild/protocompile package protoparse diff --git a/vendor/github.com/jhump/protoreflect/desc/sourceinfo/registry.go b/vendor/github.com/jhump/protoreflect/desc/sourceinfo/registry.go index de38e0d1e23..8301c40278a 100644 --- a/vendor/github.com/jhump/protoreflect/desc/sourceinfo/registry.go +++ b/vendor/github.com/jhump/protoreflect/desc/sourceinfo/registry.go @@ -30,11 +30,12 @@ package sourceinfo import ( "bytes" "compress/gzip" + "errors" "fmt" - "github.com/golang/protobuf/proto" - "io/ioutil" + "io" "sync" + "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protodesc" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/reflect/protoregistry" @@ -56,9 +57,10 @@ var ( // can include source code info in the returned descriptors. GlobalTypes TypeResolver = registry{} - mu sync.RWMutex - sourceInfoByFile = map[string]*descriptorpb.SourceCodeInfo{} - fileDescriptors = map[protoreflect.FileDescriptor]protoreflect.FileDescriptor{} + mu sync.RWMutex + sourceInfoByFile = map[string]*descriptorpb.SourceCodeInfo{} + fileDescriptors = map[protoreflect.FileDescriptor]protoreflect.FileDescriptor{} + updatedDescriptors filesWithFallback ) // Resolver can resolve file names into file descriptors and also provides methods for @@ -108,7 +110,7 @@ func RegisterEncodedSourceInfo(file string, data []byte) error { defer func() { _ = zipReader.Close() }() - unzipped, err := ioutil.ReadAll(zipReader) + unzipped, err := io.ReadAll(zipReader) if err != nil { return err } @@ -129,14 +131,25 @@ func SourceInfoForFile(file string) *descriptorpb.SourceCodeInfo { return sourceInfoByFile[file] } -func canWrap(d protoreflect.Descriptor) bool { - srcInfo := SourceInfoForFile(d.ParentFile().Path()) - return len(srcInfo.GetLocation()) > 0 +func canUpgrade(d protoreflect.Descriptor) bool { + if d == nil { + return false + } + fd := d.ParentFile() + if fd.SourceLocations().Len() > 0 { + // already has source info + return false + } + if genFile, err := protoregistry.GlobalFiles.FindFileByPath(fd.Path()); err != nil || genFile != fd { + // given descriptor is not from generated code + return false + } + return true } -func getFile(fd protoreflect.FileDescriptor) protoreflect.FileDescriptor { - if fd == nil { - return nil +func getFile(fd protoreflect.FileDescriptor) (protoreflect.FileDescriptor, error) { + if !canUpgrade(fd) { + return fd, nil } mu.RLock() @@ -144,31 +157,66 @@ func getFile(fd protoreflect.FileDescriptor) protoreflect.FileDescriptor { mu.RUnlock() if result != nil { - return result + return result, nil } mu.Lock() defer mu.Unlock() - // double-check, in case it was added to map while upgrading lock - result = fileDescriptors[fd] + result, err := getFileLocked(fd) + if err != nil { + return nil, fmt.Errorf("updating file %q: %w", fd.Path(), err) + } + return result, nil +} + +func getFileLocked(fd protoreflect.FileDescriptor) (protoreflect.FileDescriptor, error) { + result := fileDescriptors[fd] if result != nil { - return result + return result, nil } - srcInfo := sourceInfoByFile[fd.Path()] - if len(srcInfo.GetLocation()) > 0 { - result = &fileDescriptor{ - FileDescriptor: fd, - locs: &sourceLocations{ - orig: srcInfo.Location, - }, + // We have to build its dependencies, too, so that the descriptor's + // references *all* have source code info. + var deps []protoreflect.FileDescriptor + imps := fd.Imports() + for i, length := 0, imps.Len(); i < length; i++ { + origDep := imps.Get(i).FileDescriptor + updatedDep, err := getFileLocked(origDep) + if err != nil { + return nil, fmt.Errorf("updating import %q: %w", origDep.Path(), err) + } + if updatedDep != origDep && deps == nil { + // lazily init slice of deps and copy over deps before this one + deps = make([]protoreflect.FileDescriptor, i, length) + for j := 0; j < i; j++ { + deps[j] = imps.Get(i).FileDescriptor + } } - } else { - // nothing to do; don't bother wrapping - result = fd + if deps != nil { + deps = append(deps, updatedDep) + } + } + + srcInfo := sourceInfoByFile[fd.Path()] + if len(srcInfo.GetLocation()) == 0 && len(deps) == 0 { + // nothing to do; don't bother changing + return fd, nil + } + + // Add source code info and rebuild. + fdProto := protodesc.ToFileDescriptorProto(fd) + fdProto.SourceCodeInfo = srcInfo + + result, err := protodesc.NewFile(fdProto, &updatedDescriptors) + if err != nil { + return nil, err } + if err := updatedDescriptors.RegisterFile(result); err != nil { + return nil, fmt.Errorf("registering import %q: %w", result.Path(), err) + } + fileDescriptors[fd] = result - return result + return result, nil } type registry struct{} @@ -180,36 +228,34 @@ func (r registry) FindFileByPath(path string) (protoreflect.FileDescriptor, erro if err != nil { return nil, err } - return getFile(fd), nil + return getFile(fd) } func (r registry) FindDescriptorByName(name protoreflect.FullName) (protoreflect.Descriptor, error) { d, err := protoregistry.GlobalFiles.FindDescriptorByName(name) - if !canWrap(d) { - return d, nil - } if err != nil { return nil, err } + if !canUpgrade(d) { + return d, nil + } switch d := d.(type) { case protoreflect.FileDescriptor: - return getFile(d), nil + return getFile(d) case protoreflect.MessageDescriptor: - return messageDescriptor{d}, nil - case protoreflect.ExtensionTypeDescriptor: - return extensionDescriptor{d}, nil + return updateDescriptor(d) case protoreflect.FieldDescriptor: - return fieldDescriptor{d}, nil + return updateField(d) case protoreflect.OneofDescriptor: - return oneOfDescriptor{d}, nil + return updateDescriptor(d) case protoreflect.EnumDescriptor: - return enumDescriptor{d}, nil + return updateDescriptor(d) case protoreflect.EnumValueDescriptor: - return enumValueDescriptor{d}, nil + return updateDescriptor(d) case protoreflect.ServiceDescriptor: - return serviceDescriptor{d}, nil + return updateDescriptor(d) case protoreflect.MethodDescriptor: - return methodDescriptor{d}, nil + return updateDescriptor(d) default: return nil, fmt.Errorf("unrecognized descriptor type: %T", d) } @@ -220,10 +266,11 @@ func (r registry) FindMessageByName(message protoreflect.FullName) (protoreflect if err != nil { return nil, err } - if !canWrap(mt.Descriptor()) { + msg, err := updateDescriptor(mt.Descriptor()) + if err != nil { return mt, nil } - return messageType{mt}, nil + return messageType{MessageType: mt, msgDesc: msg}, nil } func (r registry) FindMessageByURL(url string) (protoreflect.MessageType, error) { @@ -231,10 +278,11 @@ func (r registry) FindMessageByURL(url string) (protoreflect.MessageType, error) if err != nil { return nil, err } - if !canWrap(mt.Descriptor()) { + msg, err := updateDescriptor(mt.Descriptor()) + if err != nil { return mt, nil } - return messageType{mt}, nil + return messageType{MessageType: mt, msgDesc: msg}, nil } func (r registry) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) { @@ -242,10 +290,11 @@ func (r registry) FindExtensionByName(field protoreflect.FullName) (protoreflect if err != nil { return nil, err } - if !canWrap(xt.TypeDescriptor()) { + ext, err := updateDescriptor(xt.TypeDescriptor().Descriptor()) + if err != nil { return xt, nil } - return extensionType{xt}, nil + return extensionType{ExtensionType: xt, extDesc: ext}, nil } func (r registry) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) { @@ -253,17 +302,39 @@ func (r registry) FindExtensionByNumber(message protoreflect.FullName, field pro if err != nil { return nil, err } - if !canWrap(xt.TypeDescriptor()) { + ext, err := updateDescriptor(xt.TypeDescriptor().Descriptor()) + if err != nil { return xt, nil } - return extensionType{xt}, nil + return extensionType{ExtensionType: xt, extDesc: ext}, nil } func (r registry) RangeExtensionsByMessage(message protoreflect.FullName, fn func(protoreflect.ExtensionType) bool) { protoregistry.GlobalTypes.RangeExtensionsByMessage(message, func(xt protoreflect.ExtensionType) bool { - if canWrap(xt.TypeDescriptor()) { - xt = extensionType{xt} + ext, err := updateDescriptor(xt.TypeDescriptor().Descriptor()) + if err != nil { + return fn(xt) } - return fn(xt) + return fn(extensionType{ExtensionType: xt, extDesc: ext}) }) } + +type filesWithFallback struct { + protoregistry.Files +} + +func (f *filesWithFallback) FindFileByPath(path string) (protoreflect.FileDescriptor, error) { + fd, err := f.Files.FindFileByPath(path) + if errors.Is(err, protoregistry.NotFound) { + fd, err = protoregistry.GlobalFiles.FindFileByPath(path) + } + return fd, err +} + +func (f *filesWithFallback) FindDescriptorByName(name protoreflect.FullName) (protoreflect.Descriptor, error) { + fd, err := f.Files.FindDescriptorByName(name) + if errors.Is(err, protoregistry.NotFound) { + fd, err = protoregistry.GlobalFiles.FindDescriptorByName(name) + } + return fd, err +} diff --git a/vendor/github.com/jhump/protoreflect/desc/sourceinfo/update.go b/vendor/github.com/jhump/protoreflect/desc/sourceinfo/update.go new file mode 100644 index 00000000000..53bc4572dc8 --- /dev/null +++ b/vendor/github.com/jhump/protoreflect/desc/sourceinfo/update.go @@ -0,0 +1,314 @@ +package sourceinfo + +import ( + "fmt" + + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" +) + +// AddSourceInfoToFile will return a new file descriptor that is a copy +// of fd except that it includes source code info. If the given file +// already contains source info, was not registered from generated code, +// or was not processed with the protoc-gen-gosrcinfo plugin, then fd +// is returned as is, unchanged. +func AddSourceInfoToFile(fd protoreflect.FileDescriptor) (protoreflect.FileDescriptor, error) { + return getFile(fd) +} + +// AddSourceInfoToMessage will return a new message descriptor that is a +// copy of md except that it includes source code info. If the file that +// contains the given message descriptor already contains source info, +// was not registered from generated code, or was not processed with the +// protoc-gen-gosrcinfo plugin, then md is returned as is, unchanged. +func AddSourceInfoToMessage(md protoreflect.MessageDescriptor) (protoreflect.MessageDescriptor, error) { + return updateDescriptor(md) +} + +// AddSourceInfoToEnum will return a new enum descriptor that is a copy +// of ed except that it includes source code info. If the file that +// contains the given enum descriptor already contains source info, was +// not registered from generated code, or was not processed with the +// protoc-gen-gosrcinfo plugin, then ed is returned as is, unchanged. +func AddSourceInfoToEnum(ed protoreflect.EnumDescriptor) (protoreflect.EnumDescriptor, error) { + return updateDescriptor(ed) +} + +// AddSourceInfoToService will return a new service descriptor that is +// a copy of sd except that it includes source code info. If the file +// that contains the given service descriptor already contains source +// info, was not registered from generated code, or was not processed +// with the protoc-gen-gosrcinfo plugin, then ed is returned as is, +// unchanged. +func AddSourceInfoToService(sd protoreflect.ServiceDescriptor) (protoreflect.ServiceDescriptor, error) { + return updateDescriptor(sd) +} + +// AddSourceInfoToExtensionType will return a new extension type that +// is a copy of xt except that its associated descriptors includes +// source code info. If the file that contains the given extension +// already contains source info, was not registered from generated +// code, or was not processed with the protoc-gen-gosrcinfo plugin, +// then xt is returned as is, unchanged. +func AddSourceInfoToExtensionType(xt protoreflect.ExtensionType) (protoreflect.ExtensionType, error) { + if genType, err := protoregistry.GlobalTypes.FindExtensionByName(xt.TypeDescriptor().FullName()); err != nil || genType != xt { + return xt, nil // not from generated code + } + ext, err := updateField(xt.TypeDescriptor().Descriptor()) + if err != nil { + return nil, err + } + return extensionType{ExtensionType: xt, extDesc: ext}, nil +} + +// AddSourceInfoToMessageType will return a new message type that +// is a copy of mt except that its associated descriptors includes +// source code info. If the file that contains the given message +// already contains source info, was not registered from generated +// code, or was not processed with the protoc-gen-gosrcinfo plugin, +// then mt is returned as is, unchanged. +func AddSourceInfoToMessageType(mt protoreflect.MessageType) (protoreflect.MessageType, error) { + if genType, err := protoregistry.GlobalTypes.FindMessageByName(mt.Descriptor().FullName()); err != nil || genType != mt { + return mt, nil // not from generated code + } + msg, err := updateDescriptor(mt.Descriptor()) + if err != nil { + return nil, err + } + return messageType{MessageType: mt, msgDesc: msg}, nil +} + +// WrapFile is present for backwards-compatibility reasons. It calls +// AddSourceInfoToFile and panics if that function returns an error. +// +// Deprecated: Use AddSourceInfoToFile directly instead. The word "wrap" is +// a misnomer since this method does not actually wrap the given value. +// Though unlikely, the operation can technically fail, so the recommended +// function allows the return of an error instead of panic'ing. +func WrapFile(fd protoreflect.FileDescriptor) protoreflect.FileDescriptor { + result, err := AddSourceInfoToFile(fd) + if err != nil { + panic(err) + } + return result +} + +// WrapMessage is present for backwards-compatibility reasons. It calls +// AddSourceInfoToMessage and panics if that function returns an error. +// +// Deprecated: Use AddSourceInfoToMessage directly instead. The word +// "wrap" is a misnomer since this method does not actually wrap the +// given value. Though unlikely, the operation can technically fail, +// so the recommended function allows the return of an error instead +// of panic'ing. +func WrapMessage(md protoreflect.MessageDescriptor) protoreflect.MessageDescriptor { + result, err := AddSourceInfoToMessage(md) + if err != nil { + panic(err) + } + return result +} + +// WrapEnum is present for backwards-compatibility reasons. It calls +// AddSourceInfoToEnum and panics if that function returns an error. +// +// Deprecated: Use AddSourceInfoToEnum directly instead. The word +// "wrap" is a misnomer since this method does not actually wrap the +// given value. Though unlikely, the operation can technically fail, +// so the recommended function allows the return of an error instead +// of panic'ing. +func WrapEnum(ed protoreflect.EnumDescriptor) protoreflect.EnumDescriptor { + result, err := AddSourceInfoToEnum(ed) + if err != nil { + panic(err) + } + return result +} + +// WrapService is present for backwards-compatibility reasons. It calls +// AddSourceInfoToService and panics if that function returns an error. +// +// Deprecated: Use AddSourceInfoToService directly instead. The word +// "wrap" is a misnomer since this method does not actually wrap the +// given value. Though unlikely, the operation can technically fail, +// so the recommended function allows the return of an error instead +// of panic'ing. +func WrapService(sd protoreflect.ServiceDescriptor) protoreflect.ServiceDescriptor { + result, err := AddSourceInfoToService(sd) + if err != nil { + panic(err) + } + return result +} + +// WrapExtensionType is present for backwards-compatibility reasons. It +// calls AddSourceInfoToExtensionType and panics if that function +// returns an error. +// +// Deprecated: Use AddSourceInfoToExtensionType directly instead. The +// word "wrap" is a misnomer since this method does not actually wrap +// the given value. Though unlikely, the operation can technically fail, +// so the recommended function allows the return of an error instead +// of panic'ing. +func WrapExtensionType(xt protoreflect.ExtensionType) protoreflect.ExtensionType { + result, err := AddSourceInfoToExtensionType(xt) + if err != nil { + panic(err) + } + return result +} + +// WrapMessageType is present for backwards-compatibility reasons. It +// calls AddSourceInfoToMessageType and panics if that function returns +// an error. +// +// Deprecated: Use AddSourceInfoToMessageType directly instead. The word +// "wrap" is a misnomer since this method does not actually wrap the +// given value. Though unlikely, the operation can technically fail, so +// the recommended function allows the return of an error instead of +// panic'ing. +func WrapMessageType(mt protoreflect.MessageType) protoreflect.MessageType { + result, err := AddSourceInfoToMessageType(mt) + if err != nil { + panic(err) + } + return result +} + +type extensionType struct { + protoreflect.ExtensionType + extDesc protoreflect.ExtensionDescriptor +} + +func (xt extensionType) TypeDescriptor() protoreflect.ExtensionTypeDescriptor { + return extensionTypeDescriptor{ExtensionDescriptor: xt.extDesc, extType: xt.ExtensionType} +} + +type extensionTypeDescriptor struct { + protoreflect.ExtensionDescriptor + extType protoreflect.ExtensionType +} + +func (xtd extensionTypeDescriptor) Type() protoreflect.ExtensionType { + return extensionType{ExtensionType: xtd.extType, extDesc: xtd.ExtensionDescriptor} +} + +func (xtd extensionTypeDescriptor) Descriptor() protoreflect.ExtensionDescriptor { + return xtd.ExtensionDescriptor +} + +type messageType struct { + protoreflect.MessageType + msgDesc protoreflect.MessageDescriptor +} + +func (mt messageType) Descriptor() protoreflect.MessageDescriptor { + return mt.msgDesc +} + +func updateField(fd protoreflect.FieldDescriptor) (protoreflect.FieldDescriptor, error) { + if xtd, ok := fd.(protoreflect.ExtensionTypeDescriptor); ok { + ext, err := updateField(xtd.Descriptor()) + if err != nil { + return nil, err + } + return extensionTypeDescriptor{ExtensionDescriptor: ext, extType: xtd.Type()}, nil + } + d, err := updateDescriptor(fd) + if err != nil { + return nil, err + } + return d.(protoreflect.FieldDescriptor), nil +} + +func updateDescriptor[D protoreflect.Descriptor](d D) (D, error) { + updatedFile, err := getFile(d.ParentFile()) + if err != nil { + var zero D + return zero, err + } + if updatedFile == d.ParentFile() { + // no change + return d, nil + } + updated := findDescriptor(updatedFile, d) + result, ok := updated.(D) + if !ok { + var zero D + return zero, fmt.Errorf("updated result is type %T which could not be converted to %T", updated, result) + } + return result, nil +} + +func findDescriptor(fd protoreflect.FileDescriptor, d protoreflect.Descriptor) protoreflect.Descriptor { + if d == nil { + return nil + } + if _, isFile := d.(protoreflect.FileDescriptor); isFile { + return fd + } + if d.Parent() == nil { + return d + } + switch d := d.(type) { + case protoreflect.MessageDescriptor: + parent := findDescriptor(fd, d.Parent()).(messageContainer) + return parent.Messages().Get(d.Index()) + case protoreflect.FieldDescriptor: + if d.IsExtension() { + parent := findDescriptor(fd, d.Parent()).(extensionContainer) + return parent.Extensions().Get(d.Index()) + } else { + parent := findDescriptor(fd, d.Parent()).(fieldContainer) + return parent.Fields().Get(d.Index()) + } + case protoreflect.OneofDescriptor: + parent := findDescriptor(fd, d.Parent()).(oneofContainer) + return parent.Oneofs().Get(d.Index()) + case protoreflect.EnumDescriptor: + parent := findDescriptor(fd, d.Parent()).(enumContainer) + return parent.Enums().Get(d.Index()) + case protoreflect.EnumValueDescriptor: + parent := findDescriptor(fd, d.Parent()).(enumValueContainer) + return parent.Values().Get(d.Index()) + case protoreflect.ServiceDescriptor: + parent := findDescriptor(fd, d.Parent()).(serviceContainer) + return parent.Services().Get(d.Index()) + case protoreflect.MethodDescriptor: + parent := findDescriptor(fd, d.Parent()).(methodContainer) + return parent.Methods().Get(d.Index()) + } + return d +} + +type messageContainer interface { + Messages() protoreflect.MessageDescriptors +} + +type extensionContainer interface { + Extensions() protoreflect.ExtensionDescriptors +} + +type fieldContainer interface { + Fields() protoreflect.FieldDescriptors +} + +type oneofContainer interface { + Oneofs() protoreflect.OneofDescriptors +} + +type enumContainer interface { + Enums() protoreflect.EnumDescriptors +} + +type enumValueContainer interface { + Values() protoreflect.EnumValueDescriptors +} + +type serviceContainer interface { + Services() protoreflect.ServiceDescriptors +} + +type methodContainer interface { + Methods() protoreflect.MethodDescriptors +} diff --git a/vendor/github.com/jhump/protoreflect/desc/sourceinfo/wrappers.go b/vendor/github.com/jhump/protoreflect/desc/sourceinfo/wrappers.go deleted file mode 100644 index 3106eaad2fe..00000000000 --- a/vendor/github.com/jhump/protoreflect/desc/sourceinfo/wrappers.go +++ /dev/null @@ -1,636 +0,0 @@ -package sourceinfo - -import ( - "fmt" - "google.golang.org/protobuf/reflect/protoreflect" -) - -// These are wrappers around the various interfaces in the -// google.golang.org/protobuf/reflect/protoreflect that all -// make sure to return a FileDescriptor that includes source -// code info. - -type fileDescriptor struct { - protoreflect.FileDescriptor - locs protoreflect.SourceLocations -} - -func (f fileDescriptor) Edition() int32 { - ed, ok := f.FileDescriptor.(interface{ Edition() int32 }) - if ok { - return ed.Edition() - } - return 0 -} - -func (f fileDescriptor) ParentFile() protoreflect.FileDescriptor { - return f -} - -func (f fileDescriptor) Parent() protoreflect.Descriptor { - return nil -} - -func (f fileDescriptor) Imports() protoreflect.FileImports { - return imports{f.FileDescriptor.Imports()} -} - -func (f fileDescriptor) Messages() protoreflect.MessageDescriptors { - return messages{f.FileDescriptor.Messages()} -} - -func (f fileDescriptor) Enums() protoreflect.EnumDescriptors { - return enums{f.FileDescriptor.Enums()} -} - -func (f fileDescriptor) Extensions() protoreflect.ExtensionDescriptors { - return extensions{f.FileDescriptor.Extensions()} -} - -func (f fileDescriptor) Services() protoreflect.ServiceDescriptors { - return services{f.FileDescriptor.Services()} -} - -func (f fileDescriptor) SourceLocations() protoreflect.SourceLocations { - return f.locs -} - -type imports struct { - protoreflect.FileImports -} - -func (im imports) Get(i int) protoreflect.FileImport { - fi := im.FileImports.Get(i) - return protoreflect.FileImport{ - FileDescriptor: getFile(fi.FileDescriptor), - IsPublic: fi.IsPublic, - IsWeak: fi.IsWeak, - } -} - -type messages struct { - protoreflect.MessageDescriptors -} - -func (m messages) Get(i int) protoreflect.MessageDescriptor { - return messageDescriptor{m.MessageDescriptors.Get(i)} -} - -func (m messages) ByName(n protoreflect.Name) protoreflect.MessageDescriptor { - return messageDescriptor{m.MessageDescriptors.ByName(n)} -} - -type enums struct { - protoreflect.EnumDescriptors -} - -func (e enums) Get(i int) protoreflect.EnumDescriptor { - return enumDescriptor{e.EnumDescriptors.Get(i)} -} - -func (e enums) ByName(n protoreflect.Name) protoreflect.EnumDescriptor { - return enumDescriptor{e.EnumDescriptors.ByName(n)} -} - -type extensions struct { - protoreflect.ExtensionDescriptors -} - -func (e extensions) Get(i int) protoreflect.ExtensionDescriptor { - d := e.ExtensionDescriptors.Get(i) - if ed, ok := d.(protoreflect.ExtensionTypeDescriptor); ok { - return extensionDescriptor{ed} - } - return fieldDescriptor{d} -} - -func (e extensions) ByName(n protoreflect.Name) protoreflect.ExtensionDescriptor { - d := e.ExtensionDescriptors.ByName(n) - if ed, ok := d.(protoreflect.ExtensionTypeDescriptor); ok { - return extensionDescriptor{ed} - } - return fieldDescriptor{d} -} - -type services struct { - protoreflect.ServiceDescriptors -} - -func (s services) Get(i int) protoreflect.ServiceDescriptor { - return serviceDescriptor{s.ServiceDescriptors.Get(i)} -} - -func (s services) ByName(n protoreflect.Name) protoreflect.ServiceDescriptor { - return serviceDescriptor{s.ServiceDescriptors.ByName(n)} -} - -type messageDescriptor struct { - protoreflect.MessageDescriptor -} - -func (m messageDescriptor) ParentFile() protoreflect.FileDescriptor { - return getFile(m.MessageDescriptor.ParentFile()) -} - -func (m messageDescriptor) Parent() protoreflect.Descriptor { - d := m.MessageDescriptor.Parent() - switch d := d.(type) { - case protoreflect.MessageDescriptor: - return messageDescriptor{d} - case protoreflect.FileDescriptor: - return getFile(d) - case nil: - return nil - default: - panic(fmt.Sprintf("unexpected descriptor type %T", d)) - } -} - -func (m messageDescriptor) Fields() protoreflect.FieldDescriptors { - return fields{m.MessageDescriptor.Fields()} -} - -func (m messageDescriptor) Oneofs() protoreflect.OneofDescriptors { - return oneOfs{m.MessageDescriptor.Oneofs()} -} - -func (m messageDescriptor) Enums() protoreflect.EnumDescriptors { - return enums{m.MessageDescriptor.Enums()} -} - -func (m messageDescriptor) Messages() protoreflect.MessageDescriptors { - return messages{m.MessageDescriptor.Messages()} -} - -func (m messageDescriptor) Extensions() protoreflect.ExtensionDescriptors { - return extensions{m.MessageDescriptor.Extensions()} -} - -type fields struct { - protoreflect.FieldDescriptors -} - -func (f fields) Get(i int) protoreflect.FieldDescriptor { - return fieldDescriptor{f.FieldDescriptors.Get(i)} -} - -func (f fields) ByName(n protoreflect.Name) protoreflect.FieldDescriptor { - return fieldDescriptor{f.FieldDescriptors.ByName(n)} -} - -func (f fields) ByJSONName(n string) protoreflect.FieldDescriptor { - return fieldDescriptor{f.FieldDescriptors.ByJSONName(n)} -} - -func (f fields) ByTextName(n string) protoreflect.FieldDescriptor { - return fieldDescriptor{f.FieldDescriptors.ByTextName(n)} -} - -func (f fields) ByNumber(n protoreflect.FieldNumber) protoreflect.FieldDescriptor { - return fieldDescriptor{f.FieldDescriptors.ByNumber(n)} -} - -type oneOfs struct { - protoreflect.OneofDescriptors -} - -func (o oneOfs) Get(i int) protoreflect.OneofDescriptor { - return oneOfDescriptor{o.OneofDescriptors.Get(i)} -} - -func (o oneOfs) ByName(n protoreflect.Name) protoreflect.OneofDescriptor { - return oneOfDescriptor{o.OneofDescriptors.ByName(n)} -} - -type fieldDescriptor struct { - protoreflect.FieldDescriptor -} - -func (f fieldDescriptor) ParentFile() protoreflect.FileDescriptor { - return getFile(f.FieldDescriptor.ParentFile()) -} - -func (f fieldDescriptor) Parent() protoreflect.Descriptor { - d := f.FieldDescriptor.Parent() - switch d := d.(type) { - case protoreflect.MessageDescriptor: - return messageDescriptor{d} - case protoreflect.FileDescriptor: - return getFile(d) - case nil: - return nil - default: - panic(fmt.Sprintf("unexpected descriptor type %T", d)) - } -} - -func (f fieldDescriptor) MapKey() protoreflect.FieldDescriptor { - fd := f.FieldDescriptor.MapKey() - if fd == nil { - return nil - } - return fieldDescriptor{fd} -} - -func (f fieldDescriptor) MapValue() protoreflect.FieldDescriptor { - fd := f.FieldDescriptor.MapValue() - if fd == nil { - return nil - } - return fieldDescriptor{fd} -} - -func (f fieldDescriptor) DefaultEnumValue() protoreflect.EnumValueDescriptor { - ed := f.FieldDescriptor.DefaultEnumValue() - if ed == nil { - return nil - } - return enumValueDescriptor{ed} -} - -func (f fieldDescriptor) ContainingOneof() protoreflect.OneofDescriptor { - od := f.FieldDescriptor.ContainingOneof() - if od == nil { - return nil - } - return oneOfDescriptor{od} -} - -func (f fieldDescriptor) ContainingMessage() protoreflect.MessageDescriptor { - return messageDescriptor{f.FieldDescriptor.ContainingMessage()} -} - -func (f fieldDescriptor) Enum() protoreflect.EnumDescriptor { - ed := f.FieldDescriptor.Enum() - if ed == nil { - return nil - } - return enumDescriptor{ed} -} - -func (f fieldDescriptor) Message() protoreflect.MessageDescriptor { - md := f.FieldDescriptor.Message() - if md == nil { - return nil - } - return messageDescriptor{md} -} - -type oneOfDescriptor struct { - protoreflect.OneofDescriptor -} - -func (o oneOfDescriptor) ParentFile() protoreflect.FileDescriptor { - return getFile(o.OneofDescriptor.ParentFile()) -} - -func (o oneOfDescriptor) Parent() protoreflect.Descriptor { - d := o.OneofDescriptor.Parent() - switch d := d.(type) { - case protoreflect.MessageDescriptor: - return messageDescriptor{d} - case nil: - return nil - default: - panic(fmt.Sprintf("unexpected descriptor type %T", d)) - } -} - -func (o oneOfDescriptor) Fields() protoreflect.FieldDescriptors { - return fields{o.OneofDescriptor.Fields()} -} - -type enumDescriptor struct { - protoreflect.EnumDescriptor -} - -func (e enumDescriptor) ParentFile() protoreflect.FileDescriptor { - return getFile(e.EnumDescriptor.ParentFile()) -} - -func (e enumDescriptor) Parent() protoreflect.Descriptor { - d := e.EnumDescriptor.Parent() - switch d := d.(type) { - case protoreflect.MessageDescriptor: - return messageDescriptor{d} - case protoreflect.FileDescriptor: - return getFile(d) - case nil: - return nil - default: - panic(fmt.Sprintf("unexpected descriptor type %T", d)) - } -} - -func (e enumDescriptor) Values() protoreflect.EnumValueDescriptors { - return enumValues{e.EnumDescriptor.Values()} -} - -type enumValues struct { - protoreflect.EnumValueDescriptors -} - -func (e enumValues) Get(i int) protoreflect.EnumValueDescriptor { - return enumValueDescriptor{e.EnumValueDescriptors.Get(i)} -} - -func (e enumValues) ByName(n protoreflect.Name) protoreflect.EnumValueDescriptor { - return enumValueDescriptor{e.EnumValueDescriptors.ByName(n)} -} - -func (e enumValues) ByNumber(n protoreflect.EnumNumber) protoreflect.EnumValueDescriptor { - return enumValueDescriptor{e.EnumValueDescriptors.ByNumber(n)} -} - -type enumValueDescriptor struct { - protoreflect.EnumValueDescriptor -} - -func (e enumValueDescriptor) ParentFile() protoreflect.FileDescriptor { - return getFile(e.EnumValueDescriptor.ParentFile()) -} - -func (e enumValueDescriptor) Parent() protoreflect.Descriptor { - d := e.EnumValueDescriptor.Parent() - switch d := d.(type) { - case protoreflect.EnumDescriptor: - return enumDescriptor{d} - case nil: - return nil - default: - panic(fmt.Sprintf("unexpected descriptor type %T", d)) - } -} - -type extensionDescriptor struct { - protoreflect.ExtensionTypeDescriptor -} - -func (e extensionDescriptor) ParentFile() protoreflect.FileDescriptor { - return getFile(e.ExtensionTypeDescriptor.ParentFile()) -} - -func (e extensionDescriptor) Parent() protoreflect.Descriptor { - d := e.ExtensionTypeDescriptor.Parent() - switch d := d.(type) { - case protoreflect.MessageDescriptor: - return messageDescriptor{d} - case protoreflect.FileDescriptor: - return getFile(d) - case nil: - return nil - default: - panic(fmt.Sprintf("unexpected descriptor type %T", d)) - } -} - -func (e extensionDescriptor) MapKey() protoreflect.FieldDescriptor { - fd := e.ExtensionTypeDescriptor.MapKey() - if fd == nil { - return nil - } - return fieldDescriptor{fd} -} - -func (e extensionDescriptor) MapValue() protoreflect.FieldDescriptor { - fd := e.ExtensionTypeDescriptor.MapValue() - if fd == nil { - return nil - } - return fieldDescriptor{fd} -} - -func (e extensionDescriptor) DefaultEnumValue() protoreflect.EnumValueDescriptor { - ed := e.ExtensionTypeDescriptor.DefaultEnumValue() - if ed == nil { - return nil - } - return enumValueDescriptor{ed} -} - -func (e extensionDescriptor) ContainingOneof() protoreflect.OneofDescriptor { - od := e.ExtensionTypeDescriptor.ContainingOneof() - if od == nil { - return nil - } - return oneOfDescriptor{od} -} - -func (e extensionDescriptor) ContainingMessage() protoreflect.MessageDescriptor { - return messageDescriptor{e.ExtensionTypeDescriptor.ContainingMessage()} -} - -func (e extensionDescriptor) Enum() protoreflect.EnumDescriptor { - ed := e.ExtensionTypeDescriptor.Enum() - if ed == nil { - return nil - } - return enumDescriptor{ed} -} - -func (e extensionDescriptor) Message() protoreflect.MessageDescriptor { - md := e.ExtensionTypeDescriptor.Message() - if md == nil { - return nil - } - return messageDescriptor{md} -} - -func (e extensionDescriptor) Descriptor() protoreflect.ExtensionDescriptor { - return e -} - -var _ protoreflect.ExtensionTypeDescriptor = extensionDescriptor{} - -type serviceDescriptor struct { - protoreflect.ServiceDescriptor -} - -func (s serviceDescriptor) ParentFile() protoreflect.FileDescriptor { - return getFile(s.ServiceDescriptor.ParentFile()) -} - -func (s serviceDescriptor) Parent() protoreflect.Descriptor { - d := s.ServiceDescriptor.Parent() - switch d := d.(type) { - case protoreflect.FileDescriptor: - return getFile(d) - case nil: - return nil - default: - panic(fmt.Sprintf("unexpected descriptor type %T", d)) - } -} - -func (s serviceDescriptor) Methods() protoreflect.MethodDescriptors { - return methods{s.ServiceDescriptor.Methods()} -} - -type methods struct { - protoreflect.MethodDescriptors -} - -func (m methods) Get(i int) protoreflect.MethodDescriptor { - return methodDescriptor{m.MethodDescriptors.Get(i)} -} - -func (m methods) ByName(n protoreflect.Name) protoreflect.MethodDescriptor { - return methodDescriptor{m.MethodDescriptors.ByName(n)} -} - -type methodDescriptor struct { - protoreflect.MethodDescriptor -} - -func (m methodDescriptor) ParentFile() protoreflect.FileDescriptor { - return getFile(m.MethodDescriptor.ParentFile()) -} - -func (m methodDescriptor) Parent() protoreflect.Descriptor { - d := m.MethodDescriptor.Parent() - switch d := d.(type) { - case protoreflect.ServiceDescriptor: - return serviceDescriptor{d} - case nil: - return nil - default: - panic(fmt.Sprintf("unexpected descriptor type %T", d)) - } -} - -func (m methodDescriptor) Input() protoreflect.MessageDescriptor { - return messageDescriptor{m.MethodDescriptor.Input()} -} - -func (m methodDescriptor) Output() protoreflect.MessageDescriptor { - return messageDescriptor{m.MethodDescriptor.Output()} -} - -type extensionType struct { - protoreflect.ExtensionType -} - -func (e extensionType) TypeDescriptor() protoreflect.ExtensionTypeDescriptor { - return extensionDescriptor{e.ExtensionType.TypeDescriptor()} -} - -type messageType struct { - protoreflect.MessageType -} - -func (m messageType) Descriptor() protoreflect.MessageDescriptor { - return messageDescriptor{m.MessageType.Descriptor()} -} - -// WrapFile wraps the given file descriptor so that it will include source -// code info that was registered with this package if the given file was -// processed with protoc-gen-gosrcinfo. Returns fd without wrapping if fd -// already contains source code info. -func WrapFile(fd protoreflect.FileDescriptor) protoreflect.FileDescriptor { - if wrapper, ok := fd.(fileDescriptor); ok { - // already wrapped - return wrapper - } - if fd.SourceLocations().Len() > 0 { - // no need to wrap since it includes source info already - return fd - } - return getFile(fd) -} - -// WrapMessage wraps the given message descriptor so that it will include source -// code info that was registered with this package if the file it is defined in -// was processed with protoc-gen-gosrcinfo. Returns md without wrapping if md's -// parent file already contains source code info. -func WrapMessage(md protoreflect.MessageDescriptor) protoreflect.MessageDescriptor { - if wrapper, ok := md.(messageDescriptor); ok { - // already wrapped - return wrapper - } - if md.ParentFile().SourceLocations().Len() > 0 { - // no need to wrap since it includes source info already - return md - } - if !canWrap(md) { - return md - } - return messageDescriptor{md} -} - -// WrapEnum wraps the given enum descriptor so that it will include source -// code info that was registered with this package if the file it is defined in -// was processed with protoc-gen-gosrcinfo. Returns ed without wrapping if ed's -// parent file already contains source code info. -func WrapEnum(ed protoreflect.EnumDescriptor) protoreflect.EnumDescriptor { - if wrapper, ok := ed.(enumDescriptor); ok { - // already wrapped - return wrapper - } - if ed.ParentFile().SourceLocations().Len() > 0 { - // no need to wrap since it includes source info already - return ed - } - if !canWrap(ed) { - return ed - } - return enumDescriptor{ed} -} - -// WrapService wraps the given service descriptor so that it will include source -// code info that was registered with this package if the file it is defined in -// was processed with protoc-gen-gosrcinfo. Returns sd without wrapping if sd's -// parent file already contains source code info. -func WrapService(sd protoreflect.ServiceDescriptor) protoreflect.ServiceDescriptor { - if wrapper, ok := sd.(serviceDescriptor); ok { - // already wrapped - return wrapper - } - if sd.ParentFile().SourceLocations().Len() > 0 { - // no need to wrap since it includes source info already - return sd - } - if !canWrap(sd) { - return sd - } - return serviceDescriptor{sd} -} - -// WrapExtensionType wraps the given extension type so that its associated -// descriptor will include source code info that was registered with this package -// if the file it is defined in was processed with protoc-gen-gosrcinfo. Returns -// xt without wrapping if the parent file of xt's descriptor already contains -// source code info. -func WrapExtensionType(xt protoreflect.ExtensionType) protoreflect.ExtensionType { - if wrapper, ok := xt.(extensionType); ok { - // already wrapped - return wrapper - } - if xt.TypeDescriptor().ParentFile().SourceLocations().Len() > 0 { - // no need to wrap since it includes source info already - return xt - } - if !canWrap(xt.TypeDescriptor()) { - return xt - } - return extensionType{xt} -} - -// WrapMessageType wraps the given message type so that its associated -// descriptor will include source code info that was registered with this package -// if the file it is defined in was processed with protoc-gen-gosrcinfo. Returns -// mt without wrapping if the parent file of mt's descriptor already contains -// source code info. -func WrapMessageType(mt protoreflect.MessageType) protoreflect.MessageType { - if wrapper, ok := mt.(messageType); ok { - // already wrapped - return wrapper - } - if mt.Descriptor().ParentFile().SourceLocations().Len() > 0 { - // no need to wrap since it includes source info already - return mt - } - if !canWrap(mt.Descriptor()) { - return mt - } - return messageType{mt} -} diff --git a/vendor/github.com/jhump/protoreflect/grpcreflect/adapt.go b/vendor/github.com/jhump/protoreflect/grpcreflect/adapt.go index 0d5615fee0e..661b9250fb4 100644 --- a/vendor/github.com/jhump/protoreflect/grpcreflect/adapt.go +++ b/vendor/github.com/jhump/protoreflect/grpcreflect/adapt.go @@ -83,48 +83,48 @@ func toV1AlphaRequest(v1 *refv1.ServerReflectionRequest) *refv1alpha.ServerRefle return &v1alpha } -func toV1AlphaResponse(v1 *refv1.ServerReflectionResponse) *refv1alpha.ServerReflectionResponse { - var v1alpha refv1alpha.ServerReflectionResponse - v1alpha.ValidHost = v1.ValidHost - if v1.OriginalRequest != nil { - v1alpha.OriginalRequest = toV1AlphaRequest(v1.OriginalRequest) +func toV1Response(v1alpha *refv1alpha.ServerReflectionResponse) *refv1.ServerReflectionResponse { + var v1 refv1.ServerReflectionResponse + v1.ValidHost = v1alpha.ValidHost + if v1alpha.OriginalRequest != nil { + v1.OriginalRequest = toV1Request(v1alpha.OriginalRequest) } - switch mr := v1.MessageResponse.(type) { - case *refv1.ServerReflectionResponse_FileDescriptorResponse: + switch mr := v1alpha.MessageResponse.(type) { + case *refv1alpha.ServerReflectionResponse_FileDescriptorResponse: if mr != nil { - v1alpha.MessageResponse = &refv1alpha.ServerReflectionResponse_FileDescriptorResponse{ - FileDescriptorResponse: &refv1alpha.FileDescriptorResponse{ + v1.MessageResponse = &refv1.ServerReflectionResponse_FileDescriptorResponse{ + FileDescriptorResponse: &refv1.FileDescriptorResponse{ FileDescriptorProto: mr.FileDescriptorResponse.GetFileDescriptorProto(), }, } } - case *refv1.ServerReflectionResponse_AllExtensionNumbersResponse: + case *refv1alpha.ServerReflectionResponse_AllExtensionNumbersResponse: if mr != nil { - v1alpha.MessageResponse = &refv1alpha.ServerReflectionResponse_AllExtensionNumbersResponse{ - AllExtensionNumbersResponse: &refv1alpha.ExtensionNumberResponse{ + v1.MessageResponse = &refv1.ServerReflectionResponse_AllExtensionNumbersResponse{ + AllExtensionNumbersResponse: &refv1.ExtensionNumberResponse{ BaseTypeName: mr.AllExtensionNumbersResponse.GetBaseTypeName(), ExtensionNumber: mr.AllExtensionNumbersResponse.GetExtensionNumber(), }, } } - case *refv1.ServerReflectionResponse_ListServicesResponse: + case *refv1alpha.ServerReflectionResponse_ListServicesResponse: if mr != nil { - svcs := make([]*refv1alpha.ServiceResponse, len(mr.ListServicesResponse.GetService())) + svcs := make([]*refv1.ServiceResponse, len(mr.ListServicesResponse.GetService())) for i, svc := range mr.ListServicesResponse.GetService() { - svcs[i] = &refv1alpha.ServiceResponse{ + svcs[i] = &refv1.ServiceResponse{ Name: svc.GetName(), } } - v1alpha.MessageResponse = &refv1alpha.ServerReflectionResponse_ListServicesResponse{ - ListServicesResponse: &refv1alpha.ListServiceResponse{ + v1.MessageResponse = &refv1.ServerReflectionResponse_ListServicesResponse{ + ListServicesResponse: &refv1.ListServiceResponse{ Service: svcs, }, } } - case *refv1.ServerReflectionResponse_ErrorResponse: + case *refv1alpha.ServerReflectionResponse_ErrorResponse: if mr != nil { - v1alpha.MessageResponse = &refv1alpha.ServerReflectionResponse_ErrorResponse{ - ErrorResponse: &refv1alpha.ErrorResponse{ + v1.MessageResponse = &refv1.ServerReflectionResponse_ErrorResponse{ + ErrorResponse: &refv1.ErrorResponse{ ErrorCode: mr.ErrorResponse.GetErrorCode(), ErrorMessage: mr.ErrorResponse.GetErrorMessage(), }, @@ -133,5 +133,5 @@ func toV1AlphaResponse(v1 *refv1.ServerReflectionResponse) *refv1alpha.ServerRef default: // no value set } - return &v1alpha + return &v1 } diff --git a/vendor/github.com/jhump/protoreflect/grpcreflect/client.go b/vendor/github.com/jhump/protoreflect/grpcreflect/client.go index cb6bf568551..1a35540ca51 100644 --- a/vendor/github.com/jhump/protoreflect/grpcreflect/client.go +++ b/vendor/github.com/jhump/protoreflect/grpcreflect/client.go @@ -7,6 +7,7 @@ import ( "io" "reflect" "runtime" + "sort" "sync" "sync/atomic" "time" @@ -17,6 +18,9 @@ import ( refv1 "google.golang.org/grpc/reflection/grpc_reflection_v1" refv1alpha "google.golang.org/grpc/reflection/grpc_reflection_v1alpha" "google.golang.org/grpc/status" + "google.golang.org/protobuf/reflect/protodesc" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" "google.golang.org/protobuf/types/descriptorpb" "github.com/jhump/protoreflect/desc" @@ -134,26 +138,37 @@ type extDesc struct { extensionNumber int32 } +type resolvers struct { + descriptorResolver protodesc.Resolver + extensionResolver protoregistry.ExtensionTypeResolver +} + +type fileEntry struct { + fd *desc.FileDescriptor + fallback bool +} + // Client is a client connection to a server for performing reflection calls // and resolving remote symbols. type Client struct { - ctx context.Context - now func() time.Time - stubV1 refv1.ServerReflectionClient - stubV1Alpha refv1alpha.ServerReflectionClient - allowMissing atomic.Bool + ctx context.Context + now func() time.Time + stubV1 refv1.ServerReflectionClient + stubV1Alpha refv1alpha.ServerReflectionClient + allowMissing atomic.Bool + fallbackResolver atomic.Pointer[resolvers] connMu sync.Mutex cancel context.CancelFunc - stream refv1alpha.ServerReflection_ServerReflectionInfoClient + stream refv1.ServerReflection_ServerReflectionInfoClient useV1Alpha bool lastTriedV1 time.Time cacheMu sync.RWMutex protosByName map[string]*descriptorpb.FileDescriptorProto - filesByName map[string]*desc.FileDescriptor - filesBySymbol map[string]*desc.FileDescriptor - filesByExtension map[extDesc]*desc.FileDescriptor + filesByName map[string]fileEntry + filesBySymbol map[string]fileEntry + filesByExtension map[extDesc]fileEntry } // NewClient creates a new Client with the given root context and using the @@ -173,6 +188,12 @@ func NewClientV1Alpha(ctx context.Context, stub refv1alpha.ServerReflectionClien return newClient(ctx, nil, stub) } +// NewClientV1 creates a new Client using the v1 version of reflection with the +// given root context and using the given RPC stub for talking to the server. +func NewClientV1(ctx context.Context, stub refv1.ServerReflectionClient) *Client { + return newClient(ctx, stub, nil) +} + func newClient(ctx context.Context, stubv1 refv1.ServerReflectionClient, stubv1alpha refv1alpha.ServerReflectionClient) *Client { cr := &Client{ ctx: ctx, @@ -180,9 +201,9 @@ func newClient(ctx context.Context, stubv1 refv1.ServerReflectionClient, stubv1a stubV1: stubv1, stubV1Alpha: stubv1alpha, protosByName: map[string]*descriptorpb.FileDescriptorProto{}, - filesByName: map[string]*desc.FileDescriptor{}, - filesBySymbol: map[string]*desc.FileDescriptor{}, - filesByExtension: map[extDesc]*desc.FileDescriptor{}, + filesByName: map[string]fileEntry{}, + filesBySymbol: map[string]fileEntry{}, + filesByExtension: map[extDesc]fileEntry{}, } // don't leak a grpc stream runtime.SetFinalizer(cr, (*Client).Reset) @@ -214,28 +235,49 @@ func (cr *Client) AllowMissingFileDescriptors() { cr.allowMissing.Store(true) } -// TODO: We should also have a NewClientV1. However that should not refer to internal -// generated code. So it will have to wait until the grpc-go team fixes this issue: -// https://github.com/grpc/grpc-go/issues/5684 +// AllowFallbackResolver configures the client to allow falling back to the +// given resolvers if the server is unable to supply descriptors for a particular +// query. This allows working around issues where servers' reflection service +// provides an incomplete set of descriptors, but the client has knowledge of +// the missing descriptors from another source. It is usually most appropriate +// to pass [protoregistry.GlobalFiles] and [protoregistry.GlobalTypes] as the +// resolver values. +// +// The first value is used as a fallback for FileByFilename and FileContainingSymbol +// queries. The second value is used as a fallback for FileContainingExtension. It +// can also be used as a fallback for AllExtensionNumbersForType if it provides +// a method with the following signature (which *[protoregistry.Types] provides): +// +// RangeExtensionsByMessage(message protoreflect.FullName, f func(protoreflect.ExtensionType) bool) +func (cr *Client) AllowFallbackResolver(descriptors protodesc.Resolver, exts protoregistry.ExtensionTypeResolver) { + if descriptors == nil && exts == nil { + cr.fallbackResolver.Store(nil) + } else { + cr.fallbackResolver.Store(&resolvers{ + descriptorResolver: descriptors, + extensionResolver: exts, + }) + } +} // FileByFilename asks the server for a file descriptor for the proto file with // the given name. func (cr *Client) FileByFilename(filename string) (*desc.FileDescriptor, error) { // hit the cache first cr.cacheMu.RLock() - if fd, ok := cr.filesByName[filename]; ok { + if entry, ok := cr.filesByName[filename]; ok { cr.cacheMu.RUnlock() - return fd, nil + return entry.fd, nil } + // not there? see if we've downloaded the proto fdp, ok := cr.protosByName[filename] cr.cacheMu.RUnlock() - // not there? see if we've downloaded the proto if ok { return cr.descriptorFromProto(fdp) } - req := &refv1alpha.ServerReflectionRequest{ - MessageRequest: &refv1alpha.ServerReflectionRequest_FileByFilename{ + req := &refv1.ServerReflectionRequest{ + MessageRequest: &refv1.ServerReflectionRequest_FileByFilename{ FileByFilename: filename, }, } @@ -245,23 +287,37 @@ func (cr *Client) FileByFilename(filename string) (*desc.FileDescriptor, error) fd, err := cr.getAndCacheFileDescriptors(req, filename, "", accept) if isNotFound(err) { - // file not found? see if we can look up via alternate name + // File not found? see if we can look up via alternate name if alternate, ok := internal.StdFileAliases[filename]; ok { - req := &refv1alpha.ServerReflectionRequest{ - MessageRequest: &refv1alpha.ServerReflectionRequest_FileByFilename{ + req := &refv1.ServerReflectionRequest{ + MessageRequest: &refv1.ServerReflectionRequest_FileByFilename{ FileByFilename: alternate, }, } fd, err = cr.getAndCacheFileDescriptors(req, alternate, filename, accept) - if isNotFound(err) { - err = fileNotFound(filename, nil) + } + } + if isNotFound(err) { + // Still no? See if we can use a fallback resolver + resolver := cr.fallbackResolver.Load() + if resolver != nil && resolver.descriptorResolver != nil { + fileDesc, fallbackErr := resolver.descriptorResolver.FindFileByPath(filename) + if fallbackErr == nil { + var wrapErr error + fd, wrapErr = desc.WrapFile(fileDesc) + if wrapErr == nil { + fd = cr.cacheFile(fd, true) + err = nil // clear error since we've succeeded via the fallback + } } - } else { - err = fileNotFound(filename, nil) } + } + if isNotFound(err) { + err = fileNotFound(filename, nil) } else if e, ok := err.(*elementNotFoundError); ok { err = fileNotFound(filename, e) } + return fd, err } @@ -270,14 +326,14 @@ func (cr *Client) FileByFilename(filename string) (*desc.FileDescriptor, error) func (cr *Client) FileContainingSymbol(symbol string) (*desc.FileDescriptor, error) { // hit the cache first cr.cacheMu.RLock() - fd, ok := cr.filesBySymbol[symbol] + entry, ok := cr.filesBySymbol[symbol] cr.cacheMu.RUnlock() if ok { - return fd, nil + return entry.fd, nil } - req := &refv1alpha.ServerReflectionRequest{ - MessageRequest: &refv1alpha.ServerReflectionRequest_FileContainingSymbol{ + req := &refv1.ServerReflectionRequest{ + MessageRequest: &refv1.ServerReflectionRequest_FileContainingSymbol{ FileContainingSymbol: symbol, }, } @@ -285,6 +341,21 @@ func (cr *Client) FileContainingSymbol(symbol string) (*desc.FileDescriptor, err return fd.FindSymbol(symbol) != nil } fd, err := cr.getAndCacheFileDescriptors(req, "", "", accept) + if isNotFound(err) { + // Symbol not found? See if we can use a fallback resolver + resolver := cr.fallbackResolver.Load() + if resolver != nil && resolver.descriptorResolver != nil { + d, fallbackErr := resolver.descriptorResolver.FindDescriptorByName(protoreflect.FullName(symbol)) + if fallbackErr == nil { + var wrapErr error + fd, wrapErr = desc.WrapFile(d.ParentFile()) + if wrapErr == nil { + fd = cr.cacheFile(fd, true) + err = nil // clear error since we've succeeded via the fallback + } + } + } + } if isNotFound(err) { err = symbolNotFound(symbol, symbolTypeUnknown, nil) } else if e, ok := err.(*elementNotFoundError); ok { @@ -299,15 +370,15 @@ func (cr *Client) FileContainingSymbol(symbol string) (*desc.FileDescriptor, err func (cr *Client) FileContainingExtension(extendedMessageName string, extensionNumber int32) (*desc.FileDescriptor, error) { // hit the cache first cr.cacheMu.RLock() - fd, ok := cr.filesByExtension[extDesc{extendedMessageName, extensionNumber}] + entry, ok := cr.filesByExtension[extDesc{extendedMessageName, extensionNumber}] cr.cacheMu.RUnlock() if ok { - return fd, nil + return entry.fd, nil } - req := &refv1alpha.ServerReflectionRequest{ - MessageRequest: &refv1alpha.ServerReflectionRequest_FileContainingExtension{ - FileContainingExtension: &refv1alpha.ExtensionRequest{ + req := &refv1.ServerReflectionRequest{ + MessageRequest: &refv1.ServerReflectionRequest_FileContainingExtension{ + FileContainingExtension: &refv1.ExtensionRequest{ ContainingType: extendedMessageName, ExtensionNumber: extensionNumber, }, @@ -317,6 +388,21 @@ func (cr *Client) FileContainingExtension(extendedMessageName string, extensionN return fd.FindExtension(extendedMessageName, extensionNumber) != nil } fd, err := cr.getAndCacheFileDescriptors(req, "", "", accept) + if isNotFound(err) { + // Extension not found? See if we can use a fallback resolver + resolver := cr.fallbackResolver.Load() + if resolver != nil && resolver.extensionResolver != nil { + extType, fallbackErr := resolver.extensionResolver.FindExtensionByNumber(protoreflect.FullName(extendedMessageName), protoreflect.FieldNumber(extensionNumber)) + if fallbackErr == nil { + var wrapErr error + fd, wrapErr = desc.WrapFile(extType.TypeDescriptor().ParentFile()) + if wrapErr == nil { + fd = cr.cacheFile(fd, true) + err = nil // clear error since we've succeeded via the fallback + } + } + } + } if isNotFound(err) { err = extensionNotFound(extendedMessageName, extensionNumber, nil) } else if e, ok := err.(*elementNotFoundError); ok { @@ -325,7 +411,7 @@ func (cr *Client) FileContainingExtension(extendedMessageName string, extensionN return fd, err } -func (cr *Client) getAndCacheFileDescriptors(req *refv1alpha.ServerReflectionRequest, expectedName, alias string, accept func(*desc.FileDescriptor) bool) (*desc.FileDescriptor, error) { +func (cr *Client) getAndCacheFileDescriptors(req *refv1.ServerReflectionRequest, expectedName, alias string, accept func(*desc.FileDescriptor) bool) (*desc.FileDescriptor, error) { resp, err := cr.send(req) if err != nil { return nil, err @@ -412,96 +498,159 @@ func (cr *Client) descriptorFromProto(fd *descriptorpb.FileDescriptorProto) (*de } return nil, err } - d = cr.cacheFile(d) + d = cr.cacheFile(d, false) return d, nil } -func (cr *Client) cacheFile(fd *desc.FileDescriptor) *desc.FileDescriptor { +func (cr *Client) cacheFile(fd *desc.FileDescriptor, fallback bool) *desc.FileDescriptor { cr.cacheMu.Lock() defer cr.cacheMu.Unlock() - // cache file descriptor by name, but don't overwrite existing entry - // (existing entry could come from concurrent caller) - if existingFd, ok := cr.filesByName[fd.GetName()]; ok { - return existingFd + // Cache file descriptor by name. If we can't overwrite an existing + // entry, return it. (Existing entry could come from concurrent caller.) + if existing, ok := cr.filesByName[fd.GetName()]; ok && !canOverwrite(existing, fallback) { + return existing.fd } - cr.filesByName[fd.GetName()] = fd + entry := fileEntry{fd: fd, fallback: fallback} + cr.filesByName[fd.GetName()] = entry // also cache by symbols and extensions for _, m := range fd.GetMessageTypes() { - cr.cacheMessageLocked(fd, m) + cr.cacheMessageLocked(m, entry) } for _, e := range fd.GetEnumTypes() { - cr.filesBySymbol[e.GetFullyQualifiedName()] = fd + if !cr.maybeCacheFileBySymbol(e.GetFullyQualifiedName(), entry) { + continue + } for _, v := range e.GetValues() { - cr.filesBySymbol[v.GetFullyQualifiedName()] = fd + cr.maybeCacheFileBySymbol(v.GetFullyQualifiedName(), entry) } } for _, e := range fd.GetExtensions() { - cr.filesBySymbol[e.GetFullyQualifiedName()] = fd - cr.filesByExtension[extDesc{e.GetOwner().GetFullyQualifiedName(), e.GetNumber()}] = fd + if !cr.maybeCacheFileBySymbol(e.GetFullyQualifiedName(), entry) { + continue + } + cr.maybeCacheFileByExtension(extDesc{e.GetOwner().GetFullyQualifiedName(), e.GetNumber()}, entry) } for _, s := range fd.GetServices() { - cr.filesBySymbol[s.GetFullyQualifiedName()] = fd + if !cr.maybeCacheFileBySymbol(s.GetFullyQualifiedName(), entry) { + continue + } for _, m := range s.GetMethods() { - cr.filesBySymbol[m.GetFullyQualifiedName()] = fd + cr.maybeCacheFileBySymbol(m.GetFullyQualifiedName(), entry) } } return fd } -func (cr *Client) cacheMessageLocked(fd *desc.FileDescriptor, md *desc.MessageDescriptor) { - cr.filesBySymbol[md.GetFullyQualifiedName()] = fd +func (cr *Client) cacheMessageLocked(md *desc.MessageDescriptor, entry fileEntry) { + if !cr.maybeCacheFileBySymbol(md.GetFullyQualifiedName(), entry) { + return + } for _, f := range md.GetFields() { - cr.filesBySymbol[f.GetFullyQualifiedName()] = fd + cr.maybeCacheFileBySymbol(f.GetFullyQualifiedName(), entry) } for _, o := range md.GetOneOfs() { - cr.filesBySymbol[o.GetFullyQualifiedName()] = fd + cr.maybeCacheFileBySymbol(o.GetFullyQualifiedName(), entry) } for _, e := range md.GetNestedEnumTypes() { - cr.filesBySymbol[e.GetFullyQualifiedName()] = fd + if !cr.maybeCacheFileBySymbol(e.GetFullyQualifiedName(), entry) { + continue + } for _, v := range e.GetValues() { - cr.filesBySymbol[v.GetFullyQualifiedName()] = fd + cr.maybeCacheFileBySymbol(v.GetFullyQualifiedName(), entry) } } for _, e := range md.GetNestedExtensions() { - cr.filesBySymbol[e.GetFullyQualifiedName()] = fd - cr.filesByExtension[extDesc{e.GetOwner().GetFullyQualifiedName(), e.GetNumber()}] = fd + if !cr.maybeCacheFileBySymbol(e.GetFullyQualifiedName(), entry) { + continue + } + cr.maybeCacheFileByExtension(extDesc{e.GetOwner().GetFullyQualifiedName(), e.GetNumber()}, entry) } for _, m := range md.GetNestedMessageTypes() { - cr.cacheMessageLocked(fd, m) // recurse + cr.cacheMessageLocked(m, entry) // recurse } } +func canOverwrite(existing fileEntry, fallback bool) bool { + return !fallback && existing.fallback +} + +func (cr *Client) maybeCacheFileBySymbol(symbol string, entry fileEntry) bool { + existing, ok := cr.filesBySymbol[symbol] + if ok && !canOverwrite(existing, entry.fallback) { + return false + } + cr.filesBySymbol[symbol] = entry + return true +} + +func (cr *Client) maybeCacheFileByExtension(ext extDesc, entry fileEntry) { + existing, ok := cr.filesByExtension[ext] + if ok && !canOverwrite(existing, entry.fallback) { + return + } + cr.filesByExtension[ext] = entry +} + // AllExtensionNumbersForType asks the server for all known extension numbers // for the given fully-qualified message name. func (cr *Client) AllExtensionNumbersForType(extendedMessageName string) ([]int32, error) { - req := &refv1alpha.ServerReflectionRequest{ - MessageRequest: &refv1alpha.ServerReflectionRequest_AllExtensionNumbersOfType{ + req := &refv1.ServerReflectionRequest{ + MessageRequest: &refv1.ServerReflectionRequest_AllExtensionNumbersOfType{ AllExtensionNumbersOfType: extendedMessageName, }, } resp, err := cr.send(req) - if err != nil { - if isNotFound(err) { - return nil, symbolNotFound(extendedMessageName, symbolTypeMessage, nil) - } + var exts []int32 + if err != nil && !isNotFound(err) { + // If the server doesn't know about the message type and returns "not found", + // we'll treat that as "no known extensions" instead of returning an error. return nil, err } + if err == nil { + extResp := resp.GetAllExtensionNumbersResponse() + if extResp == nil { + return nil, &ProtocolError{reflect.TypeOf(extResp).Elem()} + } + exts = extResp.ExtensionNumber + } - extResp := resp.GetAllExtensionNumbersResponse() - if extResp == nil { - return nil, &ProtocolError{reflect.TypeOf(extResp).Elem()} + resolver := cr.fallbackResolver.Load() + if resolver != nil && resolver.extensionResolver != nil { + type extRanger interface { + RangeExtensionsByMessage(message protoreflect.FullName, f func(protoreflect.ExtensionType) bool) + } + if ranger, ok := resolver.extensionResolver.(extRanger); ok { + // Merge results with fallback resolver + extSet := map[int32]struct{}{} + ranger.RangeExtensionsByMessage(protoreflect.FullName(extendedMessageName), func(extType protoreflect.ExtensionType) bool { + extSet[int32(extType.TypeDescriptor().Number())] = struct{}{} + return true + }) + if len(extSet) > 0 { + // De-dupe with the set of extension numbers we got + // from the server and merge the results back into exts. + for _, ext := range exts { + extSet[ext] = struct{}{} + } + exts = make([]int32, 0, len(extSet)) + for ext := range extSet { + exts = append(exts, ext) + } + sort.Slice(exts, func(i, j int) bool { return exts[i] < exts[j] }) + } + } } - return extResp.ExtensionNumber, nil + return exts, nil } // ListServices asks the server for the fully-qualified names of all exposed // services. func (cr *Client) ListServices() ([]string, error) { - req := &refv1alpha.ServerReflectionRequest{ - MessageRequest: &refv1alpha.ServerReflectionRequest_ListServices{ + req := &refv1.ServerReflectionRequest{ + MessageRequest: &refv1.ServerReflectionRequest_ListServices{ // proto doesn't indicate any purpose for this value and server impl // doesn't actually use it... ListServices: "*", @@ -523,7 +672,7 @@ func (cr *Client) ListServices() ([]string, error) { return serviceNames, nil } -func (cr *Client) send(req *refv1alpha.ServerReflectionRequest) (*refv1alpha.ServerReflectionResponse, error) { +func (cr *Client) send(req *refv1.ServerReflectionRequest) (*refv1.ServerReflectionResponse, error) { // we allow one immediate retry, in case we have a stale stream // (e.g. closed by server) resp, err := cr.doSend(req) @@ -548,7 +697,7 @@ func isNotFound(err error) bool { return ok && s.Code() == codes.NotFound } -func (cr *Client) doSend(req *refv1alpha.ServerReflectionRequest) (*refv1alpha.ServerReflectionResponse, error) { +func (cr *Client) doSend(req *refv1.ServerReflectionRequest) (*refv1.ServerReflectionResponse, error) { // TODO: Streams are thread-safe, so we shouldn't need to lock. But without locking, we'll need more machinery // (goroutines and channels) to ensure that responses are correctly correlated with their requests and thus // delivered in correct oder. @@ -557,7 +706,7 @@ func (cr *Client) doSend(req *refv1alpha.ServerReflectionRequest) (*refv1alpha.S return cr.doSendLocked(0, nil, req) } -func (cr *Client) doSendLocked(attemptCount int, prevErr error, req *refv1alpha.ServerReflectionRequest) (*refv1alpha.ServerReflectionResponse, error) { +func (cr *Client) doSendLocked(attemptCount int, prevErr error, req *refv1.ServerReflectionRequest) (*refv1.ServerReflectionResponse, error) { if attemptCount >= 3 && prevErr != nil { return nil, prevErr } @@ -610,7 +759,7 @@ func (cr *Client) initStreamLocked() error { // try the v1 API streamv1, err := cr.stubV1.ServerReflectionInfo(newCtx) if err == nil { - cr.stream = adaptStreamFromV1{streamv1} + cr.stream = streamv1 return nil } if status.Code(err) != codes.Unimplemented { @@ -622,7 +771,11 @@ func (cr *Client) initStreamLocked() error { cr.lastTriedV1 = cr.now() } var err error - cr.stream, err = cr.stubV1Alpha.ServerReflectionInfo(newCtx) + streamv1alpha, err := cr.stubV1Alpha.ServerReflectionInfo(newCtx) + if err == nil { + cr.stream = adaptStreamFromV1Alpha{streamv1alpha} + return nil + } return err } @@ -640,7 +793,7 @@ func (cr *Client) Reset() { func (cr *Client) resetLocked() { if cr.stream != nil { - cr.stream.CloseSend() + _ = cr.stream.CloseSend() for { // drain the stream, this covers io.EOF too if _, err := cr.stream.Recv(); err != nil { @@ -847,19 +1000,19 @@ func (mde msgDescriptorExtensions) nestedScopes() []extensionScope { return scopes } -type adaptStreamFromV1 struct { - refv1.ServerReflection_ServerReflectionInfoClient +type adaptStreamFromV1Alpha struct { + refv1alpha.ServerReflection_ServerReflectionInfoClient } -func (a adaptStreamFromV1) Send(request *refv1alpha.ServerReflectionRequest) error { - v1req := toV1Request(request) +func (a adaptStreamFromV1Alpha) Send(request *refv1.ServerReflectionRequest) error { + v1req := toV1AlphaRequest(request) return a.ServerReflection_ServerReflectionInfoClient.Send(v1req) } -func (a adaptStreamFromV1) Recv() (*refv1alpha.ServerReflectionResponse, error) { +func (a adaptStreamFromV1Alpha) Recv() (*refv1.ServerReflectionResponse, error) { v1resp, err := a.ServerReflection_ServerReflectionInfoClient.Recv() if err != nil { return nil, err } - return toV1AlphaResponse(v1resp), nil + return toV1Response(v1resp), nil } diff --git a/vendor/modules.txt b/vendor/modules.txt index 840bd63e72d..84dc6678b54 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -23,11 +23,14 @@ github.com/andybalholm/cascadia # github.com/beorn7/perks v1.0.1 ## explicit; go 1.11 github.com/beorn7/perks/quantile -# github.com/bufbuild/protocompile v0.10.0 -## explicit; go 1.20 +# github.com/bufbuild/protocompile v0.14.1 +## explicit; go 1.21 github.com/bufbuild/protocompile github.com/bufbuild/protocompile/ast github.com/bufbuild/protocompile/internal +github.com/bufbuild/protocompile/internal/editions +github.com/bufbuild/protocompile/internal/featuresext +github.com/bufbuild/protocompile/internal/messageset github.com/bufbuild/protocompile/linker github.com/bufbuild/protocompile/options github.com/bufbuild/protocompile/parser @@ -231,8 +234,8 @@ github.com/inconshreveable/mousetrap github.com/influxdata/influxdb1-client/models github.com/influxdata/influxdb1-client/pkg/escape github.com/influxdata/influxdb1-client/v2 -# github.com/jhump/protoreflect v1.16.0 -## explicit; go 1.19 +# github.com/jhump/protoreflect v1.17.0 +## explicit; go 1.21 github.com/jhump/protoreflect/desc github.com/jhump/protoreflect/desc/internal github.com/jhump/protoreflect/desc/protoparse