solidity_language_server/
build.rs

1use 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    // Ignore error code 5574, 3860 for test files (code size limit)
17    (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}