1use colored::*;
2use std::fmt;
3
4#[derive(Debug, Clone)]
5pub struct BorrowCheckDiagnostic {
6 pub severity: Severity,
7 pub message: String,
8 pub location: Location,
9 pub help: Option<String>,
10 pub notes: Vec<String>,
11}
12
13#[derive(Debug, Clone)]
14#[allow(dead_code)]
15pub enum Severity {
16 Error,
17 Warning,
18 Note,
19}
20
21#[derive(Debug, Clone)]
22pub struct Location {
23 pub file: String,
24 pub line: u32,
25 pub column: u32,
26 #[allow(dead_code)]
27 pub span: Option<(usize, usize)>,
28}
29
30impl fmt::Display for BorrowCheckDiagnostic {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 let severity_str = match self.severity {
33 Severity::Error => "error".red().bold(),
34 Severity::Warning => "warning".yellow().bold(),
35 Severity::Note => "note".blue().bold(),
36 };
37
38 writeln!(
39 f,
40 "{}: {}",
41 severity_str,
42 self.message.bold()
43 )?;
44
45 writeln!(
46 f,
47 " {} {}:{}:{}",
48 "-->".blue(),
49 self.location.file,
50 self.location.line,
51 self.location.column
52 )?;
53
54 if let Some(ref help) = self.help {
55 writeln!(f, "{}: {}", "help".green().bold(), help)?;
56 }
57
58 for note in &self.notes {
59 writeln!(f, "{}: {}", "note".blue(), note)?;
60 }
61
62 Ok(())
63 }
64}
65
66#[allow(dead_code)]
67pub fn format_use_after_move(var_name: &str, location: Location) -> BorrowCheckDiagnostic {
68 BorrowCheckDiagnostic {
69 severity: Severity::Error,
70 message: format!("use of moved value: `{}`", var_name),
71 location,
72 help: Some(format!(
73 "consider using `std::move` explicitly or creating a copy"
74 )),
75 notes: vec![
76 format!("value `{}` was moved previously", var_name),
77 "once a value is moved, it cannot be used again".to_string(),
78 ],
79 }
80}
81
82#[allow(dead_code)]
83pub fn format_double_borrow(var_name: &str, location: Location) -> BorrowCheckDiagnostic {
84 BorrowCheckDiagnostic {
85 severity: Severity::Error,
86 message: format!("cannot borrow `{}` as mutable more than once", var_name),
87 location,
88 help: Some("consider using a shared reference instead".to_string()),
89 notes: vec![
90 "only one mutable borrow is allowed at a time".to_string(),
91 ],
92 }
93}
94
95#[allow(dead_code)]
96pub fn format_lifetime_error(message: String, location: Location) -> BorrowCheckDiagnostic {
97 BorrowCheckDiagnostic {
98 severity: Severity::Error,
99 message,
100 location,
101 help: Some("consider adjusting the lifetime annotations".to_string()),
102 notes: vec![],
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 #[test]
111 fn test_diagnostic_creation() {
112 let diag = BorrowCheckDiagnostic {
113 severity: Severity::Error,
114 message: "Test error".to_string(),
115 location: Location {
116 file: "test.cpp".to_string(),
117 line: 10,
118 column: 5,
119 span: Some((100, 105)),
120 },
121 help: Some("Try this instead".to_string()),
122 notes: vec!["Note 1".to_string()],
123 };
124
125 assert!(matches!(diag.severity, Severity::Error));
126 assert_eq!(diag.message, "Test error");
127 assert_eq!(diag.location.line, 10);
128 assert_eq!(diag.help, Some("Try this instead".to_string()));
129 assert_eq!(diag.notes.len(), 1);
130 }
131
132 #[test]
133 fn test_format_use_after_move() {
134 let location = Location {
135 file: "test.cpp".to_string(),
136 line: 42,
137 column: 8,
138 span: None,
139 };
140
141 let diag = format_use_after_move("ptr", location);
142
143 assert!(matches!(diag.severity, Severity::Error));
144 assert!(diag.message.contains("ptr"));
145 assert!(diag.message.contains("moved"));
146 assert!(diag.help.is_some());
147 assert!(!diag.notes.is_empty());
148 }
149
150 #[test]
151 fn test_format_double_borrow() {
152 let location = Location {
153 file: "test.cpp".to_string(),
154 line: 15,
155 column: 3,
156 span: None,
157 };
158
159 let diag = format_double_borrow("value", location);
160
161 assert!(matches!(diag.severity, Severity::Error));
162 assert!(diag.message.contains("value"));
163 assert!(diag.message.contains("mutable"));
164 assert!(diag.help.is_some());
165 }
166
167 #[test]
168 fn test_format_lifetime_error() {
169 let location = Location {
170 file: "test.cpp".to_string(),
171 line: 20,
172 column: 10,
173 span: Some((200, 210)),
174 };
175
176 let diag = format_lifetime_error(
177 "Reference outlives its source".to_string(),
178 location
179 );
180
181 assert!(matches!(diag.severity, Severity::Error));
182 assert_eq!(diag.message, "Reference outlives its source");
183 assert!(diag.help.is_some());
184 }
185
186 #[test]
187 fn test_diagnostic_display() {
188 let diag = BorrowCheckDiagnostic {
189 severity: Severity::Error,
190 message: "Test error message".to_string(),
191 location: Location {
192 file: "example.cpp".to_string(),
193 line: 5,
194 column: 10,
195 span: None,
196 },
197 help: None,
198 notes: vec![],
199 };
200
201 let output = format!("{}", diag);
202 assert!(output.contains("error"));
203 assert!(output.contains("Test error message"));
204 assert!(output.contains("example.cpp:5:10"));
205 }
206}