Skip to main content

zen_expression/intellisense/
diagnostic.rs

1use crate::compiler::CompilerError;
2use crate::intellisense::type_provider::TypesProvider;
3use crate::lexer::LexerError;
4use crate::parser::Node;
5use nohash_hasher::BuildNoHashHasher;
6use serde::Serialize;
7use std::cell::RefCell;
8use std::collections::HashMap;
9
10use crate::parser::NodeMetadata;
11
12#[derive(Debug, Clone, Serialize)]
13#[serde(rename_all = "camelCase")]
14pub struct Diagnostic {
15    pub span: (u32, u32),
16    pub message: String,
17    pub severity: Severity,
18    pub source: DiagnosticSource,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
22#[serde(rename_all = "camelCase")]
23pub enum Severity {
24    Error,
25    Warning,
26    Hint,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
30#[serde(rename_all = "camelCase")]
31pub enum DiagnosticSource {
32    Lexer,
33    Parser,
34    TypeCheck,
35    Compiler,
36}
37
38pub(crate) fn lexer_error_to_diagnostic(err: &LexerError) -> Diagnostic {
39    let span = match err {
40        LexerError::UnexpectedSymbol { span, .. } => *span,
41        LexerError::UnmatchedSymbol { position, .. } => (*position, *position),
42        LexerError::UnexpectedEof { position, .. } => (*position, *position),
43    };
44
45    Diagnostic {
46        span,
47        message: err.to_string(),
48        severity: Severity::Error,
49        source: DiagnosticSource::Lexer,
50    }
51}
52
53pub(crate) fn collect_parser_diagnostics(ast: &Node, diagnostics: &mut Vec<Diagnostic>) {
54    let collected = RefCell::new(Vec::new());
55    ast.walk(|node| {
56        if let Node::Error { error, .. } = node {
57            collected.borrow_mut().push(Diagnostic {
58                span: node.span().unwrap_or_default(),
59                message: error.to_string(),
60                severity: Severity::Error,
61                source: DiagnosticSource::Parser,
62            });
63        }
64    });
65    diagnostics.extend(collected.into_inner());
66}
67
68pub(crate) fn collect_type_diagnostics(
69    ast: &Node,
70    type_data: &TypesProvider,
71    metadata: &HashMap<usize, NodeMetadata, BuildNoHashHasher<usize>>,
72    diagnostics: &mut Vec<Diagnostic>,
73) {
74    let collected = RefCell::new(Vec::new());
75    ast.walk(|node| {
76        let Some(type_info) = type_data.get_type(node) else {
77            return;
78        };
79
80        let Some(error) = &type_info.error else {
81            return;
82        };
83
84        let addr = node as *const Node as usize;
85        let span = node
86            .span()
87            .or_else(|| metadata.get(&addr).map(|m| m.span))
88            .unwrap_or_default();
89
90        let (severity, message) = if let Some(hint) = error.strip_prefix("Hint:") {
91            (Severity::Warning, hint.trim().to_string())
92        } else if let Some(lint) = error.strip_prefix("Lint:") {
93            (Severity::Hint, lint.trim().to_string())
94        } else {
95            (Severity::Error, error.clone())
96        };
97
98        collected.borrow_mut().push(Diagnostic {
99            span,
100            message,
101            severity,
102            source: DiagnosticSource::TypeCheck,
103        });
104    });
105    diagnostics.extend(collected.into_inner());
106}
107
108pub(crate) fn compiler_error_to_diagnostic(err: &CompilerError) -> Diagnostic {
109    Diagnostic {
110        span: (0, 0),
111        message: err.to_string(),
112        severity: Severity::Error,
113        source: DiagnosticSource::Compiler,
114    }
115}