]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/attr.rs
rollup merge of #19998: th0114nd/unicode-bottom
[rust.git] / src / libsyntax / attr.rs
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.
4 //
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.
10
11 // Functions dealing with attributes and meta items
12
13 pub use self::InlineAttr::*;
14 pub use self::StabilityLevel::*;
15 pub use self::ReprAttr::*;
16 pub use self::IntType::*;
17
18 use ast;
19 use ast::{AttrId, Attribute, Attribute_, MetaItem, MetaWord, MetaNameValue, MetaList};
20 use codemap::{Span, Spanned, spanned, dummy_spanned};
21 use codemap::BytePos;
22 use diagnostic::SpanHandler;
23 use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration};
24 use parse::token::InternedString;
25 use parse::token;
26 use ptr::P;
27
28 use std::cell::{RefCell, Cell};
29 use std::collections::BitvSet;
30 use std::collections::HashSet;
31
32 thread_local! { static USED_ATTRS: RefCell<BitvSet> = RefCell::new(BitvSet::new()) }
33
34 pub fn mark_used(attr: &Attribute) {
35     let AttrId(id) = attr.node.id;
36     USED_ATTRS.with(|slot| slot.borrow_mut().insert(id));
37 }
38
39 pub fn is_used(attr: &Attribute) -> bool {
40     let AttrId(id) = attr.node.id;
41     USED_ATTRS.with(|slot| slot.borrow().contains(&id))
42 }
43
44 pub trait AttrMetaMethods {
45     fn check_name(&self, name: &str) -> bool {
46         name == self.name().get()
47     }
48
49     /// Retrieve the name of the meta item, e.g. `foo` in `#[foo]`,
50     /// `#[foo="bar"]` and `#[foo(bar)]`
51     fn name(&self) -> InternedString;
52
53     /// Gets the string value if self is a MetaNameValue variant
54     /// containing a string, otherwise None.
55     fn value_str(&self) -> Option<InternedString>;
56     /// Gets a list of inner meta items from a list MetaItem type.
57     fn meta_item_list<'a>(&'a self) -> Option<&'a [P<MetaItem>]>;
58 }
59
60 impl AttrMetaMethods for Attribute {
61     fn check_name(&self, name: &str) -> bool {
62         let matches = name == self.name().get();
63         if matches {
64             mark_used(self);
65         }
66         matches
67     }
68     fn name(&self) -> InternedString { self.meta().name() }
69     fn value_str(&self) -> Option<InternedString> {
70         self.meta().value_str()
71     }
72     fn meta_item_list<'a>(&'a self) -> Option<&'a [P<MetaItem>]> {
73         self.node.value.meta_item_list()
74     }
75 }
76
77 impl AttrMetaMethods for MetaItem {
78     fn name(&self) -> InternedString {
79         match self.node {
80             MetaWord(ref n) => (*n).clone(),
81             MetaNameValue(ref n, _) => (*n).clone(),
82             MetaList(ref n, _) => (*n).clone(),
83         }
84     }
85
86     fn value_str(&self) -> Option<InternedString> {
87         match self.node {
88             MetaNameValue(_, ref v) => {
89                 match v.node {
90                     ast::LitStr(ref s, _) => Some((*s).clone()),
91                     _ => None,
92                 }
93             },
94             _ => None
95         }
96     }
97
98     fn meta_item_list<'a>(&'a self) -> Option<&'a [P<MetaItem>]> {
99         match self.node {
100             MetaList(_, ref l) => Some(l[]),
101             _ => None
102         }
103     }
104 }
105
106 // Annoying, but required to get test_cfg to work
107 impl AttrMetaMethods for P<MetaItem> {
108     fn name(&self) -> InternedString { (**self).name() }
109     fn value_str(&self) -> Option<InternedString> { (**self).value_str() }
110     fn meta_item_list<'a>(&'a self) -> Option<&'a [P<MetaItem>]> {
111         (**self).meta_item_list()
112     }
113 }
114
115
116 pub trait AttributeMethods {
117     fn meta<'a>(&'a self) -> &'a MetaItem;
118     fn with_desugared_doc<T, F>(&self, f: F) -> T where
119         F: FnOnce(&Attribute) -> T;
120 }
121
122 impl AttributeMethods for Attribute {
123     /// Extract the MetaItem from inside this Attribute.
124     fn meta<'a>(&'a self) -> &'a MetaItem {
125         &*self.node.value
126     }
127
128     /// Convert self to a normal #[doc="foo"] comment, if it is a
129     /// comment like `///` or `/** */`. (Returns self unchanged for
130     /// non-sugared doc attributes.)
131     fn with_desugared_doc<T, F>(&self, f: F) -> T where
132         F: FnOnce(&Attribute) -> T,
133     {
134         if self.node.is_sugared_doc {
135             let comment = self.value_str().unwrap();
136             let meta = mk_name_value_item_str(
137                 InternedString::new("doc"),
138                 token::intern_and_get_ident(strip_doc_comment_decoration(
139                         comment.get())[]));
140             if self.node.style == ast::AttrOuter {
141                 f(&mk_attr_outer(self.node.id, meta))
142             } else {
143                 f(&mk_attr_inner(self.node.id, meta))
144             }
145         } else {
146             f(self)
147         }
148     }
149 }
150
151 /* Constructors */
152
153 pub fn mk_name_value_item_str(name: InternedString, value: InternedString)
154                               -> P<MetaItem> {
155     let value_lit = dummy_spanned(ast::LitStr(value, ast::CookedStr));
156     mk_name_value_item(name, value_lit)
157 }
158
159 pub fn mk_name_value_item(name: InternedString, value: ast::Lit)
160                           -> P<MetaItem> {
161     P(dummy_spanned(MetaNameValue(name, value)))
162 }
163
164 pub fn mk_list_item(name: InternedString, items: Vec<P<MetaItem>>) -> P<MetaItem> {
165     P(dummy_spanned(MetaList(name, items)))
166 }
167
168 pub fn mk_word_item(name: InternedString) -> P<MetaItem> {
169     P(dummy_spanned(MetaWord(name)))
170 }
171
172 thread_local! { static NEXT_ATTR_ID: Cell<uint> = Cell::new(0) }
173
174 pub fn mk_attr_id() -> AttrId {
175     let id = NEXT_ATTR_ID.with(|slot| {
176         let r = slot.get();
177         slot.set(r + 1);
178         r
179     });
180     AttrId(id)
181 }
182
183 /// Returns an inner attribute with the given value.
184 pub fn mk_attr_inner(id: AttrId, item: P<MetaItem>) -> Attribute {
185     dummy_spanned(Attribute_ {
186         id: id,
187         style: ast::AttrInner,
188         value: item,
189         is_sugared_doc: false,
190     })
191 }
192
193 /// Returns an outer attribute with the given value.
194 pub fn mk_attr_outer(id: AttrId, item: P<MetaItem>) -> Attribute {
195     dummy_spanned(Attribute_ {
196         id: id,
197         style: ast::AttrOuter,
198         value: item,
199         is_sugared_doc: false,
200     })
201 }
202
203 pub fn mk_sugared_doc_attr(id: AttrId, text: InternedString, lo: BytePos,
204                            hi: BytePos)
205                            -> Attribute {
206     let style = doc_comment_style(text.get());
207     let lit = spanned(lo, hi, ast::LitStr(text, ast::CookedStr));
208     let attr = Attribute_ {
209         id: id,
210         style: style,
211         value: P(spanned(lo, hi, MetaNameValue(InternedString::new("doc"),
212                                                lit))),
213         is_sugared_doc: true
214     };
215     spanned(lo, hi, attr)
216 }
217
218 /* Searching */
219 /// Check if `needle` occurs in `haystack` by a structural
220 /// comparison. This is slightly subtle, and relies on ignoring the
221 /// span included in the `==` comparison a plain MetaItem.
222 pub fn contains(haystack: &[P<MetaItem>], needle: &MetaItem) -> bool {
223     debug!("attr::contains (name={})", needle.name());
224     haystack.iter().any(|item| {
225         debug!("  testing: {}", item.name());
226         item.node == needle.node
227     })
228 }
229
230 pub fn contains_name<AM: AttrMetaMethods>(metas: &[AM], name: &str) -> bool {
231     debug!("attr::contains_name (name={})", name);
232     metas.iter().any(|item| {
233         debug!("  testing: {}", item.name());
234         item.check_name(name)
235     })
236 }
237
238 pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: &str)
239                                  -> Option<InternedString> {
240     attrs.iter()
241         .find(|at| at.check_name(name))
242         .and_then(|at| at.value_str())
243 }
244
245 pub fn last_meta_item_value_str_by_name(items: &[P<MetaItem>], name: &str)
246                                      -> Option<InternedString> {
247     items.iter()
248          .rev()
249          .find(|mi| mi.check_name(name))
250          .and_then(|i| i.value_str())
251 }
252
253 /* Higher-level applications */
254
255 pub fn sort_meta_items(items: Vec<P<MetaItem>>) -> Vec<P<MetaItem>> {
256     // This is sort of stupid here, but we need to sort by
257     // human-readable strings.
258     let mut v = items.into_iter()
259         .map(|mi| (mi.name(), mi))
260         .collect::<Vec<(InternedString, P<MetaItem>)>>();
261
262     v.sort_by(|&(ref a, _), &(ref b, _)| a.cmp(b));
263
264     // There doesn't seem to be a more optimal way to do this
265     v.into_iter().map(|(_, m)| m.map(|Spanned {node, span}| {
266         Spanned {
267             node: match node {
268                 MetaList(n, mis) => MetaList(n, sort_meta_items(mis)),
269                 _ => node
270             },
271             span: span
272         }
273     })).collect()
274 }
275
276 pub fn find_crate_name(attrs: &[Attribute]) -> Option<InternedString> {
277     first_attr_value_str_by_name(attrs, "crate_name")
278 }
279
280 #[derive(Copy, PartialEq)]
281 pub enum InlineAttr {
282     InlineNone,
283     InlineHint,
284     InlineAlways,
285     InlineNever,
286 }
287
288 /// Determine what `#[inline]` attribute is present in `attrs`, if any.
289 pub fn find_inline_attr(attrs: &[Attribute]) -> InlineAttr {
290     // FIXME (#2809)---validate the usage of #[inline] and #[inline]
291     attrs.iter().fold(InlineNone, |ia,attr| {
292         match attr.node.value.node {
293             MetaWord(ref n) if *n == "inline" => {
294                 mark_used(attr);
295                 InlineHint
296             }
297             MetaList(ref n, ref items) if *n == "inline" => {
298                 mark_used(attr);
299                 if contains_name(items[], "always") {
300                     InlineAlways
301                 } else if contains_name(items[], "never") {
302                     InlineNever
303                 } else {
304                     InlineHint
305                 }
306             }
307             _ => ia
308         }
309     })
310 }
311
312 /// True if `#[inline]` or `#[inline(always)]` is present in `attrs`.
313 pub fn requests_inline(attrs: &[Attribute]) -> bool {
314     match find_inline_attr(attrs) {
315         InlineHint | InlineAlways => true,
316         InlineNone | InlineNever => false,
317     }
318 }
319
320 /// Tests if a cfg-pattern matches the cfg set
321 pub fn cfg_matches(diagnostic: &SpanHandler, cfgs: &[P<MetaItem>], cfg: &ast::MetaItem) -> bool {
322     match cfg.node {
323         ast::MetaList(ref pred, ref mis) if pred.get() == "any" =>
324             mis.iter().any(|mi| cfg_matches(diagnostic, cfgs, &**mi)),
325         ast::MetaList(ref pred, ref mis) if pred.get() == "all" =>
326             mis.iter().all(|mi| cfg_matches(diagnostic, cfgs, &**mi)),
327         ast::MetaList(ref pred, ref mis) if pred.get() == "not" => {
328             if mis.len() != 1 {
329                 diagnostic.span_err(cfg.span, "expected 1 cfg-pattern");
330                 return false;
331             }
332             !cfg_matches(diagnostic, cfgs, &*mis[0])
333         }
334         ast::MetaList(ref pred, _) => {
335             diagnostic.span_err(cfg.span, format!("invalid predicate `{}`", pred)[]);
336             false
337         },
338         ast::MetaWord(_) | ast::MetaNameValue(..) => contains(cfgs, cfg),
339     }
340 }
341
342 /// Represents the #[deprecated="foo"] and friends attributes.
343 #[derive(RustcEncodable,RustcDecodable,Clone,Show)]
344 pub struct Stability {
345     pub level: StabilityLevel,
346     pub text: Option<InternedString>
347 }
348
349 /// The available stability levels.
350 #[derive(RustcEncodable,RustcDecodable,PartialEq,PartialOrd,Clone,Show,Copy)]
351 pub enum StabilityLevel {
352     Deprecated,
353     Experimental,
354     Unstable,
355     Stable,
356     Frozen,
357     Locked
358 }
359
360 pub fn find_stability_generic<'a,
361                               AM: AttrMetaMethods,
362                               I: Iterator<Item=&'a AM>>
363                              (mut attrs: I)
364                              -> Option<(Stability, &'a AM)> {
365     for attr in attrs {
366         let level = match attr.name().get() {
367             "deprecated" => Deprecated,
368             "experimental" => Experimental,
369             "unstable" => Unstable,
370             "stable" => Stable,
371             "frozen" => Frozen,
372             "locked" => Locked,
373             _ => continue // not a stability level
374         };
375
376         return Some((Stability {
377             level: level,
378                 text: attr.value_str()
379             }, attr));
380     }
381     None
382 }
383
384 /// Find the first stability attribute. `None` if none exists.
385 pub fn find_stability(attrs: &[Attribute]) -> Option<Stability> {
386     find_stability_generic(attrs.iter()).map(|(s, attr)| {
387         mark_used(attr);
388         s
389     })
390 }
391
392 pub fn require_unique_names(diagnostic: &SpanHandler, metas: &[P<MetaItem>]) {
393     let mut set = HashSet::new();
394     for meta in metas.iter() {
395         let name = meta.name();
396
397         if !set.insert(name.clone()) {
398             diagnostic.span_fatal(meta.span,
399                                   format!("duplicate meta item `{}`", name)[]);
400         }
401     }
402 }
403
404
405 /// Parse #[repr(...)] forms.
406 ///
407 /// Valid repr contents: any of the primitive integral type names (see
408 /// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
409 /// the same discriminant size that the corresponding C enum would or C
410 /// structure layout, and `packed` to remove padding.
411 pub fn find_repr_attrs(diagnostic: &SpanHandler, attr: &Attribute) -> Vec<ReprAttr> {
412     let mut acc = Vec::new();
413     match attr.node.value.node {
414         ast::MetaList(ref s, ref items) if *s == "repr" => {
415             mark_used(attr);
416             for item in items.iter() {
417                 match item.node {
418                     ast::MetaWord(ref word) => {
419                         let hint = match word.get() {
420                             // Can't use "extern" because it's not a lexical identifier.
421                             "C" => Some(ReprExtern),
422                             "packed" => Some(ReprPacked),
423                             _ => match int_type_of_word(word.get()) {
424                                 Some(ity) => Some(ReprInt(item.span, ity)),
425                                 None => {
426                                     // Not a word we recognize
427                                     diagnostic.span_err(item.span,
428                                                         "unrecognized representation hint");
429                                     None
430                                 }
431                             }
432                         };
433
434                         match hint {
435                             Some(h) => acc.push(h),
436                             None => { }
437                         }
438                     }
439                     // Not a word:
440                     _ => diagnostic.span_err(item.span, "unrecognized enum representation hint")
441                 }
442             }
443         }
444         // Not a "repr" hint: ignore.
445         _ => { }
446     }
447     acc
448 }
449
450 fn int_type_of_word(s: &str) -> Option<IntType> {
451     match s {
452         "i8" => Some(SignedInt(ast::TyI8)),
453         "u8" => Some(UnsignedInt(ast::TyU8)),
454         "i16" => Some(SignedInt(ast::TyI16)),
455         "u16" => Some(UnsignedInt(ast::TyU16)),
456         "i32" => Some(SignedInt(ast::TyI32)),
457         "u32" => Some(UnsignedInt(ast::TyU32)),
458         "i64" => Some(SignedInt(ast::TyI64)),
459         "u64" => Some(UnsignedInt(ast::TyU64)),
460         "int" => Some(SignedInt(ast::TyI)),
461         "uint" => Some(UnsignedInt(ast::TyU)),
462         _ => None
463     }
464 }
465
466 #[derive(PartialEq, Show, RustcEncodable, RustcDecodable, Copy)]
467 pub enum ReprAttr {
468     ReprAny,
469     ReprInt(Span, IntType),
470     ReprExtern,
471     ReprPacked,
472 }
473
474 impl ReprAttr {
475     pub fn is_ffi_safe(&self) -> bool {
476         match *self {
477             ReprAny => false,
478             ReprInt(_sp, ity) => ity.is_ffi_safe(),
479             ReprExtern => true,
480             ReprPacked => false
481         }
482     }
483 }
484
485 #[derive(Eq, Hash, PartialEq, Show, RustcEncodable, RustcDecodable, Copy)]
486 pub enum IntType {
487     SignedInt(ast::IntTy),
488     UnsignedInt(ast::UintTy)
489 }
490
491 impl IntType {
492     #[inline]
493     pub fn is_signed(self) -> bool {
494         match self {
495             SignedInt(..) => true,
496             UnsignedInt(..) => false
497         }
498     }
499     fn is_ffi_safe(self) -> bool {
500         match self {
501             SignedInt(ast::TyI8) | UnsignedInt(ast::TyU8) |
502             SignedInt(ast::TyI16) | UnsignedInt(ast::TyU16) |
503             SignedInt(ast::TyI32) | UnsignedInt(ast::TyU32) |
504             SignedInt(ast::TyI64) | UnsignedInt(ast::TyU64) => true,
505             SignedInt(ast::TyI) | UnsignedInt(ast::TyU) => false
506         }
507     }
508 }