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.
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.
11 // Functions dealing with attributes and meta_items
14 use codemap::{spanned, dummy_spanned};
17 use diagnostic::span_handler;
18 use parse::comments::{doc_comment_style, strip_doc_comment_decoration};
20 use core::hashmap::HashSet;
25 pub fn mk_name_value_item_str(name: @~str, value: @~str)
27 let value_lit = dummy_spanned(ast::lit_str(value));
28 mk_name_value_item(name, value_lit)
31 pub fn mk_name_value_item(name: @~str, value: ast::lit)
33 @dummy_spanned(ast::meta_name_value(name, value))
36 pub fn mk_list_item(name: @~str, items: ~[@ast::meta_item]) ->
38 @dummy_spanned(ast::meta_list(name, items))
41 pub fn mk_word_item(name: @~str) -> @ast::meta_item {
42 @dummy_spanned(ast::meta_word(name))
45 pub fn mk_attr(item: @ast::meta_item) -> ast::attribute {
46 dummy_spanned(ast::attribute_ { style: ast::attr_inner,
48 is_sugared_doc: false })
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_ {
57 value: @spanned(lo, hi, ast::meta_name_value(@~"doc", lit)),
65 pub fn attr_meta(attr: ast::attribute) -> @ast::meta_item {
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) }
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));
87 pub fn get_attr_name(attr: &ast::attribute) -> @~str {
88 get_meta_item_name(attr.node.value)
91 pub fn get_meta_item_name(meta: @ast::meta_item) -> @~str {
93 ast::meta_word(n) => n,
94 ast::meta_name_value(n, _) => n,
95 ast::meta_list(n, _) => n,
100 * Gets the string value if the meta_item is a meta_name_value variant
101 * containing a string, otherwise none
103 pub fn get_meta_item_value_str(meta: @ast::meta_item) -> Option<@~str> {
105 ast::meta_name_value(_, v) => {
107 ast::lit_str(s) => Some(s),
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]> {
119 ast::meta_list(_, ref l) => Some(/* FIXME (#2543) */ copy *l),
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`
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) {
132 let name = attr::get_meta_item_name(item);
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) ->
145 do vec::filter_mapped(attrs) |a| {
146 if name == *get_attr_name(a) {
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) ->
158 for metas.each |mi| {
159 if name == *get_meta_item_name(*mi) {
167 * Returns true if a list of meta items contains another meta item. The
168 * comparison is performed structurally.
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; }
178 fn eq(a: @ast::meta_item, b: @ast::meta_item) -> bool {
180 ast::meta_word(ref na) => match b.node {
181 ast::meta_word(ref nb) => (*na) == (*nb),
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
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; }
194 if !misb.contains(mi) { return false; }
203 pub fn contains_name(metas: &[@ast::meta_item], name: &str) -> bool {
204 let matches = find_meta_items_by_name(metas, name);
208 pub fn attrs_contains_name(attrs: &[ast::attribute], name: &str) -> bool {
209 !find_attrs_by_name(attrs, name).is_empty()
212 pub fn first_attr_value_str_by_name(attrs: &[ast::attribute], name: &str)
215 let mattrs = find_attrs_by_name(attrs, name);
216 if mattrs.len() > 0 {
217 get_meta_item_value_str(attr_meta(mattrs[0]))
223 fn last_meta_item_by_name(items: &[@ast::meta_item], name: &str)
224 -> Option<@ast::meta_item> {
226 let items = attr::find_meta_items_by_name(items, name);
227 items.last_opt().map(|item| **item)
230 pub fn last_meta_item_value_str_by_name(items: &[@ast::meta_item], name: &str)
233 match last_meta_item_by_name(items, name) {
235 match attr::get_meta_item_value_str(item) {
236 Some(value) => Some(value),
244 pub fn last_meta_item_list_by_name(items: ~[@ast::meta_item], name: &str)
245 -> Option<~[@ast::meta_item]> {
247 match last_meta_item_by_name(items, name) {
248 Some(item) => attr::get_meta_item_list(item),
254 /* Higher-level applications */
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)
263 // There doesn't seem to be a more optimal way to do this
266 ast::meta_list(n, ref mis) => {
268 node: ast::meta_list(n, sort_meta_items(*mis)),
277 pub fn remove_meta_items_by_name(items: ~[@ast::meta_item], name: &str) ->
280 return vec::filter_mapped(items, |item| {
281 if name != *get_meta_item_name(*item) {
290 * From a list of crate attributes get only the meta_items that affect crate
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,
303 pub enum inline_attr {
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() {
319 } else if !find_meta_items_by_name(*items, ~"never").is_empty() {
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);
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));
349 // indent-tabs-mode: nil
351 // buffer-file-coding-system: utf-8-unix