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>;