Skip to main content

solidity_language_server/
lint.rs

1use serde::{Deserialize, Serialize};
2use std::path::Path;
3use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity, Position, Range};
4
5pub fn lint_output_to_diagnostics(
6    forge_output: &serde_json::Value,
7    target_file: &str,
8) -> Vec<Diagnostic> {
9    let mut diagnostics = Vec::new();
10
11    if let serde_json::Value::Array(items) = forge_output {
12        for item in items {
13            if let Ok(forge_diag) = serde_json::from_value::<ForgeDiagnostic>(item.clone()) {
14                // Only include diagnostics for the target file
15                for span in &forge_diag.spans {
16                    let target_path = Path::new(target_file)
17                        .canonicalize()
18                        .unwrap_or_else(|_| Path::new(target_file).to_path_buf());
19                    let span_path = Path::new(&span.file_name)
20                        .canonicalize()
21                        .unwrap_or_else(|_| Path::new(&span.file_name).to_path_buf());
22                    if target_path == span_path && span.is_primary {
23                        let diagnostic = Diagnostic {
24                            range: Range {
25                                start: Position {
26                                    line: (span.line_start - 1),        // LSP is 0-based
27                                    character: (span.column_start - 1), // LSP is 0-based
28                                },
29                                end: Position {
30                                    line: (span.line_end - 1),
31                                    character: (span.column_end - 1),
32                                },
33                            },
34                            severity: Some(match forge_diag.level.as_str() {
35                                "error" => DiagnosticSeverity::ERROR,
36                                "warning" => DiagnosticSeverity::WARNING,
37                                "note" => DiagnosticSeverity::INFORMATION,
38                                "help" => DiagnosticSeverity::HINT,
39                                _ => DiagnosticSeverity::INFORMATION,
40                            }),
41                            code: forge_diag.code.as_ref().map(|c| {
42                                tower_lsp::lsp_types::NumberOrString::String(c.code.clone())
43                            }),
44                            code_description: None,
45                            source: Some("forge-lint".to_string()),
46                            message: format!("[forge lint] {}", forge_diag.message),
47                            related_information: None,
48                            tags: None,
49                            data: None,
50                        };
51                        diagnostics.push(diagnostic);
52                        break; // Only take the first primary span per diagnostic
53                    }
54                }
55            }
56        }
57    }
58
59    diagnostics
60}
61
62#[derive(Debug, Deserialize, Serialize)]
63pub struct ForgeDiagnostic {
64    #[serde(rename = "$message_type")]
65    pub message_type: String,
66    pub message: String,
67    pub code: Option<ForgeLintCode>,
68    pub level: String,
69    pub spans: Vec<ForgeLintSpan>,
70    pub children: Vec<ForgeLintChild>,
71    pub rendered: Option<String>,
72}
73
74#[derive(Debug, Deserialize, Serialize)]
75pub struct ForgeLintCode {
76    pub code: String,
77    pub explanation: Option<String>,
78}
79
80#[derive(Debug, Deserialize, Serialize)]
81pub struct ForgeLintSpan {
82    pub file_name: String,
83    pub byte_start: u32,
84    pub byte_end: u32,
85    pub line_start: u32,
86    pub line_end: u32,
87    pub column_start: u32,
88    pub column_end: u32,
89    pub is_primary: bool,
90    pub text: Vec<ForgeLintText>,
91    pub label: Option<String>,
92}
93
94#[derive(Debug, Deserialize, Serialize)]
95pub struct ForgeLintText {
96    pub text: String,
97    pub highlight_start: u32,
98    pub highlight_end: u32,
99}
100
101#[derive(Debug, Deserialize, Serialize)]
102pub struct ForgeLintChild {
103    pub message: String,
104    pub code: Option<String>,
105    pub level: String,
106    pub spans: Vec<ForgeLintSpan>,
107    pub children: Vec<ForgeLintChild>,
108    pub rendered: Option<String>,
109}
110
111