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