Skip to content

Commit dcf9db2

Browse files
dmitriplotnikovcopybara-github
authored andcommitted
Generate overload IDs automatically
PiperOrigin-RevId: 869905427
1 parent c196d20 commit dcf9db2

3 files changed

Lines changed: 141 additions & 4 deletions

File tree

common/decl.cc

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include <cstddef>
1818
#include <string>
19+
#include <string_view>
1920
#include <utility>
2021
#include <vector>
2122

@@ -102,13 +103,87 @@ bool SignaturesOverlap(const OverloadDecl& lhs, const OverloadDecl& rhs) {
102103
return args_overlap;
103104
}
104105

106+
// Recursively appends a string representation of the given `type` to `result`.
107+
// Type parameters are enclosed in angle brackets and separated by commas.
108+
void AppendTypeToOverloadId(std::string* result, const Type& type) {
109+
const auto& parameters = type.GetParameters();
110+
if (parameters.empty()) {
111+
absl::StrAppend(result, type.name());
112+
} else {
113+
absl::StrAppend(result, type.name(), "<");
114+
for (size_t i = 0; i < parameters.size(); ++i) {
115+
AppendTypeToOverloadId(result, parameters[i]);
116+
if (i < parameters.size() - 1) {
117+
absl::StrAppend(result, ",");
118+
}
119+
}
120+
absl::StrAppend(result, ">");
121+
}
122+
}
123+
124+
// Generates an identifier for the overload based on the function name and
125+
// the types of the arguments. If `member` is true, the first argument type
126+
// is used as the receiver and is prepended to the function name, followed by
127+
// a dot.
128+
//
129+
// Examples:
130+
//
131+
// - `foo()`
132+
// - `foo(int)`
133+
// - `bar.foo(int)`
134+
// - `foo(int,string)`
135+
// - `foo(list<int>,list<string>)`
136+
// - `bar.foo(list<int>, list<opaque<A>>)`
137+
//
138+
std::string GenereateOverloadId(std::string_view function_name,
139+
const std::vector<Type>& args, bool member) {
140+
std::string result;
141+
result.reserve(function_name.size() + 3 + args.size() * 8);
142+
143+
if (member) {
144+
if (!args.empty()) {
145+
AppendTypeToOverloadId(&result, args[0]);
146+
}
147+
absl::StrAppend(&result, ".", function_name, "(");
148+
for (size_t i = 1; i < args.size(); ++i) {
149+
AppendTypeToOverloadId(&result, args[i]);
150+
if (i < args.size() - 1) {
151+
absl::StrAppend(&result, ",");
152+
}
153+
}
154+
absl::StrAppend(&result, ")");
155+
} else {
156+
absl::StrAppend(&result, function_name, "(");
157+
for (size_t i = 0; i < args.size(); ++i) {
158+
AppendTypeToOverloadId(&result, args[i]);
159+
if (i < args.size() - 1) {
160+
absl::StrAppend(&result, ",");
161+
}
162+
}
163+
absl::StrAppend(&result, ")");
164+
}
165+
166+
return result;
167+
}
168+
105169
template <typename Overload>
106-
void AddOverloadInternal(std::vector<OverloadDecl>& insertion_order,
170+
void AddOverloadInternal(std::string_view function_name,
171+
std::vector<OverloadDecl>& insertion_order,
107172
OverloadDeclHashSet& overloads, Overload&& overload,
108173
absl::Status& status) {
109174
if (!status.ok()) {
110175
return;
111176
}
177+
178+
if (overload.id().empty()) {
179+
OverloadDecl overload_decl = overload;
180+
overload_decl.set_id(GenereateOverloadId(
181+
function_name, overload_decl.args(), overload_decl.member()));
182+
AddOverloadInternal(function_name, insertion_order, overloads,
183+
std::move(overload_decl), status);
184+
return;
185+
}
186+
112187
if (auto it = overloads.find(overload.id()); it != overloads.end()) {
113188
status = absl::AlreadyExistsError(
114189
absl::StrCat("overload already exists: ", overload.id()));
@@ -174,13 +249,13 @@ absl::flat_hash_set<std::string> OverloadDecl::GetTypeParams() const {
174249

175250
void FunctionDecl::AddOverloadImpl(const OverloadDecl& overload,
176251
absl::Status& status) {
177-
AddOverloadInternal(overloads_.insertion_order, overloads_.set, overload,
178-
status);
252+
AddOverloadInternal(name_, overloads_.insertion_order, overloads_.set,
253+
overload, status);
179254
}
180255

181256
void FunctionDecl::AddOverloadImpl(OverloadDecl&& overload,
182257
absl::Status& status) {
183-
AddOverloadInternal(overloads_.insertion_order, overloads_.set,
258+
AddOverloadInternal(name_, overloads_.insertion_order, overloads_.set,
184259
std::move(overload), status);
185260
}
186261

common/decl.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,21 @@ inline bool operator!=(const OverloadDecl& lhs, const OverloadDecl& rhs) {
209209
return !operator==(lhs, rhs);
210210
}
211211

212+
template <typename... Args>
213+
OverloadDecl MakeOverloadDecl(Type result, Args&&... args) {
214+
OverloadDecl overload_decl;
215+
// overload_decl.set_id(std::string(id));
216+
overload_decl.set_result(std::move(result));
217+
overload_decl.set_member(false);
218+
auto& mutable_args = overload_decl.mutable_args();
219+
mutable_args.reserve(sizeof...(Args));
220+
(mutable_args.push_back(std::forward<Args>(args)), ...);
221+
return overload_decl;
222+
}
223+
224+
// Avoid this version of `MakeOverloadDecl`, it is less robust than the
225+
// version that automatically generates a descriptive overload id at the time
226+
// the overload is added to the function declaration.
212227
template <typename... Args>
213228
OverloadDecl MakeOverloadDecl(absl::string_view id, Type result,
214229
Args&&... args) {
@@ -222,6 +237,20 @@ OverloadDecl MakeOverloadDecl(absl::string_view id, Type result,
222237
return overload_decl;
223238
}
224239

240+
template <typename... Args>
241+
OverloadDecl MakeMemberOverloadDecl(Type result, Args&&... args) {
242+
OverloadDecl overload_decl;
243+
overload_decl.set_result(std::move(result));
244+
overload_decl.set_member(true);
245+
auto& mutable_args = overload_decl.mutable_args();
246+
mutable_args.reserve(sizeof...(Args));
247+
(mutable_args.push_back(std::forward<Args>(args)), ...);
248+
return overload_decl;
249+
}
250+
251+
// Avoid this version of `MakeMemberOverloadDecl`, it is less robust than the
252+
// version that automatically generates a descriptive overload id at the time
253+
// the overload is added to the function declaration.
225254
template <typename... Args>
226255
OverloadDecl MakeMemberOverloadDecl(absl::string_view id, Type result,
227256
Args&&... args) {

common/decl_test.cc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,39 @@ TEST(FunctionDecl, Overloads) {
159159
StatusIs(absl::StatusCode::kInvalidArgument));
160160
}
161161

162+
TEST(FunctionDecl, OverloadId) {
163+
google::protobuf::Arena arena;
164+
ASSERT_OK_AND_ASSIGN(
165+
auto function_decl,
166+
MakeFunctionDecl(
167+
"hello", MakeOverloadDecl(DoubleType{}),
168+
MakeOverloadDecl(StringType{}, StringType{}),
169+
MakeOverloadDecl(IntType{}, IntType{}, UintType{}),
170+
MakeOverloadDecl(
171+
ListType(&arena, TypeParamType("A")),
172+
MapType(&arena, TypeParamType("B"), TypeParamType("C")),
173+
OpaqueType(&arena, "bar",
174+
{FunctionType(&arena, TypeParamType("D"), {})})),
175+
MakeMemberOverloadDecl(IntType{}),
176+
MakeMemberOverloadDecl(StringType{}, StringType{}),
177+
MakeMemberOverloadDecl(StringType{}, StringType{},
178+
ListType(&arena, BoolType{})),
179+
MakeMemberOverloadDecl(StringType{}, StringType{}, BoolType{},
180+
DynType{})));
181+
182+
EXPECT_THAT(
183+
function_decl.overloads(),
184+
ElementsAre(
185+
Property(&OverloadDecl::id, "hello()"),
186+
Property(&OverloadDecl::id, "hello(string)"),
187+
Property(&OverloadDecl::id, "hello(int,uint)"),
188+
Property(&OverloadDecl::id, "hello(map<B,C>,bar<function<D>>)"),
189+
Property(&OverloadDecl::id, ".hello()"),
190+
Property(&OverloadDecl::id, "string.hello()"),
191+
Property(&OverloadDecl::id, "string.hello(list<bool>)"),
192+
Property(&OverloadDecl::id, "string.hello(bool,dyn)")));
193+
}
194+
162195
using common_internal::TypeIsAssignable;
163196

164197
TEST(TypeIsAssignable, BoolWrapper) {

0 commit comments

Comments
 (0)