zen_expression/intellisense/
diagnostic.rs1use 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}