Skip to content

Commit e2cec78

Browse files
Allow code generators to specify whether or not they support editions.
Editions are still flag-guarded by the `--experimental_editions` flag for now, but once that's removed in a later release generators will need to explicitly specify that their support. This will avoid cases where generators may happen to work for editions but produce incorrect code. PiperOrigin-RevId: 547959326
1 parent f1de28a commit e2cec78

13 files changed

+122
-51
lines changed

src/google/protobuf/BUILD.bazel

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -739,39 +739,7 @@ proto_library(
739739

740740
proto_library(
741741
name = "generic_test_protos",
742-
srcs = [
743-
"map_proto2_unittest.proto",
744-
"map_proto3_unittest.proto",
745-
"map_unittest.proto",
746-
"unittest.proto",
747-
"unittest_arena.proto",
748-
"unittest_custom_options.proto",
749-
"unittest_drop_unknown_fields.proto",
750-
"unittest_embed_optimize_for.proto",
751-
"unittest_empty.proto",
752-
"unittest_enormous_descriptor.proto",
753-
"unittest_import.proto",
754-
"unittest_import_public.proto",
755-
"unittest_lazy_dependencies.proto",
756-
"unittest_lazy_dependencies_custom_option.proto",
757-
"unittest_lazy_dependencies_enum.proto",
758-
"unittest_lite_imports_nonlite.proto",
759-
"unittest_mset.proto",
760-
"unittest_mset_wire_format.proto",
761-
"unittest_no_field_presence.proto",
762-
"unittest_no_generic_services.proto",
763-
"unittest_optimize_for.proto",
764-
"unittest_preserve_unknown_enum.proto",
765-
"unittest_preserve_unknown_enum2.proto",
766-
"unittest_proto3.proto",
767-
"unittest_proto3_arena.proto",
768-
"unittest_proto3_arena_lite.proto",
769-
"unittest_proto3_bad_macros.proto",
770-
"unittest_proto3_lite.proto",
771-
"unittest_proto3_optional.proto",
772-
"unittest_retention.proto",
773-
"unittest_well_known_types.proto",
774-
],
742+
srcs = [":test_proto_srcs"],
775743
strip_import_prefix = "/src",
776744
visibility = ["//:__subpackages__"],
777745
deps = [

src/google/protobuf/compiler/code_generator.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,10 @@ class PROTOC_EXPORT CodeGenerator {
113113

114114
// This must be kept in sync with plugin.proto. See that file for
115115
// documentation on each value.
116+
// TODO(b/291092901) Use CodeGeneratorResponse.Feature here.
116117
enum Feature {
117118
FEATURE_PROTO3_OPTIONAL = 1,
119+
FEATURE_SUPPORTS_EDITIONS = 2,
118120
};
119121

120122
// Implement this to indicate what features this code generator supports.

src/google/protobuf/compiler/code_generator_unittest.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,20 @@ TEST_F(CodeGeneratorTest, GetSourceFeaturesInherited) {
224224
EXPECT_EQ(ext.string_source_feature(), "field");
225225
}
226226

227+
TEST_F(CodeGeneratorTest, GetRuntimeProtoTrivial) {
228+
auto file = BuildFile(R"schema(
229+
edition = "2023";
230+
package protobuf_unittest;
231+
)schema");
232+
ASSERT_THAT(file, NotNull());
233+
234+
FileDescriptorProto proto = TestGenerator::GetRuntimeProto(*file);
235+
const FeatureSet& features = proto.options().features();
236+
237+
EXPECT_TRUE(features.has_raw_features());
238+
EXPECT_THAT(features.raw_features(), EqualsProto(R"pb()pb"));
239+
}
240+
227241
TEST_F(CodeGeneratorTest, GetRuntimeProtoRoot) {
228242
auto file = BuildFile(R"schema(
229243
edition = "2023";

src/google/protobuf/compiler/command_line_interface.cc

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,26 @@ std::string PluginName(absl::string_view plugin_prefix,
302302
}
303303

304304

305+
bool EnforceEditionsSupport(
306+
const std::string& codegen_name, uint64_t supported_features,
307+
const std::vector<const FileDescriptor*>& parsed_files) {
308+
if ((supported_features & CodeGenerator::FEATURE_SUPPORTS_EDITIONS) == 0) {
309+
for (const auto fd : parsed_files) {
310+
if (FileDescriptorLegacy(fd).syntax() ==
311+
FileDescriptorLegacy::SYNTAX_EDITIONS) {
312+
std::cerr << fd->name() << ": is an editions file, but code generator "
313+
<< codegen_name
314+
<< " hasn't been updated to support editions yet. Please ask "
315+
"the owner of this code generator to add support or "
316+
"switch back to proto2/proto3."
317+
<< std::endl;
318+
return false;
319+
}
320+
}
321+
}
322+
return true;
323+
}
324+
305325
} // namespace
306326

307327
void CommandLineInterface::GetTransitiveDependencies(
@@ -2490,6 +2510,12 @@ bool CommandLineInterface::GenerateOutput(
24902510
return false;
24912511
}
24922512

2513+
if (!EnforceEditionsSupport(
2514+
output_directive.name,
2515+
output_directive.generator->GetSupportedFeatures(), parsed_files)) {
2516+
return false;
2517+
}
2518+
24932519
if (!output_directive.generator->GenerateAll(parsed_files, parameters,
24942520
generator_context, &error)) {
24952521
// Generator returned an error.
@@ -2686,6 +2712,9 @@ bool CommandLineInterface::GeneratePluginOutput(
26862712
} else if (!EnforceProto3OptionalSupport(
26872713
plugin_name, response.supported_features(), parsed_files)) {
26882714
return false;
2715+
} else if (!EnforceEditionsSupport(plugin_name, response.supported_features(),
2716+
parsed_files)) {
2717+
return false;
26892718
}
26902719

26912720
return true;

src/google/protobuf/compiler/command_line_interface_unittest.cc

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,6 +1643,41 @@ TEST_F(CommandLineInterfaceTest, Plugin_SourceFeatures) {
16431643
}
16441644
}
16451645

1646+
TEST_F(CommandLineInterfaceTest, GeneratorNoEditionsSupport) {
1647+
CreateTempFile("foo.proto", R"schema(
1648+
edition = "2023";
1649+
message Foo {
1650+
int32 i = 1;
1651+
}
1652+
)schema");
1653+
1654+
CreateGeneratorWithMissingFeatures("--no_editions_out",
1655+
"Doesn't support editions",
1656+
CodeGenerator::FEATURE_SUPPORTS_EDITIONS);
1657+
1658+
Run("protocol_compiler --experimental_editions "
1659+
"--proto_path=$tmpdir foo.proto --no_editions_out=$tmpdir");
1660+
1661+
ExpectErrorSubstring(
1662+
"code generator --no_editions_out hasn't been updated to support "
1663+
"editions");
1664+
}
1665+
1666+
TEST_F(CommandLineInterfaceTest, PluginNoEditionsSupport) {
1667+
CreateTempFile("foo.proto", R"schema(
1668+
edition = "2023";
1669+
message Foo {
1670+
int32 i = 1;
1671+
}
1672+
)schema");
1673+
1674+
Run("protocol_compiler --experimental_editions "
1675+
"--proto_path=$tmpdir foo.proto --plug_out=no_editions:$tmpdir");
1676+
1677+
ExpectErrorSubstring(
1678+
"code generator prefix-gen-plug hasn't been updated to support editions");
1679+
}
1680+
16461681
#endif // PROTOBUF_FUTURE_EDITIONS
16471682

16481683

src/google/protobuf/compiler/cpp/generator.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class PROTOC_EXPORT CppGenerator : public CodeGenerator {
8989
std::string* error) const override;
9090

9191
uint64_t GetSupportedFeatures() const override {
92-
return FEATURE_PROTO3_OPTIONAL;
92+
return FEATURE_PROTO3_OPTIONAL | FEATURE_SUPPORTS_EDITIONS;
9393
}
9494

9595
private:

src/google/protobuf/compiler/fake_plugin.cc

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
#include "google/protobuf/compiler/plugin.pb.h"
4040
#include "google/protobuf/io/io_win32.h"
4141

42+
using google::protobuf::compiler::CodeGeneratorRequest;
43+
using google::protobuf::compiler::CodeGeneratorResponse;
44+
4245
// This fake protoc plugin does nothing but write out the CodeGeneratorRequest
4346
// in base64. This is not very useful except that it gives us a way to make
4447
// assertions in tests about the contents of requests that protoc sends to
@@ -50,10 +53,12 @@ int main(int argc, char* argv[]) {
5053
google::protobuf::io::win32::setmode(STDOUT_FILENO, _O_BINARY);
5154
#endif
5255

53-
google::protobuf::compiler::CodeGeneratorRequest request;
56+
CodeGeneratorRequest request;
5457
ABSL_CHECK(request.ParseFromFileDescriptor(STDIN_FILENO));
5558
ABSL_CHECK(!request.file_to_generate().empty());
56-
google::protobuf::compiler::CodeGeneratorResponse response;
59+
CodeGeneratorResponse response;
60+
response.set_supported_features(
61+
CodeGeneratorResponse::FEATURE_SUPPORTS_EDITIONS);
5762
response.add_file()->set_name(
5863
absl::StrCat(request.file_to_generate(0), ".request"));
5964
response.mutable_file(0)->set_content(

src/google/protobuf/compiler/mock_code_generator.cc

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include <memory>
4040
#include <ostream>
4141
#include <string>
42+
#include <utility>
4243
#include <vector>
4344

4445
#include "google/protobuf/testing/file.h"
@@ -96,7 +97,8 @@ MockCodeGenerator::MockCodeGenerator(absl::string_view name) : name_(name) {}
9697
MockCodeGenerator::~MockCodeGenerator() = default;
9798

9899
uint64_t MockCodeGenerator::GetSupportedFeatures() const {
99-
uint64_t all_features = CodeGenerator::FEATURE_PROTO3_OPTIONAL;
100+
uint64_t all_features = CodeGenerator::FEATURE_PROTO3_OPTIONAL |
101+
CodeGenerator::FEATURE_SUPPORTS_EDITIONS;
100102
return all_features & ~suppressed_features_;
101103
}
102104

@@ -212,6 +214,16 @@ bool MockCodeGenerator::Generate(const FileDescriptor* file,
212214
const std::string& parameter,
213215
GeneratorContext* context,
214216
std::string* error) const {
217+
std::vector<std::pair<std::string, std::string>> options;
218+
ParseGeneratorParameter(parameter, &options);
219+
for (const auto& option : options) {
220+
const auto& key = option.first;
221+
222+
if (key == "no_editions") {
223+
suppressed_features_ |= CodeGenerator::FEATURE_SUPPORTS_EDITIONS;
224+
}
225+
}
226+
215227
bool annotate = false;
216228
for (int i = 0; i < file->message_type_count(); i++) {
217229
if (absl::StartsWith(file->message_type(i)->name(), "MockCodeGenerator_")) {

src/google/protobuf/compiler/mock_code_generator.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,10 @@ class MockCodeGenerator : public CodeGenerator {
118118

119119
private:
120120
std::string name_;
121-
uint64_t suppressed_features_ = 0;
121+
122+
// Mark this mutable so that our test plugin can modify it during the Generate
123+
// call via generator flags.
124+
mutable uint64_t suppressed_features_ = 0;
122125

123126
static std::string GetOutputFileContent(absl::string_view generator_name,
124127
absl::string_view parameter,

src/google/protobuf/compiler/plugin.pb.cc

Lines changed: 10 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)