microcad_lang/diag/
diag_handler.rs

1// Copyright © 2024-2025 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4use crate::{diag::*, resolve::*};
5
6/// Handler for diagnostics.
7#[derive(Default)]
8pub struct DiagHandler {
9    /// The list of diagnostics per source file.
10    pub diag_list: DiagList,
11    /// The number of overall errors in the evaluation process.
12    error_count: u32,
13    /// The number of overall errors in the evaluation process.
14    warning_count: u32,
15    /// The maximum number of collected errors until abort
16    /// (`0` means unlimited number of errors).
17    error_limit: Option<u32>,
18    /// `true` after the first time error limit was reached
19    error_limit_reached: bool,
20    /// Treat warnings as errors if `true`.
21    warnings_as_errors: bool,
22    /// Line offset for error and warning messages.
23    line_offset: usize,
24}
25
26/// Handler for diagnostics.
27impl DiagHandler {
28    /// Create new diag handler.
29    pub fn new(line_offset: usize) -> Self {
30        Self {
31            line_offset,
32            ..Default::default()
33        }
34    }
35
36    /// Pretty print all errors of all files.
37    pub fn pretty_print(
38        &self,
39        f: &mut dyn std::fmt::Write,
40        source_by_hash: &impl GetSourceByHash,
41    ) -> std::fmt::Result {
42        self.diag_list
43            .pretty_print(f, source_by_hash, self.line_offset)
44    }
45
46    /// Return overall number of occurred errors.
47    pub fn warning_count(&self) -> u32 {
48        self.warning_count
49    }
50
51    /// Return overall number of occurred errors.
52    pub fn error_count(&self) -> u32 {
53        self.error_count
54    }
55
56    /// return lines with errors
57    pub fn error_lines(&self) -> std::collections::HashSet<usize> {
58        self.diag_list
59            .iter()
60            .filter_map(|d| {
61                if d.level() == Level::Error {
62                    d.line().map(|line| line + self.line_offset)
63                } else {
64                    None
65                }
66            })
67            .collect()
68    }
69
70    /// return lines with warnings
71    pub fn warning_lines(&self) -> std::collections::HashSet<usize> {
72        self.diag_list
73            .iter()
74            .filter_map(|d| {
75                if d.level() == Level::Warning {
76                    d.line().map(|line| line + self.line_offset)
77                } else {
78                    None
79                }
80            })
81            .collect()
82    }
83
84    /// Clear all errors and warnings
85    pub fn clear(&mut self) {
86        self.diag_list.clear();
87        self.error_count = 0;
88        self.warning_count = 0;
89    }
90}
91
92impl PushDiag for DiagHandler {
93    fn push_diag(&mut self, diag: Diagnostic) -> DiagResult<()> {
94        if let Some(error_limit) = self.error_limit {
95            if self.error_count >= error_limit && !self.error_limit_reached {
96                self.error(
97                    &SrcRef(None),
98                    Box::new(DiagError::ErrorLimitReached(error_limit)),
99                )?;
100                self.error_limit_reached = true;
101            }
102            return Err(DiagError::ErrorLimitReached(error_limit));
103        }
104
105        match &diag {
106            Diagnostic::Error(_) => {
107                self.error_count += 1;
108            }
109            Diagnostic::Warning(_) => {
110                if self.warnings_as_errors {
111                    self.error_count += 1;
112                } else {
113                    self.warning_count += 1;
114                }
115            }
116            _ => (),
117        }
118
119        self.diag_list.push_diag(diag)
120    }
121}