blob: a5b69e6d9d542adb3b89d237790a60241747b430 [file] [log] [blame]
Andrew Walbrandc8aa4b2022-01-13 12:20:07 +00001// 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
5use {
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)]
13pub struct Errors {
14 errors: RefCell<Vec<syn::Error>>,
15}
16
17/// Produce functions to expect particular variants of `syn::Lit`
18macro_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`
34macro_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
49impl Errors {
50 /// Issue an error like:
51 ///
52 /// Duplicate foo attribute
53 /// First foo attribute here
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
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.
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
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
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
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.
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`.
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.
150 pub fn push(&self, err: syn::Error) {
151 self.errors.borrow_mut().push(err);
152 }
153}
154
155impl ToTokens for Errors {
156 /// Convert the errors into tokens that, when emit, will cause
157 /// the user of the macro to receive compiler errors.
158 fn to_tokens(&self, tokens: &mut TokenStream) {
159 tokens.extend(self.errors.borrow().iter().map(|e| e.to_compile_error()));
160 }
161}