1 // Functions dealing with attributes and meta_items
6 use diagnostic::span_handler;
7 use ast_util::{spanned, dummy_spanned};
8 use parse::comments::{doc_comment_style, strip_doc_comment_decoration};
11 export mk_name_value_item_str;
12 export mk_name_value_item;
16 export mk_sugared_doc_attr;
21 export desugar_doc_attr;
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;
31 export find_attrs_by_name;
32 export find_meta_items_by_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;
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;
47 export find_inline_attr;
48 export require_unique_names;
52 fn mk_name_value_item_str(name: ~str, +value: ~str) ->
54 let value_lit = dummy_spanned(ast::lit_str(@value));
55 return mk_name_value_item(name, value_lit);
58 fn mk_name_value_item(name: ~str, +value: ast::lit)
60 return @dummy_spanned(ast::meta_name_value(name, value));
63 fn mk_list_item(name: ~str, +items: ~[@ast::meta_item]) ->
65 return @dummy_spanned(ast::meta_list(name, items));
68 fn mk_word_item(name: ~str) -> @ast::meta_item {
69 return @dummy_spanned(ast::meta_word(name));
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});
77 fn mk_sugared_doc_attr(text: ~str, lo: uint, hi: uint) -> ast::attribute {
78 let lit = spanned(lo, hi, ast::lit_str(@text));
80 style: doc_comment_style(text),
81 value: spanned(lo, hi, ast::meta_name_value(~"doc", lit)),
84 return spanned(lo, hi, attr);
89 fn attr_meta(attr: ast::attribute) -> @ast::meta_item { @attr.node.value }
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) }
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);
109 fn get_attr_name(attr: ast::attribute) -> ~str {
110 get_meta_item_name(@attr.node.value)
113 fn get_meta_item_name(meta: @ast::meta_item) -> ~str {
115 ast::meta_word(n) => n,
116 ast::meta_name_value(n, _) => n,
117 ast::meta_list(n, _) => n
122 * Gets the string value if the meta_item is a meta_name_value variant
123 * containing a string, otherwise none
125 fn get_meta_item_value_str(meta: @ast::meta_item) -> Option<~str> {
127 ast::meta_name_value(_, v) => match v.node {
128 ast::lit_str(s) => option::Some(*s),
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]> {
138 ast::meta_list(_, l) => option::Some(/* FIXME (#2543) */ copy l),
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`
147 fn get_name_value_str_pair(item: @ast::meta_item) -> Option<(~str, ~str)> {
148 match attr::get_meta_item_value_str(item) {
150 let name = attr::get_meta_item_name(item);
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) ->
164 fn@(a: &ast::attribute) -> Option<ast::attribute> {
165 if get_attr_name(*a) == name {
167 } else { option::None }
170 return vec::filter_map(attrs, filter);
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) ->
176 let filter = fn@(m: &@ast::meta_item) -> Option<@ast::meta_item> {
177 if get_meta_item_name(*m) == name {
179 } else { option::None }
181 return vec::filter_map(metas, filter);
185 * Returns true if a list of meta items contains another meta item. The
186 * comparison is performed structurally.
188 fn contains(haystack: ~[@ast::meta_item], needle: @ast::meta_item) -> bool {
189 for haystack.each |item| {
190 if eq(*item, needle) { return true; }
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,
201 ast::meta_name_value(na, va) => match b.node {
202 ast::meta_name_value(nb, vb) => na == nb && va.node == vb.node,
205 ast::meta_list(*) => {
208 // FIXME (#607): Needs implementing
209 // This involves probably sorting the list by name and
211 fail ~"unimplemented meta_item variant"
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;
221 fn attrs_contains_name(attrs: ~[ast::attribute], name: ~str) -> bool {
222 vec::is_not_empty(find_attrs_by_name(attrs, name))
225 fn first_attr_value_str_by_name(attrs: ~[ast::attribute], name: ~str)
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]));
235 fn last_meta_item_by_name(items: ~[@ast::meta_item], name: ~str)
236 -> Option<@ast::meta_item> {
238 let items = attr::find_meta_items_by_name(items, name);
242 fn last_meta_item_value_str_by_name(items: ~[@ast::meta_item], name: ~str)
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),
254 fn last_meta_item_list_by_name(items: ~[@ast::meta_item], name: ~str)
255 -> Option<~[@ast::meta_item]> {
257 match last_meta_item_by_name(items, name) {
258 Some(item) => attr::get_meta_item_list(item),
264 /* Higher-level applications */
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 {
272 ast::meta_word(name) => name,
273 ast::meta_name_value(name, _) => name,
274 ast::meta_list(name, _) => name
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)
286 fn remove_meta_items_by_name(items: ~[@ast::meta_item], name: ~str) ->
289 return vec::filter_map(items, |item| {
290 if get_meta_item_name(*item) != name {
291 option::Some(/* FIXME (#2543) */ copy *item)
299 * From a list of crate attributes get only the meta_items that affect crate
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,
311 fn foreign_abi(attrs: ~[ast::attribute]) -> Either<~str, ast::foreign_abi> {
312 return match attr::first_attr_value_str_by_name(attrs, ~"abi") {
314 either::Right(ast::foreign_abi_cdecl)
316 option::Some(~"rust-intrinsic") => {
317 either::Right(ast::foreign_abi_rust_intrinsic)
319 option::Some(~"cdecl") => {
320 either::Right(ast::foreign_abi_cdecl)
322 option::Some(~"stdcall") => {
323 either::Right(ast::foreign_abi_stdcall)
326 either::Left(~"unsupported abi: " + t)
338 impl inline_attr : cmp::Eq {
339 pure fn eq(other: &inline_attr) -> bool {
340 (self as uint) == ((*other) as uint)
342 pure fn ne(other: &inline_attr) -> bool { !self.eq(other) }
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")) {
354 } else if !vec::is_empty(
355 find_meta_items_by_name(items, ~"never")) {
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);
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));
378 map.insert(name, ());
386 // indent-tabs-mode: nil
388 // buffer-file-coding-system: utf-8-unix