Skip to content

Commit

Permalink
Enabled editions support for upb generated code.
Browse files Browse the repository at this point in the history
This required enabling the feature in the code generator and fixing a few edge cases around label and type.

Also added tests to verify the special cases, and to verify that required fields work as expected.

PiperOrigin-RevId: 580263087
  • Loading branch information
haberman authored and Copybara-Service committed Nov 7, 2023
1 parent bb40d91 commit cf3a6f5
Show file tree
Hide file tree
Showing 14 changed files with 277 additions and 57 deletions.
1 change: 1 addition & 0 deletions src/google/protobuf/compiler/allowlists/editions.cc
Expand Up @@ -20,6 +20,7 @@ static constexpr auto kEarlyEditionsFile = internal::MakeAllowlist(
{
// Intentionally left blank.
"google/protobuf/",
"upb/",
},
internal::AllowlistFlags::kMatchPrefix);

Expand Down
19 changes: 17 additions & 2 deletions upb/reflection/field_def.c
Expand Up @@ -123,11 +123,26 @@ upb_CType upb_FieldDef_CType(const upb_FieldDef* f) {
UPB_UNREACHABLE();
}

upb_FieldType upb_FieldDef_Type(const upb_FieldDef* f) { return f->type_; }
upb_FieldType upb_FieldDef_Type(const upb_FieldDef* f) {
// TODO: remove once we can deprecate kUpb_FieldType_Group.
if (f->type_ == kUpb_FieldType_Message &&
UPB_DESC(FeatureSet_message_encoding)(f->resolved_features) ==
UPB_DESC(FeatureSet_DELIMITED)) {
return kUpb_FieldType_Group;
}
return f->type_;
}

uint32_t upb_FieldDef_Index(const upb_FieldDef* f) { return f->index_; }

upb_Label upb_FieldDef_Label(const upb_FieldDef* f) { return f->label_; }
upb_Label upb_FieldDef_Label(const upb_FieldDef* f) {
// TODO: remove once we can deprecate kUpb_Label_Required.
if (UPB_DESC(FeatureSet_field_presence)(f->resolved_features) ==
UPB_DESC(FeatureSet_LEGACY_REQUIRED)) {
return kUpb_Label_Required;
}
return f->label_;
}

uint32_t upb_FieldDef_Number(const upb_FieldDef* f) { return f->number_; }

Expand Down
32 changes: 32 additions & 0 deletions upb/test/BUILD
Expand Up @@ -84,6 +84,24 @@ upb_c_proto_library(
deps = [":test_proto"],
)

proto_library(
name = "editions_test_proto",
testonly = 1,
srcs = ["editions_test.proto"],
)

upb_c_proto_library(
name = "editions_test_upb_c_proto",
testonly = 1,
deps = [":editions_test_proto"],
)

upb_proto_reflection_library(
name = "editions_test_upb_proto_reflection",
testonly = 1,
deps = [":editions_test_proto"],
)

proto_library(
name = "test_cpp_proto",
srcs = ["test_cpp.proto"],
Expand Down Expand Up @@ -166,6 +184,20 @@ cc_test(
],
)

cc_test(
name = "editions_test",
srcs = ["editions_test.cc"],
copts = UPB_DEFAULT_CPPOPTS,
deps = [
":editions_test_upb_c_proto",
":editions_test_upb_proto_reflection",
"@com_google_googletest//:gtest_main",
"//upb:base",
"//upb:mem",
"//upb:reflection",
],
)

cc_test(
name = "test_cpp",
srcs = ["test_cpp.cc"],
Expand Down
62 changes: 62 additions & 0 deletions upb/test/editions_test.cc
@@ -0,0 +1,62 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

#include <gtest/gtest.h>
#include "upb/base/descriptor_constants.h"
#include "upb/mem/arena.hpp"
#include "upb/reflection/def.hpp"
#include "upb/test/editions_test.upb.h"
#include "upb/test/editions_test.upbdefs.h"

TEST(EditionsTest, PlainField) {
upb::DefPool defpool;
upb::MessageDefPtr md(upb_test_2023_EditionsMessage_getmsgdef(defpool.ptr()));
upb::FieldDefPtr f(md.FindFieldByName("plain_field"));
EXPECT_TRUE(f.has_presence());
}

TEST(EditionsTest, ImplicitPresenceField) {
upb::DefPool defpool;
upb::MessageDefPtr md(upb_test_2023_EditionsMessage_getmsgdef(defpool.ptr()));
upb::FieldDefPtr f(md.FindFieldByName("implicit_presence_field"));
EXPECT_FALSE(f.has_presence());
}

TEST(EditionsTest, DelimitedField) {
upb::DefPool defpool;
upb::MessageDefPtr md(upb_test_2023_EditionsMessage_getmsgdef(defpool.ptr()));
upb::FieldDefPtr f(md.FindFieldByName("delimited_field"));
EXPECT_EQ(kUpb_FieldType_Group, f.type());
}

TEST(EditionsTest, RequiredField) {
upb::DefPool defpool;
upb::MessageDefPtr md(upb_test_2023_EditionsMessage_getmsgdef(defpool.ptr()));
upb::FieldDefPtr f(md.FindFieldByName("required_field"));
EXPECT_EQ(kUpb_Label_Required, f.label());
}

TEST(EditionsTest, ClosedEnum) {
upb::DefPool defpool;
upb::MessageDefPtr md(upb_test_2023_EditionsMessage_getmsgdef(defpool.ptr()));
upb::FieldDefPtr f(md.FindFieldByName("enum_field"));
ASSERT_TRUE(f.enum_subdef().is_closed());
}

TEST(EditionsTest, PackedField) {
upb::DefPool defpool;
upb::MessageDefPtr md(upb_test_2023_EditionsMessage_getmsgdef(defpool.ptr()));
upb::FieldDefPtr f(md.FindFieldByName("unpacked_field"));
ASSERT_FALSE(f.packed());
}

TEST(EditionsTest, ConstructProto) {
// Doesn't do anything except construct the proto. This just verifies that
// the generated code compiles successfully.
upb::Arena arena;
upb_test_2023_EditionsMessage_new(arena.ptr());
}
26 changes: 26 additions & 0 deletions upb/test/editions_test.proto
@@ -0,0 +1,26 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

edition = "2023";

package upb.test_2023;

message EditionsMessage {
int32 plain_field = 1;
int32 implicit_presence_field = 2 [features.field_presence = IMPLICIT];
int32 required_field = 3 [features.field_presence = LEGACY_REQUIRED];
EditionsMessage delimited_field = 4 [features.message_encoding = DELIMITED];
EditionsEnum enum_field = 5;
repeated int32 unpacked_field = 6
[features.repeated_field_encoding = EXPANDED];
}

enum EditionsEnum {
option features.enum_type = CLOSED;

ONE = 1;
}
6 changes: 5 additions & 1 deletion upb/util/BUILD
Expand Up @@ -106,7 +106,10 @@ cc_library(

proto_library(
name = "required_fields_test_proto",
srcs = ["required_fields_test.proto"],
srcs = [
"required_fields_editions_test.proto",
"required_fields_test.proto",
],
)

upb_c_proto_library(
Expand All @@ -131,6 +134,7 @@ cc_test(
"//upb:json",
"//upb:mem",
"//upb:reflection",
"//upb:reflection_internal",
"@com_google_absl//absl/strings",
],
)
Expand Down
2 changes: 2 additions & 0 deletions upb/util/def_to_proto.c
Expand Up @@ -511,6 +511,8 @@ static google_protobuf_FileDescriptorProto* filedef_toproto(upb_ToProto_Context*

if (upb_FileDef_Syntax(f) == kUpb_Syntax_Proto3) {
google_protobuf_FileDescriptorProto_set_syntax(proto, strviewdup(ctx, "proto3"));
} else if (upb_FileDef_Syntax(f) == kUpb_Syntax_Editions) {
google_protobuf_FileDescriptorProto_set_syntax(proto, strviewdup(ctx, "editions"));
}

size_t n;
Expand Down
28 changes: 28 additions & 0 deletions upb/util/required_fields_editions_test.proto
@@ -0,0 +1,28 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

edition = "2023";

package upb_util_2023_test;

message EmptyMessage {}

message HasRequiredField {
int32 required_int32 = 1 [features.field_presence = LEGACY_REQUIRED];
}

message TestRequiredFields {
EmptyMessage required_message = 1 [features.field_presence = LEGACY_REQUIRED];
TestRequiredFields optional_message = 2;
repeated HasRequiredField repeated_message = 3;
map<int32, HasRequiredField> map_int32_message = 4;
map<int64, HasRequiredField> map_int64_message = 5;
map<uint32, HasRequiredField> map_uint32_message = 6;
map<uint64, HasRequiredField> map_uint64_message = 7;
map<bool, HasRequiredField> map_bool_message = 8;
map<string, HasRequiredField> map_string_message = 9;
}

0 comments on commit cf3a6f5

Please sign in to comment.