Skip to content

Commit 872d3ce

Browse files
mkruskal-googlecopybara-github
authored andcommitted
Fix a bug with custom features outside of the pb package.
This was always intended to be possible, but was accidentally broken by a recent improvement in C++. This expands the generated defaults to all features, adding an implicit requirement that the message type of each feature extension is unique. The only violation of this today was in our test features. PiperOrigin-RevId: 836350099
1 parent 3079d15 commit 872d3ce

File tree

12 files changed

+425
-88
lines changed

12 files changed

+425
-88
lines changed

src/google/protobuf/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,6 +1065,7 @@ filegroup(
10651065
"map_proto2_unittest.proto",
10661066
"map_unittest.proto",
10671067
"unittest.proto",
1068+
"unittest_custom_features.proto",
10681069
"unittest_custom_options.proto",
10691070
"unittest_embed_optimize_for.proto",
10701071
"unittest_empty.proto",
@@ -1150,6 +1151,7 @@ protobuf_test_proto_library(
11501151
"map_unittest.proto",
11511152
"unittest.proto",
11521153
"unittest_arena.proto",
1154+
"unittest_custom_features.proto",
11531155
"unittest_custom_options.proto",
11541156
"unittest_delimited.proto",
11551157
"unittest_delimited_import.proto",

src/google/protobuf/compiler/code_generator_unittest.cc

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@
1717
#include "absl/log/absl_log.h"
1818
#include "absl/status/status.h"
1919
#include "absl/strings/str_format.h"
20-
#include "absl/strings/str_replace.h"
2120
#include "absl/strings/string_view.h"
2221
#include "google/protobuf/compiler/parser.h"
2322
#include "editions/edition_defaults_test_utils.h"
2423
#include "google/protobuf/io/tokenizer.h"
2524
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
2625
#include "google/protobuf/test_textproto.h"
26+
#include "google/protobuf/unittest_custom_features.pb.h"
2727
#include "google/protobuf/unittest_features.pb.h"
2828

2929
// Must be included last.
@@ -282,6 +282,45 @@ TEST_F(CodeGeneratorTest, GetResolvedSourceFeatureExtension) {
282282
EXPECT_EQ(ext2.source_feature(), ext1.source_feature());
283283
}
284284

285+
TEST_F(CodeGeneratorTest, GetResolvedSourceFeatureExtensionCustom) {
286+
TestGenerator generator;
287+
generator.set_feature_extensions(
288+
{GetExtensionReflection(custom_features::test)});
289+
ASSERT_OK(pool_.SetFeatureSetDefaults(*generator.BuildFeatureSetDefaults()));
290+
291+
ASSERT_THAT(BuildFile(DescriptorProto::descriptor()->file()), NotNull());
292+
ASSERT_THAT(
293+
BuildFile(custom_features::TestCustomFeatures::descriptor()->file()),
294+
NotNull());
295+
auto file = BuildFile(R"schema(
296+
edition = "2023";
297+
package proto2_unittest;
298+
299+
import "google/protobuf/unittest_custom_features.proto";
300+
301+
option features.(custom_features.test).file_feature = VALUE6;
302+
option features.(custom_features.test).source_feature = VALUE5;
303+
)schema");
304+
ASSERT_THAT(file, NotNull());
305+
const custom_features::TestCustomFeatures& ext1 =
306+
TestGenerator::GetResolvedSourceFeatureExtension(*file,
307+
custom_features::test);
308+
const custom_features::TestCustomFeatures& ext2 =
309+
TestGenerator::GetResolvedSourceFeatures(*file).GetExtension(
310+
custom_features::test);
311+
312+
// Since the pool provides the feature set defaults, there should be no
313+
// difference between the two results.
314+
EXPECT_EQ(ext1.enum_feature(), custom_features::EnumFeature::VALUE1);
315+
EXPECT_EQ(ext1.field_feature(), custom_features::EnumFeature::VALUE1);
316+
EXPECT_EQ(ext1.file_feature(), custom_features::EnumFeature::VALUE6);
317+
EXPECT_EQ(ext1.source_feature(), custom_features::EnumFeature::VALUE5);
318+
EXPECT_EQ(ext2.enum_feature(), ext1.enum_feature());
319+
EXPECT_EQ(ext2.field_feature(), ext1.field_feature());
320+
EXPECT_EQ(ext2.file_feature(), ext1.file_feature());
321+
EXPECT_EQ(ext2.source_feature(), ext1.source_feature());
322+
}
323+
285324
TEST_F(CodeGeneratorTest, GetResolvedSourceFeatureExtensionEditedDefaults) {
286325
FeatureSetDefaults defaults = ParseTextOrDie(R"pb(
287326
minimum_edition: EDITION_PROTO2

src/google/protobuf/compiler/cpp/extension.cc

Lines changed: 42 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -101,67 +101,59 @@ bool ExtensionGenerator::IsScoped() const {
101101
return descriptor_->extension_scope() != nullptr;
102102
}
103103

104-
namespace {
105-
bool ShouldGenerateFeatureSetDefaultData(absl::string_view extension) {
106-
return extension == "pb.java" || extension == "pb.java_mutable" ||
107-
extension == "pb.test" || extension == "pb.proto1";
104+
void ExtensionGenerator::GenerateFeatureDefaults(io::Printer* p) const {
105+
auto var = p->WithVars(variables_);
106+
auto annotate = p->WithAnnotations({{"name", descriptor_}});
107+
if (descriptor_->message_type() == nullptr) return;
108+
absl::string_view extendee = descriptor_->containing_type()->full_name();
109+
if (extendee != "google.protobuf.FeatureSet") return;
110+
111+
absl::StatusOr<FeatureSetDefaults> defaults =
112+
FeatureResolver::CompileDefaults(descriptor_->containing_type(),
113+
{descriptor_}, ProtocMinimumEdition(),
114+
MaximumKnownEdition());
115+
if (!defaults.ok()) {
116+
return;
117+
}
118+
p->Emit({{"defaults", absl::Base64Escape(defaults->SerializeAsString())},
119+
{"extension_type", ClassName(descriptor_->message_type(), true)},
120+
{"function_name", "GetFeatureSetDefaultsData"}},
121+
R"cc(
122+
namespace internal {
123+
template <>
124+
inline ::absl::string_view $function_name$<$extension_type$>() {
125+
static constexpr char kDefaults[] = "$defaults$";
126+
return kDefaults;
127+
}
128+
} // namespace internal
129+
)cc");
108130
}
109-
} // namespace
110131

111132
void ExtensionGenerator::GenerateDeclaration(io::Printer* p) const {
112133
auto var = p->WithVars(variables_);
113134
auto annotate = p->WithAnnotations({{"name", descriptor_}});
114135
p->Emit(
115-
{{"constant_qualifier",
116-
// If this is a class member, it needs to be declared
117-
// `static constexpr`.
118-
// Otherwise, it will be
119-
// `inline constexpr`.
120-
IsScoped() ? "static" : ""},
121-
{"id_qualifier",
122-
// If this is a class member, it needs to be declared "static".
123-
// Otherwise, it needs to be "extern". In the latter case, it
124-
// also needs the DLL export/import specifier.
125-
IsScoped() ? "static"
126-
: options_.dllexport_decl.empty()
127-
? "extern"
128-
: absl::StrCat(options_.dllexport_decl, " extern")},
129-
{"feature_set_defaults",
130-
[&] {
131-
if (!ShouldGenerateFeatureSetDefaultData(descriptor_->full_name())) {
132-
return;
133-
}
134-
if (descriptor_->message_type() == nullptr) return;
135-
absl::string_view extendee =
136-
descriptor_->containing_type()->full_name();
137-
if (extendee != "google.protobuf.FeatureSet") return;
138-
139-
std::vector<const FieldDescriptor*> extensions = {descriptor_};
140-
absl::StatusOr<FeatureSetDefaults> defaults =
141-
FeatureResolver::CompileDefaults(
142-
descriptor_->containing_type(), extensions,
143-
ProtocMinimumEdition(), MaximumKnownEdition());
144-
ABSL_CHECK_OK(defaults);
145-
p->Emit(
146-
{{"defaults", absl::Base64Escape(defaults->SerializeAsString())},
147-
{"extension_type", ClassName(descriptor_->message_type(), true)},
148-
{"function_name", "GetFeatureSetDefaultsData"}},
149-
R"cc(
150-
namespace internal {
151-
template <>
152-
inline ::absl::string_view $function_name$<$extension_type$>() {
153-
static constexpr char kDefaults[] = "$defaults$";
154-
return kDefaults;
155-
}
156-
} // namespace internal
157-
)cc");
158-
}}},
136+
{
137+
{"constant_qualifier",
138+
// If this is a class member, it needs to be declared
139+
// `static constexpr`.
140+
// Otherwise, it will be
141+
// `inline constexpr`.
142+
IsScoped() ? "static" : ""},
143+
{"id_qualifier",
144+
// If this is a class member, it needs to be declared "static".
145+
// Otherwise, it needs to be "extern". In the latter case, it
146+
// also needs the DLL export/import specifier.
147+
IsScoped() ? "static"
148+
: options_.dllexport_decl.empty()
149+
? "extern"
150+
: absl::StrCat(options_.dllexport_decl, " extern")},
151+
},
159152
R"cc(
160153
inline $constant_qualifier $constexpr int $constant_name$ = $number$;
161154
$id_qualifier$ $pbi$::ExtensionIdentifier<
162155
$extendee$, $pbi$::$type_traits$, $field_type$, $packed$>
163156
$name$;
164-
$feature_set_defaults$;
165157
)cc");
166158
}
167159

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ class PROTOC_EXPORT ExtensionGenerator {
5858
// Source file stuff.
5959
void GenerateDefinition(io::Printer* p);
6060

61+
// Feature-specific defaults.
62+
void GenerateFeatureDefaults(io::Printer* p) const;
63+
6164
// Extension registration can happen at different priority levels depending on
6265
// the features used.
6366
//

src/google/protobuf/compiler/cpp/file.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1608,6 +1608,10 @@ void FileGenerator::GenerateForwardDeclarations(io::Printer* p) {
16081608
decl.second.PrintTopLevelDecl(p, options_);
16091609
}
16101610

1611+
for (auto& extension_generator : extension_generators_) {
1612+
extension_generator->GenerateFeatureDefaults(p);
1613+
}
1614+
16111615
if (IsFileDescriptorProto(file_, options_)) {
16121616
ns.ChangeTo(absl::StrCat(ProtobufNamespace(options_), "::internal"));
16131617
p->Emit(R"cc(

src/google/protobuf/compiler/java/java_features.pb.h

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

src/google/protobuf/cpp_features.pb.h

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

src/google/protobuf/extension_set.h

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,6 @@ void InitializeLazyExtensionSet();
8080
} // namespace google
8181
namespace pb {
8282
class CppFeatures;
83-
namespace internal {
84-
// Forward-declares the function for FeatureSet extensions to make it visible
85-
// to the internal feature helper. It should hold and return serialized
86-
// FeatureSetDefaults data.
87-
template <class T>
88-
inline ::absl::string_view GetFeatureSetDefaultsData();
89-
} // namespace internal
9083
} // namespace pb
9184

9285
namespace google {
@@ -99,6 +92,12 @@ class FindExtensionTest;
9992
// Forward-declared from message.h.
10093
PROTOBUF_EXPORT bool IsDescendant(const Message& root, const Message& message);
10194

95+
// Forward-declares the function for FeatureSet extensions to make it visible
96+
// to the internal feature helper. It should hold and return serialized
97+
// FeatureSetDefaults data.
98+
template <class T>
99+
inline ::absl::string_view GetFeatureSetDefaultsData();
100+
102101
namespace v2 {
103102
class TableDrivenMessage;
104103
} // namespace v2

src/google/protobuf/feature_resolver_test.cc

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -154,18 +154,9 @@ TEST(FeatureResolverTest, DefaultsTestMessageExtension) {
154154
EXPECT_EQ(merged->message_encoding(), FeatureSet::LENGTH_PREFIXED);
155155
EXPECT_FALSE(merged->HasExtension(pb::test));
156156

157-
const pb::TestFeatures& ext =
157+
const pb::TestMessageFeatures& ext =
158158
merged->GetExtension(pb::TestMessage::test_message);
159159
EXPECT_EQ(ext.file_feature(), pb::VALUE3);
160-
EXPECT_EQ(ext.extension_range_feature(), pb::VALUE1);
161-
EXPECT_EQ(ext.message_feature(), pb::VALUE1);
162-
EXPECT_EQ(ext.field_feature(), pb::VALUE1);
163-
EXPECT_EQ(ext.oneof_feature(), pb::VALUE1);
164-
EXPECT_EQ(ext.enum_feature(), pb::VALUE1);
165-
EXPECT_EQ(ext.enum_entry_feature(), pb::VALUE1);
166-
EXPECT_EQ(ext.service_feature(), pb::VALUE1);
167-
EXPECT_EQ(ext.method_feature(), pb::VALUE1);
168-
EXPECT_FALSE(ext.bool_field_feature());
169160
}
170161

171162
TEST(FeatureResolverTest, DefaultsTestNestedExtension) {
@@ -179,18 +170,9 @@ TEST(FeatureResolverTest, DefaultsTestNestedExtension) {
179170
EXPECT_EQ(merged->message_encoding(), FeatureSet::LENGTH_PREFIXED);
180171
EXPECT_FALSE(merged->HasExtension(pb::test));
181172

182-
const pb::TestFeatures& ext =
173+
const pb::TestNestedFeatures& ext =
183174
merged->GetExtension(pb::TestMessage::Nested::test_nested);
184175
EXPECT_EQ(ext.file_feature(), pb::VALUE3);
185-
EXPECT_EQ(ext.extension_range_feature(), pb::VALUE1);
186-
EXPECT_EQ(ext.message_feature(), pb::VALUE1);
187-
EXPECT_EQ(ext.field_feature(), pb::VALUE1);
188-
EXPECT_EQ(ext.oneof_feature(), pb::VALUE1);
189-
EXPECT_EQ(ext.enum_feature(), pb::VALUE1);
190-
EXPECT_EQ(ext.enum_entry_feature(), pb::VALUE1);
191-
EXPECT_EQ(ext.service_feature(), pb::VALUE1);
192-
EXPECT_EQ(ext.method_feature(), pb::VALUE1);
193-
EXPECT_FALSE(ext.bool_field_feature());
194176
}
195177

196178
TEST(FeatureResolverTest, DefaultsGeneratedPoolCustom) {

src/google/protobuf/internal_feature_helper.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ class PROTOBUF_EXPORT InternalFeatureHelper {
114114

115115
auto lang_features_ret =
116116
ParseAndGetEditionResolvedFeatureSet(
117-
::pb::internal::GetFeatureSetDefaultsData<ExtType>(),
117+
::google::protobuf::internal::GetFeatureSetDefaultsData<ExtType>(),
118118
GetEdition(descriptor))
119119
.GetExtension(extension);
120120
lang_features_ret.MergeFrom(lang_features);

0 commit comments

Comments
 (0)