Skip to content

Commit 250c550

Browse files
Fix python codegen crash when C++ features are used. (#20577)
What we actually want to prevent is any future use of non-enum features or python-specific features which don't exist today. If other language features are present we can safely strip them from the python gencode. We also reserve an extension number for the future python features in descriptor.proto PiperOrigin-RevId: 733885839
1 parent 3576a1f commit 250c550

File tree

7 files changed

+125
-6
lines changed

7 files changed

+125
-6
lines changed

src/google/protobuf/compiler/cpp/BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ cc_library(
105105
visibility = [
106106
"//pkg:__pkg__",
107107
"//src/google/protobuf/compiler:__pkg__",
108+
"//src/google/protobuf/compiler/python:__pkg__", # For testing only.
108109
"@io_kythe//kythe/cxx/tools:__subpackages__",
109110
],
110111
deps = [

src/google/protobuf/compiler/python/BUILD.bazel

+4
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ cc_test(
5151
copts = COPTS,
5252
deps = [
5353
":python",
54+
"//src/google/protobuf",
55+
"//src/google/protobuf/compiler:code_generator",
5456
"//src/google/protobuf/compiler:command_line_interface",
57+
"//src/google/protobuf/compiler:command_line_interface_tester",
58+
"//src/google/protobuf/compiler/cpp",
5559
"//src/google/protobuf/io",
5660
"//src/google/protobuf/io:printer",
5761
"//src/google/protobuf/testing",

src/google/protobuf/compiler/python/generator.cc

+11-2
Original file line numberDiff line numberDiff line change
@@ -479,8 +479,17 @@ std::string Generator::GetResolvedFeatures(
479479
// Assume these are all enums. If we add non-enum global features or any
480480
// python-specific features, we will need to come back and improve this
481481
// logic.
482-
ABSL_CHECK(field->enum_type() != nullptr)
483-
<< "Unexpected non-enum field found!";
482+
if (field->type() != FieldDescriptor::TYPE_ENUM) {
483+
ABSL_CHECK(field->is_extension())
484+
<< "Unsupported non-enum global feature found: "
485+
<< field->full_name();
486+
// Placeholder for python-specific features.
487+
ABSL_CHECK(field->number() != 1003)
488+
<< "Unsupported python-specific feature found: "
489+
<< field->full_name();
490+
// Skip any non-python language-specific features.
491+
continue;
492+
}
484493
if (field->options().retention() == FieldOptions::RETENTION_SOURCE) {
485494
// Skip any source-retention features.
486495
continue;

src/google/protobuf/compiler/python/plugin_unittest.cc

+60-3
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,20 @@
99

1010
#include <memory>
1111
#include <string>
12+
#include <utility>
1213
#include <vector>
1314

1415
#include "google/protobuf/testing/file.h"
15-
#include "google/protobuf/testing/file.h"
16-
#include "google/protobuf/compiler/command_line_interface.h"
17-
#include "google/protobuf/compiler/python/generator.h"
1816
#include <gtest/gtest.h>
1917
#include "absl/log/absl_check.h"
18+
#include "absl/strings/str_cat.h"
2019
#include "absl/strings/str_split.h"
20+
#include "absl/strings/substitute.h"
21+
#include "google/protobuf/compiler/code_generator.h"
22+
#include "google/protobuf/compiler/command_line_interface_tester.h"
23+
#include "google/protobuf/compiler/cpp/generator.h"
24+
#include "google/protobuf/compiler/python/generator.h"
25+
#include "google/protobuf/cpp_features.pb.h"
2126
#include "google/protobuf/io/printer.h"
2227
#include "google/protobuf/io/zero_copy_stream.h"
2328

@@ -100,6 +105,58 @@ TEST(PythonPluginTest, ImportTest) {
100105
EXPECT_TRUE(found_expected_import);
101106
}
102107

108+
class PythonGeneratorTest : public CommandLineInterfaceTester,
109+
public testing::WithParamInterface<bool> {
110+
protected:
111+
PythonGeneratorTest() {
112+
auto generator = std::make_unique<Generator>();
113+
generator->set_opensource_runtime(GetParam());
114+
RegisterGenerator("--python_out", "--python_opt", std::move(generator),
115+
"Python test generator");
116+
117+
// Generate built-in protos.
118+
CreateTempFile(
119+
google::protobuf::DescriptorProto::descriptor()->file()->name(),
120+
google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
121+
}
122+
};
123+
124+
TEST_P(PythonGeneratorTest, PythonWithCppFeatures) {
125+
// Test that the presence of C++ features does not break Python generation.
126+
RegisterGenerator("--cpp_out", "--cpp_opt",
127+
std::make_unique<cpp::CppGenerator>(),
128+
"C++ test generator");
129+
CreateTempFile("google/protobuf/cpp_features.proto",
130+
pb::CppFeatures::descriptor()->file()->DebugString());
131+
CreateTempFile("foo.proto",
132+
R"schema(
133+
edition = "2023";
134+
135+
import "google/protobuf/cpp_features.proto";
136+
137+
package foo;
138+
139+
enum Bar {
140+
AAA = 0;
141+
BBB = 1;
142+
}
143+
144+
message Foo {
145+
Bar bar_enum = 1 [features.(pb.cpp).legacy_closed_enum = true];
146+
})schema");
147+
148+
RunProtoc(absl::Substitute(
149+
"protocol_compiler --proto_path=$$tmpdir --cpp_out=$$tmpdir "
150+
"--python_out=$$tmpdir foo.proto $0 "
151+
"google/protobuf/cpp_features.proto",
152+
google::protobuf::DescriptorProto::descriptor()->file()->name()));
153+
154+
ExpectNoErrors();
155+
}
156+
157+
INSTANTIATE_TEST_SUITE_P(PythonGeneratorTest, PythonGeneratorTest,
158+
testing::Bool());
159+
103160
} // namespace
104161
} // namespace python
105162
} // namespace compiler

src/google/protobuf/descriptor.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -5537,7 +5537,7 @@ static void InferLegacyProtoFeatures(const ProtoT& proto,
55375537
static void InferLegacyProtoFeatures(const FieldDescriptorProto& proto,
55385538
const FieldOptions& options,
55395539
Edition edition, FeatureSet& features) {
5540-
if (!features.MutableExtension(pb::cpp)->has_string_type()) {
5540+
if (!features.GetExtension(pb::cpp).has_string_type()) {
55415541
if (options.ctype() == FieldOptions::CORD) {
55425542
features.MutableExtension(pb::cpp)->set_string_type(
55435543
pb::CppFeatures::CORD);

src/google/protobuf/descriptor.proto

+5
Original file line numberDiff line numberDiff line change
@@ -1135,6 +1135,11 @@ message FeatureSet {
11351135
type: ".pb.JavaFeatures"
11361136
},
11371137
declaration = { number: 1002, full_name: ".pb.go", type: ".pb.GoFeatures" },
1138+
declaration = {
1139+
number: 1003,
1140+
full_name: ".pb.python",
1141+
type: ".pb.PythonFeatures"
1142+
},
11381143
declaration = {
11391144
number: 9990,
11401145
full_name: ".pb.proto1",

src/google/protobuf/descriptor_unittest.cc

+43
Original file line numberDiff line numberDiff line change
@@ -11799,6 +11799,49 @@ TEST_F(DescriptorPoolFeaturesTest, OverrideDefaults) {
1179911799
)pb"));
1180011800
}
1180111801

11802+
TEST_F(DescriptorPoolFeaturesTest, OverrideFieldDefaults) {
11803+
FeatureSetDefaults defaults = ParseTextOrDie(R"pb(
11804+
defaults {
11805+
edition: EDITION_PROTO2
11806+
overridable_features {
11807+
field_presence: EXPLICIT
11808+
enum_type: CLOSED
11809+
repeated_field_encoding: EXPANDED
11810+
utf8_validation: VERIFY
11811+
message_encoding: LENGTH_PREFIXED
11812+
json_format: ALLOW
11813+
enforce_naming_style: STYLE_LEGACY
11814+
}
11815+
}
11816+
minimum_edition: EDITION_PROTO2
11817+
maximum_edition: EDITION_2023
11818+
)pb");
11819+
EXPECT_OK(pool_.SetFeatureSetDefaults(std::move(defaults)));
11820+
11821+
FileDescriptorProto file_proto = ParseTextOrDie(R"pb(
11822+
name: "foo.proto"
11823+
syntax: "editions"
11824+
edition: EDITION_PROTO3
11825+
message_type {
11826+
name: "Foo"
11827+
field { name: "bar" number: 1 label: LABEL_OPTIONAL type: TYPE_INT64 }
11828+
}
11829+
)pb");
11830+
11831+
BuildDescriptorMessagesInTestPool();
11832+
const FileDescriptor* file = ABSL_DIE_IF_NULL(pool_.BuildFile(file_proto));
11833+
const FieldDescriptor* field = file->message_type(0)->field(0);
11834+
EXPECT_THAT(GetFeatures(field), EqualsProto(R"pb(
11835+
field_presence: EXPLICIT
11836+
enum_type: CLOSED
11837+
repeated_field_encoding: EXPANDED
11838+
utf8_validation: VERIFY
11839+
message_encoding: LENGTH_PREFIXED
11840+
json_format: ALLOW
11841+
enforce_naming_style: STYLE_LEGACY
11842+
)pb"));
11843+
}
11844+
1180211845
TEST_F(DescriptorPoolFeaturesTest, ResolvesFeaturesForCppDefault) {
1180311846
EXPECT_FALSE(pool_.ResolvesFeaturesFor(pb::test));
1180411847
EXPECT_FALSE(pool_.ResolvesFeaturesFor(pb::TestMessage::test_message));

0 commit comments

Comments
 (0)