solar_interface/diagnostics/
builder.rs1use super::{
2 BugAbort, Diag, DiagCtxt, DiagId, DiagMsg, ErrorGuaranteed, ExplicitBug, FatalAbort, Level,
3 MultiSpan, Style,
4};
5use crate::Span;
6use solar_data_structures::Never;
7use std::{
8 fmt,
9 marker::PhantomData,
10 mem::ManuallyDrop,
11 ops::{Deref, DerefMut},
12 panic::Location,
13};
14
15pub trait EmissionGuarantee: Sized {
21 type EmitResult;
24
25 #[track_caller]
29 fn emit_producing_guarantee(db: &mut DiagBuilder<'_, Self>) -> Self::EmitResult;
30}
31
32impl EmissionGuarantee for ErrorGuaranteed {
33 type EmitResult = Self;
34
35 fn emit_producing_guarantee(db: &mut DiagBuilder<'_, Self>) -> Self::EmitResult {
36 let guar = db.emit_producing_error_guaranteed();
37
38 assert!(
42 db.diagnostic.is_error(),
43 "emitted non-error ({:?}) diagnostic from `DiagBuilder<ErrorGuaranteed>`",
44 db.diagnostic.level,
45 );
46
47 guar.unwrap_err()
48 }
49}
50
51impl EmissionGuarantee for () {
52 type EmitResult = Self;
53
54 fn emit_producing_guarantee(db: &mut DiagBuilder<'_, Self>) -> Self::EmitResult {
55 db.emit_producing_nothing();
56 }
57}
58
59impl EmissionGuarantee for BugAbort {
60 type EmitResult = Never;
61
62 fn emit_producing_guarantee(db: &mut DiagBuilder<'_, Self>) -> Self::EmitResult {
63 db.emit_producing_nothing();
64 std::panic::panic_any(ExplicitBug);
65 }
66}
67
68impl EmissionGuarantee for FatalAbort {
69 type EmitResult = Never;
70
71 fn emit_producing_guarantee(db: &mut DiagBuilder<'_, Self>) -> Self::EmitResult {
72 db.emit_producing_nothing();
73 std::panic::panic_any(Self);
74 }
75}
76
77#[must_use = "diagnostics must be emitted or cancelled"]
82pub struct DiagBuilder<'a, G: EmissionGuarantee> {
83 dcx: &'a DiagCtxt,
84
85 diagnostic: Box<Diag>,
90
91 _marker: PhantomData<G>,
92}
93
94impl<G: EmissionGuarantee> Clone for DiagBuilder<'_, G> {
95 #[inline]
96 fn clone(&self) -> Self {
97 Self { dcx: self.dcx, diagnostic: self.diagnostic.clone(), _marker: PhantomData }
98 }
99}
100
101impl<G: EmissionGuarantee> fmt::Debug for DiagBuilder<'_, G> {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 self.diagnostic.fmt(f)
104 }
105}
106
107impl<G: EmissionGuarantee> Deref for DiagBuilder<'_, G> {
108 type Target = Diag;
109
110 #[inline]
111 fn deref(&self) -> &Self::Target {
112 &self.diagnostic
113 }
114}
115
116impl<G: EmissionGuarantee> DerefMut for DiagBuilder<'_, G> {
117 #[inline]
118 fn deref_mut(&mut self) -> &mut Self::Target {
119 &mut self.diagnostic
120 }
121}
122
123impl<G: EmissionGuarantee> Drop for DiagBuilder<'_, G> {
124 #[track_caller]
125 fn drop(&mut self) {
126 if std::thread::panicking() {
127 return;
128 }
129
130 let _ = self.dcx.emit_diagnostic(Diag::new(
131 Level::Bug,
132 "the following error was constructed but not emitted",
133 ));
134 let _ = self.dcx.emit_diagnostic_without_consuming(&mut self.diagnostic);
135 panic!("error was constructed but not emitted");
136 }
137}
138
139impl<'a, G: EmissionGuarantee> DiagBuilder<'a, G> {
140 #[inline(never)]
142 #[track_caller]
143 pub fn new<M: Into<DiagMsg>>(dcx: &'a DiagCtxt, level: Level, msg: M) -> Self {
144 Self { dcx, diagnostic: Box::new(Diag::new(level, msg)), _marker: PhantomData }
145 }
146
147 #[inline]
149 pub fn dcx(&self) -> &DiagCtxt {
150 self.dcx
151 }
152
153 #[track_caller]
155 #[inline(never)]
156 pub fn emit(mut self) -> G::EmitResult {
157 if self.dcx.track_diagnostics() {
158 self.diagnostic.locations_note(Location::caller());
159 }
160 self.consume_no_panic(G::emit_producing_guarantee)
161 }
162
163 fn emit_producing_nothing(&mut self) {
164 let _ = self.emit_producing_error_guaranteed();
165 }
166
167 fn emit_producing_error_guaranteed(&mut self) -> Result<(), ErrorGuaranteed> {
168 self.dcx.emit_diagnostic_without_consuming(&mut self.diagnostic)
169 }
170
171 #[inline]
174 pub fn cancel(self) {
175 self.consume_no_panic(|_| {});
176 }
177
178 fn consume_no_panic<R>(self, f: impl FnOnce(&mut Self) -> R) -> R {
179 let mut this = ManuallyDrop::new(self);
180 let r = f(&mut *this);
181 unsafe { std::ptr::drop_in_place(&mut this.diagnostic) };
182 r
183 }
184}
185
186macro_rules! forward {
188 (
189 $(
190 $(#[$attrs:meta])*
191 $vis:vis fn $n:ident($($name:ident: $ty:ty),* $(,)?);
192 )*
193 ) => {
194 $(
195 $(#[$attrs])*
196 #[doc = concat!("See [`Diag::", stringify!($n), "()`].")]
197 #[inline(never)]
198 $vis fn $n(mut self, $($name: $ty),*) -> Self {
199 self.diagnostic.$n($($name),*);
200 self
201 }
202 )*
203 };
204}
205
206impl<G: EmissionGuarantee> DiagBuilder<'_, G> {
208 forward! {
209 pub fn span(span: impl Into<MultiSpan>);
210 pub fn code(code: impl Into<DiagId>);
211
212 pub fn span_label(span: Span, label: impl Into<DiagMsg>);
213 pub fn span_labels(spans: impl IntoIterator<Item = Span>, label: impl Into<DiagMsg>);
214
215 pub fn warn(msg: impl Into<DiagMsg>);
216 pub fn span_warn(span: impl Into<MultiSpan>, msg: impl Into<DiagMsg>);
217
218 pub fn note(msg: impl Into<DiagMsg>);
219 pub fn span_note(span: impl Into<MultiSpan>, msg: impl Into<DiagMsg>);
220 pub fn highlighted_note(messages: Vec<(impl Into<DiagMsg>, Style)>);
221 pub fn note_once(msg: impl Into<DiagMsg>);
222 pub fn span_note_once(span: impl Into<MultiSpan>, msg: impl Into<DiagMsg>);
223
224 pub fn help(msg: impl Into<DiagMsg>);
225 pub fn help_once(msg: impl Into<DiagMsg>);
226 pub fn highlighted_help(messages: Vec<(impl Into<DiagMsg>, Style)>);
227 pub fn span_help(span: impl Into<MultiSpan>, msg: impl Into<DiagMsg>);
228 }
229}