solidity_language_server/
build.rs1use crate::utils::byte_offset_to_position;
2use serde_json::Value;
3use std::path::Path;
4use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity, NumberOrString, Range};
5
6pub fn ignored_error_code_warning(value: &serde_json::Value) -> bool {
7 let error_code = value
8 .get("errorCode")
9 .and_then(|v| v.as_str())
10 .unwrap_or_default();
11
12 error_code == "5574" || error_code == "3860"
13}
14
15pub fn build_output_to_diagnostics(
16 forge_output: &serde_json::Value,
17 path: impl AsRef<Path>,
18 content: &str,
19) -> Vec<Diagnostic> {
20 let Some(errors) = forge_output.get("errors").and_then(|v| v.as_array()) else {
21 return Vec::new();
22 };
23 let path = path.as_ref();
24 errors
25 .iter()
26 .filter_map(|err| parse_diagnostic(err, path, content))
27 .collect()
28}
29
30fn source_location_matches(source_path: &str, path: &Path) -> bool {
38 let source_path = Path::new(source_path);
39 if source_path.is_absolute() {
40 source_path == path
41 } else {
42 path.ends_with(source_path)
43 }
44}
45
46fn parse_diagnostic(err: &Value, path: &Path, content: &str) -> Option<Diagnostic> {
47 if ignored_error_code_warning(err) {
48 return None;
49 }
50 let source_file = err
51 .get("sourceLocation")
52 .and_then(|loc| loc.get("file"))
53 .and_then(|f| f.as_str())?;
54
55 if !source_location_matches(source_file, path) {
56 return None;
57 }
58
59 let start_offset = err
60 .get("sourceLocation")
61 .and_then(|loc| loc.get("start"))
62 .and_then(|s| s.as_u64())
63 .unwrap_or(0) as usize;
64
65 let end_offset = err
66 .get("sourceLocation")
67 .and_then(|loc| loc.get("end"))
68 .and_then(|s| s.as_u64())
69 .map(|v| v as usize)
70 .unwrap_or(start_offset);
71
72 let start = byte_offset_to_position(content, start_offset);
73 let end = byte_offset_to_position(content, end_offset);
74
75 let range = Range { start, end };
76
77 let message = err
78 .get("message")
79 .and_then(|m| m.as_str())
80 .unwrap_or("Unknown error");
81
82 let severity = match err.get("severity").and_then(|s| s.as_str()) {
83 Some("error") => Some(DiagnosticSeverity::ERROR),
84 Some("warning") => Some(DiagnosticSeverity::WARNING),
85 Some("note") => Some(DiagnosticSeverity::INFORMATION),
86 Some("help") => Some(DiagnosticSeverity::HINT),
87 _ => Some(DiagnosticSeverity::INFORMATION),
88 };
89
90 let code = err
91 .get("errorCode")
92 .and_then(|c| c.as_str())
93 .map(|s| NumberOrString::String(s.to_string()));
94
95 Some(Diagnostic {
96 range,
97 severity,
98 code,
99 code_description: None,
100 source: Some("forge-build".to_string()),
101 message: format!("[forge build] {message}"),
102 related_information: None,
103 tags: None,
104 data: None,
105 })
106}