1 /*
2 * Copyright (c) 2007-2021, Google LLC
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of Google LLC nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include "upbc/names.h"
29
30 #include <string>
31
32 #include "absl/strings/match.h"
33 #include "absl/strings/string_view.h"
34 #include "google/protobuf/descriptor.h"
35 #include "upb/reflection/def.hpp"
36
37 namespace upbc {
38
39 namespace protobuf = ::google::protobuf;
40
41 // Prefixes used by C code generator for field access.
42 static constexpr absl::string_view kClearMethodPrefix = "clear_";
43 static constexpr absl::string_view kSetMethodPrefix = "set_";
44 static constexpr absl::string_view kHasMethodPrefix = "has_";
45 static constexpr absl::string_view kDeleteMethodPrefix = "delete_";
46 static constexpr absl::string_view kAddToRepeatedMethodPrefix = "add_";
47 static constexpr absl::string_view kResizeArrayMethodPrefix = "resize_";
48
49 const absl::string_view kRepeatedFieldArrayGetterPostfix = "upb_array";
50 const absl::string_view kRepeatedFieldMutableArrayGetterPostfix =
51 "mutable_upb_array";
52
53 // List of generated accessor prefixes to check against.
54 // Example:
55 // optional repeated string phase = 236;
56 // optional bool clear_phase = 237;
57 static constexpr absl::string_view kAccessorPrefixes[] = {
58 kClearMethodPrefix, kDeleteMethodPrefix, kAddToRepeatedMethodPrefix,
59 kResizeArrayMethodPrefix, kSetMethodPrefix, kHasMethodPrefix};
60
ResolveFieldName(const protobuf::FieldDescriptor * field,const NameToFieldDescriptorMap & field_names)61 std::string ResolveFieldName(const protobuf::FieldDescriptor* field,
62 const NameToFieldDescriptorMap& field_names) {
63 absl::string_view field_name = field->name();
64 for (const auto prefix : kAccessorPrefixes) {
65 // If field name starts with a prefix such as clear_ and the proto
66 // contains a field name with trailing end, depending on type of field
67 // (repeated, map, message) we have a conflict to resolve.
68 if (absl::StartsWith(field_name, prefix)) {
69 auto match = field_names.find(field_name.substr(prefix.size()));
70 if (match != field_names.end()) {
71 const auto* candidate = match->second;
72 if (candidate->is_repeated() || candidate->is_map() ||
73 (candidate->cpp_type() ==
74 protobuf::FieldDescriptor::CPPTYPE_STRING &&
75 prefix == kClearMethodPrefix) ||
76 prefix == kSetMethodPrefix || prefix == kHasMethodPrefix) {
77 return absl::StrCat(field_name, "_");
78 }
79 }
80 }
81 }
82 return std::string(field_name);
83 }
84
85 // Returns field map by name to use for conflict checks.
CreateFieldNameMap(const protobuf::Descriptor * message)86 NameToFieldDescriptorMap CreateFieldNameMap(
87 const protobuf::Descriptor* message) {
88 NameToFieldDescriptorMap field_names;
89 for (int i = 0; i < message->field_count(); i++) {
90 const protobuf::FieldDescriptor* field = message->field(i);
91 field_names.emplace(field->name(), field);
92 }
93 return field_names;
94 }
95
CreateFieldNameMap(upb::MessageDefPtr message)96 NameToFieldDefMap CreateFieldNameMap(upb::MessageDefPtr message) {
97 NameToFieldDefMap field_names;
98 field_names.reserve(message.field_count());
99 for (const auto& field : message.fields()) {
100 field_names.emplace(field.name(), field);
101 }
102 return field_names;
103 }
104
ResolveFieldName(upb::FieldDefPtr field,const NameToFieldDefMap & field_names)105 std::string ResolveFieldName(upb::FieldDefPtr field,
106 const NameToFieldDefMap& field_names) {
107 absl::string_view field_name(field.name());
108 for (absl::string_view prefix : kAccessorPrefixes) {
109 // If field name starts with a prefix such as clear_ and the proto
110 // contains a field name with trailing end, depending on type of field
111 // (repeated, map, message) we have a conflict to resolve.
112 if (absl::StartsWith(field_name, prefix)) {
113 auto match = field_names.find(field_name.substr(prefix.size()));
114 if (match != field_names.end()) {
115 const auto candidate = match->second;
116 if (candidate.IsSequence() || candidate.IsMap() ||
117 (candidate.ctype() == kUpb_CType_String &&
118 prefix == kClearMethodPrefix) ||
119 prefix == kSetMethodPrefix || prefix == kHasMethodPrefix) {
120 return absl::StrCat(field_name, "_");
121 }
122 }
123 }
124 }
125 return std::string(field_name);
126 }
127
128 } // namespace upbc
129