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/descriptor.h"
#include "google/protobuf/feature_resolver.h" #include "google/protobuf/feature_resolver.h"
// Must be included last.
#include "google/protobuf/port_def.inc"
namespace google { namespace google {
namespace protobuf { namespace protobuf {
namespace compiler { namespace compiler {
@ -57,6 +60,14 @@ bool CodeGenerator::GenerateAll(const std::vector<const FileDescriptor*>& files,
absl::StatusOr<FeatureSetDefaults> CodeGenerator::BuildFeatureSetDefaults() absl::StatusOr<FeatureSetDefaults> CodeGenerator::BuildFeatureSetDefaults()
const { 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( return FeatureResolver::CompileDefaults(
FeatureSet::descriptor(), GetFeatureExtensions(), GetMinimumEdition(), FeatureSet::descriptor(), GetFeatureExtensions(), GetMinimumEdition(),
GetMaximumEdition()); GetMaximumEdition());
@ -131,3 +142,5 @@ bool IsKnownFeatureProto(absl::string_view filename) {
} // namespace compiler } // namespace compiler
} // namespace protobuf } // namespace protobuf
} // namespace google } // namespace google
#include "google/protobuf/port_undef.inc"

@ -7,6 +7,7 @@
#include "google/protobuf/compiler/code_generator.h" #include "google/protobuf/compiler/code_generator.h"
#include <cstdint>
#include <string> #include <string>
#include <vector> #include <vector>
@ -43,6 +44,9 @@ class TestGenerator : public CodeGenerator {
return true; 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 { std::vector<const FieldDescriptor*> GetFeatureExtensions() const override {
return feature_extensions_; return feature_extensions_;
} }
@ -65,6 +69,7 @@ class TestGenerator : public CodeGenerator {
using CodeGenerator::GetUnresolvedSourceFeatures; using CodeGenerator::GetUnresolvedSourceFeatures;
private: private:
uint64_t features_ = CodeGenerator::Feature::FEATURE_SUPPORTS_EDITIONS;
Edition minimum_edition_ = PROTOBUF_MINIMUM_EDITION; Edition minimum_edition_ = PROTOBUF_MINIMUM_EDITION;
Edition maximum_edition_ = PROTOBUF_MAXIMUM_EDITION; Edition maximum_edition_ = PROTOBUF_MAXIMUM_EDITION;
std::vector<const FieldDescriptor*> feature_extensions_ = { std::vector<const FieldDescriptor*> feature_extensions_ = {
@ -301,6 +306,19 @@ TEST_F(CodeGeneratorTest, BuildFeatureSetDefaults) {
)pb"))); )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" #include "google/protobuf/port_undef.inc"
} // namespace } // namespace

@ -11,6 +11,7 @@
#include "google/protobuf/compiler/command_line_interface.h" #include "google/protobuf/compiler/command_line_interface.h"
#include <cstdint>
#include <cstdlib> #include <cstdlib>
#include "absl/algorithm/container.h" #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 } // namespace
void CommandLineInterface::GetTransitiveDependencies( void CommandLineInterface::GetTransitiveDependencies(
@ -1538,24 +1517,26 @@ bool CommandLineInterface::SetupFeatureResolution(DescriptorPool& pool) {
for (const auto& output : output_directives_) { for (const auto& output : output_directives_) {
if (output.generator == nullptr) continue; if (output.generator == nullptr) continue;
if ((output.generator->GetSupportedFeatures() & if ((output.generator->GetSupportedFeatures() &
CodeGenerator::FEATURE_SUPPORTS_EDITIONS) == 0) { CodeGenerator::FEATURE_SUPPORTS_EDITIONS) != 0) {
continue; // Only validate min/max edition on generators that advertise editions
} // support. Generators still under development will always use the
if (output.generator->GetMinimumEdition() != PROTOBUF_MINIMUM_EDITION) { // correct values.
ABSL_LOG(ERROR) << "Built-in generator " << output.name if (output.generator->GetMinimumEdition() != minimum_edition) {
<< " specifies a minimum edition " ABSL_LOG(ERROR) << "Built-in generator " << output.name
<< output.generator->GetMinimumEdition() << " specifies a minimum edition "
<< " which is not the protoc minimum " << output.generator->GetMinimumEdition()
<< PROTOBUF_MINIMUM_EDITION << "."; << " which is not the protoc minimum "
return false; << minimum_edition << ".";
} return false;
if (output.generator->GetMaximumEdition() != PROTOBUF_MAXIMUM_EDITION) { }
ABSL_LOG(ERROR) << "Built-in generator " << output.name if (output.generator->GetMaximumEdition() != maximum_edition) {
<< " specifies a maximum edition " ABSL_LOG(ERROR) << "Built-in generator " << output.name
<< output.generator->GetMaximumEdition() << " specifies a maximum edition "
<< " which is not the protoc maximum " << output.generator->GetMaximumEdition()
<< PROTOBUF_MAXIMUM_EDITION << "."; << " which is not the protoc maximum "
return false; << maximum_edition << ".";
return false;
}
} }
for (const FieldDescriptor* ext : for (const FieldDescriptor* ext :
output.generator->GetFeatureExtensions()) { output.generator->GetFeatureExtensions()) {
@ -2566,6 +2547,37 @@ bool CommandLineInterface::EnforceProto3OptionalSupport(
return true; 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( bool CommandLineInterface::GenerateOutput(
const std::vector<const FileDescriptor*>& parsed_files, const std::vector<const FileDescriptor*>& parsed_files,
const OutputDirective& output_directive, const OutputDirective& output_directive,

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

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

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

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

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

Loading…
Cancel
Save