• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2023 Google LLC.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 #include "google/protobuf/compiler/hpb/gen_accessors.h"
9 
10 #include <string>
11 
12 #include "absl/container/flat_hash_set.h"
13 #include "absl/strings/match.h"
14 #include "absl/strings/str_cat.h"
15 #include "absl/strings/string_view.h"
16 #include "google/protobuf/compiler/hpb/gen_repeated_fields.h"
17 #include "google/protobuf/compiler/hpb/gen_utils.h"
18 #include "google/protobuf/compiler/hpb/keywords.h"
19 #include "google/protobuf/compiler/hpb/names.h"
20 #include "google/protobuf/compiler/hpb/output.h"
21 #include "google/protobuf/descriptor.h"
22 #include "upb_generator/c/names.h"
23 #include "upb_generator/minitable/names.h"
24 
25 namespace google::protobuf::hpb_generator {
26 
27 namespace protobuf = ::proto2;
28 
29 using NameToFieldDescriptorMap =
30     absl::flat_hash_map<absl::string_view, const protobuf::FieldDescriptor*>;
31 
32 void WriteFieldAccessorHazzer(const protobuf::Descriptor* desc,
33                               const protobuf::FieldDescriptor* field,
34                               absl::string_view resolved_field_name,
35                               absl::string_view resolved_upbc_name,
36                               Output& output);
37 void WriteFieldAccessorClear(const protobuf::Descriptor* desc,
38                              const protobuf::FieldDescriptor* field,
39                              absl::string_view resolved_field_name,
40                              absl::string_view resolved_upbc_name,
41                              Output& output);
42 void WriteMapFieldAccessors(const protobuf::Descriptor* desc,
43                             const protobuf::FieldDescriptor* field,
44                             absl::string_view resolved_field_name,
45                             absl::string_view resolved_upbc_name,
46                             Output& output);
47 
48 void WriteMapAccessorDefinitions(const protobuf::Descriptor* message,
49                                  const protobuf::FieldDescriptor* field,
50                                  absl::string_view resolved_field_name,
51                                  absl::string_view class_name, Output& output);
52 
53 // Returns C++ class member name by resolving naming conflicts across
54 // proto field names (such as clear_ prefixes) and keyword collisions.
55 //
56 // The Upb C generator prefixes all accessors with package and class names
57 // avoiding collisions. Therefore we need to use raw field names when calling
58 // into C accessors but need to fully resolve conflicts for C++ class members.
59 std::string ResolveFieldName(const protobuf::FieldDescriptor* field,
60                              const NameToFieldDescriptorMap& field_names);
61 
CreateNameMangler(const protobuf::Descriptor * message)62 upb::generator::NameMangler CreateNameMangler(
63     const protobuf::Descriptor* message) {
64   return upb::generator::NameMangler(upb::generator::GetCppFields(message));
65 }
66 
CreateFieldNameMap(const protobuf::Descriptor * message)67 NameToFieldDescriptorMap CreateFieldNameMap(
68     const protobuf::Descriptor* message) {
69   NameToFieldDescriptorMap field_names;
70   for (int i = 0; i < message->field_count(); i++) {
71     const protobuf::FieldDescriptor* field = message->field(i);
72     field_names.emplace(field->name(), field);
73   }
74   return field_names;
75 }
76 
WriteFieldAccessorsInHeader(const protobuf::Descriptor * desc,Output & output)77 void WriteFieldAccessorsInHeader(const protobuf::Descriptor* desc,
78                                  Output& output) {
79   // Generate const methods.
80   OutputIndenter i(output);
81 
82   auto field_names = CreateFieldNameMap(desc);
83   auto mangler = CreateNameMangler(desc);
84 
85   for (const auto* field : FieldNumberOrder(desc)) {
86     std::string resolved_field_name = ResolveFieldName(field, field_names);
87     std::string resolved_upbc_name = mangler.ResolveFieldName(field->name());
88     WriteFieldAccessorHazzer(desc, field, resolved_field_name,
89                              resolved_upbc_name, output);
90     WriteFieldAccessorClear(desc, field, resolved_field_name,
91                             resolved_upbc_name, output);
92 
93     if (field->is_map()) {
94       WriteMapFieldAccessors(desc, field, resolved_field_name,
95                              resolved_upbc_name, output);
96     } else if (desc->options().map_entry()) {
97       // TODO Implement map entry
98     } else if (field->is_repeated()) {
99       WriteRepeatedFieldsInMessageHeader(desc, field, resolved_field_name,
100                                          resolved_upbc_name, output);
101     } else {
102       // non-repeated.
103       if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
104         output(R"cc(
105                  $0 $1() const;
106                  void set_$1($0 value);
107                )cc",
108                CppConstType(field), resolved_field_name);
109       } else if (field->cpp_type() ==
110                  protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
111         output(R"cc(
112                  $1 $2() const;
113                  $0 mutable_$2();
114                )cc",
115                MessagePtrConstType(field, /* const */ false),
116                MessagePtrConstType(field, /* const */ true),
117                resolved_field_name);
118       } else {
119         output(
120             R"cc(
121               inline $0 $1() const { return $2_$3(msg_); }
122               inline void set_$1($0 value) { return $2_set_$3(msg_, value); }
123             )cc",
124             CppConstType(field), resolved_field_name, MessageName(desc),
125             resolved_upbc_name);
126       }
127     }
128   }
129 }
130 
WriteFieldAccessorHazzer(const protobuf::Descriptor * desc,const protobuf::FieldDescriptor * field,const absl::string_view resolved_field_name,const absl::string_view resolved_upbc_name,Output & output)131 void WriteFieldAccessorHazzer(const protobuf::Descriptor* desc,
132                               const protobuf::FieldDescriptor* field,
133                               const absl::string_view resolved_field_name,
134                               const absl::string_view resolved_upbc_name,
135                               Output& output) {
136   // Generate hazzer (if any).
137   if (field->has_presence()) {
138     // Has presence.
139     output("inline bool has_$0() const { return $1_has_$2(msg_); }\n",
140            resolved_field_name, MessageName(desc), resolved_upbc_name);
141   }
142 }
143 
WriteFieldAccessorClear(const protobuf::Descriptor * desc,const protobuf::FieldDescriptor * field,const absl::string_view resolved_field_name,const absl::string_view resolved_upbc_name,Output & output)144 void WriteFieldAccessorClear(const protobuf::Descriptor* desc,
145                              const protobuf::FieldDescriptor* field,
146                              const absl::string_view resolved_field_name,
147                              const absl::string_view resolved_upbc_name,
148                              Output& output) {
149   if (field->has_presence()) {
150     output("void clear_$0() { $2_clear_$1(msg_); }\n", resolved_field_name,
151            resolved_upbc_name, MessageName(desc));
152   }
153 }
154 
WriteMapFieldAccessors(const protobuf::Descriptor * desc,const protobuf::FieldDescriptor * field,const absl::string_view resolved_field_name,const absl::string_view resolved_upbc_name,Output & output)155 void WriteMapFieldAccessors(const protobuf::Descriptor* desc,
156                             const protobuf::FieldDescriptor* field,
157                             const absl::string_view resolved_field_name,
158                             const absl::string_view resolved_upbc_name,
159                             Output& output) {
160   const protobuf::Descriptor* entry = field->message_type();
161   const protobuf::FieldDescriptor* key = entry->FindFieldByNumber(1);
162   const protobuf::FieldDescriptor* val = entry->FindFieldByNumber(2);
163   output(
164       R"cc(
165         inline size_t $0_size() const { return $1_$3_size(msg_); }
166         inline void clear_$0() { $1_clear_$3(msg_); }
167         void delete_$0($2 key);
168       )cc",
169       resolved_field_name, MessageName(desc), CppConstType(key),
170       resolved_upbc_name);
171 
172   if (val->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
173     output(
174         R"cc(
175           bool set_$0($1 key, $3 value);
176           bool set_$0($1 key, $4 value);
177           absl::StatusOr<$3> get_$0($1 key);
178         )cc",
179         resolved_field_name, CppConstType(key), CppConstType(val),
180         MessagePtrConstType(val, /* is_const */ true),
181         MessagePtrConstType(val, /* is_const */ false));
182   } else {
183     output(
184         R"cc(
185           bool set_$0($1 key, $2 value);
186           absl::StatusOr<$2> get_$0($1 key);
187         )cc",
188         resolved_field_name, CppConstType(key), CppConstType(val));
189   }
190 }
191 
WriteAccessorsInSource(const protobuf::Descriptor * desc,Output & output)192 void WriteAccessorsInSource(const protobuf::Descriptor* desc, Output& output) {
193   std::string class_name = ClassName(desc);
194   absl::StrAppend(&class_name, "Access");
195   output("namespace internal {\n");
196   const char arena_expression[] = "arena_";
197   auto field_names = CreateFieldNameMap(desc);
198   auto mangler = CreateNameMangler(desc);
199 
200   // Generate const methods.
201   OutputIndenter i(output);
202   for (const auto* field : FieldNumberOrder(desc)) {
203     std::string resolved_field_name = ResolveFieldName(field, field_names);
204     std::string resolved_upbc_name = mangler.ResolveFieldName(field->name());
205     if (field->is_map()) {
206       WriteMapAccessorDefinitions(desc, field, resolved_field_name, class_name,
207                                   output);
208     } else if (desc->options().map_entry()) {
209       // TODO Implement map entry
210     } else if (field->is_repeated()) {
211       if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
212         WriteRepeatedMessageAccessor(desc, field, resolved_field_name,
213                                      class_name, output);
214       } else if (field->cpp_type() ==
215                  protobuf::FieldDescriptor::CPPTYPE_STRING) {
216         WriteRepeatedStringAccessor(desc, field, resolved_field_name,
217                                     class_name, output);
218       } else {
219         WriteRepeatedScalarAccessor(desc, field, resolved_field_name,
220                                     class_name, output);
221       }
222     } else {
223       // non-repeated field.
224       if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
225         output(
226             R"cc(
227               $1 $0::$2() const {
228                 return hpb::interop::upb::FromUpbStringView($3_$4(msg_));
229               }
230             )cc",
231             class_name, CppConstType(field), resolved_field_name,
232             MessageName(desc), resolved_upbc_name);
233         // Set string.
234         output(
235             R"cc(
236               void $0::set_$2($1 value) {
237                 $4_set_$3(msg_, hpb::interop::upb::CopyToUpbStringView(value, $5));
238               }
239             )cc",
240             class_name, CppConstType(field), resolved_field_name,
241             resolved_upbc_name, MessageName(desc), arena_expression);
242       } else if (field->cpp_type() ==
243                  protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
244         output(
245             R"cc(
246               $1 $0::$2() const {
247                 if (!has_$2()) {
248                   return $4::default_instance();
249                 }
250                 return ::hpb::interop::upb::MakeCHandle<$4>(
251                     (upb_Message*)($3_$5(msg_)), arena_);
252               }
253             )cc",
254             class_name, MessagePtrConstType(field, /* is_const */ true),
255             resolved_field_name, MessageName(desc),
256             MessageBaseType(field, /* maybe_const */ false),
257             resolved_upbc_name);
258 
259         output(
260             R"cc(
261               $1 $0::mutable_$2() {
262                 return hpb::interop::upb::MakeHandle<$4>(
263                     (upb_Message*)($3_mutable_$5(msg_, $6)), $6);
264               }
265             )cc",
266             class_name, MessagePtrConstType(field, /* is_const */ false),
267             resolved_field_name, MessageName(desc),
268             MessageBaseType(field, /* maybe_const */ false), resolved_upbc_name,
269             arena_expression);
270       }
271     }
272   }
273   output("\n");
274   output("}  // namespace internal\n\n");
275 }
276 
WriteMapAccessorDefinitions(const protobuf::Descriptor * message,const protobuf::FieldDescriptor * field,const absl::string_view resolved_field_name,const absl::string_view class_name,Output & output)277 void WriteMapAccessorDefinitions(const protobuf::Descriptor* message,
278                                  const protobuf::FieldDescriptor* field,
279                                  const absl::string_view resolved_field_name,
280                                  const absl::string_view class_name,
281                                  Output& output) {
282   const protobuf::Descriptor* entry = field->message_type();
283   const protobuf::FieldDescriptor* key = entry->FindFieldByNumber(1);
284   const protobuf::FieldDescriptor* val = entry->FindFieldByNumber(2);
285   absl::string_view upbc_name = field->name();
286   absl::string_view converted_key_name = "key";
287   absl::string_view optional_conversion_code = "";
288 
289   if (key->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
290     // Insert conversion from absl::string_view to upb_StringView.
291     // Creates upb_StringView on stack to prevent allocation.
292     converted_key_name = "upb_key";
293     optional_conversion_code =
294         "upb_StringView upb_key = {key.data(), key.size()};\n";
295   }
296   if (val->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
297     output(
298         R"cc(
299           bool $0::set_$1($2 key, $3 value) {
300             upb_Message* clone = upb_Message_DeepClone(
301                 ::hpb::internal::PrivateAccess::GetInternalMsg(value), &$9,
302                 arena_);
303             $6return $4_$8_set(msg_, $7, ($5*)clone, arena_);
304           }
305         )cc",
306         class_name, resolved_field_name, CppConstType(key),
307         MessagePtrConstType(val, /* is_const */ true), MessageName(message),
308         MessageName(val->message_type()), optional_conversion_code,
309         converted_key_name, upbc_name,
310         ::upb::generator::MiniTableMessageVarName(
311             val->message_type()->full_name()));
312     output(
313         R"cc(
314           bool $0::set_$1($2 key, $3 value) {
315             upb_Message* clone = upb_Message_DeepClone(
316                 ::hpb::internal::PrivateAccess::GetInternalMsg(value), &$9,
317                 arena_);
318             $6return $4_$8_set(msg_, $7, ($5*)clone, arena_);
319           }
320         )cc",
321         class_name, resolved_field_name, CppConstType(key),
322         MessagePtrConstType(val, /* is_const */ false), MessageName(message),
323         MessageName(val->message_type()), optional_conversion_code,
324         converted_key_name, upbc_name,
325         ::upb::generator::MiniTableMessageVarName(
326             val->message_type()->full_name()));
327     output(
328         R"cc(
329           absl::StatusOr<$3> $0::get_$1($2 key) {
330             $5* msg_value;
331             $7bool success = $4_$9_get(msg_, $8, &msg_value);
332             if (success) {
333               return ::hpb::interop::upb::MakeCHandle<$6>(UPB_UPCAST(msg_value), arena_);
334             }
335             return absl::NotFoundError("");
336           }
337         )cc",
338         class_name, resolved_field_name, CppConstType(key),
339         MessagePtrConstType(val, /* is_const */ true), MessageName(message),
340         MessageName(val->message_type()),
341         QualifiedClassName(val->message_type()), optional_conversion_code,
342         converted_key_name, upbc_name);
343     output(
344         R"cc(
345           void $0::delete_$1($2 key) { $6$4_$8_delete(msg_, $7); }
346         )cc",
347         class_name, resolved_field_name, CppConstType(key),
348         MessagePtrConstType(val, /* is_const */ false), MessageName(message),
349         MessageName(val->message_type()), optional_conversion_code,
350         converted_key_name, upbc_name);
351   } else if (val->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
352     output(
353         R"cc(
354           bool $0::set_$1($2 key, $3 value) {
355             $5return $4_$7_set(
356                 msg_, $6, hpb::interop::upb::CopyToUpbStringView(value, arena_),
357                 arena_);
358           }
359         )cc",
360         class_name, resolved_field_name, CppConstType(key), CppConstType(val),
361         MessageName(message), optional_conversion_code, converted_key_name,
362         upbc_name);
363     output(
364         R"cc(
365           absl::StatusOr<$3> $0::get_$1($2 key) {
366             upb_StringView value;
367             $5bool success = $4_$7_get(msg_, $6, &value);
368             if (success) {
369               return absl::string_view(value.data, value.size);
370             }
371             return absl::NotFoundError("");
372           }
373         )cc",
374         class_name, resolved_field_name, CppConstType(key), CppConstType(val),
375         MessageName(message), optional_conversion_code, converted_key_name,
376         upbc_name);
377     output(
378         R"cc(
379           void $0::delete_$1($2 key) { $5$4_$7_delete(msg_, $6); }
380         )cc",
381         class_name, resolved_field_name, CppConstType(key), CppConstType(val),
382         MessageName(message), optional_conversion_code, converted_key_name,
383         upbc_name);
384   } else {
385     output(
386         R"cc(
387           bool $0::set_$1($2 key, $3 value) {
388             $5return $4_$7_set(msg_, $6, value, arena_);
389           }
390         )cc",
391         class_name, resolved_field_name, CppConstType(key), CppConstType(val),
392         MessageName(message), optional_conversion_code, converted_key_name,
393         upbc_name);
394     output(
395         R"cc(
396           absl::StatusOr<$3> $0::get_$1($2 key) {
397             $3 value;
398             $5bool success = $4_$7_get(msg_, $6, &value);
399             if (success) {
400               return value;
401             }
402             return absl::NotFoundError("");
403           }
404         )cc",
405         class_name, resolved_field_name, CppConstType(key), CppConstType(val),
406         MessageName(message), optional_conversion_code, converted_key_name,
407         upbc_name);
408     output(
409         R"cc(
410           void $0::delete_$1($2 key) { $5$4_$7_delete(msg_, $6); }
411         )cc",
412         class_name, resolved_field_name, CppConstType(key), CppConstType(val),
413         MessageName(message), optional_conversion_code, converted_key_name,
414         upbc_name);
415   }
416 }
417 
WriteUsingAccessorsInHeader(const protobuf::Descriptor * desc,MessageClassType handle_type,Output & output)418 void WriteUsingAccessorsInHeader(const protobuf::Descriptor* desc,
419                                  MessageClassType handle_type, Output& output) {
420   bool read_only = handle_type == MessageClassType::kMessageCProxy;
421 
422   // Generate const methods.
423   OutputIndenter i(output);
424   std::string class_name = ClassName(desc);
425   auto field_names = CreateFieldNameMap(desc);
426 
427   for (const auto* field : FieldNumberOrder(desc)) {
428     std::string resolved_field_name = ResolveFieldName(field, field_names);
429     // Generate hazzer (if any).
430     if (field->has_presence()) {
431       output("using $0Access::has_$1;\n", class_name, resolved_field_name);
432       if (!read_only) {
433         output("using $0Access::clear_$1;\n", class_name, resolved_field_name);
434       }
435     }
436     if (field->is_map()) {
437       output(
438           R"cc(
439             using $0Access::$1_size;
440             using $0Access::get_$1;
441           )cc",
442           class_name, resolved_field_name);
443       if (!read_only) {
444         output(
445             R"cc(
446               using $0Access::clear_$1;
447               using $0Access::delete_$1;
448               using $0Access::set_$1;
449             )cc",
450             class_name, resolved_field_name);
451       }
452     } else if (desc->options().map_entry()) {
453       // TODO Implement map entry
454     } else if (field->is_repeated()) {
455       WriteRepeatedFieldUsingAccessors(field, class_name, resolved_field_name,
456                                        output, read_only);
457     } else {
458       if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
459         output("using $0Access::$1;\n", ClassName(desc), resolved_field_name);
460         if (!read_only) {
461           output("using $0Access::mutable_$1;\n", class_name,
462                  resolved_field_name);
463         }
464       } else {
465         output("using $0Access::$1;\n", class_name, resolved_field_name);
466         if (!read_only) {
467           output("using $0Access::set_$1;\n", class_name, resolved_field_name);
468         }
469       }
470     }
471   }
472   for (int i = 0; i < desc->real_oneof_decl_count(); ++i) {
473     const protobuf::OneofDescriptor* oneof = desc->oneof_decl(i);
474     output("using $0Access::$1_case;\n", class_name, oneof->name());
475     output("using $0Access::$1Case;\n", class_name,
476            ToCamelCase(oneof->name(), /*lower_first=*/false));
477     for (int j = 0; j < oneof->field_count(); ++j) {
478       const protobuf::FieldDescriptor* field = oneof->field(j);
479       output("using $0Access::k$1;\n", class_name,
480              ToCamelCase(field->name(), /*lower_first=*/false),
481              field->number());
482     }
483     output("using $0Access::$1_NOT_SET;\n", class_name,
484            absl::AsciiStrToUpper(oneof->name()));
485   }
486 }
487 
WriteOneofAccessorsInHeader(const protobuf::Descriptor * desc,Output & output)488 void WriteOneofAccessorsInHeader(const protobuf::Descriptor* desc,
489                                  Output& output) {
490   // Generate const methods.
491   OutputIndenter i(output);
492   std::string class_name = ClassName(desc);
493   auto field_names = CreateFieldNameMap(desc);
494   for (int i = 0; i < desc->real_oneof_decl_count(); ++i) {
495     const protobuf::OneofDescriptor* oneof = desc->oneof_decl(i);
496     output("enum $0Case {\n",
497            ToCamelCase(oneof->name(), /*lower_first=*/false));
498     for (int j = 0; j < oneof->field_count(); ++j) {
499       const protobuf::FieldDescriptor* field = oneof->field(j);
500       output("  k$0 = $1,\n", ToCamelCase(field->name(), /*lower_first=*/false),
501              field->number());
502     }
503     output("  $0_NOT_SET = 0,\n", absl::AsciiStrToUpper(oneof->name()));
504     output("};\n\n");
505     output("$0Case $1_case() const {\n",
506            ToCamelCase(oneof->name(), /*lower_first=*/false), oneof->name());
507     for (int j = 0; j < oneof->field_count(); ++j) {
508       const protobuf::FieldDescriptor* field = oneof->field(j);
509       std::string resolved_field_name = ResolveFieldName(field, field_names);
510       output("  if (has_$0()) { return k$1; }\n", resolved_field_name,
511              ToCamelCase(field->name(), /*lower_first=*/false));
512     }
513     output("  return $0_NOT_SET;\n", absl::AsciiStrToUpper(oneof->name()));
514     output("}\n;");
515   }
516 }
517 
ResolveFieldName(const protobuf::FieldDescriptor * field,const NameToFieldDescriptorMap & field_names)518 std::string ResolveFieldName(const protobuf::FieldDescriptor* field,
519                              const NameToFieldDescriptorMap& field_names) {
520   // C++ implementation specific reserved names.
521   static const auto& kReservedNames =
522       *new absl::flat_hash_set<absl::string_view>({
523           "msg",
524           "msg_",
525           "arena",
526           "arena_",
527       });
528 
529   // C++ specific prefixes used by code generator for field access.
530   static constexpr absl::string_view kClearMethodPrefix = "clear_";
531   static constexpr absl::string_view kSetMethodPrefix = "set_";
532   static constexpr absl::string_view kHasMethodPrefix = "has_";
533   static constexpr absl::string_view kDeleteMethodPrefix = "delete_";
534   static constexpr absl::string_view kAddToRepeatedMethodPrefix = "add_";
535   static constexpr absl::string_view kResizeArrayMethodPrefix = "resize_";
536 
537   // List of generated accessor prefixes to check against.
538   // Example:
539   //     optional repeated string phase = 236;
540   //     optional bool clear_phase = 237;
541   static constexpr absl::string_view kAccessorPrefixes[] = {
542       kClearMethodPrefix,       kDeleteMethodPrefix, kAddToRepeatedMethodPrefix,
543       kResizeArrayMethodPrefix, kSetMethodPrefix,    kHasMethodPrefix};
544 
545   absl::string_view field_name = field->name();
546   if (kReservedNames.count(field_name) > 0) {
547     if (absl::EndsWith(field_name, "_")) {
548       return absl::StrCat(field_name, "_");
549     } else {
550       return absl::StrCat(field_name, "__");
551     }
552   }
553   for (const auto prefix : kAccessorPrefixes) {
554     // If field name starts with a prefix such as clear_ and the proto
555     // contains a field name with trailing end, depending on type of field
556     // (repeated, map, message) we have a conflict to resolve.
557     if (absl::StartsWith(field_name, prefix)) {
558       auto match = field_names.find(field_name.substr(prefix.size()));
559       if (match != field_names.end()) {
560         const auto* candidate = match->second;
561         if (candidate->is_repeated() || candidate->is_map() ||
562             (candidate->cpp_type() ==
563                  protobuf::FieldDescriptor::CPPTYPE_STRING &&
564              prefix == kClearMethodPrefix) ||
565             prefix == kSetMethodPrefix || prefix == kHasMethodPrefix) {
566           return absl::StrCat(field_name, "_");
567         }
568       }
569     }
570   }
571   return ResolveKeywordConflict(field_name);
572 }
573 
574 }  // namespace protobuf
575 }  // namespace google::hpb_generator
576