cedar_policy_core/evaluator/
err.rs

1/*
2 * Copyright Cedar Contributors
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 crate::extensions::ExtensionFunctionLookupError;
19use crate::parser::Loc;
20use miette::Diagnostic;
21use nonempty::{nonempty, NonEmpty};
22use smol_str::SmolStr;
23use std::sync::Arc;
24use thiserror::Error;
25
26// How many attrs or tags will we store in an error before cutting off for performance reason
27const TOO_MANY_ATTRS: usize = 5;
28
29/// Enumeration of the possible errors that can occur during evaluation
30//
31// CAUTION: this type is publicly exported in `cedar-policy`.
32// Don't make fields `pub`, don't make breaking changes, and use caution when
33// adding public methods.
34#[derive(Debug, PartialEq, Eq, Clone, Diagnostic, Error)]
35pub enum EvaluationError {
36    /// Tried to lookup an entity UID, but it didn't exist in the provided
37    /// entities
38    #[error(transparent)]
39    #[diagnostic(transparent)]
40    EntityDoesNotExist(#[from] evaluation_errors::EntityDoesNotExistError),
41
42    /// Tried to get an attribute or tag, but the specified entity didn't
43    /// have that attribute or tag
44    #[error(transparent)]
45    #[diagnostic(transparent)]
46    EntityAttrDoesNotExist(#[from] evaluation_errors::EntityAttrDoesNotExistError),
47
48    /// Tried to get an attribute of a (non-entity) record, but that record
49    /// didn't have that attribute
50    #[error(transparent)]
51    #[diagnostic(transparent)]
52    RecordAttrDoesNotExist(#[from] evaluation_errors::RecordAttrDoesNotExistError),
53
54    /// An error occurred when looking up an extension function
55    #[error(transparent)]
56    #[diagnostic(transparent)]
57    FailedExtensionFunctionLookup(#[from] ExtensionFunctionLookupError),
58
59    /// Tried to evaluate an operation on values with incorrect types for that
60    /// operation
61    #[error(transparent)]
62    #[diagnostic(transparent)]
63    TypeError(#[from] evaluation_errors::TypeError),
64
65    /// Wrong number of arguments provided to an extension function
66    #[error(transparent)]
67    #[diagnostic(transparent)]
68    WrongNumArguments(#[from] evaluation_errors::WrongNumArgumentsError),
69
70    /// Overflow during an integer operation
71    #[error(transparent)]
72    #[diagnostic(transparent)]
73    IntegerOverflow(#[from] evaluation_errors::IntegerOverflowError),
74
75    /// Not all template slots were linked
76    #[error(transparent)]
77    #[diagnostic(transparent)]
78    UnlinkedSlot(#[from] evaluation_errors::UnlinkedSlotError),
79
80    /// Evaluation error thrown by an extension function
81    #[error(transparent)]
82    #[diagnostic(transparent)]
83    FailedExtensionFunctionExecution(#[from] evaluation_errors::ExtensionFunctionExecutionError),
84
85    /// This error is raised if an expression contains unknowns and cannot be
86    /// reduced to a [`Value`]. In order to return partial results, use the
87    /// partial evaluation APIs instead.
88    #[error(transparent)]
89    #[diagnostic(transparent)]
90    NonValue(#[from] evaluation_errors::NonValueError),
91
92    /// Trying to evaluate an expression AST node that gets generated when parsing fails
93    #[cfg(feature = "tolerant-ast")]
94    #[error(transparent)]
95    #[diagnostic(transparent)]
96    ASTErrorExpr(#[from] evaluation_errors::ASTErrorExprError),
97
98    /// Maximum recursion limit reached for expression evaluation
99    #[error(transparent)]
100    #[diagnostic(transparent)]
101    RecursionLimit(#[from] evaluation_errors::RecursionLimitError),
102}
103
104impl EvaluationError {
105    /// Extract the source location of the error, if one is attached
106    pub(crate) fn source_loc(&self) -> Option<&Loc> {
107        match self {
108            Self::EntityDoesNotExist(e) => e.source_loc.as_ref(),
109            Self::EntityAttrDoesNotExist(e) => e.source_loc.as_ref(),
110            Self::RecordAttrDoesNotExist(e) => e.source_loc.as_ref(),
111            Self::FailedExtensionFunctionLookup(e) => e.source_loc(),
112            Self::TypeError(e) => e.source_loc.as_ref(),
113            Self::WrongNumArguments(e) => e.source_loc.as_ref(),
114            Self::IntegerOverflow(e) => e.source_loc(),
115            Self::UnlinkedSlot(e) => e.source_loc.as_ref(),
116            Self::FailedExtensionFunctionExecution(e) => e.source_loc.as_ref(),
117            Self::NonValue(e) => e.source_loc.as_ref(),
118            Self::RecursionLimit(e) => e.source_loc.as_ref(),
119            #[cfg(feature = "tolerant-ast")]
120            Self::ASTErrorExpr(e) => e.source_loc.as_ref(),
121        }
122    }
123
124    /// Return the `EvaluationError`, but with the new `source_loc` (or `None`).
125    pub(crate) fn with_maybe_source_loc(self, source_loc: Option<Loc>) -> Self {
126        match self {
127            Self::EntityDoesNotExist(e) => {
128                Self::EntityDoesNotExist(evaluation_errors::EntityDoesNotExistError {
129                    source_loc,
130                    ..e
131                })
132            }
133            Self::EntityAttrDoesNotExist(e) => {
134                Self::EntityAttrDoesNotExist(evaluation_errors::EntityAttrDoesNotExistError {
135                    source_loc,
136                    ..e
137                })
138            }
139            Self::RecordAttrDoesNotExist(e) => {
140                Self::RecordAttrDoesNotExist(evaluation_errors::RecordAttrDoesNotExistError {
141                    source_loc,
142                    ..e
143                })
144            }
145            Self::FailedExtensionFunctionLookup(e) => {
146                Self::FailedExtensionFunctionLookup(e.with_maybe_source_loc(source_loc))
147            }
148            Self::TypeError(e) => Self::TypeError(evaluation_errors::TypeError { source_loc, ..e }),
149            Self::WrongNumArguments(e) => {
150                Self::WrongNumArguments(evaluation_errors::WrongNumArgumentsError {
151                    source_loc,
152                    ..e
153                })
154            }
155            Self::IntegerOverflow(e) => Self::IntegerOverflow(e.with_maybe_source_loc(source_loc)),
156            Self::UnlinkedSlot(e) => {
157                Self::UnlinkedSlot(evaluation_errors::UnlinkedSlotError { source_loc, ..e })
158            }
159            Self::FailedExtensionFunctionExecution(e) => Self::FailedExtensionFunctionExecution(
160                evaluation_errors::ExtensionFunctionExecutionError { source_loc, ..e },
161            ),
162            Self::NonValue(e) => {
163                Self::NonValue(evaluation_errors::NonValueError { source_loc, ..e })
164            }
165            Self::RecursionLimit(_) => {
166                Self::RecursionLimit(evaluation_errors::RecursionLimitError { source_loc })
167            }
168            #[cfg(feature = "tolerant-ast")]
169            Self::ASTErrorExpr(_) => {
170                Self::ASTErrorExpr(evaluation_errors::ASTErrorExprError { source_loc })
171            }
172        }
173    }
174
175    /// Construct a [`EntityDoesNotExist`] error
176    pub(crate) fn entity_does_not_exist(uid: Arc<EntityUID>, source_loc: Option<Loc>) -> Self {
177        evaluation_errors::EntityDoesNotExistError { uid, source_loc }.into()
178    }
179
180    /// Construct a [`EntityAttrDoesNotExist`] error
181    ///
182    /// `does_attr_exist_as_a_tag`: does `attr` exist on `entity` as a tag (rather than an attribute)
183    pub(crate) fn entity_attr_does_not_exist<'a>(
184        entity: Arc<EntityUID>,
185        attr: SmolStr,
186        available_attrs: impl IntoIterator<Item = &'a SmolStr>,
187        does_attr_exist_as_a_tag: bool,
188        total_attrs: usize,
189        source_loc: Option<Loc>,
190    ) -> Self {
191        evaluation_errors::EntityAttrDoesNotExistError {
192            entity,
193            attr_or_tag: attr,
194            was_attr: true,
195            exists_the_other_kind: does_attr_exist_as_a_tag,
196            available_attrs_or_tags: available_attrs
197                .into_iter()
198                .take(TOO_MANY_ATTRS)
199                .cloned()
200                .collect::<Vec<_>>(),
201            total_attrs_or_tags: total_attrs,
202            source_loc,
203        }
204        .into()
205    }
206
207    /// Construct an error for the case where an entity tag does not exist
208    ///
209    /// `does_tag_exist_as_an_attr`: does `tag` exist on `entity` as an attribute (rather than a tag)
210    pub(crate) fn entity_tag_does_not_exist<'a>(
211        entity: Arc<EntityUID>,
212        tag: SmolStr,
213        available_tags: impl IntoIterator<Item = &'a SmolStr>,
214        does_tag_exist_as_an_attr: bool,
215        total_tags: usize,
216        source_loc: Option<Loc>,
217    ) -> Self {
218        evaluation_errors::EntityAttrDoesNotExistError {
219            entity,
220            attr_or_tag: tag,
221            was_attr: false,
222            exists_the_other_kind: does_tag_exist_as_an_attr,
223            available_attrs_or_tags: available_tags
224                .into_iter()
225                .take(TOO_MANY_ATTRS)
226                .cloned()
227                .collect::<Vec<_>>(),
228            total_attrs_or_tags: total_tags,
229            source_loc,
230        }
231        .into()
232    }
233
234    /// Construct a [`RecordAttrDoesNotExist`] error
235    pub(crate) fn record_attr_does_not_exist<'a>(
236        attr: SmolStr,
237        available_attrs: impl IntoIterator<Item = &'a SmolStr>,
238        total_attrs: usize,
239        source_loc: Option<Loc>,
240    ) -> Self {
241        evaluation_errors::RecordAttrDoesNotExistError {
242            attr,
243            available_attrs: available_attrs
244                .into_iter()
245                .take(TOO_MANY_ATTRS)
246                .cloned()
247                .collect(),
248            total_attrs,
249            source_loc,
250        }
251        .into()
252    }
253
254    /// Construct a [`TypeError`] error
255    pub(crate) fn type_error(expected: NonEmpty<Type>, actual: &Value) -> Self {
256        evaluation_errors::TypeError {
257            expected,
258            actual: actual.type_of(),
259            advice: None,
260            source_loc: actual.source_loc().cloned(),
261        }
262        .into()
263    }
264
265    pub(crate) fn type_error_single(expected: Type, actual: &Value) -> Self {
266        Self::type_error(nonempty![expected], actual)
267    }
268
269    /// Construct a [`TypeError`] error with the advice field set
270    pub(crate) fn type_error_with_advice(
271        expected: NonEmpty<Type>,
272        actual: &Value,
273        advice: String,
274    ) -> Self {
275        evaluation_errors::TypeError {
276            expected,
277            actual: actual.type_of(),
278            advice: Some(advice),
279            source_loc: actual.source_loc().cloned(),
280        }
281        .into()
282    }
283
284    pub(crate) fn type_error_with_advice_single(
285        expected: Type,
286        actual: &Value,
287        advice: String,
288    ) -> Self {
289        Self::type_error_with_advice(nonempty![expected], actual, advice)
290    }
291
292    /// Construct a [`WrongNumArguments`] error
293    pub(crate) fn wrong_num_arguments(
294        function_name: Name,
295        expected: usize,
296        actual: usize,
297        source_loc: Option<Loc>,
298    ) -> Self {
299        evaluation_errors::WrongNumArgumentsError {
300            function_name,
301            expected,
302            actual,
303            source_loc,
304        }
305        .into()
306    }
307
308    /// Construct a [`UnlinkedSlot`] error
309    pub(crate) fn unlinked_slot(slot: SlotId, source_loc: Option<Loc>) -> Self {
310        evaluation_errors::UnlinkedSlotError { slot, source_loc }.into()
311    }
312
313    /// Construct a [`FailedExtensionFunctionApplication`] error
314    pub(crate) fn failed_extension_function_application(
315        extension_name: Name,
316        msg: String,
317        source_loc: Option<Loc>,
318        advice: Option<String>,
319    ) -> Self {
320        evaluation_errors::ExtensionFunctionExecutionError {
321            extension_name,
322            msg,
323            advice,
324            source_loc,
325        }
326        .into()
327    }
328
329    /// Construct a [`NonValue`] error
330    pub(crate) fn non_value(expr: Expr) -> Self {
331        let source_loc = expr.source_loc().cloned();
332        evaluation_errors::NonValueError { expr, source_loc }.into()
333    }
334
335    /// Construct a [`RecursionLimit`] error
336    pub(crate) fn recursion_limit(source_loc: Option<Loc>) -> Self {
337        evaluation_errors::RecursionLimitError { source_loc }.into()
338    }
339}
340
341/// Error subtypes for [`EvaluationError`]
342pub mod evaluation_errors {
343    use crate::ast::{BinaryOp, EntityUID, Expr, SlotId, Type, UnaryOp, Value};
344    use crate::parser::Loc;
345    use itertools::Itertools;
346    use miette::Diagnostic;
347    use nonempty::NonEmpty;
348    use smol_str::SmolStr;
349    use std::fmt::Write;
350    use std::sync::Arc;
351    use thiserror::Error;
352
353    use super::Name;
354
355    /// Tried to lookup an entity UID, but it didn't exist in the provided entities
356    //
357    // CAUTION: this type is publicly exported in `cedar-policy`.
358    // Don't make fields `pub`, don't make breaking changes, and use caution
359    // when adding public methods.
360    #[derive(Debug, PartialEq, Eq, Clone, Error)]
361    #[error("entity `{uid}` does not exist")]
362    pub struct EntityDoesNotExistError {
363        /// Entity UID which didn't exist in the provided entities
364        pub(crate) uid: Arc<EntityUID>,
365        /// Source location
366        pub(crate) source_loc: Option<Loc>,
367    }
368
369    // This and similar `Diagnostic` impls could just be derived with
370    //
371    // #[source_code]
372    // #[label]
373    // source_loc: Option<Loc>,
374    //
375    // if [miette#377](https://github.com/zkat/miette/issues/377) gets fixed.
376    // Or, we could have separate fields for source code and label instead of
377    // combining them into `Loc`, which would work around the issue.
378    impl Diagnostic for EntityDoesNotExistError {
379        impl_diagnostic_from_source_loc_opt_field!(source_loc);
380    }
381
382    /// Tried to get an attribute, but the specified entity didn't have that
383    /// attribute
384    //
385    // CAUTION: this type is publicly exported in `cedar-policy`.
386    // Don't make fields `pub`, don't make breaking changes, and use caution
387    // when adding public methods.
388    #[derive(Debug, PartialEq, Eq, Clone, Error)]
389    #[error("`{entity}` does not have the {} `{attr_or_tag}`", if *.was_attr { "attribute" } else { "tag" })]
390    pub struct EntityAttrDoesNotExistError {
391        /// Entity that didn't have the attribute
392        pub(crate) entity: Arc<EntityUID>,
393        /// Name of the attribute or tag it didn't have
394        pub(crate) attr_or_tag: SmolStr,
395        /// Whether this was an attempted attribute access (`true`) or tag access (`false`)
396        pub(crate) was_attr: bool,
397        /// If `true`, this is a case where we tried accessing an attribute but
398        /// there's a tag of that name, or vice versa
399        pub(crate) exists_the_other_kind: bool,
400        /// (First five) Available attributes/tags on the entity, depending on `was_attr`
401        pub(crate) available_attrs_or_tags: Vec<SmolStr>,
402        /// Total number of attributes/tags on the entity, depending on `was_attr`
403        pub(crate) total_attrs_or_tags: usize,
404        /// Source location
405        pub(crate) source_loc: Option<Loc>,
406    }
407
408    impl Diagnostic for EntityAttrDoesNotExistError {
409        impl_diagnostic_from_source_loc_opt_field!(source_loc);
410
411        fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
412            let mut help_text = if self.available_attrs_or_tags.is_empty() {
413                format!(
414                    "`{}` does not have any {}",
415                    &self.entity,
416                    if self.was_attr { "attributes" } else { "tags" }
417                )
418            } else if self.available_attrs_or_tags.len() == self.total_attrs_or_tags {
419                format!(
420                    "available {}: [{}]",
421                    if self.was_attr { "attributes" } else { "tags" },
422                    self.available_attrs_or_tags.iter().join(",")
423                )
424            } else {
425                format!(
426                    "available {}: [{}, ... ({} more attributes) ]",
427                    if self.was_attr { "attributes" } else { "tags" },
428                    self.available_attrs_or_tags.iter().join(","),
429                    self.total_attrs_or_tags - self.available_attrs_or_tags.len()
430                )
431            };
432            if self.exists_the_other_kind {
433                // PANIC SAFETY: A `write!` to a `String` cannot fail
434                #[allow(clippy::unwrap_used)]
435                write!(
436                    &mut help_text,
437                    "; note that {} (not {}) named `{}` does exist",
438                    if self.was_attr {
439                        "a tag"
440                    } else {
441                        "an attribute"
442                    },
443                    if self.was_attr {
444                        "an attribute"
445                    } else {
446                        "a tag"
447                    },
448                    self.attr_or_tag,
449                )
450                .unwrap()
451            }
452            Some(Box::new(help_text))
453        }
454    }
455
456    /// Tried to get an attribute of a (non-entity) record, but that record didn't
457    /// have that attribute
458    //
459    // CAUTION: this type is publicly exported in `cedar-policy`.
460    // Don't make fields `pub`, don't make breaking changes, and use caution
461    // when adding public methods.
462    #[derive(Debug, PartialEq, Eq, Clone, Error)]
463    #[error("record does not have the attribute `{attr}`")]
464    pub struct RecordAttrDoesNotExistError {
465        /// Name of the attribute we tried to access
466        pub(crate) attr: SmolStr,
467        /// (First five) Available attributes on the record
468        pub(crate) available_attrs: Vec<SmolStr>,
469        /// The total number of attrs this record has
470        pub(crate) total_attrs: usize,
471        /// Source location
472        pub(crate) source_loc: Option<Loc>,
473    }
474
475    impl Diagnostic for RecordAttrDoesNotExistError {
476        impl_diagnostic_from_source_loc_opt_field!(source_loc);
477
478        fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
479            if self.available_attrs.is_empty() {
480                Some(Box::new("record does not have any attributes"))
481            } else if self.available_attrs.len() == self.total_attrs {
482                Some(Box::new(format!(
483                    "available attributes: {:?}",
484                    self.available_attrs
485                )))
486            } else {
487                Some(Box::new(format!(
488                    "available attributes: [{}, ... ({} more attributes) ]",
489                    self.available_attrs.iter().join(","),
490                    self.total_attrs - self.available_attrs.len()
491                )))
492            }
493        }
494    }
495
496    /// Tried to evaluate an operation on values with incorrect types for that
497    /// operation
498    //
499    // CAUTION: this type is publicly exported in `cedar-policy`.
500    // Don't make fields `pub`, don't make breaking changes, and use caution
501    // when adding public methods.
502    #[derive(Debug, PartialEq, Eq, Clone, Error)]
503    pub struct TypeError {
504        /// Expected one of these types
505        pub(crate) expected: NonEmpty<Type>,
506        /// Encountered this type instead
507        pub(crate) actual: Type,
508        /// Optional advice for how to fix this error
509        pub(crate) advice: Option<String>,
510        /// Source location
511        pub(crate) source_loc: Option<Loc>,
512    }
513
514    impl Diagnostic for TypeError {
515        impl_diagnostic_from_source_loc_opt_field!(source_loc);
516
517        fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
518            self.advice.as_ref().map(|advice| Box::new(advice) as _)
519        }
520    }
521
522    impl std::fmt::Display for TypeError {
523        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
524            if self.expected.len() == 1 {
525                write!(
526                    f,
527                    "type error: expected {}, got {}",
528                    self.expected.first(),
529                    self.actual
530                )
531            } else {
532                write!(
533                    f,
534                    "type error: expected one of [{}], got {}",
535                    self.expected.iter().join(", "),
536                    self.actual
537                )
538            }
539        }
540    }
541
542    /// Wrong number of arguments provided to an extension function
543    //
544    // CAUTION: this type is publicly exported in `cedar-policy`.
545    // Don't make fields `pub`, don't make breaking changes, and use caution
546    // when adding public methods.
547    #[derive(Debug, PartialEq, Eq, Clone, Error)]
548    #[error("wrong number of arguments provided to extension function `{function_name}`: expected {expected}, got {actual}")]
549    pub struct WrongNumArgumentsError {
550        /// arguments to this function
551        pub(crate) function_name: Name,
552        /// expected number of arguments
553        pub(crate) expected: usize,
554        /// actual number of arguments
555        pub(crate) actual: usize,
556        /// Source location
557        pub(crate) source_loc: Option<Loc>,
558    }
559
560    impl Diagnostic for WrongNumArgumentsError {
561        impl_diagnostic_from_source_loc_opt_field!(source_loc);
562    }
563
564    /// Overflow during an integer operation
565    //
566    // CAUTION: this type is publicly exported in `cedar-policy`.
567    // Don't make fields `pub`, don't make breaking changes, and use caution
568    // when adding public methods.
569    #[derive(Debug, PartialEq, Eq, Clone, Diagnostic, Error)]
570    pub enum IntegerOverflowError {
571        /// Overflow during a binary operation
572        #[error(transparent)]
573        #[diagnostic(transparent)]
574        BinaryOp(#[from] BinaryOpOverflowError),
575
576        /// Overflow during a unary operation
577        #[error(transparent)]
578        #[diagnostic(transparent)]
579        UnaryOp(#[from] UnaryOpOverflowError),
580    }
581
582    impl IntegerOverflowError {
583        pub(crate) fn source_loc(&self) -> Option<&Loc> {
584            match self {
585                Self::BinaryOp(e) => e.source_loc.as_ref(),
586                Self::UnaryOp(e) => e.source_loc.as_ref(),
587            }
588        }
589
590        pub(crate) fn with_maybe_source_loc(self, source_loc: Option<Loc>) -> Self {
591            match self {
592                Self::BinaryOp(e) => Self::BinaryOp(BinaryOpOverflowError { source_loc, ..e }),
593                Self::UnaryOp(e) => Self::UnaryOp(UnaryOpOverflowError { source_loc, ..e }),
594            }
595        }
596    }
597
598    /// Overflow during a binary operation
599    //
600    // CAUTION: this type is publicly exported in `cedar-policy`.
601    // Don't make fields `pub`, don't make breaking changes, and use caution
602    // when adding public methods.
603    #[derive(Debug, PartialEq, Eq, Clone, Error)]
604    #[error("integer overflow while attempting to {} the values `{arg1}` and `{arg2}`", match .op { BinaryOp::Add => "add", BinaryOp::Sub => "subtract", BinaryOp::Mul => "multiply", _ => "perform an operation on" })]
605    pub struct BinaryOpOverflowError {
606        /// overflow while evaluating this operator
607        pub(crate) op: BinaryOp,
608        /// first argument to that operator
609        pub(crate) arg1: Value,
610        /// second argument to that operator
611        pub(crate) arg2: Value,
612        /// Source location
613        pub(crate) source_loc: Option<Loc>,
614    }
615
616    impl Diagnostic for BinaryOpOverflowError {
617        impl_diagnostic_from_source_loc_opt_field!(source_loc);
618    }
619
620    /// Overflow during a unary operation
621    //
622    // CAUTION: this type is publicly exported in `cedar-policy`.
623    // Don't make fields `pub`, don't make breaking changes, and use caution
624    // when adding public methods.
625    #[derive(Debug, PartialEq, Eq, Clone, Error)]
626    #[error("integer overflow while attempting to {} the value `{arg}`", match .op { UnaryOp::Neg => "negate", _ => "perform an operation on" })]
627    pub struct UnaryOpOverflowError {
628        /// overflow while evaluating this operator
629        pub(crate) op: UnaryOp,
630        /// argument to that operator
631        pub(crate) arg: Value,
632        /// Source location
633        pub(crate) source_loc: Option<Loc>,
634    }
635
636    impl Diagnostic for UnaryOpOverflowError {
637        impl_diagnostic_from_source_loc_opt_field!(source_loc);
638    }
639
640    /// Not all template slots were linked
641    //
642    // CAUTION: this type is publicly exported in `cedar-policy`.
643    // Don't make fields `pub`, don't make breaking changes, and use caution
644    // when adding public methods.
645    #[derive(Debug, PartialEq, Eq, Clone, Error)]
646    #[error("template slot `{slot}` was not linked")]
647    pub struct UnlinkedSlotError {
648        /// Slot which was not linked
649        pub(crate) slot: SlotId,
650        /// Source location
651        pub(crate) source_loc: Option<Loc>,
652    }
653
654    impl Diagnostic for UnlinkedSlotError {
655        impl_diagnostic_from_source_loc_opt_field!(source_loc);
656    }
657
658    /// Evaluation error thrown by an extension function
659    //
660    // CAUTION: this type is publicly exported in `cedar-policy`.
661    // Don't make fields `pub`, don't make breaking changes, and use caution
662    // when adding public methods.
663    #[derive(Debug, PartialEq, Eq, Clone, Error)]
664    #[error("error while evaluating `{extension_name}` extension function: {msg}")]
665    pub struct ExtensionFunctionExecutionError {
666        /// Name of the extension throwing the error
667        pub(crate) extension_name: Name,
668        /// Error message from the extension
669        pub(crate) msg: String,
670        /// Optional advice for how to fix this error
671        pub(crate) advice: Option<String>,
672        /// Source location
673        pub(crate) source_loc: Option<Loc>,
674    }
675
676    impl Diagnostic for ExtensionFunctionExecutionError {
677        impl_diagnostic_from_source_loc_opt_field!(source_loc);
678
679        fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
680            self.advice.as_ref().map(|v| Box::new(v) as _)
681        }
682    }
683
684    impl ExtensionFunctionExecutionError {
685        /// Get the name of the extension that threw this error
686        pub fn extension_name(&self) -> String {
687            self.extension_name.to_string()
688        }
689    }
690
691    /// This error is raised if an expression contains unknowns and cannot be
692    /// reduced to a [`Value`]. In order to return partial results, use the
693    /// partial evaluation APIs instead.
694    //
695    // CAUTION: this type is publicly exported in `cedar-policy`.
696    // Don't make fields `pub`, don't make breaking changes, and use caution
697    // when adding public methods.
698    #[derive(Debug, PartialEq, Eq, Clone, Error)]
699    #[error("the expression contains unknown(s): `{expr}`")]
700    pub struct NonValueError {
701        /// Expression that contained unknown(s)
702        pub(crate) expr: Expr,
703        /// Source location
704        pub(crate) source_loc: Option<Loc>,
705    }
706
707    impl Diagnostic for NonValueError {
708        impl_diagnostic_from_source_loc_opt_field!(source_loc);
709
710        fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
711            Some(Box::new("consider using the partial evaluation APIs"))
712        }
713    }
714
715    /// Represents an AST node that failed to parse - cannot be evaluated
716    //
717    // CAUTION: this type is publicly exported in `cedar-policy`.
718    // Don't make fields `pub`, don't make breaking changes, and use caution
719    // when adding public methods.
720    #[cfg(feature = "tolerant-ast")]
721    #[derive(Debug, PartialEq, Eq, Clone, Error)]
722    #[error("the expression contains an error")]
723    pub struct ASTErrorExprError {
724        /// Source location
725        pub(crate) source_loc: Option<Loc>,
726    }
727
728    #[cfg(feature = "tolerant-ast")]
729    impl Diagnostic for ASTErrorExprError {
730        impl_diagnostic_from_source_loc_opt_field!(source_loc);
731
732        fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
733            Some(Box::new("Represents an AST node that failed to parse"))
734        }
735    }
736
737    /// Maximum recursion limit reached for expression evaluation
738    //
739    // CAUTION: this type is publicly exported in `cedar-policy`.
740    // Don't make fields `pub`, don't make breaking changes, and use caution
741    // when adding public methods.
742    #[derive(Debug, PartialEq, Eq, Clone, Error)]
743    #[error("recursion limit reached")]
744    pub struct RecursionLimitError {
745        /// Source location
746        pub(crate) source_loc: Option<Loc>,
747    }
748
749    impl Diagnostic for RecursionLimitError {
750        impl_diagnostic_from_source_loc_opt_field!(source_loc);
751    }
752}
753
754/// Type alias for convenience
755pub type Result<T> = std::result::Result<T, EvaluationError>;