1use proc_macro2::{Span, TokenStream};
2
3use crate::SpanDiagnosticExt;
4use crate::line::Line;
5
6pub trait MultiSpan {
8 fn into_spans(self) -> Vec<Span>;
10}
11
12impl MultiSpan for Span {
13 fn into_spans(self) -> Vec<Span> { vec![self] }
14}
15
16impl MultiSpan for Vec<Span> {
17 fn into_spans(self) -> Vec<Span> { self }
18}
19
20impl<'a> MultiSpan for &'a [Span] {
21 fn into_spans(self) -> Vec<Span> {
22 self.to_vec()
23 }
24}
25
26#[non_exhaustive]
28#[derive(Copy, Clone, Debug, PartialEq, Eq)]
29pub enum Level {
30 Error,
32 Warning,
34 Note,
36 Help,
38}
39
40impl std::str::FromStr for Level {
41 type Err = ();
42 fn from_str(s: &str) -> Result<Self, Self::Err> {
43 if s.contains(Level::Error.as_str()) {
44 Ok(Level::Error)
45 } else if s.contains(Level::Warning.as_str()) {
46 Ok(Level::Warning)
47 } else if s.contains(Level::Note.as_str()) {
48 Ok(Level::Note)
49 } else if s.contains(Level::Help.as_str()) {
50 Ok(Level::Help)
51 } else {
52 Err(())
53 }
54 }
55}
56
57impl Level {
58 fn as_str(self) -> &'static str {
59 match self {
60 Level::Error => "error",
61 Level::Warning => "warning",
62 Level::Note => "note",
63 Level::Help => "help",
64 }
65 }
66}
67
68impl std::fmt::Display for Level {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 self.as_str().fmt(f)
71 }
72}
73
74#[derive(Clone, Debug)]
77pub struct Diagnostic {
78 level: Level,
79 message: String,
80 spans: Vec<Span>,
81 children: Vec<Diagnostic>
82}
83
84macro_rules! diagnostic_child_methods {
85 ($spanned:ident, $regular:ident, $level:expr) => (
86 pub fn $spanned<S, T>(self, spans: S, message: T) -> Diagnostic
90 where S: MultiSpan, T: Into<String>
91 {
92 self.spanned_child(spans, $level, message)
93 }
94
95 pub fn $regular<T: Into<String>>(self, message: T) -> Diagnostic {
98 self.child($level, message)
99 }
100 )
101}
102
103impl Diagnostic {
104 pub fn new<T: Into<String>>(level: Level, message: T) -> Diagnostic {
106 Diagnostic {
107 level,
108 message: message.into(),
109 spans: vec![],
110 children: vec![]
111 }
112 }
113
114 pub fn spanned<S, T>(spans: S, level: Level, message: T) -> Diagnostic
117 where S: MultiSpan, T: Into<String>
118 {
119 Diagnostic {
120 level,
121 message: message.into(),
122 spans: spans.into_spans(),
123 children: vec![]
124 }
125 }
126
127 pub fn spanned_child<S, T>(mut self, spans: S, level: Level, message: T) -> Diagnostic
130 where S: MultiSpan, T: Into<String>
131 {
132 self.children.push(Diagnostic::spanned(spans, level, message));
133 self
134 }
135
136 pub fn child<T: Into<String>>(mut self, level: Level, message: T) -> Diagnostic {
139 self.children.push(Diagnostic::new(level, message));
140 self
141 }
142
143 diagnostic_child_methods!(span_error, error, Level::Error);
144 diagnostic_child_methods!(span_warning, warning, Level::Warning);
145 diagnostic_child_methods!(span_note, note, Level::Note);
146 diagnostic_child_methods!(span_help, help, Level::Help);
147
148 pub fn children(&self) -> impl Iterator<Item=&Diagnostic> {
150 self.children.iter()
151 }
152
153 pub fn level(&self) -> Level {
155 self.level
156 }
157
158 fn stable_emit_as_tokens(self, item: bool) -> TokenStream {
159 let error: syn::parse::Error = self.into();
160 if item {
161 error.to_compile_error()
162 } else {
163 let compile_error_calls = error.into_iter().map(|e| {
164 let compile_error = e.to_compile_error();
165 quote::quote_spanned!(e.span() => #compile_error;)
166 });
167
168 quote::quote!({ #(#compile_error_calls)* })
169 }
170 }
171
172 #[cfg(not(nightly_diagnostics))]
174 fn emit_as_tokens(self, item: bool, _: TokenStream) -> TokenStream {
175 self.stable_emit_as_tokens(item)
176 }
177
178 #[cfg(nightly_diagnostics)]
180 fn emit_as_tokens(self, item: bool, default: TokenStream) -> TokenStream {
181 if !crate::nightly_works() {
182 return self.stable_emit_as_tokens(item);
183 }
184
185 proc_macro::Diagnostic::from(self).emit();
186 default
187 }
188
189 pub fn emit_as_item_tokens(self) -> TokenStream {
193 self.emit_as_tokens(true, TokenStream::new())
194 }
195
196 pub fn emit_as_item_tokens_or(self, default: TokenStream) -> TokenStream {
200 self.emit_as_tokens(true, default)
201 }
202
203 pub fn emit_as_expr_tokens(self) -> TokenStream {
207 self.emit_as_tokens(false, quote::quote!({}))
208 }
209
210 pub fn emit_as_expr_tokens_or(self, default: TokenStream) -> TokenStream {
214 self.emit_as_tokens(false, default)
215 }
216}
217
218impl From<Diagnostic> for syn::parse::Error {
219 fn from(diag: Diagnostic) -> syn::parse::Error {
220 fn diag_to_msg(diag: &Diagnostic) -> String {
221 let (spans, level, msg) = (&diag.spans, diag.level, &diag.message);
222 if spans.is_empty() {
223 Line::joined(level, msg).to_string()
224 } else {
225 if level == Level::Error {
226 return msg.into();
227 }
228
229 Line::new(level, msg).to_string()
230 }
231 }
232
233 fn diag_to_span(diag: &Diagnostic) -> Span {
234 diag.spans.get(0).cloned().unwrap_or_else(|| Span::call_site())
235 }
236
237 let mut msg = diag_to_msg(&diag);
238 let mut span = diag_to_span(&diag);
239 let mut error: Option<syn::Error> = None;
240 for child in diag.children {
241 if child.spans.is_empty() {
242 msg.push_str(&format!("\n{}", diag_to_msg(&child)));
244 } else {
245 let new_error = syn::parse::Error::new(span, &msg);
248 if let Some(ref mut error) = error {
249 error.combine(new_error);
250 } else {
251 error = Some(new_error);
252 }
253
254 span = diag_to_span(&child);
256 msg = diag_to_msg(&child);
257 }
258 }
259
260 if let Some(mut error) = error {
261 error.combine(syn::parse::Error::new(span, &msg));
262 error
263 } else {
264 syn::parse::Error::new(span, &msg)
265 }
266 }
267}
268
269impl From<syn::parse::Error> for Diagnostic {
270 fn from(error: syn::parse::Error) -> Diagnostic {
271 let mut diag: Option<Diagnostic> = None;
272 for e in &error {
273 for line in e.to_string().lines() {
274 if let Some(line) = Line::parse(line) {
275 if line.is_new() {
276 diag = diag.map(|d| d.spanned_child(e.span(), line.level, line.msg))
277 .or_else(|| Some(Diagnostic::spanned(e.span(), line.level, line.msg)));
278 } else {
279 diag = diag.map(|d| d.child(line.level, line.msg));
280 }
281 } else {
282 diag = diag.map(|d| d.span_error(e.span(), line))
283 .or_else(|| Some(e.span().error(line)));
284 }
285 }
286 }
287
288 diag.unwrap_or_else(|| error.span().error(error.to_string()))
289 }
290}
291
292#[cfg(nightly_diagnostics)]
293impl From<Diagnostic> for proc_macro::Diagnostic {
294 fn from(diag: Diagnostic) -> proc_macro::Diagnostic {
295 fn spans_to_proc_macro_spans(spans: Vec<Span>) -> Vec<proc_macro::Span> {
296 spans.into_iter()
297 .map(|s| s.unstable())
298 .collect::<Vec<proc_macro::Span>>()
299 }
300
301 let spans = spans_to_proc_macro_spans(diag.spans);
302
303 let level = match diag.level {
304 Level::Error => proc_macro::Level::Error,
305 Level::Warning => proc_macro::Level::Warning,
306 Level::Note => proc_macro::Level::Note,
307 Level::Help => proc_macro::Level::Help,
308 };
309
310 let mut proc_diag = proc_macro::Diagnostic::spanned(spans, level, diag.message);
311 for child in diag.children {
312 let spans = spans_to_proc_macro_spans(child.spans);
314 proc_diag = match child.level {
315 Level::Error => proc_diag.span_error(spans, child.message),
316 Level::Warning => proc_diag.span_warning(spans, child.message),
317 Level::Note => proc_diag.span_note(spans, child.message),
318 Level::Help => proc_diag.span_help(spans, child.message),
319 };
320 }
321
322 proc_diag
323 }
324}