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