litcheck_filecheck/ast/directive.rs
1use std::str::FromStr;
2
3use crate::common::*;
4
5use super::CheckModifier;
6
7#[derive(Debug)]
8pub enum InvalidCheckTypeError {
9 Unrecognized,
10 InvalidCount(core::num::ParseIntError),
11}
12
13/// This enum represents the kind of directive that was parsed
14#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
15pub enum Check {
16 /// Not used after parsing, represents a line with no directives
17 #[default]
18 None,
19 /// The base CHECK directive, i.e. match the pattern is somewhere in the file
20 Plain,
21 /// The CHECK-NEXT directive, i.e. the pattern must match on the next line
22 /// from the previous pattern.
23 Next,
24 /// The CHECK-SAME directive, i.e. the pattern must match on the same line
25 /// as the previous pattern.
26 Same,
27 /// The CHECK-NOT directive, i.e. the pattern must _not_ match between this
28 /// directive and the next positive match directive, or the end of file. This
29 /// is the only negative match assertion supported.
30 Not,
31 /// The CHECK-DAG directive, i.e. like CHECK, but may match in any order relative
32 /// to other CHECK-DAG directives which are all part of a single consecutive
33 /// grouping. A non-CHECK-DAG directive between two sets of CHECK-DAG directives
34 /// cause the two sets to be split into two logical groups, where the ordering
35 /// between the groups is strict, but within the group it is not.
36 Dag,
37 /// The CHECK-LABEL directive, i.e. a regular CHECK, with the additional restriction
38 /// that the pattern may not contain references to (or bind) variables. This
39 /// directive type divides the checks (and input) into logical "blocks". Checks other
40 /// than CHECK-LABEL belong to the block defined by their preceding CHECK-LABEL. Checks
41 /// before the first CHECK-LABEL are part of an implicit prologue block.
42 ///
43 /// CHECK-LABEL divides up the input into blocks as well, by first matching all of the
44 /// CHECK-LABEL patterns, and then block-by-block, matching all of the checks "owned"
45 /// by each CHECK-LABEL, rejecting any matches that would match outside the region
46 /// of input assigned to the block.
47 Label,
48 /// The CHECK-EMPTY directive, i.e. the next line must be empty, containing nothing
49 /// but a newline, no other whitespace.
50 Empty,
51 /// This is only used during parsing, but represents CHECK-COUNT-N, i.e. a CHECK
52 /// that is repeated N times. N must be non-zero.
53 Count(usize),
54 /// The COM directive, i.e. a comment.
55 ///
56 /// This is only used during parsing.
57 Comment,
58}
59impl Check {
60 pub fn suffix(&self) -> Option<&'static str> {
61 match self {
62 Self::Plain => Some(""),
63 Self::Next => Some("-NEXT"),
64 Self::Same => Some("-SAME"),
65 Self::Not => Some("-NOT"),
66 Self::Dag => Some("-DAG"),
67 Self::Label => Some("-LABEL"),
68 Self::Empty => Some("-EMPTY"),
69 Self::Count(_) => Some("-COUNT"),
70 Self::Comment | Self::None => None,
71 }
72 }
73}
74impl fmt::Display for Check {
75 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76 match self {
77 Self::None => f.write_str("CHECK-NONE"),
78 Self::Plain => f.write_str("CHECK"),
79 Self::Next => f.write_str("CHECK-NEXT"),
80 Self::Same => f.write_str("CHECK-SAME"),
81 Self::Not => f.write_str("CHECK-NOT"),
82 Self::Dag => f.write_str("CHECK-DAG"),
83 Self::Label => f.write_str("CHECK-LABEL"),
84 Self::Empty => f.write_str("CHECK-EMPTY"),
85 Self::Count(n) => write!(f, "CHECK-COUNT-{n}"),
86 Self::Comment => f.write_str("COM"),
87 }
88 }
89}
90impl FromStr for Check {
91 type Err = InvalidCheckTypeError;
92
93 fn from_str(s: &str) -> Result<Self, Self::Err> {
94 match s {
95 "" => Ok(Self::Plain),
96 "NEXT" | "next" => Ok(Self::Next),
97 "SAME" | "same" => Ok(Self::Same),
98 "NOT" | "not" => Ok(Self::Not),
99 "DAG" | "dag" => Ok(Self::Dag),
100 "LABEL" | "label" => Ok(Self::Label),
101 "EMPTY" | "empty" => Ok(Self::Empty),
102 _ => match s
103 .strip_prefix("COUNT-")
104 .or_else(|| s.strip_prefix("count-"))
105 {
106 None => Err(InvalidCheckTypeError::Unrecognized),
107 Some(count) => count
108 .parse::<usize>()
109 .map_err(InvalidCheckTypeError::InvalidCount)
110 .map(Self::Count),
111 },
112 }
113 }
114}
115
116/// This represents the complete type of a CHECK* directive
117#[derive(Debug)]
118pub struct CheckType {
119 span: SourceSpan,
120 /// The kind of directive represented
121 pub kind: Check,
122 /// Any modifiers applied to this directive
123 pub modifiers: Span<CheckModifier>,
124}
125impl Default for CheckType {
126 fn default() -> Self {
127 Self::new(SourceSpan::from(0..0), Default::default())
128 }
129}
130impl Spanned for CheckType {
131 fn span(&self) -> SourceSpan {
132 self.span
133 }
134}
135impl CheckType {
136 pub fn new(span: SourceSpan, kind: Check) -> Self {
137 Self {
138 span,
139 kind,
140 modifiers: Span::new(span, Default::default()),
141 }
142 }
143
144 pub fn with_modifiers(mut self, modifiers: Span<CheckModifier>) -> Self {
145 self.modifiers = modifiers;
146 self
147 }
148
149 pub fn is_literal_match(&self) -> bool {
150 self.modifiers.contains(CheckModifier::LITERAL)
151 }
152
153 pub fn count(&self) -> usize {
154 self.modifiers.count()
155 }
156}
157impl Eq for CheckType {}
158impl PartialEq for CheckType {
159 fn eq(&self, other: &Self) -> bool {
160 self.kind == other.kind && self.modifiers == other.modifiers
161 }
162}