1extern crate proc_macro;
4
5use std::hash::{Hash, Hasher};
6
7use itertools::Itertools;
8use proc_macro2::{Ident, Literal, Span, TokenStream};
9use quote::quote_spanned;
10use serde::{Deserialize, Serialize};
11
12use crate::pretty_span::{PrettySpan, make_source_path_relative};
13
14#[non_exhaustive]
16#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Serialize, Deserialize)]
17pub enum Level {
18 Error,
22 Warning,
26 Note,
30 Help,
34}
35impl Level {
36 pub fn iter() -> std::array::IntoIter<Self, 4> {
38 [Self::Error, Self::Warning, Self::Note, Self::Help].into_iter()
39 }
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct Diagnostic<S = Span> {
49 pub span: S,
51 pub level: Level,
53 pub message: String,
55}
56impl Diagnostic {
57 pub fn spanned(span: Span, level: Level, message: impl Into<String>) -> Self {
59 let message = message.into();
60 Self {
61 span,
62 level,
63 message,
64 }
65 }
66
67 pub fn try_emit(&self) -> Result<(), TokenStream> {
70 #[cfg(nightly)]
71 if proc_macro::is_available() {
72 let pm_diag = match self.level {
73 Level::Error => self.span.unwrap().error(&*self.message),
74 Level::Warning => self.span.unwrap().warning(&*self.message),
75 Level::Note => self.span.unwrap().note(&*self.message),
76 Level::Help => self.span.unwrap().help(&*self.message),
77 };
78 pm_diag.emit();
79 return Ok(());
80 }
81 Err(self.to_tokens())
82 }
83
84 pub fn to_tokens(&self) -> TokenStream {
87 let msg_lit: Literal = Literal::string(&self.message);
88 let unique_ident = {
89 let mut hasher = std::collections::hash_map::DefaultHasher::new();
90 self.level.hash(&mut hasher);
91 self.message.hash(&mut hasher);
92 let hash = hasher.finish();
93 Ident::new(&format!("diagnostic_{}", hash), self.span)
94 };
95
96 if Level::Error == self.level {
97 quote_spanned! {self.span=>
98 {
99 ::core::compile_error!(#msg_lit);
100 }
101 }
102 } else {
103 let level_ident = Ident::new(&format!("{:?}", self.level), self.span);
105 quote_spanned! {self.span=>
106 {
107 #[allow(dead_code, non_snake_case)]
108 fn #unique_ident() {
109 #[deprecated = #msg_lit]
110 struct #level_ident {}
111 #[warn(deprecated)]
112 #level_ident {};
113 }
114 }
115 }
116 }
117 }
118
119 pub fn to_serde(&self) -> Diagnostic<SerdeSpan> {
123 let Self {
124 span,
125 level,
126 message,
127 } = self;
128 Diagnostic {
129 span: (*span).into(),
130 level: *level,
131 message: message.clone(),
132 }
133 }
134}
135impl From<syn::Error> for Diagnostic {
136 fn from(value: syn::Error) -> Self {
137 Self::spanned(value.span(), Level::Error, value.to_string())
138 }
139}
140impl std::fmt::Display for Diagnostic {
141 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142 writeln!(f, "{:?}: {}", self.level, self.message)?;
143 write!(f, " --> {}", PrettySpan(self.span))?;
144 Ok(())
145 }
146}
147impl std::fmt::Display for Diagnostic<SerdeSpan> {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 writeln!(f, "{:?}: {}", self.level, self.message)?;
150 write!(f, " --> {}", self.span)?;
151 Ok(())
152 }
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct SerdeSpan {
159 pub file: Option<String>,
161 pub line: usize,
163 pub column: usize,
165}
166impl From<Span> for SerdeSpan {
167 fn from(span: Span) -> Self {
168 #[cfg_attr(
169 not(nightly),
170 expect(unused_labels, reason = "conditional compilation")
171 )]
172 let file = 'a: {
173 #[cfg(nightly)]
174 if proc_macro::is_available() {
175 break 'a Some(span.unwrap().file());
176 }
177
178 None
179 };
180
181 Self {
182 file,
183 line: span.start().line,
184 column: span.start().column,
185 }
186 }
187}
188impl std::fmt::Display for SerdeSpan {
189 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190 write!(
191 f,
192 "{}:{}:{}",
193 self.file
194 .as_ref()
195 .map(make_source_path_relative)
196 .map(|path| path.display().to_string())
197 .as_deref()
198 .unwrap_or("unknown"),
199 self.line,
200 self.column
201 )
202 }
203}
204
205pub struct Diagnostics<S = Span> {
207 diagnostics: Vec<Diagnostic<S>>,
208}
209
210impl<S> Default for Diagnostics<S> {
211 fn default() -> Self {
212 Self::new()
213 }
214}
215
216impl<S> Diagnostics<S> {
217 pub fn new() -> Self {
219 Self {
220 diagnostics: Vec::new(),
221 }
222 }
223
224 pub fn retain_level(&mut self, level: Level) {
226 self.diagnostics.retain(|d| d.level <= level);
227 }
228
229 pub fn has_error(&self) -> bool {
231 self.diagnostics.iter().any(|d| Level::Error == d.level)
232 }
233
234 pub fn push(&mut self, diagnostic: Diagnostic<S>) {
236 self.diagnostics.push(diagnostic);
237 }
238
239 pub fn iter(&self) -> std::slice::Iter<'_, Diagnostic<S>> {
241 self.diagnostics.iter()
242 }
243
244 pub fn len(&self) -> usize {
246 self.diagnostics.len()
247 }
248
249 pub fn is_empty(&self) -> bool {
251 self.diagnostics.is_empty()
252 }
253}
254
255impl Diagnostics {
256 pub fn try_emit_all(&self) -> Result<(), TokenStream> {
259 if let Some(tokens) = self
260 .diagnostics
261 .iter()
262 .filter_map(|diag| diag.try_emit().err())
263 .reduce(|mut tokens, next| {
264 tokens.extend(next);
265 tokens
266 })
267 {
268 Err(tokens)
269 } else {
270 Ok(())
271 }
272 }
273}
274
275impl<S> Extend<Diagnostic<S>> for Diagnostics<S> {
276 fn extend<T: IntoIterator<Item = Diagnostic<S>>>(&mut self, iter: T) {
277 self.diagnostics.extend(iter);
278 }
279}
280
281impl<S> std::fmt::Debug for Diagnostics<S>
282where
283 Diagnostic<S>: std::fmt::Display,
284{
285 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
286 if self.diagnostics.is_empty() {
287 write!(f, "Diagnostics (empty)")?;
288 } else {
289 write!(f, "Diagnostics (")?;
290 let groups = self.diagnostics.iter().into_group_map_by(|d| d.level);
291 for (level, count) in
292 Level::iter().filter_map(|level| groups.get(&level).map(|vec| (level, vec.len())))
293 {
294 write!(f, "{level:?}: {count}, ")?;
295 }
296 writeln!(f, "):")?;
297 for diagnostic in Level::iter()
298 .filter_map(|level| groups.get(&level))
299 .flatten()
300 {
301 writeln!(f, "{diagnostic}")?;
302 }
303 }
304 Ok(())
305 }
306}
307
308impl<S> FromIterator<Diagnostic<S>> for Diagnostics<S> {
309 fn from_iter<T: IntoIterator<Item = Diagnostic<S>>>(iter: T) -> Self {
310 Self {
311 diagnostics: Vec::from_iter(iter),
312 }
313 }
314}
315
316impl<S> IntoIterator for Diagnostics<S> {
317 type Item = Diagnostic<S>;
318 type IntoIter = std::vec::IntoIter<Self::Item>;
319
320 fn into_iter(self) -> Self::IntoIter {
321 self.diagnostics.into_iter()
322 }
323}