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