maybe_fatal/lib.rs
1//! Potentially fatal diagnostics and diagnostic handling for compilers.
2//!
3//! # Usage
4//!
5//! See the [examples] directory for some examples of how to use this crate.
6//!
7//! # Relationship to Other Crates
8//!
9//! This crate can be thought of as a high-level wrapper around the [ariadne] crate, as its core
10//! functionality is driven by it. It is also kind of a spiritual successor to the [rich-err] crate
11//! I wrote, but to be honest, I totally forgot that existed until I went to publish this one.
12//!
13//! There is also the [ariadnenum] crate, which fulfills a similar purpose to the
14//! [maybe-fatal-derive] crate in this repository. The primary difference is that
15//! [maybe-fatal-derive] was designed specifically for use in the context of this crate, whereas
16//! [ariadnenum] is meant for a workflow that is already using [ariadne] directly.
17//!
18//! One other crate worth mentioning is [wurm], which was similarly made for non-fatal error
19//! handling. I considered using that crate here, but it made more sense with how I had designed the
20//! [`Diagnostic`] type to just make my own stuff. Functionality relating to that can be found in
21//! the [`sink`] module.
22//!
23//! # Prerelease Status
24//!
25//! This is currently marked as being in beta, as I am still working on making sure the API is as
26//! ergonomic and flexible as possible. I would also like to add some more detailed documentation,
27//! unit tests, integration tests, and examples.
28//!
29//! [ariadnenum]: https://docs.rs/ariadnenum/latest/ariadnenum/
30//! [examples]: https://github.com/RosieTheGhostie/maybe-fatal/tree/main/examples
31//! [maybe-fatal-derive]: maybe_fatal_derive
32//! [rich-err]: https://docs.rs/rich-err/latest/rich_err/
33//! [wurm]: https://docs.rs/wurm/latest/wurm/
34
35#![cfg_attr(docsrs, feature(doc_cfg))]
36
37pub mod additional_attributes;
38pub mod code;
39pub mod prelude;
40pub mod sink;
41pub mod traits;
42
43pub use ariadne::{Config, Label};
44
45pub use classified::ClassifiedDiagnostic;
46pub use color_palette::ColorPalette;
47pub use context::Context;
48pub use severity::DiagnosticSeverity;
49
50mod classified;
51mod color_palette;
52mod context;
53mod severity;
54
55use core::borrow::Borrow;
56
57use code::DiagnosticCode;
58use traits::DiagnosticGroup;
59
60/// A contextualized message meant to assist the user in diagnosing and resolving issues.
61///
62/// This is intended for use in things like parsers and compilers, but they can be used in any
63/// text-based input processing.
64///
65/// Diagnostics are neutral by default; that is, they don't have an assigned
66/// [severity](DiagnosticSeverity). To assign a severity, use the [`classify`](Self::classify)
67/// method.
68pub struct Diagnostic<S, D = code::DefaultDiscriminant> {
69 /// The code identifying this kind of diagnostic.
70 ///
71 /// Diagnostics with identical codes are not necessarily equivalent. They may refer to different
72 /// spans and have different contextual information.
73 pub code: DiagnosticCode<D>,
74
75 /// The span this diagnostic refers to.
76 pub span: S,
77
78 /// A function that dynamically builds the diagnostic message.
79 message: Box<dyn FnOnce() -> String>,
80
81 /// Any contextual information that may accompany the message.
82 pub context_info: Vec<Context<S>>,
83}
84
85impl<S, D> Diagnostic<S, D> {
86 /// Constructs a new [`Diagnostic`] from the provided member of a [`DiagnosticGroup`].
87 pub fn new<T>(group_member: impl Borrow<T>, span: S) -> Self
88 where
89 T: DiagnosticGroup<D> + ?Sized,
90 {
91 let group_member = group_member.borrow();
92 Self {
93 code: group_member.diagnostic_code(),
94 span,
95 message: group_member.message(),
96 context_info: Vec::new(),
97 }
98 }
99
100 /// Labels a subspan of this diagnostic.
101 ///
102 /// See the documentation of [`Label`] for more details.
103 pub fn label(&mut self, label: Label<S>) -> &mut Self {
104 self.context_info.push(Context::Label(label));
105 self
106 }
107
108 /// Sequentially labels several subspans of this diagnostic.
109 ///
110 /// See the documentation of [`Label`] for more details.
111 pub fn labels(&mut self, labels: impl IntoIterator<Item = Label<S>>) -> &mut Self {
112 self.context_info
113 .extend(labels.into_iter().map(Context::Label));
114 self
115 }
116
117 /// Adds a note to this diagnostic.
118 pub fn note(&mut self, note: impl ToString) -> &mut Self {
119 self.context_info.push(Context::new_note(note));
120 self
121 }
122
123 /// Adds a help message to this diagnostic.
124 pub fn help(&mut self, help: impl ToString) -> &mut Self {
125 self.context_info.push(Context::new_help(help));
126 self
127 }
128
129 /// Classifies this diagnostic under the given severity.
130 pub const fn classify(self, severity: DiagnosticSeverity) -> ClassifiedDiagnostic<S, D> {
131 ClassifiedDiagnostic {
132 inner: self,
133 severity,
134 }
135 }
136
137 /// Reports this diagnostic using the given severity and configuration.
138 ///
139 /// See the [`ariadne`] documentation for more details.
140 pub fn report_with<C>(
141 self,
142 severity: DiagnosticSeverity,
143 config: Config,
144 cache: C,
145 ) -> std::io::Result<()>
146 where
147 S: ariadne::Span,
148 D: code::Discriminant,
149 C: ariadne::Cache<S::SourceId>,
150 {
151 let mut builder = ariadne::Report::build(severity.into(), self.span)
152 .with_config(config)
153 .with_code(self.code)
154 .with_message((self.message)());
155
156 for context in self.context_info {
157 context.add_to_report_builder(&mut builder);
158 }
159
160 builder.finish().eprint(cache)
161 }
162}
163
164#[cfg(any(docsrs, feature = "derive"))]
165mod macros {
166 #[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
167 pub use maybe_fatal_derive::*;
168}
169
170macro_rules! document_macro_reexports {
171 [$($derive_macro:ident),* $(,)?] => {
172 $(
173 #[cfg(any(docsrs, feature = "derive"))]
174 #[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
175 pub use maybe_fatal_derive::$derive_macro;
176 )*
177 };
178}
179
180document_macro_reexports![
181 Diagnose,
182 DiagnosticGroup,
183 DiagnosticInfoWrapper,
184 PartialDiagnose,
185];