Implement cc_namespace option Design Doc: go/cc_namespace_option PiperOrigin-RevId: 913770777
diff --git a/BUILD.bazel b/BUILD.bazel index 179023e..b7aac11 100644 --- a/BUILD.bazel +++ b/BUILD.bazel
@@ -483,6 +483,12 @@ ) alias( + name = "cpp_file_options_proto", + actual = "//src/google/protobuf:cpp_file_options_proto", # proto_library + visibility = ["//visibility:public"], +) + +alias( name = "json_enumvalue_options_proto", actual = "//src/google/protobuf:json_enumvalue_options_proto", visibility = ["//visibility:public"],
diff --git a/cmake/install.cmake b/cmake/install.cmake index dbf2175..9e122a2 100644 --- a/cmake/install.cmake +++ b/cmake/install.cmake
@@ -89,6 +89,8 @@ ${libprotobuf_hdrs} ${libprotoc_public_hdrs} ${wkt_protos_files} + ${cpp_file_options_proto_proto_srcs} + ${cpp_file_options_cc_proto_hdrs} ${json_enumvalue_options_proto_proto_srcs} ${cpp_features_proto_proto_srcs} ${descriptor_proto_proto_srcs}
diff --git a/cmake/libprotoc.cmake b/cmake/libprotoc.cmake index 1fd275d..cc7dd44 100644 --- a/cmake/libprotoc.cmake +++ b/cmake/libprotoc.cmake
@@ -6,6 +6,9 @@ add_library(libprotoc ${protobuf_SHARED_OR_STATIC} ${libprotoc_srcs} ${libprotoc_hdrs} + src/google/protobuf/cpp_file_options.pb.h + src/google/protobuf/cpp_file_options.pb.cc + src/google/protobuf/cpp_file_options_bootstrap.pb.h ${protobuf_version_rc_file}) if(protobuf_HAVE_LD_VERSION_SCRIPT) if(${CMAKE_VERSION} VERSION_GREATER 3.13 OR ${CMAKE_VERSION} VERSION_EQUAL 3.13)
diff --git a/pkg/BUILD.bazel b/pkg/BUILD.bazel index dca6b55..0f490b4 100644 --- a/pkg/BUILD.bazel +++ b/pkg/BUILD.bazel
@@ -118,6 +118,7 @@ ":protoc-gen-upbdefs": "protoc-gen-upbdefs", ":protoc-gen-upb_minitable": "protoc-gen-upb_minitable", # Protos: + "//src/google/protobuf:cpp_file_options_cc_proto": "cpp_file_options_cc_proto", "//src/google/protobuf:well_known_type_protos": "wkt_protos", "//src/google/protobuf:cpp_features_proto": "cpp_features_proto", "//src/google/protobuf:descriptor_proto": "descriptor_proto",
diff --git a/regenerate_stale_files.sh b/regenerate_stale_files.sh index 6fc2d46..18f952e 100755 --- a/regenerate_stale_files.sh +++ b/regenerate_stale_files.sh
@@ -18,6 +18,7 @@ "upb/reflection:bootstrap_upb_defaults_staleness_test" "cmake:test_dependencies_staleness" "src:cmake_lists_staleness_test" + "src/google/protobuf:cpp_file_options_staleness_test" "src/google/protobuf:well_known_types_staleness_test" "objectivec:well_known_types_staleness_test" "php:test_amalgamation_staleness"
diff --git a/src/google/protobuf/BUILD.bazel b/src/google/protobuf/BUILD.bazel index 5c445b2..ef931ca 100644 --- a/src/google/protobuf/BUILD.bazel +++ b/src/google/protobuf/BUILD.bazel
@@ -314,6 +314,59 @@ ], ) +genrule( + name = "gen_cpp_file_options_cc_sources", + srcs = [ + "cpp_file_options.proto", + "//:descriptor_proto", + ], + outs = [ + "google/protobuf/cpp_file_options.pb.h", + "google/protobuf/cpp_file_options.pb.cc", + ], + cmd = """ + $(execpath //:protoc) \ + --cpp_out=dllexport_decl=PROTOBUF_EXPORT:$(RULEDIR) \ + --proto_path=$$(dirname $$(dirname $$(dirname $(location cpp_file_options.proto)))) \ + --descriptor_set_in=$(location //:descriptor_proto) \ + $(location cpp_file_options.proto) + """, + tools = ["//:protoc"], + visibility = ["//visibility:private"], +) + +staleness_test( + name = "cpp_file_options_staleness_test", + outs = [ + "cpp_file_options.pb.cc", + "cpp_file_options.pb.h", + "cpp_file_options_bootstrap.pb.cc", + "cpp_file_options_bootstrap.pb.h", + ], + generated_pattern = "google/protobuf/%s", + tags = ["manual"], +) + +cc_library( + name = "cpp_file_options_bootstrap", + srcs = ["cpp_file_options_bootstrap.pb.cc"], + hdrs = [ + "cpp_file_options_bootstrap.proto.h", + ], + strip_include_prefix = "/src", + visibility = [ + "//src/google/protobuf/compiler/cpp:__subpackages__", + "//visibility:private", + ], + deps = [ + ":arena", + ":port", + ":protobuf", + ":protobuf_lite", + "//src/google/protobuf/io", + ], +) + # Built-in runtime types proto_library( @@ -360,6 +413,29 @@ ) proto_library( + name = "cpp_options_proto", + srcs = ["cpp_options.proto"], + strip_import_prefix = "/src", + visibility = ["//visibility:public"], + exports = [":cpp_file_options_proto"], + deps = ["//src/google/protobuf:cpp_file_options_proto"], +) + +proto_library( + name = "cpp_file_options_proto", + srcs = ["cpp_file_options.proto"], + strip_import_prefix = "/src", + visibility = ["//:__subpackages__"], + deps = [":descriptor_proto"], +) + +cc_proto_library( + name = "cpp_file_options_cc_proto", + visibility = ["//:__subpackages__"], + deps = [":cpp_file_options_proto"], +) + +proto_library( name = "json_options_proto", srcs = ["json_options.proto"], strip_import_prefix = "/src",
diff --git a/src/google/protobuf/compiler/cpp/BUILD.bazel b/src/google/protobuf/compiler/cpp/BUILD.bazel index f47d2a4..f9c36f8 100644 --- a/src/google/protobuf/compiler/cpp/BUILD.bazel +++ b/src/google/protobuf/compiler/cpp/BUILD.bazel
@@ -124,6 +124,7 @@ ":names", ":names_internal", "//src/google/protobuf", + "//src/google/protobuf:cpp_file_options_bootstrap", "//src/google/protobuf:micro_string", "//src/google/protobuf:port", "//src/google/protobuf:protobuf_lite",
diff --git a/src/google/protobuf/compiler/cpp/generator.cc b/src/google/protobuf/compiler/cpp/generator.cc index 98c26e1..99d1e7f 100644 --- a/src/google/protobuf/compiler/cpp/generator.cc +++ b/src/google/protobuf/compiler/cpp/generator.cc
@@ -125,6 +125,10 @@ return false; } + if (!ValidateCcNamespace(file, error)) { + return false; + } + FileGenerator file_generator(file, file_options);
diff --git a/src/google/protobuf/compiler/cpp/generator_unittest.cc b/src/google/protobuf/compiler/cpp/generator_unittest.cc index bd4efd3..1be61ad 100644 --- a/src/google/protobuf/compiler/cpp/generator_unittest.cc +++ b/src/google/protobuf/compiler/cpp/generator_unittest.cc
@@ -13,6 +13,7 @@ #include <gtest/gtest.h> #include "google/protobuf/compiler/command_line_interface_tester.h" #include "google/protobuf/cpp_features.pb.h" +#include "google/protobuf/cpp_file_options.pb.h" namespace google { @@ -33,6 +34,9 @@ google::protobuf::DescriptorProto::descriptor()->file()->DebugString()); CreateTempFile("google/protobuf/cpp_features.proto", pb::CppFeatures::descriptor()->file()->DebugString()); + CreateTempFile( + "google/protobuf/cpp_file_options.proto", + pb::file::CppFileOptions::descriptor()->file()->DebugString()); } }; @@ -339,6 +343,37 @@ "field."); } +TEST_F(CppGeneratorTest, InvalidNumberInNamespace) { + CreateTempFile("foo.proto", + R"schema( + edition = "UNSTABLE"; + import option "google/protobuf/cpp_file_options.proto"; + option (pb.file.cpp).namespace = "cpp::1test"; + message Foo { + int32 bar = 1; + })schema"); + RunProtoc( + "protocol_compiler --proto_path=$tmpdir --cpp_out=$tmpdir " + "--experimental_editions foo.proto"); + ExpectErrorSubstring("Namespace segment can not start with a number: 1test"); +} + +TEST_F(CppGeneratorTest, InvalidCharacterInNamespace) { + CreateTempFile("foo.proto", + R"schema( + edition = "UNSTABLE"; + import option "google/protobuf/cpp_file_options.proto"; + option (pb.file.cpp).namespace = "cpp.test"; + message Foo { + int32 bar = 1; + })schema"); + RunProtoc( + "protocol_compiler --proto_path=$tmpdir --cpp_out=$tmpdir " + "--experimental_editions foo.proto"); + ExpectErrorSubstring("Namespace contains invalid characters: cpp.test"); +} + + TEST_F(CppGeneratorTest, CtypeOnExtensionTest) { CreateTempFile("foo.proto", R"schema(
diff --git a/src/google/protobuf/compiler/cpp/helpers.cc b/src/google/protobuf/compiler/cpp/helpers.cc index a938518..f7d9fd3 100644 --- a/src/google/protobuf/compiler/cpp/helpers.cc +++ b/src/google/protobuf/compiler/cpp/helpers.cc
@@ -12,12 +12,14 @@ #include "google/protobuf/compiler/cpp/helpers.h" #include <algorithm> +#include <cctype> #include <cstddef> #include <cstdint> #include <limits> #include <memory> #include <queue> #include <string> +#include <string_view> #include <type_traits> #include <utility> #include <vector> @@ -48,6 +50,7 @@ #include "google/protobuf/compiler/cpp/names.h" #include "google/protobuf/compiler/cpp/options.h" #include "google/protobuf/compiler/scc.h" +#include "google/protobuf/cpp_file_options.pb.h" #include "google/protobuf/descriptor.h" #include "google/protobuf/descriptor.pb.h" #include "google/protobuf/dynamic_message.h" @@ -548,7 +551,36 @@ return absl::StrCat("::", absl::StrJoin(scope, "::")); } +bool ValidateCcNamespace(const FileDescriptor* file, std::string* error) { + if (file->options().GetExtension(::pb::file::cpp).has_namespace_()) { + auto cc_namespace = + file->options().GetExtension(::pb::file::cpp).namespace_(); + if (cc_namespace.find_first_of(".;\r\n ") != std::string::npos) { + *error = + absl::StrCat("Namespace contains invalid characters: ", cc_namespace); + return false; + } + std::vector<std::string> names = + absl::StrSplit(cc_namespace, "::", absl::SkipEmpty()); + for (auto& name : names) { + if (std::isdigit(static_cast<unsigned char>(name[0]))) { + *error = absl::StrCat("Namespace segment can not start with a number: ", + name); + return false; + } + } + } + return true; +} + std::string Namespace(const FileDescriptor* d) { + if (d->options().GetExtension(::pb::file::cpp).has_namespace_()) { + auto cc_namespace = d->options().GetExtension(::pb::file::cpp).namespace_(); + if (absl::StartsWith(cc_namespace, "::")) { + return std::string(cc_namespace); + } + return absl::StrCat("::", cc_namespace); + } return Namespace(d->package()); } @@ -1859,7 +1891,7 @@ {"third_party/protobuf/cpp_features", "third_party/protobuf/cpp_features"}, {"third_party/protobuf/cpp_file_options", - "third_party/protobuf/cpp_file_options_bootstrap"}, + "third_party/protobuf/cpp_file_options"}, {"third_party/protobuf/compiler/plugin", "third_party/protobuf/compiler/plugin"}, {"third_party/protobuf/internal_options",
diff --git a/src/google/protobuf/compiler/cpp/helpers.h b/src/google/protobuf/compiler/cpp/helpers.h index 89dd225..207cc21 100644 --- a/src/google/protobuf/compiler/cpp/helpers.h +++ b/src/google/protobuf/compiler/cpp/helpers.h
@@ -106,6 +106,8 @@ std::string Namespace(const FieldDescriptor* d); std::string Namespace(const EnumDescriptor* d); PROTOC_EXPORT std::string Namespace(const FileDescriptor* d); +PROTOC_EXPORT bool ValidateCcNamespace(const FileDescriptor* file, + std::string* error); PROTOC_EXPORT std::string Namespace(const Descriptor* d); PROTOC_EXPORT std::string Namespace(const FieldDescriptor* d); PROTOC_EXPORT std::string Namespace(const EnumDescriptor* d);
diff --git a/src/google/protobuf/cpp_file_options_test.cc b/src/google/protobuf/cpp_file_options_test.cc new file mode 100644 index 0000000..7e68681 --- /dev/null +++ b/src/google/protobuf/cpp_file_options_test.cc
@@ -0,0 +1,34 @@ +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include "google/protobuf/cpp_file_options_test.pb.h" +#include "google/protobuf/descriptor_test_utils.h" + +using ::testing::NotNull; + +namespace google { +namespace protobuf { +namespace descriptor_unittest { + +class CppFileOptionsTest : public ValidationErrorTest { + protected: + void SetUp() override { ValidationErrorTest::SetUp(); } +}; + +TEST_F(CppFileOptionsTest, NewNamespaceSymbolSameProtoName) { + BuildFileInTestPool(cpp::file::NewMessage::descriptor()->file()); + ASSERT_THAT(ParseAndBuildFile("test.proto", R"schema( + edition = "UNSTABLE"; + package test; + + import "google/protobuf/cpp_file_options_test.proto"; + + message TopLevelMessage { + cpp.file.options.test.NewMessage nested_message = 1; + } + )schema"), + NotNull()); +} + +} // namespace descriptor_unittest +} // namespace protobuf +} // namespace google
diff --git a/src/google/protobuf/cpp_file_options_test.proto b/src/google/protobuf/cpp_file_options_test.proto new file mode 100644 index 0000000..5701839 --- /dev/null +++ b/src/google/protobuf/cpp_file_options_test.proto
@@ -0,0 +1,11 @@ +edition = "UNSTABLE"; + +package cpp.file.options.test; + +import option "google/protobuf/cpp_options.proto"; + +option (pb.file.cpp).namespace = "::cpp::file"; + +message NewMessage { + string some_message = 1; +}