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<Cow<'a, str>>>>,
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 interner: &'config mut StringInterner,
76 ) -> SmartMatcherBuilder<'a, 'config> {
77 SmartMatcherBuilder::new(span, config, interner)
78 }
79
80 pub fn group_info(&self) -> &GroupInfo {
81 &self.group_info
82 }
83}
84impl<'a> MatcherMut for SmartMatcher<'a> {
85 fn try_match_mut<'input, 'context, C>(
86 &self,
87 input: Input<'input>,
88 context: &mut C,
89 ) -> DiagResult<MatchResult<'input>>
90 where
91 C: Context<'input, 'context> + ?Sized,
92 {
93 let mut captures = Captures::all(self.group_info().clone());
94 let searcher = SmartSearcher::new(self.span, input, &mut captures);
95 searcher.search_captures(&self.parts, context)?;
96 if let Some(matched) = captures.get_match() {
97 let matched =
98 extract_captures_from_match(self.span, matched, &captures, &self.parts, context)?;
99 matched.bind_captures_in(context);
100 Ok(matched)
101 } else {
102 Ok(MatchResult::failed(
103 CheckFailedError::MatchNoneButExpected {
104 span: self.span,
105 match_file: context.match_file(),
106 note: None,
107 },
108 ))
109 }
110 }
111}
112
113fn extract_captures_from_match<'a, 'input, 'context, C>(
114 pattern_span: SourceSpan,
115 matched: regex_automata::Match,
116 captures: &Captures,
117 patterns: &[MatchOp<'a>],
118 context: &C,
119) -> DiagResult<MatchResult<'input>>
120where
121 C: Context<'input, 'context> + ?Sized,
122{
123 use crate::ast::Capture;
124
125 let overall_span = SourceSpan::from(matched.range());
126 let mut capture_infos = Vec::with_capacity(captures.group_len());
127 let input = context.search();
128 let slots = captures.slots();
129 for op in patterns {
130 match op {
131 MatchOp::Regex {
132 ref source,
133 captures: group_captures,
134 ..
135 } => {
136 let pattern_span = source.span();
137 for group in group_captures.iter() {
138 if matches!(group.info, Capture::Ignore(_)) {
139 continue;
140 }
141 match capture_group_to_capture_info(
142 overall_span,
143 pattern_span,
144 group,
145 captures,
146 slots,
147 context,
148 &input,
149 ) {
150 Ok(None) => continue,
151 Ok(Some(capture_info)) => {
152 capture_infos.push(capture_info);
153 }
154 Err(error) => return Ok(MatchResult::failed(error)),
155 }
156 }
157 }
158 MatchOp::Numeric {
159 span,
160 capture: Some(ref group),
161 ..
162 } => {
163 match capture_group_to_capture_info(
164 overall_span,
165 *span,
166 group,
167 captures,
168 slots,
169 context,
170 &input,
171 ) {
172 Ok(None) => continue,
173 Ok(Some(capture_info)) => {
174 capture_infos.push(capture_info);
175 }
176 Err(error) => return Ok(MatchResult::failed(error)),
177 }
178 }
179 MatchOp::Substitution { .. }
180 | MatchOp::Bind { .. }
181 | MatchOp::Drop
182 | MatchOp::Constraint { .. }
183 | MatchOp::Literal(_)
184 | MatchOp::Numeric { .. } => (),
185 }
186 }
187 Ok(MatchResult::ok(MatchInfo {
188 span: overall_span,
189 pattern_span,
190 pattern_id: 0,
191 captures: capture_infos,
192 }))
193}
194
195fn capture_group_to_capture_info<'input, 'context, C>(
196 overall_span: SourceSpan,
197 pattern_span: SourceSpan,
198 group: &CaptureGroup,
199 captures: &Captures,
200 slots: &[Option<NonMaxUsize>],
201 context: &C,
202 input: &Input<'input>,
203) -> Result<Option<CaptureInfo<'input>>, CheckFailedError>
204where
205 C: Context<'input, 'context> + ?Sized,
206{
207 let group_info = captures.group_info();
208
209 let (a, b) = group_info
210 .slots(group.pattern_id, group.group_id)
211 .expect("invalid capture group");
212 if let Some(start) = slots[a].map(|offset| offset.get()) {
213 let end = slots[b].expect("expected end offset to be present").get();
214 let captured = input.as_str(start..end);
215 let capture_span = SourceSpan::from(start..end);
216
217 super::regex::try_convert_capture_to_type(
218 group.pattern_id,
219 group.group_id,
220 pattern_span,
221 overall_span,
222 Span::new(capture_span, captured),
223 group.info,
224 captures,
225 context,
226 )
227 .map(Some)
228 } else {
229 Ok(None)
230 }
231}