• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 variants of `syn::Lit`
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, lit: &'a syn::Lit) -> Option<&'a syn::$syn_type> {
22                 if let syn::Lit::$variant(inner) = lit {
23                     Some(inner)
24                 } else {
25                     self.unexpected_lit($lit_name, lit);
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     /// Error on literals, expecting attribute syntax.
expect_nested_meta<'a>(&self, nm: &'a syn::NestedMeta) -> Option<&'a syn::Meta>69     pub fn expect_nested_meta<'a>(&self, nm: &'a syn::NestedMeta) -> Option<&'a syn::Meta> {
70         match nm {
71             syn::NestedMeta::Lit(l) => {
72                 self.err(l, "Unexpected literal");
73                 None
74             }
75             syn::NestedMeta::Meta(m) => Some(m),
76         }
77     }
78 
79     /// Error on attribute syntax, expecting literals
expect_nested_lit<'a>(&self, nm: &'a syn::NestedMeta) -> Option<&'a syn::Lit>80     pub fn expect_nested_lit<'a>(&self, nm: &'a syn::NestedMeta) -> Option<&'a syn::Lit> {
81         match nm {
82             syn::NestedMeta::Meta(m) => {
83                 self.err(m, "Expected literal");
84                 None
85             }
86             syn::NestedMeta::Lit(l) => Some(l),
87         }
88     }
89 
90     expect_lit_fn![
91         (expect_lit_str, LitStr, Str, "string"),
92         (expect_lit_char, LitChar, Char, "character"),
93         (expect_lit_int, LitInt, Int, "integer"),
94     ];
95 
96     expect_meta_fn![
97         (expect_meta_word, Path, Path, "path"),
98         (expect_meta_list, MetaList, List, "list"),
99         (expect_meta_name_value, MetaNameValue, NameValue, "name-value pair"),
100     ];
101 
unexpected_lit(&self, expected: &str, found: &syn::Lit)102     fn unexpected_lit(&self, expected: &str, found: &syn::Lit) {
103         fn lit_kind(lit: &syn::Lit) -> &'static str {
104             use syn::Lit::{Bool, Byte, ByteStr, Char, Float, Int, Str, Verbatim};
105             match lit {
106                 Str(_) => "string",
107                 ByteStr(_) => "bytestring",
108                 Byte(_) => "byte",
109                 Char(_) => "character",
110                 Int(_) => "integer",
111                 Float(_) => "float",
112                 Bool(_) => "boolean",
113                 Verbatim(_) => "unknown (possibly extra-large integer)",
114             }
115         }
116 
117         self.err(
118             found,
119             &["Expected ", expected, " literal, found ", lit_kind(found), " literal"].concat(),
120         )
121     }
122 
unexpected_meta(&self, expected: &str, found: &syn::Meta)123     fn unexpected_meta(&self, expected: &str, found: &syn::Meta) {
124         fn meta_kind(meta: &syn::Meta) -> &'static str {
125             use syn::Meta::{List, NameValue, Path};
126             match meta {
127                 Path(_) => "path",
128                 List(_) => "list",
129                 NameValue(_) => "name-value pair",
130             }
131         }
132 
133         self.err(
134             found,
135             &["Expected ", expected, " attribute, found ", meta_kind(found), " attribute"].concat(),
136         )
137     }
138 
139     /// Issue an error relating to a particular `Spanned` structure.
err(&self, spanned: &impl syn::spanned::Spanned, msg: &str)140     pub fn err(&self, spanned: &impl syn::spanned::Spanned, msg: &str) {
141         self.err_span(spanned.span(), msg);
142     }
143 
144     /// Issue an error relating to a particular `Span`.
err_span(&self, span: Span, msg: &str)145     pub fn err_span(&self, span: Span, msg: &str) {
146         self.push(syn::Error::new(span, msg));
147     }
148 
149     /// Push a `syn::Error` onto the list of errors to issue.
push(&self, err: syn::Error)150     pub fn push(&self, err: syn::Error) {
151         self.errors.borrow_mut().push(err);
152     }
153 }
154 
155 impl ToTokens for Errors {
156     /// Convert the errors into tokens that, when emit, will cause
157     /// the user of the macro to receive compiler errors.
to_tokens(&self, tokens: &mut TokenStream)158     fn to_tokens(&self, tokens: &mut TokenStream) {
159         tokens.extend(self.errors.borrow().iter().map(|e| e.to_compile_error()));
160     }
161 }
162