Skip to main content

wit_parser/ast/
error.rs

1//! Error types for WIT parsing.
2
3use alloc::boxed::Box;
4use alloc::string::{String, ToString};
5use core::fmt;
6
7use crate::{SourceMap, Span, ast::lex};
8
9/// Convenience alias for a `Result` whose error type is [`ParseError`].
10pub type ParseResult<T, E = ParseError> = Result<T, E>;
11
12/// The category of error that occurred while parsing a WIT package.
13#[non_exhaustive]
14#[derive(Debug, PartialEq, Eq)]
15pub enum ParseErrorKind {
16    /// Lexer error (invalid character, unterminated comment, etc.)
17    Lex(lex::Error),
18    /// Syntactic or semantic error within a single package (duplicate name,
19    /// invalid attribute, etc.)
20    Syntax { span: Span, message: String },
21    /// A type/interface/world references a name that does not exist within
22    /// the same package.
23    ItemNotFound {
24        span: Span,
25        name: String,
26        kind: String,
27        hint: Option<String>,
28    },
29    /// A type/interface/world depends on itself.
30    TypeCycle {
31        span: Span,
32        name: String,
33        kind: String,
34    },
35}
36
37impl ParseErrorKind {
38    /// Returns the source span associated with this error.
39    pub fn span(&self) -> Span {
40        match self {
41            ParseErrorKind::Lex(e) => Span::new(e.position(), e.position() + 1),
42            ParseErrorKind::Syntax { span, .. }
43            | ParseErrorKind::ItemNotFound { span, .. }
44            | ParseErrorKind::TypeCycle { span, .. } => *span,
45        }
46    }
47}
48
49impl fmt::Display for ParseErrorKind {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        match self {
52            ParseErrorKind::Lex(e) => fmt::Display::fmt(e, f),
53            ParseErrorKind::Syntax { message, .. } => message.fmt(f),
54            ParseErrorKind::ItemNotFound {
55                kind, name, hint, ..
56            } => {
57                write!(f, "{kind} `{name}` does not exist")?;
58                if let Some(hint) = hint {
59                    write!(f, "\n{hint}")?;
60                }
61                Ok(())
62            }
63            ParseErrorKind::TypeCycle { kind, name, .. } => {
64                write!(f, "{kind} `{name}` depends on itself")
65            }
66        }
67    }
68}
69
70/// A single structured error from parsing a WIT package.
71#[derive(Debug, PartialEq, Eq)]
72pub struct ParseError(Box<ParseErrorKind>);
73
74impl ParseError {
75    pub fn new_syntax(span: Span, message: impl Into<String>) -> Self {
76        ParseErrorKind::Syntax {
77            span,
78            message: message.into(),
79        }
80        .into()
81    }
82
83    /// Returns the underlying error kind
84    pub fn kind(&self) -> &ParseErrorKind {
85        &self.0
86    }
87
88    /// Returns the underlying error kind (mutable).
89    pub fn kind_mut(&mut self) -> &mut ParseErrorKind {
90        &mut self.0
91    }
92
93    /// Renders this error with source context (file:line:col + snippet).
94    ///
95    /// `source_map` must be the map this error's spans are valid in.
96    pub fn render(&self, source_map: &SourceMap) -> String {
97        let e = self.kind();
98        source_map
99            .highlight_span(e.span(), e)
100            .unwrap_or_else(|| e.to_string())
101    }
102
103    pub(crate) fn adjust_spans(&mut self, offset: u32) {
104        match self.kind_mut() {
105            ParseErrorKind::Lex(e) => e.adjust_position(offset),
106            ParseErrorKind::Syntax { span, .. }
107            | ParseErrorKind::ItemNotFound { span, .. }
108            | ParseErrorKind::TypeCycle { span, .. } => span.adjust(offset),
109        }
110    }
111}
112
113impl fmt::Display for ParseError {
114    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        fmt::Display::fmt(self.kind(), f)
116    }
117}
118
119impl core::error::Error for ParseError {}
120
121impl From<ParseErrorKind> for ParseError {
122    fn from(kind: ParseErrorKind) -> Self {
123        ParseError(Box::new(kind))
124    }
125}
126
127impl From<lex::Error> for ParseError {
128    fn from(e: lex::Error) -> Self {
129        ParseErrorKind::Lex(e).into()
130    }
131}