]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/attr.rs
2f8405c6e9689c8cbf6e786a3c07d9f6cc3dc6ae
[rust.git] / src / libsyntax / attr.rs
1 // Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 // Functions dealing with attributes and meta_items
12
13 use ast;
14 use codemap::{spanned, dummy_spanned};
15 use attr;
16 use codemap::BytePos;
17 use diagnostic::span_handler;
18 use parse::comments::{doc_comment_style, strip_doc_comment_decoration};
19
20 use core::hashmap::HashSet;
21 use std;
22
23 /* Constructors */
24
25 pub fn mk_name_value_item_str(name: @~str, value: @~str)
26                            -> @ast::meta_item {
27     let value_lit = dummy_spanned(ast::lit_str(value));
28     mk_name_value_item(name, value_lit)
29 }
30
31 pub fn mk_name_value_item(name: @~str, value: ast::lit)
32         -> @ast::meta_item {
33     @dummy_spanned(ast::meta_name_value(name, value))
34 }
35
36 pub fn mk_list_item(name: @~str, items: ~[@ast::meta_item]) ->
37    @ast::meta_item {
38     @dummy_spanned(ast::meta_list(name, items))
39 }
40
41 pub fn mk_word_item(name: @~str) -> @ast::meta_item {
42     @dummy_spanned(ast::meta_word(name))
43 }
44
45 pub fn mk_attr(item: @ast::meta_item) -> ast::attribute {
46     dummy_spanned(ast::attribute_ { style: ast::attr_inner,
47                                     value: item,
48                                     is_sugared_doc: false })
49 }
50
51 pub fn mk_sugared_doc_attr(text: ~str,
52                            lo: BytePos, hi: BytePos) -> ast::attribute {
53     let style = doc_comment_style(text);
54     let lit = spanned(lo, hi, ast::lit_str(@text));
55     let attr = ast::attribute_ {
56         style: style,
57         value: @spanned(lo, hi, ast::meta_name_value(@~"doc", lit)),
58         is_sugared_doc: true
59     };
60     spanned(lo, hi, attr)
61 }
62
63 /* Conversion */
64
65 pub fn attr_meta(attr: ast::attribute) -> @ast::meta_item {
66     attr.node.value
67 }
68
69 // Get the meta_items from inside a vector of attributes
70 pub fn attr_metas(attrs: &[ast::attribute]) -> ~[@ast::meta_item] {
71     do attrs.map |a| { attr_meta(*a) }
72 }
73
74 pub fn desugar_doc_attr(attr: &ast::attribute) -> ast::attribute {
75     if attr.node.is_sugared_doc {
76         let comment = get_meta_item_value_str(attr.node.value).get();
77         let meta = mk_name_value_item_str(@~"doc",
78                                      @strip_doc_comment_decoration(*comment));
79         mk_attr(meta)
80     } else {
81         *attr
82     }
83 }
84
85 /* Accessors */
86
87 pub fn get_attr_name(attr: &ast::attribute) -> @~str {
88     get_meta_item_name(attr.node.value)
89 }
90
91 pub fn get_meta_item_name(meta: @ast::meta_item) -> @~str {
92     match meta.node {
93         ast::meta_word(n) => n,
94         ast::meta_name_value(n, _) => n,
95         ast::meta_list(n, _) => n,
96     }
97 }
98
99 /**
100  * Gets the string value if the meta_item is a meta_name_value variant
101  * containing a string, otherwise none
102  */
103 pub fn get_meta_item_value_str(meta: @ast::meta_item) -> Option<@~str> {
104     match meta.node {
105         ast::meta_name_value(_, v) => {
106             match v.node {
107                 ast::lit_str(s) => Some(s),
108                 _ => None,
109             }
110         },
111         _ => None
112     }
113 }
114
115 /// Gets a list of inner meta items from a list meta_item type
116 pub fn get_meta_item_list(meta: @ast::meta_item)
117                        -> Option<~[@ast::meta_item]> {
118     match meta.node {
119         ast::meta_list(_, ref l) => Some(/* FIXME (#2543) */ copy *l),
120         _ => None
121     }
122 }
123
124 /**
125  * If the meta item is a nam-value type with a string value then returns
126  * a tuple containing the name and string value, otherwise `none`
127  */
128 pub fn get_name_value_str_pair(item: @ast::meta_item)
129                             -> Option<(@~str, @~str)> {
130     match attr::get_meta_item_value_str(item) {
131       Some(value) => {
132         let name = attr::get_meta_item_name(item);
133         Some((name, value))
134       }
135       None => None
136     }
137 }
138
139
140 /* Searching */
141
142 /// Search a list of attributes and return only those with a specific name
143 pub fn find_attrs_by_name(attrs: &[ast::attribute], name: &str) ->
144    ~[ast::attribute] {
145     do vec::filter_mapped(attrs) |a| {
146         if name == *get_attr_name(a) {
147             Some(*a)
148         } else {
149             None
150         }
151     }
152 }
153
154 /// Search a list of meta items and return only those with a specific name
155 pub fn find_meta_items_by_name(metas: &[@ast::meta_item], name: &str) ->
156    ~[@ast::meta_item] {
157     let mut rs = ~[];
158     for metas.each |mi| {
159         if name == *get_meta_item_name(*mi) {
160             rs.push(*mi)
161         }
162     }
163     rs
164 }
165
166 /**
167  * Returns true if a list of meta items contains another meta item. The
168  * comparison is performed structurally.
169  */
170 pub fn contains(haystack: &[@ast::meta_item],
171                 needle: @ast::meta_item) -> bool {
172     for haystack.each |item| {
173         if eq(*item, needle) { return true; }
174     }
175     return false;
176 }
177
178 fn eq(a: @ast::meta_item, b: @ast::meta_item) -> bool {
179     match a.node {
180         ast::meta_word(ref na) => match b.node {
181             ast::meta_word(ref nb) => (*na) == (*nb),
182             _ => false
183         },
184         ast::meta_name_value(ref na, va) => match b.node {
185             ast::meta_name_value(ref nb, vb) => {
186                 (*na) == (*nb) && va.node == vb.node
187             }
188             _ => false
189         },
190         ast::meta_list(ref na, ref misa) => match b.node {
191             ast::meta_list(ref nb, ref misb) => {
192                 if na != nb { return false; }
193                 for misa.each |mi| {
194                     if !misb.contains(mi) { return false; }
195                 }
196                 true
197             }
198             _ => false
199         }
200     }
201 }
202
203 pub fn contains_name(metas: &[@ast::meta_item], name: &str) -> bool {
204     let matches = find_meta_items_by_name(metas, name);
205     matches.len() > 0u
206 }
207
208 pub fn attrs_contains_name(attrs: &[ast::attribute], name: &str) -> bool {
209     !find_attrs_by_name(attrs, name).is_empty()
210 }
211
212 pub fn first_attr_value_str_by_name(attrs: &[ast::attribute], name: &str)
213                                  -> Option<@~str> {
214
215     let mattrs = find_attrs_by_name(attrs, name);
216     if mattrs.len() > 0 {
217         get_meta_item_value_str(attr_meta(mattrs[0]))
218     } else {
219         None
220     }
221 }
222
223 fn last_meta_item_by_name(items: &[@ast::meta_item], name: &str)
224     -> Option<@ast::meta_item> {
225
226     let items = attr::find_meta_items_by_name(items, name);
227     items.last_opt().map(|item| **item)
228 }
229
230 pub fn last_meta_item_value_str_by_name(items: &[@ast::meta_item], name: &str)
231                                      -> Option<@~str> {
232
233     match last_meta_item_by_name(items, name) {
234         Some(item) => {
235             match attr::get_meta_item_value_str(item) {
236                 Some(value) => Some(value),
237                 None => None
238             }
239         },
240         None => None
241     }
242 }
243
244 pub fn last_meta_item_list_by_name(items: ~[@ast::meta_item], name: &str)
245     -> Option<~[@ast::meta_item]> {
246
247     match last_meta_item_by_name(items, name) {
248       Some(item) => attr::get_meta_item_list(item),
249       None => None
250     }
251 }
252
253
254 /* Higher-level applications */
255
256 pub fn sort_meta_items(items: &[@ast::meta_item]) -> ~[@ast::meta_item] {
257     // This is sort of stupid here, converting to a vec of mutables and back
258     let mut v = vec::from_slice(items);
259     do std::sort::quick_sort(v) |ma, mb| {
260         get_meta_item_name(*ma) <= get_meta_item_name(*mb)
261     }
262
263     // There doesn't seem to be a more optimal way to do this
264     do v.map |m| {
265         match m.node {
266             ast::meta_list(n, ref mis) => {
267                 @spanned {
268                     node: ast::meta_list(n, sort_meta_items(*mis)),
269                     .. /*bad*/ copy **m
270                 }
271             }
272             _ => /*bad*/ copy *m
273         }
274     }
275 }
276
277 pub fn remove_meta_items_by_name(items: ~[@ast::meta_item], name: &str) ->
278    ~[@ast::meta_item] {
279
280     return vec::filter_mapped(items, |item| {
281         if name != *get_meta_item_name(*item) {
282             Some(*item)
283         } else {
284             None
285         }
286     });
287 }
288
289 /**
290  * From a list of crate attributes get only the meta_items that affect crate
291  * linkage
292  */
293 pub fn find_linkage_metas(attrs: &[ast::attribute]) -> ~[@ast::meta_item] {
294     do find_attrs_by_name(attrs, ~"link").flat_map |attr| {
295         match attr.node.value.node {
296             ast::meta_list(_, ref items) => /* FIXME (#2543) */ copy *items,
297             _ => ~[]
298         }
299     }
300 }
301
302 #[deriving(Eq)]
303 pub enum inline_attr {
304     ia_none,
305     ia_hint,
306     ia_always,
307     ia_never,
308 }
309
310 /// True if something like #[inline] is found in the list of attrs.
311 pub fn find_inline_attr(attrs: &[ast::attribute]) -> inline_attr {
312     // FIXME (#2809)---validate the usage of #[inline] and #[inline(always)]
313     do vec::foldl(ia_none, attrs) |ia,attr| {
314         match attr.node.value.node {
315           ast::meta_word(@~"inline") => ia_hint,
316           ast::meta_list(@~"inline", ref items) => {
317             if !find_meta_items_by_name(*items, ~"always").is_empty() {
318                 ia_always
319             } else if !find_meta_items_by_name(*items, ~"never").is_empty() {
320                 ia_never
321             } else {
322                 ia_hint
323             }
324           }
325           _ => ia
326         }
327     }
328 }
329
330
331 pub fn require_unique_names(diagnostic: @span_handler,
332                             metas: &[@ast::meta_item]) {
333     let mut set = HashSet::new();
334     for metas.each |meta| {
335         let name = get_meta_item_name(*meta);
336
337         // FIXME: How do I silence the warnings? --pcw (#2619)
338         if !set.insert(name) {
339             diagnostic.span_fatal(meta.span,
340                                   fmt!("duplicate meta item `%s`", *name));
341         }
342     }
343 }
344
345 //
346 // Local Variables:
347 // mode: rust
348 // fill-column: 78;
349 // indent-tabs-mode: nil
350 // c-basic-offset: 4
351 // buffer-file-coding-system: utf-8-unix
352 // End:
353 //