Skip to content

Commit 1cf21ee

Browse files
jnthntatumcopybara-github
authored andcommitted
Optionally track structured error messages in parser.
Add a parse overload that reports issues with location to an output parameter where reasonable. This is used make the error handling more consistent when using bundled parse + typecheck. PiperOrigin-RevId: 910112013
1 parent 9e73d93 commit 1cf21ee

9 files changed

Lines changed: 186 additions & 64 deletions

File tree

compiler/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,12 @@ cc_library(
4242
hdrs = ["compiler_factory.h"],
4343
deps = [
4444
":compiler",
45+
"//checker:type_check_issue",
4546
"//checker:type_checker",
4647
"//checker:type_checker_builder",
4748
"//checker:type_checker_builder_factory",
4849
"//checker:validation_result",
50+
"//common:ast",
4951
"//common:source",
5052
"//internal:noop_delete",
5153
"//internal:status_macros",

compiler/compiler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ struct CompilerLibrarySubset {
9797
struct CompilerOptions {
9898
ParserOptions parser_options;
9999
CheckerOptions checker_options;
100+
// If true, parse errors will be adapted to issues where possible.
101+
bool adapt_parser_errors = false;
100102
};
101103

102104
// Interface for CEL CompilerBuilder objects.

compiler/compiler_factory.cc

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,19 @@
1717
#include <memory>
1818
#include <string>
1919
#include <utility>
20+
#include <vector>
2021

2122
#include "absl/container/flat_hash_set.h"
2223
#include "absl/status/status.h"
2324
#include "absl/status/statusor.h"
2425
#include "absl/strings/str_cat.h"
2526
#include "absl/strings/string_view.h"
27+
#include "checker/type_check_issue.h"
2628
#include "checker/type_checker.h"
2729
#include "checker/type_checker_builder.h"
2830
#include "checker/type_checker_builder_factory.h"
2931
#include "checker/validation_result.h"
32+
#include "common/ast.h"
3033
#include "common/source.h"
3134
#include "compiler/compiler.h"
3235
#include "internal/status_macros.h"
@@ -45,19 +48,38 @@ class CompilerImpl : public Compiler {
4548
CompilerImpl(std::unique_ptr<TypeChecker> type_checker,
4649
std::unique_ptr<Parser> parser,
4750
// Copy the validator in case builder is reused.
48-
Validator validator)
51+
Validator validator, CompilerOptions options)
4952
: type_checker_(std::move(type_checker)),
5053
parser_(std::move(parser)),
51-
validator_(std::move(validator)) {}
54+
validator_(std::move(validator)),
55+
options_(options) {}
5256

5357
absl::StatusOr<ValidationResult> Compile(
5458
absl::string_view expression, absl::string_view description,
5559
google::protobuf::Arena* arena) const override {
5660
CEL_ASSIGN_OR_RETURN(auto source,
5761
cel::NewSource(expression, std::string(description)));
58-
CEL_ASSIGN_OR_RETURN(auto ast, parser_->Parse(*source));
62+
std::vector<cel::ParseIssue> parse_issues;
63+
absl::StatusOr<std::unique_ptr<cel::Ast>> ast =
64+
parser_->Parse(*source, &parse_issues);
65+
if (!ast.ok()) {
66+
if (!options_.adapt_parser_errors ||
67+
ast.status().code() != absl::StatusCode::kInvalidArgument ||
68+
parse_issues.empty()) {
69+
return ast.status();
70+
}
71+
std::vector<TypeCheckIssue> check_issues;
72+
check_issues.reserve(parse_issues.size());
73+
for (const auto& issue : parse_issues) {
74+
check_issues.push_back(TypeCheckIssue::CreateError(
75+
issue.location(), std::string(issue.message())));
76+
}
77+
ValidationResult result(std::move(check_issues));
78+
result.SetSource(std::move(source));
79+
return result;
80+
}
5981
CEL_ASSIGN_OR_RETURN(ValidationResult result,
60-
type_checker_->Check(std::move(ast), arena));
82+
type_checker_->Check(*std::move(ast), arena));
6183

6284
result.SetSource(std::move(source));
6385
if (!validator_.validations().empty()) {
@@ -76,16 +98,18 @@ class CompilerImpl : public Compiler {
7698
std::unique_ptr<TypeChecker> type_checker_;
7799
std::unique_ptr<Parser> parser_;
78100
Validator validator_;
101+
CompilerOptions options_;
79102
};
80103

81104
class CompilerBuilderImpl : public CompilerBuilder {
82105
public:
83106
CompilerBuilderImpl(std::unique_ptr<TypeCheckerBuilder> type_checker_builder,
84107
std::unique_ptr<ParserBuilder> parser_builder,
85-
Validator validator = Validator())
108+
Validator validator, CompilerOptions options)
86109
: type_checker_builder_(std::move(type_checker_builder)),
87110
parser_builder_(std::move(parser_builder)),
88-
validator_(std::move(validator)) {}
111+
validator_(std::move(validator)),
112+
options_(options) {}
89113

90114
absl::Status AddLibrary(CompilerLibrary library) override {
91115
if (!library.id.empty()) {
@@ -146,23 +170,23 @@ class CompilerBuilderImpl : public CompilerBuilder {
146170
absl::StatusOr<std::unique_ptr<Compiler>> Build() override {
147171
CEL_ASSIGN_OR_RETURN(auto parser, parser_builder_->Build());
148172
CEL_ASSIGN_OR_RETURN(auto type_checker, type_checker_builder_->Build());
149-
return std::make_unique<CompilerImpl>(std::move(type_checker),
150-
std::move(parser), validator_);
173+
return std::make_unique<CompilerImpl>(
174+
std::move(type_checker), std::move(parser), validator_, options_);
151175
}
152176

153177
private:
154178
std::unique_ptr<TypeCheckerBuilder> type_checker_builder_;
155179
std::unique_ptr<ParserBuilder> parser_builder_;
156180
Validator validator_;
181+
CompilerOptions options_;
157182

158183
absl::flat_hash_set<std::string> library_ids_;
159184
absl::flat_hash_set<std::string> subsets_;
160185
};
161186

162187
std::unique_ptr<CompilerBuilder> CompilerImpl::ToBuilder() const {
163-
auto builder = std::make_unique<CompilerBuilderImpl>(
164-
type_checker_->ToBuilder(), parser_->ToBuilder(), validator_);
165-
return builder;
188+
return std::make_unique<CompilerBuilderImpl>(
189+
type_checker_->ToBuilder(), parser_->ToBuilder(), validator_, options_);
166190
}
167191

168192
} // namespace
@@ -179,7 +203,8 @@ absl::StatusOr<std::unique_ptr<CompilerBuilder>> NewCompilerBuilder(
179203
auto parser_builder = NewParserBuilder(options.parser_options);
180204

181205
return std::make_unique<CompilerBuilderImpl>(std::move(type_checker_builder),
182-
std::move(parser_builder));
206+
std::move(parser_builder),
207+
Validator(), options);
183208
}
184209

185210
} // namespace cel

compiler/compiler_factory_test.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,5 +413,19 @@ TEST(CompilerFactoryTest, SpecifyArenaKeepsResolvedTypes) {
413413
it->second.GetOptional().GetParameter().GetList().GetElement().IsInt());
414414
}
415415

416+
TEST(CompilerFactoryTest, ReturnsIssuesFromParser) {
417+
CompilerOptions opts;
418+
opts.adapt_parser_errors = true;
419+
ASSERT_OK_AND_ASSIGN(
420+
auto builder, NewCompilerBuilder(
421+
cel::internal::GetSharedTestingDescriptorPool(), opts));
422+
423+
ASSERT_OK_AND_ASSIGN(auto compiler, builder->Build());
424+
425+
ASSERT_OK_AND_ASSIGN(ValidationResult result, compiler->Compile("a +"));
426+
EXPECT_FALSE(result.IsValid());
427+
EXPECT_THAT(result.GetIssues(), testing::Not(testing::IsEmpty()));
428+
}
429+
416430
} // namespace
417431
} // namespace cel

extensions/math_ext_test.cc

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
#include "absl/algorithm/container.h"
2424
#include "absl/status/status.h"
2525
#include "absl/status/status_matchers.h"
26-
#include "absl/strings/str_cat.h"
2726
#include "absl/strings/string_view.h"
2827
#include "absl/types/optional.h"
2928
#include "absl/types/span.h"
@@ -110,19 +109,6 @@ struct MacroTestCase {
110109
absl::string_view err = "";
111110
};
112111

113-
std::string FormatIssues(const cel::ValidationResult& result) {
114-
std::string issues;
115-
for (const auto& issue : result.GetIssues()) {
116-
if (!issues.empty()) {
117-
absl::StrAppend(&issues, "\n",
118-
issue.ToDisplayString(*result.GetSource()));
119-
} else {
120-
issues = issue.ToDisplayString(*result.GetSource());
121-
}
122-
}
123-
return issues;
124-
}
125-
126112
class TestFunction : public CelFunction {
127113
public:
128114
explicit TestFunction(absl::string_view name)
@@ -352,10 +338,11 @@ TEST_P(MathExtMacroParamsTest, ParserTests) {
352338

353339
TEST_P(MathExtMacroParamsTest, ParserAndCheckerTests) {
354340
const MacroTestCase& test_case = GetParam();
355-
356-
ASSERT_OK_AND_ASSIGN(
357-
auto compiler_builder,
358-
cel::NewCompilerBuilder(internal::GetTestingDescriptorPool()));
341+
CompilerOptions compile_opts;
342+
compile_opts.adapt_parser_errors = true;
343+
ASSERT_OK_AND_ASSIGN(auto compiler_builder,
344+
cel::NewCompilerBuilder(
345+
internal::GetTestingDescriptorPool(), compile_opts));
359346

360347
ASSERT_THAT(compiler_builder->AddLibrary(StandardCheckerLibrary()), IsOk());
361348
ASSERT_THAT(compiler_builder->AddLibrary(MathCompilerLibrary()), IsOk());
@@ -381,16 +368,16 @@ TEST_P(MathExtMacroParamsTest, ParserAndCheckerTests) {
381368

382369
ASSERT_OK_AND_ASSIGN(auto compiler, std::move(*compiler_builder).Build());
383370

384-
auto result = compiler->Compile(test_case.expr, "<input>");
371+
ASSERT_OK_AND_ASSIGN(auto result,
372+
compiler->Compile(test_case.expr, "<input>"));
385373

386374
if (!test_case.err.empty()) {
387-
EXPECT_THAT(result.status(), StatusIs(absl::StatusCode::kInvalidArgument,
388-
HasSubstr(test_case.err)));
375+
EXPECT_FALSE(result.IsValid());
376+
EXPECT_THAT(result.FormatError(), HasSubstr(test_case.err));
389377
return;
390378
}
391379

392-
ASSERT_THAT(result, IsOk());
393-
ASSERT_TRUE(result->IsValid()) << FormatIssues(*result);
380+
ASSERT_TRUE(result.IsValid()) << result.FormatError();
394381

395382
RuntimeOptions opts;
396383
ASSERT_OK_AND_ASSIGN(
@@ -411,9 +398,8 @@ TEST_P(MathExtMacroParamsTest, ParserAndCheckerTests) {
411398
IsOk());
412399

413400
ASSERT_OK_AND_ASSIGN(auto runtime, std::move(runtime_builder).Build());
414-
415-
ASSERT_OK_AND_ASSIGN(auto program,
416-
runtime->CreateProgram(*result->ReleaseAst()));
401+
ASSERT_OK_AND_ASSIGN(auto ast, result.ReleaseAst());
402+
ASSERT_OK_AND_ASSIGN(auto program, runtime->CreateProgram(std::move(ast)));
417403

418404
google::protobuf::Arena arena;
419405
cel::Activation activation;

parser/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,9 +244,11 @@ cc_library(
244244
":options",
245245
"//common:ast",
246246
"//common:source",
247+
"@com_google_absl//absl/base:nullability",
247248
"@com_google_absl//absl/functional:any_invocable",
248249
"@com_google_absl//absl/status",
249250
"@com_google_absl//absl/status:statusor",
251+
"@com_google_absl//absl/strings:string_view",
250252
],
251253
)
252254

0 commit comments

Comments
 (0)