Skip to content

Commit d9c48db

Browse files
[wit-parser] Migrate to structured errors in the AST/package parser (#2465)
* [wit-parser] Migrate to structured errors in the AST/package parser * Update snapshots * Derive Eq and PartialEq * Remove toposort::Error * Remove useless comments * Make highlight_span pub(crate) again * Expose ast/parsing errors * Introduce new_syntax helper * PackageParseErrorKind -> PackageParseErrors * Introduce ParseResult * Rename PackageParse -> Parse * Comments * Use singular ParseError * Remove some methods
1 parent 9e5a085 commit d9c48db

19 files changed

+456
-395
lines changed

crates/wit-parser/src/ast.rs

Lines changed: 136 additions & 127 deletions
Large diffs are not rendered by default.

crates/wit-parser/src/ast/error.rs

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

0 commit comments

Comments
 (0)