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