cedar_policy_core/evaluator/err.rs
1/*
2 * Copyright 2022-2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use crate::ast::*;
18use smol_str::SmolStr;
19use std::sync::Arc;
20use thiserror::Error;
21
22/// Errors that can occur during evaluation
23#[derive(Debug, PartialEq, Clone, Error)]
24pub enum EvaluationError {
25 /// Tried to lookup this entity UID, but it didn't exist in the provided
26 /// entities
27 #[error("entity does not exist: {0}")]
28 EntityDoesNotExist(Arc<EntityUID>),
29
30 /// Tried to get this attribute, but the specified entity didn't
31 /// have that attribute
32 #[error("`{}` does not have the attribute: {}", &.entity, &.attr)]
33 EntityAttrDoesNotExist {
34 /// Entity that didn't have the attribute
35 entity: Arc<EntityUID>,
36 /// Name of the attribute it didn't have
37 attr: SmolStr,
38 },
39
40 /// Tried to access an attribute of an unspecified entity
41 #[error("cannot access attribute of unspecified entity: {0}")]
42 UnspecifiedEntityAccess(SmolStr),
43
44 /// Tried to get an attribute of a (non-entity) record, but that record
45 /// didn't have that attribute
46 #[error("record does not have the attribute: {0}. Available attributes: {1:?}")]
47 RecordAttrDoesNotExist(SmolStr, Vec<SmolStr>),
48
49 /// An error occurred when looking up an extension function
50 #[error(transparent)]
51 FailedExtensionFunctionLookup(#[from] crate::extensions::ExtensionsError),
52
53 /// Tried to evaluate an operation on values with incorrect types for that
54 /// operation
55 // INVARIANT `expected` must be non-empty
56 #[error("{}", pretty_type_error(expected, actual))]
57 TypeError {
58 /// Expected (one of) these types
59 expected: Vec<Type>,
60 /// Encountered this type instead
61 actual: Type,
62 },
63
64 /// Wrong number of arguments provided to an extension function
65 #[error("wrong number of arguments provided to extension function {function_name}: expected {expected}, got {actual}")]
66 WrongNumArguments {
67 /// arguments to this function
68 function_name: Name,
69 /// expected number of arguments
70 expected: usize,
71 /// actual number of arguments
72 actual: usize,
73 },
74
75 /// Overflow during an integer operation
76 #[error(transparent)]
77 IntegerOverflow(#[from] IntegerOverflowError),
78
79 /// Error with the use of "restricted" expressions
80 #[error(transparent)]
81 InvalidRestrictedExpression(#[from] RestrictedExpressionError),
82
83 /// Thrown when a policy is evaluated with a slot that is not linked to an
84 /// [`EntityUID`]
85 #[error("template slot `{0}` was not linked")]
86 UnlinkedSlot(SlotId),
87
88 /// Evaluation error thrown by an extension function
89 #[error("error while evaluating {extension_name} extension function: {msg}")]
90 FailedExtensionFunctionApplication {
91 /// Name of the extension throwing the error
92 extension_name: Name,
93 /// Error message from the extension
94 msg: String,
95 },
96
97 /// This error is raised if an expression contains unknowns and cannot be
98 /// reduced to a [`Value`]. In order to return partial results, use the
99 /// partial evaluation APIs instead.
100 #[error("the expression contains unknown(s) (consider using the partial evaluation API): {0}")]
101 NonValue(Expr),
102
103 /// Maximum recursion limit reached for expression evaluation
104 #[error("recursion limit reached")]
105 RecursionLimit,
106}
107
108/// helper function for pretty-printing type errors
109/// INVARIANT: `expected` must have at least one value
110fn pretty_type_error(expected: &[Type], actual: &Type) -> String {
111 match expected.len() {
112 // PANIC SAFETY, `expected` is non-empty by invariant
113 #[allow(clippy::unreachable)]
114 0 => unreachable!("should expect at least one type"),
115 // PANIC SAFETY. `len` is 1 in this branch
116 #[allow(clippy::indexing_slicing)]
117 1 => format!("type error: expected {}, got {}", expected[0], actual),
118 _ => {
119 use itertools::Itertools;
120 format!(
121 "type error: expected one of [{}], got {actual}",
122 expected.iter().join(", ")
123 )
124 }
125 }
126}
127
128#[derive(Debug, PartialEq, Clone, Error)]
129pub enum IntegerOverflowError {
130 #[error("integer overflow while attempting to {} the values {arg1} and {arg2}", match .op { BinaryOp::Add => "add", BinaryOp::Sub => "subtract", _ => "perform an operation on" })]
131 BinaryOp {
132 /// overflow while evaluating this operator
133 op: BinaryOp,
134 /// first argument to that operator
135 arg1: Value,
136 /// second argument to that operator
137 arg2: Value,
138 },
139
140 #[error("integer overflow while attempting to multiply {arg} by {constant}")]
141 Multiplication {
142 /// first argument, which wasn't necessarily a constant in the policy
143 arg: Value,
144 /// second argument, which was a constant in the policy
145 constant: i64,
146 },
147
148 /// Overflow during an integer negation operation
149 #[error("integer overflow while attempting to {} the value {arg}", match .op { UnaryOp::Neg => "negate", _ => "perform an operation on" })]
150 UnaryOp {
151 /// overflow while evaluating this operator
152 op: UnaryOp,
153 /// argument to that operator
154 arg: Value,
155 },
156}
157
158/// Type alias for convenience
159pub type Result<T> = std::result::Result<T, EvaluationError>;