1use proc_macro2::*;
2use quote::{ToTokens, TokenStreamExt};
3use syn::parse::Error;
4
5/// Provide a Diagnostic with the given span and message
6#[macro_export]
7macro_rules! err_span {
8 ($span:expr, $($msg:tt)*) => (
9 $crate::Diagnostic::spanned_error(&$span, format!($($msg)*))
10 )
11}
12
13/// Immediately fail and return an Err, with the arguments passed to err_span!
14#[macro_export]
15macro_rules! bail_span {
16 ($($t:tt)*) => (
17 return Err(err_span!($($t)*).into())
18 )
19}
20
21/// A struct representing a diagnostic to emit to the end-user as an error.
22#[derive(Debug)]
23pub struct Diagnostic {
24 inner: Repr,
25}
26
27#[derive(Debug)]
28enum Repr {
29 Single {
30 text: String,
31 span: Option<(Span, Span)>,
32 },
33 SynError(Error),
34 Multi {
35 diagnostics: Vec<Diagnostic>,
36 },
37}
38
39impl Diagnostic {
40 /// Generate a `Diagnostic` from an informational message with no Span
41 pub fn error<T: Into<String>>(text: T) -> Diagnostic {
42 Diagnostic {
43 inner: Repr::Single {
44 text: text.into(),
45 span: None,
46 },
47 }
48 }
49
50 /// Generate a `Diagnostic` from a Span and an informational message
51 pub fn span_error<T: Into<String>>(span: Span, text: T) -> Diagnostic {
52 Diagnostic {
53 inner: Repr::Single {
54 text: text.into(),
55 span: Some((span, span)),
56 },
57 }
58 }
59
60 /// Generate a `Diagnostic` from the span of any tokenizable object and a message
61 pub fn spanned_error<T: Into<String>>(node: &dyn ToTokens, text: T) -> Diagnostic {
62 Diagnostic {
63 inner: Repr::Single {
64 text: text.into(),
65 span: extract_spans(node),
66 },
67 }
68 }
69
70 /// Attempt to generate a `Diagnostic` from a vector of other `Diagnostic` instances.
71 /// If the `Vec` is empty, returns `Ok(())`, otherwise returns the new `Diagnostic`
72 pub fn from_vec(diagnostics: Vec<Diagnostic>) -> Result<(), Diagnostic> {
73 if diagnostics.is_empty() {
74 Ok(())
75 } else {
76 Err(Diagnostic {
77 inner: Repr::Multi { diagnostics },
78 })
79 }
80 }
81
82 /// Immediately trigger a panic from this `Diagnostic`
83 #[allow(unconditional_recursion)]
84 pub fn panic(&self) -> ! {
85 match &self.inner {
86 Repr::Single { text, .. } => panic!("{}", text),
87 Repr::SynError(error) => panic!("{}", error),
88 Repr::Multi { diagnostics } => diagnostics[0].panic(),
89 }
90 }
91}
92
93impl From<Error> for Diagnostic {
94 fn from(err: Error) -> Diagnostic {
95 Diagnostic {
96 inner: Repr::SynError(err),
97 }
98 }
99}
100
101fn extract_spans(node: &dyn ToTokens) -> Option<(Span, Span)> {
102 let mut t: TokenStream = TokenStream::new();
103 node.to_tokens(&mut t);
104 let mut tokens: IntoIter = t.into_iter();
105 let start: Option = tokens.next().map(|t: TokenTree| t.span());
106 let end: Option = tokens.last().map(|t: TokenTree| t.span());
107 start.map(|start: Span| (start, end.unwrap_or(default:start)))
108}
109
110impl ToTokens for Diagnostic {
111 fn to_tokens(&self, dst: &mut TokenStream) {
112 match &self.inner {
113 Repr::Single { text, span } => {
114 let cs2 = (Span::call_site(), Span::call_site());
115 let (start, end) = span.unwrap_or(cs2);
116 dst.append(Ident::new("compile_error", start));
117 dst.append(Punct::new('!', Spacing::Alone));
118 let mut message = TokenStream::new();
119 message.append(Literal::string(text));
120 let mut group = Group::new(Delimiter::Brace, message);
121 group.set_span(end);
122 dst.append(group);
123 }
124 Repr::Multi { diagnostics } => {
125 for diagnostic in diagnostics {
126 diagnostic.to_tokens(dst);
127 }
128 }
129 Repr::SynError(err) => {
130 err.to_compile_error().to_tokens(dst);
131 }
132 }
133 }
134}
135

Provided by KDAB

Privacy Policy