Skip to content

Commit 5fb198e

Browse files
dmitriplotnikovcopybara-github
authored andcommitted
Add canonical CEL signature generation utilities and automatic overload ID population.
- Introduce internal `CelSignatureUtils` in `dev.cel.common` for generating canonical CEL type and function overload signatures (`makeTypeSignature`, `makeOverloadSignature`). - Make `overloadId` optional on `CelOverloadDecl` (defaulting to `""` in `newBuilder()` and adding `overloadId`-less `newGlobalOverload` and `newMemberOverload` factories) and automatically populate empty overload IDs in `CelFunctionDecl.Builder.build()`. PiperOrigin-RevId: 999679614
1 parent abcde1c commit 5fb198e

8 files changed

Lines changed: 619 additions & 4 deletions

File tree

‎checker/src/test/java/dev/cel/checker/CelOverloadDeclTest.java‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@
2121

2222
import dev.cel.expr.Decl.FunctionDecl.Overload;
2323
import com.google.common.collect.ImmutableList;
24+
import dev.cel.common.CelFunctionDecl;
2425
import dev.cel.common.CelOverloadDecl;
2526
import dev.cel.common.CelProtoDeclConverter;
2627
import dev.cel.common.types.CelProtoTypes;
28+
import dev.cel.common.types.ListType;
2729
import dev.cel.common.types.SimpleType;
2830
import dev.cel.common.types.TypeParamType;
2931
import org.junit.Test;
@@ -105,4 +107,42 @@ public void setParameterTypes_doesNotDedupe() {
105107
.containsExactly(SimpleType.STRING, SimpleType.STRING, SimpleType.STRING, SimpleType.INT)
106108
.inOrder();
107109
}
110+
111+
@Test
112+
public void newFunctionDeclaration_populatesEmptyOverloadIds() {
113+
CelOverloadDecl globalOverload =
114+
newGlobalOverload(SimpleType.INT, SimpleType.INT, SimpleType.INT);
115+
CelOverloadDecl globalListOverload =
116+
newGlobalOverload(SimpleType.STRING, ImmutableList.of(SimpleType.INT));
117+
CelOverloadDecl memberOverload =
118+
newMemberOverload(
119+
SimpleType.BOOL, ListType.create(TypeParamType.create("T")), TypeParamType.create("T"));
120+
CelOverloadDecl memberListOverload =
121+
newMemberOverload(SimpleType.INT, ImmutableList.of(SimpleType.STRING));
122+
CelOverloadDecl explicitOverload =
123+
newGlobalOverload("custom_id", SimpleType.STRING, SimpleType.STRING);
124+
125+
assertThat(globalOverload.overloadId()).isEmpty();
126+
assertThat(globalListOverload.overloadId()).isEmpty();
127+
assertThat(memberOverload.overloadId()).isEmpty();
128+
assertThat(memberListOverload.overloadId()).isEmpty();
129+
130+
CelFunctionDecl functionDecl =
131+
CelFunctionDecl.newFunctionDeclaration(
132+
"myFunc",
133+
globalOverload,
134+
globalListOverload,
135+
memberOverload,
136+
memberListOverload,
137+
explicitOverload);
138+
139+
assertThat(functionDecl.overloads().stream().map(CelOverloadDecl::overloadId))
140+
.containsExactly(
141+
"myFunc(int,int)",
142+
"myFunc(int)",
143+
"list<~T>.myFunc(~T)",
144+
"string.myFunc()",
145+
"custom_id")
146+
.inOrder();
147+
}
108148
}

‎common/BUILD.bazel‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,18 @@ cel_android_library(
4040
exports = ["//common/src/main/java/dev/cel/common:cel_overload_decl_android"],
4141
)
4242

43+
java_library(
44+
name = "cel_signature_utils",
45+
visibility = ["//:internal"],
46+
exports = ["//common/src/main/java/dev/cel/common:cel_signature_utils"],
47+
)
48+
49+
cel_android_library(
50+
name = "cel_signature_utils_android",
51+
visibility = ["//:internal"],
52+
exports = ["//common/src/main/java/dev/cel/common:cel_signature_utils_android"],
53+
)
54+
4355
java_library(
4456
name = "cel_proto_decl_converter",
4557
exports = ["//common/src/main/java/dev/cel/common:cel_proto_decl_converter"],

‎common/src/main/java/dev/cel/common/BUILD.bazel‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ java_library(
6363
],
6464
deps = [
6565
":cel_overload_decl",
66+
":cel_signature_utils",
6667
"//:auto_value",
6768
"@maven//:com_google_errorprone_error_prone_annotations",
6869
"@maven//:com_google_guava_guava",
@@ -76,12 +77,39 @@ cel_android_library(
7677
],
7778
deps = [
7879
":cel_overload_decl_android",
80+
":cel_signature_utils_android",
7981
"//:auto_value",
8082
"@maven//:com_google_errorprone_error_prone_annotations",
8183
"@maven_android//:com_google_guava_guava",
8284
],
8385
)
8486

87+
java_library(
88+
name = "cel_signature_utils",
89+
srcs = ["CelSignatureUtils.java"],
90+
tags = [
91+
],
92+
deps = [
93+
"//common/annotations",
94+
"//common/types",
95+
"//common/types:type_providers",
96+
"@maven//:com_google_guava_guava",
97+
],
98+
)
99+
100+
cel_android_library(
101+
name = "cel_signature_utils_android",
102+
srcs = ["CelSignatureUtils.java"],
103+
tags = [
104+
],
105+
deps = [
106+
"//common/annotations",
107+
"//common/types:type_providers_android",
108+
"//common/types:types_android",
109+
"@maven_android//:com_google_guava_guava",
110+
],
111+
)
112+
85113
java_library(
86114
name = "cel_overload_decl",
87115
srcs = ["CelOverloadDecl.java"],

‎common/src/main/java/dev/cel/common/CelFunctionDecl.java‎

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
package dev.cel.common;
1616

17+
import static com.google.common.base.Preconditions.checkArgument;
1718
import static com.google.common.base.Preconditions.checkNotNull;
1819

1920
import com.google.auto.value.AutoValue;
@@ -22,6 +23,8 @@
2223
import com.google.errorprone.annotations.CheckReturnValue;
2324
import com.google.errorprone.annotations.Immutable;
2425
import java.util.Arrays;
26+
import java.util.HashSet;
27+
import java.util.Set;
2528

2629
/** Abstract representation of a CEL Function declaration. */
2730
@AutoValue
@@ -70,9 +73,50 @@ public Builder addOverloads(Iterable<CelOverloadDecl> overloads) {
7073
return this;
7174
}
7275

73-
/** Builds a new instance of {@link CelFunctionDecl} */
7476
@CheckReturnValue
75-
public abstract CelFunctionDecl build();
77+
abstract CelFunctionDecl autoBuild();
78+
79+
/**
80+
* Builds a new instance of {@link CelFunctionDecl}.
81+
*
82+
* <p>Any {@link CelOverloadDecl} with an empty {@link CelOverloadDecl#overloadId()} will have
83+
* its ID automatically populated with its canonical overload signature.
84+
*/
85+
@CheckReturnValue
86+
public CelFunctionDecl build() {
87+
ImmutableSet<CelOverloadDecl> currentOverloads = overloads();
88+
boolean needsOverloadId = false;
89+
for (CelOverloadDecl overload : currentOverloads) {
90+
if (overload.overloadId().isEmpty()) {
91+
needsOverloadId = true;
92+
break;
93+
}
94+
}
95+
if (!needsOverloadId) {
96+
return autoBuild();
97+
}
98+
String functionName = name();
99+
ImmutableSet.Builder<CelOverloadDecl> updatedOverloads =
100+
ImmutableSet.builderWithExpectedSize(currentOverloads.size());
101+
Set<String> seenOverloadIds = new HashSet<>();
102+
for (CelOverloadDecl overload : currentOverloads) {
103+
String overloadId = overload.overloadId();
104+
if (overloadId.isEmpty()) {
105+
overloadId =
106+
CelSignatureUtils.makeOverloadSignature(
107+
functionName, overload.parameterTypes(), overload.isInstanceFunction());
108+
updatedOverloads.add(overload.toBuilder().setOverloadId(overloadId).build());
109+
} else {
110+
updatedOverloads.add(overload);
111+
}
112+
checkArgument(
113+
seenOverloadIds.add(overloadId), "Duplicate overload signature: %s", overloadId);
114+
}
115+
return new AutoValue_CelFunctionDecl.Builder()
116+
.setName(functionName)
117+
.setOverloads(updatedOverloads.build())
118+
.autoBuild();
119+
}
76120
}
77121

78122
public abstract Builder toBuilder();

‎common/src/main/java/dev/cel/common/CelOverloadDecl.java‎

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@
4141
@AutoValue
4242
@Immutable
4343
public abstract class CelOverloadDecl {
44-
/** Required. Globally unique overload name. */
44+
/**
45+
* Globally unique overload name. If empty when building {@link CelFunctionDecl}, an overload ID
46+
* is automatically generated from the function name and parameter types.
47+
*/
4548
public abstract String overloadId();
4649

4750
/**
@@ -79,7 +82,7 @@ public abstract class CelOverloadDecl {
7982

8083
/** Create a new builder to construct a {@link CelOverloadDecl} instance */
8184
public static Builder newBuilder() {
82-
return new AutoValue_CelOverloadDecl.Builder().setDoc("");
85+
return new AutoValue_CelOverloadDecl.Builder().setOverloadId("").setDoc("");
8386
}
8487

8588
/** Builder for configuring the {@link CelOverloadDecl}. */
@@ -153,6 +156,44 @@ public CelOverloadDecl build() {
153156
}
154157
}
155158

159+
/**
160+
* Helper method for declaring a member function overload (e.g. {@code x.f(y, ...)}) with an
161+
* automatically generated overload ID.
162+
*
163+
* <p>The returned declaration has an empty {@link #overloadId()}, which is automatically
164+
* populated with the canonical signature (e.g. {@code "target.func(arg1,arg2)"}) when built into
165+
* a {@link CelFunctionDecl} via {@link CelFunctionDecl.Builder#build()} or {@link
166+
* CelFunctionDecl#newFunctionDeclaration}.
167+
*
168+
* @param resultType the return type of the overload
169+
* @param paramTypes the parameter types of the overload, where the first element ({@code
170+
* paramTypes[0]}) is the receiver (target) type {@code x} and any subsequent elements are the
171+
* method argument types {@code y, ...}
172+
*/
173+
@CheckReturnValue
174+
public static CelOverloadDecl newMemberOverload(CelType resultType, CelType... paramTypes) {
175+
return newMemberOverload("", resultType, Arrays.asList(paramTypes));
176+
}
177+
178+
/**
179+
* Helper method for declaring a member function overload (e.g. {@code x.f(y, ...)}) with an
180+
* automatically generated overload ID.
181+
*
182+
* <p>The returned declaration has an empty {@link #overloadId()}, which is automatically
183+
* populated with the canonical signature (e.g. {@code "target.func(arg1,arg2)"}) when built into
184+
* a {@link CelFunctionDecl} via {@link CelFunctionDecl.Builder#build()} or {@link
185+
* CelFunctionDecl#newFunctionDeclaration}.
186+
*
187+
* @param resultType the return type of the overload
188+
* @param paramTypes the parameter types of the overload, where the first element ({@code
189+
* paramTypes.get(0)}) is the receiver (target) type {@code x} and any subsequent elements are
190+
* the method argument types {@code y, ...}
191+
*/
192+
@CheckReturnValue
193+
public static CelOverloadDecl newMemberOverload(CelType resultType, List<CelType> paramTypes) {
194+
return newMemberOverload("", /* doc= */ "", resultType, paramTypes);
195+
}
196+
156197
/** Helper method for declaring a member function overload */
157198
@CheckReturnValue
158199
public static CelOverloadDecl newMemberOverload(
@@ -181,6 +222,40 @@ public static CelOverloadDecl newMemberOverload(
181222
return newOverload(overloadId, doc, resultType, paramTypes, /* isInstanceFunction= */ true);
182223
}
183224

225+
/**
226+
* Helper method for declaring a global function overload (e.g. {@code f(x, ...)}) with an
227+
* automatically generated overload ID.
228+
*
229+
* <p>The returned declaration has an empty {@link #overloadId()}, which is automatically
230+
* populated with the canonical signature (e.g. {@code "func(arg1,arg2)"}) when built into a
231+
* {@link CelFunctionDecl} via {@link CelFunctionDecl.Builder#build()} or {@link
232+
* CelFunctionDecl#newFunctionDeclaration}.
233+
*
234+
* @param resultType the return type of the overload
235+
* @param paramTypes the argument types of the overload
236+
*/
237+
@CheckReturnValue
238+
public static CelOverloadDecl newGlobalOverload(CelType resultType, CelType... paramTypes) {
239+
return newGlobalOverload("", resultType, Arrays.asList(paramTypes));
240+
}
241+
242+
/**
243+
* Helper method for declaring a global function overload (e.g. {@code f(x, ...)}) with an
244+
* automatically generated overload ID.
245+
*
246+
* <p>The returned declaration has an empty {@link #overloadId()}, which is automatically
247+
* populated with the canonical signature (e.g. {@code "func(arg1,arg2)"}) when built into a
248+
* {@link CelFunctionDecl} via {@link CelFunctionDecl.Builder#build()} or {@link
249+
* CelFunctionDecl#newFunctionDeclaration}.
250+
*
251+
* @param resultType the return type of the overload
252+
* @param paramTypes the argument types of the overload
253+
*/
254+
@CheckReturnValue
255+
public static CelOverloadDecl newGlobalOverload(CelType resultType, List<CelType> paramTypes) {
256+
return newGlobalOverload("", /* doc= */ "", resultType, paramTypes);
257+
}
258+
184259
/** Helper method for declaring a global function overload */
185260
@CheckReturnValue
186261
public static CelOverloadDecl newGlobalOverload(

0 commit comments

Comments
 (0)