Skip to content

Commit ecdf7a0

Browse files
jnthntatumcopybara-github
authored andcommitted
Add AST depth validator.
PiperOrigin-RevId: 893731697
1 parent 0820886 commit ecdf7a0

14 files changed

+923
-3
lines changed

compiler/BUILD

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ cc_library(
2727
"//checker:validation_result",
2828
"//parser:options",
2929
"//parser:parser_interface",
30+
"//validator",
3031
"@com_google_absl//absl/status",
3132
"@com_google_absl//absl/status:statusor",
3233
"@com_google_absl//absl/strings:string_view",
@@ -48,6 +49,7 @@ cc_library(
4849
"//internal:status_macros",
4950
"//parser",
5051
"//parser:parser_interface",
52+
"//validator",
5153
"@com_google_absl//absl/base:nullability",
5254
"@com_google_absl//absl/container:flat_hash_set",
5355
"@com_google_absl//absl/status",
@@ -78,6 +80,7 @@ cc_test(
7880
"//parser:macro",
7981
"//parser:parser_interface",
8082
"//testutil:baseline_tests",
83+
"//validator:timestamp_literal_validator",
8184
"@com_google_absl//absl/status",
8285
"@com_google_absl//absl/status:status_matchers",
8386
"@com_google_absl//absl/strings",

compiler/compiler.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "checker/validation_result.h"
2929
#include "parser/options.h"
3030
#include "parser/parser_interface.h"
31+
#include "validator/validator.h"
3132

3233
namespace cel {
3334

@@ -109,6 +110,7 @@ class CompilerBuilder {
109110

110111
virtual TypeCheckerBuilder& GetCheckerBuilder() = 0;
111112
virtual ParserBuilder& GetParserBuilder() = 0;
113+
virtual Validator& GetValidator() = 0;
112114

113115
virtual absl::StatusOr<std::unique_ptr<Compiler>> Build() = 0;
114116
};
@@ -135,6 +137,9 @@ class Compiler {
135137

136138
// Accessor for the underlying parser.
137139
virtual const Parser& GetParser() const = 0;
140+
141+
// Accessor for the underlying validator.
142+
virtual const Validator& GetValidator() const = 0;
138143
};
139144

140145
} // namespace cel

compiler/compiler_factory.cc

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "internal/status_macros.h"
3333
#include "parser/parser.h"
3434
#include "parser/parser_interface.h"
35+
#include "validator/validator.h"
3536
#include "google/protobuf/descriptor.h"
3637

3738
namespace cel {
@@ -41,8 +42,12 @@ namespace {
4142
class CompilerImpl : public Compiler {
4243
public:
4344
CompilerImpl(std::unique_ptr<TypeChecker> type_checker,
44-
std::unique_ptr<Parser> parser)
45-
: type_checker_(std::move(type_checker)), parser_(std::move(parser)) {}
45+
std::unique_ptr<Parser> parser,
46+
// Copy the validator in case builder is reused.
47+
Validator validator)
48+
: type_checker_(std::move(type_checker)),
49+
parser_(std::move(parser)),
50+
validator_(std::move(validator)) {}
4651

4752
absl::StatusOr<ValidationResult> Compile(
4853
absl::string_view expression,
@@ -54,15 +59,20 @@ class CompilerImpl : public Compiler {
5459
type_checker_->Check(std::move(ast)));
5560

5661
result.SetSource(std::move(source));
62+
if (!validator_.validations().empty()) {
63+
validator_.UpdateValidationResult(result);
64+
}
5765
return result;
5866
}
5967

6068
const TypeChecker& GetTypeChecker() const override { return *type_checker_; }
6169
const Parser& GetParser() const override { return *parser_; }
70+
const Validator& GetValidator() const override { return validator_; }
6271

6372
private:
6473
std::unique_ptr<TypeChecker> type_checker_;
6574
std::unique_ptr<Parser> parser_;
75+
Validator validator_;
6676
};
6777

6878
class CompilerBuilderImpl : public CompilerBuilder {
@@ -126,17 +136,19 @@ class CompilerBuilderImpl : public CompilerBuilder {
126136
TypeCheckerBuilder& GetCheckerBuilder() override {
127137
return *type_checker_builder_;
128138
}
139+
Validator& GetValidator() override { return validator_; }
129140

130141
absl::StatusOr<std::unique_ptr<Compiler>> Build() override {
131142
CEL_ASSIGN_OR_RETURN(auto parser, parser_builder_->Build());
132143
CEL_ASSIGN_OR_RETURN(auto type_checker, type_checker_builder_->Build());
133144
return std::make_unique<CompilerImpl>(std::move(type_checker),
134-
std::move(parser));
145+
std::move(parser), validator_);
135146
}
136147

137148
private:
138149
std::unique_ptr<TypeCheckerBuilder> type_checker_builder_;
139150
std::unique_ptr<ParserBuilder> parser_builder_;
151+
Validator validator_;
140152

141153
absl::flat_hash_set<std::string> library_ids_;
142154
absl::flat_hash_set<std::string> subsets_;

compiler/compiler_factory_test.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "parser/macro.h"
3636
#include "parser/parser_interface.h"
3737
#include "testutil/baseline_tests.h"
38+
#include "validator/timestamp_literal_validator.h"
3839
#include "google/protobuf/descriptor.h"
3940

4041
namespace cel {
@@ -287,6 +288,23 @@ TEST(CompilerFactoryTest, DisableStandardMacrosWithStdlib) {
287288
EXPECT_TRUE(result.IsValid());
288289
}
289290

291+
TEST(CompilerFactoryTest, AddValidator) {
292+
ASSERT_OK_AND_ASSIGN(
293+
auto builder,
294+
NewCompilerBuilder(cel::internal::GetSharedTestingDescriptorPool()));
295+
296+
ASSERT_THAT(builder->AddLibrary(StandardCompilerLibrary()), IsOk());
297+
builder->GetValidator().AddValidation(TimestampLiteralValidator());
298+
299+
ASSERT_OK_AND_ASSIGN(auto compiler, builder->Build());
300+
ASSERT_OK_AND_ASSIGN(ValidationResult result,
301+
compiler->Compile("timestamp('invalid')"));
302+
EXPECT_FALSE(result.IsValid());
303+
ASSERT_OK_AND_ASSIGN(result,
304+
compiler->Compile("timestamp('2024-01-01T00:00:00Z')"));
305+
EXPECT_TRUE(result.IsValid());
306+
}
307+
290308
TEST(CompilerFactoryTest, FailsIfLibraryAddedTwice) {
291309
ASSERT_OK_AND_ASSIGN(
292310
auto builder,

validator/BUILD

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
load("@rules_cc//cc:cc_library.bzl", "cc_library")
16+
load("@rules_cc//cc:cc_test.bzl", "cc_test")
17+
18+
package(default_visibility = ["//visibility:public"])
19+
20+
cc_library(
21+
name = "validator",
22+
srcs = ["validator.cc"],
23+
hdrs = ["validator.h"],
24+
deps = [
25+
"//checker:type_check_issue",
26+
"//checker:validation_result",
27+
"//common:ast",
28+
"//common:navigable_ast",
29+
"//common:source",
30+
"@com_google_absl//absl/base:core_headers",
31+
"@com_google_absl//absl/functional:any_invocable",
32+
"@com_google_absl//absl/log:absl_check",
33+
"@com_google_absl//absl/strings:string_view",
34+
"@com_google_absl//absl/types:span",
35+
],
36+
)
37+
38+
cc_test(
39+
name = "validator_test",
40+
srcs = ["validator_test.cc"],
41+
deps = [
42+
":validator",
43+
"//checker:type_check_issue",
44+
"//common:ast",
45+
"//common:expr",
46+
"//common:source",
47+
"//internal:testing",
48+
"@com_google_absl//absl/strings:string_view",
49+
],
50+
)
51+
52+
cc_test(
53+
name = "timestamp_literal_validator_test",
54+
srcs = ["timestamp_literal_validator_test.cc"],
55+
deps = [
56+
":timestamp_literal_validator",
57+
":validator",
58+
"//checker:validation_result",
59+
"//compiler",
60+
"//compiler:compiler_factory",
61+
"//compiler:standard_library",
62+
"//internal:testing",
63+
"//internal:testing_descriptor_pool",
64+
"@com_google_absl//absl/status",
65+
"@com_google_absl//absl/status:statusor",
66+
"@com_google_absl//absl/strings:string_view",
67+
],
68+
)
69+
70+
cc_library(
71+
name = "timestamp_literal_validator",
72+
srcs = ["timestamp_literal_validator.cc"],
73+
hdrs = ["timestamp_literal_validator.h"],
74+
deps = [
75+
":validator",
76+
"//common:constant",
77+
"//common:navigable_ast",
78+
"//common:standard_definitions",
79+
"//internal:time",
80+
"//tools:navigable_ast",
81+
"@com_google_absl//absl/base:no_destructor",
82+
"@com_google_absl//absl/status",
83+
"@com_google_absl//absl/strings",
84+
"@com_google_absl//absl/strings:string_view",
85+
"@com_google_absl//absl/time",
86+
],
87+
)
88+
89+
cc_library(
90+
name = "ast_depth_validator",
91+
srcs = ["ast_depth_validator.cc"],
92+
hdrs = ["ast_depth_validator.h"],
93+
deps = [
94+
":validator",
95+
"@com_google_absl//absl/strings",
96+
],
97+
)
98+
99+
cc_test(
100+
name = "ast_depth_validator_test",
101+
srcs = ["ast_depth_validator_test.cc"],
102+
deps = [
103+
":ast_depth_validator",
104+
":validator",
105+
"//checker:type_check_issue",
106+
"//compiler",
107+
"//compiler:compiler_factory",
108+
"//compiler:standard_library",
109+
"//internal:testing",
110+
"//internal:testing_descriptor_pool",
111+
],
112+
)
113+
114+
licenses(["notice"])

validator/ast_depth_validator.cc

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "validator/ast_depth_validator.h"
16+
17+
#include "absl/strings/str_cat.h"
18+
#include "validator/validator.h"
19+
20+
namespace cel {
21+
22+
Validation AstDepthValidator(int max_depth) {
23+
return Validation([max_depth](ValidationContext& context) {
24+
int height = context.navigable_ast().Root().height();
25+
if (height > max_depth) {
26+
context.ReportError(absl::StrCat("AST depth ", height,
27+
" exceeds maximum of ", max_depth));
28+
return false;
29+
}
30+
return true;
31+
});
32+
}
33+
34+
} // namespace cel

validator/ast_depth_validator.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef THIRD_PARTY_CEL_CPP_VALIDATOR_AST_DEPTH_VALIDATOR_H_
16+
#define THIRD_PARTY_CEL_CPP_VALIDATOR_AST_DEPTH_VALIDATOR_H_
17+
#include "validator/validator.h"
18+
19+
namespace cel {
20+
21+
// Returns a `Validation` that checks the AST depth is less than or equal to
22+
// max_depth.
23+
Validation AstDepthValidator(int max_depth);
24+
25+
} // namespace cel
26+
27+
#endif // THIRD_PARTY_CEL_CPP_VALIDATOR_AST_DEPTH_VALIDATOR_H_

0 commit comments

Comments
 (0)