diff --git a/cmake/tests.cmake b/cmake/tests.cmake index 7976546fec35f..f1331814e4e3a 100644 --- a/cmake/tests.cmake +++ b/cmake/tests.cmake @@ -38,6 +38,7 @@ foreach(proto_file ${tests_protos}) LANGUAGE cpp OUT_VAR pb_generated_files IMPORT_DIRS ${protobuf_SOURCE_DIR}/src + IMPORT_DIRS ${protobuf_SOURCE_DIR}/java/core/src/main/resources ) set(tests_proto_files ${tests_proto_files} ${pb_generated_files}) endforeach(proto_file) diff --git a/java/core/BUILD.bazel b/java/core/BUILD.bazel index 939d353b16804..f29f1febb476f 100644 --- a/java/core/BUILD.bazel +++ b/java/core/BUILD.bazel @@ -445,6 +445,7 @@ junit_tests( ":java_test_protos_java_proto", ":lite_test_protos_java_proto", ":test_util", + "//src/google/protobuf/compiler/java:test_nested_in_file_class_2024_java_proto", "@protobuf_maven//:com_google_guava_guava", "@protobuf_maven//:com_google_truth_truth", "@protobuf_maven//:junit_junit", @@ -638,6 +639,7 @@ junit_tests( ":core", ":v25_test_util_srcjar", "//compatibility:v25_test_protos_srcjar", + "//src/google/protobuf/compiler/java:test_nested_in_file_class_2024_java_proto", "@protobuf_maven//:com_google_guava_guava", "@protobuf_maven//:com_google_truth_truth", "@protobuf_maven//:junit_junit", @@ -688,6 +690,7 @@ junit_tests( ":core", ":v25_test_util_jar", "//compatibility:v25_test_protos_jar", + "//src/google/protobuf/compiler/java:test_nested_in_file_class_2024_java_proto", "@protobuf_maven//:com_google_guava_guava", "@protobuf_maven//:com_google_truth_truth", "@protobuf_maven//:junit_junit", diff --git a/java/core/src/main/resources/google/protobuf/java_features.proto b/java/core/src/main/resources/google/protobuf/java_features.proto index 0ec9ac2fd1d65..d73c2c1eb203e 100644 --- a/java/core/src/main/resources/google/protobuf/java_features.proto +++ b/java/core/src/main/resources/google/protobuf/java_features.proto @@ -83,4 +83,32 @@ message JavaFeatures { edition_defaults = { edition: EDITION_LEGACY, value: "true" }, edition_defaults = { edition: EDITION_2024, value: "false" } ]; + + enum NestInFileClass { + // Invalid default, which should never be used. + NEST_IN_FILE_CLASS_UNKNOWN = 0; + // Do not nest the generated class in the file class. + NO = 1; + // Nest the generated class in the file class. + YES = 2; + // Fall back to the `java_multiple_files` option. Users won't be able to + // set this option. + LEGACY = 3 [feature_support = { + edition_introduced: EDITION_2024 + edition_removed: EDITION_2024 + }]; + } + + optional NestInFileClass nest_in_file_class = 5 [ + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FILE, + targets = TARGET_TYPE_MESSAGE, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_SERVICE, + feature_support = { + edition_introduced: EDITION_2024, + }, + edition_defaults = { edition: EDITION_LEGACY, value: "LEGACY" }, + edition_defaults = { edition: EDITION_2024, value: "NO" } + ]; } diff --git a/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java b/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java index 920693947e14b..f5a7b67e19a85 100644 --- a/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java +++ b/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java @@ -25,6 +25,8 @@ import protobuf_unittest.OuterClassNameTest3OuterClass; import protobuf_unittest.OuterClassNameTestOuterClass; import protobuf_unittest.ServiceWithNoOuter; +import protobuf_unittest.TestNestedInFileClass2024Proto.NestedInFileClassEnum; +import protobuf_unittest.TestNestedInFileClass2024Proto.NestedInFileClassMessage; import protobuf_unittest.UnittestOptimizeFor.TestOptimizedForSize; import protobuf_unittest.UnittestOptimizeFor.TestOptionalOptimizedForSize; import protobuf_unittest.UnittestOptimizeFor.TestRequiredOptimizedForSize; @@ -41,6 +43,8 @@ import protobuf_unittest.UnittestProto.TestOneof2; import protobuf_unittest.UnittestProto.TestPackedTypes; import protobuf_unittest.UnittestProto.TestUnpackedTypes; +import protobuf_unittest.UnnestedEnum; +import protobuf_unittest.UnnestedMessage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -1973,4 +1977,25 @@ public void getAllFields_repeatedFieldsAreNotMutable() { builder.clearField(repeatedMsgField); assertThat(list).hasSize(1); } + + @Test + public void generateMultipleFilesEdition2024() throws Exception { + // Ensures that the generated code compiles. + UnnestedMessage unnested1 = + UnnestedMessage.newBuilder() + .setNestedInFileClassEnum(NestedInFileClassEnum.FOO_VALUE) + .build(); + UnnestedMessage unnested2 = + UnnestedMessage.parseFrom(unnested1.toByteString(), ExtensionRegistry.getEmptyRegistry()); + NestedInFileClassMessage nested1 = + NestedInFileClassMessage.newBuilder().setUnnestedEnum(UnnestedEnum.BAR_VALUE).build(); + NestedInFileClassMessage nested2 = + NestedInFileClassMessage.parseFrom( + nested1.toByteString(), ExtensionRegistry.getEmptyRegistry()); + + assertThat(unnested1.getNestedInFileClassEnumValue()).isEqualTo(1); + assertThat(unnested2.getNestedInFileClassEnumValue()).isEqualTo(1); + assertThat(nested1.getUnnestedEnumValue()).isEqualTo(1); + assertThat(nested2.getUnnestedEnumValue()).isEqualTo(1); + } } diff --git a/src/google/protobuf/compiler/command_line_interface_tester.cc b/src/google/protobuf/compiler/command_line_interface_tester.cc index e965d27348493..a5f5e2b00ba5c 100644 --- a/src/google/protobuf/compiler/command_line_interface_tester.cc +++ b/src/google/protobuf/compiler/command_line_interface_tester.cc @@ -175,6 +175,21 @@ void CommandLineInterfaceTester::ExpectFileContent(absl::string_view filename, file_contents); } +std::string CommandLineInterfaceTester::ReadFile(absl::string_view filename) { + std::string path = absl::StrCat(temp_directory_, "/", filename); + std::string file_contents; + ABSL_CHECK_OK(File::GetContents(path, &file_contents, true)); + return file_contents; +} + +void CommandLineInterfaceTester::ReadDescriptorSet( + absl::string_view filename, FileDescriptorSet* descriptor_set) { + std::string file_contents = ReadFile(filename); + if (!descriptor_set->ParseFromString(file_contents)) { + FAIL() << "Could not parse file contents: " << filename; + } +} + } // namespace compiler } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/compiler/command_line_interface_tester.h b/src/google/protobuf/compiler/command_line_interface_tester.h index 5f428c81933fb..7434de7005994 100644 --- a/src/google/protobuf/compiler/command_line_interface_tester.h +++ b/src/google/protobuf/compiler/command_line_interface_tester.h @@ -123,6 +123,10 @@ class CommandLineInterfaceTester : public testing::Test { void ExpectFileContent(absl::string_view filename, absl::string_view content); + std::string ReadFile(absl::string_view filename); + void ReadDescriptorSet(absl::string_view filename, + FileDescriptorSet* descriptor_set); + private: // The object we are testing. CommandLineInterface cli_; diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc index 1778672134cb8..2cbda789674f5 100644 --- a/src/google/protobuf/compiler/command_line_interface_unittest.cc +++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc @@ -186,10 +186,6 @@ class CommandLineInterfaceTest : public CommandLineInterfaceTester { #endif // _WIN32 - std::string ReadFile(absl::string_view filename); - void ReadDescriptorSet(absl::string_view filename, - FileDescriptorSet* descriptor_set); - void WriteDescriptorSet(absl::string_view filename, const FileDescriptorSet* descriptor_set); @@ -336,21 +332,6 @@ void CommandLineInterfaceTest::ExpectNullCodeGeneratorCalled( #endif // _WIN32 -std::string CommandLineInterfaceTest::ReadFile(absl::string_view filename) { - std::string path = absl::StrCat(temp_directory(), "/", filename); - std::string file_contents; - ABSL_CHECK_OK(File::GetContents(path, &file_contents, true)); - return file_contents; -} - -void CommandLineInterfaceTest::ReadDescriptorSet( - absl::string_view filename, FileDescriptorSet* descriptor_set) { - std::string file_contents = ReadFile(filename); - if (!descriptor_set->ParseFromString(file_contents)) { - FAIL() << "Could not parse file contents: " << filename; - } -} - FeatureSetDefaults CommandLineInterfaceTest::ReadEditionDefaults( absl::string_view filename) { FeatureSetDefaults defaults; @@ -2310,6 +2291,19 @@ TEST_F(CommandLineInterfaceTest, EditionDefaultsInvalidMaximumUnknown) { ExpectErrorSubstring("unknown edition \"2022\""); } +TEST_F(CommandLineInterfaceTest, JavaMultipleFilesEdition2024Invalid) { + CreateTempFile("foo.proto", + R"schema( + edition = "2024"; + option java_multiple_files = true; + message Bar {} + )schema"); + Run("protocol_compiler --proto_path=$tmpdir " + "foo.proto --test_out=$tmpdir --experimental_editions"); + ExpectErrorSubstring( + "`java_multiple_files` is not supported in editions 2024 and above"); +} + TEST_F(CommandLineInterfaceTest, DirectDependencies_Missing_EmptyList) { CreateTempFile("foo.proto", diff --git a/src/google/protobuf/compiler/java/BUILD.bazel b/src/google/protobuf/compiler/java/BUILD.bazel index b9dc0af59632b..8092b1eef5ba0 100644 --- a/src/google/protobuf/compiler/java/BUILD.bazel +++ b/src/google/protobuf/compiler/java/BUILD.bazel @@ -256,7 +256,11 @@ cc_test( ":test_file_name_cc_proto", ":test_multiple_file_no_cc_proto", ":test_multiple_file_yes_cc_proto", + "//:protobuf", + "//src/google/protobuf", + "//src/google/protobuf:port", "//src/google/protobuf/compiler:command_line_interface", + "//src/google/protobuf/compiler:command_line_interface_tester", "//src/google/protobuf/stubs:lite", "//src/google/protobuf/testing", "//src/google/protobuf/testing:file", @@ -315,6 +319,21 @@ cc_proto_library( deps = [":test_multiple_file_yes_proto"], ) +proto_library( + name = "test_nested_in_file_class_2024_proto", + testonly = 1, + srcs = ["test_nested_in_file_class_2024.proto"], + strip_import_prefix = "/src", + deps = ["//:java_features_proto"], +) + +java_proto_library( + name = "test_nested_in_file_class_2024_java_proto", + testonly = 1, + visibility = ["//java/core:__subpackages__"], + deps = [":test_nested_in_file_class_2024_proto"], +) + ################################################################################ # Distribution packaging ################################################################################ diff --git a/src/google/protobuf/compiler/java/file.cc b/src/google/protobuf/compiler/java/file.cc index 34aad86670599..f301c1d52f286 100644 --- a/src/google/protobuf/compiler/java/file.cc +++ b/src/google/protobuf/compiler/java/file.cc @@ -349,17 +349,21 @@ void FileGenerator::Generate(io::Printer* printer) { // ----------------------------------------------------------------- - if (!MultipleJavaFiles(file_, immutable_api_)) { - for (int i = 0; i < file_->enum_type_count(); i++) { + for (int i = 0; i < file_->enum_type_count(); i++) { + if (NestedInFileClass(*file_->enum_type(i), immutable_api_)) { generator_factory_->NewEnumGenerator(file_->enum_type(i)) ->Generate(printer); } - for (int i = 0; i < file_->message_type_count(); i++) { + } + for (int i = 0; i < file_->message_type_count(); i++) { + if (NestedInFileClass(*file_->message_type(i), immutable_api_)) { message_generators_[i]->GenerateInterface(printer); message_generators_[i]->Generate(printer); } - if (HasGenericServices(file_, context_->EnforceLite())) { - for (int i = 0; i < file_->service_count(); i++) { + } + if (HasGenericServices(file_, context_->EnforceLite())) { + for (int i = 0; i < file_->service_count(); i++) { + if (NestedInFileClass(*file_->service(i), immutable_api_)) { std::unique_ptr generator( generator_factory_->NewServiceGenerator(file_->service(i))); generator->Generate(printer); @@ -575,41 +579,51 @@ static void GenerateSibling( } void FileGenerator::GenerateSiblings( - const std::string& package_dir, GeneratorContext* context, - std::vector* file_list, + GeneratorContext* context, std::vector* file_list, std::vector* annotation_list) { - if (MultipleJavaFiles(file_, immutable_api_)) { - for (int i = 0; i < file_->enum_type_count(); i++) { - std::unique_ptr generator( - generator_factory_->NewEnumGenerator(file_->enum_type(i))); - GenerateSibling( - package_dir, java_package_, file_->enum_type(i), context, file_list, - options_.annotate_code, annotation_list, "", generator.get(), - options_.opensource_runtime, &EnumGenerator::Generate); - } - for (int i = 0; i < file_->message_type_count(); i++) { - if (immutable_api_) { - GenerateSibling( - package_dir, java_package_, file_->message_type(i), context, - file_list, options_.annotate_code, annotation_list, "OrBuilder", - message_generators_[i].get(), options_.opensource_runtime, - &MessageGenerator::GenerateInterface); - } + for (int i = 0; i < file_->enum_type_count(); i++) { + auto* enum_type = file_->enum_type(i); + if (NestedInFileClass(*file_->enum_type(i), immutable_api_)) continue; + std::unique_ptr generator( + generator_factory_->NewEnumGenerator(file_->enum_type(i))); + std::string java_package = + JavaPackageForType(*enum_type, immutable_api_, context_->options()); + GenerateSibling( + JavaPackageToDir(java_package), java_package, file_->enum_type(i), + context, file_list, options_.annotate_code, annotation_list, "", + generator.get(), options_.opensource_runtime, &EnumGenerator::Generate); + } + for (int i = 0; i < file_->message_type_count(); i++) { + auto* message_type = file_->message_type(i); + if (NestedInFileClass(*message_type, immutable_api_)) continue; + std::string java_package = + JavaPackageForType(*message_type, immutable_api_, context_->options()); + if (immutable_api_) { GenerateSibling( - package_dir, java_package_, file_->message_type(i), context, - file_list, options_.annotate_code, annotation_list, "", + JavaPackageToDir(java_package), java_package, message_type, context, + file_list, options_.annotate_code, annotation_list, "OrBuilder", message_generators_[i].get(), options_.opensource_runtime, - &MessageGenerator::Generate); + &MessageGenerator::GenerateInterface); } - if (HasGenericServices(file_, context_->EnforceLite())) { - for (int i = 0; i < file_->service_count(); i++) { - std::unique_ptr generator( - generator_factory_->NewServiceGenerator(file_->service(i))); - GenerateSibling( - package_dir, java_package_, file_->service(i), context, file_list, - options_.annotate_code, annotation_list, "", generator.get(), - options_.opensource_runtime, &ServiceGenerator::Generate); - } + GenerateSibling( + JavaPackageToDir(java_package), java_package, message_type, context, + file_list, options_.annotate_code, annotation_list, "", + message_generators_[i].get(), options_.opensource_runtime, + &MessageGenerator::Generate); + } + if (HasGenericServices(file_, context_->EnforceLite())) { + for (int i = 0; i < file_->service_count(); i++) { + auto* service = file_->service(i); + if (NestedInFileClass(*service, immutable_api_)) continue; + std::unique_ptr generator( + generator_factory_->NewServiceGenerator(service)); + std::string java_package = + JavaPackageForType(*service, immutable_api_, context_->options()); + GenerateSibling( + JavaPackageToDir(java_package), java_package, service, context, + file_list, options_.annotate_code, annotation_list, "", + generator.get(), options_.opensource_runtime, + &ServiceGenerator::Generate); } } } diff --git a/src/google/protobuf/compiler/java/file.h b/src/google/protobuf/compiler/java/file.h index f610befc2ca3e..c31e85413218f 100644 --- a/src/google/protobuf/compiler/java/file.h +++ b/src/google/protobuf/compiler/java/file.h @@ -61,8 +61,7 @@ class FileGenerator { // If we aren't putting everything into one file, this will write all the // files other than the outer file (i.e. one for each message, enum, and // service type). - void GenerateSiblings(const std::string& package_dir, - GeneratorContext* generator_context, + void GenerateSiblings(GeneratorContext* generator_context, std::vector* file_list, std::vector* annotation_list); diff --git a/src/google/protobuf/compiler/java/full/message.cc b/src/google/protobuf/compiler/java/full/message.cc index b2123501b9387..01caaf32fc26f 100644 --- a/src/google/protobuf/compiler/java/full/message.cc +++ b/src/google/protobuf/compiler/java/full/message.cc @@ -103,12 +103,12 @@ void ImmutableMessageGenerator::GenerateStaticVariables( if (descriptor_->containing_type() != nullptr) { vars["parent"] = UniqueFileScopeIdentifier(descriptor_->containing_type()); } - if (MultipleJavaFiles(descriptor_->file(), /* immutable = */ true)) { + if (NestedInFileClass(*descriptor_, /* immutable = */ true)) { // We can only make these package-private since the classes that use them // are in separate files. - vars["private"] = ""; - } else { vars["private"] = "private "; + } else { + vars["private"] = ""; } if (*bytecode_estimate <= kMaxStaticSize) { vars["final"] = "final "; @@ -179,12 +179,12 @@ void ImmutableMessageGenerator::GenerateFieldAccessorTable( io::Printer* printer, int* bytecode_estimate) { absl::flat_hash_map vars; vars["identifier"] = UniqueFileScopeIdentifier(descriptor_); - if (MultipleJavaFiles(descriptor_->file(), /* immutable = */ true)) { + if (NestedInFileClass(*descriptor_, /* immutable = */ true)) { // We can only make these package-private since the classes that use them // are in separate files. - vars["private"] = ""; - } else { vars["private"] = "private "; + } else { + vars["private"] = ""; } if (*bytecode_estimate <= kMaxStaticSize) { vars["final"] = "final "; diff --git a/src/google/protobuf/compiler/java/generator.cc b/src/google/protobuf/compiler/java/generator.cc index 669adb4bba52b..9817d066f35aa 100644 --- a/src/google/protobuf/compiler/java/generator.cc +++ b/src/google/protobuf/compiler/java/generator.cc @@ -142,8 +142,7 @@ bool JavaGenerator::Generate(const FileDescriptor* file, file_generator->Generate(&printer); // Generate sibling files. - file_generator->GenerateSiblings(package_dir, context, &all_files, - &all_annotations); + file_generator->GenerateSiblings(context, &all_files, &all_annotations); if (file_options.annotate_code) { std::unique_ptr info_output( diff --git a/src/google/protobuf/compiler/java/helpers.cc b/src/google/protobuf/compiler/java/helpers.cc index 8deb2dcec302a..e14a801799285 100644 --- a/src/google/protobuf/compiler/java/helpers.cc +++ b/src/google/protobuf/compiler/java/helpers.cc @@ -27,6 +27,8 @@ #include "absl/strings/str_split.h" #include "absl/strings/string_view.h" #include "absl/strings/substitute.h" +#include "google/protobuf/compiler/java/java_features.pb.h" +#include "google/protobuf/compiler/java/generator.h" #include "google/protobuf/compiler/java/name_resolver.h" #include "google/protobuf/compiler/versions.h" #include "google/protobuf/descriptor.pb.h" @@ -918,6 +920,64 @@ const FieldDescriptor* MapValueField(const FieldDescriptor* descriptor) { } +namespace { + +// Get the value of `nest_in_file_class` feature and returns whether the +// generated class should be nested in the generated proto file Java class. +template +inline bool NestInFileClass(const Descriptor& descriptor) { + // TODO b/373884685 - Clean up this check once when we have a way to query + // Java features in the C++ runtime. + if (JavaGenerator::GetEdition(*descriptor.file()) < EDITION_2024) { + return !descriptor.file()->options().java_multiple_files(); + } + auto nest_in_file_class = JavaGenerator::GetResolvedSourceFeatures(descriptor) + .GetExtension(pb::java) + .nest_in_file_class(); + ABSL_CHECK(nest_in_file_class != pb::JavaFeatures::LEGACY); + return nest_in_file_class == pb::JavaFeatures::YES; +} + +// Get the value of `nest_in_file_class` feature from the file level. +template <> +inline bool NestInFileClass(const FileDescriptor& descriptor) { + if (JavaGenerator::GetEdition(descriptor) < EDITION_2024) { + return !descriptor.options().java_multiple_files(); + } + auto nest_in_file_class = JavaGenerator::GetResolvedSourceFeatures(descriptor) + .GetExtension(pb::java) + .nest_in_file_class(); + ABSL_CHECK(nest_in_file_class != pb::JavaFeatures::LEGACY); + return nest_in_file_class == pb::JavaFeatures::YES; +} + +// Returns whether multiple Java files should be generated for the given +// descriptor, depending on different Protobuf Java API versions. +bool MultipleJavaFiles(bool multiple_files, const FileDescriptor& file, + bool immutable) { + (void)immutable; + return multiple_files; +} +} // namespace + +bool NestedInFileClass(const Descriptor& descriptor, bool immutable) { + return !MultipleJavaFiles(!NestInFileClass(descriptor), *descriptor.file(), + immutable); +} + +bool NestedInFileClass(const EnumDescriptor& descriptor, bool immutable) { + return !MultipleJavaFiles(!NestInFileClass(descriptor), *descriptor.file(), + immutable); +} + +bool NestedInFileClass(const ServiceDescriptor& descriptor, bool immutable) { + return !MultipleJavaFiles(!NestInFileClass(descriptor), *descriptor.file(), + immutable); +} + +bool NestInFileClassFileFeature(const FileDescriptor& descriptor) { + return NestInFileClass(descriptor); +} } // namespace java } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/java/helpers.h b/src/google/protobuf/compiler/java/helpers.h index ab21758a7e166..1cdd0991cf5c4 100644 --- a/src/google/protobuf/compiler/java/helpers.h +++ b/src/google/protobuf/compiler/java/helpers.h @@ -14,10 +14,7 @@ #include #include -#include -#include -#include "absl/container/flat_hash_map.h" #include "absl/strings/string_view.h" #include "google/protobuf/compiler/java/names.h" #include "google/protobuf/compiler/java/options.h" @@ -87,6 +84,17 @@ std::string FileClassName(const FileDescriptor* file, bool immutable); std::string FileJavaPackage(const FileDescriptor* file, bool immutable, Options options = {}); +// Returns the Java package name for types. +// For Immutable and Proto1, this always returns the file's Java package name. +// For Mutable API, it uses `java_multiple_files_mutable_package` if the type is +// not nested in the file's Java class. +std::string JavaPackageForType(const Descriptor& descriptor, bool immutable, + Options options = {}); +std::string JavaPackageForType(const EnumDescriptor& descriptor, bool immutable, + Options options = {}); +std::string JavaPackageForType(const ServiceDescriptor& descriptor, + bool immutable, Options options = {}); + // Returns output directory for the given package name. std::string JavaPackageToDir(std::string package_name); @@ -142,25 +150,34 @@ inline Proto1EnumRepresentation GetProto1EnumRepresentation( return Proto1EnumRepresentation::kInteger; } -// Whether we should generate multiple java files for messages. -inline bool MultipleJavaFiles(const FileDescriptor* descriptor, - bool immutable) { - (void)immutable; - return descriptor->options().java_multiple_files(); -} +// Returns true if the generated class for the type is nested in the generated +// proto file Java class. +// `immutable` should be set to true if we're generating for the immutable API. +// TODO b/372482046 - Make these functions public so that plugins can use them +// to determine whether to generate multiple files for arbitrary editions +// instead of accessing the `java_multiple_files` file option directly. +bool NestedInFileClass(const Descriptor& descriptor, bool immutable); +bool NestedInFileClass(const EnumDescriptor& descriptor, bool immutable); +bool NestedInFileClass(const ServiceDescriptor& descriptor, bool immutable); +// Returns the result of the file's `nest_in_file_class` feature value directly +// without checking the immutability. +bool NestInFileClassFileFeature(const FileDescriptor& descriptor); // Returns true if `descriptor` will be written to its own .java file. // `immutable` should be set to true if we're generating for the immutable API. +// For nested messages, this always returns false, since their generated Java +// class is always nested in their parent message's Java class i.e. they never +// have their own Java file. template bool IsOwnFile(const Descriptor* descriptor, bool immutable) { return descriptor->containing_type() == nullptr && - MultipleJavaFiles(descriptor->file(), immutable); + !NestedInFileClass(*descriptor, immutable); } template <> inline bool IsOwnFile(const ServiceDescriptor* descriptor, bool immutable) { - return MultipleJavaFiles(descriptor->file(), immutable); + return !NestedInFileClass(*descriptor, immutable); } // If `descriptor` describes an object with its own .java file, diff --git a/src/google/protobuf/compiler/java/java_features.pb.cc b/src/google/protobuf/compiler/java/java_features.pb.cc index 8942b9b296ad6..07b794c24ae8a 100644 --- a/src/google/protobuf/compiler/java/java_features.pb.cc +++ b/src/google/protobuf/compiler/java/java_features.pb.cc @@ -1,6 +1,6 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE -// source: google/protobuf/compiler/java/java_features.proto +// source: google/protobuf/java_features.proto // Protobuf C++ Version: 5.30.0-dev #include "google/protobuf/compiler/java/java_features.pb.h" @@ -31,7 +31,8 @@ inline constexpr JavaFeatures::Impl_::Impl_( : _cached_size_{0}, utf8_validation_{static_cast< ::pb::JavaFeatures_Utf8Validation >(0)}, legacy_closed_enum_{false}, - use_old_outer_classname_default_{false} {} + use_old_outer_classname_default_{false}, + nest_in_file_class_{static_cast< ::pb::JavaFeatures_NestInFileClass >(0)} {} template PROTOBUF_CONSTEXPR JavaFeatures::JavaFeatures(::_pbi::ConstantInitialized) @@ -53,11 +54,11 @@ struct JavaFeaturesDefaultTypeInternal { PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOC_EXPORT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 JavaFeaturesDefaultTypeInternal _JavaFeatures_default_instance_; } // namespace pb -static const ::_pb::EnumDescriptor* file_level_enum_descriptors_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto[1]; +static const ::_pb::EnumDescriptor* file_level_enum_descriptors_google_2fprotobuf_2fjava_5ffeatures_2eproto[2]; static constexpr const ::_pb::ServiceDescriptor** - file_level_service_descriptors_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto = nullptr; + file_level_service_descriptors_google_2fprotobuf_2fjava_5ffeatures_2eproto = nullptr; const ::uint32_t - TableStruct_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto::offsets[] ABSL_ATTRIBUTE_SECTION_VARIABLE( + TableStruct_google_2fprotobuf_2fjava_5ffeatures_2eproto::offsets[] ABSL_ATTRIBUTE_SECTION_VARIABLE( protodesc_cold) = { PROTOBUF_FIELD_OFFSET(::pb::JavaFeatures, _impl_._has_bits_), PROTOBUF_FIELD_OFFSET(::pb::JavaFeatures, _internal_metadata_), @@ -70,75 +71,91 @@ const ::uint32_t PROTOBUF_FIELD_OFFSET(::pb::JavaFeatures, _impl_.legacy_closed_enum_), PROTOBUF_FIELD_OFFSET(::pb::JavaFeatures, _impl_.utf8_validation_), PROTOBUF_FIELD_OFFSET(::pb::JavaFeatures, _impl_.use_old_outer_classname_default_), + PROTOBUF_FIELD_OFFSET(::pb::JavaFeatures, _impl_.nest_in_file_class_), 1, 0, 2, + 3, }; static const ::_pbi::MigrationSchema schemas[] ABSL_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) = { - {0, 11, -1, sizeof(::pb::JavaFeatures)}, + {0, 12, -1, sizeof(::pb::JavaFeatures)}, }; static const ::_pb::Message* const file_default_instances[] = { &::pb::_JavaFeatures_default_instance_._instance, }; -const char descriptor_table_protodef_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto[] ABSL_ATTRIBUTE_SECTION_VARIABLE( +const char descriptor_table_protodef_google_2fprotobuf_2fjava_5ffeatures_2eproto[] ABSL_ATTRIBUTE_SECTION_VARIABLE( protodesc_cold) = { - "\n1google/protobuf/compiler/java/java_fea" - "tures.proto\022\002pb\032 google/protobuf/descrip" - "tor.proto\"\314\005\n\014JavaFeatures\022\376\001\n\022legacy_cl" - "osed_enum\030\001 \001(\010B\341\001\210\001\001\230\001\004\230\001\001\242\001\t\022\004true\030\204\007\242" - "\001\n\022\005false\030\347\007\262\001\273\001\010\350\007\020\350\007\032\262\001The legacy clos" - "ed enum behavior in Java is deprecated a" - "nd is scheduled to be removed in edition" - " 2025. See http://protobuf.dev/programm" - "ing-guides/enum/#java for more informati" - "on.\022\237\002\n\017utf8_validation\030\002 \001(\0162\037.pb.JavaF" - "eatures.Utf8ValidationB\344\001\210\001\001\230\001\004\230\001\001\242\001\014\022\007D" - "EFAULT\030\204\007\262\001\310\001\010\350\007\020\351\007\032\277\001The Java-specific " - "utf8 validation feature is deprecated an" - "d is scheduled to be removed in edition " - "2025. Utf8 validation behavior should u" - "se the global cross-language utf8_valida" - "tion feature.\022Q\n\037use_old_outer_classname" - "_default\030\004 \001(\010B(\210\001\001\230\001\001\242\001\t\022\004true\030\204\007\242\001\n\022\005f" - "alse\030\351\007\262\001\006\010\351\007 \351\007\"F\n\016Utf8Validation\022\033\n\027UT" - "F8_VALIDATION_UNKNOWN\020\000\022\013\n\007DEFAULT\020\001\022\n\n\006" - "VERIFY\020\002:<\n\004java\022\033.google.protobuf.Featu" - "reSet\030\351\007 \001(\0132\020.pb.JavaFeaturesB(\n\023com.go" - "ogle.protobufB\021JavaFeaturesProto" + "\n#google/protobuf/java_features.proto\022\002p" + "b\032 google/protobuf/descriptor.proto\"\223\007\n\014" + "JavaFeatures\022\376\001\n\022legacy_closed_enum\030\001 \001(" + "\010B\341\001\210\001\001\230\001\004\230\001\001\242\001\t\022\004true\030\204\007\242\001\n\022\005false\030\347\007\262\001" + "\273\001\010\350\007\020\350\007\032\262\001The legacy closed enum behavi" + "or in Java is deprecated and is schedule" + "d to be removed in edition 2025. See ht" + "tp://protobuf.dev/programming-guides/enu" + "m/#java for more information.\022\237\002\n\017utf8_v" + "alidation\030\002 \001(\0162\037.pb.JavaFeatures.Utf8Va" + "lidationB\344\001\210\001\001\230\001\004\230\001\001\242\001\014\022\007DEFAULT\030\204\007\262\001\310\001\010" + "\350\007\020\351\007\032\277\001The Java-specific utf8 validatio" + "n feature is deprecated and is scheduled" + " to be removed in edition 2025. Utf8 va" + "lidation behavior should use the global " + "cross-language utf8_validation feature.\022" + "Q\n\037use_old_outer_classname_default\030\004 \001(\010" + "B(\210\001\001\230\001\001\242\001\t\022\004true\030\204\007\242\001\n\022\005false\030\351\007\262\001\006\010\351\007 " + "\351\007\022k\n\022nest_in_file_class\030\005 \001(\0162 .pb.Java" + "Features.NestInFileClassB-\210\001\001\230\001\001\230\001\003\230\001\006\230\001" + "\010\242\001\013\022\006LEGACY\030\204\007\242\001\007\022\002NO\030\351\007\262\001\003\010\351\007\"F\n\016Utf8V" + "alidation\022\033\n\027UTF8_VALIDATION_UNKNOWN\020\000\022\013" + "\n\007DEFAULT\020\001\022\n\n\006VERIFY\020\002\"X\n\017NestInFileCla" + "ss\022\036\n\032NEST_IN_FILE_CLASS_UNKNOWN\020\000\022\006\n\002NO" + "\020\001\022\007\n\003YES\020\002\022\024\n\006LEGACY\020\003\032\010\"\006\010\351\007 \351\007:<\n\004jav" + "a\022\033.google.protobuf.FeatureSet\030\351\007 \001(\0132\020." + "pb.JavaFeaturesB(\n\023com.google.protobufB\021" + "JavaFeaturesProto" }; -static const ::_pbi::DescriptorTable* const descriptor_table_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto_deps[1] = +static const ::_pbi::DescriptorTable* const descriptor_table_google_2fprotobuf_2fjava_5ffeatures_2eproto_deps[1] = { &::descriptor_table_google_2fprotobuf_2fdescriptor_2eproto, }; -static ::absl::once_flag descriptor_table_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto_once; -PROTOBUF_CONSTINIT const ::_pbi::DescriptorTable descriptor_table_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto = { +static ::absl::once_flag descriptor_table_google_2fprotobuf_2fjava_5ffeatures_2eproto_once; +PROTOBUF_CONSTINIT const ::_pbi::DescriptorTable descriptor_table_google_2fprotobuf_2fjava_5ffeatures_2eproto = { false, false, - 912, - descriptor_table_protodef_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto, - "google/protobuf/compiler/java/java_features.proto", - &descriptor_table_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto_once, - descriptor_table_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto_deps, + 1097, + descriptor_table_protodef_google_2fprotobuf_2fjava_5ffeatures_2eproto, + "google/protobuf/java_features.proto", + &descriptor_table_google_2fprotobuf_2fjava_5ffeatures_2eproto_once, + descriptor_table_google_2fprotobuf_2fjava_5ffeatures_2eproto_deps, 1, 1, schemas, file_default_instances, - TableStruct_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto::offsets, - file_level_enum_descriptors_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto, - file_level_service_descriptors_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto, + TableStruct_google_2fprotobuf_2fjava_5ffeatures_2eproto::offsets, + file_level_enum_descriptors_google_2fprotobuf_2fjava_5ffeatures_2eproto, + file_level_service_descriptors_google_2fprotobuf_2fjava_5ffeatures_2eproto, }; namespace pb { const ::google::protobuf::EnumDescriptor* JavaFeatures_Utf8Validation_descriptor() { - ::google::protobuf::internal::AssignDescriptors(&descriptor_table_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto); - return file_level_enum_descriptors_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto[0]; + ::google::protobuf::internal::AssignDescriptors(&descriptor_table_google_2fprotobuf_2fjava_5ffeatures_2eproto); + return file_level_enum_descriptors_google_2fprotobuf_2fjava_5ffeatures_2eproto[0]; } PROTOBUF_CONSTINIT const uint32_t JavaFeatures_Utf8Validation_internal_data_[] = { 196608u, 0u, }; bool JavaFeatures_Utf8Validation_IsValid(int value) { return 0 <= value && value <= 2; } +const ::google::protobuf::EnumDescriptor* JavaFeatures_NestInFileClass_descriptor() { + ::google::protobuf::internal::AssignDescriptors(&descriptor_table_google_2fprotobuf_2fjava_5ffeatures_2eproto); + return file_level_enum_descriptors_google_2fprotobuf_2fjava_5ffeatures_2eproto[1]; +} +PROTOBUF_CONSTINIT const uint32_t JavaFeatures_NestInFileClass_internal_data_[] = { + 262144u, 0u, }; +bool JavaFeatures_NestInFileClass_IsValid(int value) { + return 0 <= value && value <= 3; +} // =================================================================== class JavaFeatures::_Internal { @@ -179,9 +196,9 @@ inline void JavaFeatures::SharedCtor(::_pb::Arena* arena) { ::memset(reinterpret_cast(&_impl_) + offsetof(Impl_, utf8_validation_), 0, - offsetof(Impl_, use_old_outer_classname_default_) - + offsetof(Impl_, nest_in_file_class_) - offsetof(Impl_, utf8_validation_) + - sizeof(Impl_::use_old_outer_classname_default_)); + sizeof(Impl_::nest_in_file_class_)); } JavaFeatures::~JavaFeatures() { // @@protoc_insertion_point(destructor:pb.JavaFeatures) @@ -220,7 +237,7 @@ constexpr auto JavaFeatures::InternalGenerateClassData_() { false, }, &JavaFeatures::kDescriptorMethods, - &descriptor_table_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto, + &descriptor_table_google_2fprotobuf_2fjava_5ffeatures_2eproto, nullptr, // tracker }; } @@ -236,16 +253,16 @@ const ::google::protobuf::internal::ClassData* JavaFeatures::GetClassData() cons return JavaFeatures_class_data_.base(); } PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 -const ::_pbi::TcParseTable<2, 3, 1, 0, 2> JavaFeatures::_table_ = { +const ::_pbi::TcParseTable<3, 4, 2, 0, 2> JavaFeatures::_table_ = { { PROTOBUF_FIELD_OFFSET(JavaFeatures, _impl_._has_bits_), 0, // no _extensions_ - 4, 24, // max_field_number, fast_idx_mask + 5, 56, // max_field_number, fast_idx_mask offsetof(decltype(_table_), field_lookup_table), - 4294967284, // skipmap + 4294967268, // skipmap offsetof(decltype(_table_), field_entries), - 3, // num_field_entries - 1, // num_aux_entries + 4, // num_field_entries + 2, // num_aux_entries offsetof(decltype(_table_), aux_entries), JavaFeatures_class_data_.base(), nullptr, // post_loop_handler @@ -254,9 +271,7 @@ const ::_pbi::TcParseTable<2, 3, 1, 0, 2> JavaFeatures::_table_ = { ::_pbi::TcParser::GetTable<::pb::JavaFeatures>(), // to_prefetch #endif // PROTOBUF_PREFETCH_PARSE_TABLE }, {{ - // optional bool use_old_outer_classname_default = 4 [retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FILE, edition_defaults = { - {::_pbi::TcParser::SingularVarintNoZag1(), - {32, 2, 0, PROTOBUF_FIELD_OFFSET(JavaFeatures, _impl_.use_old_outer_classname_default_)}}, + {::_pbi::TcParser::MiniParse, {}}, // optional bool legacy_closed_enum = 1 [retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FIELD, targets = TARGET_TYPE_FILE, edition_defaults = { {::_pbi::TcParser::SingularVarintNoZag1(), {8, 1, 0, PROTOBUF_FIELD_OFFSET(JavaFeatures, _impl_.legacy_closed_enum_)}}, @@ -264,6 +279,14 @@ const ::_pbi::TcParseTable<2, 3, 1, 0, 2> JavaFeatures::_table_ = { {::_pbi::TcParser::FastEr0S1, {16, 0, 2, PROTOBUF_FIELD_OFFSET(JavaFeatures, _impl_.utf8_validation_)}}, {::_pbi::TcParser::MiniParse, {}}, + // optional bool use_old_outer_classname_default = 4 [retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FILE, edition_defaults = { + {::_pbi::TcParser::SingularVarintNoZag1(), + {32, 2, 0, PROTOBUF_FIELD_OFFSET(JavaFeatures, _impl_.use_old_outer_classname_default_)}}, + // optional .pb.JavaFeatures.NestInFileClass nest_in_file_class = 5 [retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FILE, targets = TARGET_TYPE_MESSAGE, targets = TARGET_TYPE_ENUM, targets = TARGET_TYPE_SERVICE, edition_defaults = { + {::_pbi::TcParser::FastEr0S1, + {40, 3, 3, PROTOBUF_FIELD_OFFSET(JavaFeatures, _impl_.nest_in_file_class_)}}, + {::_pbi::TcParser::MiniParse, {}}, + {::_pbi::TcParser::MiniParse, {}}, }}, {{ 65535, 65535 }}, {{ @@ -276,8 +299,12 @@ const ::_pbi::TcParseTable<2, 3, 1, 0, 2> JavaFeatures::_table_ = { // optional bool use_old_outer_classname_default = 4 [retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FILE, edition_defaults = { {PROTOBUF_FIELD_OFFSET(JavaFeatures, _impl_.use_old_outer_classname_default_), _Internal::kHasBitsOffset + 2, 0, (0 | ::_fl::kFcOptional | ::_fl::kBool)}, + // optional .pb.JavaFeatures.NestInFileClass nest_in_file_class = 5 [retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FILE, targets = TARGET_TYPE_MESSAGE, targets = TARGET_TYPE_ENUM, targets = TARGET_TYPE_SERVICE, edition_defaults = { + {PROTOBUF_FIELD_OFFSET(JavaFeatures, _impl_.nest_in_file_class_), _Internal::kHasBitsOffset + 3, 1, + (0 | ::_fl::kFcOptional | ::_fl::kEnumRange)}, }}, {{ {0, 3}, + {0, 4}, }}, {{ }}, }; @@ -290,10 +317,10 @@ PROTOBUF_NOINLINE void JavaFeatures::Clear() { (void) cached_has_bits; cached_has_bits = _impl_._has_bits_[0]; - if (cached_has_bits & 0x00000007u) { + if (cached_has_bits & 0x0000000fu) { ::memset(&_impl_.utf8_validation_, 0, static_cast<::size_t>( - reinterpret_cast(&_impl_.use_old_outer_classname_default_) - - reinterpret_cast(&_impl_.utf8_validation_)) + sizeof(_impl_.use_old_outer_classname_default_)); + reinterpret_cast(&_impl_.nest_in_file_class_) - + reinterpret_cast(&_impl_.utf8_validation_)) + sizeof(_impl_.nest_in_file_class_)); } _impl_._has_bits_.Clear(); _internal_metadata_.Clear<::google::protobuf::UnknownFieldSet>(); @@ -336,6 +363,13 @@ PROTOBUF_NOINLINE void JavaFeatures::Clear() { 4, this_._internal_use_old_outer_classname_default(), target); } + // optional .pb.JavaFeatures.NestInFileClass nest_in_file_class = 5 [retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FILE, targets = TARGET_TYPE_MESSAGE, targets = TARGET_TYPE_ENUM, targets = TARGET_TYPE_SERVICE, edition_defaults = { + if (cached_has_bits & 0x00000008u) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteEnumToArray( + 5, this_._internal_nest_in_file_class(), target); + } + if (ABSL_PREDICT_FALSE(this_._internal_metadata_.have_unknown_fields())) { target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( @@ -361,7 +395,7 @@ PROTOBUF_NOINLINE void JavaFeatures::Clear() { ::_pbi::Prefetch5LinesFrom7Lines(&this_); cached_has_bits = this_._impl_._has_bits_[0]; - if (cached_has_bits & 0x00000007u) { + if (cached_has_bits & 0x0000000fu) { // optional .pb.JavaFeatures.Utf8Validation utf8_validation = 2 [retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FIELD, targets = TARGET_TYPE_FILE, edition_defaults = { if (cached_has_bits & 0x00000001u) { total_size += 1 + @@ -375,6 +409,11 @@ PROTOBUF_NOINLINE void JavaFeatures::Clear() { if (cached_has_bits & 0x00000004u) { total_size += 2; } + // optional .pb.JavaFeatures.NestInFileClass nest_in_file_class = 5 [retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FILE, targets = TARGET_TYPE_MESSAGE, targets = TARGET_TYPE_ENUM, targets = TARGET_TYPE_SERVICE, edition_defaults = { + if (cached_has_bits & 0x00000008u) { + total_size += 1 + + ::_pbi::WireFormatLite::EnumSize(this_._internal_nest_in_file_class()); + } } return this_.MaybeComputeUnknownFieldsSize(total_size, &this_._impl_._cached_size_); @@ -389,7 +428,7 @@ void JavaFeatures::MergeImpl(::google::protobuf::MessageLite& to_msg, const ::go (void) cached_has_bits; cached_has_bits = from._impl_._has_bits_[0]; - if (cached_has_bits & 0x00000007u) { + if (cached_has_bits & 0x0000000fu) { if (cached_has_bits & 0x00000001u) { _this->_impl_.utf8_validation_ = from._impl_.utf8_validation_; } @@ -399,6 +438,9 @@ void JavaFeatures::MergeImpl(::google::protobuf::MessageLite& to_msg, const ::go if (cached_has_bits & 0x00000004u) { _this->_impl_.use_old_outer_classname_default_ = from._impl_.use_old_outer_classname_default_; } + if (cached_has_bits & 0x00000008u) { + _this->_impl_.nest_in_file_class_ = from._impl_.nest_in_file_class_; + } } _this->_impl_._has_bits_[0] |= cached_has_bits; _this->_internal_metadata_.MergeFrom<::google::protobuf::UnknownFieldSet>(from._internal_metadata_); @@ -417,8 +459,8 @@ void JavaFeatures::InternalSwap(JavaFeatures* PROTOBUF_RESTRICT other) { _internal_metadata_.InternalSwap(&other->_internal_metadata_); swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]); ::google::protobuf::internal::memswap< - PROTOBUF_FIELD_OFFSET(JavaFeatures, _impl_.use_old_outer_classname_default_) - + sizeof(JavaFeatures::_impl_.use_old_outer_classname_default_) + PROTOBUF_FIELD_OFFSET(JavaFeatures, _impl_.nest_in_file_class_) + + sizeof(JavaFeatures::_impl_.nest_in_file_class_) - PROTOBUF_FIELD_OFFSET(JavaFeatures, _impl_.utf8_validation_)>( reinterpret_cast(&_impl_.utf8_validation_), reinterpret_cast(&other->_impl_.utf8_validation_)); @@ -440,7 +482,7 @@ namespace protobuf { // @@protoc_insertion_point(global_scope) PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 static ::std::false_type _static_init2_ [[maybe_unused]] = - (::_pbi::AddDescriptors(&descriptor_table_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto), + (::_pbi::AddDescriptors(&descriptor_table_google_2fprotobuf_2fjava_5ffeatures_2eproto), ::_pbi::ExtensionSet::RegisterMessageExtension( &::google::protobuf::FeatureSet::default_instance(), 1001, 11, false, false, &::pb::JavaFeatures::default_instance(), diff --git a/src/google/protobuf/compiler/java/java_features.pb.h b/src/google/protobuf/compiler/java/java_features.pb.h index c54089c4b1fbc..9c2e15b1e7298 100644 --- a/src/google/protobuf/compiler/java/java_features.pb.h +++ b/src/google/protobuf/compiler/java/java_features.pb.h @@ -1,10 +1,10 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE -// source: google/protobuf/compiler/java/java_features.proto +// source: google/protobuf/java_features.proto // Protobuf C++ Version: 5.30.0-dev -#ifndef google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto_2epb_2eh -#define google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto_2epb_2eh +#ifndef google_2fprotobuf_2fjava_5ffeatures_2eproto_2epb_2eh +#define google_2fprotobuf_2fjava_5ffeatures_2eproto_2epb_2eh #include #include @@ -36,7 +36,7 @@ // Must be included last. #include "google/protobuf/port_def.inc" -#define PROTOBUF_INTERNAL_EXPORT_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto PROTOC_EXPORT +#define PROTOBUF_INTERNAL_EXPORT_google_2fprotobuf_2fjava_5ffeatures_2eproto PROTOC_EXPORT namespace google { namespace protobuf { @@ -48,14 +48,17 @@ ::absl::string_view GetAnyMessageName(); } // namespace google // Internal implementation detail -- do not use these members. -struct PROTOC_EXPORT TableStruct_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto { +struct PROTOC_EXPORT TableStruct_google_2fprotobuf_2fjava_5ffeatures_2eproto { static const ::uint32_t offsets[]; }; extern "C" { PROTOC_EXPORT extern const ::google::protobuf::internal::DescriptorTable - descriptor_table_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto; + descriptor_table_google_2fprotobuf_2fjava_5ffeatures_2eproto; } // extern "C" namespace pb { +enum JavaFeatures_NestInFileClass : int; +PROTOC_EXPORT bool JavaFeatures_NestInFileClass_IsValid(int value); +PROTOC_EXPORT extern const uint32_t JavaFeatures_NestInFileClass_internal_data_[]; enum JavaFeatures_Utf8Validation : int; PROTOC_EXPORT bool JavaFeatures_Utf8Validation_IsValid(int value); PROTOC_EXPORT extern const uint32_t JavaFeatures_Utf8Validation_internal_data_[]; @@ -67,6 +70,9 @@ PROTOC_EXPORT extern const ::google::protobuf::internal::ClassDataFull JavaFeatu namespace google { namespace protobuf { template <> +internal::EnumTraitsT<::pb::JavaFeatures_NestInFileClass_internal_data_> + internal::EnumTraitsImpl::value<::pb::JavaFeatures_NestInFileClass>; +template <> internal::EnumTraitsT<::pb::JavaFeatures_Utf8Validation_internal_data_> internal::EnumTraitsImpl::value<::pb::JavaFeatures_Utf8Validation>; } // namespace protobuf @@ -105,6 +111,39 @@ inline bool JavaFeatures_Utf8Validation_Parse(absl::string_view name, JavaFeatur return ::google::protobuf::internal::ParseNamedEnum( JavaFeatures_Utf8Validation_descriptor(), name, value); } +enum JavaFeatures_NestInFileClass : int { + JavaFeatures_NestInFileClass_NEST_IN_FILE_CLASS_UNKNOWN = 0, + JavaFeatures_NestInFileClass_NO = 1, + JavaFeatures_NestInFileClass_YES = 2, + JavaFeatures_NestInFileClass_LEGACY = 3, +}; + +PROTOC_EXPORT bool JavaFeatures_NestInFileClass_IsValid(int value); +PROTOC_EXPORT extern const uint32_t JavaFeatures_NestInFileClass_internal_data_[]; +inline constexpr JavaFeatures_NestInFileClass JavaFeatures_NestInFileClass_NestInFileClass_MIN = + static_cast(0); +inline constexpr JavaFeatures_NestInFileClass JavaFeatures_NestInFileClass_NestInFileClass_MAX = + static_cast(3); +inline constexpr int JavaFeatures_NestInFileClass_NestInFileClass_ARRAYSIZE = 3 + 1; +PROTOC_EXPORT const ::google::protobuf::EnumDescriptor* +JavaFeatures_NestInFileClass_descriptor(); +template +const std::string& JavaFeatures_NestInFileClass_Name(T value) { + static_assert(std::is_same::value || + std::is_integral::value, + "Incorrect type passed to NestInFileClass_Name()."); + return JavaFeatures_NestInFileClass_Name(static_cast(value)); +} +template <> +inline const std::string& JavaFeatures_NestInFileClass_Name(JavaFeatures_NestInFileClass value) { + return ::google::protobuf::internal::NameOfDenseEnum( + static_cast(value)); +} +inline bool JavaFeatures_NestInFileClass_Parse(absl::string_view name, JavaFeatures_NestInFileClass* value) { + return ::google::protobuf::internal::ParseNamedEnum( + JavaFeatures_NestInFileClass_descriptor(), name, value); +} // =================================================================== @@ -276,12 +315,34 @@ class PROTOC_EXPORT JavaFeatures final static inline bool Utf8Validation_Parse(absl::string_view name, Utf8Validation* value) { return JavaFeatures_Utf8Validation_Parse(name, value); } + using NestInFileClass = JavaFeatures_NestInFileClass; + static constexpr NestInFileClass NEST_IN_FILE_CLASS_UNKNOWN = JavaFeatures_NestInFileClass_NEST_IN_FILE_CLASS_UNKNOWN; + static constexpr NestInFileClass NO = JavaFeatures_NestInFileClass_NO; + static constexpr NestInFileClass YES = JavaFeatures_NestInFileClass_YES; + static constexpr NestInFileClass LEGACY = JavaFeatures_NestInFileClass_LEGACY; + static inline bool NestInFileClass_IsValid(int value) { + return JavaFeatures_NestInFileClass_IsValid(value); + } + static constexpr NestInFileClass NestInFileClass_MIN = JavaFeatures_NestInFileClass_NestInFileClass_MIN; + static constexpr NestInFileClass NestInFileClass_MAX = JavaFeatures_NestInFileClass_NestInFileClass_MAX; + static constexpr int NestInFileClass_ARRAYSIZE = JavaFeatures_NestInFileClass_NestInFileClass_ARRAYSIZE; + static inline const ::google::protobuf::EnumDescriptor* NestInFileClass_descriptor() { + return JavaFeatures_NestInFileClass_descriptor(); + } + template + static inline const std::string& NestInFileClass_Name(T value) { + return JavaFeatures_NestInFileClass_Name(value); + } + static inline bool NestInFileClass_Parse(absl::string_view name, NestInFileClass* value) { + return JavaFeatures_NestInFileClass_Parse(name, value); + } // accessors ------------------------------------------------------- enum : int { kUtf8ValidationFieldNumber = 2, kLegacyClosedEnumFieldNumber = 1, kUseOldOuterClassnameDefaultFieldNumber = 4, + kNestInFileClassFieldNumber = 5, }; // optional .pb.JavaFeatures.Utf8Validation utf8_validation = 2 [retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FIELD, targets = TARGET_TYPE_FILE, edition_defaults = { bool has_utf8_validation() const; @@ -315,13 +376,24 @@ class PROTOC_EXPORT JavaFeatures final bool _internal_use_old_outer_classname_default() const; void _internal_set_use_old_outer_classname_default(bool value); + public: + // optional .pb.JavaFeatures.NestInFileClass nest_in_file_class = 5 [retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FILE, targets = TARGET_TYPE_MESSAGE, targets = TARGET_TYPE_ENUM, targets = TARGET_TYPE_SERVICE, edition_defaults = { + bool has_nest_in_file_class() const; + void clear_nest_in_file_class() ; + ::pb::JavaFeatures_NestInFileClass nest_in_file_class() const; + void set_nest_in_file_class(::pb::JavaFeatures_NestInFileClass value); + + private: + ::pb::JavaFeatures_NestInFileClass _internal_nest_in_file_class() const; + void _internal_set_nest_in_file_class(::pb::JavaFeatures_NestInFileClass value); + public: // @@protoc_insertion_point(class_scope:pb.JavaFeatures) private: class _Internal; friend class ::google::protobuf::internal::TcParser; static const ::google::protobuf::internal::TcParseTable< - 2, 3, 1, + 3, 4, 2, 0, 2> _table_; @@ -344,10 +416,11 @@ class PROTOC_EXPORT JavaFeatures final int utf8_validation_; bool legacy_closed_enum_; bool use_old_outer_classname_default_; + int nest_in_file_class_; PROTOBUF_TSAN_DECLARE_MEMBER }; union { Impl_ _impl_; }; - friend struct ::TableStruct_google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto; + friend struct ::TableStruct_google_2fprotobuf_2fjava_5ffeatures_2eproto; }; PROTOC_EXPORT extern const ::google::protobuf::internal::ClassDataFull JavaFeatures_class_data_; @@ -459,6 +532,35 @@ inline void JavaFeatures::_internal_set_use_old_outer_classname_default(bool val _impl_.use_old_outer_classname_default_ = value; } +// optional .pb.JavaFeatures.NestInFileClass nest_in_file_class = 5 [retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FILE, targets = TARGET_TYPE_MESSAGE, targets = TARGET_TYPE_ENUM, targets = TARGET_TYPE_SERVICE, edition_defaults = { +inline bool JavaFeatures::has_nest_in_file_class() const { + bool value = (_impl_._has_bits_[0] & 0x00000008u) != 0; + return value; +} +inline void JavaFeatures::clear_nest_in_file_class() { + ::google::protobuf::internal::TSanWrite(&_impl_); + _impl_.nest_in_file_class_ = 0; + _impl_._has_bits_[0] &= ~0x00000008u; +} +inline ::pb::JavaFeatures_NestInFileClass JavaFeatures::nest_in_file_class() const { + // @@protoc_insertion_point(field_get:pb.JavaFeatures.nest_in_file_class) + return _internal_nest_in_file_class(); +} +inline void JavaFeatures::set_nest_in_file_class(::pb::JavaFeatures_NestInFileClass value) { + _internal_set_nest_in_file_class(value); + _impl_._has_bits_[0] |= 0x00000008u; + // @@protoc_insertion_point(field_set:pb.JavaFeatures.nest_in_file_class) +} +inline ::pb::JavaFeatures_NestInFileClass JavaFeatures::_internal_nest_in_file_class() const { + ::google::protobuf::internal::TSanRead(&_impl_); + return static_cast<::pb::JavaFeatures_NestInFileClass>(_impl_.nest_in_file_class_); +} +inline void JavaFeatures::_internal_set_nest_in_file_class(::pb::JavaFeatures_NestInFileClass value) { + ::google::protobuf::internal::TSanWrite(&_impl_); + assert(::pb::JavaFeatures_NestInFileClass_IsValid(value)); + _impl_.nest_in_file_class_ = value; +} + #ifdef __GNUC__ #pragma GCC diagnostic pop #endif // __GNUC__ @@ -476,6 +578,12 @@ template <> inline const EnumDescriptor* GetEnumDescriptor<::pb::JavaFeatures_Utf8Validation>() { return ::pb::JavaFeatures_Utf8Validation_descriptor(); } +template <> +struct is_proto_enum<::pb::JavaFeatures_NestInFileClass> : std::true_type {}; +template <> +inline const EnumDescriptor* GetEnumDescriptor<::pb::JavaFeatures_NestInFileClass>() { + return ::pb::JavaFeatures_NestInFileClass_descriptor(); +} } // namespace protobuf } // namespace google @@ -484,4 +592,4 @@ inline const EnumDescriptor* GetEnumDescriptor<::pb::JavaFeatures_Utf8Validation #include "google/protobuf/port_undef.inc" -#endif // google_2fprotobuf_2fcompiler_2fjava_2fjava_5ffeatures_2eproto_2epb_2eh +#endif // google_2fprotobuf_2fjava_5ffeatures_2eproto_2epb_2eh diff --git a/src/google/protobuf/compiler/java/name_resolver.cc b/src/google/protobuf/compiler/java/name_resolver.cc index 869e69a320d2d..7140983087a9f 100644 --- a/src/google/protobuf/compiler/java/name_resolver.cc +++ b/src/google/protobuf/compiler/java/name_resolver.cc @@ -236,23 +236,15 @@ std::string ClassNameResolver::GetClassName(const FileDescriptor* descriptor, return result; } -// Get the full name of a Java class by prepending the Java package name -// or outer class name. -std::string ClassNameResolver::GetClassFullName( - absl::string_view name_without_package, const FileDescriptor* file, - bool immutable, bool is_own_file) { - return GetClassFullName(name_without_package, file, immutable, is_own_file, - false); -} - -std::string ClassNameResolver::GetClassFullName( - absl::string_view name_without_package, const FileDescriptor* file, - bool immutable, bool is_own_file, bool kotlin) { +template +std::string ClassNameResolver::GetClassFullNameForType( + absl::string_view name_without_package, const Descriptor& descriptor, + bool immutable, bool nested_in_file_class, bool kotlin) { std::string result; - if (is_own_file) { - result = FileJavaPackage(file, immutable, options_); + if (nested_in_file_class) { + result = GetClassName(descriptor.file(), immutable, kotlin); } else { - result = GetClassName(file, immutable, kotlin); + result = JavaPackageForType(descriptor, immutable, options_); } if (!result.empty()) { absl::StrAppend(&result, "."); @@ -262,6 +254,28 @@ std::string ClassNameResolver::GetClassFullName( return result; } +std::string ClassNameResolver::GetClassFullName( + absl::string_view name_without_package, const Descriptor& descriptor, + bool immutable, bool nested_in_file_class) { + return GetClassFullNameForType(name_without_package, descriptor, immutable, + nested_in_file_class, + /*kotlin=*/false); +} +std::string ClassNameResolver::GetClassFullName( + absl::string_view name_without_package, const EnumDescriptor& descriptor, + bool immutable, bool nested_in_file_class) { + return GetClassFullNameForType(name_without_package, descriptor, immutable, + nested_in_file_class, + /*kotlin=*/false); +} +std::string ClassNameResolver::GetClassFullName( + absl::string_view name_without_package, const ServiceDescriptor& descriptor, + bool immutable, bool nested_in_file_class) { + return GetClassFullNameForType(name_without_package, descriptor, immutable, + nested_in_file_class, + /*kotlin=*/false); +} + std::string ClassNameResolver::GetClassName(const Descriptor* descriptor, bool immutable) { return GetClassName(descriptor, immutable, false); @@ -269,9 +283,9 @@ std::string ClassNameResolver::GetClassName(const Descriptor* descriptor, std::string ClassNameResolver::GetClassName(const Descriptor* descriptor, bool immutable, bool kotlin) { - return GetClassFullName( - ClassNameWithoutPackage(descriptor, immutable), descriptor->file(), - immutable, MultipleJavaFiles(descriptor->file(), immutable), kotlin); + return GetClassFullNameForType( + ClassNameWithoutPackage(descriptor, immutable), *descriptor, immutable, + NestedInFileClass(*descriptor, immutable), kotlin); } std::string ClassNameResolver::GetClassName(const EnumDescriptor* descriptor, @@ -281,9 +295,9 @@ std::string ClassNameResolver::GetClassName(const EnumDescriptor* descriptor, std::string ClassNameResolver::GetClassName(const EnumDescriptor* descriptor, bool immutable, bool kotlin) { - return GetClassFullName( - ClassNameWithoutPackage(descriptor, immutable), descriptor->file(), - immutable, MultipleJavaFiles(descriptor->file(), immutable), kotlin); + return GetClassFullNameForType( + ClassNameWithoutPackage(descriptor, immutable), *descriptor, immutable, + NestedInFileClass(*descriptor, immutable), kotlin); } std::string ClassNameResolver::GetClassName(const ServiceDescriptor* descriptor, @@ -293,28 +307,31 @@ std::string ClassNameResolver::GetClassName(const ServiceDescriptor* descriptor, std::string ClassNameResolver::GetClassName(const ServiceDescriptor* descriptor, bool immutable, bool kotlin) { - return GetClassFullName(ClassNameWithoutPackage(descriptor, immutable), - descriptor->file(), immutable, - IsOwnFile(descriptor, immutable), kotlin); + return GetClassFullNameForType(ClassNameWithoutPackage(descriptor, immutable), + *descriptor, immutable, + !IsOwnFile(descriptor, immutable), kotlin); } -// Get the Java Class style full name of a message. +template std::string ClassNameResolver::GetJavaClassFullName( - absl::string_view name_without_package, const FileDescriptor* file, + absl::string_view name_without_package, const Descriptor& descriptor, bool immutable) { - return GetJavaClassFullName(name_without_package, file, immutable, false); + return GetJavaClassFullName(name_without_package, descriptor, immutable, + /*kotlin =*/false); } +// Get the Java Class style full name of a type. +template std::string ClassNameResolver::GetJavaClassFullName( - absl::string_view name_without_package, const FileDescriptor* file, + absl::string_view name_without_package, const Descriptor& descriptor, bool immutable, bool kotlin) { std::string result; - if (MultipleJavaFiles(file, immutable)) { - result = FileJavaPackage(file, immutable, options_); - if (!result.empty()) result += '.'; - } else { - result = GetClassName(file, immutable, kotlin); + if (NestedInFileClass(descriptor, immutable)) { + result = GetClassName(descriptor.file(), immutable, kotlin); if (!result.empty()) result += '$'; + } else { + result = JavaPackageForType(descriptor, immutable, options_); + if (!result.empty()) result += '.'; } result += absl::StrReplaceAll(name_without_package, {{".", "$"}}); return result; @@ -341,32 +358,35 @@ std::string ClassNameResolver::GetKotlinFactoryName( std::string ClassNameResolver::GetJavaImmutableClassName( const Descriptor* descriptor) { return GetJavaClassFullName(ClassNameWithoutPackage(descriptor, true), - descriptor->file(), true); + *descriptor, true); } std::string ClassNameResolver::GetJavaImmutableClassName( const EnumDescriptor* descriptor) { return GetJavaClassFullName(ClassNameWithoutPackage(descriptor, true), - descriptor->file(), true); + *descriptor, true); } std::string ClassNameResolver::GetJavaImmutableClassName( const ServiceDescriptor* descriptor) { return GetJavaClassFullName(ClassNameWithoutPackage(descriptor, true), - descriptor->file(), true); + *descriptor, true); } std::string ClassNameResolver::GetKotlinExtensionsClassName( const Descriptor* descriptor) { - return GetClassFullName(ClassNameWithoutPackageKotlin(descriptor), - descriptor->file(), true, true, true); + return GetClassFullNameForType(ClassNameWithoutPackageKotlin(descriptor), + *descriptor, /*immutable=*/true, + /*nested_in_file_class=*/false, + /*kotlin=*/true); } std::string ClassNameResolver::GetKotlinExtensionsClassNameEscaped( const Descriptor* descriptor) { std::string name_without_package = ClassNameWithoutPackageKotlin(descriptor); - std::string full_name = GetClassFullName( - name_without_package, descriptor->file(), true, true, true); + std::string full_name = GetClassFullNameForType( + name_without_package, *descriptor, /*immutable=*/true, + /*nested_in_file_class=*/false, /*kotlin=*/true); std::string name_without_package_suffix = absl::StrCat(".", name_without_package, "Kt"); size_t package_end = full_name.rfind(name_without_package_suffix); @@ -380,19 +400,19 @@ std::string ClassNameResolver::GetKotlinExtensionsClassNameEscaped( std::string ClassNameResolver::GetJavaMutableClassName( const Descriptor* descriptor) { return GetJavaClassFullName(ClassNameWithoutPackage(descriptor, false), - descriptor->file(), false); + *descriptor, false); } std::string ClassNameResolver::GetJavaMutableClassName( const EnumDescriptor* descriptor) { return GetJavaClassFullName(ClassNameWithoutPackage(descriptor, false), - descriptor->file(), false); + *descriptor, false); } std::string ClassNameResolver::GetJavaMutableClassName( const ServiceDescriptor* descriptor) { return GetJavaClassFullName(ClassNameWithoutPackage(descriptor, false), - descriptor->file(), false); + *descriptor, false); } std::string ClassNameResolver::GetDowngradedFileClassName( diff --git a/src/google/protobuf/compiler/java/name_resolver.h b/src/google/protobuf/compiler/java/name_resolver.h index 85b80177596a2..a4cf5ecc6b943 100644 --- a/src/google/protobuf/compiler/java/name_resolver.h +++ b/src/google/protobuf/compiler/java/name_resolver.h @@ -110,24 +110,37 @@ class PROTOC_EXPORT ClassNameResolver { std::string GetDowngradedFileClassName(const FileDescriptor* file); std::string GetDowngradedClassName(const Descriptor* descriptor); - // Get the full name of a Java class by prepending the Java package name - // or outer class name. + // Get the fully qualified name of the generated type (i.e. message, enum, + // service). std::string GetClassFullName(absl::string_view name_without_package, - const FileDescriptor* file, bool immutable, - bool is_own_file); + const Descriptor& descriptor, bool immutable, + bool nested_in_file_class); std::string GetClassFullName(absl::string_view name_without_package, - const FileDescriptor* file, bool immutable, - bool is_own_file, bool kotlin); - + const EnumDescriptor& descriptor, bool immutable, + bool nested_in_file_class); + std::string GetClassFullName(absl::string_view name_without_package, + const ServiceDescriptor& descriptor, + bool immutable, bool nested_in_file_class); Options options_; private: - // Get the Java Class style full name of a message. + // Get the Java Class style full name of a type. + template std::string GetJavaClassFullName(absl::string_view name_without_package, - const FileDescriptor* file, bool immutable); + const Descriptor& descriptor, + bool immutable); + + template std::string GetJavaClassFullName(absl::string_view name_without_package, - const FileDescriptor* file, bool immutable, + const Descriptor& descriptor, bool immutable, bool kotlin); + + template + std::string GetClassFullNameForType(absl::string_view name_without_package, + const Descriptor& descriptor, + bool immutable, bool nested_in_file_class, + bool kotlin); + // Caches the result to provide better performance. absl::flat_hash_map file_immutable_outer_class_names_; diff --git a/src/google/protobuf/compiler/java/name_resolver_test.cc b/src/google/protobuf/compiler/java/name_resolver_test.cc index 27d67bcdfe269..a6eef45f84eaa 100644 --- a/src/google/protobuf/compiler/java/name_resolver_test.cc +++ b/src/google/protobuf/compiler/java/name_resolver_test.cc @@ -4,17 +4,26 @@ #include "google/protobuf/testing/file.h" #include "google/protobuf/testing/file.h" +#include "google/protobuf/descriptor.pb.h" #include #include "google/protobuf/testing/googletest.h" #include #include "absl/log/absl_check.h" #include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "google/protobuf/compiler/java/java_features.pb.h" #include "google/protobuf/compiler/command_line_interface.h" +#include "google/protobuf/compiler/command_line_interface_tester.h" #include "google/protobuf/compiler/java/generator.h" #include "google/protobuf/compiler/java/test_file_name.pb.h" #include "google/protobuf/compiler/java/test_file_name_2024.pb.h" #include "google/protobuf/compiler/java/test_multiple_file_no.pb.h" #include "google/protobuf/compiler/java/test_multiple_file_yes.pb.h" +#include "google/protobuf/descriptor.h" + +// Must be last. + +#include "google/protobuf/port_def.inc" namespace google { namespace protobuf { @@ -180,8 +189,170 @@ TEST(NameResolverTest, GetJavaClassNameMultipleFilesEnumEdition2023) { "MultipleFileNoEnum"); } +// Build protos depending on "java_features.proto" on the fly to avoid the +// redefinition error coming from the bootstrap java_features C++ proto and the +// generated ones from {cc_}proto_library(). +class NameResolverTestEdition2024 : public CommandLineInterfaceTester { + protected: + NameResolverTestEdition2024() { + // Generate built-in protos. + CreateTempFile( + "google/protobuf/descriptor.proto", + google::protobuf::DescriptorProto::descriptor()->file()->DebugString()); + CreateTempFile("third_party/java/protobuf/java_features.proto", + pb::JavaFeatures::descriptor()->file()->DebugString()); + + FileDescriptorProto descriptor_file; + google::protobuf::DescriptorProto::descriptor()->file()->CopyTo(&descriptor_file); + FileDescriptorProto java_features_file; + pb::JavaFeatures::descriptor()->file()->CopyTo(&java_features_file); + pool_.BuildFile(descriptor_file); + pool_.BuildFile(java_features_file); + } + + void RunProtocAndPopulatePool(absl::string_view command) { + RunProtoc(command); + ExpectNoErrors(); + FileDescriptorSet file_descriptor_set; + ReadDescriptorSet("descriptor_set", &file_descriptor_set); + for (const auto& file : file_descriptor_set.file()) { + pool_.BuildFile(file); + } + } + + DescriptorPool pool_; +}; + +TEST_F(NameResolverTestEdition2024, MultipleFilesService) { + CreateTempFile("foo.proto", + R"schema( + edition = "2024"; + import "third_party/java/protobuf/java_features.proto"; + package protobuf_unittest; + option java_generic_services = true; + message Dummy {} + service NestedInFileClassService { + option features.(pb.java).nest_in_file_class = YES; + rpc Method(Dummy) returns (Dummy) {} + } + service UnnestedService { + rpc Method(Dummy) returns (Dummy) {} + } + )schema"); + + RunProtocAndPopulatePool( + "protocol_compiler --proto_path=$tmpdir " + "--experimental_editions " + "--descriptor_set_out=$tmpdir/descriptor_set foo.proto"); + ClassNameResolver resolver; + auto nested_service = + pool_.FindServiceByName("protobuf_unittest.NestedInFileClassService"); + auto unnested_service = + pool_.FindServiceByName("protobuf_unittest.UnnestedService"); + + EXPECT_EQ(resolver.GetClassName( + unnested_service, + /* immutable = */ true), + PACKAGE_PREFIX "protobuf_unittest.UnnestedService"); + EXPECT_EQ(resolver.GetClassName( + nested_service, + /* immutable = */ true), + PACKAGE_PREFIX "protobuf_unittest.FooProto.NestedInFileClassService"); + EXPECT_EQ(resolver.GetJavaImmutableClassName(unnested_service), + PACKAGE_PREFIX "protobuf_unittest.UnnestedService"); + EXPECT_EQ(resolver.GetJavaImmutableClassName(nested_service), + PACKAGE_PREFIX + "protobuf_unittest.FooProto$NestedInFileClassService"); +} + +TEST_F(NameResolverTestEdition2024, MultipleFilesMessage) { + CreateTempFile("foo.proto", + R"schema( + edition = "2024"; + import "third_party/java/protobuf/java_features.proto"; + package protobuf_unittest; + message NestedInFileClassMessage { + option features.(pb.java).nest_in_file_class = YES; + int32 unused = 1; + } + message UnnestedMessage { + int32 unused = 1; + } + )schema"); + + RunProtocAndPopulatePool( + "protocol_compiler --proto_path=$tmpdir " + "--experimental_editions " + "--descriptor_set_out=$tmpdir/descriptor_set foo.proto"); + ClassNameResolver resolver; + auto nested_message = + pool_.FindMessageTypeByName("protobuf_unittest.NestedInFileClassMessage"); + auto unnested_message = + pool_.FindMessageTypeByName("protobuf_unittest.UnnestedMessage"); + + EXPECT_EQ(resolver.GetClassName( + unnested_message, + /* immutable = */ true), + PACKAGE_PREFIX "protobuf_unittest.UnnestedMessage"); + EXPECT_EQ(resolver.GetClassName( + nested_message, + /* immutable = */ true), + PACKAGE_PREFIX "protobuf_unittest.FooProto.NestedInFileClassMessage"); + EXPECT_EQ(resolver.GetJavaImmutableClassName(unnested_message), + PACKAGE_PREFIX "protobuf_unittest.UnnestedMessage"); + EXPECT_EQ(resolver.GetJavaImmutableClassName(nested_message), + PACKAGE_PREFIX + "protobuf_unittest.FooProto$NestedInFileClassMessage"); +} + +TEST_F(NameResolverTestEdition2024, MultipleFilesEnum) { + CreateTempFile("foo.proto", + R"schema( + edition = "2024"; + import "third_party/java/protobuf/java_features.proto"; + package protobuf_unittest; + enum NestedInFileClassEnum { + option features.(pb.java).nest_in_file_class = YES; + + FOO_DEFAULT = 0; + FOO_VALUE = 1; + } + + enum UnnestedEnum { + BAR_DEFAULT = 0; + BAR_VALUE = 1; + } + )schema"); + + RunProtocAndPopulatePool( + "protocol_compiler --proto_path=$tmpdir " + "--experimental_editions " + "--descriptor_set_out=$tmpdir/descriptor_set foo.proto"); + ClassNameResolver resolver; + auto nested_enum = + pool_.FindEnumTypeByName("protobuf_unittest.NestedInFileClassEnum"); + auto unnested_enum = + pool_.FindEnumTypeByName("protobuf_unittest.UnnestedEnum"); + + EXPECT_EQ(resolver.GetClassName( + unnested_enum, + /* immutable = */ true), + PACKAGE_PREFIX "protobuf_unittest.UnnestedEnum"); + EXPECT_EQ(resolver.GetClassName( + nested_enum, + /* immutable = */ true), + PACKAGE_PREFIX "protobuf_unittest.FooProto.NestedInFileClassEnum"); + EXPECT_EQ(resolver.GetJavaImmutableClassName(unnested_enum), + PACKAGE_PREFIX "protobuf_unittest.UnnestedEnum"); + EXPECT_EQ(resolver.GetJavaImmutableClassName(nested_enum), + PACKAGE_PREFIX + "protobuf_unittest.FooProto$NestedInFileClassEnum"); +} + } // namespace } // namespace java } // namespace compiler } // namespace protobuf } // namespace google + +#include "google/protobuf/port_undef.inc" diff --git a/src/google/protobuf/compiler/java/names.cc b/src/google/protobuf/compiler/java/names.cc index e1381c78ca201..5aa1a52a9bddb 100644 --- a/src/google/protobuf/compiler/java/names.cc +++ b/src/google/protobuf/compiler/java/names.cc @@ -120,27 +120,55 @@ std::string ClassName(const FileDescriptor* descriptor) { return name_resolver.GetClassName(descriptor, true); } -std::string FileJavaPackage(const FileDescriptor* file, bool immutable, - Options options) { +namespace { +std::string FileClassJavaPackage(const FileDescriptor& file, bool immutable, + Options options) { std::string result; - if (file->options().has_java_package()) { - result = file->options().java_package(); + if (file.options().has_java_package()) { + result = file.options().java_package(); } else { result = DefaultPackage(options); - if (!file->package().empty()) { + if (!file.package().empty()) { if (!result.empty()) result += '.'; - result += file->package(); + result += file.package(); } } return result; } +template +std::string JavaPackage(const Descriptor& descriptor, Options options, + bool immutable) { + const FileDescriptor* file = descriptor.file(); + + return FileClassJavaPackage(*file, immutable, options); +} +} // namespace + +std::string FileJavaPackage(const FileDescriptor* file, bool immutable, + Options options) { + return FileClassJavaPackage(*file, immutable, options); +} + std::string FileJavaPackage(const FileDescriptor* file, Options options) { return FileJavaPackage(file, true /* immutable */, options); } +std::string JavaPackageForType(const Descriptor& descriptor, bool immutable, + Options options) { + return JavaPackage(descriptor, options, immutable); +} +std::string JavaPackageForType(const EnumDescriptor& descriptor, bool immutable, + Options options) { + return JavaPackage(descriptor, options, immutable); +} +std::string JavaPackageForType(const ServiceDescriptor& descriptor, + bool immutable, Options options) { + return JavaPackage(descriptor, options, immutable); +} + std::string JavaPackageDirectory(const FileDescriptor* file) { return JavaPackageToDir(FileJavaPackage(file)); } diff --git a/src/google/protobuf/compiler/java/test_nested_in_file_class_2024.proto b/src/google/protobuf/compiler/java/test_nested_in_file_class_2024.proto new file mode 100644 index 0000000000000..9f1f83ffe8c65 --- /dev/null +++ b/src/google/protobuf/compiler/java/test_nested_in_file_class_2024.proto @@ -0,0 +1,43 @@ +edition = "2024"; + +package protobuf_unittest; + +import "google/protobuf/java_features.proto"; + +// Test generic services that are produced by the protoc builtin generators. +option cc_generic_services = true; +option java_generic_services = true; + +enum NestedInFileClassEnum { + option features.(pb.java).nest_in_file_class = YES; + + FOO_DEFAULT = 0; + FOO_VALUE = 1; +} + +enum UnnestedEnum { + BAR_DEFAULT = 0; + BAR_VALUE = 1; +} + +message NestedInFileClassMessage { + option features.(pb.java).nest_in_file_class = YES; + + UnnestedEnum unnested_enum = 1; +} + +message UnnestedMessage { + NestedInFileClassEnum nested_in_file_class_enum = 1; +} + +service NestedInFileClassService { + option features.(pb.java).nest_in_file_class = YES; + + // Test. + rpc Method(UnnestedMessage) returns (UnnestedMessage) {} +} + +service UnnestedService { + // Test. + rpc Method(UnnestedMessage) returns (UnnestedMessage) {} +} diff --git a/src/google/protobuf/descriptor.cc b/src/google/protobuf/descriptor.cc index 4ba913ba58c49..f66781d300ea9 100644 --- a/src/google/protobuf/descriptor.cc +++ b/src/google/protobuf/descriptor.cc @@ -7862,6 +7862,17 @@ void DescriptorBuilder::ValidateOptions(const FileDescriptor* file, if (file->edition() == Edition::EDITION_PROTO3) { ValidateProto3(file, proto); } + + if (file->edition() >= Edition::EDITION_2024) { + if (file->options().has_java_multiple_files()) { + AddError(file->name(), proto, DescriptorPool::ErrorCollector::OPTION_NAME, + "The file option `java_multiple_files` is not supported in " + "editions 2024 and above, which default to the feature value of" + " `nest_in_file_class = NO` (equivalent to `java_multiple_files " + "= true`). To set and use the legacy `java_multiple_files` " + "option, please set `nest_in_file_class = LEGACY`."); + } + } } void DescriptorBuilder::ValidateProto3(const FileDescriptor* file,