diff options
author | Minijackson <minijackson@riseup.net> | 2020-01-23 20:58:59 +0100 |
---|---|---|
committer | Minijackson <minijackson@riseup.net> | 2020-01-23 20:58:59 +0100 |
commit | ab5a6da519f91faa1ba5aa281112e547fcd08b88 (patch) | |
tree | 8f6192c420f420cbbe081c92ca9f273b7e9c914e /src/parser/clang/diagnostics.rs | |
parent | ab042962e3977b9519476bc73c7252d24c8f587e (diff) | |
download | poseidoc-ab5a6da519f91faa1ba5aa281112e547fcd08b88.tar.gz poseidoc-ab5a6da519f91faa1ba5aa281112e547fcd08b88.zip |
clang-parser: centralize diagnostic building/reporting
Diffstat (limited to 'src/parser/clang/diagnostics.rs')
-rw-r--r-- | src/parser/clang/diagnostics.rs | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/src/parser/clang/diagnostics.rs b/src/parser/clang/diagnostics.rs new file mode 100644 index 0000000..7aa43be --- /dev/null +++ b/src/parser/clang/diagnostics.rs | |||
@@ -0,0 +1,313 @@ | |||
1 | use anyhow::{anyhow, Context, Result}; | ||
2 | use clang::{ | ||
3 | source::{SourceLocation as ClangSourceLocation, SourceRange as ClangSourceRange}, | ||
4 | Entity as ClangEntity, | ||
5 | }; | ||
6 | use codemap::File; | ||
7 | use codemap_diagnostic::{Diagnostic, Level, SpanLabel}; | ||
8 | use lazy_static::lazy_static; | ||
9 | |||
10 | use std::collections::{hash_map, HashMap}; | ||
11 | use std::path::PathBuf; | ||
12 | use std::sync::{Arc, RwLock}; | ||
13 | |||
14 | lazy_static! { | ||
15 | pub(super) static ref CODEMAP: RwLock<CodeMap> = RwLock::new(CodeMap::new()); | ||
16 | } | ||
17 | |||
18 | #[derive(Default)] | ||
19 | pub(super) struct CodeMap { | ||
20 | files: HashMap<PathBuf, Arc<File>>, | ||
21 | raw_codemap: codemap::CodeMap, | ||
22 | } | ||
23 | |||
24 | impl CodeMap { | ||
25 | pub fn new() -> Self { | ||
26 | Default::default() | ||
27 | } | ||
28 | |||
29 | pub fn span_from_entity(&mut self, entity: ClangEntity) -> Result<codemap::Span> { | ||
30 | self.span_from_libclang_range(entity.get_range().ok_or_else(|| { | ||
31 | anyhow!("Trying to build a diagnostic from an entity that doesn't have a range") | ||
32 | })?) | ||
33 | } | ||
34 | |||
35 | pub fn span_from_libclang_range(&mut self, range: ClangSourceRange) -> Result<codemap::Span> { | ||
36 | let start_file_location = range.get_start().get_file_location(); | ||
37 | let file_name = start_file_location | ||
38 | .file | ||
39 | .ok_or_else(|| { | ||
40 | anyhow!("Trying to build a diagnostic from an entity that isn't located in a file") | ||
41 | })? | ||
42 | .get_path(); | ||
43 | |||
44 | let file = self.get_file(file_name)?; | ||
45 | |||
46 | let begin = start_file_location.offset as u64; | ||
47 | let end = range.get_end().get_file_location().offset as u64; | ||
48 | |||
49 | Ok(file.span.subspan(begin, end)) | ||
50 | } | ||
51 | |||
52 | pub fn span_from_libclang_location( | ||
53 | &mut self, | ||
54 | location: ClangSourceLocation, | ||
55 | ) -> Result<codemap::Span> { | ||
56 | let file_location = location.get_file_location(); | ||
57 | let file_name = file_location | ||
58 | .file | ||
59 | .ok_or_else(|| { | ||
60 | anyhow!("Trying to build a diagnostic from an entity that isn't located in a file") | ||
61 | })? | ||
62 | .get_path(); | ||
63 | |||
64 | let file = self.get_file(file_name)?; | ||
65 | |||
66 | let offset = file_location.offset as u64; | ||
67 | |||
68 | Ok(file.span.subspan(offset, offset)) | ||
69 | } | ||
70 | |||
71 | pub fn get_file(&mut self, file_name: PathBuf) -> Result<&mut Arc<File>> { | ||
72 | Ok(match self.files.entry(file_name.clone()) { | ||
73 | hash_map::Entry::Occupied(file) => file.into_mut(), | ||
74 | hash_map::Entry::Vacant(entry) => { | ||
75 | let file = self.raw_codemap.add_file( | ||
76 | file_name | ||
77 | .to_str() | ||
78 | .context("Filename is not valid UTF-8")? | ||
79 | .to_owned(), | ||
80 | std::fs::read_to_string(&file_name) | ||
81 | .with_context(|| format!("Cannot readfile: {:?}", file_name))?, | ||
82 | ); | ||
83 | entry.insert(file) | ||
84 | } | ||
85 | }) | ||
86 | } | ||
87 | |||
88 | pub fn emitter(&self) -> codemap_diagnostic::Emitter { | ||
89 | use codemap_diagnostic::{ColorConfig, Emitter}; | ||
90 | Emitter::stderr(ColorConfig::Auto, Some(&self.raw_codemap)) | ||
91 | } | ||
92 | } | ||
93 | |||
94 | #[derive(Debug, Clone)] | ||
95 | pub(super) struct DiagnosticBuilder { | ||
96 | level: Level, | ||
97 | message: String, | ||
98 | code: Option<String>, | ||
99 | spans: Vec<SpanLabel>, | ||
100 | } | ||
101 | |||
102 | impl DiagnosticBuilder { | ||
103 | pub fn new(level: Level, message: String) -> Self { | ||
104 | DiagnosticBuilder { | ||
105 | level, | ||
106 | message, | ||
107 | code: None, | ||
108 | spans: vec![], | ||
109 | } | ||
110 | } | ||
111 | |||
112 | /* | ||
113 | pub fn error(message: String) -> Self { | ||
114 | Self::new(Level::Error, message) | ||
115 | } | ||
116 | |||
117 | pub fn warning(message: String) -> Self { | ||
118 | Self::new(Level::Warning, message) | ||
119 | } | ||
120 | |||
121 | pub fn note(message: String) -> Self { | ||
122 | Self::new(Level::Note, message) | ||
123 | } | ||
124 | |||
125 | pub fn help(message: String) -> Self { | ||
126 | Self::new(Level::Help, message) | ||
127 | } | ||
128 | |||
129 | pub fn bug(message: String) -> Self { | ||
130 | Self::new(Level::Bug, message) | ||
131 | } | ||
132 | |||
133 | pub fn level(&mut self, level: Level) -> &mut Self { | ||
134 | self.level = level; | ||
135 | self | ||
136 | } | ||
137 | |||
138 | pub fn message(&mut self, message: String) -> &mut Self { | ||
139 | self.message = message; | ||
140 | self | ||
141 | } | ||
142 | |||
143 | pub fn code(&mut self, code: String) -> &mut Self { | ||
144 | self.code = Some(code); | ||
145 | self | ||
146 | } | ||
147 | */ | ||
148 | |||
149 | pub fn span(&mut self, span: SpanLabel) -> &mut Self { | ||
150 | self.spans.push(span); | ||
151 | self | ||
152 | } | ||
153 | |||
154 | pub fn build(self) -> Diagnostic { | ||
155 | Diagnostic { | ||
156 | level: self.level, | ||
157 | code: self.code, | ||
158 | message: self.message, | ||
159 | spans: self.spans, | ||
160 | } | ||
161 | } | ||
162 | } | ||
163 | |||
164 | fn clang_fixit_to_label( | ||
165 | fixit: clang::diagnostic::FixIt, | ||
166 | ) -> Result<SpanLabel> { | ||
167 | use clang::diagnostic::FixIt; | ||
168 | use codemap_diagnostic::SpanStyle; | ||
169 | |||
170 | let mut codemap = CODEMAP | ||
171 | .write() | ||
172 | .expect("Failed to lock the codemap for writing"); | ||
173 | |||
174 | Ok(match fixit { | ||
175 | FixIt::Deletion(range) => { | ||
176 | let span = codemap.span_from_libclang_range(range)?; | ||
177 | SpanLabel { | ||
178 | span, | ||
179 | label: Some(String::from("help: try deleting this")), | ||
180 | style: SpanStyle::Secondary, | ||
181 | } | ||
182 | } | ||
183 | FixIt::Insertion(location, text) => { | ||
184 | let span = codemap.span_from_libclang_location(location)?; | ||
185 | SpanLabel { | ||
186 | span, | ||
187 | label: Some(format!("help: try inserting {:?}", text)), | ||
188 | style: SpanStyle::Secondary, | ||
189 | } | ||
190 | } | ||
191 | FixIt::Replacement(range, text) => { | ||
192 | let span = codemap.span_from_libclang_range(range)?; | ||
193 | SpanLabel { | ||
194 | span, | ||
195 | label: Some(format!("help: try replacing this with {:?}", text)), | ||
196 | style: SpanStyle::Secondary, | ||
197 | } | ||
198 | } | ||
199 | }) | ||
200 | } | ||
201 | |||
202 | pub(super) fn clang_diagnostic_to_diagnostics( | ||
203 | diagnostic: clang::diagnostic::Diagnostic, | ||
204 | ) -> Vec<Diagnostic> { | ||
205 | use clang::diagnostic::Severity; | ||
206 | use codemap_diagnostic::SpanStyle; | ||
207 | |||
208 | let level = match diagnostic.get_severity() { | ||
209 | Severity::Error | Severity::Fatal => Level::Error, | ||
210 | Severity::Warning => Level::Warning, | ||
211 | Severity::Note => Level::Note, | ||
212 | Severity::Ignored => return vec![], | ||
213 | }; | ||
214 | |||
215 | let mut diag = DiagnosticBuilder::new(level, diagnostic.get_text()); | ||
216 | |||
217 | { | ||
218 | let mut codemap = CODEMAP | ||
219 | .write() | ||
220 | .expect("Failed to lock the codemap for writing"); | ||
221 | if let Ok(span) = codemap.span_from_libclang_location(diagnostic.get_location()) { | ||
222 | diag.span(SpanLabel { | ||
223 | span, | ||
224 | label: None, | ||
225 | style: SpanStyle::Primary, | ||
226 | }); | ||
227 | }; | ||
228 | } | ||
229 | |||
230 | for range in diagnostic.get_ranges() { | ||
231 | let mut codemap = CODEMAP | ||
232 | .write() | ||
233 | .expect("Failed to lock the codemap for writing"); | ||
234 | let span = match codemap.span_from_libclang_range(range) { | ||
235 | Ok(span) => span, | ||
236 | Err(_) => continue, | ||
237 | }; | ||
238 | |||
239 | diag.span(SpanLabel { | ||
240 | span, | ||
241 | label: None, | ||
242 | style: SpanStyle::Primary, | ||
243 | }); | ||
244 | } | ||
245 | |||
246 | let mut child_diags = diagnostic | ||
247 | .get_children() | ||
248 | .into_iter() | ||
249 | .flat_map(|diag| clang_diagnostic_to_diagnostics(diag)) | ||
250 | .collect(); | ||
251 | |||
252 | for fixit_label in diagnostic | ||
253 | .get_fix_its() | ||
254 | .into_iter() | ||
255 | .filter_map(|fixit| clang_fixit_to_label(fixit).ok()) | ||
256 | { | ||
257 | diag.span(fixit_label); | ||
258 | } | ||
259 | |||
260 | let mut diags = vec![diag.build()]; | ||
261 | diags.append(&mut child_diags); | ||
262 | diags | ||
263 | } | ||
264 | |||
265 | pub(super) fn emit(diagnostics: &[Diagnostic]) { | ||
266 | CODEMAP | ||
267 | .read() | ||
268 | .expect("Failed to lock the codemap for reading") | ||
269 | .emitter() | ||
270 | .emit(diagnostics) | ||
271 | } | ||
272 | |||
273 | pub(super) fn log(level: Level, message: String, entity: ClangEntity) { | ||
274 | use codemap_diagnostic::SpanStyle; | ||
275 | |||
276 | let mut diag = DiagnosticBuilder::new(level, message); | ||
277 | |||
278 | if let Ok(span) = CODEMAP | ||
279 | .write() | ||
280 | .expect("Failed to lock the codemap for writing") | ||
281 | .span_from_entity(entity) | ||
282 | { | ||
283 | diag.span(SpanLabel { | ||
284 | span, | ||
285 | label: None, | ||
286 | style: SpanStyle::Primary, | ||
287 | }); | ||
288 | }; | ||
289 | |||
290 | emit(&[diag.build()]); | ||
291 | } | ||
292 | |||
293 | pub(super) fn error(message: String, entity: ClangEntity) { | ||
294 | log(Level::Error, message, entity) | ||
295 | } | ||
296 | |||
297 | /* | ||
298 | pub(super) fn warn(message: String, entity: ClangEntity) { | ||
299 | log(Level::Warning, message, entity) | ||
300 | } | ||
301 | |||
302 | pub(super) fn note(message: String, entity: ClangEntity) { | ||
303 | log(Level::Note, message, entity) | ||
304 | } | ||
305 | |||
306 | pub(super) fn help(message: String, entity: ClangEntity) { | ||
307 | log(Level::Help, message, entity) | ||
308 | } | ||
309 | |||
310 | pub(super) fn bug(message: String, entity: ClangEntity) { | ||
311 | log(Level::Bug, message, entity) | ||
312 | } | ||
313 | */ | ||