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
16 use codemap
::{spanned, dummy_spanned}
;
19 use diagnostic
::span_handler
;
20 use parse
::comments
::{doc_comment_style, strip_doc_comment_decoration}
;
23 use core
::hashmap
::linear
::LinearSet
;
28 pub fn mk_name_value_item_str(name
: @
~str, value
: @
~str)
30 let value_lit
= dummy_spanned(ast
::lit_str(value
));
31 mk_name_value_item(name
, value_lit
)
34 pub fn mk_name_value_item(name
: @
~str, +value
: ast
::lit
)
36 @
dummy_spanned(ast
::meta_name_value(name
, value
))
39 pub fn mk_list_item(name
: @
~str, +items
: ~[@ast
::meta_item
]) ->
41 @
dummy_spanned(ast
::meta_list(name
, items
))
44 pub fn mk_word_item(name
: @
~str) -> @ast
::meta_item
{
45 @
dummy_spanned(ast
::meta_word(name
))
48 pub fn mk_attr(item
: @ast
::meta_item
) -> ast
::attribute
{
49 dummy_spanned(ast
::attribute_
{ style
: ast
::attr_inner
,
51 is_sugared_doc
: false })
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_
{
60 value
: @
spanned(lo
, hi
, ast
::meta_name_value(@
~"doc", lit
)),
68 pub fn attr_meta(attr
: ast
::attribute
) -> @ast
::meta_item
{
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) }
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
));
90 pub fn get_attr_name(attr
: &ast
::attribute
) -> @
~str {
91 get_meta_item_name(attr
.node
.value
)
94 pub fn get_meta_item_name(meta
: @ast
::meta_item
) -> @
~str {
96 ast
::meta_word(n
) => n
,
97 ast
::meta_name_value(n
, _
) => n
,
98 ast
::meta_list(n
, _
) => n
,
103 * Gets the string value if the meta_item is a meta_name_value variant
104 * containing a string, otherwise none
106 pub fn get_meta_item_value_str(meta
: @ast
::meta_item
) -> Option
<@
~str> {
108 ast
::meta_name_value(_
, v
) => {
110 ast
::lit_str(s
) => Some(s
),
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
]> {
122 ast
::meta_list(_
, ref l
) => Some(/* FIXME (#2543) */ copy
*l
),
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`
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
) {
135 let name
= attr
::get_meta_item_name(item
);
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) ->
148 do vec
::filter_mapped(attrs
) |a
| {
149 if name
== *get_attr_name(a
) {
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) ->
161 for metas
.each
|mi
| {
162 if name
== *get_meta_item_name(*mi
) {
170 * Returns true if a list of meta items contains another meta item. The
171 * comparison is performed structurally.
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; }
181 fn eq(a
: @ast
::meta_item
, b
: @ast
::meta_item
) -> bool
{
183 ast
::meta_word(ref na
) => match b
.node
{
184 ast
::meta_word(ref nb
) => (*na
) == (*nb
),
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
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; }
197 if !misb
.contains(mi
) { return false; }
206 pub fn contains_name(metas
: &[@ast
::meta_item
], name
: &str) -> bool
{
207 let matches
= find_meta_items_by_name(metas
, name
);
211 pub fn attrs_contains_name(attrs
: &[ast
::attribute
], name
: &str) -> bool
{
212 !find_attrs_by_name(attrs
, name
).is_empty()
215 pub fn first_attr_value_str_by_name(attrs
: &[ast
::attribute
], name
: &str)
218 let mattrs
= find_attrs_by_name(attrs
, name
);
219 if mattrs
.len() > 0 {
220 get_meta_item_value_str(attr_meta(mattrs
[0]))
226 fn last_meta_item_by_name(items
: &[@ast
::meta_item
], name
: &str)
227 -> Option
<@ast
::meta_item
> {
229 let items
= attr
::find_meta_items_by_name(items
, name
);
230 items
.last_opt().map(|item
| **item
)
233 pub fn last_meta_item_value_str_by_name(items
: &[@ast
::meta_item
], name
: &str)
236 match last_meta_item_by_name(items
, name
) {
238 match attr
::get_meta_item_value_str(item
) {
239 Some(value
) => Some(value
),
247 pub fn last_meta_item_list_by_name(items
: ~[@ast
::meta_item
], name
: &str)
248 -> Option
<~[@ast
::meta_item
]> {
250 match last_meta_item_by_name(items
, name
) {
251 Some(item
) => attr
::get_meta_item_list(item
),
257 /* Higher-level applications */
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
)
266 // There doesn't seem to be a more optimal way to do this
269 ast
::meta_list(n
, ref mis
) => {
271 node
: ast
::meta_list(n
, sort_meta_items(*mis
)),
280 pub fn remove_meta_items_by_name(items
: ~[@ast
::meta_item
], name
: &str) ->
283 return vec
::filter_mapped(items
, |item
| {
284 if name
!= *get_meta_item_name(*item
) {
293 * From a list of crate attributes get only the meta_items that affect crate
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
,
306 pub enum inline_attr
{
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() {
322 } else if !find_meta_items_by_name(*items
, ~"never").is_empty() {
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
);
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
));
352 // indent-tabs-mode: nil
354 // buffer-file-coding-system: utf-8-unix