Skip to main content

wave_compiler/diagnostics/
report.rs

1// Copyright 2026 Ojima Abraham
2// SPDX-License-Identifier: Apache-2.0
3
4//! Error reporting with source context.
5//!
6//! Formats compiler errors with the relevant source line and
7//! a caret pointing to the error location.
8
9use std::fmt::Write;
10
11use super::error::{CompileError, SourceLoc};
12use super::source_map::SourceMap;
13
14/// Format a compile error with source context for display to the user.
15#[must_use]
16pub fn format_error(
17    error: &CompileError,
18    source_map: Option<&SourceMap>,
19    loc: Option<SourceLoc>,
20) -> String {
21    let mut out = String::new();
22
23    if let Some(loc) = loc {
24        let _ = write!(out, "error at {}:{}: ", loc.line, loc.col);
25    } else {
26        out.push_str("error: ");
27    }
28
29    out.push_str(&error.to_string());
30    out.push('\n');
31
32    if let (Some(map), Some(loc)) = (source_map, loc) {
33        if let Some(line) = map.get_line(loc.line) {
34            let _ = writeln!(out, " {} | {}", loc.line, line);
35            let padding = format!("{}", loc.line).len() + 3 + (loc.col as usize - 1);
36            let _ = writeln!(out, "{}^", " ".repeat(padding));
37        }
38    }
39
40    out
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46
47    #[test]
48    fn test_format_error_with_location() {
49        let err = CompileError::UndefinedVariable { name: "x".into() };
50        let src = "let y = x + 1".to_string();
51        let map = SourceMap::new(src);
52        let loc = SourceLoc { line: 1, col: 9 };
53        let output = format_error(&err, Some(&map), Some(loc));
54        assert!(output.contains("error at 1:9"));
55        assert!(output.contains("undefined variable: x"));
56        assert!(output.contains("let y = x + 1"));
57    }
58
59    #[test]
60    fn test_format_error_without_location() {
61        let err = CompileError::InternalError {
62            message: "something went wrong".into(),
63        };
64        let output = format_error(&err, None, None);
65        assert!(output.contains("error: internal error"));
66        assert!(output.contains("something went wrong"));
67    }
68}