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");