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 let file = if proc_macro::is_available() {
169 Some(span.unwrap().file())
170 } else {
171 None
172 };
173
174 Self {
175 file,
176 line: span.start().line,
177 column: span.start().column,
178 }
179 }
180}
181impl std::fmt::Display for SerdeSpan {
182 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183 write!(
184 f,
185 "{}:{}:{}",
186 self.file
187 .as_ref()
188 .map(make_source_path_relative)
189 .map(|path| path.display().to_string())
190 .as_deref()
191 .unwrap_or("unknown"),
192 self.line,
193 self.column
194 )
195 }
196}
197
198pub struct Diagnostics<S = Span> {
200 diagnostics: Vec<Diagnostic<S>>,
201}
202
203impl<S> Default for Diagnostics<S> {
204 fn default() -> Self {
205 Self::new()
206 }
207}
208
209impl<S> Diagnostics<S> {
210 pub fn new() -> Self {
212 Self {
213 diagnostics: Vec::new(),
214 }
215 }
216
217 pub fn retain_level(&mut self, level: Level) {
219 self.diagnostics.retain(|d| d.level <= level);
220 }
221
222 pub fn has_error(&self) -> bool {
224 self.diagnostics.iter().any(|d| Level::Error == d.level)
225 }
226
227 pub fn push(&mut self, diagnostic: Diagnostic<S>) {
229 self.diagnostics.push(diagnostic);
230 }
231
232 pub fn iter(&self) -> std::slice::Iter<'_, Diagnostic<S>> {
234 self.diagnostics.iter()
235 }
236
237 pub fn len(&self) -> usize {
239 self.diagnostics.len()
240 }
241
242 pub fn is_empty(&self) -> bool {
244 self.diagnostics.is_empty()
245 }
246}
247
248impl Diagnostics {
249 pub fn try_emit_all(&self) -> Result<(), TokenStream> {
252 if let Some(tokens) = self
253 .diagnostics
254 .iter()
255 .filter_map(|diag| diag.try_emit().err())
256 .reduce(|mut tokens, next| {
257 tokens.extend(next);
258 tokens
259 })
260 {
261 Err(tokens)
262 } else {
263 Ok(())
264 }
265 }
266}
267
268impl<S> Extend<Diagnostic<S>> for Diagnostics<S> {
269 fn extend<T: IntoIterator<Item = Diagnostic<S>>>(&mut self, iter: T) {
270 self.diagnostics.extend(iter);
271 }
272}
273
274impl<S> std::fmt::Debug for Diagnostics<S>
275where
276 Diagnostic<S>: std::fmt::Display,
277{
278 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
279 if self.diagnostics.is_empty() {
280 write!(f, "Diagnostics (empty)")?;
281 } else {
282 write!(f, "Diagnostics (")?;
283 let groups = self.diagnostics.iter().into_group_map_by(|d| d.level);
284 for (level, count) in
285 Level::iter().filter_map(|level| groups.get(&level).map(|vec| (level, vec.len())))
286 {
287 write!(f, "{level:?}: {count}, ")?;
288 }
289 writeln!(f, "):")?;
290 for diagnostic in Level::iter()
291 .filter_map(|level| groups.get(&level))
292 .flatten()
293 {
294 writeln!(f, "{diagnostic}")?;
295 }
296 }
297 Ok(())
298 }
299}
300
301impl<S> FromIterator<Diagnostic<S>> for Diagnostics<S> {
302 fn from_iter<T: IntoIterator<Item = Diagnostic<S>>>(iter: T) -> Self {
303 Self {
304 diagnostics: Vec::from_iter(iter),
305 }
306 }
307}
308
309impl<S> IntoIterator for Diagnostics<S> {
310 type Item = Diagnostic<S>;
311 type IntoIter = std::vec::IntoIter<Self::Item>;
312
313 fn into_iter(self) -> Self::IntoIter {
314 self.diagnostics.into_iter()
315 }
316}