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;
+}