]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/attr.rs
Merge remote-tracking branch 'original/incoming' into incoming
[rust.git] / src / libsyntax / attr.rs
1 // Functions dealing with attributes and meta_items
2
3 use std::map;
4 use std::map::HashMap;
5 use either::Either;
6 use diagnostic::span_handler;
7 use ast_util::{spanned, dummy_spanned};
8 use parse::comments::{doc_comment_style, strip_doc_comment_decoration};
9
10 // Constructors
11 export mk_name_value_item_str;
12 export mk_name_value_item;
13 export mk_list_item;
14 export mk_word_item;
15 export mk_attr;
16 export mk_sugared_doc_attr;
17
18 // Conversion
19 export attr_meta;
20 export attr_metas;
21 export desugar_doc_attr;
22
23 // Accessors
24 export get_attr_name;
25 export get_meta_item_name;
26 export get_meta_item_value_str;
27 export get_meta_item_list;
28 export get_name_value_str_pair;
29
30 // Searching
31 export find_attrs_by_name;
32 export find_meta_items_by_name;
33 export contains;
34 export contains_name;
35 export attrs_contains_name;
36 export first_attr_value_str_by_name;
37 export last_meta_item_value_str_by_name;
38 export last_meta_item_list_by_name;
39
40 // Higher-level applications
41 export sort_meta_items;
42 export remove_meta_items_by_name;
43 export find_linkage_attrs;
44 export find_linkage_metas;
45 export foreign_abi;
46 export inline_attr;
47 export find_inline_attr;
48 export require_unique_names;
49
50 /* Constructors */
51
52 fn mk_name_value_item_str(name: ~str, +value: ~str) ->
53     @ast::meta_item {
54     let value_lit = dummy_spanned(ast::lit_str(@value));
55     return mk_name_value_item(name, value_lit);
56 }
57
58 fn mk_name_value_item(name: ~str, +value: ast::lit)
59         -> @ast::meta_item {
60     return @dummy_spanned(ast::meta_name_value(name, value));
61 }
62
63 fn mk_list_item(name: ~str, +items: ~[@ast::meta_item]) ->
64    @ast::meta_item {
65     return @dummy_spanned(ast::meta_list(name, items));
66 }
67
68 fn mk_word_item(name: ~str) -> @ast::meta_item {
69     return @dummy_spanned(ast::meta_word(name));
70 }
71
72 fn mk_attr(item: @ast::meta_item) -> ast::attribute {
73     return dummy_spanned({style: ast::attr_inner, value: *item,
74                        is_sugared_doc: false});
75 }
76
77 fn mk_sugared_doc_attr(text: ~str, lo: uint, hi: uint) -> ast::attribute {
78     let lit = spanned(lo, hi, ast::lit_str(@text));
79     let attr = {
80         style: doc_comment_style(text),
81         value: spanned(lo, hi, ast::meta_name_value(~"doc", lit)),
82         is_sugared_doc: true
83     };
84     return spanned(lo, hi, attr);
85 }
86
87 /* Conversion */
88
89 fn attr_meta(attr: ast::attribute) -> @ast::meta_item { @attr.node.value }
90
91 // Get the meta_items from inside a vector of attributes
92 fn attr_metas(attrs: ~[ast::attribute]) -> ~[@ast::meta_item] {
93     do attrs.map |a| { attr_meta(*a) }
94 }
95
96 fn desugar_doc_attr(attr: &ast::attribute) -> ast::attribute {
97     if attr.node.is_sugared_doc {
98         let comment = get_meta_item_value_str(@attr.node.value).get();
99         let meta = mk_name_value_item_str(~"doc",
100                                      strip_doc_comment_decoration(comment));
101         return mk_attr(meta);
102     } else {
103         *attr
104     }
105 }
106
107 /* Accessors */
108
109 fn get_attr_name(attr: ast::attribute) -> ~str {
110     get_meta_item_name(@attr.node.value)
111 }
112
113 fn get_meta_item_name(meta: @ast::meta_item) -> ~str {
114     match meta.node {
115       ast::meta_word(n) => n,
116       ast::meta_name_value(n, _) => n,
117       ast::meta_list(n, _) => n
118     }
119 }
120
121 /**
122  * Gets the string value if the meta_item is a meta_name_value variant
123  * containing a string, otherwise none
124  */
125 fn get_meta_item_value_str(meta: @ast::meta_item) -> Option<~str> {
126     match meta.node {
127         ast::meta_name_value(_, v) => match v.node {
128             ast::lit_str(s) => option::Some(*s),
129             _ => option::None
130         },
131         _ => option::None
132     }
133 }
134
135 /// Gets a list of inner meta items from a list meta_item type
136 fn get_meta_item_list(meta: @ast::meta_item) -> Option<~[@ast::meta_item]> {
137     match meta.node {
138       ast::meta_list(_, l) => option::Some(/* FIXME (#2543) */ copy l),
139       _ => option::None
140     }
141 }
142
143 /**
144  * If the meta item is a nam-value type with a string value then returns
145  * a tuple containing the name and string value, otherwise `none`
146  */
147 fn get_name_value_str_pair(item: @ast::meta_item) -> Option<(~str, ~str)> {
148     match attr::get_meta_item_value_str(item) {
149       Some(value) => {
150         let name = attr::get_meta_item_name(item);
151         Some((name, value))
152       }
153       None => None
154     }
155 }
156
157
158 /* Searching */
159
160 /// Search a list of attributes and return only those with a specific name
161 fn find_attrs_by_name(attrs: ~[ast::attribute], name: ~str) ->
162    ~[ast::attribute] {
163     let filter = (
164         fn@(a: &ast::attribute) -> Option<ast::attribute> {
165             if get_attr_name(*a) == name {
166                 option::Some(*a)
167             } else { option::None }
168         }
169     );
170     return vec::filter_map(attrs, filter);
171 }
172
173 /// Searcha list of meta items and return only those with a specific name
174 fn find_meta_items_by_name(metas: ~[@ast::meta_item], name: ~str) ->
175    ~[@ast::meta_item] {
176     let filter = fn@(m: &@ast::meta_item) -> Option<@ast::meta_item> {
177         if get_meta_item_name(*m) == name {
178             option::Some(*m)
179         } else { option::None }
180     };
181     return vec::filter_map(metas, filter);
182 }
183
184 /**
185  * Returns true if a list of meta items contains another meta item. The
186  * comparison is performed structurally.
187  */
188 fn contains(haystack: ~[@ast::meta_item], needle: @ast::meta_item) -> bool {
189     for haystack.each |item| {
190         if eq(*item, needle) { return true; }
191     }
192     return false;
193 }
194
195 fn eq(a: @ast::meta_item, b: @ast::meta_item) -> bool {
196     return match a.node {
197           ast::meta_word(na) => match b.node {
198             ast::meta_word(nb) => na == nb,
199             _ => false
200           },
201           ast::meta_name_value(na, va) => match b.node {
202             ast::meta_name_value(nb, vb) => na == nb && va.node == vb.node,
203             _ => false
204           },
205           ast::meta_list(*) => {
206
207             // ~[Fixme-sorting]
208             // FIXME (#607): Needs implementing
209             // This involves probably sorting the list by name and
210             // meta_item variant
211             fail ~"unimplemented meta_item variant"
212           }
213         }
214 }
215
216 fn contains_name(metas: ~[@ast::meta_item], name: ~str) -> bool {
217     let matches = find_meta_items_by_name(metas, name);
218     return vec::len(matches) > 0u;
219 }
220
221 fn attrs_contains_name(attrs: ~[ast::attribute], name: ~str) -> bool {
222     vec::is_not_empty(find_attrs_by_name(attrs, name))
223 }
224
225 fn first_attr_value_str_by_name(attrs: ~[ast::attribute], name: ~str)
226     -> Option<~str> {
227
228     let mattrs = find_attrs_by_name(attrs, name);
229     if vec::len(mattrs) > 0u {
230         return get_meta_item_value_str(attr_meta(mattrs[0]));
231     }
232     return option::None;
233 }
234
235 fn last_meta_item_by_name(items: ~[@ast::meta_item], name: ~str)
236     -> Option<@ast::meta_item> {
237
238     let items = attr::find_meta_items_by_name(items, name);
239     vec::last_opt(items)
240 }
241
242 fn last_meta_item_value_str_by_name(items: ~[@ast::meta_item], name: ~str)
243     -> Option<~str> {
244
245     match last_meta_item_by_name(items, name) {
246       Some(item) => match attr::get_meta_item_value_str(item) {
247         Some(value) => Some(value),
248         None => None
249       },
250       None => None
251     }
252 }
253
254 fn last_meta_item_list_by_name(items: ~[@ast::meta_item], name: ~str)
255     -> Option<~[@ast::meta_item]> {
256
257     match last_meta_item_by_name(items, name) {
258       Some(item) => attr::get_meta_item_list(item),
259       None => None
260     }
261 }
262
263
264 /* Higher-level applications */
265
266 // FIXME (#607): This needs to sort by meta_item variant in addition to
267 // the item name (See [Fixme-sorting])
268 fn sort_meta_items(+items: ~[@ast::meta_item]) -> ~[@ast::meta_item] {
269     pure fn lteq(ma: &@ast::meta_item, mb: &@ast::meta_item) -> bool {
270         pure fn key(m: &ast::meta_item) -> ~str {
271             match m.node {
272               ast::meta_word(name) => name,
273               ast::meta_name_value(name, _) => name,
274               ast::meta_list(name, _) => name
275             }
276         }
277         key(*ma) <= key(*mb)
278     }
279
280     // This is sort of stupid here, converting to a vec of mutables and back
281     let v: ~[mut @ast::meta_item] = vec::to_mut(items);
282     std::sort::quick_sort(v, lteq);
283     vec::from_mut(move v)
284 }
285
286 fn remove_meta_items_by_name(items: ~[@ast::meta_item], name: ~str) ->
287    ~[@ast::meta_item] {
288
289     return vec::filter_map(items, |item| {
290         if get_meta_item_name(*item) != name {
291             option::Some(/* FIXME (#2543) */ copy *item)
292         } else {
293             option::None
294         }
295     });
296 }
297
298 /**
299  * From a list of crate attributes get only the meta_items that affect crate
300  * linkage
301  */
302 fn find_linkage_metas(attrs: ~[ast::attribute]) -> ~[@ast::meta_item] {
303     do find_attrs_by_name(attrs, ~"link").flat_map |attr| {
304         match attr.node.value.node {
305             ast::meta_list(_, items) => /* FIXME (#2543) */ copy items,
306             _ => ~[]
307         }
308     }
309 }
310
311 fn foreign_abi(attrs: ~[ast::attribute]) -> Either<~str, ast::foreign_abi> {
312     return match attr::first_attr_value_str_by_name(attrs, ~"abi") {
313       option::None => {
314         either::Right(ast::foreign_abi_cdecl)
315       }
316       option::Some(~"rust-intrinsic") => {
317         either::Right(ast::foreign_abi_rust_intrinsic)
318       }
319       option::Some(~"cdecl") => {
320         either::Right(ast::foreign_abi_cdecl)
321       }
322       option::Some(~"stdcall") => {
323         either::Right(ast::foreign_abi_stdcall)
324       }
325       option::Some(t) => {
326         either::Left(~"unsupported abi: " + t)
327       }
328     };
329 }
330
331 enum inline_attr {
332     ia_none,
333     ia_hint,
334     ia_always,
335     ia_never,
336 }
337
338 impl inline_attr : cmp::Eq {
339     pure fn eq(other: &inline_attr) -> bool {
340         (self as uint) == ((*other) as uint)
341     }
342     pure fn ne(other: &inline_attr) -> bool { !self.eq(other) }
343 }
344
345 /// True if something like #[inline] is found in the list of attrs.
346 fn find_inline_attr(attrs: ~[ast::attribute]) -> inline_attr {
347     // FIXME (#2809)---validate the usage of #[inline] and #[inline(always)]
348     do vec::foldl(ia_none, attrs) |ia,attr| {
349         match attr.node.value.node {
350           ast::meta_word(~"inline") => ia_hint,
351           ast::meta_list(~"inline", items) => {
352             if !vec::is_empty(find_meta_items_by_name(items, ~"always")) {
353                 ia_always
354             } else if !vec::is_empty(
355                 find_meta_items_by_name(items, ~"never")) {
356                 ia_never
357             } else {
358                 ia_hint
359             }
360           }
361           _ => ia
362         }
363     }
364 }
365
366
367 fn require_unique_names(diagnostic: span_handler,
368                         metas: ~[@ast::meta_item]) {
369     let map = map::HashMap();
370     for metas.each |meta| {
371         let name = get_meta_item_name(*meta);
372
373         // FIXME: How do I silence the warnings? --pcw (#2619)
374         if map.contains_key(name) {
375             diagnostic.span_fatal(meta.span,
376                                   fmt!("duplicate meta item `%s`", name));
377         }
378         map.insert(name, ());
379     }
380 }
381
382 //
383 // Local Variables:
384 // mode: rust
385 // fill-column: 78;
386 // indent-tabs-mode: nil
387 // c-basic-offset: 4
388 // buffer-file-coding-system: utf-8-unix
389 // End:
390 //