Skip to main content

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_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}