Skip to content

Commit 8868650

Browse files
jnthntatumcopybara-github
authored andcommitted
Add option for using protobuf WKTs as context decl in type checking.
PiperOrigin-RevId: 761662037
1 parent de85fb1 commit 8868650

File tree

5 files changed

+125
-1
lines changed

5 files changed

+125
-1
lines changed

checker/BUILD

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ cc_test(
118118
name = "type_checker_builder_factory_test",
119119
srcs = ["type_checker_builder_factory_test.cc"],
120120
deps = [
121+
":checker_options",
122+
":standard_library",
123+
":type_checker",
121124
":type_checker_builder",
122125
":type_checker_builder_factory",
123126
":validation_result",

checker/checker_options.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,19 @@ struct CheckerOptions {
4343
// as parsed.
4444
bool update_struct_type_names = true;
4545

46+
// Well-known types defined by protobuf are treated specially in CEL, and
47+
// generally don't behave like other messages as runtime values. When used as
48+
// context declarations, this introduces some ambiguity about the intended
49+
// types of the field declarations, so it is disallowed by default.
50+
//
51+
// When enabled, the well-known types are treated like a normal message type
52+
// for the purposes for declaring context bindings (i.e no unpacking or
53+
// adapting), and use the Descriptor that is assumed by CEL.
54+
//
55+
// E.g. for google.protobuf.Any, the type checker will add a context binding
56+
// with `type_url: string` and `value: bytes` as top level variables.
57+
bool allow_well_known_type_context_declarations = false;
58+
4659
// Maximum number (inclusive) of expression nodes to check for an input
4760
// expression.
4861
//

checker/internal/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ cc_test(
197197
deps = [
198198
":test_ast_helpers",
199199
":type_checker_impl",
200+
"//checker:checker_options",
201+
"//checker:standard_library",
200202
"//checker:type_checker",
201203
"//checker:validation_result",
202204
"//common:decl",

checker/internal/type_checker_builder_impl.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,8 @@ absl::Status TypeCheckerBuilderImpl::AddContextDeclaration(
287287
absl::StrCat("context declaration '", type, "' not found"));
288288
}
289289

290-
if (IsWellKnownMessageType(desc)) {
290+
if (IsWellKnownMessageType(desc) &&
291+
!options_.allow_well_known_type_context_declarations) {
291292
return absl::InvalidArgumentError(
292293
absl::StrCat("context declaration '", type, "' is not a struct"));
293294
}

checker/type_checker_builder_factory_test.cc

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@
2121
#include "absl/status/status.h"
2222
#include "absl/status/status_matchers.h"
2323
#include "absl/strings/string_view.h"
24+
#include "checker/checker_options.h"
2425
#include "checker/internal/test_ast_helpers.h"
26+
#include "checker/standard_library.h"
27+
#include "checker/type_checker.h"
2528
#include "checker/type_checker_builder.h"
2629
#include "checker/validation_result.h"
2730
#include "common/decl.h"
@@ -391,6 +394,108 @@ TEST(TypeCheckerBuilderTest, AddContextDeclaration) {
391394
EXPECT_TRUE(result.IsValid());
392395
}
393396

397+
TEST(TypeCheckerBuilderTest, WellKnownTypeContextDeclarationError) {
398+
ASSERT_OK_AND_ASSIGN(
399+
std::unique_ptr<TypeCheckerBuilder> builder,
400+
CreateTypeCheckerBuilder(GetSharedTestingDescriptorPool()));
401+
402+
ASSERT_THAT(builder->AddContextDeclaration("google.protobuf.Any"),
403+
StatusIs(absl::StatusCode::kInvalidArgument,
404+
HasSubstr("'google.protobuf.Any' is not a struct")));
405+
}
406+
407+
TEST(TypeCheckerBuilderTest, AllowWellKnownTypeContextDeclaration) {
408+
CheckerOptions options;
409+
options.allow_well_known_type_context_declarations = true;
410+
ASSERT_OK_AND_ASSIGN(
411+
std::unique_ptr<TypeCheckerBuilder> builder,
412+
CreateTypeCheckerBuilder(GetSharedTestingDescriptorPool(), options));
413+
414+
ASSERT_THAT(builder->AddContextDeclaration("google.protobuf.Any"), IsOk());
415+
ASSERT_THAT(builder->AddLibrary(StandardCheckerLibrary()), IsOk());
416+
417+
ASSERT_OK_AND_ASSIGN(std::unique_ptr<TypeChecker> type_checker,
418+
builder->Build());
419+
ASSERT_OK_AND_ASSIGN(
420+
auto ast,
421+
MakeTestParsedAst(
422+
R"cel(value == b'' && type_url == 'type.googleapis.com/google.protobuf.Duration')cel"));
423+
ASSERT_OK_AND_ASSIGN(ValidationResult result,
424+
type_checker->Check(std::move(ast)));
425+
426+
ASSERT_TRUE(result.IsValid());
427+
}
428+
429+
TEST(TypeCheckerBuilderTest, AllowWellKnownTypeContextDeclarationStruct) {
430+
CheckerOptions options;
431+
options.allow_well_known_type_context_declarations = true;
432+
ASSERT_OK_AND_ASSIGN(
433+
std::unique_ptr<TypeCheckerBuilder> builder,
434+
CreateTypeCheckerBuilder(GetSharedTestingDescriptorPool(), options));
435+
436+
ASSERT_THAT(builder->AddContextDeclaration("google.protobuf.Struct"), IsOk());
437+
ASSERT_THAT(builder->AddLibrary(StandardCheckerLibrary()), IsOk());
438+
439+
ASSERT_OK_AND_ASSIGN(std::unique_ptr<TypeChecker> type_checker,
440+
builder->Build());
441+
ASSERT_OK_AND_ASSIGN(
442+
auto ast,
443+
MakeTestParsedAst(R"cel(fields.foo.bar_list.exists(x, x == 1))cel"));
444+
ASSERT_OK_AND_ASSIGN(ValidationResult result,
445+
type_checker->Check(std::move(ast)));
446+
447+
ASSERT_TRUE(result.IsValid());
448+
}
449+
450+
TEST(TypeCheckerBuilderTest, AllowWellKnownTypeContextDeclarationValue) {
451+
CheckerOptions options;
452+
options.allow_well_known_type_context_declarations = true;
453+
ASSERT_OK_AND_ASSIGN(
454+
std::unique_ptr<TypeCheckerBuilder> builder,
455+
CreateTypeCheckerBuilder(GetSharedTestingDescriptorPool(), options));
456+
457+
ASSERT_THAT(builder->AddContextDeclaration("google.protobuf.Value"), IsOk());
458+
ASSERT_THAT(builder->AddLibrary(StandardCheckerLibrary()), IsOk());
459+
460+
ASSERT_OK_AND_ASSIGN(std::unique_ptr<TypeChecker> type_checker,
461+
builder->Build());
462+
ASSERT_OK_AND_ASSIGN(
463+
auto ast, MakeTestParsedAst(
464+
// Note: one of fields are all added with safe traversal, so
465+
// we lose the union discriminator information.
466+
R"cel(
467+
null_value == null &&
468+
number_value == 0.0 &&
469+
string_value == '' &&
470+
list_value == [] &&
471+
struct_value == {} &&
472+
bool_value == false)cel"));
473+
ASSERT_OK_AND_ASSIGN(ValidationResult result,
474+
type_checker->Check(std::move(ast)));
475+
476+
ASSERT_TRUE(result.IsValid());
477+
}
478+
479+
TEST(TypeCheckerBuilderTest, AllowWellKnownTypeContextDeclarationInt64Value) {
480+
CheckerOptions options;
481+
options.allow_well_known_type_context_declarations = true;
482+
ASSERT_OK_AND_ASSIGN(
483+
std::unique_ptr<TypeCheckerBuilder> builder,
484+
CreateTypeCheckerBuilder(GetSharedTestingDescriptorPool(), options));
485+
486+
ASSERT_THAT(builder->AddContextDeclaration("google.protobuf.Int64Value"),
487+
IsOk());
488+
ASSERT_THAT(builder->AddLibrary(StandardCheckerLibrary()), IsOk());
489+
490+
ASSERT_OK_AND_ASSIGN(std::unique_ptr<TypeChecker> type_checker,
491+
builder->Build());
492+
ASSERT_OK_AND_ASSIGN(auto ast, MakeTestParsedAst(R"cel(value == 0)cel"));
493+
ASSERT_OK_AND_ASSIGN(ValidationResult result,
494+
type_checker->Check(std::move(ast)));
495+
496+
ASSERT_TRUE(result.IsValid());
497+
}
498+
394499
TEST(TypeCheckerBuilderTest, AddLibraryRedeclaredError) {
395500
ASSERT_OK_AND_ASSIGN(
396501
std::unique_ptr<TypeCheckerBuilder> builder,

0 commit comments

Comments
 (0)