1 // Copyright (c) 2009-2021, Google LLC
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //     * Redistributions of source code must retain the above copyright
7 //       notice, this list of conditions and the following disclaimer.
8 //     * Redistributions in binary form must reproduce the above copyright
9 //       notice, this list of conditions and the following disclaimer in the
10 //       documentation and/or other materials provided with the distribution.
11 //     * Neither the name of Google LLC nor the
12 //       names of its contributors may be used to endorse or promote products
13 //       derived from this software without specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 // ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
19 // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 
26 #include "protos_generator/gen_accessors.h"
27 
28 #include <string>
29 
30 #include "absl/container/flat_hash_set.h"
31 #include "absl/strings/match.h"
32 #include "absl/strings/str_cat.h"
33 #include "absl/strings/string_view.h"
34 #include "google/protobuf/descriptor.h"
35 #include "protos_generator/gen_repeated_fields.h"
36 #include "protos_generator/gen_utils.h"
37 #include "protos_generator/output.h"
38 #include "upbc/common.h"
39 #include "upbc/keywords.h"
40 #include "upbc/names.h"
41 
42 namespace protos_generator {
43 
44 namespace protobuf = ::google::protobuf;
45 
46 using NameToFieldDescriptorMap =
47     absl::flat_hash_map<absl::string_view, const protobuf::FieldDescriptor*>;
48 
49 void WriteFieldAccessorHazzer(const protobuf::Descriptor* desc,
50                               const protobuf::FieldDescriptor* field,
51                               absl::string_view resolved_field_name,
52                               absl::string_view resolved_upbc_name,
53                               Output& output);
54 void WriteFieldAccessorClear(const protobuf::Descriptor* desc,
55                              const protobuf::FieldDescriptor* field,
56                              absl::string_view resolved_field_name,
57                              absl::string_view resolved_upbc_name,
58                              Output& output);
59 void WriteMapFieldAccessors(const protobuf::Descriptor* desc,
60                             const protobuf::FieldDescriptor* field,
61                             absl::string_view resolved_field_name,
62                             absl::string_view resolved_upbc_name,
63                             Output& output);
64 
65 void WriteMapAccessorDefinitions(const protobuf::Descriptor* message,
66                                  const protobuf::FieldDescriptor* field,
67                                  absl::string_view resolved_field_name,
68                                  absl::string_view class_name, Output& output);
69 
70 // Returns C++ class member name by resolving naming conflicts across
71 // proto field names (such as clear_ prefixes) and keyword collisions.
72 //
73 // The Upb C generator prefixes all accessors with package and class names
74 // avoiding collisions. Therefore we need to use raw field names when calling
75 // into C accessors but need to fully resolve conflicts for C++ class members.
76 std::string ResolveFieldName(const protobuf::FieldDescriptor* field,
77                              const NameToFieldDescriptorMap& field_names);
78 
CreateFieldNameMap(const protobuf::Descriptor * message)79 NameToFieldDescriptorMap CreateFieldNameMap(
80     const protobuf::Descriptor* message) {
81   NameToFieldDescriptorMap field_names;
82   for (int i = 0; i < message->field_count(); i++) {
83     const protobuf::FieldDescriptor* field = message->field(i);
84     field_names.emplace(field->name(), field);
85   }
86   return field_names;
87 }
88 
WriteFieldAccessorsInHeader(const protobuf::Descriptor * desc,Output & output)89 void WriteFieldAccessorsInHeader(const protobuf::Descriptor* desc,
90                                  Output& output) {
91   // Generate const methods.
92   OutputIndenter i(output);
93 
94   auto field_names = CreateFieldNameMap(desc);
95   auto upbc_field_names = upbc::CreateFieldNameMap(desc);
96 
97   for (const auto* field : FieldNumberOrder(desc)) {
98     std::string resolved_field_name = ResolveFieldName(field, field_names);
99     std::string resolved_upbc_name =
100         upbc::ResolveFieldName(field, upbc_field_names);
101     WriteFieldAccessorHazzer(desc, field, resolved_field_name,
102                              resolved_upbc_name, output);
103     WriteFieldAccessorClear(desc, field, resolved_field_name,
104                             resolved_upbc_name, output);
105 
106     if (field->is_map()) {
107       WriteMapFieldAccessors(desc, field, resolved_field_name,
108                              resolved_upbc_name, output);
109     } else if (desc->options().map_entry()) {
110       // TODO(b/237399867) Implement map entry
111     } else if (field->is_repeated()) {
112       WriteRepeatedFieldsInMessageHeader(desc, field, resolved_field_name,
113                                          resolved_upbc_name, output);
114     } else {
115       // non-repeated.
116       if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
117         output(R"cc(
118                  $0 $1() const;
119                  void set_$1($0 value);
120                )cc",
121                CppConstType(field), resolved_field_name);
122       } else if (field->cpp_type() ==
123                  protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
124         output(R"cc(
125                  $1 $2() const;
126                  $0 mutable_$2();
127                )cc",
128                MessagePtrConstType(field, /* const */ false),
129                MessagePtrConstType(field, /* const */ true),
130                resolved_field_name, resolved_upbc_name);
131       } else {
132         output(
133             R"cc(
134               inline $0 $1() const { return $2_$3(msg_); }
135               inline void set_$1($0 value) { return $2_set_$3(msg_, value); }
136             )cc",
137             CppConstType(field), resolved_field_name, MessageName(desc),
138             resolved_upbc_name);
139       }
140     }
141   }
142 }
143 
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)144 void WriteFieldAccessorHazzer(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   // Generate hazzer (if any).
150   if (field->has_presence()) {
151     // Has presence.
152     output("inline bool has_$0() const { return $1_has_$2(msg_); }\n",
153            resolved_field_name, MessageName(desc), resolved_upbc_name);
154   }
155 }
156 
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)157 void WriteFieldAccessorClear(const protobuf::Descriptor* desc,
158                              const protobuf::FieldDescriptor* field,
159                              const absl::string_view resolved_field_name,
160                              const absl::string_view resolved_upbc_name,
161                              Output& output) {
162   if (field->has_presence()) {
163     output("void clear_$0() { $2_clear_$1(msg_); }\n", resolved_field_name,
164            resolved_upbc_name, MessageName(desc));
165   }
166 }
167 
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)168 void WriteMapFieldAccessors(const protobuf::Descriptor* desc,
169                             const protobuf::FieldDescriptor* field,
170                             const absl::string_view resolved_field_name,
171                             const absl::string_view resolved_upbc_name,
172                             Output& output) {
173   const protobuf::Descriptor* entry = field->message_type();
174   const protobuf::FieldDescriptor* key = entry->FindFieldByNumber(1);
175   const protobuf::FieldDescriptor* val = entry->FindFieldByNumber(2);
176   output(
177       R"cc(
178         inline size_t $0_size() const { return $1_$3_size(msg_); }
179         inline void clear_$0() { $1_clear_$3(msg_); }
180         void delete_$0($2 key);
181       )cc",
182       resolved_field_name, MessageName(desc), CppConstType(key),
183       resolved_upbc_name);
184 
185   if (val->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
186     output(
187         R"cc(
188           bool set_$0($1 key, $3 value);
189           bool set_$0($1 key, $4 value);
190           absl::StatusOr<$3> get_$0($1 key);
191         )cc",
192         resolved_field_name, CppConstType(key), CppConstType(val),
193         MessagePtrConstType(val, /* is_const */ true),
194         MessagePtrConstType(val, /* is_const */ false));
195   } else {
196     output(
197         R"cc(
198           bool set_$0($1 key, $2 value);
199           absl::StatusOr<$2> get_$0($1 key);
200         )cc",
201         resolved_field_name, CppConstType(key), CppConstType(val));
202   }
203 }
204 
WriteAccessorsInSource(const protobuf::Descriptor * desc,Output & output)205 void WriteAccessorsInSource(const protobuf::Descriptor* desc, Output& output) {
206   std::string class_name = ClassName(desc);
207   absl::StrAppend(&class_name, "Access");
208   output("namespace internal {\n");
209   const char arena_expression[] = "arena_";
210   auto field_names = CreateFieldNameMap(desc);
211   auto upbc_field_names = upbc::CreateFieldNameMap(desc);
212 
213   // Generate const methods.
214   OutputIndenter i(output);
215   for (const auto* field : FieldNumberOrder(desc)) {
216     std::string resolved_field_name = ResolveFieldName(field, field_names);
217     std::string resolved_upbc_name =
218         upbc::ResolveFieldName(field, upbc_field_names);
219     if (field->is_map()) {
220       WriteMapAccessorDefinitions(desc, field, resolved_field_name, class_name,
221                                   output);
222     } else if (desc->options().map_entry()) {
223       // TODO(b/237399867) Implement map entry
224     } else if (field->is_repeated()) {
225       if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
226         WriteRepeatedMessageAccessor(desc, field, resolved_field_name,
227                                      class_name, output);
228       } else if (field->cpp_type() ==
229                  protobuf::FieldDescriptor::CPPTYPE_STRING) {
230         WriteRepeatedStringAccessor(desc, field, resolved_field_name,
231                                     class_name, output);
232       } else {
233         WriteRepeatedScalarAccessor(desc, field, resolved_field_name,
234                                     class_name, output);
235       }
236     } else {
237       // non-repeated field.
238       if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
239         output(
240             R"cc(
241               $1 $0::$2() const {
242                 return ::protos::UpbStrToStringView($3_$4(msg_));
243               }
244             )cc",
245             class_name, CppConstType(field), resolved_field_name,
246             MessageName(desc), resolved_upbc_name);
247         // Set string.
248         output(
249             R"cc(
250               void $0::set_$2($1 value) {
251                 $4_set_$3(msg_, ::protos::UpbStrFromStringView(value, $5));
252               }
253             )cc",
254             class_name, CppConstType(field), resolved_field_name,
255             resolved_upbc_name, MessageName(desc), arena_expression);
256       } else if (field->cpp_type() ==
257                  protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
258         output(
259             R"cc(
260               $1 $0::$2() const {
261                 if (!has_$2()) {
262                   return $4::default_instance();
263                 }
264                 return ::protos::internal::CreateMessage<$4>((upb_Message*)($3_$5(msg_)));
265               }
266             )cc",
267             class_name, MessagePtrConstType(field, /* is_const */ true),
268             resolved_field_name, MessageName(desc),
269             MessageBaseType(field, /* maybe_const */ false),
270             resolved_upbc_name);
271 
272         output(
273             R"cc(
274               $1 $0::mutable_$2() {
275                 return ::protos::internal::CreateMessageProxy<$4>(
276                     (upb_Message*)($3_mutable_$5(msg_, $6)), $6);
277               }
278             )cc",
279             class_name, MessagePtrConstType(field, /* is_const */ false),
280             resolved_field_name, MessageName(desc),
281             MessageBaseType(field, /* maybe_const */ false), resolved_upbc_name,
282             arena_expression);
283       }
284     }
285   }
286   output("\n");
287   output("}  // namespace internal\n\n");
288 }
289 
WriteMapAccessorDefinitions(const protobuf::Descriptor * message,const protobuf::FieldDescriptor * field,const absl::string_view resolved_field_name,const absl::string_view class_name,Output & output)290 void WriteMapAccessorDefinitions(const protobuf::Descriptor* message,
291                                  const protobuf::FieldDescriptor* field,
292                                  const absl::string_view resolved_field_name,
293                                  const absl::string_view class_name,
294                                  Output& output) {
295   const protobuf::Descriptor* entry = field->message_type();
296   const protobuf::FieldDescriptor* key = entry->FindFieldByNumber(1);
297   const protobuf::FieldDescriptor* val = entry->FindFieldByNumber(2);
298   absl::string_view upbc_name = field->name();
299   absl::string_view converted_key_name = "key";
300   absl::string_view optional_conversion_code = "";
301 
302   if (key->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
303     // Insert conversion from absl::string_view to upb_StringView.
304     // Creates upb_StringView on stack to prevent allocation.
305     converted_key_name = "upb_key";
306     optional_conversion_code =
307         "upb_StringView upb_key = {key.data(), key.size()};\n";
308   }
309   if (val->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
310     output(
311         R"cc(
312           bool $0::set_$1($2 key, $3 value) {
313             upb_Message* clone = upb_Message_DeepClone(value->msg(), &$9, arena_);
314             $6return $4_$8_set(msg_, $7, ($5*)clone, arena_);
315           }
316         )cc",
317         class_name, resolved_field_name, CppConstType(key),
318         MessagePtrConstType(val, /* is_const */ true), MessageName(message),
319         MessageName(val->message_type()), optional_conversion_code,
320         converted_key_name, upbc_name,
321         ::upbc::MessageInit(val->message_type()->full_name()));
322     output(
323         R"cc(
324           bool $0::set_$1($2 key, $3 value) {
325             upb_Message* clone = upb_Message_DeepClone(value->msg(), &$9, arena_);
326             $6return $4_$8_set(msg_, $7, ($5*)clone, arena_);
327           }
328         )cc",
329         class_name, resolved_field_name, CppConstType(key),
330         MessagePtrConstType(val, /* is_const */ false), MessageName(message),
331         MessageName(val->message_type()), optional_conversion_code,
332         converted_key_name, upbc_name,
333         ::upbc::MessageInit(val->message_type()->full_name()));
334     output(
335         R"cc(
336           absl::StatusOr<$3> $0::get_$1($2 key) {
337             $5* msg_value;
338             $7bool success = $4_$9_get(msg_, $8, &msg_value);
339             if (success) {
340               return ::protos::internal::CreateMessage<$6>(msg_value);
341             }
342             return absl::NotFoundError("");
343           }
344         )cc",
345         class_name, resolved_field_name, CppConstType(key),
346         MessagePtrConstType(val, /* is_const */ true), MessageName(message),
347         MessageName(val->message_type()),
348         QualifiedClassName(val->message_type()), optional_conversion_code,
349         converted_key_name, upbc_name);
350     output(
351         R"cc(
352           void $0::delete_$1($2 key) { $6$4_$8_delete(msg_, $7); }
353         )cc",
354         class_name, resolved_field_name, CppConstType(key),
355         MessagePtrConstType(val, /* is_const */ false), MessageName(message),
356         MessageName(val->message_type()), optional_conversion_code,
357         converted_key_name, upbc_name);
358   } else if (val->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
359     output(
360         R"cc(
361           bool $0::set_$1($2 key, $3 value) {
362             $5return $4_$7_set(msg_, $6,
363                                ::protos::UpbStrFromStringView(value, arena_),
364                                arena_);
365           }
366         )cc",
367         class_name, resolved_field_name, CppConstType(key), CppConstType(val),
368         MessageName(message), optional_conversion_code, converted_key_name,
369         upbc_name);
370     output(
371         R"cc(
372           absl::StatusOr<$3> $0::get_$1($2 key) {
373             upb_StringView value;
374             $5bool success = $4_$7_get(msg_, $6, &value);
375             if (success) {
376               return absl::string_view(value.data, value.size);
377             }
378             return absl::NotFoundError("");
379           }
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     output(
385         R"cc(
386           void $0::delete_$1($2 key) { $5$4_$7_delete(msg_, $6); }
387         )cc",
388         class_name, resolved_field_name, CppConstType(key), CppConstType(val),
389         MessageName(message), optional_conversion_code, converted_key_name,
390         upbc_name);
391   } else {
392     output(
393         R"cc(
394           bool $0::set_$1($2 key, $3 value) {
395             $5return $4_$7_set(msg_, $6, value, arena_);
396           }
397         )cc",
398         class_name, resolved_field_name, CppConstType(key), CppConstType(val),
399         MessageName(message), optional_conversion_code, converted_key_name,
400         upbc_name);
401     output(
402         R"cc(
403           absl::StatusOr<$3> $0::get_$1($2 key) {
404             $3 value;
405             $5bool success = $4_$7_get(msg_, $6, &value);
406             if (success) {
407               return value;
408             }
409             return absl::NotFoundError("");
410           }
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     output(
416         R"cc(
417           void $0::delete_$1($2 key) { $5$4_$7_delete(msg_, $6); }
418         )cc",
419         class_name, resolved_field_name, CppConstType(key), CppConstType(val),
420         MessageName(message), optional_conversion_code, converted_key_name,
421         upbc_name);
422   }
423 }
424 
WriteUsingAccessorsInHeader(const protobuf::Descriptor * desc,MessageClassType handle_type,Output & output)425 void WriteUsingAccessorsInHeader(const protobuf::Descriptor* desc,
426                                  MessageClassType handle_type, Output& output) {
427   bool read_only = handle_type == MessageClassType::kMessageCProxy;
428 
429   // Generate const methods.
430   OutputIndenter i(output);
431   std::string class_name = ClassName(desc);
432   auto field_names = CreateFieldNameMap(desc);
433 
434   for (const auto* field : FieldNumberOrder(desc)) {
435     std::string resolved_field_name = ResolveFieldName(field, field_names);
436     // Generate hazzer (if any).
437     if (field->has_presence()) {
438       output("using $0Access::has_$1;\n", class_name, resolved_field_name);
439       output("using $0Access::clear_$1;\n", class_name, resolved_field_name);
440     }
441     if (field->is_map()) {
442       output(
443           R"cc(
444             using $0Access::$1_size;
445             using $0Access::clear_$1;
446             using $0Access::delete_$1;
447             using $0Access::get_$1;
448             using $0Access::set_$1;
449           )cc",
450           class_name, resolved_field_name);
451     } else if (desc->options().map_entry()) {
452       // TODO(b/237399867) Implement map entry
453     } else if (field->is_repeated()) {
454       WriteRepeatedFieldUsingAccessors(field, class_name, resolved_field_name,
455                                        output, read_only);
456     } else {
457       if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
458         output("using $0Access::$1;\n", ClassName(desc), resolved_field_name);
459         if (!read_only) {
460           output("using $0Access::mutable_$1;\n", class_name,
461                  resolved_field_name);
462         }
463       } else {
464         output("using $0Access::$1;\n", class_name, resolved_field_name);
465         if (!read_only) {
466           output("using $0Access::set_$1;\n", class_name, resolved_field_name);
467         }
468       }
469     }
470   }
471   for (int i = 0; i < desc->real_oneof_decl_count(); ++i) {
472     const protobuf::OneofDescriptor* oneof = desc->oneof_decl(i);
473     output("using $0Access::$1_case;\n", class_name, oneof->name());
474     output("using $0Access::$1Case;\n", class_name,
475            ToCamelCase(oneof->name(), /*lower_first=*/false));
476     for (int j = 0; j < oneof->field_count(); ++j) {
477       const protobuf::FieldDescriptor* field = oneof->field(j);
478       output("using $0Access::k$1;\n", class_name,
479              ToCamelCase(field->name(), /*lower_first=*/false),
480              field->number());
481     }
482     output("using $0Access::$1_NOT_SET;\n", class_name,
483            absl::AsciiStrToUpper(oneof->name()));
484   }
485   output("using $0Access::msg;\n", class_name);
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 upbc::ResolveKeywordConflict(std::string(field_name));
572 }
573 
574 }  // namespace protos_generator
575