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