@@ -522,7 +522,7 @@ public void trace_shortCircuitingDisabledWithUnknownsAndedToFalse_returnsFalse(S
522522 (expr , res ) -> {
523523 if (expr .constantOrDefault ().getKind ().equals (CelConstant .Kind .BOOLEAN_VALUE )
524524 || expr .identOrDefault ().name ().equals ("x" )) {
525- if (InterpreterUtil . isUnknown ( res ) ) {
525+ if (res instanceof CelUnknownSet ) {
526526 branchResults .add ("x" ); // Swap unknown result with a sentinel value for testing
527527 } else {
528528 branchResults .add (res );
@@ -577,7 +577,7 @@ public void trace_shortCircuitingDisabledWithUnknownAndedToTrue_returnsUnknown(S
577577 PartialVars partialVars = PartialVars .of (CelAttributePattern .create ("x" ));
578578 Object unknownResult = cel .createProgram (ast ).trace (partialVars , listener );
579579
580- assertThat (InterpreterUtil . isUnknown ( unknownResult )). isTrue ( );
580+ assertThat (unknownResult ). isInstanceOf ( CelUnknownSet . class );
581581 assertThat (branchResults .build ()).containsExactly (true , true , unknownResult );
582582 }
583583
@@ -653,7 +653,7 @@ public void trace_shortCircuitingDisabledWithUnknownsOredToFalse_returnsUnknown(
653653 PartialVars partialVars = PartialVars .of (CelAttributePattern .create ("x" ));
654654 Object unknownResult = cel .createProgram (ast ).trace (partialVars , listener );
655655
656- assertThat (InterpreterUtil . isUnknown ( unknownResult )). isTrue ( );
656+ assertThat (unknownResult ). isInstanceOf ( CelUnknownSet . class );
657657 assertThat (branchResults .build ()).containsExactly (false , false , unknownResult );
658658 }
659659
@@ -668,7 +668,7 @@ public void trace_shortCircuitingDisabledWithUnknownOredToTrue_returnsTrue(Strin
668668 (expr , res ) -> {
669669 if (expr .constantOrDefault ().getKind ().equals (CelConstant .Kind .BOOLEAN_VALUE )
670670 || expr .identOrDefault ().name ().equals ("x" )) {
671- if (InterpreterUtil . isUnknown ( res ) ) {
671+ if (res instanceof CelUnknownSet ) {
672672 branchResults .add ("x" ); // Swap unknown result with a sentinel value for testing
673673 } else {
674674 branchResults .add (res );
@@ -748,7 +748,7 @@ public void trace_shortCircuitingDisabled_ternaryWithUnknowns(String source) thr
748748 PartialVars partialVars = PartialVars .of (CelAttributePattern .create ("x" ));
749749 Object unknownResult = cel .createProgram (ast ).trace (partialVars , listener );
750750
751- assertThat (InterpreterUtil . isUnknown ( unknownResult )). isTrue ( );
751+ assertThat (unknownResult ). isInstanceOf ( CelUnknownSet . class );
752752 assertThat (branchResults .build ()).containsExactly (false , unknownResult , true );
753753 }
754754
@@ -944,4 +944,151 @@ public void trace_shortCircuitingDisabled_logicalOrPrefersFirstError() throws Ex
944944 CelEvaluationException e = assertThrows (CelEvaluationException .class , () -> program .eval ());
945945 assertThat (e ).hasCauseThat ().hasMessageThat ().contains ("error 1" );
946946 }
947+
948+ @ Test
949+ public void evaluate_customFunctionReturningCelUnknownSet_propagatesUnknown (
950+ @ TestParameter ({
951+ // Field selection
952+ "getMsg().single_int32" ,
953+ "getMsg().single_nested_message.bb" ,
954+ // Binary & unary operators
955+ "getMsg().single_int32 == 100" ,
956+ "getMsg().single_int32 + 5 == 10" ,
957+ "-getMsg().single_int32 == -10" ,
958+ // Boolean operators & ternary
959+ "true && (getMsg().single_int32 == 100)" ,
960+ "false || (getMsg().single_int32 == 100)" ,
961+ "(getMsg().single_int32 == 100) ? 'match' : 'no-match'" ,
962+ // Comprehensions
963+ "[1, 2, 3].exists(x, x == getMsg().single_int32)" ,
964+ "[1, 2, 3].all(x, x > 0 && getMsg().single_int32 > 0)" ,
965+ "[1, 2, 3].map(x, x + getMsg().single_int32)" ,
966+ "[1, 2, 3].filter(x, x == getMsg().single_int32)" ,
967+ })
968+ String expression )
969+ throws Exception {
970+ Cel cel =
971+ runtimeFlavor
972+ .builder ()
973+ .setStandardMacros (CelStandardMacro .STANDARD_MACROS )
974+ .addMessageTypes (TestAllTypes .getDescriptor ())
975+ .addFunctionDeclarations (
976+ CelFunctionDecl .newFunctionDeclaration (
977+ "getMsg" ,
978+ CelOverloadDecl .newGlobalOverload (
979+ "getMsg_overload" ,
980+ StructTypeReference .create (TestAllTypes .getDescriptor ().getFullName ()),
981+ ImmutableList .of ())))
982+ .addFunctionBindings (
983+ CelFunctionBinding .from (
984+ "getMsg_overload" ,
985+ ImmutableList .of (),
986+ args -> CelUnknownSet .create (CelAttribute .create ("custom_msg" ))))
987+ .build ();
988+
989+ Object result = cel .createProgram (cel .compile (expression ).getAst ()).eval ();
990+
991+ assertThat (result ).isInstanceOf (CelUnknownSet .class );
992+ }
993+
994+ @ Test
995+ // Short-circuited boolean operators
996+ @ TestParameters ("{expression: 'false && (getMsg().single_int32 == 100)', expected: false}" )
997+ @ TestParameters ("{expression: 'true || (getMsg().single_int32 == 100)', expected: true}" )
998+ // Short-circuited comprehensions
999+ @ TestParameters (
1000+ "{expression: '[1, 2, 3].exists(x, x == 1 || x == getMsg().single_int32)', expected: true}" )
1001+ @ TestParameters (
1002+ "{expression: '[1, 2, 3].all(x, x == 0 && getMsg().single_int32 > 0)', expected: false}" )
1003+ public void evaluate_customFunctionReturningCelUnknownSet_shortCircuits (
1004+ String expression , boolean expected ) throws Exception {
1005+ Cel cel =
1006+ runtimeFlavor
1007+ .builder ()
1008+ .setStandardMacros (CelStandardMacro .STANDARD_MACROS )
1009+ .addMessageTypes (TestAllTypes .getDescriptor ())
1010+ .addFunctionDeclarations (
1011+ CelFunctionDecl .newFunctionDeclaration (
1012+ "getMsg" ,
1013+ CelOverloadDecl .newGlobalOverload (
1014+ "getMsg_overload" ,
1015+ StructTypeReference .create (TestAllTypes .getDescriptor ().getFullName ()),
1016+ ImmutableList .of ())))
1017+ .addFunctionBindings (
1018+ CelFunctionBinding .from (
1019+ "getMsg_overload" ,
1020+ ImmutableList .of (),
1021+ args -> CelUnknownSet .create (CelAttribute .create ("custom_msg" ))))
1022+ .build ();
1023+
1024+ Object result = cel .createProgram (cel .compile (expression ).getAst ()).eval ();
1025+
1026+ assertThat (result ).isEqualTo (expected );
1027+ }
1028+
1029+ @ Test
1030+ public void evaluate_customFunctionReturningCelUnknownSet_differentArities () throws Exception {
1031+ Cel cel =
1032+ runtimeFlavor
1033+ .builder ()
1034+ .addFunctionDeclarations (
1035+ CelFunctionDecl .newFunctionDeclaration (
1036+ "unkZero" ,
1037+ CelOverloadDecl .newGlobalOverload (
1038+ "unk_zero" , SimpleType .INT , ImmutableList .of ())),
1039+ CelFunctionDecl .newFunctionDeclaration (
1040+ "unkUnary" ,
1041+ CelOverloadDecl .newGlobalOverload ("unk_unary" , SimpleType .INT , SimpleType .INT )),
1042+ CelFunctionDecl .newFunctionDeclaration (
1043+ "unkBinary" ,
1044+ CelOverloadDecl .newGlobalOverload (
1045+ "unk_binary" , SimpleType .INT , SimpleType .INT , SimpleType .INT )),
1046+ CelFunctionDecl .newFunctionDeclaration (
1047+ "unkMember" ,
1048+ CelOverloadDecl .newMemberOverload (
1049+ "unk_member" , SimpleType .INT , SimpleType .STRING , SimpleType .INT )),
1050+ CelFunctionDecl .newFunctionDeclaration (
1051+ "unkVarargs" ,
1052+ CelOverloadDecl .newGlobalOverload (
1053+ "unk_varargs" ,
1054+ SimpleType .INT ,
1055+ SimpleType .INT ,
1056+ SimpleType .INT ,
1057+ SimpleType .INT )))
1058+ .addFunctionBindings (
1059+ CelFunctionBinding .from (
1060+ "unk_zero" ,
1061+ ImmutableList .of (),
1062+ args -> CelUnknownSet .create (CelAttribute .create ("attr_zero" ))),
1063+ CelFunctionBinding .from (
1064+ "unk_unary" ,
1065+ Long .class ,
1066+ arg -> CelUnknownSet .create (CelAttribute .create ("attr_unary" ))),
1067+ CelFunctionBinding .from (
1068+ "unk_binary" ,
1069+ Long .class ,
1070+ Long .class ,
1071+ (a , b ) -> CelUnknownSet .create (CelAttribute .create ("attr_binary" ))),
1072+ CelFunctionBinding .from (
1073+ "unk_member" ,
1074+ String .class ,
1075+ Long .class ,
1076+ (target , arg ) -> CelUnknownSet .create (CelAttribute .create ("attr_member" ))),
1077+ CelFunctionBinding .from (
1078+ "unk_varargs" ,
1079+ ImmutableList .of (Long .class , Long .class , Long .class ),
1080+ args -> CelUnknownSet .create (CelAttribute .create ("attr_varargs" ))))
1081+ .build ();
1082+
1083+ assertThat (cel .createProgram (cel .compile ("unkZero() + 1" ).getAst ()).eval ())
1084+ .isEqualTo (CelUnknownSet .create (CelAttribute .create ("attr_zero" )));
1085+ assertThat (cel .createProgram (cel .compile ("unkUnary(1) + 1" ).getAst ()).eval ())
1086+ .isEqualTo (CelUnknownSet .create (CelAttribute .create ("attr_unary" )));
1087+ assertThat (cel .createProgram (cel .compile ("unkBinary(1, 2) + 1" ).getAst ()).eval ())
1088+ .isEqualTo (CelUnknownSet .create (CelAttribute .create ("attr_binary" )));
1089+ assertThat (cel .createProgram (cel .compile ("'target'.unkMember(1) + 1" ).getAst ()).eval ())
1090+ .isEqualTo (CelUnknownSet .create (CelAttribute .create ("attr_member" )));
1091+ assertThat (cel .createProgram (cel .compile ("unkVarargs(1, 2, 3) + 1" ).getAst ()).eval ())
1092+ .isEqualTo (CelUnknownSet .create (CelAttribute .create ("attr_varargs" )));
1093+ }
9471094}
0 commit comments