solar_interface/diagnostics/
builder.rs1use super::{
2 Applicability, BugAbort, Diag, DiagCtxt, DiagId, DiagMsg, ErrorGuaranteed, ExplicitBug,
3 FatalAbort, Level, MultiSpan, Span, Style, SuggestionStyle,
4};
5use solar_data_structures::Never;
6use std::{
7 fmt,
8 marker::PhantomData,
9 mem::ManuallyDrop,
10 ops::{Deref, DerefMut},
11 panic::Location,
12};
13
14pub trait EmissionGuarantee: Sized {
20 type EmitResult;
23
24 #[track_caller]
28 fn emit_producing_guarantee(db: &mut DiagBuilder<'_, Self>) -> Self::EmitResult;
29}
30
31impl EmissionGuarantee for ErrorGuaranteed {
32 type EmitResult = Self;
33
34 fn emit_producing_guarantee(db: &mut DiagBuilder<'_, Self>) -> Self::EmitResult {
35 let guar = db.emit_producing_error_guaranteed();
36
37 assert!(
41 db.is_error(),
42 "emitted non-error ({:?}) diagnostic from `DiagBuilder<ErrorGuaranteed>`",
43 db.level,
44 );
45
46 guar.unwrap_err()
47 }
48}
49
50impl EmissionGuarantee for () {
51 type EmitResult = Self;
52
53 fn emit_producing_guarantee(db: &mut DiagBuilder<'_, Self>) -> Self::EmitResult {
54 db.emit_producing_nothing();
55 }
56}
57
58impl EmissionGuarantee for BugAbort {
59 type EmitResult = Never;
60
61 fn emit_producing_guarantee(db: &mut DiagBuilder<'_, Self>) -> Self::EmitResult {
62 db.emit_producing_nothing();
63 std::panic::panic_any(ExplicitBug);
64 }
65}
66
67impl EmissionGuarantee for FatalAbort {
68 type EmitResult = Never;
69
70 fn emit_producing_guarantee(db: &mut DiagBuilder<'_, Self>) -> Self::EmitResult {
71 db.emit_producing_nothing();
72 std::panic::panic_any(Self);
73 }
74}
75
76#[must_use = "diagnostics must be emitted or cancelled"]
81pub struct DiagBuilder<'a, G: EmissionGuarantee> {
82 diagnostic: Box<(Diag, &'a DiagCtxt)>,
87
88 _marker: PhantomData<G>,
89}
90
91impl<G: EmissionGuarantee> Clone for DiagBuilder<'_, G> {
92 #[inline]
93 fn clone(&self) -> Self {
94 Self { 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.0.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.0
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.0
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 (diag, dcx) = &mut *self.diagnostic;
128 let _ = dcx.emit_diagnostic(Diag::new(
129 Level::Bug,
130 "the following error was constructed but not emitted",
131 ));
132 let _ = dcx.emit_diagnostic_without_consuming(diag);
133 panic!("error was constructed but not emitted");
134 }
135}
136
137impl<'a, G: EmissionGuarantee> DiagBuilder<'a, G> {
138 #[inline(never)]
140 #[track_caller]
141 pub fn new<M: Into<DiagMsg>>(dcx: &'a DiagCtxt, level: Level, msg: M) -> Self {
142 Self { diagnostic: Box::new((Diag::new(level, msg), dcx)), _marker: PhantomData }
143 }
144
145 #[inline]
147 pub fn dcx(&self) -> &DiagCtxt {
148 self.diagnostic.1
149 }
150
151 #[track_caller]
153 #[inline(never)]
154 pub fn emit(mut self) -> G::EmitResult {
155 if self.dcx().track_diagnostics() {
156 self.locations_note(Location::caller());
157 }
158 self.consume_no_panic(G::emit_producing_guarantee)
159 }
160
161 fn emit_producing_nothing(&mut self) {
162 let _ = self.emit_producing_error_guaranteed();
163 }
164
165 fn emit_producing_error_guaranteed(&mut self) -> Result<(), ErrorGuaranteed> {
166 let (diag, dcx) = &mut *self.diagnostic;
167 dcx.emit_diagnostic_without_consuming(diag)
168 }
169
170 #[inline]
173 pub fn cancel(self) {
174 self.consume_no_panic(|_| {});
175 }
176
177 fn consume_no_panic<R>(self, f: impl FnOnce(&mut Self) -> R) -> R {
178 let mut this = ManuallyDrop::new(self);
179 let r = f(&mut *this);
180 unsafe { std::ptr::drop_in_place(&mut this.diagnostic) };
181 r
182 }
183}
184
185macro_rules! forward {
187 (
188 $(
189 $(#[$attrs:meta])*
190 $vis:vis fn $n:ident($($name:ident: $ty:ty),* $(,)?);
191 )*
192 ) => {
193 $(
194 $(#[$attrs])*
195 #[doc = concat!("See [`Diag::", stringify!($n), "()`].")]
196 #[inline(never)]
197 $vis fn $n(mut self, $($name: $ty),*) -> Self {
198 self.diagnostic.0.$n($($name),*);
199 self
200 }
201 )*
202 };
203}
204
205impl<G: EmissionGuarantee> DiagBuilder<'_, G> {
207 forward! {
208 pub fn span(span: impl Into<MultiSpan>);
209 pub fn code(code: impl Into<DiagId>);
210
211 pub fn span_label(span: Span, label: impl Into<DiagMsg>);
212 pub fn span_labels(spans: impl IntoIterator<Item = Span>, label: impl Into<DiagMsg>);
213
214 pub fn warn(msg: impl Into<DiagMsg>);
215 pub fn span_warn(span: impl Into<MultiSpan>, msg: impl Into<DiagMsg>);
216
217 pub fn note(msg: impl Into<DiagMsg>);
218 pub fn span_note(span: impl Into<MultiSpan>, msg: impl Into<DiagMsg>);
219 pub fn highlighted_note(messages: Vec<(impl Into<DiagMsg>, Style)>);
220 pub fn note_once(msg: impl Into<DiagMsg>);
221 pub fn span_note_once(span: impl Into<MultiSpan>, msg: impl Into<DiagMsg>);
222
223 pub fn help(msg: impl Into<DiagMsg>);
224 pub fn help_once(msg: impl Into<DiagMsg>);
225 pub fn highlighted_help(messages: Vec<(impl Into<DiagMsg>, Style)>);
226 pub fn span_help(span: impl Into<MultiSpan>, msg: impl Into<DiagMsg>);
227
228 pub fn span_suggestion(
229 span: Span,
230 msg: impl Into<DiagMsg>,
231 suggestion: impl Into<DiagMsg>,
232 applicability: Applicability,
233 );
234 pub fn span_suggestion_with_style(
235 span: Span,
236 msg: impl Into<DiagMsg>,
237 suggestion: impl Into<DiagMsg>,
238 applicability: Applicability,
239 style: SuggestionStyle
240 );
241 pub fn multipart_suggestion(
242 msg: impl Into<DiagMsg>,
243 substitutions: Vec<(Span, DiagMsg)>,
244 applicability: Applicability,
245 );
246 pub fn multipart_suggestion_with_style(
247 msg: impl Into<DiagMsg>,
248 substitutions: Vec<(Span, DiagMsg)>,
249 applicability: Applicability,
250 style: SuggestionStyle
251 );
252 }
253}