2626#include " absl/log/absl_check.h"
2727#include " absl/status/status.h"
2828#include " absl/status/status_matchers.h"
29+ #include " absl/status/statusor.h"
2930#include " absl/strings/match.h"
3031#include " absl/strings/str_cat.h"
3132#include " absl/strings/str_join.h"
3637#include " checker/type_checker_builder.h"
3738#include " checker/validation_result.h"
3839#include " common/ast.h"
40+ #include " common/ast_proto.h"
3941#include " common/container.h"
4042#include " common/decl.h"
4143#include " common/expr.h"
4547#include " internal/status_macros.h"
4648#include " internal/testing.h"
4749#include " internal/testing_descriptor_pool.h"
50+ #include " parser/macro_registry.h"
51+ #include " parser/parser.h"
4852#include " testutil/baseline_tests.h"
53+ #include " testutil/test_macros.h"
4954#include " cel/expr/conformance/proto2/test_all_types.pb.h"
5055#include " cel/expr/conformance/proto3/test_all_types.pb.h"
5156#include " google/protobuf/arena.h"
@@ -108,6 +113,17 @@ google::protobuf::Arena* absl_nonnull TestTypeArena() {
108113 return &(*kArena );
109114}
110115
116+ absl::StatusOr<std::unique_ptr<Ast>> MakeTestParsedAstWithMacros (
117+ absl::string_view expression, const cel::MacroRegistry& registry) {
118+ CEL_ASSIGN_OR_RETURN (
119+ auto source,
120+ cel::NewSource (expression, /* description=*/ std::string (expression)));
121+ CEL_ASSIGN_OR_RETURN (auto parsed_expr, google::api::expr::parser::Parse (
122+ *source, registry,
123+ {.enable_optional_syntax = true }));
124+ return cel::CreateAstFromParsedExpr (parsed_expr);
125+ }
126+
111127FunctionDecl MakeIdentFunction () {
112128 auto decl = MakeFunctionDecl (
113129 " identity" ,
@@ -272,6 +288,12 @@ absl::Status RegisterMinimalBuiltins(google::protobuf::Arena* absl_nonnull arena
272288 /* return_type=*/ TypeType (arena, TypeParamType (" A" )),
273289 TypeParamType (" A" ))));
274290
291+ Type kParam (TypeParamType (" T" ));
292+ CEL_ASSIGN_OR_RETURN (
293+ auto block_decl,
294+ MakeFunctionDecl (" cel.@block" , MakeOverloadDecl (" cel_block_list" , kParam ,
295+ ListType (), kParam )));
296+
275297 env.InsertFunctionIfAbsent (std::move (not_op));
276298 env.InsertFunctionIfAbsent (std::move (not_strictly_false));
277299 env.InsertFunctionIfAbsent (std::move (add_op));
@@ -289,6 +311,7 @@ absl::Status RegisterMinimalBuiltins(google::protobuf::Arena* absl_nonnull arena
289311 env.InsertFunctionIfAbsent (std::move (to_type));
290312 env.InsertFunctionIfAbsent (std::move (to_duration));
291313 env.InsertFunctionIfAbsent (std::move (to_timestamp));
314+ env.InsertFunctionIfAbsent (std::move (block_decl));
292315
293316 return absl::OkStatus ();
294317}
@@ -308,6 +331,78 @@ TEST(TypeCheckerImplTest, SmokeTest) {
308331 EXPECT_THAT (result.GetIssues (), IsEmpty ());
309332}
310333
334+ TEST (TypeCheckerImplTest, BlockMacroSupport) {
335+ TypeCheckEnv env (GetSharedTestingDescriptorPool ());
336+
337+ google::protobuf::Arena arena;
338+ ASSERT_THAT (RegisterMinimalBuiltins (&arena, env), IsOk ());
339+
340+ MacroRegistry registry;
341+ ASSERT_THAT (cel::test::RegisterTestMacros (registry), IsOk ());
342+
343+ TypeCheckerImpl impl (std::move (env));
344+ ASSERT_OK_AND_ASSIGN (
345+ auto ast,
346+ MakeTestParsedAstWithMacros (
347+ " cel.block([1, 2], cel.index(0) + cel.index(1))" , registry));
348+ ASSERT_OK_AND_ASSIGN (ValidationResult result, impl.Check (std::move (ast)));
349+
350+ EXPECT_TRUE (result.IsValid ());
351+ EXPECT_THAT (result.GetIssues (), IsEmpty ());
352+
353+ // Overall type should be int.
354+ ASSERT_OK_AND_ASSIGN (auto checked_ast, result.ReleaseAst ());
355+ auto root_id = checked_ast->root_expr ().id ();
356+ EXPECT_EQ (checked_ast->type_map ().at (root_id).primitive (),
357+ PrimitiveType::kInt64 );
358+ }
359+
360+ TEST (TypeCheckerImplTest, BlockMacroSupportMixedTypes) {
361+ TypeCheckEnv env (GetSharedTestingDescriptorPool ());
362+
363+ google::protobuf::Arena arena;
364+ ASSERT_THAT (RegisterMinimalBuiltins (&arena, env), IsOk ());
365+
366+ MacroRegistry registry;
367+ ASSERT_THAT (cel::test::RegisterTestMacros (registry), IsOk ());
368+
369+ TypeCheckerImpl impl (std::move (env));
370+ ASSERT_OK_AND_ASSIGN (
371+ auto ast, MakeTestParsedAstWithMacros (" cel.block([1, 'a'], cel.index(1))" ,
372+ registry));
373+ ASSERT_OK_AND_ASSIGN (ValidationResult result, impl.Check (std::move (ast)));
374+
375+ EXPECT_TRUE (result.IsValid ());
376+ EXPECT_THAT (result.GetIssues (), IsEmpty ());
377+
378+ // cel.index(1) refers to 'a' which is string.
379+ // So overall type should be string.
380+ ASSERT_OK_AND_ASSIGN (auto checked_ast, result.ReleaseAst ());
381+ auto root_id = checked_ast->root_expr ().id ();
382+ EXPECT_EQ (checked_ast->type_map ().at (root_id).primitive (),
383+ PrimitiveType::kString );
384+ }
385+
386+ TEST (TypeCheckerImplTest, BadIndex) {
387+ TypeCheckEnv env (GetSharedTestingDescriptorPool ());
388+
389+ google::protobuf::Arena arena;
390+ ASSERT_THAT (RegisterMinimalBuiltins (&arena, env), IsOk ());
391+
392+ MacroRegistry registry;
393+ ASSERT_THAT (cel::test::RegisterTestMacros (registry), IsOk ());
394+
395+ TypeCheckerImpl impl (std::move (env));
396+ ASSERT_OK_AND_ASSIGN (
397+ auto ast, MakeTestParsedAstWithMacros (" cel.block([1, 'a'], cel.index(2))" ,
398+ registry));
399+ ASSERT_OK_AND_ASSIGN (ValidationResult result, impl.Check (std::move (ast)));
400+
401+ EXPECT_FALSE (result.IsValid ());
402+ EXPECT_THAT (result.FormatError (),
403+ HasSubstr (" undeclared reference to '@index2' (in container" ));
404+ }
405+
311406TEST (TypeCheckerImplTest, SimpleIdentsResolved) {
312407 TypeCheckEnv env (GetSharedTestingDescriptorPool ());
313408
0 commit comments