1 | use crate::spanned::Spans; |
2 | |
3 | use proc_macro2::{ |
4 | Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream as TokenStream2, |
5 | TokenTree as TokenTree2, |
6 | }; |
7 | |
8 | use std::fmt::Display; |
9 | |
10 | #[derive (Debug, Clone)] |
11 | pub struct Error { |
12 | messages: Vec<CompileError>, |
13 | } |
14 | |
15 | #[derive (Debug, Clone)] |
16 | enum CompileError { |
17 | Basic { |
18 | start_span: Span, |
19 | end_span: Span, |
20 | msg: String, |
21 | }, |
22 | #[cfg (feature = "derive" )] |
23 | Syn(TokenStream2), |
24 | } |
25 | |
26 | impl Error { |
27 | pub fn new<T: Display>(span: Span, msg: T) -> Self { |
28 | Error { |
29 | messages: vec![CompileError::Basic { |
30 | start_span: span, |
31 | end_span: span, |
32 | msg: msg.to_string(), |
33 | }], |
34 | } |
35 | } |
36 | |
37 | pub fn spanned<T: Display>(spans: Spans, msg: T) -> Self { |
38 | Error { |
39 | messages: vec![CompileError::Basic { |
40 | start_span: spans.start, |
41 | end_span: spans.end, |
42 | msg: msg.to_string(), |
43 | }], |
44 | } |
45 | } |
46 | |
47 | pub fn to_compile_error(&self) -> TokenStream2 { |
48 | macro_rules! tokenstream{ |
49 | ($($tt:expr),* $(,)*) => ({ |
50 | let list: Vec<TokenTree2> = vec![ |
51 | $($tt.into(),)* |
52 | ]; |
53 | list.into_iter().collect::<TokenStream2>() |
54 | }) |
55 | } |
56 | |
57 | self.messages |
58 | .iter() |
59 | .map(|em| match em { |
60 | CompileError::Basic { |
61 | start_span, |
62 | end_span, |
63 | msg, |
64 | } => { |
65 | let ts = tokenstream![ |
66 | Ident::new("compile_error" , *start_span), |
67 | { |
68 | let mut this = Punct::new('!' , Spacing::Alone); |
69 | this.set_span(*start_span); |
70 | this |
71 | }, |
72 | { |
73 | let mut group = Group::new( |
74 | Delimiter::Parenthesis, |
75 | tokenstream![{ |
76 | let mut lit = Literal::string(msg); |
77 | lit.set_span(*end_span); |
78 | TokenTree2::Literal(lit) |
79 | }], |
80 | ); |
81 | group.set_span(*end_span); |
82 | group |
83 | }, |
84 | ]; |
85 | |
86 | // Still have no idea why the compile_error has to be wrapped in parentheses |
87 | // so that the spans point at the stuff between start_span and end_span. |
88 | let mut this = Group::new(Delimiter::Parenthesis, ts); |
89 | this.set_span(*end_span); |
90 | tokenstream![this] |
91 | } |
92 | #[cfg (feature = "derive" )] |
93 | CompileError::Syn(x) => x.clone(), |
94 | }) |
95 | .collect() |
96 | } |
97 | |
98 | pub fn combine(&mut self, another: Error) { |
99 | self.messages.extend(another.messages) |
100 | } |
101 | } |
102 | |
103 | #[cfg (feature = "derive" )] |
104 | impl From<syn::Error> for Error { |
105 | fn from(err: syn::Error) -> Self { |
106 | Self { |
107 | messages: vec![CompileError::Syn(err.to_compile_error())], |
108 | } |
109 | } |
110 | } |
111 | |