litcheck_filecheck/pattern/matcher/matchers/
smart.rs1mod builder;
2mod instruction;
3mod operand;
4mod searcher;
5
6pub use self::builder::SmartMatcherBuilder;
7use self::instruction::{CaptureGroup, MatchOp};
8use self::operand::Operand;
9use self::searcher::SmartSearcher;
10
11use crate::common::*;
12
13use regex_automata::util::{
14 captures::{Captures, GroupInfo},
15 primitives::NonMaxUsize,
16};
17
18pub struct SmartMatcher<'a> {
25 span: SourceSpan,
26 parts: Vec<MatchOp<'a>>,
35 group_info: GroupInfo,
37}
38impl<'a> fmt::Debug for SmartMatcher<'a> {
39 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
40 f.debug_struct("SmartMatcher")
41 .field("parts", &self.parts)
42 .field(
43 "group_info",
44 &self
45 .group_info
46 .all_names()
47 .collect::<smallvec::SmallVec<[_; 4]>>(),
48 )
49 .finish()
50 }
51}
52impl<'a> Spanned for SmartMatcher<'a> {
53 fn span(&self) -> SourceSpan {
54 self.span
55 }
56}
57impl<'a> SmartMatcher<'a> {
58 fn new(
59 span: SourceSpan,
60 parts: Vec<MatchOp<'a>>,
61 group_info: Vec<Vec<Option<Symbol>>>,
62 ) -> Self {
63 let group_info =
64 GroupInfo::new(group_info).expect("expected capturing groups to meet all requirements");
65 Self {
66 span,
67 parts,
68 group_info,
69 }
70 }
71
72 pub fn build<'config>(
73 span: SourceSpan,
74 config: &'config Config,
75 ) -> SmartMatcherBuilder<'a, 'config> {
76 SmartMatcherBuilder::new(span, config)
77 }
78
79 pub fn group_info(&self) -> &GroupInfo {
80 &self.group_info
81 }
82}
83impl<'a> MatcherMut for SmartMatcher<'a> {
84 fn try_match_mut<'input, 'context, C>(
85 &self,
86 input: Input<'input>,
87 context: &mut C,
88 ) -> DiagResult<MatchResult<'input>>
89 where
90 C: Context<'input, 'context> + ?Sized,
91 {
92 let mut captures = Captures::all(self.group_info().clone());
93 let searcher = SmartSearcher::new(self.span, input, &mut captures);
94 searcher.search_captures(&self.parts, context)?;
95 if let Some(matched) = captures.get_match() {
96 let matched =
97 extract_captures_from_match(self.span, matched, &captures, &self.parts, context)?;
98 Ok(matched)
99 } else {
100 Ok(MatchResult::failed(
101 CheckFailedError::MatchNoneButExpected {
102 span: self.span,
103 match_file: context.source_file(self.span.source_id()).unwrap(),
104 note: None,
105 },
106 ))
107 }
108 }
109}
110
111fn extract_captures_from_match<'a, 'input, 'context, C>(
112 pattern_span: SourceSpan,
113 matched: regex_automata::Match,
114 captures: &Captures,
115 patterns: &[MatchOp<'a>],
116 context: &C,
117) -> DiagResult<MatchResult<'input>>
118where
119 C: Context<'input, 'context> + ?Sized,
120{
121 use crate::ast::Capture;
122
123 let input = context.search();
124 let overall_span = SourceSpan::from_range_unchecked(input.source_id(), matched.range());
125 let mut capture_infos = Vec::with_capacity(captures.group_len());
126 let slots = captures.slots();
127 for op in patterns {
128 match op {
129 MatchOp::Regex {
130 source,
131 captures: group_captures,
132 ..
133 } => {
134 let pattern_span = source.span();
135 for group in group_captures.iter() {
136 if matches!(group.info, Capture::Ignore(_)) {
137 continue;
138 }
139 match capture_group_to_capture_info(
140 overall_span,
141 pattern_span,
142 group,
143 captures,
144 slots,
145 context,
146 &input,
147 ) {
148 Ok(None) => continue,
149 Ok(Some(capture_info)) => {
150 capture_infos.push(capture_info);
151 }
152 Err(error) => return Ok(MatchResult::failed(error)),
153 }
154 }
155 }
156 MatchOp::Numeric {
157 span,
158 capture: Some(group),
159 ..
160 } => {
161 match capture_group_to_capture_info(
162 overall_span,
163 *span,
164 group,
165 captures,
166 slots,
167 context,
168 &input,
169 ) {
170 Ok(None) => continue,
171 Ok(Some(capture_info)) => {
172 capture_infos.push(capture_info);
173 }
174 Err(error) => return Ok(MatchResult::failed(error)),
175 }
176 }
177 MatchOp::Substitution { .. }
178 | MatchOp::Bind { .. }
179 | MatchOp::Drop
180 | MatchOp::Constraint { .. }
181 | MatchOp::Literal(_)
182 | MatchOp::Numeric { .. } => (),
183 }
184 }
185 Ok(MatchResult::ok(MatchInfo {
186 span: overall_span,
187 pattern_span,
188 pattern_id: 0,
189 captures: capture_infos,
190 }))
191}
192
193fn capture_group_to_capture_info<'input, 'context, C>(
194 overall_span: SourceSpan,
195 pattern_span: SourceSpan,
196 group: &CaptureGroup,
197 captures: &Captures,
198 slots: &[Option<NonMaxUsize>],
199 context: &C,
200 input: &Input<'input>,
201) -> Result<Option<CaptureInfo<'input>>, CheckFailedError>
202where
203 C: Context<'input, 'context> + ?Sized,
204{
205 let group_info = captures.group_info();
206
207 let (a, b) = group_info
208 .slots(group.pattern_id, group.group_id)
209 .expect("invalid capture group");
210 if let Some(start) = slots[a].map(|offset| offset.get()) {
211 let end = slots[b].expect("expected end offset to be present").get();
212 let captured = input.as_str(start..end);
213 let capture_span = SourceSpan::from_range_unchecked(input.source_id(), start..end);
214
215 super::regex::try_convert_capture_to_type(
216 group.pattern_id,
217 group.group_id,
218 pattern_span,
219 overall_span,
220 Span::new(capture_span, captured),
221 group.info,
222 captures,
223 context,
224 )
225 .map(Some)
226 } else {
227 Ok(None)
228 }
229}