1 use proc_macro2::{Ident, Span};
2 use syn;
3 use syn::fold::Fold;
4 use syn::spanned::Spanned;
5
6 use resolved_at_shim::*;
7 use util::*;
8
9 pub struct MetaItem {
10 meta: syn::Meta,
11 }
12
path_to_string(path: &syn::Path) -> String13 pub(crate) fn path_to_string(path: &syn::Path) -> String {
14 path.segments
15 .iter()
16 .map(|s| s.ident.to_string())
17 .collect::<Vec<String>>()
18 .join("::")
19 }
20
21 impl MetaItem {
all_with_name(attrs: &[syn::Attribute], name: &str) -> Vec<Self>22 pub fn all_with_name(attrs: &[syn::Attribute], name: &str) -> Vec<Self> {
23 attrs
24 .iter()
25 .filter_map(|attr| {
26 attr.parse_meta()
27 .ok()
28 .map(|m| FixSpan(attr.pound_token.spans[0]).fold_meta(m))
29 })
30 .filter(|m| m.path().is_ident(name))
31 .map(|meta| Self { meta })
32 .collect()
33 }
34
with_name(attrs: &[syn::Attribute], name: &str) -> Option<Self>35 pub fn with_name(attrs: &[syn::Attribute], name: &str) -> Option<Self> {
36 Self::all_with_name(attrs, name).pop()
37 }
38
empty(name: &str) -> Self39 pub fn empty(name: &str) -> Self {
40 Self {
41 meta: syn::Meta::List(syn::MetaList {
42 path: syn::Path::from(Ident::new(name, Span::call_site())),
43 paren_token: Default::default(),
44 nested: Default::default(),
45 }),
46 }
47 }
48
nested_item(&self, name: &str) -> Result<Option<Self>, Diagnostic>49 pub fn nested_item(&self, name: &str) -> Result<Option<Self>, Diagnostic> {
50 self.nested()
51 .map(|mut i| i.find(|n| n.name().is_ident(name)))
52 }
53
required_nested_item(&self, name: &str) -> Result<Self, Diagnostic>54 pub fn required_nested_item(&self, name: &str) -> Result<Self, Diagnostic> {
55 self.nested_item(name)?.ok_or_else(|| {
56 self.span()
57 .error(format!("Missing required option `{}`", name))
58 })
59 }
60
expect_bool_value(&self) -> bool61 pub fn expect_bool_value(&self) -> bool {
62 match self.str_value().as_ref().map(|s| s.as_str()) {
63 Ok("true") => true,
64 Ok("false") => false,
65 _ => {
66 self.span()
67 .error(format!(
68 "`{0}` must be in the form `{0} = \"true\"`",
69 path_to_string(&self.name())
70 ))
71 .emit();
72 false
73 }
74 }
75 }
76
expect_ident_value(&self) -> syn::Ident77 pub fn expect_ident_value(&self) -> syn::Ident {
78 self.ident_value().unwrap_or_else(|e| {
79 e.emit();
80 self.name().segments.first().unwrap().ident.clone()
81 })
82 }
83
ident_value(&self) -> Result<syn::Ident, Diagnostic>84 pub fn ident_value(&self) -> Result<syn::Ident, Diagnostic> {
85 let maybe_attr = self.nested().ok().and_then(|mut n| n.nth(0));
86 let maybe_path = maybe_attr.as_ref().and_then(|m| m.path().ok());
87 match maybe_path {
88 Some(x) => {
89 self.span()
90 .warning(format!(
91 "The form `{0}(value)` is deprecated. Use `{0} = \"value\"` instead",
92 path_to_string(&self.name()),
93 ))
94 .emit();
95 Ok(x.segments.first().unwrap().ident.clone())
96 }
97 _ => Ok(syn::Ident::new(
98 &self.str_value()?,
99 self.value_span().resolved_at(Span::call_site()),
100 )),
101 }
102 }
103
expect_path(&self) -> syn::Path104 pub fn expect_path(&self) -> syn::Path {
105 self.path().unwrap_or_else(|e| {
106 e.emit();
107 self.name()
108 })
109 }
110
path(&self) -> Result<syn::Path, Diagnostic>111 pub fn path(&self) -> Result<syn::Path, Diagnostic> {
112 use syn::Meta::*;
113
114 match self.meta {
115 Path(ref x) => Ok(x.clone()),
116 _ => {
117 let meta = &self.meta;
118 Err(self.span().error(format!(
119 "Expected `{}` found `{}`",
120 path_to_string(&self.name()),
121 quote!(#meta)
122 )))
123 }
124 }
125 }
126
nested(&self) -> Result<Nested, Diagnostic>127 pub fn nested(&self) -> Result<Nested, Diagnostic> {
128 use syn::Meta::*;
129
130 match self.meta {
131 List(ref list) => Ok(Nested(list.nested.iter())),
132 _ => Err(self.span().error(format!(
133 "`{0}` must be in the form `{0}(...)`",
134 path_to_string(&self.name())
135 ))),
136 }
137 }
138
name(&self) -> syn::Path139 pub fn name(&self) -> syn::Path {
140 self.meta.path().clone()
141 }
142
has_flag(&self, flag: &str) -> bool143 pub fn has_flag(&self, flag: &str) -> bool {
144 self.nested()
145 .map(|mut n| {
146 n.any(|m| match m.path() {
147 Ok(word) => word.is_ident(flag),
148 Err(_) => false,
149 })
150 })
151 .unwrap_or_else(|e| {
152 e.emit();
153 false
154 })
155 }
156
ty_value(&self) -> Result<syn::Type, Diagnostic>157 pub fn ty_value(&self) -> Result<syn::Type, Diagnostic> {
158 let str = self.lit_str_value()?;
159 str.parse()
160 .map_err(|_| str.span().error("Invalid Rust type"))
161 }
162
expect_str_value(&self) -> String163 pub fn expect_str_value(&self) -> String {
164 self.str_value().unwrap_or_else(|e| {
165 e.emit();
166 path_to_string(&self.name())
167 })
168 }
169
str_value(&self) -> Result<String, Diagnostic>170 fn str_value(&self) -> Result<String, Diagnostic> {
171 self.lit_str_value().map(syn::LitStr::value)
172 }
173
lit_str_value(&self) -> Result<&syn::LitStr, Diagnostic>174 fn lit_str_value(&self) -> Result<&syn::LitStr, Diagnostic> {
175 use syn::Lit::*;
176
177 match *self.lit_value()? {
178 Str(ref s) => Ok(s),
179 _ => Err(self.span().error(format!(
180 "`{0}` must be in the form `{0} = \"value\"`",
181 path_to_string(&self.name())
182 ))),
183 }
184 }
185
expect_int_value(&self) -> u64186 pub fn expect_int_value(&self) -> u64 {
187 self.int_value().emit_error().unwrap_or(0)
188 }
189
int_value(&self) -> Result<u64, Diagnostic>190 pub fn int_value(&self) -> Result<u64, Diagnostic> {
191 use syn::Lit::*;
192
193 let error = self.value_span().error("Expected a number");
194
195 match *self.lit_value()? {
196 Str(ref s) => s.value().parse().map_err(|_| error),
197 Int(ref i) => i.base10_parse().map_err(|_| error),
198 _ => Err(error),
199 }
200 }
201
lit_value(&self) -> Result<&syn::Lit, Diagnostic>202 fn lit_value(&self) -> Result<&syn::Lit, Diagnostic> {
203 use syn::Meta::*;
204
205 match self.meta {
206 NameValue(ref name_value) => Ok(&name_value.lit),
207 _ => Err(self.span().error(format!(
208 "`{0}` must be in the form `{0} = \"value\"`",
209 path_to_string(&self.name())
210 ))),
211 }
212 }
213
warn_if_other_options(&self, options: &[&str])214 pub fn warn_if_other_options(&self, options: &[&str]) {
215 let nested = match self.nested() {
216 Ok(x) => x,
217 Err(_) => return,
218 };
219 let unrecognized_options =
220 nested.filter(|n| !options.iter().any(|&o| n.name().is_ident(o)));
221 for ignored in unrecognized_options {
222 ignored
223 .span()
224 .warning(format!(
225 "Option {} has no effect",
226 path_to_string(&ignored.name())
227 ))
228 .emit();
229 }
230 }
231
value_span(&self) -> Span232 fn value_span(&self) -> Span {
233 use syn::Meta::*;
234
235 match self.meta {
236 Path(ref path) => path.span(),
237 List(ref meta) => meta.nested.span(),
238 NameValue(ref meta) => meta.lit.span(),
239 }
240 }
241
span(&self) -> Span242 pub fn span(&self) -> Span {
243 self.meta.span()
244 }
245 }
246
247 #[cfg_attr(rustfmt, rustfmt_skip)] // https://github.com/rust-lang-nursery/rustfmt/issues/2392
248 pub struct Nested<'a>(syn::punctuated::Iter<'a, syn::NestedMeta>);
249
250 impl<'a> Iterator for Nested<'a> {
251 type Item = MetaItem;
252
next(&mut self) -> Option<Self::Item>253 fn next(&mut self) -> Option<Self::Item> {
254 use syn::NestedMeta::*;
255
256 match self.0.next() {
257 Some(&Meta(ref item)) => Some(MetaItem { meta: item.clone() }),
258 Some(_) => self.next(),
259 None => None,
260 }
261 }
262 }
263
264 /// If the given span is affected by
265 /// <https://github.com/rust-lang/rust/issues/47941>,
266 /// returns the span of the pound token
267 struct FixSpan(Span);
268
269 impl Fold for FixSpan {
fold_span(&mut self, span: Span) -> Span270 fn fold_span(&mut self, span: Span) -> Span {
271 fix_span(span, self.0)
272 }
273 }
274