solidity_language_server/
build.rs1use crate::utils::byte_offset_to_position;
2use std::path::Path;
3use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity, NumberOrString, Position, Range};
4
5pub fn ignored_error_code_warning(value: &serde_json::Value) -> bool {
6 let error_code = value
7 .get("errorCode")
8 .and_then(|v| v.as_str())
9 .unwrap_or_default();
10
11 error_code == "5574" || error_code == "3860"
12}
13
14pub fn build_output_to_diagnostics(
15 forge_output: &serde_json::Value,
16 filename: &str,
17 content: &str,
18) -> Vec<Diagnostic> {
19 let mut diagnostics = Vec::new();
20
21 if let Some(errors) = forge_output.get("errors").and_then(|e| e.as_array()) {
22 for err in errors {
23 if ignored_error_code_warning(err) {
24 continue;
25 }
26
27 let source_file = err
28 .get("sourceLocation")
29 .and_then(|loc| loc.get("file"))
30 .and_then(|f| f.as_str())
31 .and_then(|full_path| Path::new(full_path).file_name())
32 .and_then(|os_str| os_str.to_str());
33
34 if source_file != Some(filename) {
35 continue;
36 }
37
38 let start_offset = err
39 .get("sourceLocation")
40 .and_then(|loc| loc.get("start"))
41 .and_then(|s| s.as_u64())
42 .unwrap_or(0) as usize;
43
44 let end_offset = err
45 .get("sourceLocation")
46 .and_then(|loc| loc.get("end"))
47 .and_then(|s| s.as_u64())
48 .map(|v| v as usize)
49 .unwrap_or(start_offset);
50
51 let (start_line, start_col) = byte_offset_to_position(content, start_offset);
52 let (mut end_line, mut end_col) = byte_offset_to_position(content, end_offset);
53
54 if end_col > 0 {
55 end_col -= 1;
56 } else if end_line > 0 {
57 end_line -= 1;
58 end_col = content
59 .lines()
60 .nth(end_line.try_into().unwrap())
61 .map(|l| l.len() as u32)
62 .unwrap_or(0);
63 }
64
65 let range = Range {
66 start: Position {
67 line: start_line,
68 character: start_col,
69 },
70 end: Position {
71 line: end_line,
72 character: end_col + 1,
73 },
74 };
75
76 let message = err
77 .get("message")
78 .and_then(|m| m.as_str())
79 .unwrap_or("Unknown error")
80 .to_string();
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 diagnostics.push(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 }
107 }
108
109 diagnostics
110}