From 01fe22219a0312b178a265e75fe35422ea6afbb1 Mon Sep 17 00:00:00 2001 From: Mike Kruskal <62662355+mkruskal-google@users.noreply.github.com> Date: Mon, 26 Sep 2022 12:36:06 -0700 Subject: [PATCH] Open up visibility for some compiler internals (#10608) * Expose language-specific naming utilities in Bazel for downstream code generators * Expose language generators without exposing implementation details * Update cmake configs * Revert "Expose language generators without exposing implementation details" This reverts commit b073d9b4ebf28b825a6340ea0dfc60f1e43ae4c1. * Give gRPC privileged access to our python generator * Naming cleanup * Add linkage for public names.h helpers * Fixing build/merge issues * Fix extension docstring --- pkg/BUILD.bazel | 8 + src/file_lists.cmake | 9 +- src/google/protobuf/compiler/BUILD.bazel | 1 + .../command_line_interface_unittest.cc | 1 + src/google/protobuf/compiler/cpp/BUILD.bazel | 38 +- .../protobuf/compiler/csharp/BUILD.bazel | 15 +- .../compiler/csharp/csharp_field_base.cc | 2 +- .../compiler/csharp/csharp_generator.cc | 3 +- .../compiler/csharp/csharp_helpers.cc | 167 +------- .../protobuf/compiler/csharp/csharp_helpers.h | 22 +- .../compiler/csharp/csharp_message.cc | 2 +- .../csharp/csharp_reflection_class.cc | 2 +- .../csharp/csharp_source_generator_base.cc | 2 +- src/google/protobuf/compiler/csharp/names.cc | 225 +++++++++++ .../csharp/{csharp_names.h => names.h} | 42 +- src/google/protobuf/compiler/java/BUILD.bazel | 43 ++- src/google/protobuf/compiler/java/context.cc | 6 +- src/google/protobuf/compiler/java/context.h | 15 + src/google/protobuf/compiler/java/helpers.cc | 136 ------- src/google/protobuf/compiler/java/helpers.h | 31 +- src/google/protobuf/compiler/java/names.cc | 194 ++++++++++ src/google/protobuf/compiler/java/names.h | 26 ++ .../protobuf/compiler/objectivec/BUILD.bazel | 32 +- .../{objectivec_helpers.cc => names.cc} | 2 +- .../protobuf/compiler/objectivec/names.h | 363 ++++++++++++++++++ .../compiler/objectivec/objectivec_helpers.h | 312 +-------------- src/google/protobuf/compiler/php/BUILD.bazel | 15 + src/google/protobuf/compiler/php/names.cc | 144 +++++++ src/google/protobuf/compiler/php/names.h | 73 ++++ .../protobuf/compiler/php/php_generator.cc | 116 +----- .../protobuf/compiler/php/php_generator.h | 8 +- .../protobuf/compiler/python/BUILD.bazel | 1 + 32 files changed, 1255 insertions(+), 801 deletions(-) create mode 100644 src/google/protobuf/compiler/csharp/names.cc rename src/google/protobuf/compiler/csharp/{csharp_names.h => names.h} (72%) create mode 100644 src/google/protobuf/compiler/java/names.cc rename src/google/protobuf/compiler/objectivec/{objectivec_helpers.cc => names.cc} (99%) create mode 100644 src/google/protobuf/compiler/objectivec/names.h create mode 100644 src/google/protobuf/compiler/php/names.cc create mode 100644 src/google/protobuf/compiler/php/names.h diff --git a/pkg/BUILD.bazel b/pkg/BUILD.bazel index 4eda549da512..34e60d6c1585 100644 --- a/pkg/BUILD.bazel +++ b/pkg/BUILD.bazel @@ -402,10 +402,18 @@ cc_dist_library( "//src/google/protobuf/compiler:code_generator", "//src/google/protobuf/compiler:command_line_interface", "//src/google/protobuf/compiler/cpp", + "//src/google/protobuf/compiler/cpp:names", + "//src/google/protobuf/compiler/cpp:names_internal", "//src/google/protobuf/compiler/csharp", + "//src/google/protobuf/compiler/csharp:names", "//src/google/protobuf/compiler/java", + "//src/google/protobuf/compiler/java:names", + "//src/google/protobuf/compiler/java:names_internal", "//src/google/protobuf/compiler/objectivec", + "//src/google/protobuf/compiler/objectivec:names", + "//src/google/protobuf/compiler/objectivec:names_internal", "//src/google/protobuf/compiler/php", + "//src/google/protobuf/compiler/php:names", "//src/google/protobuf/compiler/python", "//src/google/protobuf/compiler/ruby", ], diff --git a/src/file_lists.cmake b/src/file_lists.cmake index a61e3f13737c..e874574e8245 100644 --- a/src/file_lists.cmake +++ b/src/file_lists.cmake @@ -318,6 +318,7 @@ set(libprotoc_srcs ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc + ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/names.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/context.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/doc_comment.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/enum.cc @@ -342,24 +343,26 @@ set(libprotoc_srcs ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/message_lite.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/message_serialization.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/name_resolver.cc + ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/names.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/primitive_field.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/primitive_field_lite.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/service.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/shared_code_generator.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/string_field.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/string_field_lite.cc + ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/names.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_enum.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_extension.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_field.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_file.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_generator.cc - ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_map_field.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_message.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_message_field.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_oneof.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.cc + ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/php/names.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/php/php_generator.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/plugin.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/plugin.pb.cc @@ -402,7 +405,6 @@ set(libprotoc_hdrs ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_map_field.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_message.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_message_field.h - ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_names.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_options.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_primitive_field.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_reflection_class.h @@ -411,6 +413,7 @@ set(libprotoc_hdrs ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_source_generator_base.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_wrapper_field.h + ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/names.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/context.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/doc_comment.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/enum.h @@ -443,6 +446,7 @@ set(libprotoc_hdrs ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/shared_code_generator.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/string_field.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/string_field_lite.h + ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/names.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_enum.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_enum_field.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_extension.h @@ -457,6 +461,7 @@ set(libprotoc_hdrs ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_oneof.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_options.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.h + ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/php/names.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/php/php_generator.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/plugin.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/plugin.pb.h diff --git a/src/google/protobuf/compiler/BUILD.bazel b/src/google/protobuf/compiler/BUILD.bazel index baae70ed3acf..af05903a6a54 100644 --- a/src/google/protobuf/compiler/BUILD.bazel +++ b/src/google/protobuf/compiler/BUILD.bazel @@ -244,6 +244,7 @@ cc_test( ":command_line_interface", ":mock_code_generator", "//:protobuf", + "//src/google/protobuf/compiler/cpp:names", "//src/google/protobuf:cc_test_protos", "//src/google/protobuf:test_util2", "//src/google/protobuf/io", diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc index 39bfd1c9207d..9074c09a0f10 100644 --- a/src/google/protobuf/compiler/command_line_interface_unittest.cc +++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc @@ -66,6 +66,7 @@ #include "google/protobuf/compiler/command_line_interface.h" #include "google/protobuf/compiler/mock_code_generator.h" #include "google/protobuf/compiler/subprocess.h" +#include "google/protobuf/compiler/cpp/names.h" #include "google/protobuf/descriptor.h" #include "google/protobuf/io/io_win32.h" #include "google/protobuf/io/printer.h" diff --git a/src/google/protobuf/compiler/cpp/BUILD.bazel b/src/google/protobuf/compiler/cpp/BUILD.bazel index 4463a09652bf..7f0ef6a27de0 100644 --- a/src/google/protobuf/compiler/cpp/BUILD.bazel +++ b/src/google/protobuf/compiler/cpp/BUILD.bazel @@ -7,6 +7,38 @@ load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix") load("@rules_proto//proto:defs.bzl", "proto_library") load("//build_defs:cpp_opts.bzl", "COPTS") +cc_library( + name = "names", + hdrs = ["names.h"], + copts = COPTS, + include_prefix = "google/protobuf/compiler/cpp", + visibility = ["//visibility:public"], + deps = [ + ":names_internal", + "//src/google/protobuf/compiler:code_generator", + "//src/google/protobuf:protobuf_nowkt", + ], +) + +cc_library( + name = "names_internal", + hdrs = [ + "helpers.h", + "names.h", + "options.h", + ], + srcs = [ + "helpers.cc", + ], + copts = COPTS, + include_prefix = "google/protobuf/compiler/cpp", + visibility = ["//pkg:__pkg__"], + deps = [ + "//src/google/protobuf/compiler:code_generator", + "//src/google/protobuf:protobuf_nowkt", + ], +) + cc_library( name = "cpp", srcs = [ @@ -16,7 +48,6 @@ cc_library( "field.cc", "file.cc", "generator.cc", - "helpers.cc", "map_field.cc", "message.cc", "message_field.cc", @@ -33,13 +64,10 @@ cc_library( "field.h", "file.h", "generator.h", - "helpers.h", "map_field.h", "message.h", "message_field.h", "message_layout_helper.h", - "names.h", - "options.h", "padding_optimizer.h", "parse_function_generator.h", "primitive_field.h", @@ -53,6 +81,8 @@ cc_library( "//src/google/protobuf/compiler:__pkg__", ], deps = [ + ":names_internal", + ":names", "//src/google/protobuf:protobuf_nowkt", "//src/google/protobuf/compiler:code_generator", "@com_google_absl//absl/base:core_headers", diff --git a/src/google/protobuf/compiler/csharp/BUILD.bazel b/src/google/protobuf/compiler/csharp/BUILD.bazel index df3d7bfb17f2..433e0b238a91 100644 --- a/src/google/protobuf/compiler/csharp/BUILD.bazel +++ b/src/google/protobuf/compiler/csharp/BUILD.bazel @@ -6,6 +6,19 @@ load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix") load("//build_defs:cpp_opts.bzl", "COPTS") +cc_library( + name = "names", + hdrs = ["names.h"], + srcs = ["names.cc"], + copts = COPTS, + include_prefix = "google/protobuf/compiler/csharp", + visibility = ["//visibility:public"], + deps = [ + "//src/google/protobuf:protobuf_nowkt", + "@com_google_absl//absl/strings", + ], +) + cc_library( name = "csharp", srcs = [ @@ -36,7 +49,6 @@ cc_library( "csharp_map_field.h", "csharp_message.h", "csharp_message_field.h", - "csharp_names.h", "csharp_options.h", "csharp_primitive_field.h", "csharp_reflection_class.h", @@ -56,6 +68,7 @@ cc_library( "//src/google/protobuf/compiler:__pkg__", ], deps = [ + ":names", "//src/google/protobuf:protobuf_nowkt", "//src/google/protobuf/compiler:code_generator", "@com_google_absl//absl/container:flat_hash_set", diff --git a/src/google/protobuf/compiler/csharp/csharp_field_base.cc b/src/google/protobuf/compiler/csharp/csharp_field_base.cc index da205df60701..bc45d740ba79 100644 --- a/src/google/protobuf/compiler/csharp/csharp_field_base.cc +++ b/src/google/protobuf/compiler/csharp/csharp_field_base.cc @@ -38,7 +38,7 @@ #include "google/protobuf/descriptor.h" #include "google/protobuf/wire_format.h" #include "google/protobuf/compiler/csharp/csharp_helpers.h" -#include "google/protobuf/compiler/csharp/csharp_names.h" +#include "google/protobuf/compiler/csharp/names.h" #include "google/protobuf/descriptor.pb.h" #include "google/protobuf/io/coded_stream.h" #include "google/protobuf/io/printer.h" diff --git a/src/google/protobuf/compiler/csharp/csharp_generator.cc b/src/google/protobuf/compiler/csharp/csharp_generator.cc index 0dab0718fab9..0152072776ae 100644 --- a/src/google/protobuf/compiler/csharp/csharp_generator.cc +++ b/src/google/protobuf/compiler/csharp/csharp_generator.cc @@ -34,9 +34,10 @@ #include "google/protobuf/compiler/code_generator.h" #include "google/protobuf/descriptor.h" -#include "google/protobuf/compiler/csharp/csharp_names.h" +#include "google/protobuf/compiler/csharp/csharp_helpers.h" #include "google/protobuf/compiler/csharp/csharp_options.h" #include "google/protobuf/compiler/csharp/csharp_reflection_class.h" +#include "google/protobuf/compiler/csharp/names.h" #include "google/protobuf/descriptor.pb.h" #include "google/protobuf/io/printer.h" #include "google/protobuf/io/zero_copy_stream.h" diff --git a/src/google/protobuf/compiler/csharp/csharp_helpers.cc b/src/google/protobuf/compiler/csharp/csharp_helpers.cc index b917d8f14583..1ff5444cbca4 100644 --- a/src/google/protobuf/compiler/csharp/csharp_helpers.cc +++ b/src/google/protobuf/compiler/csharp/csharp_helpers.cc @@ -47,13 +47,13 @@ #include "google/protobuf/compiler/csharp/csharp_field_base.h" #include "google/protobuf/compiler/csharp/csharp_map_field.h" #include "google/protobuf/compiler/csharp/csharp_message_field.h" -#include "google/protobuf/compiler/csharp/csharp_names.h" #include "google/protobuf/compiler/csharp/csharp_options.h" #include "google/protobuf/compiler/csharp/csharp_primitive_field.h" #include "google/protobuf/compiler/csharp/csharp_repeated_enum_field.h" #include "google/protobuf/compiler/csharp/csharp_repeated_message_field.h" #include "google/protobuf/compiler/csharp/csharp_repeated_primitive_field.h" #include "google/protobuf/compiler/csharp/csharp_wrapper_field.h" +#include "google/protobuf/compiler/csharp/names.h" #include "google/protobuf/descriptor.pb.h" // Must be last. @@ -110,102 +110,6 @@ CSharpType GetCSharpType(FieldDescriptor::Type type) { return (CSharpType) -1; } -std::string StripDotProto(const std::string& proto_file) { - int lastindex = proto_file.find_last_of('.'); - return proto_file.substr(0, lastindex); -} - -std::string GetFileNamespace(const FileDescriptor* descriptor) { - if (descriptor->options().has_csharp_namespace()) { - return descriptor->options().csharp_namespace(); - } - return UnderscoresToCamelCase(descriptor->package(), true, true); -} - -// Returns the Pascal-cased last part of the proto file. For example, -// input of "google/protobuf/foo_bar.proto" would result in "FooBar". -std::string GetFileNameBase(const FileDescriptor* descriptor) { - std::string proto_file = descriptor->name(); - int lastslash = proto_file.find_last_of('/'); - std::string base = proto_file.substr(lastslash + 1); - return UnderscoresToPascalCase(StripDotProto(base)); -} - -std::string GetReflectionClassUnqualifiedName(const FileDescriptor* descriptor) { - // TODO: Detect collisions with existing messages, - // and append an underscore if necessary. - return GetFileNameBase(descriptor) + "Reflection"; -} - -std::string GetExtensionClassUnqualifiedName(const FileDescriptor* descriptor) { - // TODO: Detect collisions with existing messages, - // and append an underscore if necessary. - return GetFileNameBase(descriptor) + "Extensions"; -} - -// TODO(jtattermusch): can we reuse a utility function? -std::string UnderscoresToCamelCase(const std::string& input, - bool cap_next_letter, - bool preserve_period) { - std::string result; - - // Note: I distrust ctype.h due to locales. - for (int i = 0; i < input.size(); i++) { - if ('a' <= input[i] && input[i] <= 'z') { - if (cap_next_letter) { - result += input[i] + ('A' - 'a'); - } else { - result += input[i]; - } - cap_next_letter = false; - } else if ('A' <= input[i] && input[i] <= 'Z') { - if (i == 0 && !cap_next_letter) { - // Force first letter to lower-case unless explicitly told to - // capitalize it. - result += input[i] + ('a' - 'A'); - } else { - // Capital letters after the first are left as-is. - result += input[i]; - } - cap_next_letter = false; - } else if ('0' <= input[i] && input[i] <= '9') { - result += input[i]; - cap_next_letter = true; - } else { - cap_next_letter = true; - if (input[i] == '.' && preserve_period) { - result += '.'; - } - } - } - // Add a trailing "_" if the name should be altered. - if (input.size() > 0 && input[input.size() - 1] == '#') { - result += '_'; - } - - // https://github.com/protocolbuffers/protobuf/issues/8101 - // To avoid generating invalid identifiers - if the input string - // starts with _ (or multiple underscores then digit) then - // we need to preserve the underscore as an identifier cannot start - // with a digit. - // This check is being done after the loop rather than before - // to handle the case where there are multiple underscores before the - // first digit. We let them all be consumed so we can see if we would - // start with a digit. - // Note: not preserving leading underscores for all otherwise valid identifiers - // so as to not break anything that relies on the existing behaviour - if (result.size() > 0 && ('0' <= result[0] && result[0] <= '9') - && input.size() > 0 && input[0] == '_') - { - result.insert(0, 1, '_'); - } - return result; -} - -std::string UnderscoresToPascalCase(const std::string& input) { - return UnderscoresToCamelCase(input, true); -} - // Convert a string which is expected to be SHOUTY_CASE (but may not be *precisely* shouty) // into a PascalCase string. Precise rules implemented: @@ -342,32 +246,6 @@ uint GetGroupEndTag(const Descriptor* descriptor) { return 0; } -std::string ToCSharpName(const std::string& name, const FileDescriptor* file) { - std::string result = GetFileNamespace(file); - if (!result.empty()) { - result += '.'; - } - std::string classname; - if (file->package().empty()) { - classname = name; - } else { - // Strip the proto package from full_name since we've replaced it with - // the C# namespace. - classname = name.substr(file->package().size() + 1); - } - result += absl::StrReplaceAll(classname, {{".", ".Types."}}); - return "global::" + result; -} - -std::string GetReflectionClassName(const FileDescriptor* descriptor) { - std::string result = GetFileNamespace(descriptor); - if (!result.empty()) { - result += '.'; - } - result += GetReflectionClassUnqualifiedName(descriptor); - return "global::" + result; -} - std::string GetFullExtensionName(const FieldDescriptor* descriptor) { if (descriptor->extension_scope()) { return GetClassName(descriptor->extension_scope()) + ".Extensions." + GetPropertyName(descriptor); @@ -377,14 +255,6 @@ std::string GetFullExtensionName(const FieldDescriptor* descriptor) { } } -std::string GetClassName(const Descriptor* descriptor) { - return ToCSharpName(descriptor->full_name(), descriptor->file()); -} - -std::string GetClassName(const EnumDescriptor* descriptor) { - return ToCSharpName(descriptor->full_name(), descriptor->file()); -} - // Groups are hacky: The name of the field is just the lower-cased name // of the group type. In C#, though, we would like to retain the original // capitalization of the type name. @@ -437,41 +307,6 @@ std::string GetOneofCaseName(const FieldDescriptor* descriptor) { return property_name == "None" ? "None_" : property_name; } -std::string GetOutputFile(const FileDescriptor* descriptor, - const std::string file_extension, - const bool generate_directories, - const std::string base_namespace, - std::string* error) { - std::string relative_filename = GetFileNameBase(descriptor) + file_extension; - if (!generate_directories) { - return relative_filename; - } - std::string ns = GetFileNamespace(descriptor); - std::string namespace_suffix = ns; - if (!base_namespace.empty()) { - // Check that the base_namespace is either equal to or a leading part of - // the file namespace. This isn't just a simple prefix; "Foo.B" shouldn't - // be regarded as a prefix of "Foo.Bar". The simplest option is to add "." - // to both. - std::string extended_ns = ns + "."; - if (extended_ns.find(base_namespace + ".") != 0) { - *error = "Namespace " + ns + " is not a prefix namespace of base namespace " + base_namespace; - return ""; // This will be ignored, because we've set an error. - } - namespace_suffix = ns.substr(base_namespace.length()); - if (namespace_suffix.find('.') == 0) { - namespace_suffix = namespace_suffix.substr(1); - } - } - - std::string namespace_dir = - absl::StrReplaceAll(namespace_suffix, {{".", "/"}}); - if (!namespace_dir.empty()) { - namespace_dir += "/"; - } - return namespace_dir + relative_filename; -} - // TODO: c&p from Java protoc plugin // For encodings with fixed sizes, returns that size in bytes. Otherwise // returns -1. diff --git a/src/google/protobuf/compiler/csharp/csharp_helpers.h b/src/google/protobuf/compiler/csharp/csharp_helpers.h index 9c48cc8a0eaf..b9aa556a7b0c 100644 --- a/src/google/protobuf/compiler/csharp/csharp_helpers.h +++ b/src/google/protobuf/compiler/csharp/csharp_helpers.h @@ -38,6 +38,7 @@ #include #include "google/protobuf/compiler/code_generator.h" +#include "google/protobuf/compiler/csharp/names.h" #include "google/protobuf/descriptor.h" #include "google/protobuf/port.h" #include "google/protobuf/stubs/common.h" @@ -72,15 +73,6 @@ enum CSharpType { // Converts field type to corresponding C# type. CSharpType GetCSharpType(FieldDescriptor::Type type); -std::string StripDotProto(const std::string& proto_file); - -// Gets unqualified name of the reflection class -std::string GetReflectionClassUnqualifiedName(const FileDescriptor* descriptor); -// Gets unqualified name of the extension class -std::string GetExtensionClassUnqualifiedName(const FileDescriptor* descriptor); - -std::string GetClassName(const EnumDescriptor* descriptor); - std::string GetFieldName(const FieldDescriptor* descriptor); std::string GetFieldConstantName(const FieldDescriptor* field); @@ -91,18 +83,6 @@ std::string GetOneofCaseName(const FieldDescriptor* descriptor); int GetFixedSize(FieldDescriptor::Type type); -// Note that we wouldn't normally want to export this (we're not expecting -// it to be used outside libprotoc itself) but this exposes it for testing. -std::string PROTOC_EXPORT UnderscoresToCamelCase(const std::string& input, - bool cap_next_letter, - bool preserve_period); - -inline std::string UnderscoresToCamelCase(const std::string& input, bool cap_next_letter) { - return UnderscoresToCamelCase(input, cap_next_letter, false); -} - -std::string UnderscoresToPascalCase(const std::string& input); - // Note that we wouldn't normally want to export this (we're not expecting // it to be used outside libprotoc itself) but this exposes it for testing. std::string PROTOC_EXPORT GetEnumValueName(const std::string& enum_name, diff --git a/src/google/protobuf/compiler/csharp/csharp_message.cc b/src/google/protobuf/compiler/csharp/csharp_message.cc index 92fe55dd1c06..bdbd480a61fd 100644 --- a/src/google/protobuf/compiler/csharp/csharp_message.cc +++ b/src/google/protobuf/compiler/csharp/csharp_message.cc @@ -40,7 +40,7 @@ #include "google/protobuf/compiler/csharp/csharp_enum.h" #include "google/protobuf/compiler/csharp/csharp_field_base.h" #include "google/protobuf/compiler/csharp/csharp_helpers.h" -#include "google/protobuf/compiler/csharp/csharp_names.h" +#include "google/protobuf/compiler/csharp/names.h" #include "google/protobuf/compiler/csharp/csharp_options.h" #include "google/protobuf/descriptor.h" #include "google/protobuf/descriptor.pb.h" diff --git a/src/google/protobuf/compiler/csharp/csharp_reflection_class.cc b/src/google/protobuf/compiler/csharp/csharp_reflection_class.cc index 95a00566fcf2..e23c48a24eaf 100644 --- a/src/google/protobuf/compiler/csharp/csharp_reflection_class.cc +++ b/src/google/protobuf/compiler/csharp/csharp_reflection_class.cc @@ -38,7 +38,7 @@ #include "google/protobuf/compiler/csharp/csharp_field_base.h" #include "google/protobuf/compiler/csharp/csharp_helpers.h" #include "google/protobuf/compiler/csharp/csharp_message.h" -#include "google/protobuf/compiler/csharp/csharp_names.h" +#include "google/protobuf/compiler/csharp/names.h" #include "google/protobuf/compiler/csharp/csharp_options.h" #include "google/protobuf/descriptor.h" #include "google/protobuf/descriptor.pb.h" diff --git a/src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc b/src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc index 0d4b639e6458..f12db82125e4 100644 --- a/src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc +++ b/src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc @@ -35,7 +35,7 @@ #include "google/protobuf/compiler/code_generator.h" #include "google/protobuf/descriptor.h" #include "google/protobuf/compiler/csharp/csharp_helpers.h" -#include "google/protobuf/compiler/csharp/csharp_names.h" +#include "google/protobuf/compiler/csharp/names.h" #include "google/protobuf/compiler/csharp/csharp_options.h" #include "google/protobuf/descriptor.pb.h" #include "google/protobuf/io/printer.h" diff --git a/src/google/protobuf/compiler/csharp/names.cc b/src/google/protobuf/compiler/csharp/names.cc new file mode 100644 index 000000000000..b4ca40870a15 --- /dev/null +++ b/src/google/protobuf/compiler/csharp/names.cc @@ -0,0 +1,225 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include "google/protobuf/compiler/csharp/names.h" + +#include + +#include "absl/strings/str_replace.h" +#include "google/protobuf/compiler/csharp/names.h" +#include "google/protobuf/descriptor.pb.h" + +// Must be last. +#include "google/protobuf/port_def.inc" + +namespace google { +namespace protobuf { +namespace compiler { +namespace csharp { + +namespace { + +std::string StripDotProto(const std::string& proto_file) { + int lastindex = proto_file.find_last_of('.'); + return proto_file.substr(0, lastindex); +} + +// Returns the Pascal-cased last part of the proto file. For example, +// input of "google/protobuf/foo_bar.proto" would result in "FooBar". +std::string GetFileNameBase(const FileDescriptor* descriptor) { + std::string proto_file = descriptor->name(); + int lastslash = proto_file.find_last_of('/'); + std::string base = proto_file.substr(lastslash + 1); + return UnderscoresToPascalCase(StripDotProto(base)); +} + +std::string ToCSharpName(const std::string& name, const FileDescriptor* file) { + std::string result = GetFileNamespace(file); + if (!result.empty()) { + result += '.'; + } + std::string classname; + if (file->package().empty()) { + classname = name; + } else { + // Strip the proto package from full_name since we've replaced it with + // the C# namespace. + classname = name.substr(file->package().size() + 1); + } + result += absl::StrReplaceAll(classname, {{".", ".Types."}}); + return "global::" + result; +} + +} // namespace + +std::string GetFileNamespace(const FileDescriptor* descriptor) { + if (descriptor->options().has_csharp_namespace()) { + return descriptor->options().csharp_namespace(); + } + return UnderscoresToCamelCase(descriptor->package(), true, true); +} + +std::string GetClassName(const Descriptor* descriptor) { + return ToCSharpName(descriptor->full_name(), descriptor->file()); +} + +std::string GetClassName(const EnumDescriptor* descriptor) { + return ToCSharpName(descriptor->full_name(), descriptor->file()); +} + +std::string GetReflectionClassUnqualifiedName(const FileDescriptor* descriptor) { + // TODO: Detect collisions with existing messages, + // and append an underscore if necessary. + return GetFileNameBase(descriptor) + "Reflection"; +} + +std::string GetReflectionClassName(const FileDescriptor* descriptor) { + std::string result = GetFileNamespace(descriptor); + if (!result.empty()) { + result += '.'; + } + result += GetReflectionClassUnqualifiedName(descriptor); + return "global::" + result; +} + +std::string GetExtensionClassUnqualifiedName(const FileDescriptor* descriptor) { + // TODO: Detect collisions with existing messages, + // and append an underscore if necessary. + return GetFileNameBase(descriptor) + "Extensions"; +} + +std::string GetOutputFile(const FileDescriptor* descriptor, + const std::string file_extension, + const bool generate_directories, + const std::string base_namespace, + std::string* error) { + std::string relative_filename = GetFileNameBase(descriptor) + file_extension; + if (!generate_directories) { + return relative_filename; + } + std::string ns = GetFileNamespace(descriptor); + std::string namespace_suffix = ns; + if (!base_namespace.empty()) { + // Check that the base_namespace is either equal to or a leading part of + // the file namespace. This isn't just a simple prefix; "Foo.B" shouldn't + // be regarded as a prefix of "Foo.Bar". The simplest option is to add "." + // to both. + std::string extended_ns = ns + "."; + if (extended_ns.find(base_namespace + ".") != 0) { + *error = "Namespace " + ns + " is not a prefix namespace of base namespace " + base_namespace; + return ""; // This will be ignored, because we've set an error. + } + namespace_suffix = ns.substr(base_namespace.length()); + if (namespace_suffix.find('.') == 0) { + namespace_suffix = namespace_suffix.substr(1); + } + } + + std::string namespace_dir = + absl::StrReplaceAll(namespace_suffix, {{".", "/"}}); + if (!namespace_dir.empty()) { + namespace_dir += "/"; + } + return namespace_dir + relative_filename; +} + +std::string UnderscoresToPascalCase(const std::string& input) { + return UnderscoresToCamelCase(input, true); +} + +// TODO(jtattermusch): can we reuse a utility function? +std::string UnderscoresToCamelCase(const std::string& input, + bool cap_next_letter, + bool preserve_period) { + std::string result; + + // Note: I distrust ctype.h due to locales. + for (int i = 0; i < input.size(); i++) { + if ('a' <= input[i] && input[i] <= 'z') { + if (cap_next_letter) { + result += input[i] + ('A' - 'a'); + } else { + result += input[i]; + } + cap_next_letter = false; + } else if ('A' <= input[i] && input[i] <= 'Z') { + if (i == 0 && !cap_next_letter) { + // Force first letter to lower-case unless explicitly told to + // capitalize it. + result += input[i] + ('a' - 'A'); + } else { + // Capital letters after the first are left as-is. + result += input[i]; + } + cap_next_letter = false; + } else if ('0' <= input[i] && input[i] <= '9') { + result += input[i]; + cap_next_letter = true; + } else { + cap_next_letter = true; + if (input[i] == '.' && preserve_period) { + result += '.'; + } + } + } + // Add a trailing "_" if the name should be altered. + if (input.size() > 0 && input[input.size() - 1] == '#') { + result += '_'; + } + + // https://github.com/protocolbuffers/protobuf/issues/8101 + // To avoid generating invalid identifiers - if the input string + // starts with _ (or multiple underscores then digit) then + // we need to preserve the underscore as an identifier cannot start + // with a digit. + // This check is being done after the loop rather than before + // to handle the case where there are multiple underscores before the + // first digit. We let them all be consumed so we can see if we would + // start with a digit. + // Note: not preserving leading underscores for all otherwise valid identifiers + // so as to not break anything that relies on the existing behaviour + if (result.size() > 0 && ('0' <= result[0] && result[0] <= '9') + && input.size() > 0 && input[0] == '_') + { + result.insert(0, 1, '_'); + } + return result; +} + +} // namespace csharp +} // namespace compiler +} // namespace protobuf +} // namespace google + +#include "google/protobuf/port_undef.inc" diff --git a/src/google/protobuf/compiler/csharp/csharp_names.h b/src/google/protobuf/compiler/csharp/names.h similarity index 72% rename from src/google/protobuf/compiler/csharp/csharp_names.h rename to src/google/protobuf/compiler/csharp/names.h index e7688113bd8e..4826ea0a3668 100644 --- a/src/google/protobuf/compiler/csharp/csharp_names.h +++ b/src/google/protobuf/compiler/csharp/names.h @@ -71,9 +71,35 @@ std::string PROTOC_EXPORT GetClassName(const Descriptor* descriptor); // descriptor != NULL // // Returns: -// The fully-qualified name of the C# class that provides -// access to the file descriptor. Proto compiler generates +// The fully-qualified C# enum class name. +std::string GetClassName(const EnumDescriptor* descriptor); + +// Requires: +// descriptor != NULL +// +// Returns: +// The unqualified name of the C# class that provides access to the file +// descriptor. Proto compiler generates // such class for each .proto file processed. +std::string GetReflectionClassUnqualifiedName(const FileDescriptor* descriptor); + +// Gets unqualified name of the extension class +// Requires: +// descriptor != NULL +// +// Returns: +// The unqualified name of the generated C# extensions class that provide +// access to extensions. Proto compiler generates such class for each +// .proto file processed that contains extensions. +std::string GetExtensionClassUnqualifiedName(const FileDescriptor* descriptor); + +// Requires: +// descriptor != NULL +// +// Returns: +// The fully-qualified name of the C# class that provides access to the file +// descriptor. Proto compiler generates such class for each .proto file +// processed. std::string PROTOC_EXPORT GetReflectionClassName(const FileDescriptor* descriptor); @@ -97,6 +123,18 @@ std::string PROTOC_EXPORT GetOutputFile(const FileDescriptor* descriptor, const std::string base_namespace, std::string* error); +std::string UnderscoresToPascalCase(const std::string& input); + +// Note that we wouldn't normally want to export this (we're not expecting +// it to be used outside libprotoc itself) but this exposes it for testing. +std::string PROTOC_EXPORT UnderscoresToCamelCase(const std::string& input, + bool cap_next_letter, + bool preserve_period); + +inline std::string UnderscoresToCamelCase(const std::string& input, bool cap_next_letter) { + return UnderscoresToCamelCase(input, cap_next_letter, false); +} + } // namespace csharp } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/java/BUILD.bazel b/src/google/protobuf/compiler/java/BUILD.bazel index 5c4d5f162d06..3ccc73da812f 100644 --- a/src/google/protobuf/compiler/java/BUILD.bazel +++ b/src/google/protobuf/compiler/java/BUILD.bazel @@ -6,6 +6,41 @@ load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix") load("//build_defs:cpp_opts.bzl", "COPTS") +cc_library( + name = "names", + hdrs = ["names.h"], + copts = COPTS, + include_prefix = "google/protobuf/compiler/java", + visibility = ["//visibility:public"], + deps = [ + ":names_internal", + "//src/google/protobuf:protobuf_nowkt", + ], +) + +cc_library( + name = "names_internal", + hdrs = [ + "helpers.h", + "name_resolver.h", + "names.h", + "options.h", + ], + srcs = [ + "helpers.cc", + "name_resolver.cc", + "names.cc", + ], + copts = COPTS, + include_prefix = "google/protobuf/compiler/java", + visibility = ["//pkg:__pkg__"], + deps = [ + "//src/google/protobuf/compiler:code_generator", + "//src/google/protobuf:protobuf_nowkt", + "@com_google_absl//absl/container:flat_hash_set", + ], +) + cc_library( name = "java", srcs = [ @@ -21,7 +56,6 @@ cc_library( "file.cc", "generator.cc", "generator_factory.cc", - "helpers.cc", "kotlin_generator.cc", "map_field.cc", "map_field_lite.cc", @@ -32,7 +66,6 @@ cc_library( "message_field_lite.cc", "message_lite.cc", "message_serialization.cc", - "name_resolver.cc", "primitive_field.cc", "primitive_field_lite.cc", "service.cc", @@ -53,7 +86,6 @@ cc_library( "file.h", "generator.h", "generator_factory.h", - "helpers.h", "kotlin_generator.h", "map_field.h", "map_field_lite.h", @@ -64,9 +96,6 @@ cc_library( "message_field_lite.h", "message_lite.h", "message_serialization.h", - "name_resolver.h", - "names.h", - "options.h", "primitive_field.h", "primitive_field_lite.h", "service.h", @@ -81,6 +110,8 @@ cc_library( "//src/google/protobuf/compiler:__pkg__", ], deps = [ + ":names_internal", + ":names", "//src/google/protobuf:protobuf_nowkt", "//src/google/protobuf/compiler:code_generator", "@com_google_absl//absl/container:flat_hash_set", diff --git a/src/google/protobuf/compiler/java/context.cc b/src/google/protobuf/compiler/java/context.cc index d9bc0fe2a8dc..e750dcbc8609 100644 --- a/src/google/protobuf/compiler/java/context.cc +++ b/src/google/protobuf/compiler/java/context.cc @@ -131,10 +131,10 @@ void Context::InitializeFieldGeneratorInfoForFields( std::vector conflict_reason(fields.size()); for (int i = 0; i < fields.size(); ++i) { const FieldDescriptor* field = fields[i]; - const std::string& name = UnderscoresToCapitalizedCamelCase(field); + const std::string& name = CapitalizedFieldName(field); for (int j = i + 1; j < fields.size(); ++j) { const FieldDescriptor* other = fields[j]; - const std::string& other_name = UnderscoresToCapitalizedCamelCase(other); + const std::string& other_name = CapitalizedFieldName(other); if (name == other_name) { is_conflict[i] = is_conflict[j] = true; conflict_reason[i] = conflict_reason[j] = @@ -155,7 +155,7 @@ void Context::InitializeFieldGeneratorInfoForFields( const FieldDescriptor* field = fields[i]; FieldGeneratorInfo info; info.name = CamelCaseFieldName(field); - info.capitalized_name = UnderscoresToCapitalizedCamelCase(field); + info.capitalized_name = CapitalizedFieldName(field); // For fields conflicting with some other fields, we append the field // number to their field names in generated code to avoid conflicts. if (is_conflict[i]) { diff --git a/src/google/protobuf/compiler/java/context.h b/src/google/protobuf/compiler/java/context.h index 4697c8ce86ad..2291cfaac0ce 100644 --- a/src/google/protobuf/compiler/java/context.h +++ b/src/google/protobuf/compiler/java/context.h @@ -35,6 +35,7 @@ #include #include "absl/container/flat_hash_map.h" +#include "google/protobuf/compiler/java/helpers.h" #include "google/protobuf/compiler/java/options.h" #include "google/protobuf/port.h" @@ -106,6 +107,20 @@ class Context { Options options_; }; +template +void MaybePrintGeneratedAnnotation(Context* context, io::Printer* printer, + Descriptor* descriptor, bool immutable, + const std::string& suffix = "") { + if (IsOwnFile(descriptor, immutable)) { + PrintGeneratedAnnotation(printer, '$', + context->options().annotate_code + ? AnnotationFileName(descriptor, suffix) + : "", + context->options()); + } +} + + } // namespace java } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/java/helpers.cc b/src/google/protobuf/compiler/java/helpers.cc index 2fe7a0a4a460..553f00453845 100644 --- a/src/google/protobuf/compiler/java/helpers.cc +++ b/src/google/protobuf/compiler/java/helpers.cc @@ -51,7 +51,6 @@ #include "absl/strings/string_view.h" #include "absl/strings/substitute.h" #include "google/protobuf/compiler/java/name_resolver.h" -#include "google/protobuf/compiler/java/names.h" #include "google/protobuf/descriptor.pb.h" // Must be last. @@ -70,75 +69,6 @@ const char kThickSeparator[] = const char kThinSeparator[] = "// -------------------------------------------------------------------\n"; -namespace { -const char* DefaultPackage(Options options) { - return options.opensource_runtime ? "" : "com.google.protos"; -} - -bool IsReservedName(absl::string_view name) { - static const auto& kReservedNames = - *new absl::flat_hash_set({ - "abstract", "assert", "boolean", "break", "byte", - "case", "catch", "char", "class", "const", - "continue", "default", "do", "double", "else", - "enum", "extends", "final", "finally", "float", - "for", "goto", "if", "implements", "import", - "instanceof", "int", "interface", "long", "native", - "new", "package", "private", "protected", "public", - "return", "short", "static", "strictfp", "super", - "switch", "synchronized", "this", "throw", "throws", - "transient", "try", "void", "volatile", "while", - }); - return kReservedNames.contains(name); -} - -bool IsForbidden(const std::string& field_name) { - // Names that should be avoided (in UpperCamelCase format). - // Using them will cause the compiler to generate accessors whose names - // collide with methods defined in base classes. - // Keep this list in sync with specialFieldNames in - // java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java - static const auto& kForbiddenNames = - *new absl::flat_hash_set({ - // java.lang.Object: - "Class", - // com.google.protobuf.MessageLiteOrBuilder: - "DefaultInstanceForType", - // com.google.protobuf.MessageLite: - "ParserForType", - "SerializedSize", - // com.google.protobuf.MessageOrBuilder: - "AllFields", - "DescriptorForType", - "InitializationErrorString", - "UnknownFields", - // obsolete. kept for backwards compatibility of generated code - "CachedSize", - }); - return kForbiddenNames.contains(UnderscoresToCamelCase(field_name, true)); -} - -std::string FieldName(const FieldDescriptor* field) { - std::string field_name; - // Groups are hacky: The name of the field is just the lower-cased name - // of the group type. In Java, though, we would like to retain the original - // capitalization of the type name. - if (GetType(field) == FieldDescriptor::TYPE_GROUP) { - field_name = field->message_type()->name(); - } else { - field_name = field->name(); - } - if (IsForbidden(field_name)) { - // Append a trailing "#" to indicate that the name should be decorated to - // avoid collision with other names. - field_name += "#"; - } - return field_name; -} - - -} // namespace - void PrintGeneratedAnnotation(io::Printer* printer, char delimiter, const std::string& annotation_file, Options options) { @@ -236,30 +166,6 @@ std::string ToCamelCase(const std::string& input, bool lower_first) { return result; } -std::string UnderscoresToCamelCase(const FieldDescriptor* field) { - return UnderscoresToCamelCase(FieldName(field), false); -} - -std::string UnderscoresToCapitalizedCamelCase(const FieldDescriptor* field) { - return UnderscoresToCamelCase(FieldName(field), true); -} - -std::string CapitalizedFieldName(const FieldDescriptor* field) { - return UnderscoresToCapitalizedCamelCase(field); -} - -std::string UnderscoresToCamelCase(const MethodDescriptor* method) { - return UnderscoresToCamelCase(method->name(), false); -} - -std::string UnderscoresToCamelCaseCheckReserved(const FieldDescriptor* field) { - std::string name = UnderscoresToCamelCase(field); - if (IsReservedName(name)) { - return name + "_"; - } - return name; -} - // Names that should be avoided as field names in Kotlin. // All Kotlin hard keywords are in this list. bool IsForbiddenKotlin(absl::string_view field_name) { @@ -305,54 +211,12 @@ std::string FileClassName(const FileDescriptor* file, bool immutable) { return ClassNameResolver().GetFileClassName(file, immutable); } -std::string FileJavaPackage(const FileDescriptor* file, bool immutable, - Options options) { - std::string result; - - if (file->options().has_java_package()) { - result = file->options().java_package(); - } else { - result = DefaultPackage(options); - if (!file->package().empty()) { - if (!result.empty()) result += '.'; - result += file->package(); - } - } - - return result; -} - -std::string FileJavaPackage(const FileDescriptor* file, Options options) { - return FileJavaPackage(file, true /* immutable */, options); -} - std::string JavaPackageToDir(std::string package_name) { std::string package_dir = absl::StrReplaceAll(package_name, {{".", "/"}}); if (!package_dir.empty()) package_dir += "/"; return package_dir; } -std::string ClassName(const Descriptor* descriptor) { - ClassNameResolver name_resolver; - return name_resolver.GetClassName(descriptor, true); -} - -std::string ClassName(const EnumDescriptor* descriptor) { - ClassNameResolver name_resolver; - return name_resolver.GetClassName(descriptor, true); -} - -std::string ClassName(const ServiceDescriptor* descriptor) { - ClassNameResolver name_resolver; - return name_resolver.GetClassName(descriptor, true); -} - -std::string ClassName(const FileDescriptor* descriptor) { - ClassNameResolver name_resolver; - return name_resolver.GetClassName(descriptor, true); -} - - std::string ExtraMessageInterfaces(const Descriptor* descriptor) { std::string interfaces = "// @@protoc_insertion_point(message_implements:" + descriptor->full_name() + ")"; diff --git a/src/google/protobuf/compiler/java/helpers.h b/src/google/protobuf/compiler/java/helpers.h index 6fe43aedced4..9273ae3c0ba6 100644 --- a/src/google/protobuf/compiler/java/helpers.h +++ b/src/google/protobuf/compiler/java/helpers.h @@ -41,7 +41,7 @@ #include "google/protobuf/io/printer.h" #include "google/protobuf/descriptor.h" #include "absl/strings/string_view.h" -#include "google/protobuf/compiler/java/context.h" +#include "google/protobuf/compiler/java/names.h" #include "google/protobuf/compiler/java/options.h" #include "google/protobuf/descriptor.pb.h" @@ -84,22 +84,6 @@ void PrintEnumVerifierLogic(io::Printer* printer, // first letter. std::string ToCamelCase(const std::string& input, bool lower_first); -// Converts a name to camel-case. If cap_first_letter is true, capitalize the -// first letter. -std::string UnderscoresToCamelCase(const std::string& name, - bool cap_first_letter); -// Converts the field's name to camel-case, e.g. "foo_bar_baz" becomes -// "fooBarBaz" or "FooBarBaz", respectively. -std::string UnderscoresToCamelCase(const FieldDescriptor* field); -std::string UnderscoresToCapitalizedCamelCase(const FieldDescriptor* field); - -// Similar, but for method names. (Typically, this merely has the effect -// of lower-casing the first letter of the name.) -std::string UnderscoresToCamelCase(const MethodDescriptor* method); - -// Same as UnderscoresToCamelCase, but checks for reserved keywords -std::string UnderscoresToCamelCaseCheckReserved(const FieldDescriptor* field); - // Similar to UnderscoresToCamelCase, but guarantees that the result is a // complete Java identifier by adding a _ if needed. std::string CamelCaseFieldName(const FieldDescriptor* field); @@ -205,19 +189,6 @@ std::string AnnotationFileName(const Descriptor* descriptor, return descriptor->name() + suffix + ".java.pb.meta"; } -template -void MaybePrintGeneratedAnnotation(Context* context, io::Printer* printer, - Descriptor* descriptor, bool immutable, - const std::string& suffix = "") { - if (IsOwnFile(descriptor, immutable)) { - PrintGeneratedAnnotation(printer, '$', - context->options().annotate_code - ? AnnotationFileName(descriptor, suffix) - : "", - context->options()); - } -} - // Get the unqualified name that should be used for a field's field // number constant. std::string FieldConstantName(const FieldDescriptor* field); diff --git a/src/google/protobuf/compiler/java/names.cc b/src/google/protobuf/compiler/java/names.cc new file mode 100644 index 000000000000..7d6ec92a5087 --- /dev/null +++ b/src/google/protobuf/compiler/java/names.cc @@ -0,0 +1,194 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include "google/protobuf/compiler/java/names.h" + +#include + +#include "absl/container/flat_hash_set.h" +#include "google/protobuf/compiler/java/helpers.h" +#include "google/protobuf/compiler/java/name_resolver.h" +#include "google/protobuf/compiler/java/names.h" +#include "google/protobuf/compiler/java/options.h" +#include "google/protobuf/descriptor.pb.h" + +// Must be last. +#include "google/protobuf/port_def.inc" + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +namespace { + +const char* DefaultPackage(Options options) { + return options.opensource_runtime ? "" : "com.google.protos"; +} + +bool IsReservedName(absl::string_view name) { + static const auto& kReservedNames = + *new absl::flat_hash_set({ + "abstract", "assert", "boolean", "break", "byte", + "case", "catch", "char", "class", "const", + "continue", "default", "do", "double", "else", + "enum", "extends", "final", "finally", "float", + "for", "goto", "if", "implements", "import", + "instanceof", "int", "interface", "long", "native", + "new", "package", "private", "protected", "public", + "return", "short", "static", "strictfp", "super", + "switch", "synchronized", "this", "throw", "throws", + "transient", "try", "void", "volatile", "while", + }); + return kReservedNames.contains(name); +} + +bool IsForbidden(const std::string& field_name) { + // Names that should be avoided (in UpperCamelCase format). + // Using them will cause the compiler to generate accessors whose names + // collide with methods defined in base classes. + // Keep this list in sync with specialFieldNames in + // java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java + static const auto& kForbiddenNames = + *new absl::flat_hash_set({ + // java.lang.Object: + "Class", + // com.google.protobuf.MessageLiteOrBuilder: + "DefaultInstanceForType", + // com.google.protobuf.MessageLite: + "ParserForType", + "SerializedSize", + // com.google.protobuf.MessageOrBuilder: + "AllFields", + "DescriptorForType", + "InitializationErrorString", + "UnknownFields", + // obsolete. kept for backwards compatibility of generated code + "CachedSize", + }); + return kForbiddenNames.contains(UnderscoresToCamelCase(field_name, true)); +} + +std::string FieldName(const FieldDescriptor* field) { + std::string field_name; + // Groups are hacky: The name of the field is just the lower-cased name + // of the group type. In Java, though, we would like to retain the original + // capitalization of the type name. + if (GetType(field) == FieldDescriptor::TYPE_GROUP) { + field_name = field->message_type()->name(); + } else { + field_name = field->name(); + } + if (IsForbidden(field_name)) { + // Append a trailing "#" to indicate that the name should be decorated to + // avoid collision with other names. + field_name += "#"; + } + return field_name; +} + +} // namespace + +std::string ClassName(const Descriptor* descriptor) { + ClassNameResolver name_resolver; + return name_resolver.GetClassName(descriptor, true); +} + +std::string ClassName(const EnumDescriptor* descriptor) { + ClassNameResolver name_resolver; + return name_resolver.GetClassName(descriptor, true); +} + +std::string ClassName(const ServiceDescriptor* descriptor) { + ClassNameResolver name_resolver; + return name_resolver.GetClassName(descriptor, true); +} + +std::string ClassName(const FileDescriptor* descriptor) { + ClassNameResolver name_resolver; + return name_resolver.GetClassName(descriptor, true); +} + + +std::string FileJavaPackage(const FileDescriptor* file, bool immutable, + Options options) { + std::string result; + + if (file->options().has_java_package()) { + result = file->options().java_package(); + } else { + result = DefaultPackage(options); + if (!file->package().empty()) { + if (!result.empty()) result += '.'; + result += file->package(); + } + } + + return result; +} + +std::string FileJavaPackage(const FileDescriptor* file, Options options) { + return FileJavaPackage(file, true /* immutable */, options); +} + +std::string CapitalizedFieldName(const FieldDescriptor* field) { + return UnderscoresToCamelCase(FieldName(field), true); +} + +std::string UnderscoresToCamelCase(const FieldDescriptor* field) { + return UnderscoresToCamelCase(FieldName(field), false); +} + +std::string UnderscoresToCapitalizedCamelCase(const FieldDescriptor* field) { + return UnderscoresToCamelCase(FieldName(field), true); +} + +std::string UnderscoresToCamelCase(const MethodDescriptor* method) { + return UnderscoresToCamelCase(method->name(), false); +} + +std::string UnderscoresToCamelCaseCheckReserved(const FieldDescriptor* field) { + std::string name = UnderscoresToCamelCase(field); + if (IsReservedName(name)) { + return name + "_"; + } + return name; +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google + +#include "google/protobuf/port_undef.inc" diff --git a/src/google/protobuf/compiler/java/names.h b/src/google/protobuf/compiler/java/names.h index f9c4b953840f..73c78fb2363c 100644 --- a/src/google/protobuf/compiler/java/names.h +++ b/src/google/protobuf/compiler/java/names.h @@ -40,6 +40,7 @@ #include +#include "google/protobuf/descriptor.h" #include "google/protobuf/compiler/java/options.h" namespace google { @@ -96,6 +97,31 @@ std::string FileJavaPackage(const FileDescriptor* descriptor, // Capitalized camel case name field name. std::string CapitalizedFieldName(const FieldDescriptor* descriptor); +// Returns: +// Converts a name to camel-case. If cap_first_letter is true, capitalize the +// first letter. +std::string UnderscoresToCamelCase(const std::string& name, + bool cap_first_letter); +// Requires: +// field != NULL +// Returns: +// Converts the field's name to camel-case, e.g. "foo_bar_baz" becomes +// "fooBarBaz" or "FooBarBaz", respectively. +std::string UnderscoresToCamelCase(const FieldDescriptor* field); + +// Requires: +// method != NULL +// Returns: +// Similar, but for method names. (Typically, this merely has the effect +// of lower-casing the first letter of the name.) +std::string UnderscoresToCamelCase(const MethodDescriptor* method); + +// Requires: +// field != NULL +// Returns: +// Same as UnderscoresToCamelCase, but checks for reserved keywords +std::string UnderscoresToCamelCaseCheckReserved(const FieldDescriptor* field); + } // namespace java } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/objectivec/BUILD.bazel b/src/google/protobuf/compiler/objectivec/BUILD.bazel index ee2ef1f0e0be..248e2ab8cde4 100644 --- a/src/google/protobuf/compiler/objectivec/BUILD.bazel +++ b/src/google/protobuf/compiler/objectivec/BUILD.bazel @@ -6,6 +6,35 @@ load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix") load("//build_defs:cpp_opts.bzl", "COPTS") +cc_library( + name = "names", + hdrs = ["names.h"], + copts = COPTS, + include_prefix = "google/protobuf/compiler/objectivec", + visibility = ["//visibility:public"], + deps = [ + ":names_internal", + ], +) + +cc_library( + name = "names_internal", + hdrs = [ + "names.h", + "objectivec_nsobject_methods.h", + ], + srcs = [ + "names.cc", + ], + copts = COPTS, + include_prefix = "google/protobuf/compiler/objectivec", + visibility = ["//pkg:__pkg__"], + deps = [ + "//src/google/protobuf/compiler:code_generator", + "//src/google/protobuf:protobuf_nowkt", + ], +) + cc_library( name = "objectivec", srcs = [ @@ -15,7 +44,6 @@ cc_library( "objectivec_field.cc", "objectivec_file.cc", "objectivec_generator.cc", - "objectivec_helpers.cc", "objectivec_map_field.cc", "objectivec_message.cc", "objectivec_message_field.cc", @@ -33,7 +61,6 @@ cc_library( "objectivec_map_field.h", "objectivec_message.h", "objectivec_message_field.h", - "objectivec_nsobject_methods.h", "objectivec_oneof.h", "objectivec_options.h", "objectivec_primitive_field.h", @@ -45,6 +72,7 @@ cc_library( "//src/google/protobuf/compiler:__pkg__", ], deps = [ + ":names", "//src/google/protobuf:protobuf_nowkt", "//src/google/protobuf/compiler:code_generator", "@com_google_absl//absl/strings", diff --git a/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc b/src/google/protobuf/compiler/objectivec/names.cc similarity index 99% rename from src/google/protobuf/compiler/objectivec/objectivec_helpers.cc rename to src/google/protobuf/compiler/objectivec/names.cc index a9404ca270a7..5ae447fe4b43 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc +++ b/src/google/protobuf/compiler/objectivec/names.cc @@ -49,7 +49,7 @@ #include "absl/strings/str_replace.h" #include "absl/strings/str_split.h" #include "absl/strings/strip.h" -#include "google/protobuf/compiler/objectivec/objectivec_helpers.h" +#include "google/protobuf/compiler/objectivec/names.h" #include "google/protobuf/compiler/objectivec/objectivec_nsobject_methods.h" #include "google/protobuf/descriptor.pb.h" #include "google/protobuf/io/coded_stream.h" diff --git a/src/google/protobuf/compiler/objectivec/names.h b/src/google/protobuf/compiler/objectivec/names.h new file mode 100644 index 000000000000..e75738f323ca --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/names.h @@ -0,0 +1,363 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Helper functions for generating ObjectiveC code. + +#ifndef GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_NAMES_H__ +#define GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_NAMES_H__ + +#include +#include + +#include "google/protobuf/descriptor.h" +#include "google/protobuf/descriptor.pb.h" +#include "google/protobuf/io/zero_copy_stream.h" + +// clang-format off +#include "google/protobuf/port_def.inc" +// clang-format on + +namespace google { +namespace protobuf { +namespace compiler { +namespace objectivec { + +// Get/Set the path to a file to load for objc class prefix lookups. +std::string PROTOC_EXPORT GetPackageToPrefixMappingsPath(); +void PROTOC_EXPORT SetPackageToPrefixMappingsPath( + const std::string& file_path); +// Get/Set if the proto package should be used to make the default prefix for +// symbols. This will then impact most of the type naming apis below. It is done +// as a global to not break any other generator reusing the methods since they +// are exported. +bool PROTOC_EXPORT UseProtoPackageAsDefaultPrefix(); +void PROTOC_EXPORT SetUseProtoPackageAsDefaultPrefix(bool on_or_off); +// Get/Set the path to a file to load as exceptions when +// `UseProtoPackageAsDefaultPrefix()` is `true`. An empty string means there +// should be no exceptions. +std::string PROTOC_EXPORT GetProtoPackagePrefixExceptionList(); +void PROTOC_EXPORT SetProtoPackagePrefixExceptionList( + const std::string& file_path); +// Get/Set a prefix to add before the prefix generated from the package name. +// This is only used when UseProtoPackageAsDefaultPrefix() is True. +std::string PROTOC_EXPORT GetForcedPackagePrefix(); +void PROTOC_EXPORT SetForcedPackagePrefix(const std::string& prefix); + +// Generator Prefix Validation Options (see objectivec_generator.cc for a +// description of each): +struct Options { + Options(); + std::string expected_prefixes_path; + std::vector expected_prefixes_suppressions; + bool prefixes_must_be_registered; + bool require_prefixes; +}; + +// Escape C++ trigraphs by escaping question marks to "\?". +std::string PROTOC_EXPORT EscapeTrigraphs(absl::string_view to_escape); + +// Remove white space from either end of a absl::string_view. +void PROTOC_EXPORT TrimWhitespace(absl::string_view* input); + +// Returns true if the name requires a ns_returns_not_retained attribute applied +// to it. +bool PROTOC_EXPORT IsRetainedName(const std::string& name); + +// Returns true if the name starts with "init" and will need to have special +// handling under ARC. +bool PROTOC_EXPORT IsInitName(const std::string& name); + +// Returns true if the name requires a cf_returns_not_retained attribute applied +// to it. +bool PROTOC_EXPORT IsCreateName(const std::string& name); + +// Gets the objc_class_prefix or the prefix made from the proto package. +std::string PROTOC_EXPORT FileClassPrefix(const FileDescriptor* file); + +// Gets the path of the file we're going to generate (sans the .pb.h +// extension). The path will be dependent on the objectivec package +// declared in the proto package. +std::string PROTOC_EXPORT FilePath(const FileDescriptor* file); + +// Just like FilePath(), but without the directory part. +std::string PROTOC_EXPORT FilePathBasename(const FileDescriptor* file); + +// Gets the name of the root class we'll generate in the file. This class +// is not meant for external consumption, but instead contains helpers that +// the rest of the classes need +std::string PROTOC_EXPORT FileClassName(const FileDescriptor* file); + +// These return the fully-qualified class name corresponding to the given +// descriptor. +std::string PROTOC_EXPORT ClassName(const Descriptor* descriptor); +std::string PROTOC_EXPORT ClassName(const Descriptor* descriptor, + std::string* out_suffix_added); +std::string PROTOC_EXPORT EnumName(const EnumDescriptor* descriptor); + +// Returns the fully-qualified name of the enum value corresponding to the +// the descriptor. +std::string PROTOC_EXPORT EnumValueName(const EnumValueDescriptor* descriptor); + +// Returns the name of the enum value corresponding to the descriptor. +std::string PROTOC_EXPORT EnumValueShortName(const EnumValueDescriptor* descriptor); + +// Reverse what an enum does. +std::string PROTOC_EXPORT UnCamelCaseEnumShortName(const std::string& name); + +// Returns the name to use for the extension (used as the method off the file's +// Root class). +std::string PROTOC_EXPORT ExtensionMethodName(const FieldDescriptor* descriptor); + +// Returns the transformed field name. +std::string PROTOC_EXPORT FieldName(const FieldDescriptor* field); +std::string PROTOC_EXPORT FieldNameCapitalized(const FieldDescriptor* field); + +// Returns the transformed oneof name. +std::string PROTOC_EXPORT OneofEnumName(const OneofDescriptor* descriptor); +std::string PROTOC_EXPORT OneofName(const OneofDescriptor* descriptor); +std::string PROTOC_EXPORT OneofNameCapitalized(const OneofDescriptor* descriptor); + +// Returns a symbol that can be used in C code to refer to an Objective C +// class without initializing the class. +std::string PROTOC_EXPORT ObjCClass(const std::string& class_name); + +// Declares an Objective C class without initializing the class so that it can +// be refrerred to by ObjCClass. +std::string PROTOC_EXPORT ObjCClassDeclaration(const std::string& class_name); + +inline bool HasPreservingUnknownEnumSemantics(const FileDescriptor* file) { + return file->syntax() == FileDescriptor::SYNTAX_PROTO3; +} + +inline bool IsMapEntryMessage(const Descriptor* descriptor) { + return descriptor->options().map_entry(); +} + +// Reverse of the above. +std::string PROTOC_EXPORT UnCamelCaseFieldName(const std::string& name, + const FieldDescriptor* field); + +enum ObjectiveCType { + OBJECTIVECTYPE_INT32, + OBJECTIVECTYPE_UINT32, + OBJECTIVECTYPE_INT64, + OBJECTIVECTYPE_UINT64, + OBJECTIVECTYPE_FLOAT, + OBJECTIVECTYPE_DOUBLE, + OBJECTIVECTYPE_BOOLEAN, + OBJECTIVECTYPE_STRING, + OBJECTIVECTYPE_DATA, + OBJECTIVECTYPE_ENUM, + OBJECTIVECTYPE_MESSAGE +}; + +enum FlagType { + FLAGTYPE_DESCRIPTOR_INITIALIZATION, + FLAGTYPE_EXTENSION, + FLAGTYPE_FIELD +}; + +template +std::string GetOptionalDeprecatedAttribute(const TDescriptor* descriptor, + const FileDescriptor* file = NULL, + bool preSpace = true, + bool postNewline = false) { + bool isDeprecated = descriptor->options().deprecated(); + // The file is only passed when checking Messages & Enums, so those types + // get tagged. At the moment, it doesn't seem to make sense to tag every + // field or enum value with when the file is deprecated. + bool isFileLevelDeprecation = false; + if (!isDeprecated && file) { + isFileLevelDeprecation = file->options().deprecated(); + isDeprecated = isFileLevelDeprecation; + } + if (isDeprecated) { + std::string message; + const FileDescriptor* sourceFile = descriptor->file(); + if (isFileLevelDeprecation) { + message = sourceFile->name() + " is deprecated."; + } else { + message = descriptor->full_name() + " is deprecated (see " + + sourceFile->name() + ")."; + } + + std::string result = std::string("GPB_DEPRECATED_MSG(\"") + message + "\")"; + if (preSpace) { + result.insert(0, " "); + } + if (postNewline) { + result.append("\n"); + } + return result; + } else { + return ""; + } +} + +std::string PROTOC_EXPORT GetCapitalizedType(const FieldDescriptor* field); + +ObjectiveCType PROTOC_EXPORT +GetObjectiveCType(FieldDescriptor::Type field_type); + +inline ObjectiveCType GetObjectiveCType(const FieldDescriptor* field) { + return GetObjectiveCType(field->type()); +} + +bool PROTOC_EXPORT IsPrimitiveType(const FieldDescriptor* field); +bool PROTOC_EXPORT IsReferenceType(const FieldDescriptor* field); + +std::string PROTOC_EXPORT +GPBGenericValueFieldName(const FieldDescriptor* field); +std::string PROTOC_EXPORT DefaultValue(const FieldDescriptor* field); +bool PROTOC_EXPORT HasNonZeroDefaultValue(const FieldDescriptor* field); + +std::string PROTOC_EXPORT +BuildFlagsString(const FlagType type, const std::vector& strings); + +// Builds HeaderDoc/appledoc style comments out of the comments in the .proto +// file. +std::string PROTOC_EXPORT BuildCommentsString(const SourceLocation& location, + bool prefer_single_line); + +// The name the commonly used by the library when built as a framework. +// This lines up to the name used in the CocoaPod. +extern PROTOC_EXPORT const char* const ProtobufLibraryFrameworkName; +// Returns the CPP symbol name to use as the gate for framework style imports +// for the given framework name to use. +std::string PROTOC_EXPORT +ProtobufFrameworkImportSymbol(const std::string& framework_name); + +// Checks if the file is one of the proto's bundled with the library. +bool PROTOC_EXPORT +IsProtobufLibraryBundledProtoFile(const FileDescriptor* file); + +// Checks the prefix for the given files and outputs any warnings as needed. If +// there are flat out errors, then out_error is filled in with the first error +// and the result is false. +bool PROTOC_EXPORT ValidateObjCClassPrefixes( + const std::vector& files, + const Options& validation_options, std::string* out_error); +// Same was the other ValidateObjCClassPrefixes() calls, but the options all +// come from the environment variables. +bool PROTOC_EXPORT ValidateObjCClassPrefixes( + const std::vector& files, std::string* out_error); + +// Generate decode data needed for ObjC's GPBDecodeTextFormatName() to transform +// the input into the expected output. +class PROTOC_EXPORT TextFormatDecodeData { + public: + TextFormatDecodeData(); + ~TextFormatDecodeData(); + + TextFormatDecodeData(const TextFormatDecodeData&) = delete; + TextFormatDecodeData& operator=(const TextFormatDecodeData&) = delete; + + void AddString(int32_t key, const std::string& input_for_decode, + const std::string& desired_output); + size_t num_entries() const { return entries_.size(); } + std::string Data() const; + + static std::string DecodeDataForString(const std::string& input_for_decode, + const std::string& desired_output); + + private: + typedef std::pair DataEntry; + std::vector entries_; +}; + +// Helper for parsing simple files. +class PROTOC_EXPORT LineConsumer { + public: + LineConsumer(); + virtual ~LineConsumer(); + virtual bool ConsumeLine(const absl::string_view& line, std::string* out_error) = 0; +}; + +bool PROTOC_EXPORT ParseSimpleFile(const std::string& path, + LineConsumer* line_consumer, + std::string* out_error); + +bool PROTOC_EXPORT ParseSimpleStream(io::ZeroCopyInputStream& input_stream, + const std::string& stream_name, + LineConsumer* line_consumer, + std::string* out_error); + +// Helper class for parsing framework import mappings and generating +// import statements. +class PROTOC_EXPORT ImportWriter { + public: + ImportWriter(const std::string& generate_for_named_framework, + const std::string& named_framework_to_proto_path_mappings_path, + const std::string& runtime_import_prefix, + bool include_wkt_imports); + ~ImportWriter(); + + void AddFile(const FileDescriptor* file, const std::string& header_extension); + void Print(io::Printer* printer) const; + + static void PrintRuntimeImports(io::Printer* printer, + const std::vector& header_to_import, + const std::string& runtime_import_prefix, + bool default_cpp_symbol = false); + + private: + class ProtoFrameworkCollector : public LineConsumer { + public: + ProtoFrameworkCollector(std::map* inout_proto_file_to_framework_name) + : map_(inout_proto_file_to_framework_name) {} + + virtual bool ConsumeLine(const absl::string_view& line, std::string* out_error) override; + + private: + std::map* map_; + }; + + void ParseFrameworkMappings(); + + const std::string generate_for_named_framework_; + const std::string named_framework_to_proto_path_mappings_path_; + const std::string runtime_import_prefix_; + const bool include_wkt_imports_; + std::map proto_file_to_framework_name_; + bool need_to_parse_mapping_file_; + + std::vector protobuf_imports_; + std::vector other_framework_imports_; + std::vector other_imports_; +}; + +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google + +#include "google/protobuf/port_undef.inc" + +#endif // GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_NAMES_H__ diff --git a/src/google/protobuf/compiler/objectivec/objectivec_helpers.h b/src/google/protobuf/compiler/objectivec/objectivec_helpers.h index a0a8d5b6e8d5..9c61dfa57ba1 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_helpers.h +++ b/src/google/protobuf/compiler/objectivec/objectivec_helpers.h @@ -33,12 +33,7 @@ #ifndef GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_HELPERS_H__ #define GOOGLE_PROTOBUF_COMPILER_OBJECTIVEC_HELPERS_H__ -#include -#include - -#include "google/protobuf/descriptor.h" -#include "google/protobuf/descriptor.pb.h" -#include "google/protobuf/io/zero_copy_stream.h" +#include "google/protobuf/compiler/objectivec/names.h" // clang-format off #include "google/protobuf/port_def.inc" @@ -49,309 +44,8 @@ namespace protobuf { namespace compiler { namespace objectivec { -// Get/Set the path to a file to load for objc class prefix lookups. -std::string PROTOC_EXPORT GetPackageToPrefixMappingsPath(); -void PROTOC_EXPORT SetPackageToPrefixMappingsPath( - const std::string& file_path); -// Get/Set if the proto package should be used to make the default prefix for -// symbols. This will then impact most of the type naming apis below. It is done -// as a global to not break any other generator reusing the methods since they -// are exported. -bool PROTOC_EXPORT UseProtoPackageAsDefaultPrefix(); -void PROTOC_EXPORT SetUseProtoPackageAsDefaultPrefix(bool on_or_off); -// Get/Set the path to a file to load as exceptions when -// `UseProtoPackageAsDefaultPrefix()` is `true`. An empty string means there -// should be no exceptions. -std::string PROTOC_EXPORT GetProtoPackagePrefixExceptionList(); -void PROTOC_EXPORT SetProtoPackagePrefixExceptionList( - const std::string& file_path); -// Get/Set a prefix to add before the prefix generated from the package name. -// This is only used when UseProtoPackageAsDefaultPrefix() is True. -std::string PROTOC_EXPORT GetForcedPackagePrefix(); -void PROTOC_EXPORT SetForcedPackagePrefix(const std::string& prefix); - -// Generator Prefix Validation Options (see objectivec_generator.cc for a -// description of each): -struct Options { - Options(); - std::string expected_prefixes_path; - std::vector expected_prefixes_suppressions; - bool prefixes_must_be_registered; - bool require_prefixes; -}; - -// Escape C++ trigraphs by escaping question marks to "\?". -std::string PROTOC_EXPORT EscapeTrigraphs(absl::string_view to_escape); - -// Remove white space from either end of a absl::string_view. -void PROTOC_EXPORT TrimWhitespace(absl::string_view* input); - -// Returns true if the name requires a ns_returns_not_retained attribute applied -// to it. -bool PROTOC_EXPORT IsRetainedName(const std::string& name); - -// Returns true if the name starts with "init" and will need to have special -// handling under ARC. -bool PROTOC_EXPORT IsInitName(const std::string& name); - -// Returns true if the name requires a cf_returns_not_retained attribute applied -// to it. -bool PROTOC_EXPORT IsCreateName(const std::string& name); - -// Gets the objc_class_prefix or the prefix made from the proto package. -std::string PROTOC_EXPORT FileClassPrefix(const FileDescriptor* file); - -// Gets the path of the file we're going to generate (sans the .pb.h -// extension). The path will be dependent on the objectivec package -// declared in the proto package. -std::string PROTOC_EXPORT FilePath(const FileDescriptor* file); - -// Just like FilePath(), but without the directory part. -std::string PROTOC_EXPORT FilePathBasename(const FileDescriptor* file); - -// Gets the name of the root class we'll generate in the file. This class -// is not meant for external consumption, but instead contains helpers that -// the rest of the classes need -std::string PROTOC_EXPORT FileClassName(const FileDescriptor* file); - -// These return the fully-qualified class name corresponding to the given -// descriptor. -std::string PROTOC_EXPORT ClassName(const Descriptor* descriptor); -std::string PROTOC_EXPORT ClassName(const Descriptor* descriptor, - std::string* out_suffix_added); -std::string PROTOC_EXPORT EnumName(const EnumDescriptor* descriptor); - -// Returns the fully-qualified name of the enum value corresponding to the -// the descriptor. -std::string PROTOC_EXPORT EnumValueName(const EnumValueDescriptor* descriptor); - -// Returns the name of the enum value corresponding to the descriptor. -std::string PROTOC_EXPORT EnumValueShortName(const EnumValueDescriptor* descriptor); - -// Reverse what an enum does. -std::string PROTOC_EXPORT UnCamelCaseEnumShortName(const std::string& name); - -// Returns the name to use for the extension (used as the method off the file's -// Root class). -std::string PROTOC_EXPORT ExtensionMethodName(const FieldDescriptor* descriptor); - -// Returns the transformed field name. -std::string PROTOC_EXPORT FieldName(const FieldDescriptor* field); -std::string PROTOC_EXPORT FieldNameCapitalized(const FieldDescriptor* field); - -// Returns the transformed oneof name. -std::string PROTOC_EXPORT OneofEnumName(const OneofDescriptor* descriptor); -std::string PROTOC_EXPORT OneofName(const OneofDescriptor* descriptor); -std::string PROTOC_EXPORT OneofNameCapitalized(const OneofDescriptor* descriptor); - -// Returns a symbol that can be used in C code to refer to an Objective C -// class without initializing the class. -std::string PROTOC_EXPORT ObjCClass(const std::string& class_name); - -// Declares an Objective C class without initializing the class so that it can -// be refrerred to by ObjCClass. -std::string PROTOC_EXPORT ObjCClassDeclaration(const std::string& class_name); - -inline bool HasPreservingUnknownEnumSemantics(const FileDescriptor* file) { - return file->syntax() == FileDescriptor::SYNTAX_PROTO3; -} - -inline bool IsMapEntryMessage(const Descriptor* descriptor) { - return descriptor->options().map_entry(); -} - -// Reverse of the above. -std::string PROTOC_EXPORT UnCamelCaseFieldName(const std::string& name, - const FieldDescriptor* field); - -enum ObjectiveCType { - OBJECTIVECTYPE_INT32, - OBJECTIVECTYPE_UINT32, - OBJECTIVECTYPE_INT64, - OBJECTIVECTYPE_UINT64, - OBJECTIVECTYPE_FLOAT, - OBJECTIVECTYPE_DOUBLE, - OBJECTIVECTYPE_BOOLEAN, - OBJECTIVECTYPE_STRING, - OBJECTIVECTYPE_DATA, - OBJECTIVECTYPE_ENUM, - OBJECTIVECTYPE_MESSAGE -}; - -enum FlagType { - FLAGTYPE_DESCRIPTOR_INITIALIZATION, - FLAGTYPE_EXTENSION, - FLAGTYPE_FIELD -}; - -template -std::string GetOptionalDeprecatedAttribute(const TDescriptor* descriptor, - const FileDescriptor* file = NULL, - bool preSpace = true, - bool postNewline = false) { - bool isDeprecated = descriptor->options().deprecated(); - // The file is only passed when checking Messages & Enums, so those types - // get tagged. At the moment, it doesn't seem to make sense to tag every - // field or enum value with when the file is deprecated. - bool isFileLevelDeprecation = false; - if (!isDeprecated && file) { - isFileLevelDeprecation = file->options().deprecated(); - isDeprecated = isFileLevelDeprecation; - } - if (isDeprecated) { - std::string message; - const FileDescriptor* sourceFile = descriptor->file(); - if (isFileLevelDeprecation) { - message = sourceFile->name() + " is deprecated."; - } else { - message = descriptor->full_name() + " is deprecated (see " + - sourceFile->name() + ")."; - } - - std::string result = std::string("GPB_DEPRECATED_MSG(\"") + message + "\")"; - if (preSpace) { - result.insert(0, " "); - } - if (postNewline) { - result.append("\n"); - } - return result; - } else { - return ""; - } -} - -std::string PROTOC_EXPORT GetCapitalizedType(const FieldDescriptor* field); - -ObjectiveCType PROTOC_EXPORT -GetObjectiveCType(FieldDescriptor::Type field_type); - -inline ObjectiveCType GetObjectiveCType(const FieldDescriptor* field) { - return GetObjectiveCType(field->type()); -} - -bool PROTOC_EXPORT IsPrimitiveType(const FieldDescriptor* field); -bool PROTOC_EXPORT IsReferenceType(const FieldDescriptor* field); - -std::string PROTOC_EXPORT -GPBGenericValueFieldName(const FieldDescriptor* field); -std::string PROTOC_EXPORT DefaultValue(const FieldDescriptor* field); -bool PROTOC_EXPORT HasNonZeroDefaultValue(const FieldDescriptor* field); - -std::string PROTOC_EXPORT -BuildFlagsString(const FlagType type, const std::vector& strings); - -// Builds HeaderDoc/appledoc style comments out of the comments in the .proto -// file. -std::string PROTOC_EXPORT BuildCommentsString(const SourceLocation& location, - bool prefer_single_line); - -// The name the commonly used by the library when built as a framework. -// This lines up to the name used in the CocoaPod. -extern PROTOC_EXPORT const char* const ProtobufLibraryFrameworkName; -// Returns the CPP symbol name to use as the gate for framework style imports -// for the given framework name to use. -std::string PROTOC_EXPORT -ProtobufFrameworkImportSymbol(const std::string& framework_name); - -// Checks if the file is one of the proto's bundled with the library. -bool PROTOC_EXPORT -IsProtobufLibraryBundledProtoFile(const FileDescriptor* file); - -// Checks the prefix for the given files and outputs any warnings as needed. If -// there are flat out errors, then out_error is filled in with the first error -// and the result is false. -bool PROTOC_EXPORT ValidateObjCClassPrefixes( - const std::vector& files, - const Options& validation_options, std::string* out_error); -// Same was the other ValidateObjCClassPrefixes() calls, but the options all -// come from the environment variables. -bool PROTOC_EXPORT ValidateObjCClassPrefixes( - const std::vector& files, std::string* out_error); - -// Generate decode data needed for ObjC's GPBDecodeTextFormatName() to transform -// the input into the expected output. -class PROTOC_EXPORT TextFormatDecodeData { - public: - TextFormatDecodeData(); - ~TextFormatDecodeData(); - - TextFormatDecodeData(const TextFormatDecodeData&) = delete; - TextFormatDecodeData& operator=(const TextFormatDecodeData&) = delete; - - void AddString(int32_t key, const std::string& input_for_decode, - const std::string& desired_output); - size_t num_entries() const { return entries_.size(); } - std::string Data() const; - - static std::string DecodeDataForString(const std::string& input_for_decode, - const std::string& desired_output); - - private: - typedef std::pair DataEntry; - std::vector entries_; -}; - -// Helper for parsing simple files. -class PROTOC_EXPORT LineConsumer { - public: - LineConsumer(); - virtual ~LineConsumer(); - virtual bool ConsumeLine(const absl::string_view& line, std::string* out_error) = 0; -}; - -bool PROTOC_EXPORT ParseSimpleFile(const std::string& path, - LineConsumer* line_consumer, - std::string* out_error); - -bool PROTOC_EXPORT ParseSimpleStream(io::ZeroCopyInputStream& input_stream, - const std::string& stream_name, - LineConsumer* line_consumer, - std::string* out_error); - -// Helper class for parsing framework import mappings and generating -// import statements. -class PROTOC_EXPORT ImportWriter { - public: - ImportWriter(const std::string& generate_for_named_framework, - const std::string& named_framework_to_proto_path_mappings_path, - const std::string& runtime_import_prefix, - bool include_wkt_imports); - ~ImportWriter(); - - void AddFile(const FileDescriptor* file, const std::string& header_extension); - void Print(io::Printer* printer) const; - - static void PrintRuntimeImports(io::Printer* printer, - const std::vector& header_to_import, - const std::string& runtime_import_prefix, - bool default_cpp_symbol = false); - - private: - class ProtoFrameworkCollector : public LineConsumer { - public: - ProtoFrameworkCollector(std::map* inout_proto_file_to_framework_name) - : map_(inout_proto_file_to_framework_name) {} - - virtual bool ConsumeLine(const absl::string_view& line, std::string* out_error) override; - - private: - std::map* map_; - }; - - void ParseFrameworkMappings(); - - const std::string generate_for_named_framework_; - const std::string named_framework_to_proto_path_mappings_path_; - const std::string runtime_import_prefix_; - const bool include_wkt_imports_; - std::map proto_file_to_framework_name_; - bool need_to_parse_mapping_file_; - - std::vector protobuf_imports_; - std::vector other_framework_imports_; - std::vector other_imports_; -}; +// TODO(thomasvl) Move internal helpers in names.h back to here. Currently +// the dependencies are too interwoven to easily split up. } // namespace objectivec } // namespace compiler diff --git a/src/google/protobuf/compiler/php/BUILD.bazel b/src/google/protobuf/compiler/php/BUILD.bazel index d2540189d1d5..3f8376fae3fd 100644 --- a/src/google/protobuf/compiler/php/BUILD.bazel +++ b/src/google/protobuf/compiler/php/BUILD.bazel @@ -6,6 +6,20 @@ load("@rules_cc//cc:defs.bzl", "cc_library") load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix") load("//build_defs:cpp_opts.bzl", "COPTS") +cc_library( + name = "names", + hdrs = ["names.h"], + srcs = ["names.cc"], + copts = COPTS, + include_prefix = "google/protobuf/compiler/php", + visibility = ["//visibility:public"], + deps = [ + "//src/google/protobuf:protobuf_nowkt", + "//src/google/protobuf/compiler:code_generator", + "@com_google_absl//absl/strings", + ], +) + cc_library( name = "php", srcs = ["php_generator.cc"], @@ -17,6 +31,7 @@ cc_library( "//src/google/protobuf/compiler:__pkg__", ], deps = [ + ":names", "//src/google/protobuf:protobuf_nowkt", "//src/google/protobuf/compiler:code_generator", "@com_google_absl//absl/strings", diff --git a/src/google/protobuf/compiler/php/names.cc b/src/google/protobuf/compiler/php/names.cc new file mode 100644 index 000000000000..20e169e96f0e --- /dev/null +++ b/src/google/protobuf/compiler/php/names.cc @@ -0,0 +1,144 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "google/protobuf/compiler/php/names.h" + +#include + +#include "google/protobuf/compiler/code_generator.h" +#include "google/protobuf/compiler/plugin.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/descriptor.pb.h" + +const char* const kReservedNames[] = { + "abstract", "and", "array", "as", "break", + "callable", "case", "catch", "class", "clone", + "const", "continue", "declare", "default", "die", + "do", "echo", "else", "elseif", "empty", + "enddeclare", "endfor", "endforeach", "endif", "endswitch", + "endwhile", "eval", "exit", "extends", "final", + "finally", "fn", "for", "foreach", "function", + "global", "goto", "if", "implements", "include", + "include_once", "instanceof", "insteadof", "interface", "isset", + "list", "match", "namespace", "new", "or", + "parent", "print", "private", "protected", "public", + "readonly", "require", "require_once", "return", "self", + "static", "switch", "throw", "trait", "try", + "unset", "use", "var", "while", "xor", + "yield", "int", "float", "bool", "string", + "true", "false", "null", "void", "iterable"}; +const int kReservedNamesSize = 80; + +namespace google { +namespace protobuf { +namespace compiler { +namespace php { + +bool IsReservedName(absl::string_view name) { + std::string lower(name); + std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower); + for (int i = 0; i < kReservedNamesSize; i++) { + if (lower == kReservedNames[i]) { + return true; + } + } + return false; +} + +std::string ReservedNamePrefix(const std::string& classname, + const FileDescriptor* file) { + if (IsReservedName(classname)) { + if (file->package() == "google.protobuf") { + return "GPB"; + } else { + return "PB"; + } + } + + return ""; +} + +namespace { + +template +std::string ClassNamePrefixImpl(const std::string& classname, + const DescriptorType* desc) { + const std::string& prefix = (desc->file()->options()).php_class_prefix(); + if (!prefix.empty()) { + return prefix; + } + + return ReservedNamePrefix(classname, desc->file()); +} + +template +std::string GeneratedClassNameImpl(const DescriptorType* desc) { + std::string classname = ClassNamePrefixImpl(desc->name(), desc) + desc->name(); + const Descriptor* containing = desc->containing_type(); + while (containing != NULL) { + classname = ClassNamePrefixImpl(containing->name(), desc) + containing->name() + + '\\' + classname; + containing = containing->containing_type(); + } + return classname; +} + +std::string GeneratedClassNameImpl(const ServiceDescriptor* desc) { + std::string classname = desc->name(); + return ClassNamePrefixImpl(classname, desc) + classname; +} + +} // namespace + +std::string ClassNamePrefix(const std::string& classname, + const Descriptor* desc) { + return ClassNamePrefixImpl(classname, desc); +} +std::string ClassNamePrefix(const std::string& classname, + const EnumDescriptor* desc) { + return ClassNamePrefixImpl(classname, desc); +} + +std::string GeneratedClassName(const Descriptor* desc) { + return GeneratedClassNameImpl(desc); +} + +std::string GeneratedClassName(const EnumDescriptor* desc) { + return GeneratedClassNameImpl(desc); +} + +std::string GeneratedClassName(const ServiceDescriptor* desc) { + return GeneratedClassNameImpl(desc); +} + +} // namespace php +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/php/names.h b/src/google/protobuf/compiler/php/names.h new file mode 100644 index 000000000000..cf4f942fca60 --- /dev/null +++ b/src/google/protobuf/compiler/php/names.h @@ -0,0 +1,73 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef GOOGLE_PROTOBUF_COMPILER_PHP_NAMES_H__ +#define GOOGLE_PROTOBUF_COMPILER_PHP_NAMES_H__ + +#include "google/protobuf/descriptor.h" + +#include + +#include "absl/strings/string_view.h" +#include "google/protobuf/port_def.inc" + +namespace google { +namespace protobuf { +namespace compiler { +namespace php { + +// Whether or not a name is reserved. +PROTOC_EXPORT bool IsReservedName(absl::string_view name); + +// A prefix to stick in front of reserved names to avoid clashes. +PROTOC_EXPORT std::string ReservedNamePrefix(const std::string& classname, + const FileDescriptor* file); + +// A prefix to stick in front of all class names. +PROTOC_EXPORT std::string ClassNamePrefix(const std::string& classname, + const Descriptor* desc); +PROTOC_EXPORT std::string ClassNamePrefix(const std::string& classname, + const EnumDescriptor* desc); + +// To skip reserved keywords in php, some generated classname are prefixed. +// Other code generators may need following API to figure out the actual +// classname. +PROTOC_EXPORT std::string GeneratedClassName(const Descriptor* desc); +PROTOC_EXPORT std::string GeneratedClassName(const EnumDescriptor* desc); +PROTOC_EXPORT std::string GeneratedClassName(const ServiceDescriptor* desc); + +} // namespace php +} // namespace compiler +} // namespace protobuf +} // namespace google + +#include "google/protobuf/port_undef.inc" + +#endif // GOOGLE_PROTOBUF_COMPILER_PHP_NAMES_H__ diff --git a/src/google/protobuf/compiler/php/php_generator.cc b/src/google/protobuf/compiler/php/php_generator.cc index 7466f74e5aec..2903c3d432b5 100644 --- a/src/google/protobuf/compiler/php/php_generator.cc +++ b/src/google/protobuf/compiler/php/php_generator.cc @@ -50,29 +50,11 @@ const std::string kDescriptorMetadataFile = "GPBMetadata/Google/Protobuf/Internal/Descriptor.php"; const std::string kDescriptorDirName = "Google/Protobuf/Internal"; const std::string kDescriptorPackageName = "Google\\Protobuf\\Internal"; -const char* const kReservedNames[] = { - "abstract", "and", "array", "as", "break", - "callable", "case", "catch", "class", "clone", - "const", "continue", "declare", "default", "die", - "do", "echo", "else", "elseif", "empty", - "enddeclare", "endfor", "endforeach", "endif", "endswitch", - "endwhile", "eval", "exit", "extends", "final", - "finally", "fn", "for", "foreach", "function", - "global", "goto", "if", "implements", "include", - "include_once", "instanceof", "insteadof", "interface", "isset", - "list", "match", "namespace", "new", "or", - "parent", "print", "private", "protected", "public", - "readonly", "require", "require_once", "return", "self", - "static", "switch", "throw", "trait", "try", - "unset", "use", "var", "while", "xor", - "yield", "int", "float", "bool", "string", - "true", "false", "null", "void", "iterable"}; const char* const kValidConstantNames[] = { "int", "float", "bool", "string", "true", "false", "null", "void", "iterable", "parent", "self", "readonly" }; -const int kReservedNamesSize = 80; const int kValidConstantNamesSize = 12; const int kFieldSetter = 1; const int kFieldGetter = 2; @@ -124,31 +106,6 @@ void GenerateServiceDocComment(io::Printer* printer, void GenerateServiceMethodDocComment(io::Printer* printer, const MethodDescriptor* method); -std::string ReservedNamePrefix(const std::string& classname, - const FileDescriptor* file) { - bool is_reserved = false; - - std::string lower = classname; - std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower); - - for (int i = 0; i < kReservedNamesSize; i++) { - if (lower == kReservedNames[i]) { - is_reserved = true; - break; - } - } - - if (is_reserved) { - if (file->package() == "google.protobuf") { - return "GPB"; - } else { - return "PB"; - } - } - - return ""; -} - template std::string DescriptorFullName(const DescriptorType* desc, bool is_internal) { if (is_internal) { @@ -159,34 +116,6 @@ std::string DescriptorFullName(const DescriptorType* desc, bool is_internal) { } } -template -std::string ClassNamePrefix(const std::string& classname, - const DescriptorType* desc) { - const std::string& prefix = (desc->file()->options()).php_class_prefix(); - if (!prefix.empty()) { - return prefix; - } - - return ReservedNamePrefix(classname, desc->file()); -} - -template -std::string GeneratedClassNameImpl(const DescriptorType* desc) { - std::string classname = ClassNamePrefix(desc->name(), desc) + desc->name(); - const Descriptor* containing = desc->containing_type(); - while (containing != NULL) { - classname = ClassNamePrefix(containing->name(), desc) + containing->name() - + '\\' + classname; - containing = containing->containing_type(); - } - return classname; -} - -std::string GeneratedClassNameImpl(const ServiceDescriptor* desc) { - std::string classname = desc->name(); - return ClassNamePrefix(classname, desc) + classname; -} - template std::string LegacyGeneratedClassName(const DescriptorType* desc) { std::string classname = desc->name(); @@ -198,31 +127,13 @@ std::string LegacyGeneratedClassName(const DescriptorType* desc) { return ClassNamePrefix(classname, desc) + classname; } -std::string ClassNamePrefix(const std::string& classname) { - std::string lower = classname; - std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower); - - for (int i = 0; i < kReservedNamesSize; i++) { - if (lower == kReservedNames[i]) { - return "PB"; - } - } - - return ""; -} - std::string ConstantNamePrefix(const std::string& classname) { bool is_reserved = false; std::string lower = classname; std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower); - for (int i = 0; i < kReservedNamesSize; i++) { - if (lower == kReservedNames[i]) { - is_reserved = true; - break; - } - } + is_reserved = IsReservedName(lower); for (int i = 0; i < kValidConstantNamesSize; i++) { if (lower == kValidConstantNames[i]) { @@ -257,7 +168,7 @@ std::string RootPhpNamespace(const DescriptorType* desc, template std::string FullClassName(const DescriptorType* desc, const Options& options) { - std::string classname = GeneratedClassNameImpl(desc); + std::string classname = GeneratedClassName(desc); std::string php_namespace = RootPhpNamespace(desc, options); if (!php_namespace.empty()) { return php_namespace + "\\" + classname; @@ -283,6 +194,11 @@ std::string LegacyFullClassName(const DescriptorType* desc, return classname; } +std::string PhpNamePrefix(const std::string& classname) { + if (IsReservedName(classname)) return "PB"; + return ""; +} + std::string PhpName(const std::string& full_name, const Options& options) { if (options.is_descriptor) { return kDescriptorPackageName; @@ -296,7 +212,7 @@ std::string PhpName(const std::string& full_name, const Options& options) { segment += full_name[i] + ('A' - 'a'); cap_next_letter = false; } else if (full_name[i] == '.') { - result += ClassNamePrefix(segment) + segment + '\\'; + result += PhpNamePrefix(segment) + segment + '\\'; segment = ""; cap_next_letter = true; } else { @@ -304,7 +220,7 @@ std::string PhpName(const std::string& full_name, const Options& options) { cap_next_letter = false; } } - result += ClassNamePrefix(segment) + segment; + result += PhpNamePrefix(segment) + segment; return result; } @@ -1312,7 +1228,7 @@ void LegacyGenerateClassFile(const FileDescriptor* file, Outdent(&printer); printer.Print("}\n"); printer.Print("class_exists(^new^::class);\n", - "new", GeneratedClassNameImpl(desc)); + "new", GeneratedClassName(desc)); printer.Print("@trigger_error('^old^ is deprecated and will be removed in " "the next major release. Use ^fullname^ instead', E_USER_DEPRECATED);\n\n", "old", LegacyFullClassName(desc, options), @@ -2342,18 +2258,6 @@ void GenerateCWellKnownTypes(const std::vector& files, } // namespace -std::string GeneratedClassName(const Descriptor* desc) { - return GeneratedClassNameImpl(desc); -} - -std::string GeneratedClassName(const EnumDescriptor* desc) { - return GeneratedClassNameImpl(desc); -} - -std::string GeneratedClassName(const ServiceDescriptor* desc) { - return GeneratedClassNameImpl(desc); -} - bool Generator::Generate(const FileDescriptor* file, const std::string& parameter, GeneratorContext* generator_context, diff --git a/src/google/protobuf/compiler/php/php_generator.h b/src/google/protobuf/compiler/php/php_generator.h index 5007c71283ca..2729b4a67bb0 100644 --- a/src/google/protobuf/compiler/php/php_generator.h +++ b/src/google/protobuf/compiler/php/php_generator.h @@ -32,6 +32,7 @@ #define GOOGLE_PROTOBUF_COMPILER_PHP_GENERATOR_H__ #include "google/protobuf/compiler/code_generator.h" +#include "google/protobuf/compiler/php/names.h" #include "google/protobuf/descriptor.h" #include @@ -70,13 +71,6 @@ class PROTOC_EXPORT Generator : public CodeGenerator { std::string* error) const; }; -// To skip reserved keywords in php, some generated classname are prefixed. -// Other code generators may need following API to figure out the actual -// classname. -PROTOC_EXPORT std::string GeneratedClassName(const Descriptor* desc); -PROTOC_EXPORT std::string GeneratedClassName(const EnumDescriptor* desc); -PROTOC_EXPORT std::string GeneratedClassName(const ServiceDescriptor* desc); - inline bool IsWrapperType(const FieldDescriptor* descriptor) { return descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && descriptor->message_type()->file()->name() == "google/protobuf/wrappers.proto"; diff --git a/src/google/protobuf/compiler/python/BUILD.bazel b/src/google/protobuf/compiler/python/BUILD.bazel index fadb1767cc36..933d0631fc1e 100644 --- a/src/google/protobuf/compiler/python/BUILD.bazel +++ b/src/google/protobuf/compiler/python/BUILD.bazel @@ -23,6 +23,7 @@ cc_library( visibility = [ "//pkg:__pkg__", "//src/google/protobuf/compiler:__pkg__", + "@com_github_grpc_grpc//tools/distrib/python/grpcio_tools:__subpackages__", ], deps = [ "//src/google/protobuf:protobuf_nowkt",