From f083ebf21f56783fd838c006880f8e16e05d1f1e Mon Sep 17 00:00:00 2001 From: Mike Kruskal Date: Wed, 6 Sep 2023 14:25:40 -0700 Subject: [PATCH] Editions: Migrate edition strings to enum in C++ code. The edition specification in proto files will remain unchanged, but it will be immediately converted to an enum by the parser. This gives us more control over the valid set of editions and simplifies ordering (just an integer comparison now). We plan to release exactly one edition per year. PiperOrigin-RevId: 563215992 --- src/google/protobuf/compiler/code_generator.h | 8 +- .../compiler/code_generator_unittest.cc | 26 +- .../compiler/command_line_interface.cc | 4 +- .../command_line_interface_unittest.cc | 12 +- .../protobuf/compiler/mock_code_generator.h | 16 +- src/google/protobuf/compiler/parser.cc | 10 +- src/google/protobuf/compiler/parser.h | 2 +- .../protobuf/compiler/parser_unittest.cc | 49 +++- src/google/protobuf/descriptor.cc | 33 +-- src/google/protobuf/descriptor.h | 19 +- src/google/protobuf/descriptor_unittest.cc | 220 ++++++++------- src/google/protobuf/feature_resolver.cc | 73 ++--- src/google/protobuf/feature_resolver.h | 4 +- src/google/protobuf/feature_resolver_test.cc | 264 ++++++++++-------- src/google/protobuf/port_def.inc | 4 +- src/google/protobuf/unittest_features.proto | 66 +++-- .../protobuf/util/type_resolver_util_test.cc | 4 +- 17 files changed, 447 insertions(+), 367 deletions(-) diff --git a/src/google/protobuf/compiler/code_generator.h b/src/google/protobuf/compiler/code_generator.h index 2b8c2ff075..3a44a7302b 100644 --- a/src/google/protobuf/compiler/code_generator.h +++ b/src/google/protobuf/compiler/code_generator.h @@ -142,15 +142,11 @@ class PROTOC_EXPORT CodeGenerator { // Returns the minimum edition (inclusive) supported by this generator. Any // proto files with an edition before this will result in an error. - virtual absl::string_view GetMinimumEdition() const { - return PROTOBUF_MINIMUM_EDITION; - } + virtual Edition GetMinimumEdition() const { return PROTOBUF_MINIMUM_EDITION; } // Returns the maximum edition (inclusive) supported by this generator. Any // proto files with an edition after this will result in an error. - virtual absl::string_view GetMaximumEdition() const { - return PROTOBUF_MAXIMUM_EDITION; - } + virtual Edition GetMaximumEdition() const { return PROTOBUF_MAXIMUM_EDITION; } // Builds a default feature set mapping for this generator. // diff --git a/src/google/protobuf/compiler/code_generator_unittest.cc b/src/google/protobuf/compiler/code_generator_unittest.cc index e765548db5..8c4fb7ff3f 100644 --- a/src/google/protobuf/compiler/code_generator_unittest.cc +++ b/src/google/protobuf/compiler/code_generator_unittest.cc @@ -73,17 +73,13 @@ class TestGenerator : public CodeGenerator { feature_extensions_ = extensions; } - absl::string_view GetMinimumEdition() const override { - return minimum_edition_; - } - void set_minimum_edition(absl::string_view minimum_edition) { + Edition GetMinimumEdition() const override { return minimum_edition_; } + void set_minimum_edition(Edition minimum_edition) { minimum_edition_ = minimum_edition; } - absl::string_view GetMaximumEdition() const override { - return maximum_edition_; - } - void set_maximum_edition(absl::string_view maximum_edition) { + Edition GetMaximumEdition() const override { return maximum_edition_; } + void set_maximum_edition(Edition maximum_edition) { maximum_edition_ = maximum_edition; } @@ -92,8 +88,8 @@ class TestGenerator : public CodeGenerator { using CodeGenerator::GetUnresolvedSourceFeatures; private: - absl::string_view minimum_edition_ = PROTOBUF_MINIMUM_EDITION; - absl::string_view maximum_edition_ = PROTOBUF_MAXIMUM_EDITION; + Edition minimum_edition_ = PROTOBUF_MINIMUM_EDITION; + Edition maximum_edition_ = PROTOBUF_MAXIMUM_EDITION; std::vector feature_extensions_ = { GetExtensionReflection(pb::test)}; }; @@ -286,12 +282,12 @@ TEST_F(CodeGeneratorTest, BuildFeatureSetDefaultsInvalidExtension) { TEST_F(CodeGeneratorTest, BuildFeatureSetDefaults) { TestGenerator generator; generator.set_feature_extensions({}); - generator.set_minimum_edition("2020"); - generator.set_maximum_edition("2024"); + generator.set_minimum_edition(EDITION_1_TEST_ONLY); + generator.set_maximum_edition(EDITION_99999_TEST_ONLY); EXPECT_THAT(generator.BuildFeatureSetDefaults(), IsOkAndHolds(EqualsProto(R"pb( defaults { - edition: "2023" + edition_enum: EDITION_2023 features { field_presence: EXPLICIT enum_type: OPEN @@ -300,8 +296,8 @@ TEST_F(CodeGeneratorTest, BuildFeatureSetDefaults) { json_format: ALLOW } } - minimum_edition: "2020" - maximum_edition: "2024" + minimum_edition_enum: EDITION_1_TEST_ONLY + maximum_edition_enum: EDITION_99999_TEST_ONLY )pb"))); } diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc index 8ff924fe22..a17cf83a87 100644 --- a/src/google/protobuf/compiler/command_line_interface.cc +++ b/src/google/protobuf/compiler/command_line_interface.cc @@ -1538,8 +1538,8 @@ bool CommandLineInterface::SetupFeatureResolution(DescriptorPool& pool) { // Calculate the feature defaults for each built-in generator. All generators // that support editions must agree on the supported edition range. std::vector feature_extensions; - absl::string_view minimum_edition = PROTOBUF_MINIMUM_EDITION; - absl::string_view maximum_edition = PROTOBUF_MAXIMUM_EDITION; + Edition minimum_edition = PROTOBUF_MINIMUM_EDITION; + Edition maximum_edition = PROTOBUF_MAXIMUM_EDITION; for (const auto& output : output_directives_) { if (output.generator == nullptr) continue; if ((output.generator->GetSupportedFeatures() & diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc index ab2ca3a95c..e0a9009fef 100644 --- a/src/google/protobuf/compiler/command_line_interface_unittest.cc +++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc @@ -1494,25 +1494,25 @@ TEST_F(CommandLineInterfaceTest, FeatureExtensionError) { TEST_F(CommandLineInterfaceTest, InvalidMinimumEditionError) { CreateTempFile("foo.proto", R"schema(edition = "2023";)schema"); - mock_generator_->set_minimum_edition("2022"); + mock_generator_->set_minimum_edition(EDITION_1_TEST_ONLY); Run("protocol_compiler --proto_path=$tmpdir --test_out=$tmpdir " "--experimental_editions foo.proto"); ExpectErrorSubstring( - "generator --test_out specifies a minimum edition 2022 which is not the " - "protoc minimum 2023"); + "generator --test_out specifies a minimum edition 1_TEST_ONLY which is " + "not the protoc minimum 2023"); } TEST_F(CommandLineInterfaceTest, InvalidMaximumEditionError) { CreateTempFile("foo.proto", R"schema(edition = "2023";)schema"); - mock_generator_->set_maximum_edition("2123"); + mock_generator_->set_maximum_edition(EDITION_99999_TEST_ONLY); Run("protocol_compiler --proto_path=$tmpdir --test_out=$tmpdir " "--experimental_editions foo.proto"); ExpectErrorSubstring( - "generator --test_out specifies a maximum edition 2123 which is not " - "the protoc maximum 2023"); + "generator --test_out specifies a maximum edition 99999_TEST_ONLY which " + "is not the protoc maximum 2023"); } TEST_F(CommandLineInterfaceTest, InvalidFeatureExtensionError) { diff --git a/src/google/protobuf/compiler/mock_code_generator.h b/src/google/protobuf/compiler/mock_code_generator.h index b98fc7c366..9d5a9e120a 100644 --- a/src/google/protobuf/compiler/mock_code_generator.h +++ b/src/google/protobuf/compiler/mock_code_generator.h @@ -129,25 +129,21 @@ class MockCodeGenerator : public CodeGenerator { feature_extensions_ = extensions; } - absl::string_view GetMinimumEdition() const override { - return minimum_edition_; - } - void set_minimum_edition(absl::string_view minimum_edition) { + Edition GetMinimumEdition() const override { return minimum_edition_; } + void set_minimum_edition(Edition minimum_edition) { minimum_edition_ = minimum_edition; } - absl::string_view GetMaximumEdition() const override { - return maximum_edition_; - } - void set_maximum_edition(absl::string_view maximum_edition) { + Edition GetMaximumEdition() const override { return maximum_edition_; } + void set_maximum_edition(Edition maximum_edition) { maximum_edition_ = maximum_edition; } private: std::string name_; uint64_t suppressed_features_ = 0; - absl::string_view minimum_edition_ = PROTOBUF_MINIMUM_EDITION; - absl::string_view maximum_edition_ = PROTOBUF_MAXIMUM_EDITION; + Edition minimum_edition_ = PROTOBUF_MINIMUM_EDITION; + Edition maximum_edition_ = PROTOBUF_MAXIMUM_EDITION; std::vector feature_extensions_ = { GetExtensionReflection(pb::test)}; diff --git a/src/google/protobuf/compiler/parser.cc b/src/google/protobuf/compiler/parser.cc index b6a3da8ce5..ccb74c89bd 100644 --- a/src/google/protobuf/compiler/parser.cc +++ b/src/google/protobuf/compiler/parser.cc @@ -694,7 +694,7 @@ bool Parser::Parse(io::Tokenizer* input, FileDescriptorProto* file) { if (file != nullptr) { file->set_syntax(syntax_identifier_); if (syntax_identifier_ == "editions") { - file->set_edition(edition_); + file->set_edition_enum(edition_); } } } else if (!stop_after_syntax_identifier_) { @@ -752,17 +752,17 @@ bool Parser::ParseSyntaxIdentifier(const FileDescriptorProto* file, DO(ConsumeString(&syntax, "Expected syntax identifier.")); DO(ConsumeEndOfDeclaration(";", &syntax_location)); - (has_edition ? edition_ : syntax_identifier_) = syntax; if (has_edition) { - if (syntax.empty()) { + if (!Edition_Parse(absl::StrCat("EDITION_", syntax), &edition_) || + edition_ == Edition::EDITION_UNKNOWN) { RecordError(syntax_token.line, syntax_token.column, - "A file's edition must be a nonempty string."); + absl::StrCat("Unknown edition \"", syntax, "\".")); return false; } - edition_ = syntax; syntax_identifier_ = "editions"; return true; } + syntax_identifier_ = syntax; if (syntax != "proto2" && syntax != "proto3" && !stop_after_syntax_identifier_) { diff --git a/src/google/protobuf/compiler/parser.h b/src/google/protobuf/compiler/parser.h index 04028c192a..e803eb93c7 100644 --- a/src/google/protobuf/compiler/parser.h +++ b/src/google/protobuf/compiler/parser.h @@ -553,7 +553,7 @@ class PROTOBUF_EXPORT Parser { bool require_syntax_identifier_; bool stop_after_syntax_identifier_; std::string syntax_identifier_; - std::string edition_; + Edition edition_ = Edition::EDITION_UNKNOWN; // Leading doc comments for the next declaration. These are not complete // yet; use ConsumeEndOfDeclaration() to get the complete comments. diff --git a/src/google/protobuf/compiler/parser_unittest.cc b/src/google/protobuf/compiler/parser_unittest.cc index 429d9b2e3e..7373f83479 100644 --- a/src/google/protobuf/compiler/parser_unittest.cc +++ b/src/google/protobuf/compiler/parser_unittest.cc @@ -295,7 +295,7 @@ TEST_F(ParserTest, RegressionNestedOpenBraceDoNotStackOverflow) { input, "0:10: Unexpected end of string.\n" "0:10: Invalid control characters encountered in text.\n" - "0:12: Expected top-level statement (e.g. \"message\").\n"); + "0:8: Unknown edition \"a\".\n"); } // =================================================================== @@ -891,7 +891,7 @@ TEST_F(ParseMessageTest, ReservedIdentifiers) { "}\n", "syntax: \"editions\" " - "edition: \"2023\" " + "edition_enum: EDITION_2023 " "message_type {" " name: \"TestMessage\"" " reserved_name: \"foo\"" @@ -1279,7 +1279,7 @@ TEST_F(ParseEnumTest, ReservedIdentifiers) { "}\n", "syntax: \"editions\" " - "edition: \"2023\" " + "edition_enum: EDITION_2023 " "enum_type {" " name: \"TestEnum\"" " value { name:\"FOO\" number:0 }" @@ -4067,7 +4067,7 @@ typedef ParserTest ParseEditionsTest; TEST_F(ParseEditionsTest, Editions) { ExpectParsesTo( R"schema( - edition = "super-cool"; + edition = "2023"; message A { int32 b = 1; })schema", @@ -4081,7 +4081,16 @@ TEST_F(ParseEditionsTest, Editions) { " }" "}" "syntax: \"editions\"" - "edition: \"super-cool\"\n"); + "edition_enum: EDITION_2023\n"); +} + +TEST_F(ParseEditionsTest, TestEdition) { + ExpectParsesTo( + R"schema( + edition = "99998_TEST_ONLY"; + )schema", + "syntax: \"editions\"" + "edition_enum: EDITION_99998_TEST_ONLY\n"); } TEST_F(ParseEditionsTest, ExtensionsParse) { @@ -4107,7 +4116,7 @@ TEST_F(ParseEditionsTest, ExtensionsParse) { " type: TYPE_STRING" "}" "syntax: \"editions\"" - "edition: \"2023\"\n"); + "edition_enum: EDITION_2023\n"); } TEST_F(ParseEditionsTest, MapFeatures) { @@ -4166,7 +4175,7 @@ TEST_F(ParseEditionsTest, MapFeatures) { } } syntax: "editions" - edition: "2023")pb"); + edition_enum: EDITION_2023)pb"); } TEST_F(ParseEditionsTest, EmptyEdition) { @@ -4176,7 +4185,27 @@ TEST_F(ParseEditionsTest, EmptyEdition) { message A { optional int32 b = 1; })schema", - "1:18: A file's edition must be a nonempty string.\n"); + "1:18: Unknown edition \"\".\n"); +} + +TEST_F(ParseEditionsTest, InvalidEdition) { + ExpectHasEarlyExitErrors( + R"schema( + edition = "2023_INVALID"; + message A { + optional int32 b = 1; + })schema", + "1:18: Unknown edition \"2023_INVALID\".\n"); +} + +TEST_F(ParseEditionsTest, UnknownEdition) { + ExpectHasEarlyExitErrors( + R"schema( + edition = "UNKNOWN"; + message A { + optional int32 b = 1; + })schema", + "1:18: Unknown edition \"UNKNOWN\".\n"); } TEST_F(ParseEditionsTest, SyntaxEditions) { @@ -4194,7 +4223,7 @@ TEST_F(ParseEditionsTest, MixedSyntaxAndEdition) { ExpectHasErrors( R"schema( syntax = "proto2"; - edition = "super-cool"; + edition = "2023"; message A { optional int32 b = 1; })schema", @@ -4204,7 +4233,7 @@ TEST_F(ParseEditionsTest, MixedSyntaxAndEdition) { TEST_F(ParseEditionsTest, MixedEditionAndSyntax) { ExpectHasErrors( R"schema( - edition = "super-cool"; + edition = "2023"; syntax = "proto2"; message A { int32 b = 1; diff --git a/src/google/protobuf/descriptor.cc b/src/google/protobuf/descriptor.cc index 6bd5aa41f0..368be5dce7 100644 --- a/src/google/protobuf/descriptor.cc +++ b/src/google/protobuf/descriptor.cc @@ -2818,7 +2818,7 @@ void FileDescriptor::CopyHeadingTo(FileDescriptorProto* proto) const { proto->set_syntax(FileDescriptorLegacy::SyntaxName(syntax)); } if (syntax == FileDescriptorLegacy::Syntax::SYNTAX_EDITIONS) { - proto->set_edition(edition()); + proto->set_edition_enum(edition()); } if (&options() != &FileOptions::default_instance()) { @@ -3260,8 +3260,7 @@ std::string FileDescriptor::DebugStringWithOptions( if (FileDescriptorLegacy(this).syntax() == FileDescriptorLegacy::SYNTAX_EDITIONS) { absl::SubstituteAndAppend(&contents, "edition = \"$0\";\n\n", edition()); - } else // NOLINT(readability/braces) - { + } else { absl::SubstituteAndAppend(&contents, "syntax = \"$0\";\n\n", FileDescriptorLegacy::SyntaxName( FileDescriptorLegacy(this).syntax())); @@ -5595,10 +5594,9 @@ static void PlanAllocationSize(const FileDescriptorProto& proto, internal::FlatAllocator& alloc) { alloc.PlanArray(1); alloc.PlanArray(1); - alloc.PlanArray( - 2 + (proto.has_edition() ? 1 : 0)); // name + package + alloc.PlanArray(2); // name + package if (proto.has_options()) alloc.PlanArray(1); - if (proto.has_edition()) { + if (proto.has_edition_enum()) { alloc.PlanArray(1); if (HasFeatures(proto.options())) { alloc.PlanArray(1); @@ -5705,14 +5703,14 @@ FileDescriptor* DescriptorBuilder::BuildFileImpl( FileDescriptor* result = alloc.AllocateArray(1); file_ = result; - if (proto.has_edition()) { + if (proto.has_edition_enum()) { const FeatureSetDefaults& defaults = pool_->feature_set_defaults_spec_ == nullptr ? GetCppFeatureSetDefaults() : *pool_->feature_set_defaults_spec_; absl::StatusOr feature_resolver = - FeatureResolver::Create(proto.edition(), defaults); + FeatureResolver::Create(proto.edition_enum(), defaults); if (!feature_resolver.ok()) { AddError( proto.name(), proto, DescriptorPool::ErrorCollector::EDITIONS, @@ -5755,10 +5753,10 @@ FileDescriptor* DescriptorBuilder::BuildFileImpl( return absl::StrCat("Unrecognized syntax: ", proto.syntax()); }); } - if (proto.has_edition()) { - file_->edition_ = alloc.AllocateStrings(proto.edition()); + if (proto.has_edition_enum()) { + file_->edition_ = proto.edition_enum(); } else { - file_->edition_ = nullptr; + file_->edition_ = Edition::EDITION_UNKNOWN; } result->name_ = alloc.AllocateStrings(proto.name()); @@ -9568,15 +9566,14 @@ bool IsLazilyInitializedFile(absl::string_view filename) { } // namespace cpp } // namespace internal -absl::string_view FileDescriptor::edition() const { - // ASLR will help give this a random value across processes. - static const void* kAntiHyrumText = &kAntiHyrumText; - absl::string_view anti_hyrum_string( - reinterpret_cast(kAntiHyrumText), - (reinterpret_cast(kAntiHyrumText) >> 3) % sizeof(void*)); +Edition FileDescriptor::edition() const { return edition_; } - return edition_ == nullptr ? anti_hyrum_string : *edition_; +namespace internal { +absl::string_view ShortEditionName(Edition edition) { + return absl::StripPrefix(Edition_Name(edition), "EDITION_"); } +} // namespace internal + } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/descriptor.h b/src/google/protobuf/descriptor.h index 2c6a779852..51a195804f 100644 --- a/src/google/protobuf/descriptor.h +++ b/src/google/protobuf/descriptor.h @@ -102,6 +102,11 @@ class DescriptorDatabase; class DescriptorPool; // Defined in descriptor.proto +#ifndef SWIG +enum Edition : int; +#else // !SWIG +typedef int Edition; +#endif // !SWIG class DescriptorProto; class DescriptorProto_ExtensionRange; class FieldDescriptorProto; @@ -293,8 +298,16 @@ class PROTOBUF_EXPORT InternalFeatureHelper { } }; +PROTOBUF_EXPORT absl::string_view ShortEditionName(Edition edition); + } // namespace internal +// Provide an Abseil formatter for edition names. +template +void AbslStringify(Sink& sink, Edition edition) { + absl::Format(&sink, "%v", internal::ShortEditionName(edition)); +} + // Describes a type of protocol message, or a particular group within a // message. To obtain the Descriptor for a given message object, call // Message::GetDescriptor(). Generated message classes also have a @@ -1910,8 +1923,8 @@ class PROTOBUF_EXPORT FileDescriptor : private internal::SymbolBase { PROTOBUF_IGNORE_DEPRECATION_STOP public: - // Returns an unspecified value if syntax() is not SYNTAX_EDITIONS. - absl::string_view edition() const; + // Returns EDITION_UNKNOWN if syntax() is not SYNTAX_EDITIONS. + Edition edition() const; // Find a top-level message type by name (not full_name). Returns nullptr if // not found. @@ -1996,7 +2009,7 @@ class PROTOBUF_EXPORT FileDescriptor : private internal::SymbolBase { const std::string* name_; const std::string* package_; const DescriptorPool* pool_; - const std::string* edition_ = nullptr; + Edition edition_; // Get the merged features that apply to this file. These are specified in // the .proto file through the feature options in the message definition. diff --git a/src/google/protobuf/descriptor_unittest.cc b/src/google/protobuf/descriptor_unittest.cc index 76f98a5f55..81e8ce6fd2 100644 --- a/src/google/protobuf/descriptor_unittest.cc +++ b/src/google/protobuf/descriptor_unittest.cc @@ -85,6 +85,9 @@ // Must be included last. #include "google/protobuf/port_def.inc" +using ::google::protobuf::internal::cpp::GetUtf8CheckMode; +using ::google::protobuf::internal::cpp::HasPreservingUnknownEnumSemantics; +using ::google::protobuf::internal::cpp::Utf8CheckMode; using ::testing::AnyOf; using ::testing::ElementsAre; using ::testing::HasSubstr; @@ -516,17 +519,17 @@ TEST_F(FileDescriptorTest, Syntax) { } { proto.set_syntax("editions"); - proto.set_edition("2023"); + proto.set_edition_enum(EDITION_2023); DescriptorPool pool; const FileDescriptor* file = pool.BuildFile(proto); ASSERT_TRUE(file != nullptr); EXPECT_EQ(FileDescriptorLegacy::Syntax::SYNTAX_EDITIONS, FileDescriptorLegacy(file).syntax()); - EXPECT_EQ("2023", file->edition()); + EXPECT_EQ(file->edition(), EDITION_2023); FileDescriptorProto other; file->CopyTo(&other); EXPECT_EQ("editions", other.syntax()); - EXPECT_EQ("2023", other.edition()); + EXPECT_EQ(other.edition_enum(), EDITION_2023); } } @@ -554,7 +557,7 @@ TEST_F(FileDescriptorTest, CopyHeadingTo) { EXPECT_EQ(&other.options().features(), &FeatureSet::default_instance()); { proto.set_syntax("editions"); - proto.set_edition("2023"); + proto.set_edition_enum(EDITION_2023); DescriptorPool pool; const FileDescriptor* file = pool.BuildFile(proto); @@ -565,7 +568,7 @@ TEST_F(FileDescriptorTest, CopyHeadingTo) { EXPECT_EQ(other.name(), "foo.proto"); EXPECT_EQ(other.package(), "foo.bar.baz"); EXPECT_EQ(other.syntax(), "editions"); - EXPECT_EQ(other.edition(), "2023"); + EXPECT_EQ(other.edition_enum(), EDITION_2023); EXPECT_EQ(other.options().java_package(), "foo.bar.baz"); EXPECT_TRUE(other.message_type().empty()); EXPECT_EQ(&other.options().features(), &FeatureSet::default_instance()); @@ -7249,7 +7252,7 @@ class FeaturesTest : public FeaturesBaseTest { {GetExtensionReflection(pb::cpp), GetExtensionReflection(pb::test), GetExtensionReflection(pb::TestMessage::test_message), GetExtensionReflection(pb::TestMessage::Nested::test_nested)}, - "2023", "2025"); + EDITION_2023, EDITION_99999_TEST_ONLY); ASSERT_OK(default_spec); pool_.SetFeatureSetDefaults(std::move(default_spec).value()); } @@ -7364,8 +7367,11 @@ TEST_F(FeaturesTest, Proto2Features) { })pb")); EXPECT_TRUE(field->has_presence()); EXPECT_FALSE(field->requires_utf8_validation()); + EXPECT_EQ(GetUtf8CheckMode(field, /*is_lite=*/false), Utf8CheckMode::kVerify); + EXPECT_EQ(GetUtf8CheckMode(field, /*is_lite=*/true), Utf8CheckMode::kNone); EXPECT_FALSE(field->is_packed()); EXPECT_FALSE(field->legacy_enum_field_treated_as_closed()); + EXPECT_FALSE(HasPreservingUnknownEnumSemantics(field)); EXPECT_FALSE(message->FindFieldByName("str")->requires_utf8_validation()); EXPECT_FALSE(message->FindFieldByName("rep")->is_packed()); EXPECT_FALSE(message->FindFieldByName("utf8")->requires_utf8_validation()); @@ -7430,8 +7436,11 @@ TEST_F(FeaturesTest, Proto3Features) { })pb")); EXPECT_FALSE(field->has_presence()); EXPECT_FALSE(field->requires_utf8_validation()); + EXPECT_EQ(GetUtf8CheckMode(field, /*is_lite=*/false), Utf8CheckMode::kStrict); + EXPECT_EQ(GetUtf8CheckMode(field, /*is_lite=*/true), Utf8CheckMode::kStrict); EXPECT_FALSE(field->is_packed()); EXPECT_FALSE(field->legacy_enum_field_treated_as_closed()); + EXPECT_FALSE(HasPreservingUnknownEnumSemantics(field)); EXPECT_TRUE(message->FindFieldByName("rep")->is_packed()); EXPECT_TRUE(message->FindFieldByName("str")->requires_utf8_validation()); EXPECT_FALSE(message->FindFieldByName("expanded")->is_packed()); @@ -7544,10 +7553,11 @@ TEST_F(FeaturesTest, Proto3Extensions) { } TEST_F(FeaturesTest, Edition2023Defaults) { - FileDescriptorProto file_proto = - ParseTextOrDie(R"pb( - name: "foo.proto" syntax: "editions" edition: "2023" - )pb"); + FileDescriptorProto file_proto = ParseTextOrDie(R"pb( + name: "foo.proto" + syntax: "editions" + edition_enum: EDITION_2023 + )pb"); BuildDescriptorMessagesInTestPool(); const FileDescriptor* file = ABSL_DIE_IF_NULL(pool_.BuildFile(file_proto)); @@ -7574,7 +7584,7 @@ TEST_F(FeaturesBaseTest, DefaultEdition2023Defaults) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" )pb"); ASSERT_NE(file, nullptr); @@ -7597,7 +7607,7 @@ TEST_F(FeaturesTest, ClearsOptions) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 options { java_package: "bar" features { field_presence: IMPLICIT } @@ -7622,7 +7632,7 @@ TEST_F(FeaturesTest, RestoresOptionsRoundTrip) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" options { java_package: "bar" @@ -7769,7 +7779,7 @@ TEST_F(FeaturesTest, RestoresLabelRoundTrip) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" field { @@ -7799,7 +7809,7 @@ TEST_F(FeaturesTest, RestoresGroupRoundTrip) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" nested_type { @@ -7833,7 +7843,7 @@ TEST_F(FeaturesTest, OnlyMessagesInheritGroupEncoding) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 options { features { message_encoding: DELIMITED } } message_type { name: "Foo" @@ -7861,10 +7871,11 @@ TEST_F(FeaturesTest, OnlyMessagesInheritGroupEncoding) { TEST_F(FeaturesTest, NoOptions) { BuildDescriptorMessagesInTestPool(); - const FileDescriptor* file = - BuildFile(R"pb( - name: "foo.proto" syntax: "editions" edition: "2023" - )pb"); + const FileDescriptor* file = BuildFile(R"pb( + name: "foo.proto" + syntax: "editions" + edition_enum: EDITION_2023 + )pb"); EXPECT_EQ(&file->options(), &FileOptions::default_instance()); EXPECT_THAT(GetCoreFeatures(file), EqualsProto(R"pb( field_presence: EXPLICIT @@ -7882,9 +7893,11 @@ TEST_F(FeaturesTest, InvalidEdition) { BuildDescriptorMessagesInTestPool(); BuildFileWithErrors( R"pb( - name: "foo.proto" syntax: "editions" edition: "2022" + name: "foo.proto" + syntax: "editions" + edition_enum: EDITION_1_TEST_ONLY )pb", - "foo.proto: foo.proto: EDITIONS: Edition 2022 is earlier than the " + "foo.proto: foo.proto: EDITIONS: Edition 1_TEST_ONLY is earlier than the " "minimum supported edition 2023\n"); } @@ -7893,7 +7906,7 @@ TEST_F(FeaturesTest, FileFeatures) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 options { features { field_presence: IMPLICIT } } )pb"); EXPECT_THAT(file->options(), EqualsProto("")); @@ -7915,22 +7928,22 @@ TEST_F(FeaturesTest, FileFeaturesExtension) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2024" + edition_enum: EDITION_99998_TEST_ONLY dependency: "google/protobuf/unittest_features.proto" options { features { field_presence: IMPLICIT } } )pb"); EXPECT_THAT(file->options(), EqualsProto("")); EXPECT_EQ(GetFeatures(file).field_presence(), FeatureSet::IMPLICIT); EXPECT_EQ(GetFeatures(file).enum_type(), FeatureSet::OPEN); - EXPECT_EQ(GetFeatures(file).GetExtension(pb::test).int_file_feature(), 4); + EXPECT_EQ(GetFeatures(file).GetExtension(pb::test).int_file_feature(), 3); EXPECT_EQ(GetFeatures(file) .GetExtension(pb::TestMessage::test_message) .int_file_feature(), - 4); + 3); EXPECT_EQ(GetFeatures(file) .GetExtension(pb::TestMessage::Nested::test_nested) .int_file_feature(), - 4); + 3); } TEST_F(FeaturesTest, FileFeaturesExtensionOverride) { @@ -7939,7 +7952,7 @@ TEST_F(FeaturesTest, FileFeaturesExtensionOverride) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2024" + edition_enum: EDITION_99998_TEST_ONLY dependency: "google/protobuf/unittest_features.proto" options { features { @@ -7969,7 +7982,7 @@ TEST_F(FeaturesTest, MessageFeaturesDefault) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" } )pb"); const Descriptor* message = file->message_type(0); @@ -7991,7 +8004,7 @@ TEST_F(FeaturesTest, MessageFeaturesInherit) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 options { features { field_presence: IMPLICIT } } message_type { name: "Foo" } )pb"); @@ -8006,7 +8019,7 @@ TEST_F(FeaturesTest, MessageFeaturesOverride) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" options { features { @@ -8034,7 +8047,7 @@ TEST_F(FeaturesTest, NestedMessageFeaturesOverride) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" options { features { @@ -8073,7 +8086,7 @@ TEST_F(FeaturesTest, FieldFeaturesDefault) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" field { name: "bar" number: 1 label: LABEL_REPEATED type: TYPE_INT64 } @@ -8099,7 +8112,7 @@ TEST_F(FeaturesTest, FieldFeaturesInherit) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" options { features { @@ -8130,7 +8143,7 @@ TEST_F(FeaturesTest, FieldFeaturesOverride) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" options { features { @@ -8174,7 +8187,7 @@ TEST_F(FeaturesTest, OneofFieldFeaturesInherit) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" options { features { @@ -8219,7 +8232,7 @@ TEST_F(FeaturesTest, OneofFieldFeaturesOverride) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" options { features { @@ -8328,7 +8341,7 @@ TEST_F(FeaturesTest, RootExtensionFeaturesOverride) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" options { features { @@ -8369,7 +8382,7 @@ TEST_F(FeaturesTest, MessageExtensionFeaturesOverride) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" options { features { @@ -8417,7 +8430,7 @@ TEST_F(FeaturesTest, EnumFeaturesDefault) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 enum_type { name: "Foo" value { name: "BAR" number: 0 } @@ -8442,7 +8455,7 @@ TEST_F(FeaturesTest, EnumFeaturesInherit) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 options { features { enum_type: CLOSED } } enum_type { name: "Foo" @@ -8460,7 +8473,7 @@ TEST_F(FeaturesTest, EnumFeaturesOverride) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" options { features { @@ -8488,7 +8501,7 @@ TEST_F(FeaturesTest, NestedEnumFeatures) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" options { features { @@ -8526,7 +8539,7 @@ TEST_F(FeaturesTest, EnumValueFeaturesDefault) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 enum_type { name: "Foo" value { name: "BAR" number: 0 } @@ -8551,7 +8564,7 @@ TEST_F(FeaturesTest, EnumValueFeaturesInherit) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 options { features { enum_type: CLOSED } } enum_type { name: "Foo" @@ -8575,7 +8588,7 @@ TEST_F(FeaturesTest, EnumValueFeaturesOverride) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" options { features { @@ -8613,7 +8626,7 @@ TEST_F(FeaturesTest, OneofFeaturesDefault) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" field { @@ -8645,7 +8658,7 @@ TEST_F(FeaturesTest, OneofFeaturesInherit) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 options { features { enum_type: CLOSED } } message_type { name: "Foo" @@ -8676,7 +8689,7 @@ TEST_F(FeaturesTest, OneofFeaturesOverride) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" options { features { @@ -8720,7 +8733,7 @@ TEST_F(FeaturesTest, ExtensionRangeFeaturesDefault) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" extension_range { start: 1 end: 100 } @@ -8746,7 +8759,7 @@ TEST_F(FeaturesTest, ExtensionRangeFeaturesInherit) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 options { features { enum_type: CLOSED } } message_type { name: "Foo" @@ -8771,7 +8784,7 @@ TEST_F(FeaturesTest, ExtensionRangeFeaturesOverride) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" options { features { @@ -8810,7 +8823,7 @@ TEST_F(FeaturesTest, ServiceFeaturesDefault) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 service { name: "Foo" } )pb"); const ServiceDescriptor* service = file->service(0); @@ -8832,7 +8845,7 @@ TEST_F(FeaturesTest, ServiceFeaturesInherit) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 options { features { enum_type: CLOSED } } service { name: "Foo" } )pb"); @@ -8847,7 +8860,7 @@ TEST_F(FeaturesTest, ServiceFeaturesOverride) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" options { features { @@ -8874,7 +8887,7 @@ TEST_F(FeaturesTest, MethodFeaturesDefault) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "EmptyMsg" } service { name: "Foo" @@ -8901,7 +8914,7 @@ TEST_F(FeaturesTest, MethodFeaturesInherit) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" message_type { name: "EmptyMsg" } options { features { enum_type: CLOSED } } @@ -8928,7 +8941,7 @@ TEST_F(FeaturesTest, MethodFeaturesOverride) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" message_type { name: "EmptyMsg" } options { @@ -8972,7 +8985,7 @@ TEST_F(FeaturesTest, FieldFeatureHelpers) { name: "foo.proto" syntax: "editions" dependency: "google/protobuf/cpp_features.proto" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" field { name: "def" number: 1 label: LABEL_OPTIONAL type: TYPE_STRING } @@ -9044,10 +9057,18 @@ TEST_F(FeaturesTest, FieldFeatureHelpers) { EXPECT_FALSE(default_field->is_required()); EXPECT_TRUE(default_field->has_presence()); EXPECT_TRUE(default_field->requires_utf8_validation()); + EXPECT_EQ(GetUtf8CheckMode(default_field, /*is_lite=*/false), + Utf8CheckMode::kStrict); + EXPECT_EQ(GetUtf8CheckMode(default_field, /*is_lite=*/true), + Utf8CheckMode::kStrict); EXPECT_TRUE(default_repeated_field->is_packed()); EXPECT_FALSE(default_repeated_field->has_presence()); EXPECT_FALSE(default_repeated_field->requires_utf8_validation()); + EXPECT_EQ(GetUtf8CheckMode(default_repeated_field, /*is_lite=*/false), + Utf8CheckMode::kStrict); + EXPECT_EQ(GetUtf8CheckMode(default_repeated_field, /*is_lite=*/true), + Utf8CheckMode::kStrict); EXPECT_TRUE(required_field->has_presence()); EXPECT_TRUE(required_field->is_required()); @@ -9057,7 +9078,15 @@ TEST_F(FeaturesTest, FieldFeatureHelpers) { EXPECT_FALSE(implicit_field->has_presence()); EXPECT_FALSE(expanded_field->is_packed()); EXPECT_FALSE(utf8_verify_field->requires_utf8_validation()); + EXPECT_EQ(GetUtf8CheckMode(utf8_verify_field, /*is_lite=*/false), + Utf8CheckMode::kVerify); + EXPECT_EQ(GetUtf8CheckMode(utf8_verify_field, /*is_lite=*/true), + Utf8CheckMode::kNone); EXPECT_FALSE(utf8_none_field->requires_utf8_validation()); + EXPECT_EQ(GetUtf8CheckMode(utf8_verify_field, /*is_lite=*/false), + Utf8CheckMode::kVerify); + EXPECT_EQ(GetUtf8CheckMode(utf8_verify_field, /*is_lite=*/true), + Utf8CheckMode::kNone); } TEST_F(FeaturesTest, EnumFeatureHelpers) { @@ -9067,7 +9096,7 @@ TEST_F(FeaturesTest, EnumFeatureHelpers) { name: "foo.proto" syntax: "editions" dependency: "google/protobuf/cpp_features.proto" - edition: "2023" + edition_enum: EDITION_2023 enum_type { name: "FooOpen" value { name: "BAR" number: 0 } @@ -9119,6 +9148,9 @@ TEST_F(FeaturesTest, EnumFeatureHelpers) { EXPECT_FALSE(field_open->legacy_enum_field_treated_as_closed()); EXPECT_TRUE(field_closed->legacy_enum_field_treated_as_closed()); EXPECT_TRUE(field_legacy_closed->legacy_enum_field_treated_as_closed()); + EXPECT_TRUE(HasPreservingUnknownEnumSemantics(field_open)); + EXPECT_FALSE(HasPreservingUnknownEnumSemantics(field_closed)); + EXPECT_FALSE(HasPreservingUnknownEnumSemantics(field_legacy_closed)); } TEST_F(FeaturesTest, MergeFeatureValidationFailed) { @@ -9128,7 +9160,7 @@ TEST_F(FeaturesTest, MergeFeatureValidationFailed) { R"pb( name: "foo.proto" syntax: "editions" - edition: "2024" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" options { features { field_presence: FIELD_PRESENCE_UNKNOWN } } )pb", @@ -9157,7 +9189,7 @@ TEST_F(FeaturesTest, InvalidFieldImplicitDefault) { R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" field { @@ -9180,7 +9212,7 @@ TEST_F(FeaturesTest, InvalidFieldImplicitOpen) { R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" field { @@ -9208,7 +9240,7 @@ TEST_F(FeaturesTest, InvalidFieldRequiredExtension) { R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" extension_range { start: 1 end: 100 } @@ -9231,7 +9263,7 @@ TEST_F(FeaturesTest, InvalidFieldImplicitExtension) { R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" extension_range { start: 1 end: 100 } @@ -9254,7 +9286,7 @@ TEST_F(FeaturesTest, InvalidFieldMessageImplicit) { R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" field { @@ -9277,7 +9309,7 @@ TEST_F(FeaturesTest, InvalidFieldRepeatedImplicit) { R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" field { @@ -9327,7 +9359,7 @@ TEST_F(FeaturesTest, InvalidFieldOneofImplicit) { R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" field { @@ -9350,7 +9382,7 @@ TEST_F(FeaturesTest, InvalidFieldRepeatedRequired) { R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" field { @@ -9372,7 +9404,7 @@ TEST_F(FeaturesTest, InvalidFieldOneofRequired) { R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" field { @@ -9395,7 +9427,7 @@ TEST_F(FeaturesTest, InvalidFieldNonRepeatedWithRepeatedEncoding) { R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" field { @@ -9417,7 +9449,7 @@ TEST_F(FeaturesTest, InvalidFieldNonPackableWithPackedRepeatedEncoding) { R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" field { @@ -9439,7 +9471,7 @@ TEST_F(FeaturesTest, InvalidFieldNonMessageWithMessageEncoding) { R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" field { @@ -9490,7 +9522,7 @@ TEST_F(FeaturesTest, InvalidOpenEnumNonZeroFirstValue) { R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 enum_type { name: "Enum" value { name: "BAR" number: 1 } @@ -9507,7 +9539,7 @@ TEST_F(FeaturesTest, ClosedEnumNonZeroFirstValue) { R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 enum_type { name: "Enum" value { name: "BAR" number: 9 } @@ -9524,7 +9556,7 @@ TEST_F(FeaturesTest, CopyToIncludesFeatures) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" options { java_package: "pkg" @@ -9565,7 +9597,7 @@ TEST_F(FeaturesTest, UninterpretedOptions) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 options { uninterpreted_option { name { name_part: "features" is_extension: false } @@ -9592,7 +9624,7 @@ TEST_F(FeaturesTest, UninterpretedOptionsMerge) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 options { uninterpreted_option { name { name_part: "features" is_extension: false } @@ -9630,7 +9662,7 @@ TEST_F(FeaturesTest, UninterpretedOptionsMergeExtension) { const FileDescriptor* file = BuildFile(R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" options { uninterpreted_option { @@ -9698,7 +9730,7 @@ TEST_F(FeaturesTest, InvalidJsonUniquenessDefaultWarning) { R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" field { @@ -9725,7 +9757,7 @@ TEST_F(FeaturesTest, InvalidJsonUniquenessDefaultError) { R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" field { @@ -9752,7 +9784,7 @@ TEST_F(FeaturesTest, InvalidJsonUniquenessCustomError) { R"pb( name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" field { @@ -9832,13 +9864,13 @@ INSTANTIATE_TEST_SUITE_P( testing::Values( std::make_tuple("Empty", R"pb(name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 )pb"), std::make_tuple( "FileFeature", R"pb(name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" options { features { @@ -9849,7 +9881,7 @@ INSTANTIATE_TEST_SUITE_P( std::make_tuple("FieldFeature", R"pb(name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" field { @@ -9866,7 +9898,7 @@ INSTANTIATE_TEST_SUITE_P( std::make_tuple("Required", R"pb(name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" field { @@ -9883,7 +9915,7 @@ INSTANTIATE_TEST_SUITE_P( std::make_tuple("Group", R"pb(name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" nested_type { @@ -9910,7 +9942,7 @@ INSTANTIATE_TEST_SUITE_P( std::make_tuple("MessageFeature", R"pb(name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 message_type { name: "Foo" options { @@ -9922,7 +9954,7 @@ INSTANTIATE_TEST_SUITE_P( "OneofFeature", R"pb(name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" message_type { name: "Foo" @@ -9946,7 +9978,7 @@ INSTANTIATE_TEST_SUITE_P( "ExtensionRangeFeature", R"pb(name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" message_type { name: "Foo" @@ -9964,7 +9996,7 @@ INSTANTIATE_TEST_SUITE_P( std::make_tuple("EnumFeature", R"pb(name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 enum_type { name: "Foo" value { name: "BAR" number: 1 } @@ -9975,7 +10007,7 @@ INSTANTIATE_TEST_SUITE_P( "EnumValueFeature", R"pb(name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" enum_type { name: "Foo" @@ -9995,7 +10027,7 @@ INSTANTIATE_TEST_SUITE_P( "ServiceFeature", R"pb(name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" service { name: "FooService" @@ -10010,7 +10042,7 @@ INSTANTIATE_TEST_SUITE_P( "MethodFeature", R"pb(name: "foo.proto" syntax: "editions" - edition: "2023" + edition_enum: EDITION_2023 dependency: "google/protobuf/unittest_features.proto" message_type { name: "EmptyMessage" } service { diff --git a/src/google/protobuf/feature_resolver.cc b/src/google/protobuf/feature_resolver.cc index aa4858b0d5..7bb28af855 100644 --- a/src/google/protobuf/feature_resolver.cc +++ b/src/google/protobuf/feature_resolver.cc @@ -76,24 +76,6 @@ absl::Status Error(Args... args) { return absl::FailedPreconditionError(absl::StrCat(args...)); } -struct EditionsLessThan { - bool operator()(absl::string_view a, absl::string_view b) const { - std::vector as = absl::StrSplit(a, '.'); - std::vector bs = absl::StrSplit(b, '.'); - size_t min_size = std::min(as.size(), bs.size()); - for (size_t i = 0; i < min_size; ++i) { - if (as[i].size() != bs[i].size()) { - return as[i].size() < bs[i].size(); - } else if (as[i] != bs[i]) { - return as[i] < bs[i]; - } - } - // Both strings are equal up until an extra element, which makes that string - // more recent. - return as.size() < bs.size(); - } -}; - absl::Status ValidateDescriptor(const Descriptor& descriptor) { if (descriptor.oneof_decl_count() > 0) { return Error("Type ", descriptor.full_name(), @@ -152,27 +134,26 @@ absl::Status ValidateExtension(const Descriptor& feature_set, return absl::OkStatus(); } -void CollectEditions(const Descriptor& descriptor, - absl::string_view minimum_edition, - absl::string_view maximum_edition, - absl::btree_set& editions) { +void CollectEditions(const Descriptor& descriptor, Edition minimum_edition, + Edition maximum_edition, + absl::btree_set& editions) { for (int i = 0; i < descriptor.field_count(); ++i) { for (const auto& def : descriptor.field(i)->options().edition_defaults()) { - if (EditionsLessThan()(maximum_edition, def.edition())) continue; - editions.insert(def.edition()); + if (maximum_edition < def.edition_enum()) continue; + editions.insert(def.edition_enum()); } } } -absl::Status FillDefaults(absl::string_view edition, Message& msg) { +absl::Status FillDefaults(Edition edition, Message& msg) { const Descriptor& descriptor = *msg.GetDescriptor(); auto comparator = [](const FieldOptions::EditionDefault& a, const FieldOptions::EditionDefault& b) { - return EditionsLessThan()(a.edition(), b.edition()); + return a.edition_enum() < b.edition_enum(); }; FieldOptions::EditionDefault edition_lookup; - edition_lookup.set_edition(edition); + edition_lookup.set_edition_enum(edition); for (int i = 0; i < descriptor.field_count(); ++i) { const FieldDescriptor& field = *descriptor.field(i); @@ -239,7 +220,7 @@ absl::Status ValidateMergedFeatures(const Message& msg) { absl::StatusOr FeatureResolver::CompileDefaults( const Descriptor* feature_set, absl::Span extensions, - absl::string_view minimum_edition, absl::string_view maximum_edition) { + Edition minimum_edition, Edition maximum_edition) { // Find and validate the FeatureSet in the pool. if (feature_set == nullptr) { return Error( @@ -254,7 +235,7 @@ absl::StatusOr FeatureResolver::CompileDefaults( } // Collect all the editions with unique defaults. - absl::btree_set editions; + absl::btree_set editions; CollectEditions(*feature_set, minimum_edition, maximum_edition, editions); for (const auto* extension : extensions) { CollectEditions(*extension->message_type(), minimum_edition, @@ -263,8 +244,8 @@ absl::StatusOr FeatureResolver::CompileDefaults( // Fill the default spec. FeatureSetDefaults defaults; - defaults.set_minimum_edition(minimum_edition); - defaults.set_maximum_edition(maximum_edition); + defaults.set_minimum_edition_enum(minimum_edition); + defaults.set_maximum_edition_enum(maximum_edition); auto message_factory = absl::make_unique(); for (const auto& edition : editions) { auto defaults_dynamic = @@ -276,7 +257,7 @@ absl::StatusOr FeatureResolver::CompileDefaults( defaults_dynamic.get(), extension))); } auto* edition_defaults = defaults.mutable_defaults()->Add(); - edition_defaults->set_edition(edition); + edition_defaults->set_edition_enum(edition); edition_defaults->mutable_features()->MergeFromString( defaults_dynamic->SerializeAsString()); } @@ -284,38 +265,42 @@ absl::StatusOr FeatureResolver::CompileDefaults( } absl::StatusOr FeatureResolver::Create( - absl::string_view edition, const FeatureSetDefaults& compiled_defaults) { - if (EditionsLessThan()(edition, compiled_defaults.minimum_edition())) { + Edition edition, const FeatureSetDefaults& compiled_defaults) { + if (edition < compiled_defaults.minimum_edition_enum()) { return Error("Edition ", edition, " is earlier than the minimum supported edition ", - compiled_defaults.minimum_edition()); + compiled_defaults.minimum_edition_enum()); } - if (EditionsLessThan()(compiled_defaults.maximum_edition(), edition)) { + if (compiled_defaults.maximum_edition_enum() < edition) { return Error("Edition ", edition, " is later than the maximum supported edition ", - compiled_defaults.maximum_edition()); + compiled_defaults.maximum_edition_enum()); } // Validate compiled defaults. - absl::string_view prev_edition; + Edition prev_edition = EDITION_UNKNOWN; for (const auto& edition_default : compiled_defaults.defaults()) { - if (!prev_edition.empty()) { - if (!EditionsLessThan()(prev_edition, edition_default.edition())) { + if (edition_default.edition_enum() == EDITION_UNKNOWN) { + return Error("Invalid edition ", edition_default.edition_enum(), + " specified."); + } + if (prev_edition != EDITION_UNKNOWN) { + if (edition_default.edition_enum() <= prev_edition) { return Error( "Feature set defaults are not strictly increasing. Edition ", prev_edition, " is greater than or equal to edition ", - edition_default.edition(), "."); + edition_default.edition_enum(), "."); } } - prev_edition = edition_default.edition(); + prev_edition = edition_default.edition_enum(); } // Select the matching edition defaults. auto comparator = [](const auto& a, const auto& b) { - return EditionsLessThan()(a.edition(), b.edition()); + return a.edition_enum() < b.edition_enum(); }; FeatureSetDefaults::FeatureSetEditionDefault search; - search.set_edition(edition); + search.set_edition_enum(edition); auto first_nonmatch = absl::c_upper_bound(compiled_defaults.defaults(), search, comparator); if (first_nonmatch == compiled_defaults.defaults().begin()) { diff --git a/src/google/protobuf/feature_resolver.h b/src/google/protobuf/feature_resolver.h index 6b4ca27ea2..b2599a55bb 100644 --- a/src/google/protobuf/feature_resolver.h +++ b/src/google/protobuf/feature_resolver.h @@ -64,12 +64,12 @@ class PROTOBUF_EXPORT FeatureResolver { static absl::StatusOr CompileDefaults( const Descriptor* feature_set, absl::Span extensions, - absl::string_view minimum_edition, absl::string_view maximum_edition); + Edition minimum_edition, Edition maximum_edition); // Creates a new FeatureResolver at a specific edition. This calculates the // default feature set for that edition, using the output of CompileDefaults. static absl::StatusOr Create( - absl::string_view edition, const FeatureSetDefaults& defaults); + Edition edition, const FeatureSetDefaults& defaults); // Creates a new feature set using inheritance and default behavior. This is // designed to be called recursively, and the parent feature set is expected diff --git a/src/google/protobuf/feature_resolver_test.cc b/src/google/protobuf/feature_resolver_test.cc index aa49bfbb0d..b6b07b14d2 100644 --- a/src/google/protobuf/feature_resolver_test.cc +++ b/src/google/protobuf/feature_resolver_test.cc @@ -93,17 +93,17 @@ const FieldDescriptor* GetExtension( } template -absl::StatusOr SetupFeatureResolver(absl::string_view edition, +absl::StatusOr SetupFeatureResolver(Edition edition, Extensions... extensions) { absl::StatusOr defaults = FeatureResolver::CompileDefaults(FeatureSet::descriptor(), - {GetExtension(extensions)...}, "2023", - "2023"); + {GetExtension(extensions)...}, + EDITION_2023, EDITION_99997_TEST_ONLY); RETURN_IF_ERROR(defaults.status()); return FeatureResolver::Create(edition, *defaults); } -absl::StatusOr GetDefaults(absl::string_view edition, +absl::StatusOr GetDefaults(Edition edition, const FeatureSetDefaults& defaults) { absl::StatusOr resolver = FeatureResolver::Create(edition, defaults); @@ -113,12 +113,12 @@ absl::StatusOr GetDefaults(absl::string_view edition, } template -absl::StatusOr GetDefaults(absl::string_view edition, +absl::StatusOr GetDefaults(Edition edition, Extensions... extensions) { absl::StatusOr defaults = FeatureResolver::CompileDefaults(FeatureSet::descriptor(), - {GetExtension(extensions)...}, "0", - "99999"); + {GetExtension(extensions)...}, + EDITION_2023, EDITION_99999_TEST_ONLY); RETURN_IF_ERROR(defaults.status()); return GetDefaults(edition, *defaults); } @@ -130,7 +130,7 @@ FileDescriptorProto GetProto(const FileDescriptor* file) { } TEST(FeatureResolverTest, DefaultsCore2023) { - absl::StatusOr merged = GetDefaults("2023"); + absl::StatusOr merged = GetDefaults(EDITION_2023); ASSERT_OK(merged); EXPECT_EQ(merged->field_presence(), FeatureSet::EXPLICIT); @@ -141,7 +141,7 @@ TEST(FeatureResolverTest, DefaultsCore2023) { } TEST(FeatureResolverTest, DefaultsTest2023) { - absl::StatusOr merged = GetDefaults("2023", pb::test); + absl::StatusOr merged = GetDefaults(EDITION_2023, pb::test); ASSERT_OK(merged); EXPECT_EQ(merged->field_presence(), FeatureSet::EXPLICIT); @@ -169,7 +169,7 @@ TEST(FeatureResolverTest, DefaultsTest2023) { TEST(FeatureResolverTest, DefaultsTestMessageExtension) { absl::StatusOr merged = - GetDefaults("2023", pb::TestMessage::test_message); + GetDefaults(EDITION_2023, pb::TestMessage::test_message); ASSERT_OK(merged); EXPECT_EQ(merged->field_presence(), FeatureSet::EXPLICIT); @@ -199,7 +199,7 @@ TEST(FeatureResolverTest, DefaultsTestMessageExtension) { TEST(FeatureResolverTest, DefaultsTestNestedExtension) { absl::StatusOr merged = - GetDefaults("2023", pb::TestMessage::Nested::test_nested); + GetDefaults(EDITION_2023, pb::TestMessage::Nested::test_nested); ASSERT_OK(merged); EXPECT_EQ(merged->field_presence(), FeatureSet::EXPLICIT); @@ -237,10 +237,10 @@ TEST(FeatureResolverTest, DefaultsGeneratedPoolCustom) { absl::StatusOr defaults = FeatureResolver::CompileDefaults( pool.FindMessageTypeByName("google.protobuf.FeatureSet"), - {pool.FindExtensionByName("pb.test")}, "2023", "2023"); + {pool.FindExtensionByName("pb.test")}, EDITION_2023, EDITION_2023); ASSERT_OK(defaults); ASSERT_EQ(defaults->defaults().size(), 1); - ASSERT_EQ(defaults->defaults().at(0).edition(), "2023"); + ASSERT_EQ(defaults->defaults().at(0).edition_enum(), EDITION_2023); FeatureSet merged = defaults->defaults().at(0).features(); EXPECT_EQ(merged.field_presence(), FeatureSet::EXPLICIT); @@ -250,44 +250,43 @@ TEST(FeatureResolverTest, DefaultsGeneratedPoolCustom) { } TEST(FeatureResolverTest, DefaultsTooEarly) { - absl::StatusOr merged = GetDefaults("2022", pb::test); + absl::StatusOr defaults = + FeatureResolver::CompileDefaults( + FeatureSet::descriptor(), {GetExtension(pb::test)}, + EDITION_1_TEST_ONLY, EDITION_99999_TEST_ONLY); + ASSERT_OK(defaults); + absl::StatusOr merged = + GetDefaults(EDITION_1_TEST_ONLY, *defaults); EXPECT_THAT(merged, HasError(AllOf(HasSubstr("No valid default found"), - HasSubstr("2022")))); + HasSubstr("1_TEST_ONLY")))); } TEST(FeatureResolverTest, DefaultsFarFuture) { - absl::StatusOr merged = GetDefaults("3456", pb::test); + absl::StatusOr merged = + GetDefaults(EDITION_99999_TEST_ONLY, pb::test); ASSERT_OK(merged); pb::TestFeatures ext = merged->GetExtension(pb::test); - EXPECT_EQ(ext.int_file_feature(), 5); + EXPECT_EQ(ext.int_file_feature(), 3); EXPECT_THAT(ext.message_field_feature(), - EqualsProto("bool_field: true int_field: 4 float_field: 1.5 " - "string_field: '2025'")); - EXPECT_EQ(ext.enum_field_feature(), pb::TestFeatures::ENUM_VALUE5); + EqualsProto("bool_field: true int_field: 2 float_field: 1.5 " + "string_field: '2024'")); + EXPECT_EQ(ext.enum_field_feature(), pb::TestFeatures::ENUM_VALUE3); } TEST(FeatureResolverTest, DefaultsMiddleEdition) { - absl::StatusOr merged = GetDefaults("2024.1", pb::test); - ASSERT_OK(merged); - - pb::TestFeatures ext = merged->GetExtension(pb::test); - EXPECT_EQ(ext.int_file_feature(), 4); - EXPECT_EQ(ext.enum_field_feature(), pb::TestFeatures::ENUM_VALUE4); -} - -TEST(FeatureResolverTest, DefaultsDifferentDigitCount) { - absl::StatusOr merged = GetDefaults("2023.90", pb::test); + absl::StatusOr merged = + GetDefaults(EDITION_99997_TEST_ONLY, pb::test); ASSERT_OK(merged); pb::TestFeatures ext = merged->GetExtension(pb::test); - EXPECT_EQ(ext.int_file_feature(), 3); - EXPECT_EQ(ext.enum_field_feature(), pb::TestFeatures::ENUM_VALUE3); + EXPECT_EQ(ext.int_file_feature(), 2); + EXPECT_EQ(ext.enum_field_feature(), pb::TestFeatures::ENUM_VALUE2); } TEST(FeatureResolverTest, DefaultsMessageMerge) { { - absl::StatusOr merged = GetDefaults("2023", pb::test); + absl::StatusOr merged = GetDefaults(EDITION_2023, pb::test); ASSERT_OK(merged); pb::TestFeatures ext = merged->GetExtension(pb::test); EXPECT_THAT(ext.message_field_feature(), @@ -297,7 +296,8 @@ TEST(FeatureResolverTest, DefaultsMessageMerge) { string_field: '2023')pb")); } { - absl::StatusOr merged = GetDefaults("2023.1", pb::test); + absl::StatusOr merged = + GetDefaults(EDITION_99997_TEST_ONLY, pb::test); ASSERT_OK(merged); pb::TestFeatures ext = merged->GetExtension(pb::test); EXPECT_THAT(ext.message_field_feature(), @@ -307,7 +307,8 @@ TEST(FeatureResolverTest, DefaultsMessageMerge) { string_field: '2023')pb")); } { - absl::StatusOr merged = GetDefaults("2024", pb::test); + absl::StatusOr merged = + GetDefaults(EDITION_99998_TEST_ONLY, pb::test); ASSERT_OK(merged); pb::TestFeatures ext = merged->GetExtension(pb::test); EXPECT_THAT(ext.message_field_feature(), @@ -320,37 +321,61 @@ TEST(FeatureResolverTest, DefaultsMessageMerge) { TEST(FeatureResolverTest, CreateFromUnsortedDefaults) { FeatureSetDefaults defaults = ParseTextOrDie(R"pb( - minimum_edition: "2022" - maximum_edition: "2024" + minimum_edition_enum: EDITION_2_TEST_ONLY + maximum_edition_enum: EDITION_99997_TEST_ONLY defaults { - edition: "2022" + edition_enum: EDITION_2_TEST_ONLY features {} } defaults { - edition: "2024" + edition_enum: EDITION_99997_TEST_ONLY features {} } defaults { - edition: "2023" + edition_enum: EDITION_2023 features {} } )pb"); - EXPECT_THAT( - FeatureResolver::Create("2023", defaults), - HasError(AllOf( - HasSubstr("not strictly increasing."), - HasSubstr("Edition 2024 is greater than or equal to edition 2023")))); + EXPECT_THAT(FeatureResolver::Create(EDITION_2023, defaults), + HasError(AllOf(HasSubstr("not strictly increasing."), + HasSubstr("Edition 99997_TEST_ONLY is greater " + "than or equal to edition 2023")))); +} + +TEST(FeatureResolverTest, CreateUnknownEdition) { + FeatureSetDefaults defaults = ParseTextOrDie(R"pb( + minimum_edition_enum: EDITION_UNKNOWN + maximum_edition_enum: EDITION_99999_TEST_ONLY + defaults { + edition_enum: EDITION_UNKNOWN + features {} + } + )pb"); + EXPECT_THAT(FeatureResolver::Create(EDITION_2023, defaults), + HasError(HasSubstr("Invalid edition UNKNOWN"))); +} + +TEST(FeatureResolverTest, CreateMissingEdition) { + FeatureSetDefaults defaults = ParseTextOrDie(R"pb( + minimum_edition_enum: EDITION_UNKNOWN + maximum_edition_enum: EDITION_99999_TEST_ONLY + defaults { features {} } + )pb"); + EXPECT_THAT(FeatureResolver::Create(EDITION_2023, defaults), + HasError(HasSubstr("Invalid edition UNKNOWN"))); } TEST(FeatureResolverTest, CompileDefaultsMissingDescriptor) { - EXPECT_THAT(FeatureResolver::CompileDefaults(nullptr, {}, "2023", "2023"), - HasError(HasSubstr("find definition of google.protobuf.FeatureSet"))); + EXPECT_THAT( + FeatureResolver::CompileDefaults(nullptr, {}, EDITION_2023, EDITION_2023), + HasError(HasSubstr("find definition of google.protobuf.FeatureSet"))); } TEST(FeatureResolverTest, CompileDefaultsMissingExtension) { - EXPECT_THAT(FeatureResolver::CompileDefaults(FeatureSet::descriptor(), - {nullptr}, "2023", "2023"), - HasError(HasSubstr("Unknown extension"))); + EXPECT_THAT( + FeatureResolver::CompileDefaults(FeatureSet::descriptor(), {nullptr}, + EDITION_2023, EDITION_2023), + HasError(HasSubstr("Unknown extension"))); } TEST(FeatureResolverTest, CompileDefaultsInvalidExtension) { @@ -358,12 +383,12 @@ TEST(FeatureResolverTest, CompileDefaultsInvalidExtension) { FeatureResolver::CompileDefaults( FeatureSet::descriptor(), {GetExtension(protobuf_unittest::file_opt1, FileOptions::descriptor())}, - "2023", "2023"), + EDITION_2023, EDITION_2023), HasError(HasSubstr("is not an extension of"))); } TEST(FeatureResolverTest, MergeFeaturesChildOverrideCore) { - absl::StatusOr resolver = SetupFeatureResolver("2023"); + absl::StatusOr resolver = SetupFeatureResolver(EDITION_2023); ASSERT_OK(resolver); FeatureSet child = ParseTextOrDie(R"pb( field_presence: IMPLICIT @@ -381,7 +406,7 @@ TEST(FeatureResolverTest, MergeFeaturesChildOverrideCore) { TEST(FeatureResolverTest, MergeFeaturesChildOverrideComplex) { absl::StatusOr resolver = - SetupFeatureResolver("2023", pb::test); + SetupFeatureResolver(EDITION_2023, pb::test); ASSERT_OK(resolver); FeatureSet child = ParseTextOrDie(R"pb( field_presence: IMPLICIT @@ -413,7 +438,7 @@ TEST(FeatureResolverTest, MergeFeaturesChildOverrideComplex) { TEST(FeatureResolverTest, MergeFeaturesParentOverrides) { absl::StatusOr resolver = - SetupFeatureResolver("2023", pb::test); + SetupFeatureResolver(EDITION_2023, pb::test); ASSERT_OK(resolver); FeatureSet parent = ParseTextOrDie(R"pb( field_presence: IMPLICIT @@ -458,7 +483,7 @@ TEST(FeatureResolverTest, MergeFeaturesParentOverrides) { } TEST(FeatureResolverTest, MergeFeaturesFieldPresenceUnknown) { - absl::StatusOr resolver = SetupFeatureResolver("2023"); + absl::StatusOr resolver = SetupFeatureResolver(EDITION_2023); ASSERT_OK(resolver); FeatureSet child = ParseTextOrDie(R"pb( field_presence: FIELD_PRESENCE_UNKNOWN @@ -470,7 +495,7 @@ TEST(FeatureResolverTest, MergeFeaturesFieldPresenceUnknown) { } TEST(FeatureResolverTest, MergeFeaturesEnumTypeUnknown) { - absl::StatusOr resolver = SetupFeatureResolver("2023"); + absl::StatusOr resolver = SetupFeatureResolver(EDITION_2023); ASSERT_OK(resolver); FeatureSet child = ParseTextOrDie(R"pb( enum_type: ENUM_TYPE_UNKNOWN @@ -481,7 +506,7 @@ TEST(FeatureResolverTest, MergeFeaturesEnumTypeUnknown) { } TEST(FeatureResolverTest, MergeFeaturesRepeatedFieldEncodingUnknown) { - absl::StatusOr resolver = SetupFeatureResolver("2023"); + absl::StatusOr resolver = SetupFeatureResolver(EDITION_2023); ASSERT_OK(resolver); FeatureSet child = ParseTextOrDie(R"pb( repeated_field_encoding: REPEATED_FIELD_ENCODING_UNKNOWN @@ -493,7 +518,7 @@ TEST(FeatureResolverTest, MergeFeaturesRepeatedFieldEncodingUnknown) { } TEST(FeatureResolverTest, MergeFeaturesMessageEncodingUnknown) { - absl::StatusOr resolver = SetupFeatureResolver("2023"); + absl::StatusOr resolver = SetupFeatureResolver(EDITION_2023); ASSERT_OK(resolver); FeatureSet child = ParseTextOrDie(R"pb( message_encoding: MESSAGE_ENCODING_UNKNOWN @@ -506,7 +531,7 @@ TEST(FeatureResolverTest, MergeFeaturesMessageEncodingUnknown) { TEST(FeatureResolverTest, MergeFeaturesExtensionEnumUnknown) { absl::StatusOr resolver = - SetupFeatureResolver("2023", pb::test); + SetupFeatureResolver(EDITION_2023, pb::test); ASSERT_OK(resolver); FeatureSet child = ParseTextOrDie(R"pb( [pb.test] { enum_field_feature: TEST_ENUM_FEATURE_UNKNOWN } @@ -519,15 +544,16 @@ TEST(FeatureResolverTest, MergeFeaturesExtensionEnumUnknown) { } TEST(FeatureResolverTest, MergeFeaturesDistantPast) { - EXPECT_THAT(SetupFeatureResolver("1000"), - HasError(AllOf(HasSubstr("Edition 1000"), + EXPECT_THAT(SetupFeatureResolver(EDITION_1_TEST_ONLY), + HasError(AllOf(HasSubstr("Edition 1_TEST_ONLY"), HasSubstr("minimum supported edition 2023")))); } TEST(FeatureResolverTest, MergeFeaturesDistantFuture) { - EXPECT_THAT(SetupFeatureResolver("9999"), - HasError(AllOf(HasSubstr("Edition 9999"), - HasSubstr("maximum supported edition 2023")))); + EXPECT_THAT( + SetupFeatureResolver(EDITION_99998_TEST_ONLY), + HasError(AllOf(HasSubstr("Edition 99998_TEST_ONLY"), + HasSubstr("maximum supported edition 99997_TEST_ONLY")))); } class FakeErrorCollector : public io::ErrorCollector { @@ -550,8 +576,8 @@ class FeatureResolverPoolTest : public testing::Test { ASSERT_NE(pool_.BuildFile(file), nullptr); feature_set_ = pool_.FindMessageTypeByName("google.protobuf.FeatureSet"); ASSERT_NE(feature_set_, nullptr); - auto defaults = - FeatureResolver::CompileDefaults(feature_set_, {}, "2023", "2023"); + auto defaults = FeatureResolver::CompileDefaults( + feature_set_, {}, EDITION_2023, EDITION_2023); ASSERT_OK(defaults); defaults_ = std::move(defaults).value(); } @@ -590,10 +616,10 @@ TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidNonMessage) { ASSERT_NE(file, nullptr); const FieldDescriptor* ext = file->extension(0); - EXPECT_THAT( - FeatureResolver::CompileDefaults(feature_set_, {ext}, "2023", "2023"), - HasError( - AllOf(HasSubstr("test.bar"), HasSubstr("is not of message type")))); + EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext}, + EDITION_2023, EDITION_2023), + HasError(AllOf(HasSubstr("test.bar"), + HasSubstr("is not of message type")))); } TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidRepeated) { @@ -611,7 +637,8 @@ TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidRepeated) { const FieldDescriptor* ext = file->extension(0); EXPECT_THAT( - FeatureResolver::CompileDefaults(feature_set_, {ext}, "2023", "2023"), + FeatureResolver::CompileDefaults(feature_set_, {ext}, EDITION_2023, + EDITION_2023), HasError(AllOf(HasSubstr("test.bar"), HasSubstr("repeated extension")))); } @@ -630,7 +657,7 @@ TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidWithExtensions) { extend Foo { optional Foo bar2 = 1 [ targets = TARGET_TYPE_FIELD, - edition_defaults = { edition: "2023", value: "" } + edition_defaults = { edition_enum: EDITION_2023, value: "" } ]; } )schema"); @@ -638,7 +665,8 @@ TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidWithExtensions) { const FieldDescriptor* ext = file->extension(0); EXPECT_THAT( - FeatureResolver::CompileDefaults(feature_set_, {ext}, "2023", "2023"), + FeatureResolver::CompileDefaults(feature_set_, {ext}, EDITION_2023, + EDITION_2023), HasError(AllOf(HasSubstr("test.bar"), HasSubstr("Nested extensions")))); } @@ -655,11 +683,11 @@ TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidWithOneof) { oneof x { int32 int_field = 1 [ targets = TARGET_TYPE_FIELD, - edition_defaults = { edition: "2023", value: "1" } + edition_defaults = { edition_enum: EDITION_2023, value: "1" } ]; string string_field = 2 [ targets = TARGET_TYPE_FIELD, - edition_defaults = { edition: "2023", value: "'hello'" } + edition_defaults = { edition_enum: EDITION_2023, value: "'hello'" } ]; } } @@ -667,10 +695,10 @@ TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidWithOneof) { ASSERT_NE(file, nullptr); const FieldDescriptor* ext = file->extension(0); - EXPECT_THAT( - FeatureResolver::CompileDefaults(feature_set_, {ext}, "2023", "2023"), - HasError( - AllOf(HasSubstr("test.Foo"), HasSubstr("oneof feature fields")))); + EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext}, + EDITION_2023, EDITION_2023), + HasError(AllOf(HasSubstr("test.Foo"), + HasSubstr("oneof feature fields")))); } TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidWithRequired) { @@ -685,17 +713,17 @@ TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidWithRequired) { message Foo { required int32 required_field = 1 [ targets = TARGET_TYPE_FIELD, - edition_defaults = { edition: "2023", value: "" } + edition_defaults = { edition_enum: EDITION_2023, value: "" } ]; } )schema"); ASSERT_NE(file, nullptr); const FieldDescriptor* ext = file->extension(0); - EXPECT_THAT( - FeatureResolver::CompileDefaults(feature_set_, {ext}, "2023", "2023"), - HasError(AllOf(HasSubstr("test.Foo.required_field"), - HasSubstr("required field")))); + EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext}, + EDITION_2023, EDITION_2023), + HasError(AllOf(HasSubstr("test.Foo.required_field"), + HasSubstr("required field")))); } TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidWithRepeated) { @@ -710,17 +738,17 @@ TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidWithRepeated) { message Foo { repeated int32 repeated_field = 1 [ targets = TARGET_TYPE_FIELD, - edition_defaults = { edition: "2023", value: "1" } + edition_defaults = { edition_enum: EDITION_2023, value: "1" } ]; } )schema"); ASSERT_NE(file, nullptr); const FieldDescriptor* ext = file->extension(0); - EXPECT_THAT( - FeatureResolver::CompileDefaults(feature_set_, {ext}, "2023", "2023"), - HasError(AllOf(HasSubstr("test.Foo.repeated_field"), - HasSubstr("repeated field")))); + EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext}, + EDITION_2023, EDITION_2023), + HasError(AllOf(HasSubstr("test.Foo.repeated_field"), + HasSubstr("repeated field")))); } TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidWithMissingTarget) { @@ -734,17 +762,17 @@ TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidWithMissingTarget) { } message Foo { optional int32 int_field = 1 [ - edition_defaults = { edition: "2023", value: "1" } + edition_defaults = { edition_enum: EDITION_2023, value: "1" } ]; } )schema"); ASSERT_NE(file, nullptr); const FieldDescriptor* ext = file->extension(0); - EXPECT_THAT( - FeatureResolver::CompileDefaults(feature_set_, {ext}, "2023", "2023"), - HasError(AllOf(HasSubstr("test.Foo.int_field"), - HasSubstr("no target specified")))); + EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext}, + EDITION_2023, EDITION_2023), + HasError(AllOf(HasSubstr("test.Foo.int_field"), + HasSubstr("no target specified")))); } TEST_F(FeatureResolverPoolTest, @@ -763,7 +791,7 @@ TEST_F(FeatureResolverPoolTest, } optional MessageFeature message_field_feature = 12 [ targets = TARGET_TYPE_FIELD, - edition_defaults = { edition: "2023", value: "9987" } + edition_defaults = { edition_enum: EDITION_2023, value: "9987" } ]; } )schema"); @@ -771,7 +799,8 @@ TEST_F(FeatureResolverPoolTest, const FieldDescriptor* ext = file->extension(0); EXPECT_THAT( - FeatureResolver::CompileDefaults(feature_set_, {ext}, "2023", "2023"), + FeatureResolver::CompileDefaults(feature_set_, {ext}, EDITION_2023, + EDITION_2023), HasError(AllOf(HasSubstr("in edition_defaults"), HasSubstr("9987")))); } @@ -791,9 +820,9 @@ TEST_F(FeatureResolverPoolTest, } optional MessageFeature message_field_feature = 12 [ targets = TARGET_TYPE_FIELD, - edition_defaults = { edition: "2025", value: "int_field: 2" }, - edition_defaults = { edition: "2024", value: "int_field: 1" }, - edition_defaults = { edition: "2023", value: "9987" } + edition_defaults = { edition_enum: EDITION_99998_TEST_ONLY, value: "int_field: 2" }, + edition_defaults = { edition_enum: EDITION_99997_TEST_ONLY, value: "int_field: 1" }, + edition_defaults = { edition_enum: EDITION_2023, value: "9987" } ]; } )schema"); @@ -801,7 +830,8 @@ TEST_F(FeatureResolverPoolTest, const FieldDescriptor* ext = file->extension(0); EXPECT_THAT( - FeatureResolver::CompileDefaults(feature_set_, {ext}, "2023", "2025"), + FeatureResolver::CompileDefaults(feature_set_, {ext}, EDITION_2023, + EDITION_99998_TEST_ONLY), HasError(AllOf(HasSubstr("in edition_defaults"), HasSubstr("9987")))); } @@ -821,20 +851,20 @@ TEST_F(FeatureResolverPoolTest, } optional MessageFeature message_field_feature = 12 [ targets = TARGET_TYPE_FIELD, - edition_defaults = { edition: "2024", value: "int_field: 2" }, - edition_defaults = { edition: "2023", value: "int_field: 1" }, - edition_defaults = { edition: "2025", value: "9987" } + edition_defaults = { edition_enum: EDITION_99997_TEST_ONLY, value: "int_field: 2" }, + edition_defaults = { edition_enum: EDITION_2023, value: "int_field: 1" }, + edition_defaults = { edition_enum: EDITION_99998_TEST_ONLY, value: "9987" } ]; } )schema"); ASSERT_NE(file, nullptr); const FieldDescriptor* ext = file->extension(0); - auto defaults = - FeatureResolver::CompileDefaults(feature_set_, {ext}, "2023", "2024"); + auto defaults = FeatureResolver::CompileDefaults( + feature_set_, {ext}, EDITION_2023, EDITION_99997_TEST_ONLY); ASSERT_OK(defaults); - auto resolver = FeatureResolver::Create("2023", *defaults); + auto resolver = FeatureResolver::Create(EDITION_2023, *defaults); ASSERT_OK(resolver); FeatureSet parent, child; EXPECT_OK(resolver->MergeFeatures(parent, child)); @@ -853,7 +883,7 @@ TEST_F(FeatureResolverPoolTest, message Foo { optional int32 int_field_feature = 12 [ targets = TARGET_TYPE_FIELD, - edition_defaults = { edition: "2023", value: "1.23" } + edition_defaults = { edition_enum: EDITION_2023, value: "1.23" } ]; } )schema"); @@ -861,7 +891,8 @@ TEST_F(FeatureResolverPoolTest, const FieldDescriptor* ext = file->extension(0); EXPECT_THAT( - FeatureResolver::CompileDefaults(feature_set_, {ext}, "2023", "2023"), + FeatureResolver::CompileDefaults(feature_set_, {ext}, EDITION_2023, + EDITION_2023), HasError(AllOf(HasSubstr("in edition_defaults"), HasSubstr("1.23")))); } @@ -878,19 +909,19 @@ TEST_F(FeatureResolverPoolTest, message Foo { optional int32 int_field_feature = 12 [ targets = TARGET_TYPE_FIELD, - edition_defaults = { edition: "2024", value: "1.5" }, - edition_defaults = { edition: "2023", value: "1" } + edition_defaults = { edition_enum: EDITION_99997_TEST_ONLY, value: "1.5" }, + edition_defaults = { edition_enum: EDITION_2023, value: "1" } ]; } )schema"); ASSERT_NE(file, nullptr); const FieldDescriptor* ext = file->extension(0); - auto defaults = - FeatureResolver::CompileDefaults(feature_set_, {ext}, "2023", "2023"); + auto defaults = FeatureResolver::CompileDefaults(feature_set_, {ext}, + EDITION_2023, EDITION_2023); ASSERT_OK(defaults); - auto resolver = FeatureResolver::Create("2023", *defaults); + auto resolver = FeatureResolver::Create(EDITION_2023, *defaults); ASSERT_OK(resolver); FeatureSet parent, child; EXPECT_OK(resolver->MergeFeatures(parent, child)); @@ -908,7 +939,7 @@ TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidDefaultsTooEarly) { message Foo { optional int32 int_field_feature = 12 [ targets = TARGET_TYPE_FIELD, - edition_defaults = { edition: "2022", value: "1" } + edition_defaults = { edition_enum: EDITION_2_TEST_ONLY, value: "1" } ]; } )schema"); @@ -916,8 +947,9 @@ TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidDefaultsTooEarly) { const FieldDescriptor* ext = file->extension(0); EXPECT_THAT( - FeatureResolver::CompileDefaults(feature_set_, {ext}, "2023", "2023"), - HasError(HasSubstr("No valid default found for edition 2022"))); + FeatureResolver::CompileDefaults(feature_set_, {ext}, EDITION_2023, + EDITION_2023), + HasError(HasSubstr("No valid default found for edition 2_TEST_ONLY"))); } } // namespace diff --git a/src/google/protobuf/port_def.inc b/src/google/protobuf/port_def.inc index 783d3b222a..1555b05065 100644 --- a/src/google/protobuf/port_def.inc +++ b/src/google/protobuf/port_def.inc @@ -275,12 +275,12 @@ static_assert(PROTOBUF_ABSL_MIN(20230125, 3), #ifdef PROTOBUF_MINIMUM_EDITION #error PROTOBUF_MINIMUM_EDITION was previously defined #endif -#define PROTOBUF_MINIMUM_EDITION "2023" +#define PROTOBUF_MINIMUM_EDITION EDITION_2023 #ifdef PROTOBUF_MAXIMUM_EDITION #error PROTOBUF_MAXIMUM_EDITION was previously defined #endif -#define PROTOBUF_MAXIMUM_EDITION "2023" +#define PROTOBUF_MAXIMUM_EDITION EDITION_2023 #ifdef PROTOBUF_ALWAYS_INLINE #error PROTOBUF_ALWAYS_INLINE was previously defined diff --git a/src/google/protobuf/unittest_features.proto b/src/google/protobuf/unittest_features.proto index 6dd3dc8ac2..27672cadfc 100644 --- a/src/google/protobuf/unittest_features.proto +++ b/src/google/protobuf/unittest_features.proto @@ -57,51 +57,49 @@ message TestFeatures { optional int32 int_file_feature = 1 [ retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FILE, - edition_defaults = { edition: "2025", value: "5" }, - edition_defaults = { edition: "2023.1.2", value: "3" }, - edition_defaults = { edition: "2023", value: "1" }, - edition_defaults = { edition: "2023.1", value: "2" }, - edition_defaults = { edition: "2024", value: "4" } + edition_defaults = { edition_enum: EDITION_2023, value: "1" }, + edition_defaults = { edition_enum: EDITION_99997_TEST_ONLY, value: "2" }, + edition_defaults = { edition_enum: EDITION_99998_TEST_ONLY, value: "3" } ]; optional int32 int_extension_range_feature = 2 [ retention = RETENTION_RUNTIME, targets = TARGET_TYPE_EXTENSION_RANGE, - edition_defaults = { edition: "2023", value: "1" } + edition_defaults = { edition_enum: EDITION_2023, value: "1" } ]; optional int32 int_message_feature = 3 [ retention = RETENTION_RUNTIME, targets = TARGET_TYPE_MESSAGE, - edition_defaults = { edition: "2023", value: "1" } + edition_defaults = { edition_enum: EDITION_2023, value: "1" } ]; optional int32 int_field_feature = 4 [ retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FIELD, - edition_defaults = { edition: "2023", value: "1" } + edition_defaults = { edition_enum: EDITION_2023, value: "1" } ]; optional int32 int_oneof_feature = 5 [ retention = RETENTION_RUNTIME, targets = TARGET_TYPE_ONEOF, - edition_defaults = { edition: "2023", value: "1" } + edition_defaults = { edition_enum: EDITION_2023, value: "1" } ]; optional int32 int_enum_feature = 6 [ retention = RETENTION_RUNTIME, targets = TARGET_TYPE_ENUM, - edition_defaults = { edition: "2023", value: "1" } + edition_defaults = { edition_enum: EDITION_2023, value: "1" } ]; optional int32 int_enum_entry_feature = 7 [ retention = RETENTION_RUNTIME, targets = TARGET_TYPE_ENUM_ENTRY, - edition_defaults = { edition: "2023", value: "1" } + edition_defaults = { edition_enum: EDITION_2023, value: "1" } ]; optional int32 int_service_feature = 8 [ retention = RETENTION_RUNTIME, targets = TARGET_TYPE_SERVICE, - edition_defaults = { edition: "2023", value: "1" } + edition_defaults = { edition_enum: EDITION_2023, value: "1" } ]; optional int32 int_method_feature = 9 [ retention = RETENTION_RUNTIME, targets = TARGET_TYPE_METHOD, - edition_defaults = { edition: "2023", value: "1" } + edition_defaults = { edition_enum: EDITION_2023, value: "1" } ]; optional int32 int_multiple_feature = 10 [ retention = RETENTION_RUNTIME, @@ -114,20 +112,20 @@ message TestFeatures { targets = TARGET_TYPE_METHOD, targets = TARGET_TYPE_ONEOF, targets = TARGET_TYPE_EXTENSION_RANGE, - edition_defaults = { edition: "2023", value: "1" } + edition_defaults = { edition_enum: EDITION_2023, value: "1" } ]; optional bool bool_field_feature = 11 [ retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FIELD, - edition_defaults = { edition: "2023", value: "false" }, - edition_defaults = { edition: "2023.1", value: "true" } + edition_defaults = { edition_enum: EDITION_2023, value: "false" }, + edition_defaults = { edition_enum: EDITION_99997_TEST_ONLY, value: "true" } ]; optional float float_field_feature = 12 [ retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FIELD, - edition_defaults = { edition: "2023", value: "1.1" }, - edition_defaults = { edition: "2023.1", value: "1.2" } + edition_defaults = { edition_enum: EDITION_2023, value: "1.1" }, + edition_defaults = { edition_enum: EDITION_99997_TEST_ONLY, value: "1.2" } ]; message MessageFeature { @@ -139,15 +137,17 @@ message TestFeatures { optional MessageFeature message_field_feature = 13 [ retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FIELD, - edition_defaults = { edition: "2023.1", value: "int_field: 2" }, - edition_defaults = { edition: "2024", value: "string_field: '2024'" }, edition_defaults = { - edition: "2023", - value: "bool_field: true int_field: 1 float_field: 1.5 string_field: '2023'" + edition_enum: EDITION_99997_TEST_ONLY, + value: "int_field: 2" + }, + edition_defaults = { + edition_enum: EDITION_99998_TEST_ONLY, + value: "string_field: '2024'" }, edition_defaults = { - edition: "2025", - value: "int_field: 4 string_field: '2025'" + edition_enum: EDITION_2023, + value: "bool_field: true int_field: 1 float_field: 1.5 string_field: '2023'" } ]; @@ -162,11 +162,15 @@ message TestFeatures { optional EnumFeature enum_field_feature = 14 [ retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FIELD, - edition_defaults = { edition: "2023.10", value: "ENUM_VALUE3" }, - edition_defaults = { edition: "2023.9", value: "ENUM_VALUE2" }, - edition_defaults = { edition: "2025", value: "ENUM_VALUE5" }, - edition_defaults = { edition: "2023", value: "ENUM_VALUE1" }, - edition_defaults = { edition: "2024", value: "ENUM_VALUE4" } + edition_defaults = { edition_enum: EDITION_2023, value: "ENUM_VALUE1" }, + edition_defaults = { + edition_enum: EDITION_99997_TEST_ONLY, + value: "ENUM_VALUE2" + }, + edition_defaults = { + edition_enum: EDITION_99998_TEST_ONLY, + value: "ENUM_VALUE3" + } ]; optional int32 int_source_feature = 15 [ @@ -180,7 +184,7 @@ message TestFeatures { targets = TARGET_TYPE_METHOD, targets = TARGET_TYPE_ONEOF, targets = TARGET_TYPE_EXTENSION_RANGE, - edition_defaults = { edition: "2023", value: "1" } + edition_defaults = { edition_enum: EDITION_2023, value: "1" } ]; optional string string_source_feature = 16 [ @@ -194,7 +198,7 @@ message TestFeatures { targets = TARGET_TYPE_METHOD, targets = TARGET_TYPE_ONEOF, targets = TARGET_TYPE_EXTENSION_RANGE, - edition_defaults = { edition: "2023", value: "'2023'" } + edition_defaults = { edition_enum: EDITION_2023, value: "'2023'" } ]; } diff --git a/src/google/protobuf/util/type_resolver_util_test.cc b/src/google/protobuf/util/type_resolver_util_test.cc index 00391693ff..4400e69f69 100644 --- a/src/google/protobuf/util/type_resolver_util_test.cc +++ b/src/google/protobuf/util/type_resolver_util_test.cc @@ -446,13 +446,13 @@ class DescriptorPoolTypeResolverSyntaxTest : public testing::Test { const FileDescriptor* BuildFile( absl::string_view syntax, - absl::optional edition = absl::nullopt) { + absl::optional edition = absl::nullopt) { FileDescriptorProto proto; proto.set_package("test"); proto.set_name("foo"); proto.set_syntax(syntax); if (edition.has_value()) { - proto.set_edition(*edition); + proto.set_edition_enum(*edition); } DescriptorProto* message = proto.add_message_type(); message->set_name("MyMessage");