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
13 pub use self::StabilityLevel
::*;
14 pub use self::ReprAttr
::*;
15 pub use self::IntType
::*;
18 use ast
::{AttrId, Attribute, Attribute_, MetaItem, MetaWord, MetaNameValue, MetaList}
;
19 use codemap
::{Span, Spanned, spanned, dummy_spanned}
;
21 use diagnostic
::SpanHandler
;
22 use parse
::lexer
::comments
::{doc_comment_style, strip_doc_comment_decoration}
;
23 use parse
::token
::{InternedString, intern_and_get_ident}
;
27 use std
::cell
::{RefCell, Cell}
;
28 use std
::collections
::BitSet
;
29 use std
::collections
::HashSet
;
32 thread_local
! { static USED_ATTRS: RefCell<BitSet> = RefCell::new(BitSet::new()) }
34 pub fn mark_used(attr
: &Attribute
) {
35 let AttrId(id
) = attr
.node
.id
;
36 USED_ATTRS
.with(|slot
| slot
.borrow_mut().insert(id
));
39 pub fn is_used(attr
: &Attribute
) -> bool
{
40 let AttrId(id
) = attr
.node
.id
;
41 USED_ATTRS
.with(|slot
| slot
.borrow().contains(&id
))
44 pub trait AttrMetaMethods
{
45 fn check_name(&self, name
: &str) -> bool
{
46 name
== &self.name()[..]
49 /// Retrieve the name of the meta item, e.g. `foo` in `#[foo]`,
50 /// `#[foo="bar"]` and `#[foo(bar)]`
51 fn name(&self) -> InternedString
;
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
>]>;
59 fn span(&self) -> Span
;
62 impl AttrMetaMethods
for Attribute
{
63 fn check_name(&self, name
: &str) -> bool
{
64 let matches
= name
== &self.name()[..];
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
[P
<MetaItem
>]> {
75 self.node
.value
.meta_item_list()
77 fn span(&self) -> Span { self.meta().span }
80 impl AttrMetaMethods
for MetaItem
{
81 fn name(&self) -> InternedString
{
83 MetaWord(ref n
) => (*n
).clone(),
84 MetaNameValue(ref n
, _
) => (*n
).clone(),
85 MetaList(ref n
, _
) => (*n
).clone(),
89 fn value_str(&self) -> Option
<InternedString
> {
91 MetaNameValue(_
, ref v
) => {
93 ast
::LitStr(ref s
, _
) => Some((*s
).clone()),
101 fn meta_item_list
<'a
>(&'a
self) -> Option
<&'a
[P
<MetaItem
>]> {
103 MetaList(_
, ref l
) => Some(&l
[..]),
107 fn span(&self) -> Span { self.span }
110 // Annoying, but required to get test_cfg to work
111 impl AttrMetaMethods
for P
<MetaItem
> {
112 fn name(&self) -> InternedString { (**self).name() }
113 fn value_str(&self) -> Option
<InternedString
> { (**self).value_str() }
114 fn meta_item_list
<'a
>(&'a
self) -> Option
<&'a
[P
<MetaItem
>]> {
115 (**self).meta_item_list()
117 fn span(&self) -> Span { (**self).span() }
121 pub trait AttributeMethods
{
122 fn meta
<'a
>(&'a
self) -> &'a MetaItem
;
123 fn with_desugared_doc
<T
, F
>(&self, f
: F
) -> T
where
124 F
: FnOnce(&Attribute
) -> T
;
127 impl AttributeMethods
for Attribute
{
128 /// Extract the MetaItem from inside this Attribute.
129 fn meta
<'a
>(&'a
self) -> &'a MetaItem
{
133 /// Convert self to a normal #[doc="foo"] comment, if it is a
134 /// comment like `///` or `/** */`. (Returns self unchanged for
135 /// non-sugared doc attributes.)
136 fn with_desugared_doc
<T
, F
>(&self, f
: F
) -> T
where
137 F
: FnOnce(&Attribute
) -> T
,
139 if self.node
.is_sugared_doc
{
140 let comment
= self.value_str().unwrap();
141 let meta
= mk_name_value_item_str(
142 InternedString
::new("doc"),
143 token
::intern_and_get_ident(&strip_doc_comment_decoration(
145 if self.node
.style
== ast
::AttrOuter
{
146 f(&mk_attr_outer(self.node
.id
, meta
))
148 f(&mk_attr_inner(self.node
.id
, meta
))
158 pub fn mk_name_value_item_str(name
: InternedString
, value
: InternedString
)
160 let value_lit
= dummy_spanned(ast
::LitStr(value
, ast
::CookedStr
));
161 mk_name_value_item(name
, value_lit
)
164 pub fn mk_name_value_item(name
: InternedString
, value
: ast
::Lit
)
166 P(dummy_spanned(MetaNameValue(name
, value
)))
169 pub fn mk_list_item(name
: InternedString
, items
: Vec
<P
<MetaItem
>>) -> P
<MetaItem
> {
170 P(dummy_spanned(MetaList(name
, items
)))
173 pub fn mk_word_item(name
: InternedString
) -> P
<MetaItem
> {
174 P(dummy_spanned(MetaWord(name
)))
177 thread_local
! { static NEXT_ATTR_ID: Cell<usize> = Cell::new(0) }
179 pub fn mk_attr_id() -> AttrId
{
180 let id
= NEXT_ATTR_ID
.with(|slot
| {
188 /// Returns an inner attribute with the given value.
189 pub fn mk_attr_inner(id
: AttrId
, item
: P
<MetaItem
>) -> Attribute
{
190 dummy_spanned(Attribute_
{
192 style
: ast
::AttrInner
,
194 is_sugared_doc
: false,
198 /// Returns an outer attribute with the given value.
199 pub fn mk_attr_outer(id
: AttrId
, item
: P
<MetaItem
>) -> Attribute
{
200 dummy_spanned(Attribute_
{
202 style
: ast
::AttrOuter
,
204 is_sugared_doc
: false,
208 pub fn mk_sugared_doc_attr(id
: AttrId
, text
: InternedString
, lo
: BytePos
,
211 let style
= doc_comment_style(&text
);
212 let lit
= spanned(lo
, hi
, ast
::LitStr(text
, ast
::CookedStr
));
213 let attr
= Attribute_
{
216 value
: P(spanned(lo
, hi
, MetaNameValue(InternedString
::new("doc"),
220 spanned(lo
, hi
, attr
)
224 /// Check if `needle` occurs in `haystack` by a structural
225 /// comparison. This is slightly subtle, and relies on ignoring the
226 /// span included in the `==` comparison a plain MetaItem.
227 pub fn contains(haystack
: &[P
<MetaItem
>], needle
: &MetaItem
) -> bool
{
228 debug
!("attr::contains (name={})", needle
.name());
229 haystack
.iter().any(|item
| {
230 debug
!(" testing: {}", item
.name());
231 item
.node
== needle
.node
235 pub fn contains_name
<AM
: AttrMetaMethods
>(metas
: &[AM
], name
: &str) -> bool
{
236 debug
!("attr::contains_name (name={})", name
);
237 metas
.iter().any(|item
| {
238 debug
!(" testing: {}", item
.name());
239 item
.check_name(name
)
243 pub fn first_attr_value_str_by_name(attrs
: &[Attribute
], name
: &str)
244 -> Option
<InternedString
> {
246 .find(|at
| at
.check_name(name
))
247 .and_then(|at
| at
.value_str())
250 pub fn last_meta_item_value_str_by_name(items
: &[P
<MetaItem
>], name
: &str)
251 -> Option
<InternedString
> {
254 .find(|mi
| mi
.check_name(name
))
255 .and_then(|i
| i
.value_str())
258 /* Higher-level applications */
260 pub fn sort_meta_items(items
: Vec
<P
<MetaItem
>>) -> Vec
<P
<MetaItem
>> {
261 // This is sort of stupid here, but we need to sort by
262 // human-readable strings.
263 let mut v
= items
.into_iter()
264 .map(|mi
| (mi
.name(), mi
))
265 .collect
::<Vec
<(InternedString
, P
<MetaItem
>)>>();
267 v
.sort_by(|&(ref a
, _
), &(ref b
, _
)| a
.cmp(b
));
269 // There doesn't seem to be a more optimal way to do this
270 v
.into_iter().map(|(_
, m
)| m
.map(|Spanned {node, span}
| {
273 MetaList(n
, mis
) => MetaList(n
, sort_meta_items(mis
)),
281 pub fn find_crate_name(attrs
: &[Attribute
]) -> Option
<InternedString
> {
282 first_attr_value_str_by_name(attrs
, "crate_name")
285 #[derive(Copy, Clone, PartialEq)]
286 pub enum InlineAttr
{
293 /// Determine what `#[inline]` attribute is present in `attrs`, if any.
294 pub fn find_inline_attr(diagnostic
: Option
<&SpanHandler
>, attrs
: &[Attribute
]) -> InlineAttr
{
295 // FIXME (#2809)---validate the usage of #[inline] and #[inline]
296 attrs
.iter().fold(InlineAttr
::None
, |ia
,attr
| {
297 match attr
.node
.value
.node
{
298 MetaWord(ref n
) if *n
== "inline" => {
302 MetaList(ref n
, ref items
) if *n
== "inline" => {
304 if items
.len() != 1 {
305 diagnostic
.map(|d
|{ d.span_err(attr.span, "expected one argument"); }
);
307 } else if contains_name(&items
[..], "always") {
309 } else if contains_name(&items
[..], "never") {
312 diagnostic
.map(|d
|{ d.span_err((*items[0]).span, "invalid argument"); }
);
321 /// True if `#[inline]` or `#[inline(always)]` is present in `attrs`.
322 pub fn requests_inline(attrs
: &[Attribute
]) -> bool
{
323 match find_inline_attr(None
, attrs
) {
324 InlineAttr
::Hint
| InlineAttr
::Always
=> true,
325 InlineAttr
::None
| InlineAttr
::Never
=> false,
329 /// Tests if a cfg-pattern matches the cfg set
330 pub fn cfg_matches(diagnostic
: &SpanHandler
, cfgs
: &[P
<MetaItem
>], cfg
: &ast
::MetaItem
) -> bool
{
332 ast
::MetaList(ref pred
, ref mis
) if &pred
[..] == "any" =>
333 mis
.iter().any(|mi
| cfg_matches(diagnostic
, cfgs
, &**mi
)),
334 ast
::MetaList(ref pred
, ref mis
) if &pred
[..] == "all" =>
335 mis
.iter().all(|mi
| cfg_matches(diagnostic
, cfgs
, &**mi
)),
336 ast
::MetaList(ref pred
, ref mis
) if &pred
[..] == "not" => {
338 diagnostic
.span_err(cfg
.span
, "expected 1 cfg-pattern");
341 !cfg_matches(diagnostic
, cfgs
, &*mis
[0])
343 ast
::MetaList(ref pred
, _
) => {
344 diagnostic
.span_err(cfg
.span
, &format
!("invalid predicate `{}`", pred
));
347 ast
::MetaWord(_
) | ast
::MetaNameValue(..) => contains(cfgs
, cfg
),
351 /// Represents the #[deprecated] and friends attributes.
352 #[derive(RustcEncodable,RustcDecodable,Clone,Debug)]
353 pub struct Stability
{
354 pub level
: StabilityLevel
,
355 pub feature
: InternedString
,
356 pub since
: Option
<InternedString
>,
357 pub deprecated_since
: Option
<InternedString
>,
358 // The reason for the current stability level. If deprecated, the
359 // reason for deprecation.
360 pub reason
: Option
<InternedString
>,
363 /// The available stability levels.
364 #[derive(RustcEncodable,RustcDecodable,PartialEq,PartialOrd,Clone,Debug,Copy)]
365 pub enum StabilityLevel
{
370 impl fmt
::Display
for StabilityLevel
{
371 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
372 fmt
::Debug
::fmt(self, f
)
376 fn find_stability_generic
<'a
,
378 I
: Iterator
<Item
=&'a AM
>>
379 (diagnostic
: &SpanHandler
, attrs
: I
, item_sp
: Span
)
380 -> (Option
<Stability
>, Vec
<&'a AM
>) {
382 let mut stab
: Option
<Stability
> = None
;
383 let mut deprecated
: Option
<(InternedString
, Option
<InternedString
>)> = None
;
384 let mut used_attrs
: Vec
<&'a AM
> = vec
![];
386 'outer
: for attr
in attrs
{
387 let tag
= attr
.name();
389 if tag
!= "deprecated" && tag
!= "unstable" && tag
!= "stable" {
390 continue // not a stability level
393 used_attrs
.push(attr
);
395 let (feature
, since
, reason
) = match attr
.meta_item_list() {
397 let mut feature
= None
;
398 let mut since
= None
;
399 let mut reason
= None
;
400 for meta
in metas
.iter() {
401 if meta
.name() == "feature" {
402 match meta
.value_str() {
403 Some(v
) => feature
= Some(v
),
405 diagnostic
.span_err(meta
.span
, "incorrect meta item");
410 if &meta
.name()[..] == "since" {
411 match meta
.value_str() {
412 Some(v
) => since
= Some(v
),
414 diagnostic
.span_err(meta
.span
, "incorrect meta item");
419 if &meta
.name()[..] == "reason" {
420 match meta
.value_str() {
421 Some(v
) => reason
= Some(v
),
423 diagnostic
.span_err(meta
.span
, "incorrect meta item");
429 (feature
, since
, reason
)
432 diagnostic
.span_err(attr
.span(), "incorrect stability attribute type");
437 // Deprecated tags don't require feature names
438 if feature
== None
&& tag
!= "deprecated" {
439 diagnostic
.span_err(attr
.span(), "missing 'feature'");
442 // Unstable tags don't require a version
443 if since
== None
&& tag
!= "unstable" {
444 diagnostic
.span_err(attr
.span(), "missing 'since'");
447 if tag
== "unstable" || tag
== "stable" {
449 diagnostic
.span_err(item_sp
, "multiple stability levels");
452 let level
= match tag
{
453 "unstable" => Unstable
,
458 stab
= Some(Stability
{
460 feature
: feature
.unwrap_or(intern_and_get_ident("bogus")),
462 deprecated_since
: None
,
465 } else { // "deprecated"
466 if deprecated
.is_some() {
467 diagnostic
.span_err(item_sp
, "multiple deprecated attributes");
470 deprecated
= Some((since
.unwrap_or(intern_and_get_ident("bogus")), reason
));
474 // Merge the deprecation info into the stability info
475 if deprecated
.is_some() {
478 let (since
, reason
) = deprecated
.unwrap();
479 s
.deprecated_since
= Some(since
);
483 diagnostic
.span_err(item_sp
, "deprecated attribute must be paired with \
484 either stable or unstable attribute");
492 /// Find the first stability attribute. `None` if none exists.
493 pub fn find_stability(diagnostic
: &SpanHandler
, attrs
: &[Attribute
],
494 item_sp
: Span
) -> Option
<Stability
> {
495 let (s
, used
) = find_stability_generic(diagnostic
, attrs
.iter(), item_sp
);
496 for used
in used { mark_used(used) }
500 pub fn require_unique_names(diagnostic
: &SpanHandler
, metas
: &[P
<MetaItem
>]) {
501 let mut set
= HashSet
::new();
503 let name
= meta
.name();
505 if !set
.insert(name
.clone()) {
506 diagnostic
.span_fatal(meta
.span
,
507 &format
!("duplicate meta item `{}`", name
));
513 /// Parse #[repr(...)] forms.
515 /// Valid repr contents: any of the primitive integral type names (see
516 /// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
517 /// the same discriminant size that the corresponding C enum would or C
518 /// structure layout, and `packed` to remove padding.
519 pub fn find_repr_attrs(diagnostic
: &SpanHandler
, attr
: &Attribute
) -> Vec
<ReprAttr
> {
520 let mut acc
= Vec
::new();
521 match attr
.node
.value
.node
{
522 ast
::MetaList(ref s
, ref items
) if *s
== "repr" => {
526 ast
::MetaWord(ref word
) => {
527 let hint
= match &word
[..] {
528 // Can't use "extern" because it's not a lexical identifier.
529 "C" => Some(ReprExtern
),
530 "packed" => Some(ReprPacked
),
531 _
=> match int_type_of_word(&word
) {
532 Some(ity
) => Some(ReprInt(item
.span
, ity
)),
534 // Not a word we recognize
535 diagnostic
.span_err(item
.span
,
536 "unrecognized representation hint");
543 Some(h
) => acc
.push(h
),
548 _
=> diagnostic
.span_err(item
.span
, "unrecognized enum representation hint")
552 // Not a "repr" hint: ignore.
558 fn int_type_of_word(s
: &str) -> Option
<IntType
> {
560 "i8" => Some(SignedInt(ast
::TyI8
)),
561 "u8" => Some(UnsignedInt(ast
::TyU8
)),
562 "i16" => Some(SignedInt(ast
::TyI16
)),
563 "u16" => Some(UnsignedInt(ast
::TyU16
)),
564 "i32" => Some(SignedInt(ast
::TyI32
)),
565 "u32" => Some(UnsignedInt(ast
::TyU32
)),
566 "i64" => Some(SignedInt(ast
::TyI64
)),
567 "u64" => Some(UnsignedInt(ast
::TyU64
)),
568 "isize" => Some(SignedInt(ast
::TyIs
)),
569 "usize" => Some(UnsignedInt(ast
::TyUs
)),
574 #[derive(PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)]
577 ReprInt(Span
, IntType
),
583 pub fn is_ffi_safe(&self) -> bool
{
586 ReprInt(_sp
, ity
) => ity
.is_ffi_safe(),
593 #[derive(Eq, Hash, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)]
595 SignedInt(ast
::IntTy
),
596 UnsignedInt(ast
::UintTy
)
601 pub fn is_signed(self) -> bool
{
603 SignedInt(..) => true,
604 UnsignedInt(..) => false
607 fn is_ffi_safe(self) -> bool
{
609 SignedInt(ast
::TyI8
) | UnsignedInt(ast
::TyU8
) |
610 SignedInt(ast
::TyI16
) | UnsignedInt(ast
::TyU16
) |
611 SignedInt(ast
::TyI32
) | UnsignedInt(ast
::TyU32
) |
612 SignedInt(ast
::TyI64
) | UnsignedInt(ast
::TyU64
) => true,
613 SignedInt(ast
::TyIs
) | UnsignedInt(ast
::TyUs
) => false