51
51
#include " common/source.h"
52
52
#include " common/type.h"
53
53
#include " common/type_kind.h"
54
+ #include " internal/annotations.h"
54
55
#include " internal/status_macros.h"
55
56
#include " google/protobuf/arena.h"
56
57
@@ -68,6 +69,10 @@ std::string FormatCandidate(absl::Span<const std::string> qualifiers) {
68
69
return absl::StrJoin (qualifiers, " ." );
69
70
}
70
71
72
+ static const TraversalOptions kTraversalOptions = {
73
+ /* .use_comprehension_callbacks=*/ true ,
74
+ };
75
+
71
76
SourceLocation ComputeSourceLocation (const AstImpl& ast, int64_t expr_id) {
72
77
const auto & source_info = ast.source_info ();
73
78
auto iter = source_info.positions ().find (expr_id);
@@ -235,6 +240,8 @@ absl::StatusOr<AstType> FlattenType(const Type& type) {
235
240
}
236
241
}
237
242
243
+ using AnnotationMap = internal::AnnotationMap;
244
+
238
245
class ResolveVisitor : public AstVisitorBase {
239
246
public:
240
247
struct FunctionResolution {
@@ -247,13 +254,17 @@ class ResolveVisitor : public AstVisitorBase {
247
254
const TypeCheckEnv& env, const AstImpl& ast,
248
255
TypeInferenceContext& inference_context,
249
256
std::vector<TypeCheckIssue>& issues,
257
+ AnnotationMap& annotations,
258
+ cel::CheckerAnnotationSupport annotation_support,
250
259
absl::Nonnull<google::protobuf::Arena*> arena)
251
260
: container_(container),
261
+ annotation_support_ (annotation_support),
252
262
namespace_generator_(std::move(namespace_generator)),
253
263
env_(&env),
254
264
inference_context_(&inference_context),
255
265
issues_(&issues),
256
266
ast_(&ast),
267
+ annotations_(&annotations),
257
268
root_scope_(env.MakeVariableScope()),
258
269
arena_(arena),
259
270
current_scope_(&root_scope_) {}
@@ -265,6 +276,43 @@ class ResolveVisitor : public AstVisitorBase {
265
276
return ;
266
277
}
267
278
expr_stack_.pop_back ();
279
+ if (!status_.ok ()) {
280
+ return ;
281
+ }
282
+ if (annotation_support_ != CheckerAnnotationSupport::kCheck ) {
283
+ return ;
284
+ }
285
+ auto annotations = annotations_->find (expr.id ());
286
+ if (annotations == annotations_->end ()) {
287
+ return ;
288
+ }
289
+ if (annotation_context_.has_value ()) {
290
+ issues_->push_back (
291
+ TypeCheckIssue::CreateError (ComputeSourceLocation (*ast_, expr.id ()),
292
+ " Nested annotations are not supported." ));
293
+ return ;
294
+ }
295
+ auto annotation_scope = current_scope_->MakeNestedScope ();
296
+ VariableScope* annotation_scope_ptr = annotation_scope.get ();
297
+ // bit of a misuse, but annotation scope is largely the same as for
298
+ // comprehensions.
299
+ comprehension_vars_.push_back (std::move (annotation_scope));
300
+
301
+ annotation_context_ = {current_scope_};
302
+ current_scope_ = annotation_scope_ptr;
303
+ Type annotated_expr_type = GetDeducedType (&expr);
304
+ annotation_scope_ptr->InsertVariableIfAbsent (
305
+ MakeVariableDecl (" cel.annotated_value" , annotated_expr_type));
306
+
307
+ // Note: this does not need to happen now during the main traversal, but
308
+ // it's a easier to reason about for me. It's equally valid to just record
309
+ // the relevant annotations and do a separate check pass later.
310
+ for (const auto & annotation : annotations->second ) {
311
+ CheckAnnotation (annotation, expr, annotated_expr_type);
312
+ }
313
+
314
+ current_scope_ = annotation_context_->parent ;
315
+ annotation_context_.reset ();
268
316
}
269
317
270
318
void PostVisitConst (const Expr& expr, const Constant& constant) override ;
@@ -341,6 +389,10 @@ class ResolveVisitor : public AstVisitorBase {
341
389
const FunctionDecl* decl;
342
390
};
343
391
392
+ struct AnnotationContext {
393
+ const VariableScope* parent;
394
+ };
395
+
344
396
void ResolveSimpleIdentifier (const Expr& expr, absl::string_view name);
345
397
346
398
void ResolveQualifiedIdentifier (const Expr& expr,
@@ -459,12 +511,17 @@ class ResolveVisitor : public AstVisitorBase {
459
511
return DynType ();
460
512
}
461
513
514
+ void CheckAnnotation (const internal::AnnotationRep& annotation_expr,
515
+ const Expr& annotated_expr, const Type& annotated_type);
516
+
462
517
absl::string_view container_;
518
+ CheckerAnnotationSupport annotation_support_;
463
519
NamespaceGenerator namespace_generator_;
464
520
absl::Nonnull<const TypeCheckEnv*> env_;
465
521
absl::Nonnull<TypeInferenceContext*> inference_context_;
466
522
absl::Nonnull<std::vector<TypeCheckIssue>*> issues_;
467
523
absl::Nonnull<const ast_internal::AstImpl*> ast_;
524
+ absl::Nonnull<AnnotationMap*> annotations_;
468
525
VariableScope root_scope_;
469
526
absl::Nonnull<google::protobuf::Arena*> arena_;
470
527
@@ -479,6 +536,7 @@ class ResolveVisitor : public AstVisitorBase {
479
536
absl::flat_hash_set<const Expr*> deferred_select_operations_;
480
537
std::vector<std::unique_ptr<VariableScope>> comprehension_vars_;
481
538
std::vector<ComprehensionScope> comprehension_scopes_;
539
+ absl::optional<AnnotationContext> annotation_context_;
482
540
absl::Status status_;
483
541
int error_count_ = 0 ;
484
542
@@ -535,6 +593,64 @@ void ResolveVisitor::PostVisitIdent(const Expr& expr, const IdentExpr& ident) {
535
593
}
536
594
}
537
595
596
+ void ResolveVisitor::CheckAnnotation (const internal::AnnotationRep& annotation,
597
+ const Expr& annotated_expr,
598
+ const Type& annotated_type) {
599
+ const auto * annotation_decl = env_->LookupAnnotation (annotation.name );
600
+ if (annotation_decl == nullptr ) {
601
+ ReportIssue (TypeCheckIssue::CreateError (
602
+ ComputeSourceLocation (*ast_, annotated_expr.id ()),
603
+ absl::StrCat (" undefined annotation '" , annotation.name , " '" )));
604
+ return ;
605
+ }
606
+
607
+ // Checking if assignable to Dyn may influence the type inference so skip
608
+ // here.
609
+ if (!annotation_decl->applicable_type ().IsDyn ()) {
610
+ if (!inference_context_->IsAssignable (annotated_type,
611
+ annotation_decl->applicable_type ())) {
612
+ ReportIssue (TypeCheckIssue::CreateError (
613
+ ComputeSourceLocation (*ast_, annotated_expr.id ()),
614
+ absl::StrCat (
615
+ " annotation '" , annotation.name , " ' is not applicable to type '" ,
616
+ inference_context_->FinalizeType (annotated_type).DebugString (),
617
+ " '" )));
618
+ return ;
619
+ }
620
+ }
621
+
622
+ if (annotation.inspect_only ) {
623
+ // Nothing to do -- the value expression is not intended to be evaluated.
624
+ // Examples are for things like a pointer to another file if the
625
+ // subexpression is inlined from somewhere else.
626
+ return ;
627
+ }
628
+
629
+ // TODO - re-entrant traversal bypasses the complexity limits.
630
+ AstTraverse (*annotation.value_expr , *this , kTraversalOptions );
631
+
632
+ if (!status_.ok ()) {
633
+ return ;
634
+ }
635
+
636
+ Type value_expression_type = GetDeducedType (annotation.value_expr );
637
+
638
+ if (!annotation_decl->expected_type ().IsDyn ()) {
639
+ if (!inference_context_->IsAssignable (value_expression_type,
640
+ annotation_decl->expected_type ())) {
641
+ ReportIssue (TypeCheckIssue::CreateError (
642
+ ComputeSourceLocation (*ast_, annotated_expr.id ()),
643
+ absl::StrCat (" annotation '" , annotation.name ,
644
+ " ' value expression type '" ,
645
+ inference_context_->FinalizeType (value_expression_type)
646
+ .DebugString (),
647
+ " ' is not assignable to '" ,
648
+ annotation_decl->expected_type ().DebugString (), " '" )));
649
+ return ;
650
+ }
651
+ }
652
+ }
653
+
538
654
void ResolveVisitor::PostVisitConst (const Expr& expr,
539
655
const Constant& constant) {
540
656
switch (constant.kind ().index ()) {
@@ -1276,13 +1392,28 @@ absl::StatusOr<ValidationResult> TypeCheckerImpl::Check(
1276
1392
1277
1393
TypeInferenceContext type_inference_context (
1278
1394
&type_arena, options_.enable_legacy_null_assignment );
1395
+
1396
+ internal::AnnotationMap annotation_exprs;
1397
+ Expr* root = &ast_impl.root_expr ();
1398
+ if (ast_impl.root_expr ().has_call_expr () &&
1399
+ ast_impl.root_expr ().call_expr ().function () == " cel.@annotated" &&
1400
+ ast_impl.root_expr ().call_expr ().args ().size () == 2 ) {
1401
+ if (options_.annotation_support == CheckerAnnotationSupport::kStrip ) {
1402
+ ast_impl.root_expr () =
1403
+ std::move (ast_impl.root_expr ().mutable_call_expr ().mutable_args ()[0 ]);
1404
+ root = &ast_impl.root_expr ();
1405
+ } else {
1406
+ annotation_exprs = internal::BuildAnnotationMap (ast_impl);
1407
+ root = &ast_impl.root_expr ().mutable_call_expr ().mutable_args ()[0 ];
1408
+ }
1409
+ }
1410
+
1279
1411
ResolveVisitor visitor (env_.container (), std::move (generator), env_, ast_impl,
1280
- type_inference_context, issues, &type_arena);
1412
+ type_inference_context, issues, annotation_exprs,
1413
+ options_.annotation_support , &type_arena);
1281
1414
1282
- TraversalOptions opts;
1283
- opts.use_comprehension_callbacks = true ;
1284
1415
bool error_limit_reached = false ;
1285
- auto traversal = AstTraversal::Create (ast_impl. root_expr (), opts );
1416
+ auto traversal = AstTraversal::Create (*root, kTraversalOptions );
1286
1417
1287
1418
for (int step = 0 ; step < options_.max_expression_node_count * 2 ; ++step) {
1288
1419
bool has_next = traversal.Step (visitor);
@@ -1300,7 +1431,7 @@ absl::StatusOr<ValidationResult> TypeCheckerImpl::Check(
1300
1431
1301
1432
if (!traversal.IsDone () && !error_limit_reached) {
1302
1433
return absl::InvalidArgumentError (
1303
- absl::StrCat (" Maximum expression node count exceeded: " ,
1434
+ absl::StrCat (" maximum expression node count exceeded: " ,
1304
1435
options_.max_expression_node_count ));
1305
1436
}
1306
1437
@@ -1309,7 +1440,7 @@ absl::StatusOr<ValidationResult> TypeCheckerImpl::Check(
1309
1440
{}, absl::StrCat (" maximum number of ERROR issues exceeded: " ,
1310
1441
options_.max_error_issues )));
1311
1442
} else if (env_.expected_type ().has_value ()) {
1312
- visitor.AssertExpectedType (ast_impl. root_expr () , *env_.expected_type ());
1443
+ visitor.AssertExpectedType (*root , *env_.expected_type ());
1313
1444
}
1314
1445
1315
1446
// If any issues are errors, return without an AST.
@@ -1324,7 +1455,14 @@ absl::StatusOr<ValidationResult> TypeCheckerImpl::Check(
1324
1455
// been invalidated by other updates.
1325
1456
ResolveRewriter rewriter (visitor, type_inference_context, options_,
1326
1457
ast_impl.reference_map (), ast_impl.type_map ());
1327
- AstRewrite (ast_impl.root_expr (), rewriter);
1458
+ AstRewrite (*root, rewriter);
1459
+ if (options_.annotation_support == CheckerAnnotationSupport::kCheck ) {
1460
+ for (auto & annotations : annotation_exprs) {
1461
+ for (auto & annotation : annotations.second ) {
1462
+ AstRewrite (*annotation.value_expr , rewriter);
1463
+ }
1464
+ }
1465
+ }
1328
1466
1329
1467
CEL_RETURN_IF_ERROR (rewriter.status ());
1330
1468
0 commit comments