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 /// Find the value of #[export_name=*] attribute and check its validity.
286 pub fn find_export_name_attr(diag
: &SpanHandler
, attrs
: &[Attribute
]) -> Option
<InternedString
> {
287 attrs
.iter().fold(None
, |ia
,attr
| {
288 if attr
.check_name("export_name") {
289 if let s@
Some(_
) = attr
.value_str() {
292 diag
.span_err(attr
.span
, "export_name attribute has invalid format");
293 diag
.handler
.help("use #[export_name=\"*\"]");
302 #[derive(Copy, Clone, PartialEq)]
303 pub enum InlineAttr
{
310 /// Determine what `#[inline]` attribute is present in `attrs`, if any.
311 pub fn find_inline_attr(diagnostic
: Option
<&SpanHandler
>, attrs
: &[Attribute
]) -> InlineAttr
{
312 // FIXME (#2809)---validate the usage of #[inline] and #[inline]
313 attrs
.iter().fold(InlineAttr
::None
, |ia
,attr
| {
314 match attr
.node
.value
.node
{
315 MetaWord(ref n
) if *n
== "inline" => {
319 MetaList(ref n
, ref items
) if *n
== "inline" => {
321 if items
.len() != 1 {
322 diagnostic
.map(|d
|{ d.span_err(attr.span, "expected one argument"); }
);
324 } else if contains_name(&items
[..], "always") {
326 } else if contains_name(&items
[..], "never") {
329 diagnostic
.map(|d
|{ d.span_err((*items[0]).span, "invalid argument"); }
);
338 /// True if `#[inline]` or `#[inline(always)]` is present in `attrs`.
339 pub fn requests_inline(attrs
: &[Attribute
]) -> bool
{
340 match find_inline_attr(None
, attrs
) {
341 InlineAttr
::Hint
| InlineAttr
::Always
=> true,
342 InlineAttr
::None
| InlineAttr
::Never
=> false,
346 /// Tests if a cfg-pattern matches the cfg set
347 pub fn cfg_matches(diagnostic
: &SpanHandler
, cfgs
: &[P
<MetaItem
>], cfg
: &ast
::MetaItem
) -> bool
{
349 ast
::MetaList(ref pred
, ref mis
) if &pred
[..] == "any" =>
350 mis
.iter().any(|mi
| cfg_matches(diagnostic
, cfgs
, &**mi
)),
351 ast
::MetaList(ref pred
, ref mis
) if &pred
[..] == "all" =>
352 mis
.iter().all(|mi
| cfg_matches(diagnostic
, cfgs
, &**mi
)),
353 ast
::MetaList(ref pred
, ref mis
) if &pred
[..] == "not" => {
355 diagnostic
.span_err(cfg
.span
, "expected 1 cfg-pattern");
358 !cfg_matches(diagnostic
, cfgs
, &*mis
[0])
360 ast
::MetaList(ref pred
, _
) => {
361 diagnostic
.span_err(cfg
.span
, &format
!("invalid predicate `{}`", pred
));
364 ast
::MetaWord(_
) | ast
::MetaNameValue(..) => contains(cfgs
, cfg
),
368 /// Represents the #[deprecated] and friends attributes.
369 #[derive(RustcEncodable,RustcDecodable,Clone,Debug)]
370 pub struct Stability
{
371 pub level
: StabilityLevel
,
372 pub feature
: InternedString
,
373 pub since
: Option
<InternedString
>,
374 pub deprecated_since
: Option
<InternedString
>,
375 // The reason for the current stability level. If deprecated, the
376 // reason for deprecation.
377 pub reason
: Option
<InternedString
>,
380 /// The available stability levels.
381 #[derive(RustcEncodable,RustcDecodable,PartialEq,PartialOrd,Clone,Debug,Copy)]
382 pub enum StabilityLevel
{
387 impl fmt
::Display
for StabilityLevel
{
388 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
389 fmt
::Debug
::fmt(self, f
)
393 fn find_stability_generic
<'a
,
395 I
: Iterator
<Item
=&'a AM
>>
396 (diagnostic
: &SpanHandler
, attrs
: I
, item_sp
: Span
)
397 -> (Option
<Stability
>, Vec
<&'a AM
>) {
399 let mut stab
: Option
<Stability
> = None
;
400 let mut deprecated
: Option
<(InternedString
, Option
<InternedString
>)> = None
;
401 let mut used_attrs
: Vec
<&'a AM
> = vec
![];
403 'outer
: for attr
in attrs
{
404 let tag
= attr
.name();
406 if tag
!= "deprecated" && tag
!= "unstable" && tag
!= "stable" {
407 continue // not a stability level
410 used_attrs
.push(attr
);
412 let (feature
, since
, reason
) = match attr
.meta_item_list() {
414 let mut feature
= None
;
415 let mut since
= None
;
416 let mut reason
= None
;
417 for meta
in metas
.iter() {
418 if meta
.name() == "feature" {
419 match meta
.value_str() {
420 Some(v
) => feature
= Some(v
),
422 diagnostic
.span_err(meta
.span
, "incorrect meta item");
427 if &meta
.name()[..] == "since" {
428 match meta
.value_str() {
429 Some(v
) => since
= Some(v
),
431 diagnostic
.span_err(meta
.span
, "incorrect meta item");
436 if &meta
.name()[..] == "reason" {
437 match meta
.value_str() {
438 Some(v
) => reason
= Some(v
),
440 diagnostic
.span_err(meta
.span
, "incorrect meta item");
446 (feature
, since
, reason
)
449 diagnostic
.span_err(attr
.span(), "incorrect stability attribute type");
454 // Deprecated tags don't require feature names
455 if feature
== None
&& tag
!= "deprecated" {
456 diagnostic
.span_err(attr
.span(), "missing 'feature'");
459 // Unstable tags don't require a version
460 if since
== None
&& tag
!= "unstable" {
461 diagnostic
.span_err(attr
.span(), "missing 'since'");
464 if tag
== "unstable" || tag
== "stable" {
466 diagnostic
.span_err(item_sp
, "multiple stability levels");
469 let level
= match tag
{
470 "unstable" => Unstable
,
475 stab
= Some(Stability
{
477 feature
: feature
.unwrap_or(intern_and_get_ident("bogus")),
479 deprecated_since
: None
,
482 } else { // "deprecated"
483 if deprecated
.is_some() {
484 diagnostic
.span_err(item_sp
, "multiple deprecated attributes");
487 deprecated
= Some((since
.unwrap_or(intern_and_get_ident("bogus")), reason
));
491 // Merge the deprecation info into the stability info
492 if deprecated
.is_some() {
495 let (since
, reason
) = deprecated
.unwrap();
496 s
.deprecated_since
= Some(since
);
500 diagnostic
.span_err(item_sp
, "deprecated attribute must be paired with \
501 either stable or unstable attribute");
509 /// Find the first stability attribute. `None` if none exists.
510 pub fn find_stability(diagnostic
: &SpanHandler
, attrs
: &[Attribute
],
511 item_sp
: Span
) -> Option
<Stability
> {
512 let (s
, used
) = find_stability_generic(diagnostic
, attrs
.iter(), item_sp
);
513 for used
in used { mark_used(used) }
517 pub fn require_unique_names(diagnostic
: &SpanHandler
, metas
: &[P
<MetaItem
>]) {
518 let mut set
= HashSet
::new();
520 let name
= meta
.name();
522 if !set
.insert(name
.clone()) {
523 panic
!(diagnostic
.span_fatal(meta
.span
,
524 &format
!("duplicate meta item `{}`", name
)));
530 /// Parse #[repr(...)] forms.
532 /// Valid repr contents: any of the primitive integral type names (see
533 /// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
534 /// the same discriminant size that the corresponding C enum would or C
535 /// structure layout, and `packed` to remove padding.
536 pub fn find_repr_attrs(diagnostic
: &SpanHandler
, attr
: &Attribute
) -> Vec
<ReprAttr
> {
537 let mut acc
= Vec
::new();
538 match attr
.node
.value
.node
{
539 ast
::MetaList(ref s
, ref items
) if *s
== "repr" => {
543 ast
::MetaWord(ref word
) => {
544 let hint
= match &word
[..] {
545 // Can't use "extern" because it's not a lexical identifier.
546 "C" => Some(ReprExtern
),
547 "packed" => Some(ReprPacked
),
548 _
=> match int_type_of_word(&word
) {
549 Some(ity
) => Some(ReprInt(item
.span
, ity
)),
551 // Not a word we recognize
552 diagnostic
.span_err(item
.span
,
553 "unrecognized representation hint");
560 Some(h
) => acc
.push(h
),
565 _
=> diagnostic
.span_err(item
.span
, "unrecognized enum representation hint")
569 // Not a "repr" hint: ignore.
575 fn int_type_of_word(s
: &str) -> Option
<IntType
> {
577 "i8" => Some(SignedInt(ast
::TyI8
)),
578 "u8" => Some(UnsignedInt(ast
::TyU8
)),
579 "i16" => Some(SignedInt(ast
::TyI16
)),
580 "u16" => Some(UnsignedInt(ast
::TyU16
)),
581 "i32" => Some(SignedInt(ast
::TyI32
)),
582 "u32" => Some(UnsignedInt(ast
::TyU32
)),
583 "i64" => Some(SignedInt(ast
::TyI64
)),
584 "u64" => Some(UnsignedInt(ast
::TyU64
)),
585 "isize" => Some(SignedInt(ast
::TyIs
)),
586 "usize" => Some(UnsignedInt(ast
::TyUs
)),
591 #[derive(PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)]
594 ReprInt(Span
, IntType
),
600 pub fn is_ffi_safe(&self) -> bool
{
603 ReprInt(_sp
, ity
) => ity
.is_ffi_safe(),
610 #[derive(Eq, Hash, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)]
612 SignedInt(ast
::IntTy
),
613 UnsignedInt(ast
::UintTy
)
618 pub fn is_signed(self) -> bool
{
620 SignedInt(..) => true,
621 UnsignedInt(..) => false
624 fn is_ffi_safe(self) -> bool
{
626 SignedInt(ast
::TyI8
) | UnsignedInt(ast
::TyU8
) |
627 SignedInt(ast
::TyI16
) | UnsignedInt(ast
::TyU16
) |
628 SignedInt(ast
::TyI32
) | UnsignedInt(ast
::TyU32
) |
629 SignedInt(ast
::TyI64
) | UnsignedInt(ast
::TyU64
) => true,
630 SignedInt(ast
::TyIs
) | UnsignedInt(ast
::TyUs
) => false