1use crate::ast::NodeKind;
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
73pub enum NodeKindCategory {
74 Program,
76 Statement,
78 Expression,
80 Declaration,
82 Scope,
84 Literal,
86 Operator,
88 CommentDoc,
90 Recovery,
92 Unknown,
94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq)]
102pub struct NodeKindFlags {
103 pub executable: bool,
108
109 pub introduces_scope: bool,
114
115 pub declares_symbol: bool,
120
121 pub references_symbol: bool,
126
127 pub contains_children: bool,
132
133 pub recovery_artifact: bool,
138
139 pub safe_for_breakpoint: bool,
148}
149
150impl NodeKindFlags {
151 pub fn validate(&self) -> Result<(), String> {
156 if self.recovery_artifact && self.safe_for_breakpoint {
157 Err("recovery_artifact and safe_for_breakpoint must not both be true".into())
158 } else {
159 Ok(())
160 }
161 }
162}
163
164macro_rules! flags {
169 (
170 exec=$exec:expr,
171 scope=$scope:expr,
172 decl=$decl:expr,
173 refs=$refs:expr,
174 children=$children:expr,
175 recovery=$recovery:expr,
176 bp=$bp:expr
177 ) => {
178 NodeKindFlags {
179 executable: $exec,
180 introduces_scope: $scope,
181 declares_symbol: $decl,
182 references_symbol: $refs,
183 contains_children: $children,
184 recovery_artifact: $recovery,
185 safe_for_breakpoint: $bp,
186 }
187 };
188}
189
190impl NodeKind {
191 pub fn category(&self) -> NodeKindCategory {
196 match self {
197 NodeKind::Program { .. } => NodeKindCategory::Program,
198
199 NodeKind::ExpressionStatement { .. }
200 | NodeKind::Defer { .. }
201 | NodeKind::Try { .. }
202 | NodeKind::If { .. }
203 | NodeKind::LabeledStatement { .. }
204 | NodeKind::While { .. }
205 | NodeKind::For { .. }
206 | NodeKind::Foreach { .. }
207 | NodeKind::Given { .. }
208 | NodeKind::When { .. }
209 | NodeKind::Default { .. }
210 | NodeKind::StatementModifier { .. }
211 | NodeKind::Return { .. }
212 | NodeKind::LoopControl { .. }
213 | NodeKind::Goto { .. } => NodeKindCategory::Statement,
214
215 NodeKind::Variable { .. }
216 | NodeKind::VariableWithAttributes { .. }
217 | NodeKind::Assignment { .. }
218 | NodeKind::Binary { .. }
219 | NodeKind::Ternary { .. }
220 | NodeKind::Unary { .. }
221 | NodeKind::Diamond
222 | NodeKind::Readline { .. }
223 | NodeKind::Glob { .. }
224 | NodeKind::Typeglob { .. }
225 | NodeKind::ArrayLiteral { .. }
226 | NodeKind::HashLiteral { .. }
227 | NodeKind::Eval { .. }
228 | NodeKind::Do { .. }
229 | NodeKind::Tie { .. }
230 | NodeKind::Untie { .. }
231 | NodeKind::MethodCall { .. }
232 | NodeKind::FunctionCall { .. }
233 | NodeKind::IndirectCall { .. }
234 | NodeKind::Match { .. }
235 | NodeKind::Substitution { .. }
236 | NodeKind::Transliteration { .. }
237 | NodeKind::Identifier { .. } => NodeKindCategory::Expression,
238
239 NodeKind::VariableDeclaration { .. }
240 | NodeKind::VariableListDeclaration { .. }
241 | NodeKind::NestedVariableList { .. }
242 | NodeKind::Subroutine { .. }
243 | NodeKind::Prototype { .. }
244 | NodeKind::Signature { .. }
245 | NodeKind::MandatoryParameter { .. }
246 | NodeKind::OptionalParameter { .. }
247 | NodeKind::SlurpyParameter { .. }
248 | NodeKind::NamedParameter { .. }
249 | NodeKind::Method { .. }
250 | NodeKind::Package { .. }
251 | NodeKind::Use { .. }
252 | NodeKind::No { .. }
253 | NodeKind::PhaseBlock { .. }
254 | NodeKind::DataSection { .. }
255 | NodeKind::Class { .. }
256 | NodeKind::Format { .. } => NodeKindCategory::Declaration,
257
258 NodeKind::Block { .. } => NodeKindCategory::Scope,
259
260 NodeKind::Number { .. }
261 | NodeKind::String { .. }
262 | NodeKind::Heredoc { .. }
263 | NodeKind::Regex { .. }
264 | NodeKind::Undef => NodeKindCategory::Literal,
265
266 NodeKind::Ellipsis => NodeKindCategory::Operator,
267
268 NodeKind::Error { .. }
269 | NodeKind::MissingExpression
270 | NodeKind::MissingStatement
271 | NodeKind::MissingIdentifier
272 | NodeKind::MissingBlock
273 | NodeKind::UnknownRest => NodeKindCategory::Recovery,
274 }
275 }
276
277 pub fn flags(&self) -> NodeKindFlags {
288 match self {
290 NodeKind::Program { .. } => flags!(
291 exec = false,
292 scope = true,
293 decl = false,
294 refs = false,
295 children = true,
296 recovery = false,
297 bp = false
298 ),
299 NodeKind::ExpressionStatement { .. } => flags!(
300 exec = true,
301 scope = false,
302 decl = false,
303 refs = false,
304 children = true,
305 recovery = false,
306 bp = true
307 ),
308 NodeKind::VariableDeclaration { .. } => flags!(
309 exec = true,
310 scope = true,
311 decl = true,
312 refs = false,
313 children = true,
314 recovery = false,
315 bp = true
316 ),
317 NodeKind::VariableListDeclaration { .. } => flags!(
318 exec = true,
319 scope = true,
320 decl = true,
321 refs = false,
322 children = true,
323 recovery = false,
324 bp = true
325 ),
326 NodeKind::NestedVariableList { .. } => flags!(
327 exec = false,
328 scope = false,
329 decl = true,
330 refs = false,
331 children = true,
332 recovery = false,
333 bp = false
334 ),
335 NodeKind::Variable { .. } => flags!(
336 exec = false,
337 scope = false,
338 decl = false,
339 refs = true,
340 children = false,
341 recovery = false,
342 bp = false
343 ),
344 NodeKind::VariableWithAttributes { .. } => flags!(
345 exec = false,
346 scope = false,
347 decl = false,
348 refs = true,
349 children = true,
350 recovery = false,
351 bp = false
352 ),
353 NodeKind::Assignment { .. } => flags!(
354 exec = true,
355 scope = false,
356 decl = false,
357 refs = true,
358 children = true,
359 recovery = false,
360 bp = true
361 ),
362 NodeKind::Binary { .. } => flags!(
363 exec = true,
364 scope = false,
365 decl = false,
366 refs = true,
367 children = true,
368 recovery = false,
369 bp = true
370 ),
371 NodeKind::Ternary { .. } => flags!(
372 exec = true,
373 scope = false,
374 decl = false,
375 refs = true,
376 children = true,
377 recovery = false,
378 bp = true
379 ),
380 NodeKind::Unary { .. } => flags!(
381 exec = true,
382 scope = false,
383 decl = false,
384 refs = true,
385 children = true,
386 recovery = false,
387 bp = true
388 ),
389 NodeKind::Diamond => flags!(
390 exec = true,
391 scope = false,
392 decl = false,
393 refs = false,
394 children = false,
395 recovery = false,
396 bp = true
397 ),
398 NodeKind::Ellipsis => flags!(
399 exec = false,
400 scope = false,
401 decl = false,
402 refs = false,
403 children = false,
404 recovery = false,
405 bp = false
406 ),
407 NodeKind::Undef => flags!(
408 exec = false,
409 scope = false,
410 decl = false,
411 refs = false,
412 children = false,
413 recovery = false,
414 bp = false
415 ),
416 NodeKind::Readline { .. } => flags!(
417 exec = true,
418 scope = false,
419 decl = false,
420 refs = true,
421 children = false,
422 recovery = false,
423 bp = true
424 ),
425 NodeKind::Glob { .. } => flags!(
426 exec = true,
427 scope = false,
428 decl = false,
429 refs = false,
430 children = false,
431 recovery = false,
432 bp = true
433 ),
434 NodeKind::Typeglob { .. } => flags!(
435 exec = false,
436 scope = false,
437 decl = false,
438 refs = true,
439 children = false,
440 recovery = false,
441 bp = false
442 ),
443 NodeKind::Number { .. } => flags!(
444 exec = false,
445 scope = false,
446 decl = false,
447 refs = false,
448 children = false,
449 recovery = false,
450 bp = false
451 ),
452 NodeKind::String { .. } => flags!(
453 exec = false,
454 scope = false,
455 decl = false,
456 refs = false,
457 children = false,
458 recovery = false,
459 bp = false
460 ),
461 NodeKind::Heredoc { .. } => flags!(
462 exec = true,
463 scope = false,
464 decl = false,
465 refs = false,
466 children = false,
467 recovery = false,
468 bp = true
469 ),
470 NodeKind::ArrayLiteral { .. } => flags!(
471 exec = false,
472 scope = false,
473 decl = false,
474 refs = false,
475 children = true,
476 recovery = false,
477 bp = false
478 ),
479 NodeKind::HashLiteral { .. } => flags!(
480 exec = false,
481 scope = false,
482 decl = false,
483 refs = false,
484 children = true,
485 recovery = false,
486 bp = false
487 ),
488 NodeKind::Block { .. } => flags!(
489 exec = true,
490 scope = true,
491 decl = false,
492 refs = false,
493 children = true,
494 recovery = false,
495 bp = true
496 ),
497 NodeKind::Eval { .. } => flags!(
498 exec = true,
499 scope = true,
500 decl = false,
501 refs = false,
502 children = true,
503 recovery = false,
504 bp = true
505 ),
506 NodeKind::Do { .. } => flags!(
507 exec = true,
508 scope = false,
509 decl = false,
510 refs = false,
511 children = true,
512 recovery = false,
513 bp = true
514 ),
515 NodeKind::Defer { .. } => flags!(
516 exec = true,
517 scope = true,
518 decl = false,
519 refs = false,
520 children = true,
521 recovery = false,
522 bp = true
523 ),
524 NodeKind::Try { .. } => flags!(
525 exec = true,
526 scope = true,
527 decl = false,
528 refs = false,
529 children = true,
530 recovery = false,
531 bp = true
532 ),
533 NodeKind::If { .. } => flags!(
534 exec = true,
535 scope = false,
536 decl = false,
537 refs = true,
538 children = true,
539 recovery = false,
540 bp = true
541 ),
542 NodeKind::LabeledStatement { .. } => flags!(
543 exec = true,
544 scope = false,
545 decl = false,
546 refs = false,
547 children = true,
548 recovery = false,
549 bp = true
550 ),
551 NodeKind::While { .. } => flags!(
552 exec = true,
553 scope = false,
554 decl = false,
555 refs = true,
556 children = true,
557 recovery = false,
558 bp = true
559 ),
560 NodeKind::Tie { .. } => flags!(
561 exec = true,
562 scope = false,
563 decl = false,
564 refs = true,
565 children = true,
566 recovery = false,
567 bp = true
568 ),
569 NodeKind::Untie { .. } => flags!(
570 exec = true,
571 scope = false,
572 decl = false,
573 refs = true,
574 children = true,
575 recovery = false,
576 bp = true
577 ),
578 NodeKind::For { .. } => flags!(
579 exec = true,
580 scope = false,
581 decl = false,
582 refs = true,
583 children = true,
584 recovery = false,
585 bp = true
586 ),
587 NodeKind::Foreach { .. } => flags!(
588 exec = true,
589 scope = false,
590 decl = false,
591 refs = true,
592 children = true,
593 recovery = false,
594 bp = true
595 ),
596 NodeKind::Given { .. } => flags!(
597 exec = true,
598 scope = false,
599 decl = false,
600 refs = true,
601 children = true,
602 recovery = false,
603 bp = true
604 ),
605 NodeKind::When { .. } => flags!(
606 exec = true,
607 scope = false,
608 decl = false,
609 refs = true,
610 children = true,
611 recovery = false,
612 bp = true
613 ),
614 NodeKind::Default { .. } => flags!(
615 exec = true,
616 scope = false,
617 decl = false,
618 refs = false,
619 children = true,
620 recovery = false,
621 bp = true
622 ),
623 NodeKind::StatementModifier { .. } => flags!(
624 exec = true,
625 scope = false,
626 decl = false,
627 refs = true,
628 children = true,
629 recovery = false,
630 bp = true
631 ),
632 NodeKind::Subroutine { .. } => flags!(
633 exec = false,
634 scope = true,
635 decl = true,
636 refs = false,
637 children = true,
638 recovery = false,
639 bp = true
640 ),
641 NodeKind::Prototype { .. } => flags!(
642 exec = false,
643 scope = false,
644 decl = false,
645 refs = false,
646 children = false,
647 recovery = false,
648 bp = false
649 ),
650 NodeKind::Signature { .. } => flags!(
651 exec = false,
652 scope = false,
653 decl = false,
654 refs = false,
655 children = true,
656 recovery = false,
657 bp = false
658 ),
659 NodeKind::MandatoryParameter { .. } => flags!(
660 exec = false,
661 scope = false,
662 decl = true,
663 refs = false,
664 children = true,
665 recovery = false,
666 bp = false
667 ),
668 NodeKind::OptionalParameter { .. } => flags!(
669 exec = false,
670 scope = false,
671 decl = true,
672 refs = true,
673 children = true,
674 recovery = false,
675 bp = false
676 ),
677 NodeKind::SlurpyParameter { .. } => flags!(
678 exec = false,
679 scope = false,
680 decl = true,
681 refs = false,
682 children = true,
683 recovery = false,
684 bp = false
685 ),
686 NodeKind::NamedParameter { .. } => flags!(
687 exec = false,
688 scope = false,
689 decl = true,
690 refs = false,
691 children = true,
692 recovery = false,
693 bp = false
694 ),
695 NodeKind::Method { .. } => flags!(
696 exec = false,
697 scope = true,
698 decl = true,
699 refs = false,
700 children = true,
701 recovery = false,
702 bp = true
703 ),
704 NodeKind::Return { .. } => flags!(
705 exec = true,
706 scope = false,
707 decl = false,
708 refs = true,
709 children = true,
710 recovery = false,
711 bp = true
712 ),
713 NodeKind::LoopControl { .. } => flags!(
714 exec = true,
715 scope = false,
716 decl = false,
717 refs = false,
718 children = false,
719 recovery = false,
720 bp = true
721 ),
722 NodeKind::Goto { .. } => flags!(
723 exec = true,
724 scope = false,
725 decl = false,
726 refs = true,
727 children = true,
728 recovery = false,
729 bp = true
730 ),
731 NodeKind::MethodCall { .. } => flags!(
732 exec = true,
733 scope = false,
734 decl = false,
735 refs = true,
736 children = true,
737 recovery = false,
738 bp = true
739 ),
740 NodeKind::FunctionCall { .. } => flags!(
741 exec = true,
742 scope = false,
743 decl = false,
744 refs = true,
745 children = true,
746 recovery = false,
747 bp = true
748 ),
749 NodeKind::IndirectCall { .. } => flags!(
750 exec = true,
751 scope = false,
752 decl = false,
753 refs = true,
754 children = true,
755 recovery = false,
756 bp = true
757 ),
758 NodeKind::Regex { .. } => flags!(
759 exec = false,
760 scope = false,
761 decl = false,
762 refs = false,
763 children = false,
764 recovery = false,
765 bp = false
766 ),
767 NodeKind::Match { .. } => flags!(
768 exec = true,
769 scope = false,
770 decl = false,
771 refs = true,
772 children = true,
773 recovery = false,
774 bp = true
775 ),
776 NodeKind::Substitution { .. } => flags!(
777 exec = true,
778 scope = false,
779 decl = false,
780 refs = true,
781 children = true,
782 recovery = false,
783 bp = true
784 ),
785 NodeKind::Transliteration { .. } => flags!(
786 exec = true,
787 scope = false,
788 decl = false,
789 refs = true,
790 children = true,
791 recovery = false,
792 bp = true
793 ),
794 NodeKind::Package { .. } => flags!(
795 exec = true,
796 scope = true,
797 decl = true,
798 refs = false,
799 children = true,
800 recovery = false,
801 bp = true
802 ),
803 NodeKind::Use { .. } => flags!(
807 exec = true,
808 scope = false,
809 decl = false,
810 refs = false,
811 children = false,
812 recovery = false,
813 bp = false
814 ),
815 NodeKind::No { .. } => flags!(
819 exec = true,
820 scope = false,
821 decl = false,
822 refs = false,
823 children = false,
824 recovery = false,
825 bp = false
826 ),
827 NodeKind::PhaseBlock { .. } => flags!(
828 exec = true,
829 scope = true,
830 decl = false,
831 refs = false,
832 children = true,
833 recovery = false,
834 bp = true
835 ),
836 NodeKind::DataSection { .. } => flags!(
837 exec = false,
838 scope = false,
839 decl = false,
840 refs = false,
841 children = false,
842 recovery = false,
843 bp = false
844 ),
845 NodeKind::Class { .. } => flags!(
846 exec = false,
847 scope = true,
848 decl = true,
849 refs = false,
850 children = true,
851 recovery = false,
852 bp = true
853 ),
854 NodeKind::Format { .. } => flags!(
855 exec = false,
856 scope = false,
857 decl = true,
858 refs = false,
859 children = false,
860 recovery = false,
861 bp = false
862 ),
863 NodeKind::Identifier { .. } => flags!(
864 exec = false,
865 scope = false,
866 decl = false,
867 refs = true,
868 children = false,
869 recovery = false,
870 bp = false
871 ),
872 NodeKind::Error { .. } => flags!(
873 exec = false,
874 scope = false,
875 decl = false,
876 refs = false,
877 children = true,
878 recovery = true,
879 bp = false
880 ),
881 NodeKind::MissingExpression => flags!(
882 exec = false,
883 scope = false,
884 decl = false,
885 refs = false,
886 children = false,
887 recovery = true,
888 bp = false
889 ),
890 NodeKind::MissingStatement => flags!(
891 exec = false,
892 scope = false,
893 decl = false,
894 refs = false,
895 children = false,
896 recovery = true,
897 bp = false
898 ),
899 NodeKind::MissingIdentifier => flags!(
900 exec = false,
901 scope = false,
902 decl = false,
903 refs = false,
904 children = false,
905 recovery = true,
906 bp = false
907 ),
908 NodeKind::MissingBlock => flags!(
909 exec = false,
910 scope = false,
911 decl = false,
912 refs = false,
913 children = false,
914 recovery = true,
915 bp = false
916 ),
917 NodeKind::UnknownRest => flags!(
918 exec = false,
919 scope = false,
920 decl = false,
921 refs = false,
922 children = false,
923 recovery = true,
924 bp = false
925 ),
926 }
927 }
928
929 #[inline]
937 pub fn is_executable(&self) -> bool {
938 self.flags().executable
939 }
940
941 #[inline]
945 pub fn introduces_scope(&self) -> bool {
946 self.flags().introduces_scope
947 }
948
949 #[inline]
954 pub fn declares_symbol(&self) -> bool {
955 self.flags().declares_symbol
956 }
957
958 #[inline]
962 pub fn references_symbol(&self) -> bool {
963 self.flags().references_symbol
964 }
965
966 #[inline]
974 pub fn safe_for_breakpoint(&self) -> bool {
975 self.flags().safe_for_breakpoint
976 }
977
978 #[inline]
985 pub fn is_recovery(&self) -> bool {
986 self.flags().recovery_artifact
987 }
988
989 #[inline]
1001 pub fn contains_children(&self) -> bool {
1002 self.flags().contains_children
1003 }
1004}
1005
1006#[cfg(test)]
1015mod tests {
1016 use super::NodeKindCategory;
1017 use super::NodeKindFlags;
1018 use crate::ast::{Node, NodeKind};
1019 use perl_position_tracking::SourceLocation;
1020
1021 fn loc() -> SourceLocation {
1022 SourceLocation::new(0, 1)
1023 }
1024
1025 fn leaf() -> Node {
1026 Node::new(NodeKind::Identifier { name: "x".to_string() }, loc())
1027 }
1028
1029 fn block_node() -> Node {
1030 Node::new(NodeKind::Block { statements: vec![] }, loc())
1031 }
1032
1033 fn all_variants() -> Vec<NodeKind> {
1036 vec![
1037 NodeKind::Program { statements: vec![] },
1038 NodeKind::ExpressionStatement { expression: Box::new(leaf()) },
1039 NodeKind::VariableDeclaration {
1040 declarator: "my".to_string(),
1041 variable: Box::new(leaf()),
1042 attributes: vec![],
1043 initializer: None,
1044 },
1045 NodeKind::VariableListDeclaration {
1046 declarator: "my".to_string(),
1047 variables: vec![],
1048 attributes: vec![],
1049 initializer: None,
1050 },
1051 NodeKind::NestedVariableList { items: vec![] },
1052 NodeKind::Variable { sigil: "$".to_string(), name: "x".to_string() },
1053 NodeKind::VariableWithAttributes { variable: Box::new(leaf()), attributes: vec![] },
1054 NodeKind::Assignment {
1055 lhs: Box::new(leaf()),
1056 rhs: Box::new(leaf()),
1057 op: "=".to_string(),
1058 },
1059 NodeKind::Binary {
1060 op: "+".to_string(),
1061 left: Box::new(leaf()),
1062 right: Box::new(leaf()),
1063 },
1064 NodeKind::Ternary {
1065 condition: Box::new(leaf()),
1066 then_expr: Box::new(leaf()),
1067 else_expr: Box::new(leaf()),
1068 },
1069 NodeKind::Unary { op: "-".to_string(), operand: Box::new(leaf()) },
1070 NodeKind::Diamond,
1071 NodeKind::Ellipsis,
1072 NodeKind::Undef,
1073 NodeKind::Readline { filehandle: None },
1074 NodeKind::Glob { pattern: "*.pl".to_string() },
1075 NodeKind::Typeglob { name: "foo".to_string() },
1076 NodeKind::Number { value: "42".to_string() },
1077 NodeKind::String { value: "hello".to_string(), interpolated: false },
1078 NodeKind::Heredoc {
1079 delimiter: "EOF".to_string(),
1080 content: "body".to_string(),
1081 interpolated: false,
1082 indented: false,
1083 command: false,
1084 body_span: None,
1085 },
1086 NodeKind::ArrayLiteral { elements: vec![] },
1087 NodeKind::HashLiteral { pairs: vec![] },
1088 NodeKind::Block { statements: vec![] },
1089 NodeKind::Eval { block: Box::new(block_node()) },
1090 NodeKind::Do { block: Box::new(block_node()) },
1091 NodeKind::Defer { block: Box::new(block_node()) },
1092 NodeKind::Try {
1093 body: Box::new(block_node()),
1094 catch_blocks: vec![],
1095 finally_block: None,
1096 },
1097 NodeKind::If {
1098 condition: Box::new(leaf()),
1099 then_branch: Box::new(block_node()),
1100 elsif_branches: vec![],
1101 else_branch: None,
1102 keyword: None,
1103 },
1104 NodeKind::LabeledStatement {
1105 label: "OUTER".to_string(),
1106 statement: Box::new(Node::new(
1107 NodeKind::LoopControl { op: "next".to_string(), label: None },
1108 loc(),
1109 )),
1110 },
1111 NodeKind::While {
1112 condition: Box::new(leaf()),
1113 body: Box::new(block_node()),
1114 continue_block: None,
1115 keyword: None,
1116 },
1117 NodeKind::Tie { variable: Box::new(leaf()), package: Box::new(leaf()), args: vec![] },
1118 NodeKind::Untie { variable: Box::new(leaf()) },
1119 NodeKind::For {
1120 init: None,
1121 condition: None,
1122 update: None,
1123 body: Box::new(block_node()),
1124 continue_block: None,
1125 },
1126 NodeKind::Foreach {
1127 variable: Box::new(leaf()),
1128 list: Box::new(leaf()),
1129 body: Box::new(block_node()),
1130 continue_block: None,
1131 },
1132 NodeKind::Given { expr: Box::new(leaf()), body: Box::new(block_node()) },
1133 NodeKind::When { condition: Box::new(leaf()), body: Box::new(block_node()) },
1134 NodeKind::Default { body: Box::new(block_node()) },
1135 NodeKind::StatementModifier {
1136 statement: Box::new(leaf()),
1137 modifier: "if".to_string(),
1138 condition: Box::new(leaf()),
1139 },
1140 NodeKind::Subroutine {
1141 name: Some("foo".to_string()),
1142 name_span: None,
1143 declarator: None,
1144 prototype: None,
1145 signature: None,
1146 attributes: vec![],
1147 body: Box::new(block_node()),
1148 },
1149 NodeKind::Prototype { content: "$@".to_string() },
1150 NodeKind::Signature { parameters: vec![] },
1151 NodeKind::MandatoryParameter { variable: Box::new(leaf()) },
1152 NodeKind::OptionalParameter {
1153 variable: Box::new(leaf()),
1154 default_value: Box::new(leaf()),
1155 },
1156 NodeKind::SlurpyParameter { variable: Box::new(leaf()) },
1157 NodeKind::NamedParameter { variable: Box::new(leaf()) },
1158 NodeKind::Method {
1159 name: "bar".to_string(),
1160 name_span: None,
1161 signature: None,
1162 attributes: vec![],
1163 body: Box::new(block_node()),
1164 },
1165 NodeKind::Return { value: None },
1166 NodeKind::LoopControl { op: "next".to_string(), label: None },
1167 NodeKind::Goto { target: Box::new(leaf()) },
1168 NodeKind::MethodCall {
1169 object: Box::new(leaf()),
1170 method: "foo".to_string(),
1171 args: vec![],
1172 },
1173 NodeKind::FunctionCall { name: "print".to_string(), args: vec![] },
1174 NodeKind::IndirectCall {
1175 method: "new".to_string(),
1176 object: Box::new(leaf()),
1177 args: vec![],
1178 },
1179 NodeKind::Regex {
1180 pattern: "foo".to_string(),
1181 replacement: None,
1182 modifiers: "".to_string(),
1183 has_embedded_code: false,
1184 },
1185 NodeKind::Match {
1186 expr: Box::new(leaf()),
1187 pattern: "foo".to_string(),
1188 modifiers: "".to_string(),
1189 has_embedded_code: false,
1190 negated: false,
1191 },
1192 NodeKind::Substitution {
1193 expr: Box::new(leaf()),
1194 pattern: "foo".to_string(),
1195 replacement: "bar".to_string(),
1196 modifiers: "".to_string(),
1197 has_embedded_code: false,
1198 negated: false,
1199 },
1200 NodeKind::Transliteration {
1201 expr: Box::new(leaf()),
1202 search: "a".to_string(),
1203 replace: "b".to_string(),
1204 modifiers: "".to_string(),
1205 negated: false,
1206 },
1207 NodeKind::Package { name: "Foo".to_string(), name_span: loc(), block: None },
1208 NodeKind::Use { module: "strict".to_string(), args: vec![], has_filter_risk: false },
1209 NodeKind::No { module: "strict".to_string(), args: vec![], has_filter_risk: false },
1210 NodeKind::PhaseBlock {
1211 phase: "BEGIN".to_string(),
1212 phase_span: None,
1213 block: Box::new(block_node()),
1214 },
1215 NodeKind::DataSection { marker: "__DATA__".to_string(), body: None },
1216 NodeKind::Class {
1217 name: "Foo".to_string(),
1218 name_span: None,
1219 parents: vec![],
1220 body: Box::new(block_node()),
1221 },
1222 NodeKind::Format { name: "STDOUT".to_string(), name_span: None, body: "".to_string() },
1223 NodeKind::Identifier { name: "foo".to_string() },
1224 NodeKind::Error {
1225 message: "oops".to_string(),
1226 expected: vec![],
1227 found: None,
1228 partial: None,
1229 },
1230 NodeKind::MissingExpression,
1231 NodeKind::MissingStatement,
1232 NodeKind::MissingIdentifier,
1233 NodeKind::MissingBlock,
1234 NodeKind::UnknownRest,
1235 ]
1236 }
1237
1238 #[test]
1241 fn all_variants_return_category_and_valid_flags() {
1242 for kind in all_variants() {
1243 let _cat = kind.category();
1244 let flags = kind.flags();
1245 assert!(
1246 flags.validate().is_ok(),
1247 "variant {} failed validate(): {:?}",
1248 kind.kind_name(),
1249 flags.validate()
1250 );
1251 }
1252 }
1253
1254 #[test]
1257 fn category_spot_checks() {
1258 assert_eq!(NodeKind::Program { statements: vec![] }.category(), NodeKindCategory::Program);
1259 assert_eq!(
1260 NodeKind::If {
1261 condition: Box::new(leaf()),
1262 then_branch: Box::new(block_node()),
1263 elsif_branches: vec![],
1264 else_branch: None,
1265 keyword: None,
1266 }
1267 .category(),
1268 NodeKindCategory::Statement
1269 );
1270 assert_eq!(
1271 NodeKind::FunctionCall { name: "print".to_string(), args: vec![] }.category(),
1272 NodeKindCategory::Expression
1273 );
1274 assert_eq!(
1275 NodeKind::Subroutine {
1276 name: Some("foo".to_string()),
1277 name_span: None,
1278 declarator: None,
1279 prototype: None,
1280 signature: None,
1281 attributes: vec![],
1282 body: Box::new(block_node()),
1283 }
1284 .category(),
1285 NodeKindCategory::Declaration
1286 );
1287 assert_eq!(NodeKind::Block { statements: vec![] }.category(), NodeKindCategory::Scope);
1288 assert_eq!(
1289 NodeKind::Number { value: "1".to_string() }.category(),
1290 NodeKindCategory::Literal
1291 );
1292 assert_eq!(NodeKind::Ellipsis.category(), NodeKindCategory::Operator);
1293 assert_eq!(NodeKind::MissingExpression.category(), NodeKindCategory::Recovery);
1294 assert_eq!(NodeKind::UnknownRest.category(), NodeKindCategory::Recovery);
1295 }
1296
1297 #[test]
1300 fn recovery_artifact_implies_not_safe_for_breakpoint() {
1301 for kind in all_variants() {
1302 let flags = kind.flags();
1303 if flags.recovery_artifact {
1304 assert!(
1305 !flags.safe_for_breakpoint,
1306 "variant {} has recovery_artifact=true but safe_for_breakpoint=true",
1307 kind.kind_name()
1308 );
1309 }
1310 }
1311 }
1312
1313 #[test]
1316 fn convenience_accessors_match_flags() {
1317 for kind in all_variants() {
1318 let flags = kind.flags();
1319 assert_eq!(
1320 kind.is_executable(),
1321 flags.executable,
1322 "{}: is_executable() != flags.executable",
1323 kind.kind_name()
1324 );
1325 assert_eq!(
1326 kind.introduces_scope(),
1327 flags.introduces_scope,
1328 "{}: introduces_scope() != flags.introduces_scope",
1329 kind.kind_name()
1330 );
1331 assert_eq!(
1332 kind.declares_symbol(),
1333 flags.declares_symbol,
1334 "{}: declares_symbol() != flags.declares_symbol",
1335 kind.kind_name()
1336 );
1337 assert_eq!(
1338 kind.references_symbol(),
1339 flags.references_symbol,
1340 "{}: references_symbol() != flags.references_symbol",
1341 kind.kind_name()
1342 );
1343 assert_eq!(
1344 kind.contains_children(),
1345 flags.contains_children,
1346 "{}: contains_children() != flags.contains_children",
1347 kind.kind_name()
1348 );
1349 assert_eq!(
1350 kind.safe_for_breakpoint(),
1351 flags.safe_for_breakpoint,
1352 "{}: safe_for_breakpoint() != flags.safe_for_breakpoint",
1353 kind.kind_name()
1354 );
1355 assert_eq!(
1356 kind.is_recovery(),
1357 flags.recovery_artifact,
1358 "{}: is_recovery() != flags.recovery_artifact",
1359 kind.kind_name()
1360 );
1361 }
1362 }
1363
1364 #[test]
1367 fn flag_values_for_representative_variants() {
1368 let fc = NodeKind::FunctionCall { name: "say".to_string(), args: vec![] };
1370 assert!(fc.is_executable());
1371 assert!(fc.references_symbol());
1372 assert!(fc.safe_for_breakpoint());
1373 assert!(!fc.introduces_scope());
1374 assert!(!fc.declares_symbol());
1375 assert!(!fc.is_recovery());
1376
1377 let vd = NodeKind::VariableDeclaration {
1379 declarator: "my".to_string(),
1380 variable: Box::new(leaf()),
1381 attributes: vec![],
1382 initializer: None,
1383 };
1384 assert!(vd.is_executable());
1385 assert!(vd.introduces_scope());
1386 assert!(vd.declares_symbol());
1387 assert!(vd.safe_for_breakpoint());
1388
1389 let num = NodeKind::Number { value: "0".to_string() };
1391 assert!(!num.is_executable());
1392 assert!(!num.introduces_scope());
1393 assert!(!num.declares_symbol());
1394 assert!(!num.references_symbol());
1395 assert!(!num.safe_for_breakpoint());
1396 assert!(!num.is_recovery());
1397
1398 let err = NodeKind::Error {
1400 message: "bad".to_string(),
1401 expected: vec![],
1402 found: None,
1403 partial: None,
1404 };
1405 assert!(err.is_recovery());
1406 assert!(!err.safe_for_breakpoint());
1407
1408 for recovery_kind in [
1410 NodeKind::MissingExpression,
1411 NodeKind::MissingStatement,
1412 NodeKind::MissingIdentifier,
1413 NodeKind::MissingBlock,
1414 NodeKind::UnknownRest,
1415 ] {
1416 assert!(
1417 recovery_kind.is_recovery(),
1418 "{} should be recovery",
1419 recovery_kind.kind_name()
1420 );
1421 assert!(
1422 !recovery_kind.safe_for_breakpoint(),
1423 "{} should not be safe_for_breakpoint",
1424 recovery_kind.kind_name()
1425 );
1426 }
1427
1428 let blk = NodeKind::Block { statements: vec![] };
1430 assert!(blk.introduces_scope());
1431 assert!(blk.is_executable());
1432 assert!(blk.safe_for_breakpoint());
1433 }
1434
1435 #[test]
1438 fn flags_validate_rejects_recovery_with_breakpoint() {
1439 let good = NodeKindFlags {
1440 executable: false,
1441 introduces_scope: false,
1442 declares_symbol: false,
1443 references_symbol: false,
1444 contains_children: false,
1445 recovery_artifact: true,
1446 safe_for_breakpoint: false,
1447 };
1448 assert!(good.validate().is_ok());
1449
1450 let bad = NodeKindFlags {
1451 executable: false,
1452 introduces_scope: false,
1453 declares_symbol: false,
1454 references_symbol: false,
1455 contains_children: false,
1456 recovery_artifact: true,
1457 safe_for_breakpoint: true, };
1459 assert!(bad.validate().is_err());
1460 }
1461
1462 #[test]
1468 fn contains_children_accessor_leaf_variants() {
1469 assert!(
1471 !NodeKind::Number { value: "42".to_string() }.contains_children(),
1472 "Number should not contain children"
1473 );
1474 assert!(
1475 !NodeKind::String { value: "hello".to_string(), interpolated: false }
1476 .contains_children(),
1477 "String should not contain children"
1478 );
1479 assert!(
1480 !NodeKind::Variable { sigil: "$".to_string(), name: "x".to_string() }
1481 .contains_children(),
1482 "Variable should not contain children"
1483 );
1484 assert!(!NodeKind::Diamond.contains_children(), "Diamond should not contain children");
1485 assert!(!NodeKind::Undef.contains_children(), "Undef should not contain children");
1486 }
1487
1488 #[test]
1489 fn contains_children_accessor_parent_variants() {
1490 assert!(
1492 NodeKind::Block { statements: vec![leaf()] }.contains_children(),
1493 "Block with statements should contain children"
1494 );
1495 assert!(
1496 NodeKind::Program { statements: vec![leaf()] }.contains_children(),
1497 "Program should contain children"
1498 );
1499 assert!(
1500 NodeKind::VariableDeclaration {
1501 declarator: "my".to_string(),
1502 variable: Box::new(leaf()),
1503 attributes: vec![],
1504 initializer: None,
1505 }
1506 .contains_children(),
1507 "VariableDeclaration with variable should contain children"
1508 );
1509 assert!(
1510 NodeKind::Binary {
1511 op: "+".to_string(),
1512 left: Box::new(leaf()),
1513 right: Box::new(leaf()),
1514 }
1515 .contains_children(),
1516 "Binary should contain children"
1517 );
1518 assert!(
1519 NodeKind::FunctionCall { name: "print".to_string(), args: vec![leaf()] }
1520 .contains_children(),
1521 "FunctionCall with args should contain children"
1522 );
1523 }
1524
1525 fn all_variants_maximal() -> Vec<Node> {
1540 let n = |kind| Node::new(kind, loc());
1541 vec![
1542 n(NodeKind::Program { statements: vec![leaf()] }),
1543 n(NodeKind::ExpressionStatement { expression: Box::new(leaf()) }),
1544 n(NodeKind::VariableDeclaration {
1545 declarator: "my".to_string(),
1546 variable: Box::new(leaf()),
1547 attributes: vec![],
1548 initializer: Some(Box::new(leaf())),
1549 }),
1550 n(NodeKind::VariableListDeclaration {
1551 declarator: "my".to_string(),
1552 variables: vec![leaf()],
1553 attributes: vec![],
1554 initializer: Some(Box::new(leaf())),
1555 }),
1556 n(NodeKind::NestedVariableList { items: vec![leaf()] }),
1557 n(NodeKind::Variable { sigil: "$".to_string(), name: "x".to_string() }),
1558 n(NodeKind::VariableWithAttributes { variable: Box::new(leaf()), attributes: vec![] }),
1559 n(NodeKind::Assignment {
1560 lhs: Box::new(leaf()),
1561 rhs: Box::new(leaf()),
1562 op: "=".to_string(),
1563 }),
1564 n(NodeKind::Binary {
1565 op: "+".to_string(),
1566 left: Box::new(leaf()),
1567 right: Box::new(leaf()),
1568 }),
1569 n(NodeKind::Ternary {
1570 condition: Box::new(leaf()),
1571 then_expr: Box::new(leaf()),
1572 else_expr: Box::new(leaf()),
1573 }),
1574 n(NodeKind::Unary { op: "-".to_string(), operand: Box::new(leaf()) }),
1575 n(NodeKind::Diamond),
1576 n(NodeKind::Ellipsis),
1577 n(NodeKind::Undef),
1578 n(NodeKind::Readline { filehandle: Some("STDIN".to_string()) }),
1579 n(NodeKind::Glob { pattern: "*.pl".to_string() }),
1580 n(NodeKind::Typeglob { name: "foo".to_string() }),
1581 n(NodeKind::Number { value: "42".to_string() }),
1582 n(NodeKind::String { value: "hello".to_string(), interpolated: false }),
1583 n(NodeKind::Heredoc {
1584 delimiter: "EOF".to_string(),
1585 content: "body".to_string(),
1586 interpolated: false,
1587 indented: false,
1588 command: false,
1589 body_span: None,
1590 }),
1591 n(NodeKind::ArrayLiteral { elements: vec![leaf()] }),
1592 n(NodeKind::HashLiteral { pairs: vec![(leaf(), leaf())] }),
1593 n(NodeKind::Block { statements: vec![leaf()] }),
1594 n(NodeKind::Eval { block: Box::new(block_node()) }),
1595 n(NodeKind::Do { block: Box::new(block_node()) }),
1596 n(NodeKind::Defer { block: Box::new(block_node()) }),
1597 n(NodeKind::Try {
1598 body: Box::new(block_node()),
1599 catch_blocks: vec![(None, Box::new(block_node()))],
1600 finally_block: Some(Box::new(block_node())),
1601 }),
1602 n(NodeKind::If {
1603 condition: Box::new(leaf()),
1604 then_branch: Box::new(block_node()),
1605 elsif_branches: vec![(Box::new(leaf()), Box::new(block_node()))],
1606 else_branch: Some(Box::new(block_node())),
1607 keyword: None,
1608 }),
1609 n(NodeKind::LabeledStatement {
1610 label: "OUTER".to_string(),
1611 statement: Box::new(leaf()),
1612 }),
1613 n(NodeKind::While {
1614 condition: Box::new(leaf()),
1615 body: Box::new(block_node()),
1616 continue_block: Some(Box::new(block_node())),
1617 keyword: None,
1618 }),
1619 n(NodeKind::Tie {
1620 variable: Box::new(leaf()),
1621 package: Box::new(leaf()),
1622 args: vec![leaf()],
1623 }),
1624 n(NodeKind::Untie { variable: Box::new(leaf()) }),
1625 n(NodeKind::For {
1626 init: Some(Box::new(leaf())),
1627 condition: Some(Box::new(leaf())),
1628 update: Some(Box::new(leaf())),
1629 body: Box::new(block_node()),
1630 continue_block: Some(Box::new(block_node())),
1631 }),
1632 n(NodeKind::Foreach {
1633 variable: Box::new(leaf()),
1634 list: Box::new(leaf()),
1635 body: Box::new(block_node()),
1636 continue_block: Some(Box::new(block_node())),
1637 }),
1638 n(NodeKind::Given { expr: Box::new(leaf()), body: Box::new(block_node()) }),
1639 n(NodeKind::When { condition: Box::new(leaf()), body: Box::new(block_node()) }),
1640 n(NodeKind::Default { body: Box::new(block_node()) }),
1641 n(NodeKind::StatementModifier {
1642 statement: Box::new(leaf()),
1643 modifier: "if".to_string(),
1644 condition: Box::new(leaf()),
1645 }),
1646 n(NodeKind::Subroutine {
1647 name: Some("foo".to_string()),
1648 name_span: None,
1649 declarator: None,
1650 prototype: Some(Box::new(Node::new(
1651 NodeKind::Prototype { content: "$@".to_string() },
1652 loc(),
1653 ))),
1654 signature: Some(Box::new(Node::new(
1655 NodeKind::Signature { parameters: vec![] },
1656 loc(),
1657 ))),
1658 attributes: vec![],
1659 body: Box::new(block_node()),
1660 }),
1661 n(NodeKind::Prototype { content: "$@".to_string() }),
1662 n(NodeKind::Signature { parameters: vec![leaf()] }),
1663 n(NodeKind::MandatoryParameter { variable: Box::new(leaf()) }),
1664 n(NodeKind::OptionalParameter {
1665 variable: Box::new(leaf()),
1666 default_value: Box::new(leaf()),
1667 }),
1668 n(NodeKind::SlurpyParameter { variable: Box::new(leaf()) }),
1669 n(NodeKind::NamedParameter { variable: Box::new(leaf()) }),
1670 n(NodeKind::Method {
1671 name: "bar".to_string(),
1672 name_span: None,
1673 signature: Some(Box::new(Node::new(
1674 NodeKind::Signature { parameters: vec![] },
1675 loc(),
1676 ))),
1677 attributes: vec![],
1678 body: Box::new(block_node()),
1679 }),
1680 n(NodeKind::Return { value: Some(Box::new(leaf())) }),
1681 n(NodeKind::LoopControl { op: "next".to_string(), label: None }),
1682 n(NodeKind::Goto { target: Box::new(leaf()) }),
1683 n(NodeKind::MethodCall {
1684 object: Box::new(leaf()),
1685 method: "foo".to_string(),
1686 args: vec![leaf()],
1687 }),
1688 n(NodeKind::FunctionCall { name: "print".to_string(), args: vec![leaf()] }),
1689 n(NodeKind::IndirectCall {
1690 method: "new".to_string(),
1691 object: Box::new(leaf()),
1692 args: vec![leaf()],
1693 }),
1694 n(NodeKind::Regex {
1695 pattern: "foo".to_string(),
1696 replacement: None,
1697 modifiers: "".to_string(),
1698 has_embedded_code: false,
1699 }),
1700 n(NodeKind::Match {
1701 expr: Box::new(leaf()),
1702 pattern: "foo".to_string(),
1703 modifiers: "".to_string(),
1704 has_embedded_code: false,
1705 negated: false,
1706 }),
1707 n(NodeKind::Substitution {
1708 expr: Box::new(leaf()),
1709 pattern: "foo".to_string(),
1710 replacement: "bar".to_string(),
1711 modifiers: "".to_string(),
1712 has_embedded_code: false,
1713 negated: false,
1714 }),
1715 n(NodeKind::Transliteration {
1716 expr: Box::new(leaf()),
1717 search: "a".to_string(),
1718 replace: "b".to_string(),
1719 modifiers: "".to_string(),
1720 negated: false,
1721 }),
1722 n(NodeKind::Package {
1723 name: "Foo".to_string(),
1724 name_span: loc(),
1725 block: Some(Box::new(block_node())),
1726 }),
1727 n(NodeKind::Use {
1728 module: "strict".to_string(),
1729 args: vec!["foo".to_string()],
1730 has_filter_risk: false,
1731 }),
1732 n(NodeKind::No {
1733 module: "strict".to_string(),
1734 args: vec!["foo".to_string()],
1735 has_filter_risk: false,
1736 }),
1737 n(NodeKind::PhaseBlock {
1738 phase: "BEGIN".to_string(),
1739 phase_span: None,
1740 block: Box::new(block_node()),
1741 }),
1742 n(NodeKind::DataSection { marker: "__DATA__".to_string(), body: None }),
1743 n(NodeKind::Class {
1744 name: "Foo".to_string(),
1745 name_span: None,
1746 parents: vec![],
1747 body: Box::new(block_node()),
1748 }),
1749 n(NodeKind::Format {
1750 name: "STDOUT".to_string(),
1751 name_span: None,
1752 body: "".to_string(),
1753 }),
1754 n(NodeKind::Identifier { name: "foo".to_string() }),
1755 n(NodeKind::Error {
1756 message: "oops".to_string(),
1757 expected: vec![],
1758 found: None,
1759 partial: Some(Box::new(leaf())),
1760 }),
1761 n(NodeKind::MissingExpression),
1762 n(NodeKind::MissingStatement),
1763 n(NodeKind::MissingIdentifier),
1764 n(NodeKind::MissingBlock),
1765 n(NodeKind::UnknownRest),
1766 ]
1767 }
1768
1769 #[test]
1770 fn contains_children_matches_for_each_child() {
1771 for node in all_variants_maximal() {
1772 let has_children = node.child_count() > 0;
1773 assert_eq!(
1774 node.kind.contains_children(),
1775 has_children,
1776 "{}: contains_children() = {} but for_each_child yields {} children",
1777 node.kind.kind_name(),
1778 node.kind.contains_children(),
1779 node.child_count(),
1780 );
1781 }
1782 }
1783}