diff --git a/.gitignore b/.gitignore index e8052913c..3a2e0015d 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,5 @@ mvn-artifacts *.swp *.lock +.eclipse +.vscode diff --git a/checker/src/main/java/dev/cel/checker/Env.java b/checker/src/main/java/dev/cel/checker/Env.java index 15079cff7..449358a60 100644 --- a/checker/src/main/java/dev/cel/checker/Env.java +++ b/checker/src/main/java/dev/cel/checker/Env.java @@ -451,17 +451,28 @@ public Env add(String name, Type type) { * until the root package is reached. If {@code container} starts with {@code .}, the resolution * is in the root container only. * - *
Returns {@code null} if the function cannot be found. + *
Returns {@code null} if the ident cannot be found. */ public @Nullable CelIdentDecl tryLookupCelIdent(CelContainer container, String name) { - // Attempt to find the decl with just the ident name to account for shadowed variables - CelIdentDecl decl = tryLookupCelIdentFromLocalScopes(name); - if (decl != null) { - return decl; + // A name with a leading '.' always resolves in the root scope, bypassing local scopes. + if (!name.startsWith(".")) { + // Check if this is a qualified ident, or a field selection. + String simpleName = name; + int dotIndex = name.indexOf('.'); + if (dotIndex > 0) { + simpleName = name.substring(0, dotIndex); + } + + // Attempt to find the decl with just the ident name to account for shadowed variables. + CelIdentDecl decl = tryLookupCelIdentFromLocalScopes(simpleName); + if (decl != null) { + // Null signals field selection + return dotIndex > 0 ? null : decl; + } } for (String cand : container.resolveCandidateNames(name)) { - decl = tryLookupCelIdent(cand); + CelIdentDecl decl = tryLookupCelIdent(cand); if (decl != null) { return decl; } @@ -503,7 +514,13 @@ public Env add(String name, Type type) { return null; } - private @Nullable CelIdentDecl tryLookupCelIdentFromLocalScopes(String name) { + /** + * Lookup a local identifier by name. This searches only comprehension scopes, bypassing standard environment or user-defined environment. + * + *
Returns {@code null} if not found in local scopes.
+ */
+ @Nullable
+ CelIdentDecl tryLookupCelIdentFromLocalScopes(String name) {
int firstUserSpaceScope = 2;
// Iterate from the top of the stack down to the first local scope.
// Note that:
diff --git a/checker/src/main/java/dev/cel/checker/ExprChecker.java b/checker/src/main/java/dev/cel/checker/ExprChecker.java
index 9ed121a20..a3b9f76b2 100644
--- a/checker/src/main/java/dev/cel/checker/ExprChecker.java
+++ b/checker/src/main/java/dev/cel/checker/ExprChecker.java
@@ -240,12 +240,24 @@ private CelExpr visit(CelExpr expr, CelExpr.CelIdent ident) {
env.setRef(expr, makeReference(decl));
return expr;
}
- if (!decl.name().equals(ident.name())) {
+
+ // Preserve leading dot to signal runtime to bypass local scopes.
+ String refName = decl.name();
+ if (ident.name().startsWith(".")) {
+ refName = "." + refName;
+ }
+
+ if (!refName.equals(ident.name())) {
// Overwrite the identifier with its fully qualified name.
- expr = replaceIdentSubtree(expr, decl.name());
+ expr = replaceIdentSubtree(expr, refName);
}
env.setType(expr, decl.type());
- env.setRef(expr, makeReference(decl));
+ // Build reference with the (potentially prefixed) name, preserving constant value if present.
+ CelReference.Builder refBuilder = CelReference.newBuilder().setName(refName);
+ if (decl.constant().isPresent()) {
+ refBuilder.setValue(decl.constant().get());
+ }
+ env.setRef(expr, refBuilder.build());
return expr;
}
@@ -260,13 +272,24 @@ private CelExpr visit(CelExpr expr, CelExpr.CelSelect select) {
env.reportError(expr.id(), getPosition(expr), "expression does not select a field");
env.setType(expr, SimpleType.BOOL);
} else {
+ // Preserve leading dot to signal runtime to bypass local scopes.
+ String refName = decl.name();
+ if (qname.startsWith(".")) {
+ refName = "." + refName;
+ }
+
if (namespacedDeclarations) {
// Rewrite the node to be a variable reference to the resolved fully-qualified
// variable name.
- expr = replaceIdentSubtree(expr, decl.name());
+ expr = replaceIdentSubtree(expr, refName);
}
env.setType(expr, decl.type());
- env.setRef(expr, makeReference(decl));
+ // Build reference with the (potentially prefixed) name, preserving constant value.
+ CelReference.Builder refBuilder = CelReference.newBuilder().setName(refName);
+ if (decl.constant().isPresent()) {
+ refBuilder.setValue(decl.constant().get());
+ }
+ env.setRef(expr, refBuilder.build());
}
return expr;
}
diff --git a/runtime/src/main/java/dev/cel/runtime/RuntimeUnknownResolver.java b/runtime/src/main/java/dev/cel/runtime/RuntimeUnknownResolver.java
index b28729417..f14e75dd7 100644
--- a/runtime/src/main/java/dev/cel/runtime/RuntimeUnknownResolver.java
+++ b/runtime/src/main/java/dev/cel/runtime/RuntimeUnknownResolver.java
@@ -98,6 +98,11 @@ Optional