1lalrpop_mod!(
21 #[allow(warnings, unused, missing_debug_implementations)]
22 #[allow(clippy::unwrap_used)]
24 #[allow(clippy::indexing_slicing)]
26 #[allow(clippy::unreachable)]
28 #[allow(clippy::panic)]
30 pub grammar,
31 "/src/parser/grammar.rs"
32);
33
34use super::*;
35use std::sync::Arc;
36
37fn parse_collect_errors<'a, P, T>(
41 parser: &P,
42 parse: impl FnOnce(
43 &P,
44 &mut Vec<err::RawErrorRecovery<'a>>,
45 &Arc<str>,
46 &'a str,
47 ) -> Result<T, err::RawParseError<'a>>,
48 text: &'a str,
49) -> Result<T, err::ParseErrors> {
50 let mut errs = Vec::new();
51 let result = parse(parser, &mut errs, &Arc::from(text), text);
52
53 let errors = errs
54 .into_iter()
55 .map(|rc| err::ToCSTError::from_raw_err_recovery(rc, Arc::from(text)))
56 .map(Into::into);
57 let parsed = match result {
58 Ok(parsed) => parsed,
59 Err(e) => {
60 return Err(err::ParseErrors::new(
61 err::ToCSTError::from_raw_parse_err(e, Arc::from(text)).into(),
62 errors,
63 ));
64 }
65 };
66 match err::ParseErrors::from_iter(errors) {
67 Some(errors) => Err(errors),
68 None => Ok(parsed),
69 }
70}
71
72lazy_static::lazy_static! {
74 static ref POLICIES_PARSER: grammar::PoliciesParser = grammar::PoliciesParser::new();
75 static ref POLICY_PARSER: grammar::PolicyParser = grammar::PolicyParser::new();
76 static ref EXPR_PARSER: grammar::ExprParser = grammar::ExprParser::new();
77 static ref REF_PARSER: grammar::RefParser = grammar::RefParser::new();
78 static ref PRIMARY_PARSER: grammar::PrimaryParser = grammar::PrimaryParser::new();
79 static ref NAME_PARSER: grammar::NameParser = grammar::NameParser::new();
80 static ref IDENT_PARSER: grammar::IdentParser = grammar::IdentParser::new();
81}
82
83pub fn parse_policies(text: &str) -> Result<Node<Option<cst::Policies>>, err::ParseErrors> {
85 parse_collect_errors(&*POLICIES_PARSER, grammar::PoliciesParser::parse, text)
86}
87
88pub fn parse_policy(text: &str) -> Result<Node<Option<cst::Policy>>, err::ParseErrors> {
90 parse_collect_errors(&*POLICY_PARSER, grammar::PolicyParser::parse, text)
91}
92
93pub fn parse_expr(text: &str) -> Result<Node<Option<cst::Expr>>, err::ParseErrors> {
95 parse_collect_errors(&*EXPR_PARSER, grammar::ExprParser::parse, text)
96}
97
98pub fn parse_ref(text: &str) -> Result<Node<Option<cst::Ref>>, err::ParseErrors> {
100 parse_collect_errors(&*REF_PARSER, grammar::RefParser::parse, text)
101}
102
103pub fn parse_primary(text: &str) -> Result<Node<Option<cst::Primary>>, err::ParseErrors> {
105 parse_collect_errors(&*PRIMARY_PARSER, grammar::PrimaryParser::parse, text)
106}
107
108pub fn parse_name(text: &str) -> Result<Node<Option<cst::Name>>, err::ParseErrors> {
110 parse_collect_errors(&*NAME_PARSER, grammar::NameParser::parse, text)
111}
112
113pub fn parse_ident(text: &str) -> Result<Node<Option<cst::Ident>>, err::ParseErrors> {
115 parse_collect_errors(&*IDENT_PARSER, grammar::IdentParser::parse, text)
116}
117
118#[allow(clippy::panic)]
120#[allow(clippy::indexing_slicing)]
122#[cfg(test)]
123mod tests {
124 use crate::parser::test_utils::*;
125 use crate::test_utils::*;
126
127 use super::*;
128
129 #[track_caller]
130 fn assert_parse_succeeds<T>(
131 parse: impl FnOnce(&str) -> Result<Node<Option<T>>, err::ParseErrors>,
132 text: &str,
133 ) -> T {
134 parse(text)
135 .unwrap_or_else(|errs| panic!("failed to parse:\n{:?}", miette::Report::new(errs)))
136 .node
137 .expect("failed get CST")
138 }
139
140 #[track_caller]
141 fn assert_parse_fails<T: std::fmt::Debug>(
142 parse: impl FnOnce(&str) -> Result<Node<Option<T>>, err::ParseErrors>,
143 text: &str,
144 ) -> err::ParseErrors {
145 match parse(text) {
146 Ok(node) => {
147 panic!("parsing should have failed, but succeeded with:\n{node:?}")
148 }
149 Err(errs) => errs,
150 }
151 }
152
153 #[test]
154 fn expr1() {
155 assert_parse_succeeds(
156 parse_expr,
157 r#"
158 1
159 "#,
160 );
161 }
162
163 #[test]
164 fn expr2() {
165 assert_parse_succeeds(
166 parse_expr,
167 r#"
168 "string"
169 "#,
170 );
171 }
172
173 #[test]
174 fn expr3() {
175 assert_parse_succeeds(
176 parse_expr,
177 r#"
178 "string".foo == !7
179 "#,
180 );
181 }
182
183 #[test]
184 fn expr4() {
185 assert_parse_succeeds(
186 parse_expr,
187 r#"
188 5 < 3 || -7 == 2 && 3 >= 6
189 "#,
190 );
191 }
192
193 #[test]
194 fn expr5() {
195 assert_parse_succeeds(
196 parse_expr,
197 r#"
198 if 7 then 6 > 5 else !5 || "thursday"
199 "#,
200 );
201 }
202
203 #[test]
204 fn expr6() {
205 assert_parse_succeeds(
206 parse_expr,
207 r#"
208 if 7 then 6 > 5 else !5 || "thursday" && ((8) >= "fish")
209 "#,
210 );
211 }
212
213 #[test]
214 fn expr_overflow() {
215 let src = r#"
217 principal == -5555555555555555555555
218 "#;
219 let errs = assert_parse_fails(parse_expr, src);
220 expect_exactly_one_error(
221 src,
222 &errs,
223 &ExpectedErrorMessageBuilder::error(
224 "integer parse error: number too large to fit in target type",
225 )
226 .exactly_one_underline("5555555555555555555555")
227 .build(),
228 );
229 let src = r#"
230 principal == 5555555555555555555555
231 "#;
232 let errs = assert_parse_fails(parse_expr, src);
233 expect_exactly_one_error(
234 src,
235 &errs,
236 &ExpectedErrorMessageBuilder::error(
237 "integer parse error: number too large to fit in target type",
238 )
239 .exactly_one_underline("5555555555555555555555")
240 .build(),
241 );
242 }
243
244 #[test]
245 fn variable1() {
246 assert_parse_succeeds(
247 parse_policy,
248 r#"
249 permit(principal, action, var:h in 1);
250 "#,
251 );
252 }
253
254 #[test]
255 fn variable2() {
256 assert_parse_succeeds(
257 parse_policy,
258 r#"
259 permit(principal, action, more in 2);
260 "#,
261 );
262 }
263
264 #[test]
265 fn variable3() {
266 assert_parse_succeeds(
267 parse_policy,
268 r#"
269 permit(principal, action:a_name, resource);
270 "#,
271 );
272 }
273
274 #[test]
275 fn variable4() {
276 assert_parse_succeeds(
277 parse_policy,
278 r#"
279 permit(principalorsomeotherident, action, resource);
280 "#,
281 );
282 }
283
284 #[test]
285 fn variable6() {
286 let src = r#"
287 permit(var : in 6, action, resource);
288 "#;
289 let errs = assert_parse_fails(parse_policy, src);
290 expect_exactly_one_error(
291 src,
292 &errs,
293 &ExpectedErrorMessageBuilder::error("unexpected token `6`")
294 .exactly_one_underline_with_label(
295 "6",
296 "expected `!=`, `)`, `,`, `::`, `<`, `<=`, `==`, `>`, `>=`, `in`, or `is`",
297 )
298 .build(),
299 );
300 }
301
302 #[test]
303 fn member1() {
304 assert_parse_succeeds(
305 parse_policy,
306 r#"
307 permit(principal, action, resource)
308 when{
309 2._field // oh, look, comments!
310 };
311 "#,
312 );
313 }
314
315 #[test]
316 fn member2() {
317 assert_parse_succeeds(
318 parse_policy,
319 r#"
320 permit(principal, action, resource)
321 when{
322 "first".some_ident()
323 };
324 "#,
325 );
326 }
327
328 #[test]
329 fn member3() {
330 assert_parse_succeeds(
331 parse_policy,
332 r#"
333 permit(principal, action, resource)
334 when{
335 [2,3,4].foo[2]
336 };
337 "#,
338 );
339 }
340
341 #[test]
342 fn member4() {
343 assert_parse_succeeds(
344 parse_policy,
345 r#"
346 permit(principal, action, resource)
347 when{
348 {3<-4:"what?","ok then":-5>4}
349 };
350 "#,
351 );
352 }
353
354 #[test]
355 fn member5() {
356 assert_parse_succeeds(
357 parse_policy,
358 r#"
359 permit(principal, action, resource)
360 when{
361 [3<4,"ok then",17,("none")]
362 };
363 "#,
364 );
365 }
366
367 #[test]
368 fn member6() {
369 assert_parse_succeeds(
370 parse_policy,
371 r#"
372 permit(principal, action, resource)
373 when{
374 one.two
375 };
376 "#,
377 );
378 }
379
380 #[test] fn member7() {
382 let src = r#"
383 permit(principal, action, resource)
384 when{
385 one{num:true,trivia:"first!"}
386 };
387 "#;
388 let errs = assert_parse_fails(parse_policy, src);
389 expect_n_errors(src, &errs, 2);
390 expect_some_error_matches(
391 src,
392 &errs,
393 &ExpectedErrorMessageBuilder::error("unexpected token `{`")
394 .exactly_one_underline_with_label("{", "expected `!=`, `&&`, `(`, `*`, `+`, `-`, `.`, `::`, `<`, `<=`, `==`, `>`, `>=`, `[`, `||`, `}`, `has`, `in`, `is`, or `like`")
395 .build(),
396 );
397 expect_some_error_matches(
398 src,
399 &errs,
400 &ExpectedErrorMessageBuilder::error("unexpected token `}`")
401 .exactly_one_underline_with_label("}", "expected `;` or identifier")
402 .build(),
403 );
404 }
405
406 #[test]
407 fn member8() {
408 assert_parse_succeeds(
409 parse_policy,
410 r#"
411 permit(principal, action, resource)
412 when{
413 {2:true,4:me}.with["pizza"]
414 };
415 "#,
416 );
417 }
418
419 #[test]
420 fn member9() {
421 assert_parse_succeeds(
422 parse_policy,
423 r#"
424 permit(principal, action, resource)
425 when{
426 AllRects({two:2,four:3+5/5})
427 };
428 "#,
429 );
430 }
431
432 #[test]
433 fn ident1() {
434 assert_parse_succeeds(
435 parse_ident,
436 r#"
437 principal
438 "#,
439 );
440 }
441
442 #[test]
443 fn ident2() {
444 assert_parse_succeeds(
446 parse_ident,
447 r#"
448 if
449 "#,
450 );
451 assert_parse_succeeds(
453 parse_ident,
454 r#"
455 false
456 "#,
457 );
458 }
459
460 #[test]
461 fn ident3() {
462 let src = r#"
464 if
465 "#;
466 let errs = assert_parse_fails(parse_expr, src);
467 expect_exactly_one_error(
468 src,
469 &errs,
470 &ExpectedErrorMessageBuilder::error("unexpected end of input")
471 .exactly_one_underline_with_label("", "expected `!`, `(`, `-`, `::`, `[`, `{`, `false`, identifier, `if`, number, `?principal`, `?resource`, string literal, or `true`")
472 .build(),
473 );
474 assert_parse_succeeds(
477 parse_expr,
478 r#"
479 foo
480 "#,
481 );
482 assert_parse_succeeds(
484 parse_expr,
485 r#"
486 foo
487 "#,
488 );
489 assert_parse_succeeds(
492 parse_expr,
493 r#"
494 if::then::else
495 "#,
496 );
497 assert_parse_succeeds(
498 parse_expr,
499 r#"
500 if::true::then::false::else::true
501 "#,
502 );
503 }
504
505 #[test]
506 fn ident4() {
507 assert_parse_succeeds(
509 parse_expr,
510 r#"
511 true(true)
512 "#,
513 );
514 let src = r#"
516 if(true)
517 "#;
518 let errs = assert_parse_fails(parse_expr, src);
519 expect_exactly_one_error(
520 src,
521 &errs,
522 &ExpectedErrorMessageBuilder::error("unexpected end of input")
523 .exactly_one_underline_with_label("", "expected `then`")
524 .build(),
525 );
526 }
527
528 #[test]
529 fn ident5() {
530 assert_parse_succeeds(
533 parse_expr,
534 r#"
535 {true : false}
536 "#,
537 );
538 assert_parse_succeeds(
539 parse_expr,
540 r#"
541 { if : true }
542 "#,
543 );
544 }
545
546 #[test]
547 fn ident6() {
548 assert_parse_succeeds(
551 parse_expr,
552 r#"
553 {true : false} has false
554 "#,
555 );
556 assert_parse_succeeds(
557 parse_expr,
558 r#"
559 { if : true } has if
560 "#,
561 );
562 }
563
564 #[test]
565 fn comments_has() {
566 assert_parse_succeeds(
568 parse_policy,
569 r#"
570 permit(principal, action,resource)
571 when{ principal //comment p
572 has //comment has
573 age //comment
574 };
575 "#,
576 );
577 }
578
579 #[test]
580 fn comments_like() {
581 assert_parse_succeeds(
583 parse_policy,
584 r#"
585 permit(principal, action,resource)
586 when{ principal //comment p
587 like //comment like
588
589 age //comment
590 };
591 "#,
592 );
593 }
594
595 #[test]
596 fn comments_and() {
597 assert_parse_succeeds(
599 parse_policy,
600 r#"
601 permit(principal, action,resource)
602 when{ 1 //comment p
603 && //comment &&
604 //comment &&
605 "hello" //comment
606 };
607 "#,
608 );
609 }
610
611 #[test]
612 fn comments_or() {
613 assert_parse_succeeds(
615 parse_policy,
616 r#"
617 permit(principal, action,resource)
618 when{ 1 //comment 1
619 // comment 1
620 || //comment ||
621 //comments ||
622 "hello" //comment
623 //comment hello
624 };
625 "#,
626 );
627 }
628
629 #[test]
630 fn comments_add() {
631 assert_parse_succeeds(
633 parse_policy,
634 r#"
635 permit(principal, action,resource)
636 when{ 1 //comment 1
637 //comment 1_2
638 + //comment +
639 //comment +
640 2 //comment 2
641 //comment 2
642 };
643 "#,
644 );
645 }
646
647 #[test]
648 fn comments_paren() {
649 assert_parse_succeeds(
651 parse_policy,
652 r#"
653 permit(principal, action,resource)
654 when{
655 ( //comment 1
656 ( //comment 2
657 1
658 ) //comment 3
659 ) //comment 4
660 };
661 "#,
662 );
663 }
664
665 #[test]
666 fn comments_set() {
667 assert_parse_succeeds(
669 parse_policy,
670 r#"
671 permit(principal, action,resource)
672 when{
673 [ // comment 1
674 "hello" //comment 2
675 , // comment 3
676 // comment 3-2
677 1 //comment 4
678 //comment 5
679 ] //comment 5-0
680
681 . //comment 5-1
682
683 contains //comment 5-2
684
685 ( //comment 6
686
687 "a" //comment 7
688
689 ) //comment 20
690 };
691 "#,
692 );
693 }
694
695 #[test]
696 fn comments_if() {
697 assert_parse_succeeds(
699 parse_policy,
700 r#"
701 permit(principal, action,resource)
702 when{
703 ( //comment open outer
704 ( //comment open inner
705 if //comment if
706 1 //comment
707 < //comment <
708 2 //commment 2
709 then // comment then
710 "hello" //comment hello
711 else //comment else
712 1 //comment 1
713 ) //comment close inner
714 ) //comment close outer
715 };
716 "#,
717 );
718 }
719
720 #[test]
721 fn comments_member_access() {
722 assert_parse_succeeds(
724 parse_policy,
725 r#"
726 permit(principal, action,resource)
727 when{ principal. //comment .
728 age // comment age
729 };
730 "#,
731 );
732 }
733
734 #[test]
735 fn comments_principal() {
736 assert_parse_succeeds(
738 parse_policy,
739 r#"
740 permit(principal //comment 1
741 ==
742 User::"alice" //comment 3
743 , //comment 4
744 action,resource);
745 "#,
746 );
747 }
748
749 #[test]
750 fn comments_annotation() {
751 assert_parse_succeeds(
753 parse_policy,
754 r#"
755 //comment policy
756 // comment policy 2
757 @anno("good annotation") // comments after annotation
758 // comments after annotation 2
759 permit(principal //comment 1
760 ==
761 User::"alice" //comment 3
762 , //comment 4
763 action,resource);
764 "#,
765 );
766 }
767
768 #[test]
769 fn comments_policy() {
770 assert_parse_succeeds(
772 parse_policy,
773 r#"
774 //comment policy 1
775 //comment policy 2
776 permit( //comment 3
777 // comment 4
778 principal //comment principal
779 == //comment == 1
780 //comment == 2
781 User::"alice" //comment alice
782 , //comment comma 1
783 //comment comma 2
784 action //comment action 1
785 //comment action 2
786 , //comment comma action
787 resource // comment resource
788 )
789 //comment 5
790 //comment 6
791 ;
792 "#,
793 );
794 let src = r#" /* multi-line
796 comment */
797 permit(principal, action, resource)
798 when{
799 one.two
800 };
801 "#;
802 let errs = assert_parse_fails(parse_policy, src);
803 expect_exactly_one_error(
804 src,
805 &errs,
806 &ExpectedErrorMessageBuilder::error("unexpected token `/`")
807 .exactly_one_underline_with_label("/", "expected `@` or identifier")
808 .build(),
809 );
810 let src = r#"
811 1 /* multi-line
812 comment */d
813 "#;
814 let errs = assert_parse_fails(parse_expr, src);
815 expect_exactly_one_error(
816 src,
817 &errs,
818 &ExpectedErrorMessageBuilder::error("unexpected token `*`")
819 .exactly_one_underline_with_label("*", "expected `!`, `(`, `-`, `[`, `{`, `false`, identifier, `if`, number, `?principal`, `?resource`, string literal, or `true`")
820 .build(),
821 );
822 }
823
824 #[test]
825 fn no_comments_policy() {
826 assert_parse_succeeds(
828 parse_policy,
829 r#"
830 permit(
831 principal
832 ==
833 User::"alice"
834 ,
835 action
836
837 ,
838 resource
839 )
840 ;
841 "#,
842 );
843 }
844
845 #[test]
846 fn no_comments_policy2() {
847 assert_parse_succeeds(
848 parse_policy,
849 r#"permit (
850 principal == IAM::Principal::"arn:aws:iam::12345678901:user/Dave",
851 action == S3::Action::"GetAccountPublicAccessBlock",
852 resource == Account::"12345678901"
853 );"#,
854 );
855 }
856
857 #[test]
858 fn no_comments_policy4() {
859 assert_parse_succeeds(
860 parse_policy,
861 r#"
862 permit(principal,action,resource,context)
863 when {
864 context.contains(3,"four",five(6,7))
865};"#,
866 );
867 }
868 #[test]
869 fn no_comments_policy5() {
870 assert_parse_succeeds(
871 parse_policy,
872 r#"
873 permit (
874 principal,
875 action,
876 resource == Album::{uid: "772358b3-de11-42dc-8681-f0a32e34aab8",
877 displayName: "vacation_photos"}
878);"#,
879 );
880 }
881
882 #[test]
883 fn policies1() {
884 assert_parse_succeeds(
885 parse_policy,
886 r#"
887 permit(principal:p,action:a,resource:r)when{w}unless{u}advice{"doit"};
888 "#,
889 );
890 }
891
892 #[test]
893 fn policies2() {
894 assert_parse_succeeds(
895 parse_policies,
896 r#"
897 permit(
898 principal in Group::"jane_friends", // Policy c1
899 action in [PhotoOp::"view", PhotoOp::"comment"],
900 resource in Album::"jane_trips",
901 context:Group
902 );
903 "#,
904 );
905 }
906
907 #[test]
908 fn policies3() {
909 let policies = assert_parse_succeeds(
910 parse_policies,
911 r#"
912 forbid(principal, action, resource) // Policy c2
913 when { "private" in resource.tags } // resource.tags is a set of strings
914 unless { resource in user.account };
915 "#,
916 );
917
918 assert!(
920 policies.0.iter().all(|p| p.node.is_some()),
921 "Unexpected parser failure"
922 );
923 }
924
925 #[test]
926 fn policies3p() {
929 let policies = assert_parse_succeeds(
930 parse_policies,
931 r#"
932 forbid(principality, action, resource) // Policy c2
933 when { "private" in resource.tags } // resource.tags is a set of strings
934 unless { resource in user.account };
935 "#,
936 );
937
938 assert!(
940 policies.0.iter().all(|p| p.node.is_some()),
941 "Unexpected parser failure"
942 );
943 }
944
945 #[test]
946 fn policies4() {
947 let policies = assert_parse_succeeds(
948 parse_policies,
949 r#"
950 permit(principal:p,action:a,resource:r)when{w}unless{u}advice{"doit"};
951
952 permit(principal in Group::"jane_friends", // Policy c1
953 action in [PhotoOp::"view", PhotoOp::"comment"],
954 resource in Album::"jane_trips");
955
956 forbid(principal, action, resource) // Policy c2
957 when { "private" in resource.tags } // resource.tags is a set of strings
958 unless { resource in user.account };
959 "#,
960 );
961
962 assert!(
964 policies.0.iter().all(|p| p.node.is_some()),
965 "Unexpected parser failure"
966 );
967 }
968
969 #[test]
970 fn policies5() {
971 let policies = assert_parse_succeeds(
972 parse_policies,
973 r#"
974 permit (
975 principal == User::"alice",
976 action in PhotoflashRole::"viewer",
977 resource in Account::"jane"
978 )
979 advice {
980 "{\"type\":\"PhotoFilterInstruction\", \"anonymize\":true}"
981 };
982 "#,
983 );
984
985 assert!(
987 policies.0.iter().all(|p| p.node.is_some()),
988 "Unexpected parser failure"
989 );
990 }
991
992 #[test]
993 fn policies6() {
994 let src = r#"
996 // use a number to error
997 3(principal:p,action:a,resource:r)when{w}unless{u}advice{"doit"};
998 permit(principal:p,action:a,resource:r)when{w}unless{u}advice{"doit"};
999 permit(principal:p,action:a,resource:r)when{w}unless{u}advice{"doit"};
1000 "#;
1001 let policies = POLICIES_PARSER
1002 .parse(&mut Vec::new(), &Arc::from(src), src)
1003 .expect("parser error")
1004 .node
1005 .expect("no data");
1006 assert_eq!(policies.0.into_iter().filter_map(|p| p.node).count(), 2);
1007 }
1008
1009 #[test]
1010 fn policy_annotations_ok() {
1011 let policies = assert_parse_succeeds(
1012 parse_policies,
1013 r#"
1014 @anno("good annotation") permit (principal, action, resource);
1015 @anno1("good")@anno2("annotation") permit (principal, action, resource);
1016 @long6wordphraseisident007("good annotation") permit (principal, action, resource);
1017 @ spacy ( " good annotation " ) permit (principal, action, resource);
1018 "#,
1019 );
1020 assert_eq!(policies.0.into_iter().filter_map(|p| p.node).count(), 4);
1022 }
1023
1024 #[test]
1025 fn policy_annotations_no_value_ok() {
1026 let policy = assert_parse_succeeds(
1027 parse_policy,
1028 r#"@foo permit (principal, action, resource);"#,
1029 );
1030 let annotation = policy.annotations.first().unwrap().as_inner().unwrap();
1031 assert_eq!(annotation.value, None);
1032 assert_eq!(
1033 annotation.key.as_inner().unwrap().to_string(),
1034 "foo".to_string()
1035 );
1036 assert_eq!(policy.annotations.len(), 1);
1037 }
1038
1039 #[test]
1040 fn policy_annotations_bad_id() {
1041 let src = r#"
1042 @bad-annotation("bad") permit (principal, action, resource);
1043 "#;
1044 let errs = assert_parse_fails(parse_policies, src);
1045 expect_exactly_one_error(
1046 src,
1047 &errs,
1048 &ExpectedErrorMessageBuilder::error("unexpected token `-`")
1049 .exactly_one_underline_with_label("-", "expected `(`, `@`, or identifier")
1050 .build(),
1051 );
1052
1053 let src = r#"
1054 @hi mom("this should be invalid")
1055 permit(principal, action, resource);
1056 "#;
1057 let errs = assert_parse_fails(parse_policies, src);
1058 expect_exactly_one_error(
1059 src,
1060 &errs,
1061 &ExpectedErrorMessageBuilder::error("unexpected token `\"this should be invalid\"`")
1062 .exactly_one_underline_with_label(
1063 "\"this should be invalid\"",
1064 "expected `)` or identifier",
1065 )
1066 .build(),
1067 );
1068
1069 let src = r#"
1070 @hi+mom("this should be invalid")
1071 permit(principal, action, resource);
1072 "#;
1073 let errs = assert_parse_fails(parse_policies, src);
1074 expect_exactly_one_error(
1075 src,
1076 &errs,
1077 &ExpectedErrorMessageBuilder::error("unexpected token `+`")
1078 .exactly_one_underline_with_label("+", "expected `(`, `@`, or identifier")
1079 .build(),
1080 );
1081 }
1082
1083 #[test]
1084 fn policy_annotations_bad_val() {
1085 let src = r#"
1086 @bad_annotation("bad","annotation") permit (principal, action, resource);
1087 "#;
1088 let errs = assert_parse_fails(parse_policies, src);
1089 expect_exactly_one_error(
1090 src,
1091 &errs,
1092 &ExpectedErrorMessageBuilder::error("unexpected token `,`")
1093 .exactly_one_underline_with_label(",", "expected `)`")
1094 .build(),
1095 );
1096
1097 let src = r#"
1098 @bad_annotation() permit (principal, action, resource);
1099 "#;
1100 let errs = assert_parse_fails(parse_policies, src);
1101 expect_exactly_one_error(
1102 src,
1103 &errs,
1104 &ExpectedErrorMessageBuilder::error("unexpected token `)`")
1105 .exactly_one_underline_with_label(")", "expected string literal")
1106 .build(),
1107 );
1108
1109 let src = r#"
1110 @bad_annotation(bad_annotation) permit (principal, action, resource);
1111 "#;
1112 let errs = assert_parse_fails(parse_policies, src);
1113 expect_exactly_one_error(
1114 src,
1115 &errs,
1116 &ExpectedErrorMessageBuilder::error("unexpected token `bad_annotation`")
1117 .exactly_one_underline_with_label("bad_annotation", "expected string literal")
1118 .build(),
1119 );
1120 }
1121
1122 #[test]
1123 fn policy_annotation_bad_position() {
1124 let src = r#"
1125 permit (@comment("your name here") principal, action, resource);
1126 "#;
1127 let errs = assert_parse_fails(parse_policies, src);
1128 expect_exactly_one_error(
1129 src,
1130 &errs,
1131 &ExpectedErrorMessageBuilder::error("unexpected token `@`")
1132 .exactly_one_underline_with_label("@", "expected `)` or identifier")
1133 .build(),
1134 );
1135 }
1136
1137 #[test]
1138 fn parse_idempotent() {
1139 let many_policies =
1140 std::fs::read_to_string("src/parser/testfiles/policies.cedar").expect("missing file");
1141 let cst1 = assert_parse_succeeds(parse_policies, &many_policies);
1142 let revert = format!("{}", cst1);
1143 let cst2 = assert_parse_succeeds(parse_policies, &revert);
1144 assert_eq!(cst1, cst2);
1145 }
1146
1147 #[test]
1148 fn error_recovery() {
1149 let src = r#"
1155 permit(principal, action, !) when { principal.foo == resource.bar};
1156 permit(principal, action, resource);
1157 "#;
1158 let errs = assert_parse_fails(parse_policies, src);
1159 expect_exactly_one_error(
1160 src,
1161 &errs,
1162 &ExpectedErrorMessageBuilder::error("unexpected token `!`")
1163 .exactly_one_underline_with_label("!", "expected identifier")
1164 .build(),
1165 );
1166
1167 let src = r#"
1169 permit(principal, action, !) when { principal.foo == resource.bar};
1170 permit(principal, action, +);
1171 "#;
1172 let errs = assert_parse_fails(parse_policies, src);
1173 expect_some_error_matches(
1174 src,
1175 &errs,
1176 &ExpectedErrorMessageBuilder::error("unexpected token `!`")
1177 .exactly_one_underline_with_label("!", "expected identifier")
1178 .build(),
1179 );
1180 expect_some_error_matches(
1181 src,
1182 &errs,
1183 &ExpectedErrorMessageBuilder::error("unexpected token `+`")
1184 .exactly_one_underline_with_label("+", "expected identifier")
1185 .build(),
1186 );
1187 expect_n_errors(src, &errs, 2);
1188
1189 let src = r#"
1191 permit(principal, action, !) when { principal.foo == resource.bar}
1192 "#;
1193 let errs = assert_parse_fails(parse_policies, src);
1194 expect_exactly_one_error(
1195 src,
1196 &errs,
1197 &ExpectedErrorMessageBuilder::error("unexpected token `!`")
1198 .exactly_one_underline_with_label("!", "expected identifier")
1199 .build(),
1200 );
1201 }
1202
1203 #[test]
1204 fn extended_has() {
1205 assert_parse_succeeds(
1206 parse_policy,
1207 r#"
1208 permit(principal, action, resource) when {
1209 principal has a.b
1210 };
1211 "#,
1212 );
1213 assert_parse_succeeds(
1214 parse_policy,
1215 r#"
1216 permit(principal, action, resource) when {
1217 principal has a.if
1218 };
1219 "#,
1220 );
1221 assert_parse_succeeds(
1222 parse_policy,
1223 r#"
1224 permit(principal, action, resource) when {
1225 principal has if.a
1226 };
1227 "#,
1228 );
1229 assert_parse_succeeds(
1230 parse_policy,
1231 r#"
1232 permit(principal, action, resource) when {
1233 principal has if.if
1234 };
1235 "#,
1236 );
1237 assert_parse_succeeds(
1238 parse_policy,
1239 r#"
1240 permit(principal, action, resource) when {
1241 principal has true.if
1242 };
1243 "#,
1244 );
1245 assert_parse_succeeds(
1246 parse_policy,
1247 r#"
1248 permit(principal, action, resource) when {
1249 principal has if.true
1250 };
1251 "#,
1252 );
1253 assert_parse_succeeds(
1254 parse_policy,
1255 r#"
1256 permit(principal, action, resource) when {
1257 principal has if.then.else.in.like.has.is.__cedar
1258 };
1259 "#,
1260 );
1261 assert_parse_succeeds(
1262 parse_policy,
1263 r#"
1264 permit(principal, action, resource) when {
1265 principal has 1+1
1266 };
1267 "#,
1268 );
1269 assert_parse_succeeds(
1270 parse_policy,
1271 r#"permit(principal, action, resource) when {
1272 principal has a - 1
1273 };"#,
1274 );
1275 assert_parse_succeeds(
1276 parse_policy,
1277 r#"permit(principal, action, resource) when {
1278 principal has a*3 + 1
1279 };"#,
1280 );
1281 assert_parse_succeeds(
1282 parse_policy,
1283 r#"permit(principal, action, resource) when {
1284 principal has 3*a
1285 };"#,
1286 );
1287 assert_parse_succeeds(
1288 parse_policy,
1289 r#"permit(principal, action, resource) when {
1290 principal has -a.b
1291 };"#,
1292 );
1293 assert_parse_succeeds(
1294 parse_policy,
1295 r#"permit(principal, action, resource) when {
1296 principal has !a.b
1297 };"#,
1298 );
1299 assert_parse_succeeds(
1300 parse_policy,
1301 r#"permit(principal, action, resource) when {
1302 principal has a::b.c
1303 };"#,
1304 );
1305 assert_parse_succeeds(
1306 parse_policy,
1307 r#"permit(principal, action, resource) when {
1308 principal has A::""
1309 };"#,
1310 );
1311 assert_parse_succeeds(
1312 parse_policy,
1313 r#"permit(principal, action, resource) when {
1314 principal has A::"".a
1315 };"#,
1316 );
1317 assert_parse_succeeds(
1318 parse_policy,
1319 r#"permit(principal, action, resource) when {
1320 principal has ?principal
1321 };"#,
1322 );
1323 assert_parse_succeeds(
1324 parse_policy,
1325 r#"permit(principal, action, resource) when {
1326 principal has ?principal.a
1327 };"#,
1328 );
1329 assert_parse_succeeds(
1330 parse_policy,
1331 r#"
1332 permit(principal, action, resource) when {
1333 principal has (b).a
1334 };
1335 "#,
1336 );
1337 assert_parse_fails(
1338 parse_policy,
1339 r#"
1340 permit(principal, action, resource) when {
1341 principal has a.(b)
1342 };
1343 "#,
1344 );
1345 assert_parse_fails(
1346 parse_policy,
1347 r#"
1348 permit(principal, action, resource) when {
1349 principal has a.1
1350 };
1351 "#,
1352 );
1353 }
1354}