prqlc_parser/
error.rs

1use std::fmt::Debug;
2
3use chumsky::error::Cheap;
4use serde::Serialize;
5
6use super::parser::perror::PError;
7use crate::span::Span;
8
9/// A prqlc error. Used internally, exposed as prqlc::ErrorMessage.
10#[derive(Debug, Clone)]
11pub struct Error {
12    /// Message kind. Currently only Error is implemented.
13    pub kind: MessageKind,
14    pub span: Option<Span>,
15    pub reason: Reason,
16    pub hints: Vec<String>,
17    /// Machine readable identifier error code eg, "E0001"
18    pub code: Option<&'static str>,
19    // pub source: ErrorSource
20}
21
22#[derive(Clone, Debug, Default)]
23pub enum ErrorSource {
24    Lexer(Cheap<char>),
25    Parser(PError),
26    #[default]
27    Unknown,
28    NameResolver,
29    TypeResolver,
30    SQL,
31}
32
33/// Multiple prqlc errors. Used internally, exposed as prqlc::ErrorMessages.
34#[derive(Debug, Clone)]
35pub struct Errors(pub Vec<Error>);
36
37/// Compile message kind. Currently only Error is implemented.
38#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
39pub enum MessageKind {
40    Error,
41    Warning,
42    Lint,
43}
44
45#[derive(Debug, Clone)]
46pub enum Reason {
47    Simple(String),
48    Expected {
49        /// Where we were
50        // (could rename to `where` / `location` / `within`?)
51        who: Option<String>,
52        /// What we expected
53        expected: String,
54        /// What we found
55        found: String,
56    },
57    Unexpected {
58        found: String,
59    },
60    NotFound {
61        name: String,
62        namespace: String,
63    },
64    Bug {
65        issue: Option<i32>,
66        details: Option<String>,
67    },
68}
69
70impl Error {
71    pub fn new(reason: Reason) -> Self {
72        Error {
73            kind: MessageKind::Error,
74            span: None,
75            reason,
76            hints: Vec::new(),
77            code: None,
78            // source: ErrorSource::default()
79        }
80    }
81
82    pub fn new_simple<S: ToString>(reason: S) -> Self {
83        Error::new(Reason::Simple(reason.to_string()))
84    }
85
86    pub fn new_bug(issue_no: i32) -> Self {
87        Error::new(Reason::Bug {
88            issue: Some(issue_no),
89            details: None,
90        })
91    }
92
93    /// Used for things that you *think* should never happen, but are not sure.
94    pub fn new_assert<S: ToString>(details: S) -> Self {
95        Error::new(Reason::Bug {
96            issue: None,
97            details: Some(details.to_string()),
98        })
99    }
100}
101
102impl std::fmt::Display for Reason {
103    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104        match self {
105            Reason::Simple(text) => f.write_str(text),
106            Reason::Expected {
107                who,
108                expected,
109                found,
110            } => {
111                if let Some(who) = who {
112                    write!(f, "{who} ")?;
113                }
114                write!(f, "expected {expected}, but found {found}")
115            }
116            Reason::Unexpected { found } => write!(f, "unexpected {found}"),
117            Reason::NotFound { name, namespace } => write!(f, "{namespace} `{name}` not found"),
118            Reason::Bug { issue, details } => {
119                write!(f, "internal compiler error")?;
120                if let Some(details) = details {
121                    write!(f, "; {details}")?;
122                }
123                if let Some(issue_no) = issue {
124                    write!(
125                        f,
126                        "; tracked at https://github.com/PRQL/prql/issues/{issue_no}"
127                    )?;
128                }
129                Ok(())
130            }
131        }
132    }
133}
134
135impl From<Error> for Errors {
136    fn from(error: Error) -> Self {
137        Errors(vec![error])
138    }
139}
140
141// Needed for anyhow
142impl std::error::Error for Error {}
143
144// Needed for anyhow
145impl std::error::Error for Errors {}
146
147// Needed for StdError
148impl std::fmt::Display for Error {
149    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150        Debug::fmt(&self, f)
151    }
152}
153
154// Needed for StdError
155impl std::fmt::Display for Errors {
156    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157        Debug::fmt(&self, f)
158    }
159}
160
161pub trait WithErrorInfo: Sized {
162    fn push_hint<S: Into<String>>(self, hint: S) -> Self;
163
164    fn with_hints<S: Into<String>, I: IntoIterator<Item = S>>(self, hints: I) -> Self;
165
166    fn with_span(self, span: Option<Span>) -> Self;
167
168    fn with_span_fallback(self, span: Option<Span>) -> Self;
169
170    fn with_code(self, code: &'static str) -> Self;
171
172    fn with_source(self, source: ErrorSource) -> Self;
173}
174
175impl WithErrorInfo for Error {
176    fn push_hint<S: Into<String>>(mut self, hint: S) -> Self {
177        self.hints.push(hint.into());
178        self
179    }
180
181    fn with_hints<S: Into<String>, I: IntoIterator<Item = S>>(mut self, hints: I) -> Self {
182        self.hints = hints.into_iter().map(|x| x.into()).collect();
183        self
184    }
185
186    fn with_span(mut self, span: Option<Span>) -> Self {
187        self.span = span;
188        self
189    }
190
191    fn with_code(mut self, code: &'static str) -> Self {
192        self.code = Some(code);
193        self
194    }
195
196    fn with_span_fallback(mut self, span: Option<Span>) -> Self {
197        self.span = self.span.or(span);
198        self
199    }
200
201    fn with_source(self, _source: ErrorSource) -> Self {
202        // self.source = source;
203        self
204    }
205}
206
207impl<T, E: WithErrorInfo> WithErrorInfo for Result<T, E> {
208    fn push_hint<S: Into<String>>(self, hint: S) -> Self {
209        self.map_err(|e| e.push_hint(hint))
210    }
211
212    fn with_hints<S: Into<String>, I: IntoIterator<Item = S>>(self, hints: I) -> Self {
213        self.map_err(|e| e.with_hints(hints))
214    }
215
216    fn with_span(self, span: Option<Span>) -> Self {
217        self.map_err(|e| e.with_span(span))
218    }
219
220    fn with_span_fallback(self, span: Option<Span>) -> Self {
221        self.map_err(|e| e.with_span_fallback(span))
222    }
223
224    fn with_code(self, code: &'static str) -> Self {
225        self.map_err(|e| e.with_code(code))
226    }
227
228    fn with_source(self, source: ErrorSource) -> Self {
229        self.map_err(|e| e.with_source(source))
230    }
231}