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
pull/13824/head
Mike Kruskal 1 year ago committed by Copybara-Service
parent 0e484c7777
commit f083ebf21f
  1. 8
      src/google/protobuf/compiler/code_generator.h
  2. 26
      src/google/protobuf/compiler/code_generator_unittest.cc
  3. 4
      src/google/protobuf/compiler/command_line_interface.cc
  4. 12
      src/google/protobuf/compiler/command_line_interface_unittest.cc
  5. 16
      src/google/protobuf/compiler/mock_code_generator.h
  6. 10
      src/google/protobuf/compiler/parser.cc
  7. 2
      src/google/protobuf/compiler/parser.h
  8. 49
      src/google/protobuf/compiler/parser_unittest.cc
  9. 33
      src/google/protobuf/descriptor.cc
  10. 19
      src/google/protobuf/descriptor.h
  11. 220
      src/google/protobuf/descriptor_unittest.cc
  12. 73
      src/google/protobuf/feature_resolver.cc
  13. 4
      src/google/protobuf/feature_resolver.h
  14. 264
      src/google/protobuf/feature_resolver_test.cc
  15. 4
      src/google/protobuf/port_def.inc
  16. 66
      src/google/protobuf/unittest_features.proto
  17. 4
      src/google/protobuf/util/type_resolver_util_test.cc

@ -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.
//

@ -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<const FieldDescriptor*> 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")));
}

@ -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<const FieldDescriptor*> 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() &

@ -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) {

@ -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<const FieldDescriptor*> feature_extensions_ = {
GetExtensionReflection(pb::test)};

@ -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_) {

@ -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.

@ -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;

@ -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<FileDescriptor>(1);
alloc.PlanArray<FileDescriptorTables>(1);
alloc.PlanArray<std::string>(
2 + (proto.has_edition() ? 1 : 0)); // name + package
alloc.PlanArray<std::string>(2); // name + package
if (proto.has_options()) alloc.PlanArray<FileOptions>(1);
if (proto.has_edition()) {
if (proto.has_edition_enum()) {
alloc.PlanArray<FeatureSet>(1);
if (HasFeatures(proto.options())) {
alloc.PlanArray<FeatureSet>(1);
@ -5705,14 +5703,14 @@ FileDescriptor* DescriptorBuilder::BuildFileImpl(
FileDescriptor* result = alloc.AllocateArray<FileDescriptor>(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<FeatureResolver> 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<const char*>(kAntiHyrumText),
(reinterpret_cast<size_t>(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

@ -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 <typename Sink>
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.

@ -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 {

@ -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<absl::string_view> as = absl::StrSplit(a, '.');
std::vector<absl::string_view> 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<std::string, EditionsLessThan>& editions) {
void CollectEditions(const Descriptor& descriptor, Edition minimum_edition,
Edition maximum_edition,
absl::btree_set<Edition>& 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<FeatureSetDefaults> FeatureResolver::CompileDefaults(
const Descriptor* feature_set,
absl::Span<const FieldDescriptor* const> 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<FeatureSetDefaults> FeatureResolver::CompileDefaults(
}
// Collect all the editions with unique defaults.
absl::btree_set<std::string, EditionsLessThan> editions;
absl::btree_set<Edition> 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<FeatureSetDefaults> 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<DynamicMessageFactory>();
for (const auto& edition : editions) {
auto defaults_dynamic =
@ -276,7 +257,7 @@ absl::StatusOr<FeatureSetDefaults> 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<FeatureSetDefaults> FeatureResolver::CompileDefaults(
}
absl::StatusOr<FeatureResolver> 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()) {

@ -64,12 +64,12 @@ class PROTOBUF_EXPORT FeatureResolver {
static absl::StatusOr<FeatureSetDefaults> CompileDefaults(
const Descriptor* feature_set,
absl::Span<const FieldDescriptor* const> 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<FeatureResolver> 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

@ -93,17 +93,17 @@ const FieldDescriptor* GetExtension(
}
template <typename... Extensions>
absl::StatusOr<FeatureResolver> SetupFeatureResolver(absl::string_view edition,
absl::StatusOr<FeatureResolver> SetupFeatureResolver(Edition edition,
Extensions... extensions) {
absl::StatusOr<FeatureSetDefaults> 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<FeatureSet> GetDefaults(absl::string_view edition,
absl::StatusOr<FeatureSet> GetDefaults(Edition edition,
const FeatureSetDefaults& defaults) {
absl::StatusOr<FeatureResolver> resolver =
FeatureResolver::Create(edition, defaults);
@ -113,12 +113,12 @@ absl::StatusOr<FeatureSet> GetDefaults(absl::string_view edition,
}
template <typename... Extensions>
absl::StatusOr<FeatureSet> GetDefaults(absl::string_view edition,
absl::StatusOr<FeatureSet> GetDefaults(Edition edition,
Extensions... extensions) {
absl::StatusOr<FeatureSetDefaults> 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<FeatureSet> merged = GetDefaults("2023");
absl::StatusOr<FeatureSet> 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<FeatureSet> merged = GetDefaults("2023", pb::test);
absl::StatusOr<FeatureSet> 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<FeatureSet> 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<FeatureSet> 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<FeatureSetDefaults> 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<FeatureSet> merged = GetDefaults("2022", pb::test);
absl::StatusOr<FeatureSetDefaults> defaults =
FeatureResolver::CompileDefaults(
FeatureSet::descriptor(), {GetExtension(pb::test)},
EDITION_1_TEST_ONLY, EDITION_99999_TEST_ONLY);
ASSERT_OK(defaults);
absl::StatusOr<FeatureSet> 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<FeatureSet> merged = GetDefaults("3456", pb::test);
absl::StatusOr<FeatureSet> 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<FeatureSet> 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<FeatureSet> merged = GetDefaults("2023.90", pb::test);
absl::StatusOr<FeatureSet> 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<FeatureSet> merged = GetDefaults("2023", pb::test);
absl::StatusOr<FeatureSet> 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<FeatureSet> merged = GetDefaults("2023.1", pb::test);
absl::StatusOr<FeatureSet> 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<FeatureSet> merged = GetDefaults("2024", pb::test);
absl::StatusOr<FeatureSet> 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<FeatureResolver> resolver = SetupFeatureResolver("2023");
absl::StatusOr<FeatureResolver> 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<FeatureResolver> 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<FeatureResolver> 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<FeatureResolver> resolver = SetupFeatureResolver("2023");
absl::StatusOr<FeatureResolver> 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<FeatureResolver> resolver = SetupFeatureResolver("2023");
absl::StatusOr<FeatureResolver> 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<FeatureResolver> resolver = SetupFeatureResolver("2023");
absl::StatusOr<FeatureResolver> 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<FeatureResolver> resolver = SetupFeatureResolver("2023");
absl::StatusOr<FeatureResolver> 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<FeatureResolver> 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

@ -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

@ -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'" }
];
}

@ -446,13 +446,13 @@ class DescriptorPoolTypeResolverSyntaxTest : public testing::Test {
const FileDescriptor* BuildFile(
absl::string_view syntax,
absl::optional<absl::string_view> edition = absl::nullopt) {
absl::optional<Edition> 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");

Loading…
Cancel
Save