const_format_proc_macros/
error.rs

1use crate::spanned::Spans;
2
3use proc_macro2::{
4    Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream as TokenStream2,
5    TokenTree as TokenTree2,
6};
7
8use std::fmt::Display;
9
10#[derive(Debug, Clone)]
11pub struct Error {
12    messages: Vec<CompileError>,
13}
14
15#[derive(Debug, Clone)]
16enum CompileError {
17    Basic {
18        start_span: Span,
19        end_span: Span,
20        msg: String,
21    },
22    #[cfg(feature = "derive")]
23    Syn(TokenStream2),
24}
25
26impl 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")]
104impl 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}