Add annotate_snippets for better parsing errors (#477)

Adds nicer errors for Podlang code, using the `annotate_snippets` crate, the same crate used by the Rust compiler to generate contextual errors. This prints a short snippet of the code containing the error within the error message, highlighting the part that needs to be fixed.

It also includes a change to the `load_module` function, changing a `Vec` function argument to a slice.
This commit is contained in:
Rob Knight 2026-02-11 11:14:23 +01:00 committed by GitHub
parent acab26e5c1
commit 09d67de989
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 612 additions and 40 deletions

View file

@ -26,6 +26,7 @@
//! Large predicates are automatically split into chains of smaller predicates;
//! `apply_predicate` handles this transparently.
//!
pub mod diagnostics;
pub mod error;
pub mod frontend_ast;
pub mod frontend_ast_lower;
@ -37,7 +38,8 @@ pub mod pretty_print;
use std::sync::Arc;
pub use error::LangError;
pub use diagnostics::render_error;
pub use error::{LangError, LangErrorKind};
pub use frontend_ast_split::{SplitChainInfo, SplitChainPiece, SplitResult};
pub use module::{Module, MultiOperationError};
pub use parser::{parse_podlang, Pairs, ParseError, Rule};
@ -57,7 +59,17 @@ pub fn load_module(
source: &str,
name: &str,
params: &Params,
available_modules: Vec<Arc<Module>>,
available_modules: &[Arc<Module>],
) -> Result<Module, LangError> {
load_module_inner(source, name, params, available_modules)
.map_err(|e| e.with_source(source.to_string(), None))
}
fn load_module_inner(
source: &str,
name: &str,
params: &Params,
available_modules: &[Arc<Module>],
) -> Result<Module, LangError> {
let pairs = parse_podlang(source)?;
let document_pair = pairs
@ -89,6 +101,15 @@ pub fn parse_request(
source: &str,
params: &Params,
available_modules: &[Arc<Module>],
) -> Result<PodRequest, LangError> {
parse_request_inner(source, params, available_modules)
.map_err(|e| e.with_source(source.to_string(), None))
}
fn parse_request_inner(
source: &str,
params: &Params,
available_modules: &[Arc<Module>],
) -> Result<PodRequest, LangError> {
let pairs = parse_podlang(source)?;
let document_pair = pairs
@ -160,7 +181,7 @@ mod tests {
"#;
let params = Params::default();
let module = load_module(input, "test_module", &params, vec![])?;
let module = load_module(input, "test_module", &params, &[])?;
assert_eq!(module.batch.predicates().len(), 1);
@ -235,7 +256,7 @@ mod tests {
"#;
let params = Params::default();
let module = load_module(input, "test_module", &params, vec![])?;
let module = load_module(input, "test_module", &params, &[])?;
assert_eq!(module.batch.predicates().len(), 1);
@ -281,7 +302,7 @@ mod tests {
"#;
let params = Params::default();
let module = Arc::new(load_module(module_input, "my_module", &params, vec![])?);
let module = Arc::new(load_module(module_input, "my_module", &params, &[])?);
assert_eq!(module.batch.predicates().len(), 1);
@ -330,7 +351,7 @@ mod tests {
"#;
let params = Params::default();
let module = Arc::new(load_module(module_input, "some_module", &params, vec![])?);
let module = Arc::new(load_module(module_input, "some_module", &params, &[])?);
let module_hash = module.id().encode_hex::<String>();
@ -585,7 +606,7 @@ mod tests {
)
"#;
let module = load_module(input, "ethdos", &params, vec![])?;
let module = load_module(input, "ethdos", &params, &[])?;
assert_eq!(
module.batch.predicates().len(),
@ -855,7 +876,7 @@ mod tests {
);
// 3. Load as module
let module = load_module(&input, "test", &params, vec![extmod])?;
let module = load_module(&input, "test", &params, &[extmod])?;
assert_eq!(
module.batch.predicates().len(),
@ -1011,8 +1032,8 @@ mod tests {
assert!(result.is_err());
match result.err().unwrap() {
LangError::Validation(e) => match *e {
match result.err().unwrap().kind {
LangErrorKind::Validation(e) => match *e {
frontend_ast_validate::ValidationError::ModuleNotFound { name, .. } => {
// The error now carries the hex-formatted hash
assert_eq!(name, fake_hash);
@ -1034,12 +1055,12 @@ mod tests {
)
"#;
let result = load_module(input, "test", &params, vec![]);
let result = load_module(input, "test", &params, &[]);
assert!(result.is_err());
match result.err().unwrap() {
LangError::Validation(e) => match *e {
match result.err().unwrap().kind {
LangErrorKind::Validation(e) => match *e {
frontend_ast_validate::ValidationError::UndefinedWildcard {
name,
pred_name,