Change the behavior of --experimental_editions.

Previously, this gated all editions access and then further checks were applied.  This removes those checks so that we can incrementally roll out editions support to individual languages.  Anyone using the --experimental_editions flag should be careful not to use it on generators that don't support editions.  As we launch editions support for individual generators, this flag won't be required to use them.

PiperOrigin-RevId: 574577631
pull/14373/head
Mike Kruskal 1 year ago committed by Copybara-Service
parent a2ba8bc78e
commit 8ff15497f3
  1. 13
      src/google/protobuf/compiler/code_generator.cc
  2. 18
      src/google/protobuf/compiler/code_generator_unittest.cc
  3. 92
      src/google/protobuf/compiler/command_line_interface.cc
  4. 4
      src/google/protobuf/compiler/command_line_interface.h
  5. 17
      src/google/protobuf/compiler/command_line_interface_unittest.cc
  6. 3
      src/google/protobuf/compiler/java/generator.cc
  7. 3
      src/google/protobuf/compiler/java/kotlin_generator.cc
  8. 19
      src/google/protobuf/compiler/plugin.cc

@ -23,6 +23,9 @@
#include "google/protobuf/descriptor.h"
#include "google/protobuf/feature_resolver.h"
// Must be included last.
#include "google/protobuf/port_def.inc"
namespace google {
namespace protobuf {
namespace compiler {
@ -57,6 +60,14 @@ bool CodeGenerator::GenerateAll(const std::vector<const FileDescriptor*>& files,
absl::StatusOr<FeatureSetDefaults> CodeGenerator::BuildFeatureSetDefaults()
const {
if ((GetSupportedFeatures() & FEATURE_SUPPORTS_EDITIONS) == 0) {
// For generators that don't fully support editions yet, provide an
// optimistic set of defaults. Protoc will check this condition later
// anyway.
return FeatureResolver::CompileDefaults(
FeatureSet::descriptor(), GetFeatureExtensions(),
PROTOBUF_MINIMUM_EDITION, PROTOBUF_MAXIMUM_EDITION);
}
return FeatureResolver::CompileDefaults(
FeatureSet::descriptor(), GetFeatureExtensions(), GetMinimumEdition(),
GetMaximumEdition());
@ -131,3 +142,5 @@ bool IsKnownFeatureProto(absl::string_view filename) {
} // namespace compiler
} // namespace protobuf
} // namespace google
#include "google/protobuf/port_undef.inc"

@ -7,6 +7,7 @@
#include "google/protobuf/compiler/code_generator.h"
#include <cstdint>
#include <string>
#include <vector>
@ -43,6 +44,9 @@ class TestGenerator : public CodeGenerator {
return true;
}
uint64_t GetSupportedFeatures() const override { return features_; }
void set_supported_features(uint64_t features) { features_ = features; }
std::vector<const FieldDescriptor*> GetFeatureExtensions() const override {
return feature_extensions_;
}
@ -65,6 +69,7 @@ class TestGenerator : public CodeGenerator {
using CodeGenerator::GetUnresolvedSourceFeatures;
private:
uint64_t features_ = CodeGenerator::Feature::FEATURE_SUPPORTS_EDITIONS;
Edition minimum_edition_ = PROTOBUF_MINIMUM_EDITION;
Edition maximum_edition_ = PROTOBUF_MAXIMUM_EDITION;
std::vector<const FieldDescriptor*> feature_extensions_ = {
@ -301,6 +306,19 @@ TEST_F(CodeGeneratorTest, BuildFeatureSetDefaults) {
)pb")));
}
TEST_F(CodeGeneratorTest, BuildFeatureSetDefaultsUnsupported) {
TestGenerator generator;
generator.set_supported_features(0);
generator.set_feature_extensions({});
generator.set_minimum_edition(EDITION_99997_TEST_ONLY);
generator.set_maximum_edition(EDITION_99999_TEST_ONLY);
auto result = generator.BuildFeatureSetDefaults();
ASSERT_TRUE(result.ok()) << result.status().message();
EXPECT_EQ(result->minimum_edition(), PROTOBUF_MINIMUM_EDITION);
EXPECT_EQ(result->maximum_edition(), PROTOBUF_MAXIMUM_EDITION);
}
#include "google/protobuf/port_undef.inc"
} // namespace

@ -11,6 +11,7 @@
#include "google/protobuf/compiler/command_line_interface.h"
#include <cstdint>
#include <cstdlib>
#include "absl/algorithm/container.h"
@ -295,28 +296,6 @@ bool GetBootstrapParam(const std::string& parameter) {
}
bool EnforceEditionsSupport(
const std::string& codegen_name, uint64_t supported_features,
const std::vector<const FileDescriptor*>& parsed_files) {
if ((supported_features & CodeGenerator::FEATURE_SUPPORTS_EDITIONS) == 0) {
for (const auto fd : parsed_files) {
if (FileDescriptorLegacy(fd).syntax() ==
FileDescriptorLegacy::SYNTAX_EDITIONS) {
std::cerr
<< fd->name() << ": is an editions file, but code generator "
<< codegen_name
<< " hasn't been updated to support editions yet. Please ask "
"the owner of this code generator to add support or "
"switch back to proto2/proto3.\n\nSee "
"https://protobuf.dev/editions/overview/ for more information."
<< std::endl;
return false;
}
}
}
return true;
}
} // namespace
void CommandLineInterface::GetTransitiveDependencies(
@ -1538,24 +1517,26 @@ bool CommandLineInterface::SetupFeatureResolution(DescriptorPool& pool) {
for (const auto& output : output_directives_) {
if (output.generator == nullptr) continue;
if ((output.generator->GetSupportedFeatures() &
CodeGenerator::FEATURE_SUPPORTS_EDITIONS) == 0) {
continue;
}
if (output.generator->GetMinimumEdition() != PROTOBUF_MINIMUM_EDITION) {
ABSL_LOG(ERROR) << "Built-in generator " << output.name
<< " specifies a minimum edition "
<< output.generator->GetMinimumEdition()
<< " which is not the protoc minimum "
<< PROTOBUF_MINIMUM_EDITION << ".";
return false;
}
if (output.generator->GetMaximumEdition() != PROTOBUF_MAXIMUM_EDITION) {
ABSL_LOG(ERROR) << "Built-in generator " << output.name
<< " specifies a maximum edition "
<< output.generator->GetMaximumEdition()
<< " which is not the protoc maximum "
<< PROTOBUF_MAXIMUM_EDITION << ".";
return false;
CodeGenerator::FEATURE_SUPPORTS_EDITIONS) != 0) {
// Only validate min/max edition on generators that advertise editions
// support. Generators still under development will always use the
// correct values.
if (output.generator->GetMinimumEdition() != minimum_edition) {
ABSL_LOG(ERROR) << "Built-in generator " << output.name
<< " specifies a minimum edition "
<< output.generator->GetMinimumEdition()
<< " which is not the protoc minimum "
<< minimum_edition << ".";
return false;
}
if (output.generator->GetMaximumEdition() != maximum_edition) {
ABSL_LOG(ERROR) << "Built-in generator " << output.name
<< " specifies a maximum edition "
<< output.generator->GetMaximumEdition()
<< " which is not the protoc maximum "
<< maximum_edition << ".";
return false;
}
}
for (const FieldDescriptor* ext :
output.generator->GetFeatureExtensions()) {
@ -2566,6 +2547,37 @@ bool CommandLineInterface::EnforceProto3OptionalSupport(
return true;
}
bool CommandLineInterface::EnforceEditionsSupport(
const std::string& codegen_name, uint64_t supported_features,
const std::vector<const FileDescriptor*>& parsed_files) const {
if (supported_features & CodeGenerator::FEATURE_SUPPORTS_EDITIONS) {
// This generator explicitly supports editions.
return true;
}
if (experimental_editions_) {
// The user has explicitly specified the experimental flag.
return true;
}
for (const auto* fd : parsed_files) {
// Skip enforcement for allowlisted files.
if (IsEarlyEditionsFile(fd->name())) continue;
if (FileDescriptorLegacy(fd).syntax() ==
FileDescriptorLegacy::SYNTAX_EDITIONS) {
std::cerr
<< fd->name() << ": is an editions file, but code generator "
<< codegen_name
<< " hasn't been updated to support editions yet. Please ask "
"the owner of this code generator to add support or "
"switch back to proto2/proto3.\n\nSee "
"https://protobuf.dev/editions/overview/ for more information."
<< std::endl;
return false;
}
}
return true;
}
bool CommandLineInterface::GenerateOutput(
const std::vector<const FileDescriptor*>& parsed_files,
const OutputDirective& output_directive,

@ -222,6 +222,10 @@ class PROTOC_EXPORT CommandLineInterface {
const std::string& codegen_name, uint64_t supported_features,
const std::vector<const FileDescriptor*>& parsed_files) const;
bool EnforceEditionsSupport(
const std::string& codegen_name, uint64_t supported_features,
const std::vector<const FileDescriptor*>& parsed_files) const;
// Return status for ParseArguments() and InterpretArgument().
enum ParseArgumentStatus {

@ -1354,16 +1354,14 @@ TEST_F(CommandLineInterfaceTest, AllowServicesHasService) {
ExpectGenerated("test_generator", "", "foo.proto", "Foo");
}
TEST_F(CommandLineInterfaceTest, EditionsAreNotAllowed) {
TEST_F(CommandLineInterfaceTest, NonExperimentalEditions) {
CreateTempFile("foo.proto",
"edition = \"2023\";\n"
"message FooRequest {}\n");
Run("protocol_compiler --proto_path=$tmpdir --test_out=$tmpdir foo.proto");
ExpectErrorSubstring(
"This file uses editions, but --experimental_editions has not been "
"enabled.");
ExpectErrorSubstring("--experimental_editions has not been enabled");
}
TEST_F(CommandLineInterfaceTest, EditionsFlagExplicitTrue) {
@ -1713,12 +1711,10 @@ TEST_F(CommandLineInterfaceTest, GeneratorNoEditionsSupport) {
"Doesn't support editions",
CodeGenerator::FEATURE_SUPPORTS_EDITIONS);
Run("protocol_compiler --experimental_editions "
Run("protocol_compiler "
"--proto_path=$tmpdir foo.proto --no_editions_out=$tmpdir");
ExpectErrorSubstring(
"code generator --no_editions_out hasn't been updated to support "
"editions");
ExpectErrorSubstring("--experimental_editions has not been enabled");
}
TEST_F(CommandLineInterfaceTest, PluginNoEditionsSupport) {
@ -1730,11 +1726,10 @@ TEST_F(CommandLineInterfaceTest, PluginNoEditionsSupport) {
)schema");
SetMockGeneratorTestCase("no_editions");
Run("protocol_compiler --experimental_editions "
Run("protocol_compiler "
"--proto_path=$tmpdir foo.proto --plug_out=$tmpdir");
ExpectErrorSubstring(
"code generator prefix-gen-plug hasn't been updated to support editions");
ExpectErrorSubstring("--experimental_editions has not been enabled");
}
TEST_F(CommandLineInterfaceTest, EditionDefaults) {

@ -38,8 +38,7 @@ JavaGenerator::JavaGenerator() {}
JavaGenerator::~JavaGenerator() {}
uint64_t JavaGenerator::GetSupportedFeatures() const {
return CodeGenerator::Feature::FEATURE_PROTO3_OPTIONAL |
CodeGenerator::Feature::FEATURE_SUPPORTS_EDITIONS;
return CodeGenerator::Feature::FEATURE_PROTO3_OPTIONAL;
}
bool JavaGenerator::Generate(const FileDescriptor* file,

@ -22,8 +22,7 @@ KotlinGenerator::KotlinGenerator() {}
KotlinGenerator::~KotlinGenerator() {}
uint64_t KotlinGenerator::GetSupportedFeatures() const {
return CodeGenerator::Feature::FEATURE_PROTO3_OPTIONAL |
CodeGenerator::Feature::FEATURE_SUPPORTS_EDITIONS;
return CodeGenerator::Feature::FEATURE_PROTO3_OPTIONAL;
}
bool KotlinGenerator::Generate(const FileDescriptor* file,

@ -94,18 +94,15 @@ bool GenerateCode(const CodeGeneratorRequest& request,
CodeGeneratorResponse* response, std::string* error_msg) {
DescriptorPool pool;
if (generator.GetSupportedFeatures() &
CodeGenerator::FEATURE_SUPPORTS_EDITIONS) {
// Initialize feature set default mapping.
absl::StatusOr<FeatureSetDefaults> defaults =
generator.BuildFeatureSetDefaults();
if (!defaults.ok()) {
*error_msg = absl::StrCat("error generating feature defaults: ",
defaults.status().message());
return false;
}
pool.SetFeatureSetDefaults(*defaults);
// Initialize feature set default mapping.
absl::StatusOr<FeatureSetDefaults> defaults =
generator.BuildFeatureSetDefaults();
if (!defaults.ok()) {
*error_msg = absl::StrCat("error generating feature defaults: ",
defaults.status().message());
return false;
}
pool.SetFeatureSetDefaults(*defaults);
for (int i = 0; i < request.proto_file_size(); i++) {
const FileDescriptor* file = pool.BuildFile(request.proto_file(i));

Loading…
Cancel
Save