1*1c12ee1eSDan Willemsen// Copyright 2019 The Go Authors. All rights reserved. 2*1c12ee1eSDan Willemsen// Use of this source code is governed by a BSD-style 3*1c12ee1eSDan Willemsen// license that can be found in the LICENSE file. 4*1c12ee1eSDan Willemsen 5*1c12ee1eSDan Willemsenpackage protodesc 6*1c12ee1eSDan Willemsen 7*1c12ee1eSDan Willemsenimport ( 8*1c12ee1eSDan Willemsen "strings" 9*1c12ee1eSDan Willemsen "unicode" 10*1c12ee1eSDan Willemsen 11*1c12ee1eSDan Willemsen "google.golang.org/protobuf/encoding/protowire" 12*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/errors" 13*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/filedesc" 14*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/flags" 15*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/genid" 16*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/strs" 17*1c12ee1eSDan Willemsen "google.golang.org/protobuf/reflect/protoreflect" 18*1c12ee1eSDan Willemsen 19*1c12ee1eSDan Willemsen "google.golang.org/protobuf/types/descriptorpb" 20*1c12ee1eSDan Willemsen) 21*1c12ee1eSDan Willemsen 22*1c12ee1eSDan Willemsenfunc validateEnumDeclarations(es []filedesc.Enum, eds []*descriptorpb.EnumDescriptorProto) error { 23*1c12ee1eSDan Willemsen for i, ed := range eds { 24*1c12ee1eSDan Willemsen e := &es[i] 25*1c12ee1eSDan Willemsen if err := e.L2.ReservedNames.CheckValid(); err != nil { 26*1c12ee1eSDan Willemsen return errors.New("enum %q reserved names has %v", e.FullName(), err) 27*1c12ee1eSDan Willemsen } 28*1c12ee1eSDan Willemsen if err := e.L2.ReservedRanges.CheckValid(); err != nil { 29*1c12ee1eSDan Willemsen return errors.New("enum %q reserved ranges has %v", e.FullName(), err) 30*1c12ee1eSDan Willemsen } 31*1c12ee1eSDan Willemsen if len(ed.GetValue()) == 0 { 32*1c12ee1eSDan Willemsen return errors.New("enum %q must contain at least one value declaration", e.FullName()) 33*1c12ee1eSDan Willemsen } 34*1c12ee1eSDan Willemsen allowAlias := ed.GetOptions().GetAllowAlias() 35*1c12ee1eSDan Willemsen foundAlias := false 36*1c12ee1eSDan Willemsen for i := 0; i < e.Values().Len(); i++ { 37*1c12ee1eSDan Willemsen v1 := e.Values().Get(i) 38*1c12ee1eSDan Willemsen if v2 := e.Values().ByNumber(v1.Number()); v1 != v2 { 39*1c12ee1eSDan Willemsen foundAlias = true 40*1c12ee1eSDan Willemsen if !allowAlias { 41*1c12ee1eSDan Willemsen return errors.New("enum %q has conflicting non-aliased values on number %d: %q with %q", e.FullName(), v1.Number(), v1.Name(), v2.Name()) 42*1c12ee1eSDan Willemsen } 43*1c12ee1eSDan Willemsen } 44*1c12ee1eSDan Willemsen } 45*1c12ee1eSDan Willemsen if allowAlias && !foundAlias { 46*1c12ee1eSDan Willemsen return errors.New("enum %q allows aliases, but none were found", e.FullName()) 47*1c12ee1eSDan Willemsen } 48*1c12ee1eSDan Willemsen if e.Syntax() == protoreflect.Proto3 { 49*1c12ee1eSDan Willemsen if v := e.Values().Get(0); v.Number() != 0 { 50*1c12ee1eSDan Willemsen return errors.New("enum %q using proto3 semantics must have zero number for the first value", v.FullName()) 51*1c12ee1eSDan Willemsen } 52*1c12ee1eSDan Willemsen // Verify that value names in proto3 do not conflict if the 53*1c12ee1eSDan Willemsen // case-insensitive prefix is removed. 54*1c12ee1eSDan Willemsen // See protoc v3.8.0: src/google/protobuf/descriptor.cc:4991-5055 55*1c12ee1eSDan Willemsen names := map[string]protoreflect.EnumValueDescriptor{} 56*1c12ee1eSDan Willemsen prefix := strings.Replace(strings.ToLower(string(e.Name())), "_", "", -1) 57*1c12ee1eSDan Willemsen for i := 0; i < e.Values().Len(); i++ { 58*1c12ee1eSDan Willemsen v1 := e.Values().Get(i) 59*1c12ee1eSDan Willemsen s := strs.EnumValueName(strs.TrimEnumPrefix(string(v1.Name()), prefix)) 60*1c12ee1eSDan Willemsen if v2, ok := names[s]; ok && v1.Number() != v2.Number() { 61*1c12ee1eSDan Willemsen return errors.New("enum %q using proto3 semantics has conflict: %q with %q", e.FullName(), v1.Name(), v2.Name()) 62*1c12ee1eSDan Willemsen } 63*1c12ee1eSDan Willemsen names[s] = v1 64*1c12ee1eSDan Willemsen } 65*1c12ee1eSDan Willemsen } 66*1c12ee1eSDan Willemsen 67*1c12ee1eSDan Willemsen for j, vd := range ed.GetValue() { 68*1c12ee1eSDan Willemsen v := &e.L2.Values.List[j] 69*1c12ee1eSDan Willemsen if vd.Number == nil { 70*1c12ee1eSDan Willemsen return errors.New("enum value %q must have a specified number", v.FullName()) 71*1c12ee1eSDan Willemsen } 72*1c12ee1eSDan Willemsen if e.L2.ReservedNames.Has(v.Name()) { 73*1c12ee1eSDan Willemsen return errors.New("enum value %q must not use reserved name", v.FullName()) 74*1c12ee1eSDan Willemsen } 75*1c12ee1eSDan Willemsen if e.L2.ReservedRanges.Has(v.Number()) { 76*1c12ee1eSDan Willemsen return errors.New("enum value %q must not use reserved number %d", v.FullName(), v.Number()) 77*1c12ee1eSDan Willemsen } 78*1c12ee1eSDan Willemsen } 79*1c12ee1eSDan Willemsen } 80*1c12ee1eSDan Willemsen return nil 81*1c12ee1eSDan Willemsen} 82*1c12ee1eSDan Willemsen 83*1c12ee1eSDan Willemsenfunc validateMessageDeclarations(ms []filedesc.Message, mds []*descriptorpb.DescriptorProto) error { 84*1c12ee1eSDan Willemsen for i, md := range mds { 85*1c12ee1eSDan Willemsen m := &ms[i] 86*1c12ee1eSDan Willemsen 87*1c12ee1eSDan Willemsen // Handle the message descriptor itself. 88*1c12ee1eSDan Willemsen isMessageSet := md.GetOptions().GetMessageSetWireFormat() 89*1c12ee1eSDan Willemsen if err := m.L2.ReservedNames.CheckValid(); err != nil { 90*1c12ee1eSDan Willemsen return errors.New("message %q reserved names has %v", m.FullName(), err) 91*1c12ee1eSDan Willemsen } 92*1c12ee1eSDan Willemsen if err := m.L2.ReservedRanges.CheckValid(isMessageSet); err != nil { 93*1c12ee1eSDan Willemsen return errors.New("message %q reserved ranges has %v", m.FullName(), err) 94*1c12ee1eSDan Willemsen } 95*1c12ee1eSDan Willemsen if err := m.L2.ExtensionRanges.CheckValid(isMessageSet); err != nil { 96*1c12ee1eSDan Willemsen return errors.New("message %q extension ranges has %v", m.FullName(), err) 97*1c12ee1eSDan Willemsen } 98*1c12ee1eSDan Willemsen if err := (*filedesc.FieldRanges).CheckOverlap(&m.L2.ReservedRanges, &m.L2.ExtensionRanges); err != nil { 99*1c12ee1eSDan Willemsen return errors.New("message %q reserved and extension ranges has %v", m.FullName(), err) 100*1c12ee1eSDan Willemsen } 101*1c12ee1eSDan Willemsen for i := 0; i < m.Fields().Len(); i++ { 102*1c12ee1eSDan Willemsen f1 := m.Fields().Get(i) 103*1c12ee1eSDan Willemsen if f2 := m.Fields().ByNumber(f1.Number()); f1 != f2 { 104*1c12ee1eSDan Willemsen return errors.New("message %q has conflicting fields: %q with %q", m.FullName(), f1.Name(), f2.Name()) 105*1c12ee1eSDan Willemsen } 106*1c12ee1eSDan Willemsen } 107*1c12ee1eSDan Willemsen if isMessageSet && !flags.ProtoLegacy { 108*1c12ee1eSDan Willemsen return errors.New("message %q is a MessageSet, which is a legacy proto1 feature that is no longer supported", m.FullName()) 109*1c12ee1eSDan Willemsen } 110*1c12ee1eSDan Willemsen if isMessageSet && (m.Syntax() != protoreflect.Proto2 || m.Fields().Len() > 0 || m.ExtensionRanges().Len() == 0) { 111*1c12ee1eSDan Willemsen return errors.New("message %q is an invalid proto1 MessageSet", m.FullName()) 112*1c12ee1eSDan Willemsen } 113*1c12ee1eSDan Willemsen if m.Syntax() == protoreflect.Proto3 { 114*1c12ee1eSDan Willemsen if m.ExtensionRanges().Len() > 0 { 115*1c12ee1eSDan Willemsen return errors.New("message %q using proto3 semantics cannot have extension ranges", m.FullName()) 116*1c12ee1eSDan Willemsen } 117*1c12ee1eSDan Willemsen // Verify that field names in proto3 do not conflict if lowercased 118*1c12ee1eSDan Willemsen // with all underscores removed. 119*1c12ee1eSDan Willemsen // See protoc v3.8.0: src/google/protobuf/descriptor.cc:5830-5847 120*1c12ee1eSDan Willemsen names := map[string]protoreflect.FieldDescriptor{} 121*1c12ee1eSDan Willemsen for i := 0; i < m.Fields().Len(); i++ { 122*1c12ee1eSDan Willemsen f1 := m.Fields().Get(i) 123*1c12ee1eSDan Willemsen s := strings.Replace(strings.ToLower(string(f1.Name())), "_", "", -1) 124*1c12ee1eSDan Willemsen if f2, ok := names[s]; ok { 125*1c12ee1eSDan Willemsen return errors.New("message %q using proto3 semantics has conflict: %q with %q", m.FullName(), f1.Name(), f2.Name()) 126*1c12ee1eSDan Willemsen } 127*1c12ee1eSDan Willemsen names[s] = f1 128*1c12ee1eSDan Willemsen } 129*1c12ee1eSDan Willemsen } 130*1c12ee1eSDan Willemsen 131*1c12ee1eSDan Willemsen for j, fd := range md.GetField() { 132*1c12ee1eSDan Willemsen f := &m.L2.Fields.List[j] 133*1c12ee1eSDan Willemsen if m.L2.ReservedNames.Has(f.Name()) { 134*1c12ee1eSDan Willemsen return errors.New("message field %q must not use reserved name", f.FullName()) 135*1c12ee1eSDan Willemsen } 136*1c12ee1eSDan Willemsen if !f.Number().IsValid() { 137*1c12ee1eSDan Willemsen return errors.New("message field %q has an invalid number: %d", f.FullName(), f.Number()) 138*1c12ee1eSDan Willemsen } 139*1c12ee1eSDan Willemsen if !f.Cardinality().IsValid() { 140*1c12ee1eSDan Willemsen return errors.New("message field %q has an invalid cardinality: %d", f.FullName(), f.Cardinality()) 141*1c12ee1eSDan Willemsen } 142*1c12ee1eSDan Willemsen if m.L2.ReservedRanges.Has(f.Number()) { 143*1c12ee1eSDan Willemsen return errors.New("message field %q must not use reserved number %d", f.FullName(), f.Number()) 144*1c12ee1eSDan Willemsen } 145*1c12ee1eSDan Willemsen if m.L2.ExtensionRanges.Has(f.Number()) { 146*1c12ee1eSDan Willemsen return errors.New("message field %q with number %d in extension range", f.FullName(), f.Number()) 147*1c12ee1eSDan Willemsen } 148*1c12ee1eSDan Willemsen if fd.Extendee != nil { 149*1c12ee1eSDan Willemsen return errors.New("message field %q may not have extendee: %q", f.FullName(), fd.GetExtendee()) 150*1c12ee1eSDan Willemsen } 151*1c12ee1eSDan Willemsen if f.L1.IsProto3Optional { 152*1c12ee1eSDan Willemsen if f.Syntax() != protoreflect.Proto3 { 153*1c12ee1eSDan Willemsen return errors.New("message field %q under proto3 optional semantics must be specified in the proto3 syntax", f.FullName()) 154*1c12ee1eSDan Willemsen } 155*1c12ee1eSDan Willemsen if f.Cardinality() != protoreflect.Optional { 156*1c12ee1eSDan Willemsen return errors.New("message field %q under proto3 optional semantics must have optional cardinality", f.FullName()) 157*1c12ee1eSDan Willemsen } 158*1c12ee1eSDan Willemsen if f.ContainingOneof() != nil && f.ContainingOneof().Fields().Len() != 1 { 159*1c12ee1eSDan Willemsen return errors.New("message field %q under proto3 optional semantics must be within a single element oneof", f.FullName()) 160*1c12ee1eSDan Willemsen } 161*1c12ee1eSDan Willemsen } 162*1c12ee1eSDan Willemsen if f.IsWeak() && !flags.ProtoLegacy { 163*1c12ee1eSDan Willemsen return errors.New("message field %q is a weak field, which is a legacy proto1 feature that is no longer supported", f.FullName()) 164*1c12ee1eSDan Willemsen } 165*1c12ee1eSDan Willemsen if f.IsWeak() && (f.Syntax() != protoreflect.Proto2 || !isOptionalMessage(f) || f.ContainingOneof() != nil) { 166*1c12ee1eSDan Willemsen return errors.New("message field %q may only be weak for an optional message", f.FullName()) 167*1c12ee1eSDan Willemsen } 168*1c12ee1eSDan Willemsen if f.IsPacked() && !isPackable(f) { 169*1c12ee1eSDan Willemsen return errors.New("message field %q is not packable", f.FullName()) 170*1c12ee1eSDan Willemsen } 171*1c12ee1eSDan Willemsen if err := checkValidGroup(f); err != nil { 172*1c12ee1eSDan Willemsen return errors.New("message field %q is an invalid group: %v", f.FullName(), err) 173*1c12ee1eSDan Willemsen } 174*1c12ee1eSDan Willemsen if err := checkValidMap(f); err != nil { 175*1c12ee1eSDan Willemsen return errors.New("message field %q is an invalid map: %v", f.FullName(), err) 176*1c12ee1eSDan Willemsen } 177*1c12ee1eSDan Willemsen if f.Syntax() == protoreflect.Proto3 { 178*1c12ee1eSDan Willemsen if f.Cardinality() == protoreflect.Required { 179*1c12ee1eSDan Willemsen return errors.New("message field %q using proto3 semantics cannot be required", f.FullName()) 180*1c12ee1eSDan Willemsen } 181*1c12ee1eSDan Willemsen if f.Enum() != nil && !f.Enum().IsPlaceholder() && f.Enum().Syntax() != protoreflect.Proto3 { 182*1c12ee1eSDan Willemsen return errors.New("message field %q using proto3 semantics may only depend on a proto3 enum", f.FullName()) 183*1c12ee1eSDan Willemsen } 184*1c12ee1eSDan Willemsen } 185*1c12ee1eSDan Willemsen } 186*1c12ee1eSDan Willemsen seenSynthetic := false // synthetic oneofs for proto3 optional must come after real oneofs 187*1c12ee1eSDan Willemsen for j := range md.GetOneofDecl() { 188*1c12ee1eSDan Willemsen o := &m.L2.Oneofs.List[j] 189*1c12ee1eSDan Willemsen if o.Fields().Len() == 0 { 190*1c12ee1eSDan Willemsen return errors.New("message oneof %q must contain at least one field declaration", o.FullName()) 191*1c12ee1eSDan Willemsen } 192*1c12ee1eSDan Willemsen if n := o.Fields().Len(); n-1 != (o.Fields().Get(n-1).Index() - o.Fields().Get(0).Index()) { 193*1c12ee1eSDan Willemsen return errors.New("message oneof %q must have consecutively declared fields", o.FullName()) 194*1c12ee1eSDan Willemsen } 195*1c12ee1eSDan Willemsen 196*1c12ee1eSDan Willemsen if o.IsSynthetic() { 197*1c12ee1eSDan Willemsen seenSynthetic = true 198*1c12ee1eSDan Willemsen continue 199*1c12ee1eSDan Willemsen } 200*1c12ee1eSDan Willemsen if !o.IsSynthetic() && seenSynthetic { 201*1c12ee1eSDan Willemsen return errors.New("message oneof %q must be declared before synthetic oneofs", o.FullName()) 202*1c12ee1eSDan Willemsen } 203*1c12ee1eSDan Willemsen 204*1c12ee1eSDan Willemsen for i := 0; i < o.Fields().Len(); i++ { 205*1c12ee1eSDan Willemsen f := o.Fields().Get(i) 206*1c12ee1eSDan Willemsen if f.Cardinality() != protoreflect.Optional { 207*1c12ee1eSDan Willemsen return errors.New("message field %q belongs in a oneof and must be optional", f.FullName()) 208*1c12ee1eSDan Willemsen } 209*1c12ee1eSDan Willemsen if f.IsWeak() { 210*1c12ee1eSDan Willemsen return errors.New("message field %q belongs in a oneof and must not be a weak reference", f.FullName()) 211*1c12ee1eSDan Willemsen } 212*1c12ee1eSDan Willemsen } 213*1c12ee1eSDan Willemsen } 214*1c12ee1eSDan Willemsen 215*1c12ee1eSDan Willemsen if err := validateEnumDeclarations(m.L1.Enums.List, md.GetEnumType()); err != nil { 216*1c12ee1eSDan Willemsen return err 217*1c12ee1eSDan Willemsen } 218*1c12ee1eSDan Willemsen if err := validateMessageDeclarations(m.L1.Messages.List, md.GetNestedType()); err != nil { 219*1c12ee1eSDan Willemsen return err 220*1c12ee1eSDan Willemsen } 221*1c12ee1eSDan Willemsen if err := validateExtensionDeclarations(m.L1.Extensions.List, md.GetExtension()); err != nil { 222*1c12ee1eSDan Willemsen return err 223*1c12ee1eSDan Willemsen } 224*1c12ee1eSDan Willemsen } 225*1c12ee1eSDan Willemsen return nil 226*1c12ee1eSDan Willemsen} 227*1c12ee1eSDan Willemsen 228*1c12ee1eSDan Willemsenfunc validateExtensionDeclarations(xs []filedesc.Extension, xds []*descriptorpb.FieldDescriptorProto) error { 229*1c12ee1eSDan Willemsen for i, xd := range xds { 230*1c12ee1eSDan Willemsen x := &xs[i] 231*1c12ee1eSDan Willemsen // NOTE: Avoid using the IsValid method since extensions to MessageSet 232*1c12ee1eSDan Willemsen // may have a field number higher than normal. This check only verifies 233*1c12ee1eSDan Willemsen // that the number is not negative or reserved. We check again later 234*1c12ee1eSDan Willemsen // if we know that the extendee is definitely not a MessageSet. 235*1c12ee1eSDan Willemsen if n := x.Number(); n < 0 || (protowire.FirstReservedNumber <= n && n <= protowire.LastReservedNumber) { 236*1c12ee1eSDan Willemsen return errors.New("extension field %q has an invalid number: %d", x.FullName(), x.Number()) 237*1c12ee1eSDan Willemsen } 238*1c12ee1eSDan Willemsen if !x.Cardinality().IsValid() || x.Cardinality() == protoreflect.Required { 239*1c12ee1eSDan Willemsen return errors.New("extension field %q has an invalid cardinality: %d", x.FullName(), x.Cardinality()) 240*1c12ee1eSDan Willemsen } 241*1c12ee1eSDan Willemsen if xd.JsonName != nil { 242*1c12ee1eSDan Willemsen // A bug in older versions of protoc would always populate the 243*1c12ee1eSDan Willemsen // "json_name" option for extensions when it is meaningless. 244*1c12ee1eSDan Willemsen // When it did so, it would always use the camel-cased field name. 245*1c12ee1eSDan Willemsen if xd.GetJsonName() != strs.JSONCamelCase(string(x.Name())) { 246*1c12ee1eSDan Willemsen return errors.New("extension field %q may not have an explicitly set JSON name: %q", x.FullName(), xd.GetJsonName()) 247*1c12ee1eSDan Willemsen } 248*1c12ee1eSDan Willemsen } 249*1c12ee1eSDan Willemsen if xd.OneofIndex != nil { 250*1c12ee1eSDan Willemsen return errors.New("extension field %q may not be part of a oneof", x.FullName()) 251*1c12ee1eSDan Willemsen } 252*1c12ee1eSDan Willemsen if md := x.ContainingMessage(); !md.IsPlaceholder() { 253*1c12ee1eSDan Willemsen if !md.ExtensionRanges().Has(x.Number()) { 254*1c12ee1eSDan Willemsen return errors.New("extension field %q extends %q with non-extension field number: %d", x.FullName(), md.FullName(), x.Number()) 255*1c12ee1eSDan Willemsen } 256*1c12ee1eSDan Willemsen isMessageSet := md.Options().(*descriptorpb.MessageOptions).GetMessageSetWireFormat() 257*1c12ee1eSDan Willemsen if isMessageSet && !isOptionalMessage(x) { 258*1c12ee1eSDan Willemsen return errors.New("extension field %q extends MessageSet and must be an optional message", x.FullName()) 259*1c12ee1eSDan Willemsen } 260*1c12ee1eSDan Willemsen if !isMessageSet && !x.Number().IsValid() { 261*1c12ee1eSDan Willemsen return errors.New("extension field %q has an invalid number: %d", x.FullName(), x.Number()) 262*1c12ee1eSDan Willemsen } 263*1c12ee1eSDan Willemsen } 264*1c12ee1eSDan Willemsen if xd.GetOptions().GetWeak() { 265*1c12ee1eSDan Willemsen return errors.New("extension field %q cannot be a weak reference", x.FullName()) 266*1c12ee1eSDan Willemsen } 267*1c12ee1eSDan Willemsen if x.IsPacked() && !isPackable(x) { 268*1c12ee1eSDan Willemsen return errors.New("extension field %q is not packable", x.FullName()) 269*1c12ee1eSDan Willemsen } 270*1c12ee1eSDan Willemsen if err := checkValidGroup(x); err != nil { 271*1c12ee1eSDan Willemsen return errors.New("extension field %q is an invalid group: %v", x.FullName(), err) 272*1c12ee1eSDan Willemsen } 273*1c12ee1eSDan Willemsen if md := x.Message(); md != nil && md.IsMapEntry() { 274*1c12ee1eSDan Willemsen return errors.New("extension field %q cannot be a map entry", x.FullName()) 275*1c12ee1eSDan Willemsen } 276*1c12ee1eSDan Willemsen if x.Syntax() == protoreflect.Proto3 { 277*1c12ee1eSDan Willemsen switch x.ContainingMessage().FullName() { 278*1c12ee1eSDan Willemsen case (*descriptorpb.FileOptions)(nil).ProtoReflect().Descriptor().FullName(): 279*1c12ee1eSDan Willemsen case (*descriptorpb.EnumOptions)(nil).ProtoReflect().Descriptor().FullName(): 280*1c12ee1eSDan Willemsen case (*descriptorpb.EnumValueOptions)(nil).ProtoReflect().Descriptor().FullName(): 281*1c12ee1eSDan Willemsen case (*descriptorpb.MessageOptions)(nil).ProtoReflect().Descriptor().FullName(): 282*1c12ee1eSDan Willemsen case (*descriptorpb.FieldOptions)(nil).ProtoReflect().Descriptor().FullName(): 283*1c12ee1eSDan Willemsen case (*descriptorpb.OneofOptions)(nil).ProtoReflect().Descriptor().FullName(): 284*1c12ee1eSDan Willemsen case (*descriptorpb.ExtensionRangeOptions)(nil).ProtoReflect().Descriptor().FullName(): 285*1c12ee1eSDan Willemsen case (*descriptorpb.ServiceOptions)(nil).ProtoReflect().Descriptor().FullName(): 286*1c12ee1eSDan Willemsen case (*descriptorpb.MethodOptions)(nil).ProtoReflect().Descriptor().FullName(): 287*1c12ee1eSDan Willemsen default: 288*1c12ee1eSDan Willemsen return errors.New("extension field %q cannot be declared in proto3 unless extended descriptor options", x.FullName()) 289*1c12ee1eSDan Willemsen } 290*1c12ee1eSDan Willemsen } 291*1c12ee1eSDan Willemsen } 292*1c12ee1eSDan Willemsen return nil 293*1c12ee1eSDan Willemsen} 294*1c12ee1eSDan Willemsen 295*1c12ee1eSDan Willemsen// isOptionalMessage reports whether this is an optional message. 296*1c12ee1eSDan Willemsen// If the kind is unknown, it is assumed to be a message. 297*1c12ee1eSDan Willemsenfunc isOptionalMessage(fd protoreflect.FieldDescriptor) bool { 298*1c12ee1eSDan Willemsen return (fd.Kind() == 0 || fd.Kind() == protoreflect.MessageKind) && fd.Cardinality() == protoreflect.Optional 299*1c12ee1eSDan Willemsen} 300*1c12ee1eSDan Willemsen 301*1c12ee1eSDan Willemsen// isPackable checks whether the pack option can be specified. 302*1c12ee1eSDan Willemsenfunc isPackable(fd protoreflect.FieldDescriptor) bool { 303*1c12ee1eSDan Willemsen switch fd.Kind() { 304*1c12ee1eSDan Willemsen case protoreflect.StringKind, protoreflect.BytesKind, protoreflect.MessageKind, protoreflect.GroupKind: 305*1c12ee1eSDan Willemsen return false 306*1c12ee1eSDan Willemsen } 307*1c12ee1eSDan Willemsen return fd.IsList() 308*1c12ee1eSDan Willemsen} 309*1c12ee1eSDan Willemsen 310*1c12ee1eSDan Willemsen// checkValidGroup reports whether fd is a valid group according to the same 311*1c12ee1eSDan Willemsen// rules that protoc imposes. 312*1c12ee1eSDan Willemsenfunc checkValidGroup(fd protoreflect.FieldDescriptor) error { 313*1c12ee1eSDan Willemsen md := fd.Message() 314*1c12ee1eSDan Willemsen switch { 315*1c12ee1eSDan Willemsen case fd.Kind() != protoreflect.GroupKind: 316*1c12ee1eSDan Willemsen return nil 317*1c12ee1eSDan Willemsen case fd.Syntax() != protoreflect.Proto2: 318*1c12ee1eSDan Willemsen return errors.New("invalid under proto2 semantics") 319*1c12ee1eSDan Willemsen case md == nil || md.IsPlaceholder(): 320*1c12ee1eSDan Willemsen return errors.New("message must be resolvable") 321*1c12ee1eSDan Willemsen case fd.FullName().Parent() != md.FullName().Parent(): 322*1c12ee1eSDan Willemsen return errors.New("message and field must be declared in the same scope") 323*1c12ee1eSDan Willemsen case !unicode.IsUpper(rune(md.Name()[0])): 324*1c12ee1eSDan Willemsen return errors.New("message name must start with an uppercase") 325*1c12ee1eSDan Willemsen case fd.Name() != protoreflect.Name(strings.ToLower(string(md.Name()))): 326*1c12ee1eSDan Willemsen return errors.New("field name must be lowercased form of the message name") 327*1c12ee1eSDan Willemsen } 328*1c12ee1eSDan Willemsen return nil 329*1c12ee1eSDan Willemsen} 330*1c12ee1eSDan Willemsen 331*1c12ee1eSDan Willemsen// checkValidMap checks whether the field is a valid map according to the same 332*1c12ee1eSDan Willemsen// rules that protoc imposes. 333*1c12ee1eSDan Willemsen// See protoc v3.8.0: src/google/protobuf/descriptor.cc:6045-6115 334*1c12ee1eSDan Willemsenfunc checkValidMap(fd protoreflect.FieldDescriptor) error { 335*1c12ee1eSDan Willemsen md := fd.Message() 336*1c12ee1eSDan Willemsen switch { 337*1c12ee1eSDan Willemsen case md == nil || !md.IsMapEntry(): 338*1c12ee1eSDan Willemsen return nil 339*1c12ee1eSDan Willemsen case fd.FullName().Parent() != md.FullName().Parent(): 340*1c12ee1eSDan Willemsen return errors.New("message and field must be declared in the same scope") 341*1c12ee1eSDan Willemsen case md.Name() != protoreflect.Name(strs.MapEntryName(string(fd.Name()))): 342*1c12ee1eSDan Willemsen return errors.New("incorrect implicit map entry name") 343*1c12ee1eSDan Willemsen case fd.Cardinality() != protoreflect.Repeated: 344*1c12ee1eSDan Willemsen return errors.New("field must be repeated") 345*1c12ee1eSDan Willemsen case md.Fields().Len() != 2: 346*1c12ee1eSDan Willemsen return errors.New("message must have exactly two fields") 347*1c12ee1eSDan Willemsen case md.ExtensionRanges().Len() > 0: 348*1c12ee1eSDan Willemsen return errors.New("message must not have any extension ranges") 349*1c12ee1eSDan Willemsen case md.Enums().Len()+md.Messages().Len()+md.Extensions().Len() > 0: 350*1c12ee1eSDan Willemsen return errors.New("message must not have any nested declarations") 351*1c12ee1eSDan Willemsen } 352*1c12ee1eSDan Willemsen kf := md.Fields().Get(0) 353*1c12ee1eSDan Willemsen vf := md.Fields().Get(1) 354*1c12ee1eSDan Willemsen switch { 355*1c12ee1eSDan Willemsen case kf.Name() != genid.MapEntry_Key_field_name || kf.Number() != genid.MapEntry_Key_field_number || kf.Cardinality() != protoreflect.Optional || kf.ContainingOneof() != nil || kf.HasDefault(): 356*1c12ee1eSDan Willemsen return errors.New("invalid key field") 357*1c12ee1eSDan Willemsen case vf.Name() != genid.MapEntry_Value_field_name || vf.Number() != genid.MapEntry_Value_field_number || vf.Cardinality() != protoreflect.Optional || vf.ContainingOneof() != nil || vf.HasDefault(): 358*1c12ee1eSDan Willemsen return errors.New("invalid value field") 359*1c12ee1eSDan Willemsen } 360*1c12ee1eSDan Willemsen switch kf.Kind() { 361*1c12ee1eSDan Willemsen case protoreflect.BoolKind: // bool 362*1c12ee1eSDan Willemsen case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: // int32 363*1c12ee1eSDan Willemsen case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: // int64 364*1c12ee1eSDan Willemsen case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: // uint32 365*1c12ee1eSDan Willemsen case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: // uint64 366*1c12ee1eSDan Willemsen case protoreflect.StringKind: // string 367*1c12ee1eSDan Willemsen default: 368*1c12ee1eSDan Willemsen return errors.New("invalid key kind: %v", kf.Kind()) 369*1c12ee1eSDan Willemsen } 370*1c12ee1eSDan Willemsen if e := vf.Enum(); e != nil && e.Values().Len() > 0 && e.Values().Get(0).Number() != 0 { 371*1c12ee1eSDan Willemsen return errors.New("map enum value must have zero number for the first value") 372*1c12ee1eSDan Willemsen } 373*1c12ee1eSDan Willemsen return nil 374*1c12ee1eSDan Willemsen} 375