1 // Copyright 2012-2014 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 ast::{AttrId, Attribute, Attribute_, MetaItem, MetaWord, MetaNameValue, MetaList};
15 use codemap::{Span, Spanned, spanned, dummy_spanned};
17 use diagnostic::SpanHandler;
18 use parse::comments::{doc_comment_style, strip_doc_comment_decoration};
19 use parse::token::InternedString;
23 use collections::HashSet;
25 local_data_key!(used_attrs: HashSet<AttrId>)
27 pub fn mark_used(attr: &Attribute) {
28 let mut used = used_attrs.replace(None).unwrap_or_else(|| HashSet::new());
29 used.insert(attr.node.id);
30 used_attrs.replace(Some(used));
33 pub fn is_used(attr: &Attribute) -> bool {
34 used_attrs.get().map_or(false, |used| used.contains(&attr.node.id))
37 pub trait AttrMetaMethods {
38 fn check_name(&self, name: &str) -> bool {
39 name == self.name().get()
42 /// Retrieve the name of the meta item, e.g. foo in #[foo],
43 /// #[foo="bar"] and #[foo(bar)]
44 fn name(&self) -> InternedString;
47 * Gets the string value if self is a MetaNameValue variant
48 * containing a string, otherwise None.
50 fn value_str(&self) -> Option<InternedString>;
51 /// Gets a list of inner meta items from a list MetaItem type.
52 fn meta_item_list<'a>(&'a self) -> Option<&'a [@MetaItem]>;
55 * If the meta item is a name-value type with a string value then returns
56 * a tuple containing the name and string value, otherwise `None`
58 fn name_str_pair(&self) -> Option<(InternedString,InternedString)>;
61 impl AttrMetaMethods for Attribute {
62 fn check_name(&self, name: &str) -> bool {
63 if name == self.name().get() {
70 fn name(&self) -> InternedString { self.meta().name() }
71 fn value_str(&self) -> Option<InternedString> {
72 self.meta().value_str()
74 fn meta_item_list<'a>(&'a self) -> Option<&'a [@MetaItem]> {
75 self.node.value.meta_item_list()
77 fn name_str_pair(&self) -> Option<(InternedString,InternedString)> {
78 self.meta().name_str_pair()
82 impl AttrMetaMethods for MetaItem {
83 fn name(&self) -> InternedString {
85 MetaWord(ref n) => (*n).clone(),
86 MetaNameValue(ref n, _) => (*n).clone(),
87 MetaList(ref n, _) => (*n).clone(),
91 fn value_str(&self) -> Option<InternedString> {
93 MetaNameValue(_, ref v) => {
95 ast::LitStr(ref s, _) => Some((*s).clone()),
103 fn meta_item_list<'a>(&'a self) -> Option<&'a [@MetaItem]> {
105 MetaList(_, ref l) => Some(l.as_slice()),
110 fn name_str_pair(&self) -> Option<(InternedString,InternedString)> {
111 self.value_str().map(|s| (self.name(), s))
115 // Annoying, but required to get test_cfg to work
116 impl AttrMetaMethods for @MetaItem {
117 fn name(&self) -> InternedString { (**self).name() }
118 fn value_str(&self) -> Option<InternedString> { (**self).value_str() }
119 fn meta_item_list<'a>(&'a self) -> Option<&'a [@MetaItem]> {
120 (**self).meta_item_list()
122 fn name_str_pair(&self) -> Option<(InternedString,InternedString)> {
123 (**self).name_str_pair()
128 pub trait AttributeMethods {
129 fn meta(&self) -> @MetaItem;
130 fn desugar_doc(&self) -> Attribute;
133 impl AttributeMethods for Attribute {
134 /// Extract the MetaItem from inside this Attribute.
135 fn meta(&self) -> @MetaItem {
139 /// Convert self to a normal #[doc="foo"] comment, if it is a
140 /// comment like `///` or `/** */`. (Returns self unchanged for
141 /// non-sugared doc attributes.)
142 fn desugar_doc(&self) -> Attribute {
143 if self.node.is_sugared_doc {
144 let comment = self.value_str().unwrap();
145 let meta = mk_name_value_item_str(
146 InternedString::new("doc"),
147 token::intern_and_get_ident(strip_doc_comment_decoration(
148 comment.get()).as_slice()));
149 if self.node.style == ast::AttrOuter {
150 mk_attr_outer(self.node.id, meta)
152 mk_attr_inner(self.node.id, meta)
162 pub fn mk_name_value_item_str(name: InternedString, value: InternedString)
164 let value_lit = dummy_spanned(ast::LitStr(value, ast::CookedStr));
165 mk_name_value_item(name, value_lit)
168 pub fn mk_name_value_item(name: InternedString, value: ast::Lit)
170 @dummy_spanned(MetaNameValue(name, value))
173 pub fn mk_list_item(name: InternedString, items: Vec<@MetaItem> ) -> @MetaItem {
174 @dummy_spanned(MetaList(name, items))
177 pub fn mk_word_item(name: InternedString) -> @MetaItem {
178 @dummy_spanned(MetaWord(name))
181 local_data_key!(next_attr_id: uint)
183 pub fn mk_attr_id() -> AttrId {
184 let id = next_attr_id.replace(None).unwrap_or(0);
185 next_attr_id.replace(Some(id + 1));
189 /// Returns an inner attribute with the given value.
190 pub fn mk_attr_inner(id: AttrId, item: @MetaItem) -> Attribute {
191 dummy_spanned(Attribute_ {
193 style: ast::AttrInner,
195 is_sugared_doc: false,
199 /// Returns an outer attribute with the given value.
200 pub fn mk_attr_outer(id: AttrId, item: @MetaItem) -> Attribute {
201 dummy_spanned(Attribute_ {
203 style: ast::AttrOuter,
205 is_sugared_doc: false,
209 pub fn mk_sugared_doc_attr(id: AttrId, text: InternedString, lo: BytePos,
212 let style = doc_comment_style(text.get());
213 let lit = spanned(lo, hi, ast::LitStr(text, ast::CookedStr));
214 let attr = Attribute_ {
217 value: @spanned(lo, hi, MetaNameValue(InternedString::new("doc"),
221 spanned(lo, hi, attr)
225 /// Check if `needle` occurs in `haystack` by a structural
226 /// comparison. This is slightly subtle, and relies on ignoring the
227 /// span included in the `==` comparison a plain MetaItem.
228 pub fn contains(haystack: &[@ast::MetaItem],
229 needle: @ast::MetaItem) -> bool {
230 debug!("attr::contains (name={})", needle.name());
231 haystack.iter().any(|item| {
232 debug!(" testing: {}", item.name());
233 item.node == needle.node
237 pub fn contains_name<AM: AttrMetaMethods>(metas: &[AM], name: &str) -> bool {
238 debug!("attr::contains_name (name={})", name);
239 metas.iter().any(|item| {
240 debug!(" testing: {}", item.name());
241 item.check_name(name)
245 pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: &str)
246 -> Option<InternedString> {
248 .find(|at| at.check_name(name))
249 .and_then(|at| at.value_str())
252 pub fn last_meta_item_value_str_by_name(items: &[@MetaItem], name: &str)
253 -> Option<InternedString> {
256 .find(|mi| mi.check_name(name))
257 .and_then(|i| i.value_str())
260 /* Higher-level applications */
262 pub fn sort_meta_items(items: &[@MetaItem]) -> Vec<@MetaItem> {
263 // This is sort of stupid here, but we need to sort by
264 // human-readable strings.
265 let mut v = items.iter()
266 .map(|&mi| (mi.name(), mi))
267 .collect::<Vec<(InternedString, @MetaItem)> >();
269 v.sort_by(|&(ref a, _), &(ref b, _)| a.cmp(b));
271 // There doesn't seem to be a more optimal way to do this
272 v.move_iter().map(|(_, m)| {
274 MetaList(ref n, ref mis) => {
276 node: MetaList((*n).clone(),
277 sort_meta_items(mis.as_slice())),
278 .. /*bad*/ (*m).clone()
287 * From a list of crate attributes get only the meta_items that affect crate
290 pub fn find_linkage_metas(attrs: &[Attribute]) -> Vec<@MetaItem> {
291 let mut result = Vec::new();
292 for attr in attrs.iter().filter(|at| at.check_name("link")) {
293 match attr.meta().node {
294 MetaList(_, ref items) => result.push_all(items.as_slice()),
301 pub fn find_crateid(attrs: &[Attribute]) -> Option<CrateId> {
302 match first_attr_value_str_by_name(attrs, "crate_id") {
304 Some(id) => from_str::<CrateId>(id.get()),
309 pub enum InlineAttr {
316 /// True if something like #[inline] is found in the list of attrs.
317 pub fn find_inline_attr(attrs: &[Attribute]) -> InlineAttr {
318 // FIXME (#2809)---validate the usage of #[inline] and #[inline]
319 attrs.iter().fold(InlineNone, |ia,attr| {
320 match attr.node.value.node {
321 MetaWord(ref n) if n.equiv(&("inline")) => {
325 MetaList(ref n, ref items) if n.equiv(&("inline")) => {
327 if contains_name(items.as_slice(), "always") {
329 } else if contains_name(items.as_slice(), "never") {
340 /// Tests if any `cfg(...)` meta items in `metas` match `cfg`. e.g.
342 /// test_cfg(`[foo="a", bar]`, `[cfg(foo), cfg(bar)]`) == true
343 /// test_cfg(`[foo="a", bar]`, `[cfg(not(bar))]`) == false
344 /// test_cfg(`[foo="a", bar]`, `[cfg(bar, foo="a")]`) == true
345 /// test_cfg(`[foo="a", bar]`, `[cfg(bar, foo="b")]`) == false
346 pub fn test_cfg<AM: AttrMetaMethods, It: Iterator<AM>>
347 (cfg: &[@MetaItem], mut metas: It) -> bool {
348 // having no #[cfg(...)] attributes counts as matching.
349 let mut no_cfgs = true;
351 // this would be much nicer as a chain of iterator adaptors, but
352 // this doesn't work.
353 let some_cfg_matches = metas.any(|mi| {
354 debug!("testing name: {}", mi.name());
355 if mi.check_name("cfg") { // it is a #[cfg()] attribute
358 // only #[cfg(...)] ones are understood.
359 match mi.meta_item_list() {
361 debug!("is cfg(...)");
362 cfg_meta.iter().all(|cfg_mi| {
363 debug!("cfg({}[...])", cfg_mi.name());
365 ast::MetaList(ref s, ref not_cfgs)
366 if s.equiv(&("not")) => {
368 // inside #[cfg(not(...))], so these need to all
370 !not_cfgs.iter().all(|mi| {
371 debug!("cfg(not({}[...]))", mi.name());
375 _ => contains(cfg, *cfg_mi)
385 debug!("test_cfg (no_cfgs={}, some_cfg_matches={})", no_cfgs, some_cfg_matches);
386 no_cfgs || some_cfg_matches
389 /// Represents the #[deprecated="foo"] and friends attributes.
390 pub struct Stability {
391 pub level: StabilityLevel,
392 pub text: Option<InternedString>
395 /// The available stability levels.
396 #[deriving(Eq,Ord,Clone,Show)]
397 pub enum StabilityLevel {
406 pub fn find_stability_generic<'a,
410 -> Option<(Stability, &'a AM)> {
412 let level = match attr.name().get() {
413 "deprecated" => Deprecated,
414 "experimental" => Experimental,
415 "unstable" => Unstable,
419 _ => continue // not a stability level
422 return Some((Stability {
424 text: attr.value_str()
430 /// Find the first stability attribute. `None` if none exists.
431 pub fn find_stability(attrs: &[Attribute]) -> Option<Stability> {
432 find_stability_generic(attrs.iter()).map(|(s, attr)| {
438 pub fn require_unique_names(diagnostic: &SpanHandler, metas: &[@MetaItem]) {
439 let mut set = HashSet::new();
440 for meta in metas.iter() {
441 let name = meta.name();
443 if !set.insert(name.clone()) {
444 diagnostic.span_fatal(meta.span,
445 format!("duplicate meta item `{}`",
453 * Fold this over attributes to parse #[repr(...)] forms.
455 * Valid repr contents: any of the primitive integral type names (see
456 * `int_type_of_word`, below) to specify the discriminant type; and `C`, to use
457 * the same discriminant size that the corresponding C enum would. These are
458 * not allowed on univariant or zero-variant enums, which have no discriminant.
460 * If a discriminant type is so specified, then the discriminant will be
461 * present (before fields, if any) with that type; reprensentation
462 * optimizations which would remove it will not be done.
464 pub fn find_repr_attr(diagnostic: &SpanHandler, attr: &Attribute, acc: ReprAttr)
467 info!("{}", ::print::pprust::attribute_to_str(attr));
468 match attr.node.value.node {
469 ast::MetaList(ref s, ref items) if s.equiv(&("repr")) => {
471 for item in items.iter() {
473 ast::MetaWord(ref word) => {
474 let hint = match word.get() {
475 // Can't use "extern" because it's not a lexical identifier.
477 _ => match int_type_of_word(word.get()) {
478 Some(ity) => ReprInt(item.span, ity),
480 // Not a word we recognize
481 diagnostic.span_err(item.span,
482 "unrecognized representation hint");
490 } else if acc != hint {
491 diagnostic.span_warn(item.span,
492 "conflicting representation hint ignored")
497 _ => diagnostic.span_err(item.span, "unrecognized representation hint")
501 // Not a "repr" hint: ignore.
507 fn int_type_of_word(s: &str) -> Option<IntType> {
509 "i8" => Some(SignedInt(ast::TyI8)),
510 "u8" => Some(UnsignedInt(ast::TyU8)),
511 "i16" => Some(SignedInt(ast::TyI16)),
512 "u16" => Some(UnsignedInt(ast::TyU16)),
513 "i32" => Some(SignedInt(ast::TyI32)),
514 "u32" => Some(UnsignedInt(ast::TyU32)),
515 "i64" => Some(SignedInt(ast::TyI64)),
516 "u64" => Some(UnsignedInt(ast::TyU64)),
517 "int" => Some(SignedInt(ast::TyI)),
518 "uint" => Some(UnsignedInt(ast::TyU)),
523 #[deriving(Eq, Show)]
526 ReprInt(Span, IntType),
531 pub fn is_ffi_safe(&self) -> bool {
534 ReprInt(_sp, ity) => ity.is_ffi_safe(),
540 #[deriving(Eq, Show)]
542 SignedInt(ast::IntTy),
543 UnsignedInt(ast::UintTy)
548 pub fn is_signed(self) -> bool {
550 SignedInt(..) => true,
551 UnsignedInt(..) => false
554 fn is_ffi_safe(self) -> bool {
556 SignedInt(ast::TyI8) | UnsignedInt(ast::TyU8) |
557 SignedInt(ast::TyI16) | UnsignedInt(ast::TyU16) |
558 SignedInt(ast::TyI32) | UnsignedInt(ast::TyU32) |
559 SignedInt(ast::TyI64) | UnsignedInt(ast::TyU64) => true,