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