From 4df096f1e9f8f5f7d80bd5db560fa33492ab0a8d Mon Sep 17 00:00:00 2001 From: Protobuf Team Bot Date: Thu, 1 Dec 2022 09:00:00 -0800 Subject: [PATCH] [ObjC] Move GenerateHeader over to Printer::Emit. Update ImportWriter to have a second Emit api for the runtime files to streamline things a bit better. PiperOrigin-RevId: 492210147 --- objectivec/GPBAny.pbobjc.h | 2 +- objectivec/GPBApi.pbobjc.h | 2 +- objectivec/GPBDuration.pbobjc.h | 2 +- objectivec/GPBEmpty.pbobjc.h | 2 +- objectivec/GPBFieldMask.pbobjc.h | 2 +- objectivec/GPBSourceContext.pbobjc.h | 2 +- objectivec/GPBStruct.pbobjc.h | 2 +- objectivec/GPBTimestamp.pbobjc.h | 2 +- objectivec/GPBType.pbobjc.h | 2 +- objectivec/GPBWrappers.pbobjc.h | 2 +- .../compiler/objectivec/enum_field.cc | 2 +- .../protobuf/compiler/objectivec/file.cc | 299 ++++++++++-------- .../compiler/objectivec/import_writer.cc | 57 +++- .../compiler/objectivec/import_writer.h | 13 +- .../protobuf/compiler/objectivec/map_field.cc | 2 +- .../compiler/objectivec/message_field.cc | 4 +- 16 files changed, 222 insertions(+), 175 deletions(-) diff --git a/objectivec/GPBAny.pbobjc.h b/objectivec/GPBAny.pbobjc.h index cb2b4f76f4..bb19e5fea9 100644 --- a/objectivec/GPBAny.pbobjc.h +++ b/objectivec/GPBAny.pbobjc.h @@ -177,4 +177,4 @@ CF_EXTERN_C_END // @@protoc_insertion_point(global_scope) -// clange-format on +// clang-format on diff --git a/objectivec/GPBApi.pbobjc.h b/objectivec/GPBApi.pbobjc.h index 877a0b5345..bbae2edbf1 100644 --- a/objectivec/GPBApi.pbobjc.h +++ b/objectivec/GPBApi.pbobjc.h @@ -298,4 +298,4 @@ CF_EXTERN_C_END // @@protoc_insertion_point(global_scope) -// clange-format on +// clang-format on diff --git a/objectivec/GPBDuration.pbobjc.h b/objectivec/GPBDuration.pbobjc.h index 223d262d88..05c63d85a9 100644 --- a/objectivec/GPBDuration.pbobjc.h +++ b/objectivec/GPBDuration.pbobjc.h @@ -133,4 +133,4 @@ CF_EXTERN_C_END // @@protoc_insertion_point(global_scope) -// clange-format on +// clang-format on diff --git a/objectivec/GPBEmpty.pbobjc.h b/objectivec/GPBEmpty.pbobjc.h index d027837e31..3de240d9e2 100644 --- a/objectivec/GPBEmpty.pbobjc.h +++ b/objectivec/GPBEmpty.pbobjc.h @@ -60,4 +60,4 @@ CF_EXTERN_C_END // @@protoc_insertion_point(global_scope) -// clange-format on +// clang-format on diff --git a/objectivec/GPBFieldMask.pbobjc.h b/objectivec/GPBFieldMask.pbobjc.h index db4e76af93..3e7d349c18 100644 --- a/objectivec/GPBFieldMask.pbobjc.h +++ b/objectivec/GPBFieldMask.pbobjc.h @@ -261,4 +261,4 @@ CF_EXTERN_C_END // @@protoc_insertion_point(global_scope) -// clange-format on +// clang-format on diff --git a/objectivec/GPBSourceContext.pbobjc.h b/objectivec/GPBSourceContext.pbobjc.h index 6736828a0a..a55939ab60 100644 --- a/objectivec/GPBSourceContext.pbobjc.h +++ b/objectivec/GPBSourceContext.pbobjc.h @@ -65,4 +65,4 @@ CF_EXTERN_C_END // @@protoc_insertion_point(global_scope) -// clange-format on +// clang-format on diff --git a/objectivec/GPBStruct.pbobjc.h b/objectivec/GPBStruct.pbobjc.h index 2196639224..9bedafc237 100644 --- a/objectivec/GPBStruct.pbobjc.h +++ b/objectivec/GPBStruct.pbobjc.h @@ -192,4 +192,4 @@ CF_EXTERN_C_END // @@protoc_insertion_point(global_scope) -// clange-format on +// clang-format on diff --git a/objectivec/GPBTimestamp.pbobjc.h b/objectivec/GPBTimestamp.pbobjc.h index e0566da452..a374a0adca 100644 --- a/objectivec/GPBTimestamp.pbobjc.h +++ b/objectivec/GPBTimestamp.pbobjc.h @@ -162,4 +162,4 @@ CF_EXTERN_C_END // @@protoc_insertion_point(global_scope) -// clange-format on +// clang-format on diff --git a/objectivec/GPBType.pbobjc.h b/objectivec/GPBType.pbobjc.h index 02388b0b5a..6eb1d04db2 100644 --- a/objectivec/GPBType.pbobjc.h +++ b/objectivec/GPBType.pbobjc.h @@ -432,4 +432,4 @@ CF_EXTERN_C_END // @@protoc_insertion_point(global_scope) -// clange-format on +// clang-format on diff --git a/objectivec/GPBWrappers.pbobjc.h b/objectivec/GPBWrappers.pbobjc.h index 463bc57fd8..11e220b201 100644 --- a/objectivec/GPBWrappers.pbobjc.h +++ b/objectivec/GPBWrappers.pbobjc.h @@ -207,4 +207,4 @@ CF_EXTERN_C_END // @@protoc_insertion_point(global_scope) -// clange-format on +// clang-format on diff --git a/src/google/protobuf/compiler/objectivec/enum_field.cc b/src/google/protobuf/compiler/objectivec/enum_field.cc index e5e881a643..7e425c7bf9 100644 --- a/src/google/protobuf/compiler/objectivec/enum_field.cc +++ b/src/google/protobuf/compiler/objectivec/enum_field.cc @@ -134,7 +134,7 @@ void EnumFieldGenerator::DetermineForwardDeclarations( !IsProtobufLibraryBundledProtoFile(descriptor_->enum_type()->file())) { // Enum name is already in "storage_type". const std::string& name = variable("storage_type"); - fwd_decls->insert("GPB_ENUM_FWD_DECLARE(" + name + ")"); + fwd_decls->insert(absl::StrCat("GPB_ENUM_FWD_DECLARE(", name, ");")); } } diff --git a/src/google/protobuf/compiler/objectivec/file.cc b/src/google/protobuf/compiler/objectivec/file.cc index efe24431e9..15ec93dece 100644 --- a/src/google/protobuf/compiler/objectivec/file.cc +++ b/src/google/protobuf/compiler/objectivec/file.cc @@ -44,6 +44,7 @@ #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" #include "google/protobuf/compiler/objectivec/enum.h" #include "google/protobuf/compiler/objectivec/extension.h" #include "google/protobuf/compiler/objectivec/import_writer.h" @@ -242,155 +243,172 @@ FileGenerator::FileGenerator(const FileDescriptor* file, } void FileGenerator::GenerateHeader(io::Printer* printer) const { - std::vector headers; - // Generated files bundled with the library get minimal imports, everything - // else gets the wrapper so everything is usable. - if (is_bundled_proto_) { - headers.push_back("GPBDescriptor.h"); - headers.push_back("GPBMessage.h"); - headers.push_back("GPBRootObject.h"); - for (int i = 0; i < file_->dependency_count(); i++) { - const std::string header_name = BundledFileName(file_->dependency(i)); - headers.push_back(header_name); - } - } else { - headers.push_back("GPBProtocolBuffers.h"); - } - PrintFileRuntimePreamble(printer, headers); - - // Add some verification that the generated code matches the source the - // code is being compiled with. - // NOTE: This captures the raw numeric values at the time the generator was - // compiled, since that will be the versions for the ObjC runtime at that - // time. The constants in the generated code will then get their values at - // at compile time (so checking against the headers being used to compile). - // clang-format off - printer->Print( - "#if GOOGLE_PROTOBUF_OBJC_VERSION < $google_protobuf_objc_version$\n" - "#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.\n" - "#endif\n" - "#if $google_protobuf_objc_version$ < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION\n" - "#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.\n" - "#endif\n" - "\n", - "google_protobuf_objc_version", absl::StrCat(GOOGLE_PROTOBUF_OBJC_VERSION)); - // clang-format on - // The bundled protos (WKTs) don't use of forward declarations. bool headers_use_forward_declarations = generation_options_.headers_use_forward_declarations && !is_bundled_proto_; - { - ImportWriter import_writer( - generation_options_.generate_for_named_framework, - generation_options_.named_framework_to_proto_path_mappings_path, - generation_options_.runtime_import_prefix, - /* include_wkt_imports = */ false); - const std::string header_extension(kHeaderExtension); - if (headers_use_forward_declarations) { - // #import any headers for "public imports" in the proto file. - for (int i = 0; i < file_->public_dependency_count(); i++) { - import_writer.AddFile(file_->public_dependency(i), header_extension); - } - } else { - for (int i = 0; i < file_->dependency_count(); i++) { - import_writer.AddFile(file_->dependency(i), header_extension); - } - } - import_writer.Emit(printer); - } + ImportWriter import_writer( + generation_options_.generate_for_named_framework, + generation_options_.named_framework_to_proto_path_mappings_path, + generation_options_.runtime_import_prefix, + /* for_bundled_proto = */ is_bundled_proto_); + const std::string header_extension(kHeaderExtension); - // Note: - // deprecated-declarations suppression is only needed if some place in this - // proto file is something deprecated or if it references something from - // another file that is deprecated. - // clang-format off - printer->Print( - "// @@protoc_insertion_point(imports)\n" - "\n" - "#pragma clang diagnostic push\n" - "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n" - "\n" - "CF_EXTERN_C_BEGIN\n" - "\n"); - // clang-format on - - absl::btree_set fwd_decls; - for (const auto& generator : message_generators_) { - generator->DetermineForwardDeclarations( - &fwd_decls, - /* include_external_types = */ headers_use_forward_declarations); - } - for (const auto& fwd_decl : fwd_decls) { - printer->Print("$value$;\n", "value", fwd_decl); - } - if (fwd_decls.begin() != fwd_decls.end()) { - printer->Print("\n"); - } - - printer->Print( - "NS_ASSUME_NONNULL_BEGIN\n" - "\n"); - - // need to write out all enums first - for (const auto& generator : enum_generators_) { - generator->GenerateHeader(printer); + // Generated files bundled with the library get minimal imports, everything + // else gets the wrapper so everything is usable. + if (is_bundled_proto_) { + import_writer.AddRuntimeImport("GPBDescriptor.h"); + import_writer.AddRuntimeImport("GPBMessage.h"); + import_writer.AddRuntimeImport("GPBRootObject.h"); + } else { + import_writer.AddRuntimeImport("GPBProtocolBuffers.h"); } - // For extensions to chain together, the Root gets created even if there - // are no extensions. - printer->Print( - // clang-format off - "#pragma mark - $root_class_name$\n" - "\n" - "/**\n" - " * Exposes the extension registry for this file.\n" - " *\n" - " * The base class provides:\n" - " * @code\n" - " * + (GPBExtensionRegistry *)extensionRegistry;\n" - " * @endcode\n" - " * which is a @c GPBExtensionRegistry that includes all the extensions defined by\n" - " * this file and all files that it depends on.\n" - " **/\n" - "GPB_FINAL @interface $root_class_name$ : GPBRootObject\n" - "@end\n" - "\n", - // clang-format off - "root_class_name", root_class_name_); - - // The dynamic methods block is only needed if there are extensions that are - // file level scoped (not message scoped). The first file_->extension_count() - // of extension_generators_ are the file scoped ones. - if (file_->extension_count()) { - printer->Print( - "@interface $root_class_name$ (DynamicMethods)\n", - "root_class_name", root_class_name_); - - for (int i = 0; i < file_->extension_count(); i++) { - extension_generators_[i]->GenerateMembersHeader(printer); + if (headers_use_forward_declarations) { + // #import any headers for "public imports" in the proto file. + for (int i = 0; i < file_->public_dependency_count(); i++) { + import_writer.AddFile(file_->public_dependency(i), header_extension); + } + } else { + for (int i = 0; i < file_->dependency_count(); i++) { + import_writer.AddFile(file_->dependency(i), header_extension); } - - printer->Print("@end\n\n"); - } // file_->extension_count() - - for (const auto& generator : message_generators_) { - generator->GenerateMessageHeader(printer); } - // clang-format off - printer->Print( - "NS_ASSUME_NONNULL_END\n" - "\n" - "CF_EXTERN_C_END\n" - "\n" - "#pragma clang diagnostic pop\n" - "\n" - "// @@protoc_insertion_point(global_scope)\n" - "\n" - "// clange-format on\n"); - // clang-format on + printer->Emit( + { + // Avoid the directive within the string below being seen by the + // tool. + {"clangfmt", "clang-format"}, + {"filename", file_->name()}, + // For extensions to chain together, the Root gets created even if + // there are no extensions. + {"root_class_name", root_class_name_}, + {"runtime_imports", + [&] { + import_writer.EmitRuntimeImports( + printer, /* default_cpp_symbol = */ !is_bundled_proto_); + }}, + // Add some verification that the generated code matches the source + // the code is being compiled with. + // + // NOTE: Where used, this captures the raw numeric values at the + // time the generator was compiled, since that will be the versions + // for the ObjC runtime at that time. The constants in the + // generated code will then get their values at at compile time (so + // checking against the headers being used to compile). + {"google_protobuf_objc_version", GOOGLE_PROTOBUF_OBJC_VERSION}, + {"file_imports", [&] { import_writer.EmitFileImports(printer); }}, + {"fwd_decls", + [&] { + absl::btree_set fwd_decls; + for (const auto& generator : message_generators_) { + generator->DetermineForwardDeclarations( + &fwd_decls, + /* include_external_types = */ + headers_use_forward_declarations); + } + + if (!fwd_decls.empty()) { + printer->Emit({{"decls", absl::StrJoin(fwd_decls, "\n")}}, + R"objc( + $decls$ + + )objc"); + } + }}, + {"enums", + [&] { + for (const auto& generator : enum_generators_) { + generator->GenerateHeader(printer); + } + }}, + {"root_extensions", + [&] { + // The dynamic methods block is only needed if there are + // extensions that are file level scoped (not message scoped). + // The first file_->extension_count() of extension_generators_ + // are the file scoped ones. + if (file_->extension_count()) { + printer->Emit( + { + {"extension_methods", + [&] { + for (int i = 0; i < file_->extension_count(); i++) { + extension_generators_[i]->GenerateMembersHeader( + printer); + } + }}, + }, + R"objc( + @interface $root_class_name$ (DynamicMethods) + $extension_methods$; + @end + + )objc"); + } // file_->extension_count() + }}, + {"messages", + [&] { + for (const auto& generator : message_generators_) { + generator->GenerateMessageHeader(printer); + } + }}, + }, + R"objc( + // Generated by the protocol buffer compiler. DO NOT EDIT! + // $clangfmt$ off + // source: $filename$ + + $runtime_imports$ + + #if GOOGLE_PROTOBUF_OBJC_VERSION < $google_protobuf_objc_version$ + #error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources. + #endif + #if $google_protobuf_objc_version$ < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION + #error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources. + #endif + + $file_imports$ + // @@protoc_insertion_point(imports) + + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + + CF_EXTERN_C_BEGIN + + $fwd_decls$; + NS_ASSUME_NONNULL_BEGIN + + $enums$; + #pragma mark - $root_class_name$ + + /** + * Exposes the extension registry for this file. + * + * The base class provides: + * @code + * + (GPBExtensionRegistry *)extensionRegistry; + * @endcode + * which is a @c GPBExtensionRegistry that includes all the extensions defined by + * this file and all files that it depends on. + **/ + GPB_FINAL @interface $root_class_name$ : GPBRootObject + @end + + $root_extensions$; + $messages$; + NS_ASSUME_NONNULL_END + + CF_EXTERN_C_END + + #pragma clang diagnostic pop + + // @@protoc_insertion_point(global_scope) + + // $clangfmt$ on + )objc"); } void FileGenerator::GenerateSource(io::Printer* printer) const { @@ -455,7 +473,7 @@ void FileGenerator::GenerateSource(io::Printer* printer) const { } } - import_writer.Emit(printer); + import_writer.EmitFileImports(printer); } bool includes_oneof = false; @@ -712,7 +730,8 @@ void FileGenerator::PrintFileRuntimePreamble( } else { ImportWriter::EmitRuntimeImports(printer, headers_to_import, generation_options_.runtime_import_prefix, - true); + /* is_bundled_proto = */ false, + /* default_cpp_symbol = */ true); } printer->Print("\n"); diff --git a/src/google/protobuf/compiler/objectivec/import_writer.cc b/src/google/protobuf/compiler/objectivec/import_writer.cc index 6ddb4f6f2c..1450166050 100644 --- a/src/google/protobuf/compiler/objectivec/import_writer.cc +++ b/src/google/protobuf/compiler/objectivec/import_writer.cc @@ -118,12 +118,12 @@ bool ProtoFrameworkCollector::ConsumeLine(absl::string_view line, ImportWriter::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) + const std::string& runtime_import_prefix, bool for_bundled_proto) : generate_for_named_framework_(generate_for_named_framework), named_framework_to_proto_path_mappings_path_( named_framework_to_proto_path_mappings_path), runtime_import_prefix_(runtime_import_prefix), - include_wkt_imports_(include_wkt_imports), + for_bundled_proto_(for_bundled_proto), need_to_parse_mapping_file_(true) {} void ImportWriter::AddFile(const FileDescriptor* file, @@ -132,7 +132,7 @@ void ImportWriter::AddFile(const FileDescriptor* file, // The imports of the WKTs are only needed within the library itself, // in other cases, they get skipped because the generated code already // import GPBProtocolBuffers.h and hence proves them. - if (include_wkt_imports_) { + if (for_bundled_proto_) { const std::string header_name = "GPB" + FilePathBasename(file) + header_extension; protobuf_imports_.push_back(header_name); @@ -162,15 +162,13 @@ void ImportWriter::AddFile(const FileDescriptor* file, other_imports_.push_back(FilePath(file) + header_extension); } -void ImportWriter::Emit(io::Printer* p) const { +void ImportWriter::AddRuntimeImport(const std::string& header_name) { + protobuf_imports_.push_back(header_name); +} + +void ImportWriter::EmitFileImports(io::Printer* p) const { p->Emit( { - {"runtime_import", - [&] { - if (!protobuf_imports_.empty()) { - EmitRuntimeImports(p, protobuf_imports_, runtime_import_prefix_); - } - }}, {"other_framework_imports", [&] { for (const auto& header : other_framework_imports_) { @@ -191,15 +189,21 @@ void ImportWriter::Emit(io::Printer* p) const { }}, }, R"objc( - $runtime_import$; $other_framework_imports$; $other_imports$; )objc"); } +void ImportWriter::EmitRuntimeImports(io::Printer* p, + bool default_cpp_symbol) const { + EmitRuntimeImports(p, protobuf_imports_, runtime_import_prefix_, + for_bundled_proto_, default_cpp_symbol); +} + void ImportWriter::EmitRuntimeImports( - io::Printer* p, const std::vector& header_to_import, - const std::string& runtime_import_prefix, bool default_cpp_symbol) { + io::Printer* p, const std::vector& headers_to_import, + const std::string& runtime_import_prefix, bool is_bundled_proto, + bool default_cpp_symbol) { // Given an override, use that. if (!runtime_import_prefix.empty()) { p->Emit( @@ -207,7 +211,7 @@ void ImportWriter::EmitRuntimeImports( {"import_prefix", runtime_import_prefix}, {"imports", [&] { - for (const auto& header : header_to_import) { + for (const auto& header : headers_to_import) { p->Emit({{"header", header}}, R"objc( #import "$import_prefix$/$header$" @@ -222,6 +226,27 @@ void ImportWriter::EmitRuntimeImports( return; } + // If bundled, no need to do the framework support below. + if (is_bundled_proto) { + GOOGLE_CHECK(!default_cpp_symbol); + p->Emit( + { + {"imports", + [&] { + for (const auto& header : headers_to_import) { + p->Emit({{"header", header}}, + R"objc( + #import "$header$" + )objc"); + } + }}, + }, + R"objc( + $imports$; + )objc"); + return; + } + auto v = p->WithVars({ {"cpp_symbol", ProtobufFrameworkImportSymbol(ProtobufLibraryFrameworkName)}, @@ -244,7 +269,7 @@ void ImportWriter::EmitRuntimeImports( {"framework_name", ProtobufLibraryFrameworkName}, {"framework_imports", [&] { - for (const auto& header : header_to_import) { + for (const auto& header : headers_to_import) { p->Emit({{"header", header}}, R"objc( #import <$framework_name$/$header$> @@ -253,7 +278,7 @@ void ImportWriter::EmitRuntimeImports( }}, {"raw_imports", [&] { - for (const auto& header : header_to_import) { + for (const auto& header : headers_to_import) { p->Emit({{"header", header}}, R"objc( #import "$header$" diff --git a/src/google/protobuf/compiler/objectivec/import_writer.h b/src/google/protobuf/compiler/objectivec/import_writer.h index f1134e1307..2fec890d9a 100644 --- a/src/google/protobuf/compiler/objectivec/import_writer.h +++ b/src/google/protobuf/compiler/objectivec/import_writer.h @@ -50,15 +50,18 @@ class ImportWriter { 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); + bool for_bundled_proto); ~ImportWriter() = default; void AddFile(const FileDescriptor* file, const std::string& header_extension); - void Emit(io::Printer* p) const; + void AddRuntimeImport(const std::string& header_name); + + void EmitFileImports(io::Printer* p) const; + void EmitRuntimeImports(io::Printer* p, bool default_cpp_symbol) const; static void EmitRuntimeImports( - io::Printer* p, const std::vector& header_to_import, - const std::string& runtime_import_prefix, + io::Printer* p, const std::vector& headers_to_import, + const std::string& runtime_import_prefix, bool is_bundled_proto = false, bool default_cpp_symbol = false); private: @@ -67,8 +70,8 @@ class ImportWriter { 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_; absl::flat_hash_map proto_file_to_framework_name_; + bool for_bundled_proto_; bool need_to_parse_mapping_file_; std::vector protobuf_imports_; diff --git a/src/google/protobuf/compiler/objectivec/map_field.cc b/src/google/protobuf/compiler/objectivec/map_field.cc index 5285cde946..a9c73fd654 100644 --- a/src/google/protobuf/compiler/objectivec/map_field.cc +++ b/src/google/protobuf/compiler/objectivec/map_field.cc @@ -187,7 +187,7 @@ void MapFieldGenerator::DetermineForwardDeclarations( descriptor_->file() == value_msg_descriptor->file()) { const std::string& value_storage_type = value_field_generator_->variable("storage_type"); - fwd_decls->insert("@class " + value_storage_type); + fwd_decls->insert(absl::StrCat("@class ", value_storage_type, ";")); } } diff --git a/src/google/protobuf/compiler/objectivec/message_field.cc b/src/google/protobuf/compiler/objectivec/message_field.cc index c4e17d753a..8da2f82bab 100644 --- a/src/google/protobuf/compiler/objectivec/message_field.cc +++ b/src/google/protobuf/compiler/objectivec/message_field.cc @@ -77,7 +77,7 @@ void MessageFieldGenerator::DetermineForwardDeclarations( descriptor_->message_type()->file())) || descriptor_->file() == descriptor_->message_type()->file()) { // Class name is already in "storage_type". - fwd_decls->insert("@class " + variable("storage_type")); + fwd_decls->insert(absl::StrCat("@class ", variable("storage_type"), ";")); } } @@ -107,7 +107,7 @@ void RepeatedMessageFieldGenerator::DetermineForwardDeclarations( descriptor_->message_type()->file())) || descriptor_->file() == descriptor_->message_type()->file()) { // Class name is already in "storage_type". - fwd_decls->insert("@class " + variable("storage_type")); + fwd_decls->insert(absl::StrCat("@class ", variable("storage_type"), ";")); } }