]> git.proxmox.com Git - rustc.git/blame - vendor/regex-syntax-0.5.6/src/error.rs
New upstream version 1.33.0+dfsg1
[rustc.git] / vendor / regex-syntax-0.5.6 / src / error.rs
CommitLineData
94b46f34
XL
1use std::cmp;
2use std::error;
3use std::fmt;
4use std::result;
5
6use ast;
7use hir;
8
9/// A type alias for dealing with errors returned by this crate.
10pub type Result<T> = result::Result<T, Error>;
11
12/// This error type encompasses any error that can be returned by this crate.
13#[derive(Clone, Debug, Eq, PartialEq)]
14pub enum Error {
15 /// An error that occurred while translating concrete syntax into abstract
16 /// syntax (AST).
17 Parse(ast::Error),
18 /// An error that occurred while translating abstract syntax into a high
19 /// level intermediate representation (HIR).
20 Translate(hir::Error),
21 /// Hints that destructuring should not be exhaustive.
22 ///
23 /// This enum may grow additional variants, so this makes sure clients
24 /// don't count on exhaustive matching. (Otherwise, adding a new variant
25 /// could break existing code.)
26 #[doc(hidden)]
27 __Nonexhaustive,
28}
29
30impl From<ast::Error> for Error {
31 fn from(err: ast::Error) -> Error {
32 Error::Parse(err)
33 }
34}
35
36impl From<hir::Error> for Error {
37 fn from(err: hir::Error) -> Error {
38 Error::Translate(err)
39 }
40}
41
42impl error::Error for Error {
43 fn description(&self) -> &str {
44 match *self {
45 Error::Parse(ref x) => x.description(),
46 Error::Translate(ref x) => x.description(),
47 _ => unreachable!(),
48 }
49 }
50}
51
52impl fmt::Display for Error {
53 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54 match *self {
55 Error::Parse(ref x) => x.fmt(f),
56 Error::Translate(ref x) => x.fmt(f),
57 _ => unreachable!(),
58 }
59 }
60}
61
62/// A helper type for formatting nice error messages.
63///
64/// This type is responsible for reporting regex parse errors in a nice human
65/// readable format. Most of its complexity is from interspersing notational
66/// markers pointing out the position where an error occurred.
67#[derive(Debug)]
68pub struct Formatter<'e, E: 'e> {
69 /// The original regex pattern in which the error occurred.
70 pattern: &'e str,
71 /// The error kind. It must impl fmt::Display.
72 err: &'e E,
73 /// The primary span of the error.
74 span: &'e ast::Span,
75 /// An auxiliary and optional span, in case the error needs to point to
76 /// two locations (e.g., when reporting a duplicate capture group name).
77 aux_span: Option<&'e ast::Span>,
78}
79
80impl<'e> From<&'e ast::Error> for Formatter<'e, ast::ErrorKind> {
81 fn from(err: &'e ast::Error) -> Self {
82 Formatter {
83 pattern: err.pattern(),
84 err: err.kind(),
85 span: err.span(),
86 aux_span: err.auxiliary_span(),
87 }
88 }
89}
90
91impl<'e> From<&'e hir::Error> for Formatter<'e, hir::ErrorKind> {
92 fn from(err: &'e hir::Error) -> Self {
93 Formatter {
94 pattern: err.pattern(),
95 err: err.kind(),
96 span: err.span(),
97 aux_span: None,
98 }
99 }
100}
101
102impl<'e, E: fmt::Display> fmt::Display for Formatter<'e, E> {
103 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
104 let spans = Spans::from_formatter(self);
105 if self.pattern.contains('\n') {
106 let divider = repeat_char('~', 79);
107
108 try!(writeln!(f, "regex parse error:"));
109 try!(writeln!(f, "{}", divider));
110 let notated = spans.notate();
111 try!(write!(f, "{}", notated));
112 try!(writeln!(f, "{}", divider));
113 // If we have error spans that cover multiple lines, then we just
114 // note the line numbers.
115 if !spans.multi_line.is_empty() {
116 let mut notes = vec![];
117 for span in &spans.multi_line {
118 notes.push(format!(
119 "on line {} (column {}) through line {} (column {})",
120 span.start.line, span.start.column,
121 span.end.line, span.end.column - 1));
122 }
123 try!(writeln!(f, "{}", notes.join("\n")));
124 }
125 try!(write!(f, "error: {}", self.err));
126 } else {
127 try!(writeln!(f, "regex parse error:"));
128 let notated = Spans::from_formatter(self).notate();
129 try!(write!(f, "{}", notated));
130 try!(write!(f, "error: {}", self.err));
131 }
132 Ok(())
133 }
134}
135
136/// This type represents an arbitrary number of error spans in a way that makes
137/// it convenient to notate the regex pattern. ("Notate" means "point out
138/// exactly where the error occurred in the regex pattern.")
139///
140/// Technically, we can only ever have two spans given our current error
141/// structure. However, after toiling with a specific algorithm for handling
142/// two spans, it became obvious that an algorithm to handle an arbitrary
143/// number of spans was actually much simpler.
144struct Spans<'p> {
145 /// The original regex pattern string.
146 pattern: &'p str,
147 /// The total width that should be used for line numbers. The width is
148 /// used for left padding the line numbers for alignment.
149 ///
150 /// A value of `0` means line numbers should not be displayed. That is,
151 /// the pattern is itself only one line.
152 line_number_width: usize,
153 /// All error spans that occur on a single line. This sequence always has
154 /// length equivalent to the number of lines in `pattern`, where the index
155 /// of the sequence represents a line number, starting at `0`. The spans
156 /// in each line are sorted in ascending order.
157 by_line: Vec<Vec<ast::Span>>,
158 /// All error spans that occur over one or more lines. That is, the start
159 /// and end position of the span have different line numbers. The spans are
160 /// sorted in ascending order.
161 multi_line: Vec<ast::Span>,
162}
163
164impl<'p> Spans<'p> {
165 /// Build a sequence of spans from a formatter.
166 fn from_formatter<'e, E: fmt::Display>(
167 fmter: &'p Formatter<'e, E>,
168 ) -> Spans<'p> {
169 let mut line_count = fmter.pattern.lines().count();
170 // If the pattern ends with a `\n` literal, then our line count is
171 // off by one, since a span can occur immediately after the last `\n`,
172 // which is consider to be an additional line.
173 if fmter.pattern.ends_with('\n') {
174 line_count += 1;
175 }
176 let line_number_width =
177 if line_count <= 1 {
178 0
179 } else {
180 line_count.to_string().len()
181 };
182 let mut spans = Spans {
183 pattern: &fmter.pattern,
184 line_number_width: line_number_width,
185 by_line: vec![vec![]; line_count],
186 multi_line: vec![],
187 };
188 spans.add(fmter.span.clone());
189 if let Some(span) = fmter.aux_span {
190 spans.add(span.clone());
191 }
192 spans
193 }
194
195 /// Add the given span to this sequence, putting it in the right place.
196 fn add(&mut self, span: ast::Span) {
197 // This is grossly inefficient since we sort after each add, but right
198 // now, we only ever add two spans at most.
199 if span.is_one_line() {
200 let i = span.start.line - 1; // because lines are 1-indexed
201 self.by_line[i].push(span);
202 self.by_line[i].sort();
203 } else {
204 self.multi_line.push(span);
205 self.multi_line.sort();
206 }
207 }
208
209 /// Notate the pattern string with carents (`^`) pointing at each span
210 /// location. This only applies to spans that occur within a single line.
211 fn notate(&self) -> String {
212 let mut notated = String::new();
213 for (i, line) in self.pattern.lines().enumerate() {
214 if self.line_number_width > 0 {
215 notated.push_str(&self.left_pad_line_number(i + 1));
216 notated.push_str(": ");
217 } else {
218 notated.push_str(" ");
219 }
220 notated.push_str(line);
221 notated.push('\n');
222 if let Some(notes) = self.notate_line(i) {
223 notated.push_str(&notes);
224 notated.push('\n');
225 }
226 }
227 notated
228 }
229
230 /// Return notes for the line indexed at `i` (zero-based). If there are no
231 /// spans for the given line, then `None` is returned. Otherwise, an
232 /// appropriately space padded string with correctly positioned `^` is
233 /// returned, accounting for line numbers.
234 fn notate_line(&self, i: usize) -> Option<String> {
235 let spans = &self.by_line[i];
236 if spans.is_empty() {
237 return None;
238 }
239 let mut notes = String::new();
240 for _ in 0..self.line_number_padding() {
241 notes.push(' ');
242 }
243 let mut pos = 0;
244 for span in spans {
245 for _ in pos..(span.start.column - 1) {
246 notes.push(' ');
247 pos += 1;
248 }
249 let note_len = span.end.column.saturating_sub(span.start.column);
250 for _ in 0..cmp::max(1, note_len) {
251 notes.push('^');
252 pos += 1;
253 }
254 }
255 Some(notes)
256 }
257
258 /// Left pad the given line number with spaces such that it is aligned with
259 /// other line numbers.
260 fn left_pad_line_number(&self, n: usize) -> String {
261 let n = n.to_string();
262 let pad = self.line_number_width.checked_sub(n.len()).unwrap();
263 let mut result = repeat_char(' ', pad);
264 result.push_str(&n);
265 result
266 }
267
268 /// Return the line number padding beginning at the start of each line of
269 /// the pattern.
270 ///
271 /// If the pattern is only one line, then this returns a fixed padding
272 /// for visual indentation.
273 fn line_number_padding(&self) -> usize {
274 if self.line_number_width == 0 {
275 4
276 } else {
277 2 + self.line_number_width
278 }
279 }
280}
281
282fn repeat_char(c: char, count: usize) -> String {
283 ::std::iter::repeat(c).take(count).collect()
284}
285
286#[cfg(test)]
287mod tests {
288 use ast::parse::Parser;
289
290 // See: https://github.com/rust-lang/regex/issues/464
291 #[test]
292 fn regression_464() {
293 let err = Parser::new().parse("a{\n").unwrap_err();
294 // This test checks that the error formatter doesn't panic.
295 assert!(!err.to_string().is_empty());
296 }
297}