Skip to content

Commit 308aaf4

Browse files
mkruskal-googlecopybara-github
authored andcommitted
Fix python codegen crash when C++ features are used.
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 5dfc546 commit 308aaf4

File tree

7 files changed

+125
-6
lines changed

7 files changed

+125
-6
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ cc_library(
107107
visibility = [
108108
"//pkg:__pkg__",
109109
"//src/google/protobuf/compiler:__pkg__",
110+
"//src/google/protobuf/compiler/python:__pkg__", # For testing only.
110111
"@io_kythe//kythe/cxx/tools:__subpackages__",
111112
],
112113
deps = [

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

Lines changed: 4 additions & 0 deletions
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

Lines changed: 11 additions & 2 deletions
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

Lines changed: 60 additions & 3 deletions
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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5610,7 +5610,7 @@ static void InferLegacyProtoFeatures(const ProtoT& proto,
56105610
static void InferLegacyProtoFeatures(const FieldDescriptorProto& proto,
56115611
const FieldOptions& options,
56125612
Edition edition, FeatureSet& features) {
5613-
if (!features.MutableExtension(pb::cpp)->has_string_type()) {
5613+
if (!features.GetExtension(pb::cpp).has_string_type()) {
56145614
if (options.ctype() == FieldOptions::CORD) {
56155615
features.MutableExtension(pb::cpp)->set_string_type(
56165616
pb::CppFeatures::CORD);

src/google/protobuf/descriptor.proto

Lines changed: 5 additions & 0 deletions
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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12053,6 +12053,49 @@ TEST_F(DescriptorPoolFeaturesTest, OverrideDefaults) {
1205312053
)pb"));
1205412054
}
1205512055

12056+
TEST_F(DescriptorPoolFeaturesTest, OverrideFieldDefaults) {
12057+
FeatureSetDefaults defaults = ParseTextOrDie(R"pb(
12058+
defaults {
12059+
edition: EDITION_PROTO2
12060+
overridable_features {
12061+
field_presence: EXPLICIT
12062+
enum_type: CLOSED
12063+
repeated_field_encoding: EXPANDED
12064+
utf8_validation: VERIFY
12065+
message_encoding: LENGTH_PREFIXED
12066+
json_format: ALLOW
12067+
enforce_naming_style: STYLE_LEGACY
12068+
}
12069+
}
12070+
minimum_edition: EDITION_PROTO2
12071+
maximum_edition: EDITION_2023
12072+
)pb");
12073+
EXPECT_OK(pool_.SetFeatureSetDefaults(std::move(defaults)));
12074+
12075+
FileDescriptorProto file_proto = ParseTextOrDie(R"pb(
12076+
name: "foo.proto"
12077+
syntax: "editions"
12078+
edition: EDITION_PROTO3
12079+
message_type {
12080+
name: "Foo"
12081+
field { name: "bar" number: 1 label: LABEL_OPTIONAL type: TYPE_INT64 }
12082+
}
12083+
)pb");
12084+
12085+
BuildDescriptorMessagesInTestPool();
12086+
const FileDescriptor* file = ABSL_DIE_IF_NULL(pool_.BuildFile(file_proto));
12087+
const FieldDescriptor* field = file->message_type(0)->field(0);
12088+
EXPECT_THAT(GetFeatures(field), EqualsProto(R"pb(
12089+
field_presence: EXPLICIT
12090+
enum_type: CLOSED
12091+
repeated_field_encoding: EXPANDED
12092+
utf8_validation: VERIFY
12093+
message_encoding: LENGTH_PREFIXED
12094+
json_format: ALLOW
12095+
enforce_naming_style: STYLE_LEGACY
12096+
)pb"));
12097+
}
12098+
1205612099
TEST_F(DescriptorPoolFeaturesTest, ResolvesFeaturesForCppDefault) {
1205712100
EXPECT_FALSE(pool_.ResolvesFeaturesFor(pb::test));
1205812101
EXPECT_FALSE(pool_.ResolvesFeaturesFor(pb::TestMessage::test_message));

0 commit comments

Comments
 (0)