Skip to main content

surql_parser/upstream/syn/error/
mod.rs

1use crate::upstream::syn::token::Span;
2use std::fmt::Display;
3mod location;
4mod mac;
5mod render;
6pub use location::Location;
7pub(crate) use mac::{bail, syntax_error};
8pub use render::{RenderedError, Snippet};
9#[derive(Debug, Clone, Copy)]
10pub enum MessageKind {
11	Suggestion,
12	Error,
13}
14#[derive(Debug)]
15enum DiagnosticKind {
16	Cause(String),
17	Span {
18		kind: MessageKind,
19		span: Span,
20		label: Option<String>,
21	},
22}
23#[derive(Debug)]
24pub struct Diagnostic {
25	kind: DiagnosticKind,
26	next: Option<Box<Diagnostic>>,
27}
28/// A parsing error.
29#[derive(Debug)]
30pub struct SyntaxError {
31	diagnostic: Box<Diagnostic>,
32}
33impl SyntaxError {
34	/// Create a new parse error.
35	#[cold]
36	pub fn new<T>(message: T) -> Self
37	where
38		T: Display,
39	{
40		let diagnostic = Diagnostic {
41			kind: DiagnosticKind::Cause(message.to_string()),
42			next: None,
43		};
44		Self {
45			diagnostic: Box::new(diagnostic),
46		}
47	}
48	#[cold]
49	pub fn with_span(mut self, span: Span, kind: MessageKind) -> Self {
50		self.diagnostic = Box::new(Diagnostic {
51			kind: DiagnosticKind::Span {
52				kind,
53				span,
54				label: None,
55			},
56			next: Some(self.diagnostic),
57		});
58		self
59	}
60	#[cold]
61	pub fn with_labeled_span<T: Display>(
62		mut self,
63		span: Span,
64		kind: MessageKind,
65		label: T,
66	) -> Self {
67		self.diagnostic = Box::new(Diagnostic {
68			kind: DiagnosticKind::Span {
69				kind,
70				span,
71				label: Some(label.to_string()),
72			},
73			next: Some(self.diagnostic),
74		});
75		self
76	}
77	#[cold]
78	pub fn with_cause<T: Display>(mut self, t: T) -> Self {
79		self.diagnostic = Box::new(Diagnostic {
80			kind: DiagnosticKind::Cause(t.to_string()),
81			next: Some(self.diagnostic),
82		});
83		self
84	}
85	pub fn render_on(&self, source: &str) -> RenderedError {
86		let mut res = RenderedError {
87			errors: Vec::new(),
88			snippets: Vec::new(),
89		};
90		Self::render_on_inner(&self.diagnostic, source, &mut res);
91		res
92	}
93	pub fn render_on_bytes(&self, source: &[u8]) -> RenderedError {
94		let source = String::from_utf8_lossy(source);
95		self.render_on(&source)
96	}
97	/// Traverses all diagnostics and hands a mutable reference to the diagnostic span to the given
98	/// callback.
99	pub fn update_spans<F: FnMut(&mut Span)>(mut self, mut cb: F) -> Self {
100		let mut cur = &mut *self.diagnostic;
101		loop {
102			match cur.kind {
103				DiagnosticKind::Cause(_) => {}
104				DiagnosticKind::Span { ref mut span, .. } => cb(span),
105			}
106			let Some(next) = cur.next.as_mut() else {
107				break;
108			};
109			cur = &mut *next;
110		}
111		self
112	}
113	fn render_on_inner(diagnostic: &Diagnostic, source: &str, res: &mut RenderedError) {
114		if let Some(ref x) = diagnostic.next {
115			Self::render_on_inner(x, source, res);
116		}
117		match diagnostic.kind {
118			DiagnosticKind::Cause(ref x) => res.errors.push(x.clone()),
119			DiagnosticKind::Span {
120				ref span,
121				ref label,
122				ref kind,
123			} => {
124				let locations = Location::range_of_span(source, *span);
125				let snippet = Snippet::from_source_location_range(
126					source,
127					locations,
128					label.as_ref().map(|x| x.as_str()),
129					*kind,
130				);
131				res.snippets.push(snippet)
132			}
133		}
134	}
135}