Skip to main content

plc_diagnostics/reporter/
clang.rs

1use codespan_reporting::files::{Files, Location, SimpleFile, SimpleFiles};
2
3use crate::diagnostics::Severity;
4
5use super::{DiagnosticReporter, ResolvedDiagnostics};
6
7/// A reporter that reports diagnostics in clang format. Specifically the messages have the following
8/// form `<filename>:<range>: <severity>: <message>`.
9pub struct ClangFormatDiagnosticReporter {
10    files: SimpleFiles<String, String>,
11}
12
13impl ClangFormatDiagnosticReporter {
14    fn new() -> Self {
15        ClangFormatDiagnosticReporter { files: SimpleFiles::new() }
16    }
17}
18
19impl Default for ClangFormatDiagnosticReporter {
20    fn default() -> Self {
21        ClangFormatDiagnosticReporter::new()
22    }
23}
24
25impl DiagnosticReporter for ClangFormatDiagnosticReporter {
26    fn report(&mut self, diagnostics: &[ResolvedDiagnostics]) {
27        for diagnostic in diagnostics.iter().filter(|it| it.severity > Severity::Ignore) {
28            let file_id = diagnostic.main_location.file_handle;
29            let location = &diagnostic.main_location;
30
31            let file = self.files.get(file_id).ok();
32            let start =
33                self.files.location(file_id, location.span.to_range().map(|it| it.start).unwrap_or(0)).ok();
34            let end =
35                self.files.location(file_id, location.span.to_range().map(|it| it.end).unwrap_or(0)).ok();
36
37            let res = self.build_diagnostic_msg(
38                file,
39                start.as_ref(),
40                end.as_ref(),
41                &diagnostic.code,
42                &diagnostic.severity,
43                &diagnostic.message,
44            );
45
46            eprintln!("{res}");
47        }
48    }
49    fn register(&mut self, path: String, src: String) -> usize {
50        self.files.add(path, src)
51    }
52}
53
54impl ClangFormatDiagnosticReporter {
55    /// returns diagnostic message in clang format
56    /// file-name:{range}: severity: message
57    /// optional parameters that are none will not be included
58    pub(crate) fn build_diagnostic_msg(
59        &self,
60        file: Option<&SimpleFile<String, String>>,
61        start: Option<&Location>,
62        end: Option<&Location>,
63        code: &str,
64        severity: &Severity,
65        msg: &str,
66    ) -> String {
67        let mut str = String::new();
68        // file name
69        if let Some(f) = file {
70            str.push_str(format!("{}:", f.name().as_str()).as_str());
71            // range
72            if let Some(s) = start {
73                if let Some(e) = end {
74                    // if start and end are equal there is no need to show the range
75                    if s.eq(e) {
76                        str.push_str(format!("{}:{}: ", s.line_number, s.column_number).as_str());
77                    } else {
78                        str.push_str(
79                            format!(
80                                "{}:{}:{{{}:{}-{}:{}}}: ",
81                                s.line_number,
82                                s.column_number,
83                                s.line_number,
84                                s.column_number,
85                                e.line_number,
86                                e.column_number
87                            )
88                            .as_str(),
89                        );
90                    }
91                }
92            } else {
93                str.push(' ');
94            }
95        }
96        // severity
97        str.push_str(format!("{severity}[{code}]: ").as_str());
98        // msg
99        str.push_str(msg);
100
101        str
102    }
103}