1 // Copyright (c) 2020 Google LLC All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4 
5 use {
6     proc_macro2::{Span, TokenStream},
7     quote::ToTokens,
8     std::cell::RefCell,
9 };
10 
11 /// A type for collecting procedural macro errors.
12 #[derive(Default)]
13 pub struct Errors {
14     errors: RefCell<Vec<syn::Error>>,
15 }
16 
17 /// Produce functions to expect particular literals in `syn::Expr`
18 macro_rules! expect_lit_fn {
19     ($(($fn_name:ident, $syn_type:ident, $variant:ident, $lit_name:literal),)*) => {
20         $(
21             pub fn $fn_name<'a>(&self, e: &'a syn::Expr) -> Option<&'a syn::$syn_type> {
22                 if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::$variant(inner), .. }) = e {
23                     Some(inner)
24                 } else {
25                     self.unexpected_lit($lit_name, e);
26                     None
27                 }
28             }
29         )*
30     }
31 }
32 
33 /// Produce functions to expect particular variants of `syn::Meta`
34 macro_rules! expect_meta_fn {
35     ($(($fn_name:ident, $syn_type:ident, $variant:ident, $meta_name:literal),)*) => {
36         $(
37             pub fn $fn_name<'a>(&self, meta: &'a syn::Meta) -> Option<&'a syn::$syn_type> {
38                 if let syn::Meta::$variant(inner) = meta {
39                     Some(inner)
40                 } else {
41                     self.unexpected_meta($meta_name, meta);
42                     None
43                 }
44             }
45         )*
46     }
47 }
48 
49 impl Errors {
50     /// Issue an error like:
51     ///
52     /// Duplicate foo attribute
53     /// First foo attribute here
duplicate_attrs( &self, attr_kind: &str, first: &impl syn::spanned::Spanned, second: &impl syn::spanned::Spanned, )54     pub fn duplicate_attrs(
55         &self,
56         attr_kind: &str,
57         first: &impl syn::spanned::Spanned,
58         second: &impl syn::spanned::Spanned,
59     ) {
60         self.duplicate_attrs_inner(attr_kind, first.span(), second.span())
61     }
62 
duplicate_attrs_inner(&self, attr_kind: &str, first: Span, second: Span)63     fn duplicate_attrs_inner(&self, attr_kind: &str, first: Span, second: Span) {
64         self.err_span(second, &["Duplicate ", attr_kind, " attribute"].concat());
65         self.err_span(first, &["First ", attr_kind, " attribute here"].concat());
66     }
67 
68     expect_lit_fn![
69         (expect_lit_str, LitStr, Str, "string"),
70         (expect_lit_char, LitChar, Char, "character"),
71         (expect_lit_int, LitInt, Int, "integer"),
72     ];
73 
74     expect_meta_fn![
75         (expect_meta_word, Path, Path, "path"),
76         (expect_meta_list, MetaList, List, "list"),
77         (expect_meta_name_value, MetaNameValue, NameValue, "name-value pair"),
78     ];
79 
unexpected_lit(&self, expected: &str, found: &syn::Expr)80     fn unexpected_lit(&self, expected: &str, found: &syn::Expr) {
81         fn lit_kind(lit: &syn::Lit) -> &'static str {
82             use syn::Lit::{Bool, Byte, ByteStr, Char, Float, Int, Str, Verbatim};
83             match lit {
84                 Str(_) => "string",
85                 ByteStr(_) => "bytestring",
86                 Byte(_) => "byte",
87                 Char(_) => "character",
88                 Int(_) => "integer",
89                 Float(_) => "float",
90                 Bool(_) => "boolean",
91                 Verbatim(_) => "unknown (possibly extra-large integer)",
92                 _ => "unknown literal kind",
93             }
94         }
95 
96         if let syn::Expr::Lit(syn::ExprLit { lit, .. }) = found {
97             self.err(
98                 found,
99                 &["Expected ", expected, " literal, found ", lit_kind(lit), " literal"].concat(),
100             )
101         } else {
102             self.err(
103                 found,
104                 &["Expected ", expected, " literal, found non-literal expression."].concat(),
105             )
106         }
107     }
108 
unexpected_meta(&self, expected: &str, found: &syn::Meta)109     fn unexpected_meta(&self, expected: &str, found: &syn::Meta) {
110         fn meta_kind(meta: &syn::Meta) -> &'static str {
111             use syn::Meta::{List, NameValue, Path};
112             match meta {
113                 Path(_) => "path",
114                 List(_) => "list",
115                 NameValue(_) => "name-value pair",
116             }
117         }
118 
119         self.err(
120             found,
121             &["Expected ", expected, " attribute, found ", meta_kind(found), " attribute"].concat(),
122         )
123     }
124 
125     /// Issue an error relating to a particular `Spanned` structure.
err(&self, spanned: &impl syn::spanned::Spanned, msg: &str)126     pub fn err(&self, spanned: &impl syn::spanned::Spanned, msg: &str) {
127         self.err_span(spanned.span(), msg);
128     }
129 
130     /// Issue an error relating to a particular `Span`.
err_span(&self, span: Span, msg: &str)131     pub fn err_span(&self, span: Span, msg: &str) {
132         self.push(syn::Error::new(span, msg));
133     }
134 
135     /// Issue an error spanning over the given syntax tree node.
err_span_tokens<T: ToTokens>(&self, tokens: T, msg: &str)136     pub fn err_span_tokens<T: ToTokens>(&self, tokens: T, msg: &str) {
137         self.push(syn::Error::new_spanned(tokens, msg));
138     }
139 
140     /// Push a `syn::Error` onto the list of errors to issue.
push(&self, err: syn::Error)141     pub fn push(&self, err: syn::Error) {
142         self.errors.borrow_mut().push(err);
143     }
144 
145     /// Convert a `syn::Result` to an `Option`, logging the error if present.
ok<T>(&self, r: syn::Result<T>) -> Option<T>146     pub fn ok<T>(&self, r: syn::Result<T>) -> Option<T> {
147         match r {
148             Ok(v) => Some(v),
149             Err(e) => {
150                 self.push(e);
151                 None
152             }
153         }
154     }
155 }
156 
157 impl ToTokens for Errors {
158     /// Convert the errors into tokens that, when emit, will cause
159     /// the user of the macro to receive compiler errors.
to_tokens(&self, tokens: &mut TokenStream)160     fn to_tokens(&self, tokens: &mut TokenStream) {
161         tokens.extend(self.errors.borrow().iter().map(|e| e.to_compile_error()));
162     }
163 }
164