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