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