hemtt_preprocessor/
error.rs

1use hemtt_error::{make_source, thiserror, PrettyError, Source};
2use hemtt_tokens::{Symbol, Token};
3
4use crate::{
5    defines::{Defines, DefinitionLibrary},
6    parse::Rule,
7};
8
9#[derive(thiserror::Error, Debug)]
10/// Errors that can occur during preprocessing
11pub enum Error {
12    #[error("Expected `{expected:?}`, found `{token:?}`,")]
13    /// Expected a token, found something else
14    UnexpectedToken {
15        /// The [`Token`] that was found
16        token: Box<Token>,
17        /// The valid [`Symbol`]s that were expected
18        expected: Vec<Symbol>,
19        /// The [`Token`] stack trace
20        trace: Vec<Token>,
21    },
22    #[error("Unexpected EOF at `{token:?}`")]
23    /// Unexpected end of file
24    UnexpectedEOF {
25        /// The token that was found
26        token: Box<Token>,
27    },
28    #[error("Expected `{{ident}}`, found `{token:?}`, ")]
29    /// Expected an identifier, found something else
30    ExpectedIdent {
31        /// The [`Token`] that was found
32        token: Box<Token>,
33        /// The [`Token`] stack trace
34        trace: Vec<Token>,
35    },
36    #[error("Unknown directive `{directive:?}`, ")]
37    /// Unknown directive
38    UnknownDirective {
39        /// The [`Token`] that was found
40        directive: Box<Token>,
41        /// The [`Token`] stack trace
42        trace: Vec<Token>,
43    },
44    #[error("Function definition has multi-token arguments, `{token:?}`")]
45    /// Tried to create a [`FunctionDefinition`](crate::context::FunctionDefinition) that has multi-token arguments
46    ///
47    /// ```cpp
48    /// #define FUNC(my arg) ...
49    /// ```
50    DefineMultiTokenArgument {
51        /// The [`Token`] that was found
52        token: Box<Token>,
53        /// The [`Token`] stack trace
54        trace: Vec<Token>,
55    },
56    #[error("Can not change built-in macros `{token:?}`")]
57    /// Tried to change a built-in macro
58    ChangeBuiltin {
59        /// The [`Token`] that was found
60        token: Box<Token>,
61        /// The [`Token`] stack trace
62        trace: Vec<Token>,
63    },
64    #[error("Attempted to use `#if` on a unit or function macro, `{token:?}`")]
65    /// Tried to use `#if` on a [`Unit`](crate::context::Definition::Unit) or [`FunctionDefinition`](crate::context::Definition::Function)
66    IfUnitOrFunction {
67        /// The [`Token`] that was found
68        token: Box<Token>,
69        /// The [`Token`] stack trace
70        trace: Vec<Token>,
71    },
72    #[error("Attempted to use `#if` on an undefined macro, `{token:?}`")]
73    /// Tried to use `#if` on an undefined macro
74    IfUndefined {
75        /// The [`Token`] that was found
76        token: Box<Token>,
77        /// The [`Token`] stack trace
78        trace: Vec<Token>,
79    },
80    #[error("Function call with incorrect number of arguments, expected `{expected}` got `{got}`. `{token:?}`")]
81    /// Tried to call a [`FunctionDefinition`](crate::context::FunctionDefinition) with the wrong number of arguments
82    FunctionCallArgumentCount {
83        /// The [`Token`] that was found
84        token: Box<Token>,
85        /// The number of arguments that were expected
86        expected: usize,
87        /// The number of arguments that were found
88        got: usize,
89        /// The [`Token`] stack trace
90        trace: Vec<Token>,
91        /// The defines at the point of the error
92        defines: Defines,
93    },
94    #[error("Expected Function or Value, found Unit, `{token:?}`")]
95    /// Tried to use a [`Unit`](crate::context::Definition::Unit) as a function or value
96    ExpectedFunctionOrValue {
97        /// The [`Token`] that was found
98        token: Box<Token>,
99        /// The [`Token`] stack trace
100        trace: Vec<Token>,
101        /// Skipped tokens of Unit
102        skipped: Vec<Token>,
103    },
104    #[error("`#include` was encountered while using `NoResolver`")]
105    /// Tried to use `#include` with [`NoResolver`](crate::resolver::resolvers::NoResolver)
106    ResolveWithNoResolver {
107        /// The [`Token`] stack trace
108        token: Box<Token>,
109        /// The [`Token`] stack trace
110        trace: Vec<Token>,
111    },
112    #[error("`#include` target `{target:?}` was not found")]
113    /// The [`Resolver`](crate::resolver::Resolver) could not find the target
114    IncludeNotFound {
115        /// The target that was not found
116        target: Vec<Token>,
117        /// The [`Token`] stack trace
118        trace: Vec<Token>,
119    },
120    #[error("IO Error: {0}")]
121    /// [`std::io::Error`]
122    Io(Box<std::io::Error>),
123    #[error("Pest Error: {0}")]
124    /// [`pest::error::Error`]
125    Pest(Box<pest::error::Error<Rule>>),
126}
127
128impl From<std::io::Error> for Error {
129    fn from(e: std::io::Error) -> Self {
130        Self::Io(Box::new(e))
131    }
132}
133
134impl From<pest::error::Error<Rule>> for Error {
135    fn from(e: pest::error::Error<Rule>) -> Self {
136        Self::Pest(Box::new(e))
137    }
138}
139
140impl PrettyError for Error {
141    fn brief(&self) -> String {
142        match self {
143            Self::UnexpectedToken {
144                token,
145                expected,
146                trace: _,
147            } => {
148                format!(
149                    "Expected `{expected:?}`, found `{symbol:?}`,",
150                    symbol = token.symbol(),
151                    expected = expected
152                )
153            }
154            Self::UnexpectedEOF { token } => {
155                format!("Unexpected EOF near `{token:?}`,")
156            }
157            Self::ExpectedIdent { token, trace: _ } => {
158                format!(
159                    "Expected `{{ident}}`, found `{symbol:?}`,",
160                    symbol = token.symbol()
161                )
162            }
163            Self::UnknownDirective {
164                directive,
165                trace: _,
166            } => {
167                format!(
168                    "Unknown directive `{directive:?}`,",
169                    directive = directive.symbol()
170                )
171            }
172            Self::DefineMultiTokenArgument { .. } => {
173                "Function definition has multi-token arguments".to_string()
174            }
175            Self::ChangeBuiltin { token, trace: _ } => {
176                format!(
177                    "Can not change built-in macros `{symbol:?}`",
178                    symbol = token.symbol()
179                )
180            }
181            Self::IfUnitOrFunction { token, trace: _ } => {
182                format!(
183                    "Attempted to use `#if` on a unit or function macro, `{symbol:?}`",
184                    symbol = token.symbol()
185                )
186            }
187            Self::IfUndefined { token, trace: _ } => {
188                format!(
189                    "Attempted to use `#if` on an undefined macro, `{symbol:?}`",
190                    symbol = token.symbol()
191                )
192            }
193            Self::FunctionCallArgumentCount {
194                token,
195                expected,
196                got,
197                trace: _,
198                defines: _,
199            } => {
200                format!("Function call with incorrect number of arguments, expected `{expected}` got `{got}`. `{symbol:?}`", symbol = token.symbol())
201            }
202            Self::ExpectedFunctionOrValue { token, .. } => {
203                format!(
204                    "Expected Function or Value, found Unit, `{symbol:?}`",
205                    symbol = token.symbol()
206                )
207            }
208            Self::ResolveWithNoResolver { token: _, trace: _ } => {
209                "`#include` was encountered while using `NoResolver`".to_string()
210            }
211            Self::IncludeNotFound { target, trace: _ } => {
212                let target = target
213                    .iter()
214                    .map(|t| t.symbol().to_string())
215                    .collect::<String>();
216                format!("`#include` target `{target:?}` was not found")
217            }
218            Self::Io(e) => {
219                format!("IO Error: {e}")
220            }
221            Self::Pest(e) => {
222                format!("Pest Error: {e}")
223            }
224        }
225    }
226
227    fn details(&self) -> Option<String> {
228        match self {
229            Self::ExpectedFunctionOrValue { skipped, .. } => {
230                let empty_comment = skipped.iter().all(|t| {
231                    matches!(t.symbol(), Symbol::Comment(_))
232                        || matches!(t.symbol(), Symbol::Whitespace(_))
233                });
234                if empty_comment {
235                    Some(String::from("`#define` with only a comment is considered a Unit (flag). This differs from other preprocessors."))
236                } else {
237                    None
238                }
239            }
240            _ => None,
241        }
242    }
243
244    fn help(&self) -> Option<String> {
245        match self {
246            Self::ExpectedFunctionOrValue { token, skipped, .. } => {
247                let empty_comment = skipped.iter().all(|t| {
248                    matches!(t.symbol(), Symbol::Comment(_))
249                        || matches!(t.symbol(), Symbol::Whitespace(_))
250                });
251                if empty_comment {
252                    Some(format!(
253                        "Try using `#define {} ; /* .. */`",
254                        token.symbol().output()
255                    ))
256                } else {
257                    None
258                }
259            }
260            Self::FunctionCallArgumentCount {
261                token,
262                expected: _,
263                got,
264                trace: _,
265                defines,
266            } => {
267                let Symbol::Word(function) = token.symbol() else {
268                    return None;
269                };
270                let did_you_mean = defines.similar_function(function, Some(*got));
271                Some(format!("Did you mean `{}`?", did_you_mean.join("`, `")))
272            }
273            _ => None,
274        }
275    }
276
277    fn source(&self) -> Option<Box<Source>> {
278        match self {
279            Self::UnexpectedToken {
280                token,
281                expected,
282                trace: _,
283            } => make_source(token, format!("expected one of: {expected:?}"))
284                .ok()
285                .map(Box::new),
286            Self::ExpectedIdent { token, trace: _ } => {
287                make_source(token, "expected an identifier".to_string())
288                    .ok()
289                    .map(Box::new)
290            }
291            Self::UnknownDirective {
292                directive,
293                trace: _,
294            } => make_source(directive, "unknown directive".to_string())
295                .ok()
296                .map(Box::new),
297            Self::DefineMultiTokenArgument { token, trace: _ } => {
298                make_source(token, "invalid arguments".to_string())
299                    .ok()
300                    .map(Box::new)
301            }
302            Self::ChangeBuiltin { token, trace: _ } => {
303                make_source(token, "build-in macro".to_string())
304                    .ok()
305                    .map(Box::new)
306            }
307            Self::IfUnitOrFunction { token, trace: _ } => {
308                make_source(token, "invalid macro type".to_string())
309                    .ok()
310                    .map(Box::new)
311            }
312            Self::IfUndefined { token, trace: _ } => {
313                make_source(token, "macro is undefined".to_string())
314                    .ok()
315                    .map(Box::new)
316            }
317            Self::FunctionCallArgumentCount {
318                token, expected, ..
319            } => make_source(token, format!("Expects {expected} arguments"))
320                .ok()
321                .map(Box::new),
322            Self::ExpectedFunctionOrValue { token, .. } => {
323                make_source(token, "expects function or value".to_string())
324                    .ok()
325                    .map(Box::new)
326            }
327            _ => None,
328        }
329    }
330
331    fn trace(&self) -> Vec<Source> {
332        let trace = match self {
333            Self::UnexpectedToken { trace, .. }
334            | Self::ExpectedIdent { trace, .. }
335            | Self::UnknownDirective { trace, .. }
336            | Self::DefineMultiTokenArgument { trace, .. }
337            | Self::ChangeBuiltin { trace, .. }
338            | Self::IfUnitOrFunction { trace, .. }
339            | Self::IfUndefined { trace, .. }
340            | Self::FunctionCallArgumentCount { trace, .. }
341            | Self::ExpectedFunctionOrValue { trace, .. }
342            | Self::ResolveWithNoResolver { trace, .. }
343            | Self::IncludeNotFound { trace, .. } => trace.clone(),
344            _ => vec![],
345        };
346        trace
347            .into_iter()
348            .map(|t| make_source(&t, String::new()).unwrap()) // TODO remove unwrap
349            .collect()
350    }
351}