Skip to main content

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                #[expect(clippy::unwrap_used, reason = "A `write!` to a `String` cannot fail")]
434                write!(
435                    &mut help_text,
436                    "; note that {} (not {}) named `{}` does exist",
437                    if self.was_attr {
438                        "a tag"
439                    } else {
440                        "an attribute"
441                    },
442                    if self.was_attr {
443                        "an attribute"
444                    } else {
445                        "a tag"
446                    },
447                    self.attr_or_tag,
448                )
449                .unwrap()
450            }
451            Some(Box::new(help_text))
452        }
453    }
454
455    /// Tried to get an attribute of a (non-entity) record, but that record didn't
456    /// have that attribute
457    //
458    // CAUTION: this type is publicly exported in `cedar-policy`.
459    // Don't make fields `pub`, don't make breaking changes, and use caution
460    // when adding public methods.
461    #[derive(Debug, PartialEq, Eq, Clone, Error)]
462    #[error("record does not have the attribute `{attr}`")]
463    pub struct RecordAttrDoesNotExistError {
464        /// Name of the attribute we tried to access
465        pub(crate) attr: SmolStr,
466        /// (First five) Available attributes on the record
467        pub(crate) available_attrs: Vec<SmolStr>,
468        /// The total number of attrs this record has
469        pub(crate) total_attrs: usize,
470        /// Source location
471        pub(crate) source_loc: Option<Loc>,
472    }
473
474    impl Diagnostic for RecordAttrDoesNotExistError {
475        impl_diagnostic_from_source_loc_opt_field!(source_loc);
476
477        fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
478            if self.available_attrs.is_empty() {
479                Some(Box::new("record does not have any attributes"))
480            } else if self.available_attrs.len() == self.total_attrs {
481                Some(Box::new(format!(
482                    "available attributes: {:?}",
483                    self.available_attrs
484                )))
485            } else {
486                Some(Box::new(format!(
487                    "available attributes: [{}, ... ({} more attributes) ]",
488                    self.available_attrs.iter().join(","),
489                    self.total_attrs - self.available_attrs.len()
490                )))
491            }
492        }
493    }
494
495    /// Tried to evaluate an operation on values with incorrect types for that
496    /// operation
497    //
498    // CAUTION: this type is publicly exported in `cedar-policy`.
499    // Don't make fields `pub`, don't make breaking changes, and use caution
500    // when adding public methods.
501    #[derive(Debug, PartialEq, Eq, Clone, Error)]
502    pub struct TypeError {
503        /// Expected one of these types
504        pub(crate) expected: NonEmpty<Type>,
505        /// Encountered this type instead
506        pub(crate) actual: Type,
507        /// Optional advice for how to fix this error
508        pub(crate) advice: Option<String>,
509        /// Source location
510        pub(crate) source_loc: Option<Loc>,
511    }
512
513    impl Diagnostic for TypeError {
514        impl_diagnostic_from_source_loc_opt_field!(source_loc);
515
516        fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
517            self.advice.as_ref().map(|advice| Box::new(advice) as _)
518        }
519    }
520
521    impl std::fmt::Display for TypeError {
522        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
523            if self.expected.len() == 1 {
524                write!(
525                    f,
526                    "type error: expected {}, got {}",
527                    self.expected.first(),
528                    self.actual
529                )
530            } else {
531                write!(
532                    f,
533                    "type error: expected one of [{}], got {}",
534                    self.expected.iter().join(", "),
535                    self.actual
536                )
537            }
538        }
539    }
540
541    /// Wrong number of arguments provided to an extension function
542    //
543    // CAUTION: this type is publicly exported in `cedar-policy`.
544    // Don't make fields `pub`, don't make breaking changes, and use caution
545    // when adding public methods.
546    #[derive(Debug, PartialEq, Eq, Clone, Error)]
547    #[error("wrong number of arguments provided to extension function `{function_name}`: expected {expected}, got {actual}")]
548    pub struct WrongNumArgumentsError {
549        /// arguments to this function
550        pub(crate) function_name: Name,
551        /// expected number of arguments
552        pub(crate) expected: usize,
553        /// actual number of arguments
554        pub(crate) actual: usize,
555        /// Source location
556        pub(crate) source_loc: Option<Loc>,
557    }
558
559    impl Diagnostic for WrongNumArgumentsError {
560        impl_diagnostic_from_source_loc_opt_field!(source_loc);
561    }
562
563    /// Overflow during an integer operation
564    //
565    // CAUTION: this type is publicly exported in `cedar-policy`.
566    // Don't make fields `pub`, don't make breaking changes, and use caution
567    // when adding public methods.
568    #[derive(Debug, PartialEq, Eq, Clone, Diagnostic, Error)]
569    pub enum IntegerOverflowError {
570        /// Overflow during a binary operation
571        #[error(transparent)]
572        #[diagnostic(transparent)]
573        BinaryOp(#[from] BinaryOpOverflowError),
574
575        /// Overflow during a unary operation
576        #[error(transparent)]
577        #[diagnostic(transparent)]
578        UnaryOp(#[from] UnaryOpOverflowError),
579    }
580
581    impl IntegerOverflowError {
582        pub(crate) fn source_loc(&self) -> Option<&Loc> {
583            match self {
584                Self::BinaryOp(e) => e.source_loc.as_ref(),
585                Self::UnaryOp(e) => e.source_loc.as_ref(),
586            }
587        }
588
589        pub(crate) fn with_maybe_source_loc(self, source_loc: Option<Loc>) -> Self {
590            match self {
591                Self::BinaryOp(e) => Self::BinaryOp(BinaryOpOverflowError { source_loc, ..e }),
592                Self::UnaryOp(e) => Self::UnaryOp(UnaryOpOverflowError { source_loc, ..e }),
593            }
594        }
595    }
596
597    /// Overflow during a binary operation
598    //
599    // CAUTION: this type is publicly exported in `cedar-policy`.
600    // Don't make fields `pub`, don't make breaking changes, and use caution
601    // when adding public methods.
602    #[derive(Debug, PartialEq, Eq, Clone, Error)]
603    #[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" })]
604    pub struct BinaryOpOverflowError {
605        /// overflow while evaluating this operator
606        pub(crate) op: BinaryOp,
607        /// first argument to that operator
608        pub(crate) arg1: Value,
609        /// second argument to that operator
610        pub(crate) arg2: Value,
611        /// Source location
612        pub(crate) source_loc: Option<Loc>,
613    }
614
615    impl Diagnostic for BinaryOpOverflowError {
616        impl_diagnostic_from_source_loc_opt_field!(source_loc);
617    }
618
619    /// Overflow during a unary operation
620    //
621    // CAUTION: this type is publicly exported in `cedar-policy`.
622    // Don't make fields `pub`, don't make breaking changes, and use caution
623    // when adding public methods.
624    #[derive(Debug, PartialEq, Eq, Clone, Error)]
625    #[error("integer overflow while attempting to {} the value `{arg}`", match .op { UnaryOp::Neg => "negate", _ => "perform an operation on" })]
626    pub struct UnaryOpOverflowError {
627        /// overflow while evaluating this operator
628        pub(crate) op: UnaryOp,
629        /// argument to that operator
630        pub(crate) arg: Value,
631        /// Source location
632        pub(crate) source_loc: Option<Loc>,
633    }
634
635    impl Diagnostic for UnaryOpOverflowError {
636        impl_diagnostic_from_source_loc_opt_field!(source_loc);
637    }
638
639    /// Not all template slots were linked
640    //
641    // CAUTION: this type is publicly exported in `cedar-policy`.
642    // Don't make fields `pub`, don't make breaking changes, and use caution
643    // when adding public methods.
644    #[derive(Debug, PartialEq, Eq, Clone, Error)]
645    #[error("template slot `{slot}` was not linked")]
646    pub struct UnlinkedSlotError {
647        /// Slot which was not linked
648        pub(crate) slot: SlotId,
649        /// Source location
650        pub(crate) source_loc: Option<Loc>,
651    }
652
653    impl Diagnostic for UnlinkedSlotError {
654        impl_diagnostic_from_source_loc_opt_field!(source_loc);
655    }
656
657    /// Evaluation error thrown by an extension function
658    //
659    // CAUTION: this type is publicly exported in `cedar-policy`.
660    // Don't make fields `pub`, don't make breaking changes, and use caution
661    // when adding public methods.
662    #[derive(Debug, PartialEq, Eq, Clone, Error)]
663    #[error("error while evaluating `{extension_name}` extension function: {msg}")]
664    pub struct ExtensionFunctionExecutionError {
665        /// Name of the extension throwing the error
666        pub(crate) extension_name: Name,
667        /// Error message from the extension
668        pub(crate) msg: String,
669        /// Optional advice for how to fix this error
670        pub(crate) advice: Option<String>,
671        /// Source location
672        pub(crate) source_loc: Option<Loc>,
673    }
674
675    impl Diagnostic for ExtensionFunctionExecutionError {
676        impl_diagnostic_from_source_loc_opt_field!(source_loc);
677
678        fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
679            self.advice.as_ref().map(|v| Box::new(v) as _)
680        }
681    }
682
683    impl ExtensionFunctionExecutionError {
684        /// Get the name of the extension that threw this error
685        pub fn extension_name(&self) -> String {
686            self.extension_name.to_string()
687        }
688    }
689
690    /// This error is raised if an expression contains unknowns and cannot be
691    /// reduced to a [`Value`]. In order to return partial results, use the
692    /// partial evaluation APIs instead.
693    //
694    // CAUTION: this type is publicly exported in `cedar-policy`.
695    // Don't make fields `pub`, don't make breaking changes, and use caution
696    // when adding public methods.
697    #[derive(Debug, PartialEq, Eq, Clone, Error)]
698    #[error("the expression contains unknown(s): `{expr}`")]
699    pub struct NonValueError {
700        /// Expression that contained unknown(s)
701        pub(crate) expr: Expr,
702        /// Source location
703        pub(crate) source_loc: Option<Loc>,
704    }
705
706    impl Diagnostic for NonValueError {
707        impl_diagnostic_from_source_loc_opt_field!(source_loc);
708
709        fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
710            Some(Box::new("consider using the partial evaluation APIs"))
711        }
712    }
713
714    /// Represents an AST node that failed to parse - cannot be evaluated
715    //
716    // CAUTION: this type is publicly exported in `cedar-policy`.
717    // Don't make fields `pub`, don't make breaking changes, and use caution
718    // when adding public methods.
719    #[cfg(feature = "tolerant-ast")]
720    #[derive(Debug, PartialEq, Eq, Clone, Error)]
721    #[error("the expression contains an error")]
722    pub struct ASTErrorExprError {
723        /// Source location
724        pub(crate) source_loc: Option<Loc>,
725    }
726
727    #[cfg(feature = "tolerant-ast")]
728    impl Diagnostic for ASTErrorExprError {
729        impl_diagnostic_from_source_loc_opt_field!(source_loc);
730
731        fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
732            Some(Box::new("Represents an AST node that failed to parse"))
733        }
734    }
735
736    /// Maximum recursion limit reached for expression evaluation
737    //
738    // CAUTION: this type is publicly exported in `cedar-policy`.
739    // Don't make fields `pub`, don't make breaking changes, and use caution
740    // when adding public methods.
741    #[derive(Debug, PartialEq, Eq, Clone, Error)]
742    #[error("recursion limit reached")]
743    pub struct RecursionLimitError {
744        /// Source location
745        pub(crate) source_loc: Option<Loc>,
746    }
747
748    impl Diagnostic for RecursionLimitError {
749        impl_diagnostic_from_source_loc_opt_field!(source_loc);
750    }
751}
752
753/// Type alias for convenience
754pub type Result<T> = std::result::Result<T, EvaluationError>;