solidity_language_server/
lint.rs1use 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 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), character: (span.column_start - 1), },
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: if !forge_diag.message.is_empty() {
50 forge_diag.message.clone()
51 } else if let Some(rendered) = &forge_diag.rendered {
52 rendered.clone()
53 } else if let Some(label) = &span.label {
54 label.clone()
55 } else {
56 "Lint warning".to_string()
57 },
58 related_information: None,
59 tags: None,
60 data: None,
61 };
62 diagnostics.push(diagnostic);
63 break; }
65 }
66 }
67 }
68 }
69
70 diagnostics
71}
72
73#[derive(Debug, Deserialize, Serialize)]
74pub struct ForgeDiagnostic {
75 #[serde(rename = "$message_type")]
76 pub message_type: String,
77 pub message: String,
78 pub code: Option<ForgeLintCode>,
79 pub level: String,
80 pub spans: Vec<ForgeLintSpan>,
81 pub children: Vec<ForgeLintChild>,
82 pub rendered: Option<String>,
83}
84
85#[derive(Debug, Deserialize, Serialize)]
86pub struct ForgeLintCode {
87 pub code: String,
88 pub explanation: Option<String>,
89}
90
91#[derive(Debug, Deserialize, Serialize)]
92pub struct ForgeLintSpan {
93 pub file_name: String,
94 pub byte_start: u32,
95 pub byte_end: u32,
96 pub line_start: u32,
97 pub line_end: u32,
98 pub column_start: u32,
99 pub column_end: u32,
100 pub is_primary: bool,
101 pub text: Vec<ForgeLintText>,
102 pub label: Option<String>,
103}
104
105#[derive(Debug, Deserialize, Serialize)]
106pub struct ForgeLintText {
107 pub text: String,
108 pub highlight_start: u32,
109 pub highlight_end: u32,
110}
111
112#[derive(Debug, Deserialize, Serialize)]
113pub struct ForgeLintChild {
114 pub message: String,
115 pub code: Option<String>,
116 pub level: String,
117 pub spans: Vec<ForgeLintSpan>,
118 pub children: Vec<ForgeLintChild>,
119 pub rendered: Option<String>,
120}