1use crate::{
18 ast::{EntityUID, ReservedNameError},
19 transitive_closure,
20};
21use itertools::{Either, Itertools};
22use miette::Diagnostic;
23use nonempty::NonEmpty;
24use thiserror::Error;
25
26use crate::validator::cedar_schema;
27
28#[derive(Debug, Error, Diagnostic)]
30pub enum CedarSchemaError {
31 #[error(transparent)]
33 #[diagnostic(transparent)]
34 Schema(#[from] SchemaError),
35 #[error(transparent)]
37 IO(#[from] std::io::Error),
38 #[error(transparent)]
40 #[diagnostic(transparent)]
41 Parsing(#[from] CedarSchemaParseError),
42}
43
44#[derive(Debug, Error)]
47#[error("error parsing schema: {errs}")]
48pub struct CedarSchemaParseError {
49 errs: cedar_schema::parser::CedarSchemaParseErrors,
51 suspect_json_format: bool,
54}
55
56impl Diagnostic for CedarSchemaParseError {
57 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
58 let suspect_json_help = if self.suspect_json_format {
59 Some(Box::new("this API was expecting a schema in the Cedar schema format; did you mean to use a different function, which expects a JSON-format Cedar schema"))
60 } else {
61 None
62 };
63 match (suspect_json_help, self.errs.help()) {
64 (Some(json), Some(inner)) => Some(Box::new(format!("{inner}\n{json}"))),
65 (Some(h), None) => Some(h),
66 (None, Some(h)) => Some(h),
67 (None, None) => None,
68 }
69 }
70
71 fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
74 self.errs.code()
75 }
76 fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
77 self.errs.labels()
78 }
79 fn severity(&self) -> Option<miette::Severity> {
80 self.errs.severity()
81 }
82 fn url<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
83 self.errs.url()
84 }
85 fn source_code(&self) -> Option<&dyn miette::SourceCode> {
86 self.errs.source_code()
87 }
88 fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
89 self.errs.diagnostic_source()
90 }
91 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
92 self.errs.related()
93 }
94}
95
96impl CedarSchemaParseError {
97 pub(crate) fn new(errs: cedar_schema::parser::CedarSchemaParseErrors, src: &str) -> Self {
101 let suspect_json_format = match src.trim_start().chars().next() {
103 None => false, Some('{') => true, Some(_) => false, };
107 Self {
108 errs,
109 suspect_json_format,
110 }
111 }
112
113 pub fn suspect_json_format(&self) -> bool {
118 self.suspect_json_format
119 }
120
121 pub fn errors(&self) -> &cedar_schema::parser::CedarSchemaParseErrors {
123 &self.errs
124 }
125}
126
127#[derive(Debug, Diagnostic, Error)]
133#[non_exhaustive]
134pub enum SchemaError {
135 #[error(transparent)]
137 #[diagnostic(transparent)]
138 JsonSerialization(#[from] schema_errors::JsonSerializationError),
139 #[error(transparent)]
141 #[diagnostic(transparent)]
142 JsonDeserialization(#[from] schema_errors::JsonDeserializationError),
143 #[error(transparent)]
146 #[diagnostic(transparent)]
147 ActionTransitiveClosure(#[from] schema_errors::ActionTransitiveClosureError),
148 #[error(transparent)]
151 #[diagnostic(transparent)]
152 EntityTypeTransitiveClosure(#[from] schema_errors::EntityTypeTransitiveClosureError),
153 #[error(transparent)]
155 #[diagnostic(transparent)]
156 UnsupportedFeature(#[from] schema_errors::UnsupportedFeatureError),
157 #[error(transparent)]
162 #[diagnostic(transparent)]
163 UndeclaredEntityTypes(#[from] schema_errors::UndeclaredEntityTypesError),
164 #[error(transparent)]
167 #[diagnostic(transparent)]
168 TypeNotDefined(#[from] schema_errors::TypeNotDefinedError),
169 #[error(transparent)]
173 #[diagnostic(transparent)]
174 ActionNotDefined(#[from] schema_errors::ActionNotDefinedError),
175 #[error(transparent)]
179 #[diagnostic(transparent)]
180 TypeShadowing(#[from] schema_errors::TypeShadowingError),
181 #[error(transparent)]
185 #[diagnostic(transparent)]
186 ActionShadowing(#[from] schema_errors::ActionShadowingError),
187 #[error(transparent)]
189 #[diagnostic(transparent)]
190 DuplicateEntityType(#[from] schema_errors::DuplicateEntityTypeError),
191 #[error(transparent)]
193 #[diagnostic(transparent)]
194 DuplicateAction(#[from] schema_errors::DuplicateActionError),
195 #[error(transparent)]
197 #[diagnostic(transparent)]
198 DuplicateCommonType(#[from] schema_errors::DuplicateCommonTypeError),
199 #[error(transparent)]
201 #[diagnostic(transparent)]
202 CycleInActionHierarchy(#[from] schema_errors::CycleInActionHierarchyError),
203 #[error(transparent)]
205 #[diagnostic(transparent)]
206 CycleInCommonTypeReferences(#[from] schema_errors::CycleInCommonTypeReferencesError),
207 #[error(transparent)]
212 #[diagnostic(transparent)]
213 ActionEntityTypeDeclared(#[from] schema_errors::ActionEntityTypeDeclaredError),
214 #[error(transparent)]
216 #[diagnostic(transparent)]
217 ContextOrShapeNotRecord(#[from] schema_errors::ContextOrShapeNotRecordError),
218 #[error(transparent)]
220 #[diagnostic(transparent)]
221 #[allow(deprecated)]
222 #[deprecated = "this error is deprecated and should never be returned"]
223 ActionAttributesContainEmptySet(#[from] schema_errors::ActionAttributesContainEmptySetError),
224 #[error(transparent)]
226 #[diagnostic(transparent)]
227 #[allow(deprecated)]
228 #[deprecated = "this error is deprecated and should never be returned"]
229 UnsupportedActionAttribute(#[from] schema_errors::UnsupportedActionAttributeError),
230 #[error(transparent)]
232 #[diagnostic(transparent)]
233 #[allow(deprecated)]
234 #[deprecated = "this error is deprecated and should never be returned"]
235 ActionAttrEval(#[from] schema_errors::ActionAttrEvalError),
236 #[error(transparent)]
238 #[diagnostic(transparent)]
239 #[allow(deprecated)]
240 #[deprecated = "this error is deprecated and should never be returned"]
241 ExprEscapeUsed(#[from] schema_errors::ExprEscapeUsedError),
242 #[error(transparent)]
244 #[diagnostic(transparent)]
245 UnknownExtensionType(schema_errors::UnknownExtensionTypeError),
246 #[error(transparent)]
248 #[diagnostic(transparent)]
249 ReservedName(#[from] ReservedNameError),
250 #[error(transparent)]
253 #[diagnostic(transparent)]
254 CommonTypeInvariantViolation(#[from] schema_errors::CommonTypeInvariantViolationError),
255 #[error(transparent)]
258 #[diagnostic(transparent)]
259 ActionInvariantViolation(#[from] schema_errors::ActionInvariantViolationError),
260}
261
262impl From<transitive_closure::TcError<EntityUID>> for SchemaError {
263 fn from(e: transitive_closure::TcError<EntityUID>) -> Self {
264 match e {
268 transitive_closure::TcError::MissingTcEdge { .. } => {
269 SchemaError::ActionTransitiveClosure(Box::new(e).into())
270 }
271 transitive_closure::TcError::HasCycle(err) => {
272 schema_errors::CycleInActionHierarchyError {
273 uid: err.vertex_with_loop().clone(),
274 }
275 .into()
276 }
277 }
278 }
279}
280
281impl SchemaError {
282 pub fn join_nonempty(errs: NonEmpty<SchemaError>) -> SchemaError {
285 let (type_ndef_errors, non_type_ndef_errors): (Vec<_>, Vec<_>) =
289 errs.into_iter().partition_map(|e| match e {
290 SchemaError::TypeNotDefined(e) => Either::Left(e),
291 _ => Either::Right(e),
292 });
293 if let Some(errs) = NonEmpty::from_vec(type_ndef_errors) {
294 schema_errors::TypeNotDefinedError::join_nonempty(errs).into()
295 } else {
296 let (action_ndef_errors, other_errors): (Vec<_>, Vec<_>) =
297 non_type_ndef_errors.into_iter().partition_map(|e| match e {
298 SchemaError::ActionNotDefined(e) => Either::Left(e),
299 _ => Either::Right(e),
300 });
301 if let Some(errs) = NonEmpty::from_vec(action_ndef_errors) {
302 schema_errors::ActionNotDefinedError::join_nonempty(errs).into()
303 } else {
304 #[allow(clippy::expect_used)]
311 other_errors.into_iter().next().expect("cannot be empty")
312 }
313 }
314 }
315}
316
317impl From<NonEmpty<SchemaError>> for SchemaError {
318 fn from(errs: NonEmpty<SchemaError>) -> Self {
319 Self::join_nonempty(errs)
320 }
321}
322
323impl From<NonEmpty<schema_errors::ActionNotDefinedError>> for SchemaError {
324 fn from(errs: NonEmpty<schema_errors::ActionNotDefinedError>) -> Self {
325 Self::ActionNotDefined(schema_errors::ActionNotDefinedError::join_nonempty(errs))
326 }
327}
328
329impl From<NonEmpty<schema_errors::TypeNotDefinedError>> for SchemaError {
330 fn from(errs: NonEmpty<schema_errors::TypeNotDefinedError>) -> Self {
331 Self::TypeNotDefined(schema_errors::TypeNotDefinedError::join_nonempty(errs))
332 }
333}
334
335pub type Result<T> = std::result::Result<T, SchemaError>;
337
338pub mod schema_errors {
340
341 #![allow(deprecated)]
345
346 use std::fmt::Display;
347
348 use crate::ast::{EntityType, EntityUID, InternalName, Name};
349 use crate::parser::{join_with_conjunction, Loc};
350 use crate::{
351 impl_diagnostic_from_method_on_field, impl_diagnostic_from_method_on_nonempty_field,
352 transitive_closure,
353 };
354 use itertools::Itertools;
355 use miette::Diagnostic;
356 use nonempty::NonEmpty;
357 use smol_str::SmolStr;
358 use thiserror::Error;
359
360 #[derive(Debug, Diagnostic, Error)]
366 #[error(transparent)]
367 pub struct JsonSerializationError(#[from] pub(crate) serde_json::Error);
368
369 #[derive(Debug, Diagnostic, Error)]
375 #[error("transitive closure computation/enforcement error on action hierarchy")]
376 #[diagnostic(transparent)]
377 pub struct ActionTransitiveClosureError(
378 #[from] pub(crate) Box<transitive_closure::TcError<EntityUID>>,
379 );
380
381 #[derive(Debug, Diagnostic, Error)]
387 #[error("transitive closure computation/enforcement error on entity type hierarchy")]
388 #[diagnostic(transparent)]
389 pub struct EntityTypeTransitiveClosureError(
390 #[from] pub(crate) Box<transitive_closure::TcError<EntityType>>,
391 );
392
393 #[derive(Debug, Error)]
399 pub struct UndeclaredEntityTypesError {
400 pub(crate) types: NonEmpty<EntityType>,
402 }
403
404 impl Display for UndeclaredEntityTypesError {
405 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
406 if self.types.len() == 1 {
407 write!(f, "undeclared entity type: ")?;
408 } else {
409 write!(f, "undeclared entity types: ")?;
410 }
411 join_with_conjunction(f, "and", self.types.iter().sorted_unstable(), |f, s| {
412 s.fmt(f)
413 })
414 }
415 }
416
417 impl Diagnostic for UndeclaredEntityTypesError {
418 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
419 Some(Box::new("any entity types appearing anywhere in a schema need to be declared in `entityTypes`"))
420 }
421
422 impl_diagnostic_from_method_on_nonempty_field!(types, loc);
423 }
424
425 #[derive(Debug, Error)]
431 #[error("failed to resolve type{}: {}", if .undefined_types.len() > 1 { "s" } else { "" }, .undefined_types.iter().map(crate::validator::ConditionalName::raw).join(", "))]
432 pub struct TypeNotDefinedError {
433 pub(crate) undefined_types: NonEmpty<crate::validator::ConditionalName>,
435 }
436
437 impl Diagnostic for TypeNotDefinedError {
438 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
439 Some(Box::new(
441 self.undefined_types.first().resolution_failure_help(),
442 ))
443 }
444
445 impl_diagnostic_from_method_on_nonempty_field!(undefined_types, loc);
446 }
447
448 impl TypeNotDefinedError {
449 pub(crate) fn join_nonempty(errs: NonEmpty<TypeNotDefinedError>) -> Self {
454 Self {
455 undefined_types: errs.flat_map(|err| err.undefined_types),
456 }
457 }
458 }
459
460 impl From<NonEmpty<TypeNotDefinedError>> for TypeNotDefinedError {
461 fn from(value: NonEmpty<TypeNotDefinedError>) -> Self {
462 Self::join_nonempty(value)
463 }
464 }
465
466 #[derive(Debug, Diagnostic, Error)]
472 #[diagnostic(help("any actions appearing as parents need to be declared as actions"))]
473 pub struct ActionNotDefinedError(
474 pub(crate) NonEmpty<
475 crate::validator::json_schema::ActionEntityUID<crate::validator::ConditionalName>,
476 >,
477 );
478
479 impl ActionNotDefinedError {
480 pub(crate) fn join_nonempty(errs: NonEmpty<ActionNotDefinedError>) -> Self {
485 Self(errs.flat_map(|err| err.0))
486 }
487 }
488
489 impl From<NonEmpty<ActionNotDefinedError>> for ActionNotDefinedError {
490 fn from(value: NonEmpty<ActionNotDefinedError>) -> Self {
491 Self::join_nonempty(value)
492 }
493 }
494
495 impl Display for ActionNotDefinedError {
496 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
497 if self.0.len() == 1 {
498 write!(f, "undeclared action: ")?;
499 } else {
500 write!(f, "undeclared actions: ")?;
501 }
502 join_with_conjunction(
503 f,
504 "and",
505 self.0.iter().map(|aeuid| aeuid.as_raw()),
506 |f, s| s.fmt(f),
507 )
508 }
509 }
510
511 #[derive(Debug, Error)]
519 #[error(
520 "definition of `{shadowing_def}` illegally shadows the existing definition of `{shadowed_def}`"
521 )]
522 pub struct TypeShadowingError {
523 pub(crate) shadowed_def: InternalName,
525 pub(crate) shadowing_def: InternalName,
527 }
528
529 impl Diagnostic for TypeShadowingError {
530 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
531 Some(Box::new(format!(
532 "try renaming one of the definitions, or moving `{}` to a different namespace",
533 self.shadowed_def
534 )))
535 }
536
537 impl_diagnostic_from_method_on_field!(shadowing_def, loc);
540 }
541
542 #[derive(Debug, Error)]
550 #[error(
551 "definition of `{shadowing_def}` illegally shadows the existing definition of `{shadowed_def}`"
552 )]
553 pub struct ActionShadowingError {
554 pub(crate) shadowed_def: EntityUID,
556 pub(crate) shadowing_def: EntityUID,
558 }
559
560 impl Diagnostic for ActionShadowingError {
561 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
562 Some(Box::new(format!(
563 "try renaming one of the actions, or moving `{}` to a different namespace",
564 self.shadowed_def
565 )))
566 }
567
568 impl_diagnostic_from_method_on_field!(shadowing_def, loc);
571 }
572
573 #[derive(Debug, Error)]
579 #[error("duplicate entity type `{ty}`")]
580 pub struct DuplicateEntityTypeError {
581 pub(crate) ty: EntityType,
582 }
583
584 impl Diagnostic for DuplicateEntityTypeError {
585 impl_diagnostic_from_method_on_field!(ty, loc);
586 }
587
588 #[derive(Debug, Diagnostic, Error)]
594 #[error("duplicate action `{0}`")]
595 pub struct DuplicateActionError(pub(crate) SmolStr);
596
597 #[derive(Debug, Error)]
603 #[error("duplicate common type `{ty}`")]
604 pub struct DuplicateCommonTypeError {
605 pub(crate) ty: InternalName,
606 }
607
608 impl Diagnostic for DuplicateCommonTypeError {
609 impl_diagnostic_from_method_on_field!(ty, loc);
610 }
611
612 #[derive(Debug, Error)]
618 #[error("cycle in action hierarchy containing `{uid}`")]
619 pub struct CycleInActionHierarchyError {
620 pub(crate) uid: EntityUID,
621 }
622
623 impl Diagnostic for CycleInActionHierarchyError {
624 impl_diagnostic_from_method_on_field!(uid, loc);
625 }
626
627 #[derive(Debug, Error)]
633 #[error("cycle in common type references containing `{ty}`")]
634 pub struct CycleInCommonTypeReferencesError {
635 pub(crate) ty: InternalName,
636 }
637
638 impl Diagnostic for CycleInCommonTypeReferencesError {
639 impl_diagnostic_from_method_on_field!(ty, loc);
640 }
641
642 #[derive(Debug, Clone, Diagnostic, Error)]
648 #[error("entity type `Action` declared in `entityTypes` list")]
649 pub struct ActionEntityTypeDeclaredError {}
650
651 #[derive(Debug, Error)]
657 #[error("{ctx_or_shape} is declared with a type other than `Record`")]
658 pub struct ContextOrShapeNotRecordError {
659 pub(crate) ctx_or_shape: ContextOrShape,
660 }
661
662 impl Diagnostic for ContextOrShapeNotRecordError {
663 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
664 match &self.ctx_or_shape {
665 ContextOrShape::ActionContext(_) => {
666 Some(Box::new("action contexts must have type `Record`"))
667 }
668 ContextOrShape::EntityTypeShape(_) => {
669 Some(Box::new("entity type shapes must have type `Record`"))
670 }
671 }
672 }
673
674 impl_diagnostic_from_method_on_field!(ctx_or_shape, loc);
675 }
676
677 #[derive(Diagnostic, Debug, Error)]
683 #[error("internal invariant violated: this error is deprecated and should never be returned")]
684 #[deprecated = "this error is deprecated and should never be returned"]
685 pub struct ActionAttributesContainEmptySetError {}
686
687 #[derive(Diagnostic, Debug, Error)]
693 #[error("internal invariant violated: this error is deprecated and should never be returned")]
694 #[deprecated = "this error is deprecated and should never be returned"]
695 pub struct UnsupportedActionAttributeError {}
696
697 #[derive(Diagnostic, Debug, Error)]
703 #[error("internal invariant violated: this error is deprecated and should never be returned")]
704 #[deprecated = "this error is deprecated and should never be returned"]
705 pub struct ExprEscapeUsedError {}
706
707 #[derive(Diagnostic, Debug, Error)]
713 #[error("internal invariant violated: this error is deprecated and should never be returned")]
714 #[deprecated = "this error is deprecated and should never be returned"]
715 pub struct ActionAttrEvalError();
716
717 #[derive(Debug, Diagnostic, Error)]
723 #[error("unsupported feature used in schema")]
724 #[diagnostic(transparent)]
725 pub struct UnsupportedFeatureError(#[from] pub(crate) UnsupportedFeature);
726
727 #[derive(Debug)]
728 pub(crate) enum ContextOrShape {
729 ActionContext(EntityUID),
730 EntityTypeShape(EntityType),
731 }
732
733 impl ContextOrShape {
734 pub fn loc(&self) -> Option<&Loc> {
735 match self {
736 ContextOrShape::ActionContext(uid) => uid.loc(),
737 ContextOrShape::EntityTypeShape(ty) => ty.loc(),
738 }
739 }
740 }
741
742 impl std::fmt::Display for ContextOrShape {
743 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
744 match self {
745 ContextOrShape::ActionContext(action) => write!(f, "Context for action {action}"),
746 ContextOrShape::EntityTypeShape(entity_type) => {
747 write!(f, "Shape for entity type {entity_type}")
748 }
749 }
750 }
751 }
752
753 #[derive(Debug, Diagnostic, Error)]
754 pub(crate) enum UnsupportedFeature {
755 #[error("records and entities with `additionalAttributes` are experimental, but the experimental `partial-validate` feature is not enabled")]
756 OpenRecordsAndEntities,
757 #[error("action declared with attributes: [{}]", .0.iter().join(", "))]
759 ActionAttributes(Vec<String>),
760 }
761
762 #[derive(Debug, Error)]
768 #[error("{err}")]
769 pub struct JsonDeserializationError {
770 err: serde_json::Error,
772 advice: Option<JsonDeserializationAdvice>,
774 }
775
776 impl Diagnostic for JsonDeserializationError {
777 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
778 self.advice
779 .as_ref()
780 .map(|h| Box::new(h) as Box<dyn Display>)
781 }
782 }
783
784 #[derive(Debug, Error)]
785 enum JsonDeserializationAdvice {
786 #[error("this API was expecting a schema in the JSON format; did you mean to use a different function, which expects the Cedar schema format?")]
787 CedarFormat,
788 #[error("JSON formatted schema must specify a namespace. If you want to use the empty namespace, explicitly specify it with `{{ \"\": {{..}} }}`")]
789 MissingNamespace,
790 }
791
792 impl JsonDeserializationError {
793 pub(crate) fn new(err: serde_json::Error, src: Option<&str>) -> Self {
797 match src {
798 None => Self { err, advice: None },
799 Some(src) => {
800 let advice = match src.trim_start().chars().next() {
802 None => None, Some('{') => {
804 if let Ok(serde_json::Value::Object(obj)) =
807 serde_json::from_str::<serde_json::Value>(src)
808 {
809 if obj.contains_key("entityTypes")
810 || obj.contains_key("actions")
811 || obj.contains_key("commonTypes")
812 {
813 Some(JsonDeserializationAdvice::MissingNamespace)
816 } else {
817 None
819 }
820 } else {
821 None
823 }
824 }
825 Some(_) => Some(JsonDeserializationAdvice::CedarFormat), };
827 Self { err, advice }
828 }
829 }
830 }
831 }
832
833 #[derive(Error, Debug)]
839 #[error("unknown extension type `{actual}`")]
840 pub struct UnknownExtensionTypeError {
841 pub(crate) actual: Name,
842 pub(crate) suggested_replacement: Option<String>,
843 }
844
845 impl Diagnostic for UnknownExtensionTypeError {
846 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
847 self.suggested_replacement.as_ref().map(|suggestion| {
848 Box::new(format!("did you mean `{suggestion}`?")) as Box<dyn Display>
849 })
850 }
851
852 impl_diagnostic_from_method_on_field!(actual, loc);
853 }
854
855 #[derive(Error, Debug)]
862 #[error("internal invariant violated: failed to find a common-type definition for {name}")]
863 pub struct CommonTypeInvariantViolationError {
864 pub(crate) name: InternalName,
866 }
867
868 impl Diagnostic for CommonTypeInvariantViolationError {
869 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
870 Some(Box::new("please file an issue at <https://github.com/cedar-policy/cedar/issues> including the schema that caused this error"))
871 }
872
873 impl_diagnostic_from_method_on_field!(name, loc);
874 }
875
876 #[derive(Error, Debug)]
883 #[error("internal invariant violated: failed to find {} for {}", if .euids.len() > 1 { "action definitions" } else { "an action definition" }, .euids.iter().join(", "))]
884 pub struct ActionInvariantViolationError {
885 pub(crate) euids: NonEmpty<EntityUID>,
887 }
888
889 impl Diagnostic for ActionInvariantViolationError {
890 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
891 Some(Box::new("please file an issue at <https://github.com/cedar-policy/cedar/issues> including the schema that caused this error"))
892 }
893
894 impl_diagnostic_from_method_on_nonempty_field!(euids, loc);
895 }
896}