litcheck_filecheck/rules/
next.rs1use crate::common::*;
2
3#[derive(Debug)]
4pub struct CheckNext<M> {
5 pattern: M,
6}
7impl<M> CheckNext<M>
8where
9 M: MatcherMut,
10{
11 pub fn new(pattern: M) -> Self {
12 Self { pattern }
13 }
14}
15impl<M> Spanned for CheckNext<M>
16where
17 M: MatcherMut,
18{
19 fn span(&self) -> SourceSpan {
20 self.pattern.span()
21 }
22}
23impl<M> Rule for CheckNext<M>
24where
25 M: MatcherMut,
26{
27 fn kind(&self) -> Check {
28 Check::Next
29 }
30
31 fn apply<'input, 'context, C>(&self, context: &mut C) -> DiagResult<Matches<'input>>
32 where
33 C: Context<'input, 'context> + ?Sized,
34 {
35 let cursor = context.cursor();
36 let start = cursor.start_of_next_line();
37 let block_end = cursor.end();
38 let next_eol = cursor.next_newline_from(start).unwrap_or(block_end);
39 if start >= block_end {
40 return Ok(Matches::from(MatchResult {
43 ty: MatchType::Failed(CheckFailedError::MatchNoneButExpected {
44 span: self.pattern.span(),
45 match_file: context.match_file(),
46 note: if cursor.end_of_file() == block_end {
47 None
48 } else {
49 Some(
50 "search was stopped at end of the preceding CHECK-LABEL scope"
51 .to_string(),
52 )
53 },
54 }),
55 info: None,
56 }));
57 }
58 let input = context.search_range(start..next_eol);
59 let result = self.pattern.try_match_mut(input, context)?;
60 match &result {
61 MatchResult {
62 ty,
63 info: Some(ref info),
64 } if ty.is_ok() => {
65 context.cursor_mut().set_start(info.span.end());
66 }
67 _ => (),
68 }
69 Ok(result.into())
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76 use crate::pattern::matcher::*;
77 use crate::rules::CheckPlain;
78
79 #[test]
80 fn check_next_test() -> DiagResult<()> {
81 let mut context = TestContext::new();
82 context
83 .with_checks(
84 "
85CHECK: @inc4
86CHECK-NEXT: entry:
87",
88 )
89 .with_input(
90 "
91define void @inc4(i64* %p) {
92entry:
93 %0 = tail call i64 @llvm.atomic.load.add.i64.p0i64(i64* %p, i64 1)
94 ret void
95}
96",
97 );
98 let mut mctx = context.match_context();
99 let pattern = SubstringMatcher::new(
100 Span::new(SourceSpan::from(8..12), Cow::Borrowed("@inc4")),
101 &mctx.config,
102 )
103 .expect("expected pattern to be valid");
104 let rule = CheckPlain::new(pattern);
105 let matches = rule
106 .apply(&mut mctx)
107 .expect("expected non-fatal application of rule");
108 TestResult::from_matches(matches, &mctx).into_result()?;
109
110 let pattern = SubstringMatcher::new(
111 Span::new(SourceSpan::from(22..30), Cow::Borrowed("entry:")),
112 &mctx.config,
113 )
114 .expect("expected pattern to be valid");
115 let rule = CheckNext::new(pattern);
116 let matches = rule
117 .apply(&mut mctx)
118 .expect("expected non-fatal application of rule");
119 let matched = TestResult::from_matches(matches, &mctx).into_result()?;
120 assert_eq!(matched.len(), 1);
121 assert_eq!(matched[0].span.offset(), 30);
122 assert_eq!(matched[0].span.len(), 6);
123 let input = mctx.search();
124 assert_eq!(input.as_str(matched[0].matched_range()), "entry:");
125 assert_eq!(input.buffer()[mctx.cursor().start()], b'\n');
126
127 Ok(())
128 }
129
130 #[test]
131 fn check_next_multiple_times_test() -> DiagResult<()> {
132 let mut context = TestContext::new();
133 context
134 .with_checks(
135 "
136CHECK: @inc4
137CHECK-NEXT: entry:
138CHECK-NEXT: %0 = tail call
139CHECK-NEXT: ret void
140CHECK-NEXT: }
141",
142 )
143 .with_input(
144 "
145define void @inc4(i64* %p) {
146entry:
147 %0 = tail call i64 @llvm.atomic.load.add.i64.p0i64(i64* %p, i64 1)
148 ret void
149}
150",
151 );
152
153 context.check()?;
154
155 Ok(())
156 }
157
158 #[test]
159 fn check_next_real() -> DiagResult<()> {
160 let mut context = TestContext::new();
161 context
162 .with_checks(
163 "
164;; RUN: midenc compile --stdout --emit=hir $s | filecheck %s
165(module
166 (func $main (local i32)
167 i32.const 1
168 local.set 0
169 local.get 0
170 drop
171 )
172)
173
174;; CHECK: (module #locals
175;; CHECK-NEXT: ;; Functions
176;; CHECK-NEXT: (func (export #main)
177;; CHECK-NEXT: (block 0
178;; CHECK-NEXT: (let (v0 i32) (const.i32 0))
179;; CHECK-NEXT: (let (v1 i32) (const.i32 1))
180;; CHECK-NEXT: (br (block 1)))
181;; CHECK-EMPTY:
182;; CHECK-NEXT: (block 1
183;; CHECK-NEXT: (ret))
184;; CHECK-NEXT: )
185;; CHECK-NEXT: )
186",
187 )
188 .with_input(
189 "
190(module #locals
191 ;; Functions
192 (func (export #main)
193 (block 0
194 (let (v0 i32) (const.i32 0))
195 (let (v1 i32) (const.i32 1))
196 (br (block 1)))
197
198 (block 1
199 (ret))
200 )
201)
202",
203 );
204
205 context.check()?;
206
207 Ok(())
208 }
209}