rust/src/libsyntax/attr.rs

405 lines
11 KiB
Rust
Raw Normal View History

// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Functions dealing with attributes and meta_items
2012-09-04 13:37:29 -05:00
use std::map;
2012-09-10 17:38:28 -05:00
use std::map::HashMap;
2012-09-04 13:37:29 -05:00
use either::Either;
use diagnostic::span_handler;
use ast_util::{spanned, dummy_spanned};
use parse::comments::{doc_comment_style, strip_doc_comment_decoration};
use codemap::BytePos;
// Constructors
export mk_name_value_item_str;
export mk_name_value_item;
export mk_list_item;
export mk_word_item;
export mk_attr;
export mk_sugared_doc_attr;
// Conversion
export attr_meta;
export attr_metas;
export desugar_doc_attr;
// Accessors
export get_attr_name;
export get_meta_item_name;
export get_meta_item_value_str;
export get_meta_item_list;
export get_name_value_str_pair;
// Searching
export find_attrs_by_name;
2011-06-28 18:52:11 -05:00
export find_meta_items_by_name;
export contains;
export contains_name;
export attrs_contains_name;
export first_attr_value_str_by_name;
export last_meta_item_value_str_by_name;
export last_meta_item_list_by_name;
// Higher-level applications
export sort_meta_items;
export remove_meta_items_by_name;
export find_linkage_attrs;
export find_linkage_metas;
export foreign_abi;
export inline_attr;
export find_inline_attr;
export require_unique_names;
/* Constructors */
2012-07-18 18:18:02 -05:00
fn mk_name_value_item_str(name: ~str, +value: ~str) ->
@ast::meta_item {
2012-06-10 02:49:59 -05:00
let value_lit = dummy_spanned(ast::lit_str(@value));
2012-08-01 19:30:05 -05:00
return mk_name_value_item(name, value_lit);
}
2012-07-18 18:18:02 -05:00
fn mk_name_value_item(name: ~str, +value: ast::lit)
-> @ast::meta_item {
return @dummy_spanned(ast::meta_name_value(name, value));
}
fn mk_list_item(name: ~str, +items: ~[@ast::meta_item]) ->
@ast::meta_item {
return @dummy_spanned(ast::meta_list(name, items));
}
fn mk_word_item(name: ~str) -> @ast::meta_item {
return @dummy_spanned(ast::meta_word(name));
}
fn mk_attr(item: @ast::meta_item) -> ast::attribute {
return dummy_spanned({style: ast::attr_inner, value: *item,
is_sugared_doc: false});
}
fn mk_sugared_doc_attr(text: ~str,
+lo: BytePos, +hi: BytePos) -> ast::attribute {
let lit = spanned(lo, hi, ast::lit_str(@text));
let attr = {
style: doc_comment_style(text),
2012-07-18 18:18:02 -05:00
value: spanned(lo, hi, ast::meta_name_value(~"doc", lit)),
is_sugared_doc: true
};
2012-08-01 19:30:05 -05:00
return spanned(lo, hi, attr);
}
/* Conversion */
fn attr_meta(attr: ast::attribute) -> @ast::meta_item { @attr.node.value }
// Get the meta_items from inside a vector of attributes
fn attr_metas(attrs: ~[ast::attribute]) -> ~[@ast::meta_item] {
do attrs.map |a| { attr_meta(*a) }
}
fn desugar_doc_attr(attr: &ast::attribute) -> ast::attribute {
if attr.node.is_sugared_doc {
let comment = get_meta_item_value_str(@attr.node.value).get();
2012-07-18 18:18:02 -05:00
let meta = mk_name_value_item_str(~"doc",
strip_doc_comment_decoration(comment));
2012-08-01 19:30:05 -05:00
return mk_attr(meta);
} else {
*attr
}
}
/* Accessors */
2012-07-18 18:18:02 -05:00
fn get_attr_name(attr: ast::attribute) -> ~str {
get_meta_item_name(@attr.node.value)
}
fn get_meta_item_name(meta: @ast::meta_item) -> ~str {
2012-08-06 14:34:08 -05:00
match meta.node {
ast::meta_word(ref n) => (*n),
ast::meta_name_value(ref n, _) => (*n),
ast::meta_list(ref n, _) => (*n)
}
}
/**
* Gets the string value if the meta_item is a meta_name_value variant
* containing a string, otherwise none
*/
fn get_meta_item_value_str(meta: @ast::meta_item) -> Option<~str> {
2012-08-06 14:34:08 -05:00
match meta.node {
2012-07-18 18:18:02 -05:00
ast::meta_name_value(_, v) => match v.node {
2012-08-20 14:23:37 -05:00
ast::lit_str(s) => option::Some(*s),
_ => option::None
2012-07-18 18:18:02 -05:00
},
2012-08-20 14:23:37 -05:00
_ => option::None
}
}
/// Gets a list of inner meta items from a list meta_item type
fn get_meta_item_list(meta: @ast::meta_item) -> Option<~[@ast::meta_item]> {
2012-08-06 14:34:08 -05:00
match meta.node {
2012-08-20 14:23:37 -05:00
ast::meta_list(_, l) => option::Some(/* FIXME (#2543) */ copy l),
_ => option::None
}
}
/**
* If the meta item is a nam-value type with a string value then returns
* a tuple containing the name and string value, otherwise `none`
*/
fn get_name_value_str_pair(item: @ast::meta_item) -> Option<(~str, ~str)> {
2012-08-06 14:34:08 -05:00
match attr::get_meta_item_value_str(item) {
Some(ref value) => {
let name = attr::get_meta_item_name(item);
Some((name, (*value)))
}
2012-08-20 14:23:37 -05:00
None => None
}
}
/* Searching */
/// Search a list of attributes and return only those with a specific name
2012-07-18 18:18:02 -05:00
fn find_attrs_by_name(attrs: ~[ast::attribute], name: ~str) ->
~[ast::attribute] {
let filter = (
2012-09-28 00:20:47 -05:00
fn@(a: &ast::attribute) -> Option<ast::attribute> {
if get_attr_name(*a) == name {
option::Some(*a)
2012-08-20 14:23:37 -05:00
} else { option::None }
}
);
2012-08-01 19:30:05 -05:00
return vec::filter_map(attrs, filter);
}
2012-12-09 01:26:12 -06:00
/// Search a list of meta items and return only those with a specific name
fn find_meta_items_by_name(metas: ~[@ast::meta_item], name: ~str) ->
~[@ast::meta_item] {
2012-09-28 00:20:47 -05:00
let filter = fn@(m: &@ast::meta_item) -> Option<@ast::meta_item> {
if get_meta_item_name(*m) == name {
option::Some(*m)
2012-08-20 14:23:37 -05:00
} else { option::None }
};
2012-08-01 19:30:05 -05:00
return vec::filter_map(metas, filter);
}
/**
* Returns true if a list of meta items contains another meta item. The
* comparison is performed structurally.
*/
fn contains(haystack: ~[@ast::meta_item], needle: @ast::meta_item) -> bool {
2012-06-30 18:19:07 -05:00
for haystack.each |item| {
if eq(*item, needle) { return true; }
}
2012-08-01 19:30:05 -05:00
return false;
}
fn eq(a: @ast::meta_item, b: @ast::meta_item) -> bool {
2012-08-06 14:34:08 -05:00
return match a.node {
ast::meta_word(ref na) => match b.node {
ast::meta_word(ref nb) => (*na) == (*nb),
2012-08-03 21:59:04 -05:00
_ => false
},
ast::meta_name_value(ref na, va) => match b.node {
2012-12-04 23:13:02 -06:00
ast::meta_name_value(ref nb, vb) => {
(*na) == (*nb) && va.node == vb.node
}
2012-08-03 21:59:04 -05:00
_ => false
},
ast::meta_list(*) => {
2011-07-27 07:19:39 -05:00
// ~[Fixme-sorting]
2011-07-01 14:51:46 -05:00
// FIXME (#607): Needs implementing
// This involves probably sorting the list by name and
// meta_item variant
fail ~"unimplemented meta_item variant"
2011-07-27 07:19:39 -05:00
}
}
}
fn contains_name(metas: ~[@ast::meta_item], name: ~str) -> bool {
2011-07-27 07:19:39 -05:00
let matches = find_meta_items_by_name(metas, name);
2012-08-01 19:30:05 -05:00
return vec::len(matches) > 0u;
}
2012-07-18 18:18:02 -05:00
fn attrs_contains_name(attrs: ~[ast::attribute], name: ~str) -> bool {
vec::is_not_empty(find_attrs_by_name(attrs, name))
}
2012-07-18 18:18:02 -05:00
fn first_attr_value_str_by_name(attrs: ~[ast::attribute], name: ~str)
2012-08-20 14:23:37 -05:00
-> Option<~str> {
2012-07-18 18:18:02 -05:00
let mattrs = find_attrs_by_name(attrs, name);
if vec::len(mattrs) > 0u {
return get_meta_item_value_str(attr_meta(mattrs[0]));
}
2012-08-20 14:23:37 -05:00
return option::None;
}
fn last_meta_item_by_name(items: ~[@ast::meta_item], name: ~str)
-> Option<@ast::meta_item> {
2012-07-18 18:18:02 -05:00
let items = attr::find_meta_items_by_name(items, name);
vec::last_opt(items)
}
fn last_meta_item_value_str_by_name(items: ~[@ast::meta_item], name: ~str)
2012-08-20 14:23:37 -05:00
-> Option<~str> {
2012-07-18 18:18:02 -05:00
2012-08-06 14:34:08 -05:00
match last_meta_item_by_name(items, name) {
Some(item) => match attr::get_meta_item_value_str(item) {
Some(ref value) => Some((*value)),
2012-08-20 14:23:37 -05:00
None => None
},
2012-08-20 14:23:37 -05:00
None => None
}
}
fn last_meta_item_list_by_name(items: ~[@ast::meta_item], name: ~str)
-> Option<~[@ast::meta_item]> {
2012-07-18 18:18:02 -05:00
2012-08-06 14:34:08 -05:00
match last_meta_item_by_name(items, name) {
Some(item) => attr::get_meta_item_list(item),
2012-08-20 14:23:37 -05:00
None => None
}
}
/* Higher-level applications */
// FIXME (#607): This needs to sort by meta_item variant in addition to
// the item name (See [Fixme-sorting])
fn sort_meta_items(+items: ~[@ast::meta_item]) -> ~[@ast::meta_item] {
pure fn lteq(ma: &@ast::meta_item, mb: &@ast::meta_item) -> bool {
2012-07-18 18:18:02 -05:00
pure fn key(m: &ast::meta_item) -> ~str {
2012-08-06 14:34:08 -05:00
match m.node {
ast::meta_word(ref name) => (*name),
ast::meta_name_value(ref name, _) => (*name),
ast::meta_list(ref name, _) => (*name)
}
}
key(*ma) <= key(*mb)
}
// This is sort of stupid here, converting to a vec of mutables and back
let v: ~[mut @ast::meta_item] = vec::to_mut(items);
std::sort::quick_sort(v, lteq);
2012-09-10 20:28:00 -05:00
vec::from_mut(move v)
}
fn remove_meta_items_by_name(items: ~[@ast::meta_item], name: ~str) ->
~[@ast::meta_item] {
2012-08-01 19:30:05 -05:00
return vec::filter_map(items, |item| {
2012-09-28 00:20:47 -05:00
if get_meta_item_name(*item) != name {
option::Some(/* FIXME (#2543) */ copy *item)
} else {
2012-08-20 14:23:37 -05:00
option::None
}
});
}
/**
* From a list of crate attributes get only the meta_items that affect crate
* linkage
*/
fn find_linkage_metas(attrs: ~[ast::attribute]) -> ~[@ast::meta_item] {
do find_attrs_by_name(attrs, ~"link").flat_map |attr| {
match attr.node.value.node {
ast::meta_list(_, items) => /* FIXME (#2543) */ copy items,
_ => ~[]
}
}
}
2012-08-14 18:54:13 -05:00
fn foreign_abi(attrs: ~[ast::attribute]) -> Either<~str, ast::foreign_abi> {
2012-08-06 14:34:08 -05:00
return match attr::first_attr_value_str_by_name(attrs, ~"abi") {
2012-08-20 14:23:37 -05:00
option::None => {
2012-08-14 18:54:13 -05:00
either::Right(ast::foreign_abi_cdecl)
2011-11-20 12:15:40 -06:00
}
2012-08-20 14:23:37 -05:00
option::Some(~"rust-intrinsic") => {
2012-08-14 18:54:13 -05:00
either::Right(ast::foreign_abi_rust_intrinsic)
}
2012-08-20 14:23:37 -05:00
option::Some(~"cdecl") => {
2012-08-14 18:54:13 -05:00
either::Right(ast::foreign_abi_cdecl)
2011-11-20 12:15:40 -06:00
}
2012-08-20 14:23:37 -05:00
option::Some(~"stdcall") => {
2012-08-14 18:54:13 -05:00
either::Right(ast::foreign_abi_stdcall)
2011-11-20 12:15:40 -06:00
}
option::Some(ref t) => {
either::Left(~"unsupported abi: " + (*t))
2011-11-20 12:15:40 -06:00
}
};
}
enum inline_attr {
ia_none,
ia_hint,
ia_always,
ia_never,
}
impl inline_attr : cmp::Eq {
pure fn eq(&self, other: &inline_attr) -> bool {
((*self) as uint) == ((*other) as uint)
}
pure fn ne(&self, other: &inline_attr) -> bool { !(*self).eq(other) }
}
/// True if something like #[inline] is found in the list of attrs.
fn find_inline_attr(attrs: ~[ast::attribute]) -> inline_attr {
// FIXME (#2809)---validate the usage of #[inline] and #[inline(always)]
2012-06-30 18:19:07 -05:00
do vec::foldl(ia_none, attrs) |ia,attr| {
2012-08-06 14:34:08 -05:00
match attr.node.value.node {
2012-07-18 18:18:02 -05:00
ast::meta_word(~"inline") => ia_hint,
ast::meta_list(~"inline", items) => {
if !vec::is_empty(find_meta_items_by_name(items, ~"always")) {
ia_always
} else if !vec::is_empty(
find_meta_items_by_name(items, ~"never")) {
ia_never
} else {
ia_hint
}
}
2012-08-03 21:59:04 -05:00
_ => ia
}
}
}
fn require_unique_names(diagnostic: span_handler,
metas: ~[@ast::meta_item]) {
let map = map::HashMap();
2012-06-30 18:19:07 -05:00
for metas.each |meta| {
let name = get_meta_item_name(*meta);
2012-06-14 20:46:33 -05:00
// FIXME: How do I silence the warnings? --pcw (#2619)
2012-07-18 18:18:02 -05:00
if map.contains_key(name) {
diagnostic.span_fatal(meta.span,
2012-08-22 19:24:52 -05:00
fmt!("duplicate meta item `%s`", name));
}
2012-07-18 18:18:02 -05:00
map.insert(name, ());
}
}
//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//