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_code_for_tests(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 let file_path = value
11 .get("sourceLocation")
12 .and_then(|loc| loc.get("file"))
13 .and_then(|f| f.as_str())
14 .unwrap_or_default();
15
16 (error_code == "5574" && (file_path.contains(".t.sol") || file_path.contains(".s.sol")))
18 || (error_code == "3860" && (file_path.contains(".t.sol") || file_path.contains(".s.sol")))
19}
20
21pub fn build_output_to_diagnostics(
22 forge_output: &serde_json::Value,
23 filename: &str,
24 content: &str,
25) -> Vec<Diagnostic> {
26 let mut diagnostics = Vec::new();
27
28 if let Some(errors) = forge_output.get("errors").and_then(|e| e.as_array()) {
29 for err in errors {
30 if ignored_code_for_tests(err) {
31 continue;
32 }
33
34 let source_file = err
35 .get("sourceLocation")
36 .and_then(|loc| loc.get("file"))
37 .and_then(|f| f.as_str())
38 .and_then(|full_path| Path::new(full_path).file_name())
39 .and_then(|os_str| os_str.to_str());
40
41 if source_file != Some(filename) {
42 continue;
43 }
44
45 let start_offset = err
46 .get("sourceLocation")
47 .and_then(|loc| loc.get("start"))
48 .and_then(|s| s.as_u64())
49 .unwrap_or(0) as usize;
50
51 let end_offset = err
52 .get("sourceLocation")
53 .and_then(|loc| loc.get("end"))
54 .and_then(|s| s.as_u64())
55 .map(|v| v as usize)
56 .unwrap_or(start_offset);
57
58 let (start_line, start_col) = byte_offset_to_position(content, start_offset);
59 let (mut end_line, mut end_col) = byte_offset_to_position(content, end_offset);
60
61 if end_col > 0 {
62 end_col -= 1;
63 } else if end_line > 0 {
64 end_line -= 1;
65 end_col = content
66 .lines()
67 .nth(end_line.try_into().unwrap())
68 .map(|l| l.len() as u32)
69 .unwrap_or(0);
70 }
71
72 let range = Range {
73 start: Position {
74 line: start_line,
75 character: start_col,
76 },
77 end: Position {
78 line: end_line,
79 character: end_col + 1,
80 },
81 };
82
83 let message = err
84 .get("message")
85 .and_then(|m| m.as_str())
86 .unwrap_or("Unknown error")
87 .to_string();
88
89 let severity = match err.get("severity").and_then(|s| s.as_str()) {
90 Some("error") => Some(DiagnosticSeverity::ERROR),
91 Some("warning") => Some(DiagnosticSeverity::WARNING),
92 Some("note") => Some(DiagnosticSeverity::INFORMATION),
93 Some("help") => Some(DiagnosticSeverity::HINT),
94 _ => Some(DiagnosticSeverity::INFORMATION),
95 };
96
97 let code = err
98 .get("errorCode")
99 .and_then(|c| c.as_str())
100 .map(|s| NumberOrString::String(s.to_string()));
101
102 diagnostics.push(Diagnostic {
103 range,
104 severity,
105 code,
106 code_description: None,
107 source: Some("forge-build".to_string()),
108 message: format!("[forge build] {message}"),
109 related_information: None,
110 tags: None,
111 data: None,
112 });
113 }
114 }
115
116 diagnostics
117}