summaryrefslogtreecommitdiffstats
path: root/src/parser/clang/diagnostics.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/parser/clang/diagnostics.rs')
-rw-r--r--src/parser/clang/diagnostics.rs313
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 @@
1use anyhow::{anyhow, Context, Result};
2use clang::{
3 source::{SourceLocation as ClangSourceLocation, SourceRange as ClangSourceRange},
4 Entity as ClangEntity,
5};
6use codemap::File;
7use codemap_diagnostic::{Diagnostic, Level, SpanLabel};
8use lazy_static::lazy_static;
9
10use std::collections::{hash_map, HashMap};
11use std::path::PathBuf;
12use std::sync::{Arc, RwLock};
13
14lazy_static! {
15 pub(super) static ref CODEMAP: RwLock<CodeMap> = RwLock::new(CodeMap::new());
16}
17
18#[derive(Default)]
19pub(super) struct CodeMap {
20 files: HashMap<PathBuf, Arc<File>>,
21 raw_codemap: codemap::CodeMap,
22}
23
24impl 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)]
95pub(super) struct DiagnosticBuilder {
96 level: Level,
97 message: String,
98 code: Option<String>,
99 spans: Vec<SpanLabel>,
100}
101
102impl 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
164fn 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
202pub(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
265pub(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
273pub(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
293pub(super) fn error(message: String, entity: ClangEntity) {
294 log(Level::Error, message, entity)
295}
296
297/*
298pub(super) fn warn(message: String, entity: ClangEntity) {
299 log(Level::Warning, message, entity)
300}
301
302pub(super) fn note(message: String, entity: ClangEntity) {
303 log(Level::Note, message, entity)
304}
305
306pub(super) fn help(message: String, entity: ClangEntity) {
307 log(Level::Help, message, entity)
308}
309
310pub(super) fn bug(message: String, entity: ClangEntity) {
311 log(Level::Bug, message, entity)
312}
313*/