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