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
16 pub use self::StabilityLevel
::*;
17 pub use self::ReprAttr
::*;
18 pub use self::IntType
::*;
21 use ast
::{AttrId, Attribute, Attribute_, MetaItem, MetaWord, MetaNameValue, MetaList}
;
22 use codemap
::{Span, Spanned, spanned, dummy_spanned}
;
24 use diagnostic
::SpanHandler
;
25 use parse
::lexer
::comments
::{doc_comment_style, strip_doc_comment_decoration}
;
26 use parse
::token
::{InternedString, intern_and_get_ident}
;
30 use std
::cell
::{RefCell, Cell}
;
31 use std
::collections
::BitSet
;
32 use std
::collections
::HashSet
;
35 thread_local
! { static USED_ATTRS: RefCell<BitSet> = RefCell::new(BitSet::new()) }
37 pub fn mark_used(attr
: &Attribute
) {
38 let AttrId(id
) = attr
.node
.id
;
39 USED_ATTRS
.with(|slot
| slot
.borrow_mut().insert(id
));
42 pub fn is_used(attr
: &Attribute
) -> bool
{
43 let AttrId(id
) = attr
.node
.id
;
44 USED_ATTRS
.with(|slot
| slot
.borrow().contains(&id
))
47 pub trait AttrMetaMethods
{
48 fn check_name(&self, name
: &str) -> bool
{
49 name
== &self.name()[..]
52 /// Retrieve the name of the meta item, e.g. `foo` in `#[foo]`,
53 /// `#[foo="bar"]` and `#[foo(bar)]`
54 fn name(&self) -> InternedString
;
56 /// Gets the string value if self is a MetaNameValue variant
57 /// containing a string, otherwise None.
58 fn value_str(&self) -> Option
<InternedString
>;
59 /// Gets a list of inner meta items from a list MetaItem type.
60 fn meta_item_list
<'a
>(&'a
self) -> Option
<&'a
[P
<MetaItem
>]>;
62 fn span(&self) -> Span
;
65 impl AttrMetaMethods
for Attribute
{
66 fn check_name(&self, name
: &str) -> bool
{
67 let matches
= name
== &self.name()[..];
73 fn name(&self) -> InternedString { self.meta().name() }
74 fn value_str(&self) -> Option
<InternedString
> {
75 self.meta().value_str()
77 fn meta_item_list
<'a
>(&'a
self) -> Option
<&'a
[P
<MetaItem
>]> {
78 self.node
.value
.meta_item_list()
80 fn span(&self) -> Span { self.meta().span }
83 impl AttrMetaMethods
for MetaItem
{
84 fn name(&self) -> InternedString
{
86 MetaWord(ref n
) => (*n
).clone(),
87 MetaNameValue(ref n
, _
) => (*n
).clone(),
88 MetaList(ref n
, _
) => (*n
).clone(),
92 fn value_str(&self) -> Option
<InternedString
> {
94 MetaNameValue(_
, ref v
) => {
96 ast
::LitStr(ref s
, _
) => Some((*s
).clone()),
104 fn meta_item_list
<'a
>(&'a
self) -> Option
<&'a
[P
<MetaItem
>]> {
106 MetaList(_
, ref l
) => Some(&l
[..]),
110 fn span(&self) -> Span { self.span }
113 // Annoying, but required to get test_cfg to work
114 impl AttrMetaMethods
for P
<MetaItem
> {
115 fn name(&self) -> InternedString { (**self).name() }
116 fn value_str(&self) -> Option
<InternedString
> { (**self).value_str() }
117 fn meta_item_list
<'a
>(&'a
self) -> Option
<&'a
[P
<MetaItem
>]> {
118 (**self).meta_item_list()
120 fn span(&self) -> Span { (**self).span() }
124 pub trait AttributeMethods
{
125 fn meta
<'a
>(&'a
self) -> &'a MetaItem
;
126 fn with_desugared_doc
<T
, F
>(&self, f
: F
) -> T
where
127 F
: FnOnce(&Attribute
) -> T
;
130 impl AttributeMethods
for Attribute
{
131 /// Extract the MetaItem from inside this Attribute.
132 fn meta
<'a
>(&'a
self) -> &'a MetaItem
{
136 /// Convert self to a normal #[doc="foo"] comment, if it is a
137 /// comment like `///` or `/** */`. (Returns self unchanged for
138 /// non-sugared doc attributes.)
139 fn with_desugared_doc
<T
, F
>(&self, f
: F
) -> T
where
140 F
: FnOnce(&Attribute
) -> T
,
142 if self.node
.is_sugared_doc
{
143 let comment
= self.value_str().unwrap();
144 let meta
= mk_name_value_item_str(
145 InternedString
::new("doc"),
146 token
::intern_and_get_ident(&strip_doc_comment_decoration(
148 if self.node
.style
== ast
::AttrOuter
{
149 f(&mk_attr_outer(self.node
.id
, meta
))
151 f(&mk_attr_inner(self.node
.id
, meta
))
161 pub fn mk_name_value_item_str(name
: InternedString
, value
: InternedString
)
163 let value_lit
= dummy_spanned(ast
::LitStr(value
, ast
::CookedStr
));
164 mk_name_value_item(name
, value_lit
)
167 pub fn mk_name_value_item(name
: InternedString
, value
: ast
::Lit
)
169 P(dummy_spanned(MetaNameValue(name
, value
)))
172 pub fn mk_list_item(name
: InternedString
, items
: Vec
<P
<MetaItem
>>) -> P
<MetaItem
> {
173 P(dummy_spanned(MetaList(name
, items
)))
176 pub fn mk_word_item(name
: InternedString
) -> P
<MetaItem
> {
177 P(dummy_spanned(MetaWord(name
)))
180 thread_local
! { static NEXT_ATTR_ID: Cell<usize> = Cell::new(0) }
182 pub fn mk_attr_id() -> AttrId
{
183 let id
= NEXT_ATTR_ID
.with(|slot
| {
191 /// Returns an inner attribute with the given value.
192 pub fn mk_attr_inner(id
: AttrId
, item
: P
<MetaItem
>) -> Attribute
{
193 dummy_spanned(Attribute_
{
195 style
: ast
::AttrInner
,
197 is_sugared_doc
: false,
201 /// Returns an outer attribute with the given value.
202 pub fn mk_attr_outer(id
: AttrId
, item
: P
<MetaItem
>) -> Attribute
{
203 dummy_spanned(Attribute_
{
205 style
: ast
::AttrOuter
,
207 is_sugared_doc
: false,
211 pub fn mk_sugared_doc_attr(id
: AttrId
, text
: InternedString
, lo
: BytePos
,
214 let style
= doc_comment_style(&text
);
215 let lit
= spanned(lo
, hi
, ast
::LitStr(text
, ast
::CookedStr
));
216 let attr
= Attribute_
{
219 value
: P(spanned(lo
, hi
, MetaNameValue(InternedString
::new("doc"),
223 spanned(lo
, hi
, attr
)
227 /// Check if `needle` occurs in `haystack` by a structural
228 /// comparison. This is slightly subtle, and relies on ignoring the
229 /// span included in the `==` comparison a plain MetaItem.
230 pub fn contains(haystack
: &[P
<MetaItem
>], needle
: &MetaItem
) -> bool
{
231 debug
!("attr::contains (name={})", needle
.name());
232 haystack
.iter().any(|item
| {
233 debug
!(" testing: {}", item
.name());
234 item
.node
== needle
.node
238 pub fn contains_name
<AM
: AttrMetaMethods
>(metas
: &[AM
], name
: &str) -> bool
{
239 debug
!("attr::contains_name (name={})", name
);
240 metas
.iter().any(|item
| {
241 debug
!(" testing: {}", item
.name());
242 item
.check_name(name
)
246 pub fn first_attr_value_str_by_name(attrs
: &[Attribute
], name
: &str)
247 -> Option
<InternedString
> {
249 .find(|at
| at
.check_name(name
))
250 .and_then(|at
| at
.value_str())
253 pub fn last_meta_item_value_str_by_name(items
: &[P
<MetaItem
>], name
: &str)
254 -> Option
<InternedString
> {
257 .find(|mi
| mi
.check_name(name
))
258 .and_then(|i
| i
.value_str())
261 /* Higher-level applications */
263 pub fn sort_meta_items(items
: Vec
<P
<MetaItem
>>) -> Vec
<P
<MetaItem
>> {
264 // This is sort of stupid here, but we need to sort by
265 // human-readable strings.
266 let mut v
= items
.into_iter()
267 .map(|mi
| (mi
.name(), mi
))
268 .collect
::<Vec
<(InternedString
, P
<MetaItem
>)>>();
270 v
.sort_by(|&(ref a
, _
), &(ref b
, _
)| a
.cmp(b
));
272 // There doesn't seem to be a more optimal way to do this
273 v
.into_iter().map(|(_
, m
)| m
.map(|Spanned {node, span}
| {
276 MetaList(n
, mis
) => MetaList(n
, sort_meta_items(mis
)),
284 pub fn find_crate_name(attrs
: &[Attribute
]) -> Option
<InternedString
> {
285 first_attr_value_str_by_name(attrs
, "crate_name")
288 /// Find the value of #[export_name=*] attribute and check its validity.
289 pub fn find_export_name_attr(diag
: &SpanHandler
, attrs
: &[Attribute
]) -> Option
<InternedString
> {
290 attrs
.iter().fold(None
, |ia
,attr
| {
291 if attr
.check_name("export_name") {
292 if let s@
Some(_
) = attr
.value_str() {
295 diag
.span_err(attr
.span
, "export_name attribute has invalid format");
296 diag
.handler
.help("use #[export_name=\"*\"]");
305 #[derive(Copy, Clone, PartialEq)]
306 pub enum InlineAttr
{
313 /// Determine what `#[inline]` attribute is present in `attrs`, if any.
314 pub fn find_inline_attr(diagnostic
: Option
<&SpanHandler
>, attrs
: &[Attribute
]) -> InlineAttr
{
315 // FIXME (#2809)---validate the usage of #[inline] and #[inline]
316 attrs
.iter().fold(InlineAttr
::None
, |ia
,attr
| {
317 match attr
.node
.value
.node
{
318 MetaWord(ref n
) if *n
== "inline" => {
322 MetaList(ref n
, ref items
) if *n
== "inline" => {
324 if items
.len() != 1 {
325 diagnostic
.map(|d
|{ d.span_err(attr.span, "expected one argument"); }
);
327 } else if contains_name(&items
[..], "always") {
329 } else if contains_name(&items
[..], "never") {
332 diagnostic
.map(|d
|{ d.span_err((*items[0]).span, "invalid argument"); }
);
341 /// True if `#[inline]` or `#[inline(always)]` is present in `attrs`.
342 pub fn requests_inline(attrs
: &[Attribute
]) -> bool
{
343 match find_inline_attr(None
, attrs
) {
344 InlineAttr
::Hint
| InlineAttr
::Always
=> true,
345 InlineAttr
::None
| InlineAttr
::Never
=> false,
349 /// Tests if a cfg-pattern matches the cfg set
350 pub fn cfg_matches(diagnostic
: &SpanHandler
, cfgs
: &[P
<MetaItem
>], cfg
: &ast
::MetaItem
) -> bool
{
352 ast
::MetaList(ref pred
, ref mis
) if &pred
[..] == "any" =>
353 mis
.iter().any(|mi
| cfg_matches(diagnostic
, cfgs
, &**mi
)),
354 ast
::MetaList(ref pred
, ref mis
) if &pred
[..] == "all" =>
355 mis
.iter().all(|mi
| cfg_matches(diagnostic
, cfgs
, &**mi
)),
356 ast
::MetaList(ref pred
, ref mis
) if &pred
[..] == "not" => {
358 diagnostic
.span_err(cfg
.span
, "expected 1 cfg-pattern");
361 !cfg_matches(diagnostic
, cfgs
, &*mis
[0])
363 ast
::MetaList(ref pred
, _
) => {
364 diagnostic
.span_err(cfg
.span
, &format
!("invalid predicate `{}`", pred
));
367 ast
::MetaWord(_
) | ast
::MetaNameValue(..) => contains(cfgs
, cfg
),
371 /// Represents the #[deprecated] and friends attributes.
372 #[derive(RustcEncodable, RustcDecodable, Clone, Debug, PartialEq, Eq, Hash)]
373 pub struct Stability
{
374 pub level
: StabilityLevel
,
375 pub feature
: InternedString
,
376 pub since
: Option
<InternedString
>,
377 pub deprecated_since
: Option
<InternedString
>,
378 // The reason for the current stability level. If deprecated, the
379 // reason for deprecation.
380 pub reason
: Option
<InternedString
>,
381 // The relevant rust-lang issue
382 pub issue
: Option
<u32>
385 /// The available stability levels.
386 #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Copy, Eq, Hash)]
387 pub enum StabilityLevel
{
392 impl fmt
::Display
for StabilityLevel
{
393 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
394 fmt
::Debug
::fmt(self, f
)
398 fn find_stability_generic
<'a
,
400 I
: Iterator
<Item
=&'a AM
>>
401 (diagnostic
: &SpanHandler
, attrs
: I
, item_sp
: Span
)
402 -> (Option
<Stability
>, Vec
<&'a AM
>) {
404 let mut stab
: Option
<Stability
> = None
;
405 let mut deprecated
: Option
<(Option
<InternedString
>, Option
<InternedString
>)> = None
;
406 let mut used_attrs
: Vec
<&'a AM
> = vec
![];
408 'outer
: for attr
in attrs
{
409 let tag
= attr
.name();
411 if tag
!= "deprecated" && tag
!= "unstable" && tag
!= "stable" {
412 continue // not a stability level
415 used_attrs
.push(attr
);
417 let (feature
, since
, reason
, issue
) = match attr
.meta_item_list() {
419 let mut feature
= None
;
420 let mut since
= None
;
421 let mut reason
= None
;
422 let mut issue
= None
;
424 match &*meta
.name() {
426 match meta
.value_str() {
427 Some(v
) => feature
= Some(v
),
429 diagnostic
.span_err(meta
.span
, "incorrect meta item");
435 match meta
.value_str() {
436 Some(v
) => since
= Some(v
),
438 diagnostic
.span_err(meta
.span
, "incorrect meta item");
444 match meta
.value_str() {
445 Some(v
) => reason
= Some(v
),
447 diagnostic
.span_err(meta
.span
, "incorrect meta item");
453 match meta
.value_str().and_then(|s
| s
.parse().ok()) {
454 Some(v
) => issue
= Some(v
),
456 diagnostic
.span_err(meta
.span
, "incorrect meta item");
464 (feature
, since
, reason
, issue
)
467 diagnostic
.span_err(attr
.span(), "incorrect stability attribute type");
472 // Deprecated tags don't require feature names
473 if feature
== None
&& tag
!= "deprecated" {
474 diagnostic
.span_err(attr
.span(), "missing 'feature'");
477 // Unstable tags don't require a version
478 if since
== None
&& tag
!= "unstable" {
479 diagnostic
.span_err(attr
.span(), "missing 'since'");
482 if tag
== "unstable" || tag
== "stable" {
484 diagnostic
.span_err(item_sp
, "multiple stability levels");
487 let level
= match tag
{
488 "unstable" => Unstable
,
493 stab
= Some(Stability
{
495 feature
: feature
.unwrap_or(intern_and_get_ident("bogus")),
497 deprecated_since
: None
,
501 } else { // "deprecated"
502 if deprecated
.is_some() {
503 diagnostic
.span_err(item_sp
, "multiple deprecated attributes");
506 deprecated
= Some((since
, reason
));
510 // Merge the deprecation info into the stability info
511 if deprecated
.is_some() {
514 let (since
, reason
) = deprecated
.unwrap();
515 s
.deprecated_since
= since
;
519 diagnostic
.span_err(item_sp
, "deprecated attribute must be paired with \
520 either stable or unstable attribute");
523 } else if stab
.as_ref().map_or(false, |s
| s
.level
== Unstable
&& s
.issue
.is_none()) {
524 // non-deprecated unstable items need to point to issues.
525 // FIXME: uncomment this error
526 // diagnostic.span_err(item_sp,
527 // "non-deprecated unstable items need to point \
528 // to an issue with `issue = \"NNN\"`");
534 /// Find the first stability attribute. `None` if none exists.
535 pub fn find_stability(diagnostic
: &SpanHandler
, attrs
: &[Attribute
],
536 item_sp
: Span
) -> Option
<Stability
> {
537 let (s
, used
) = find_stability_generic(diagnostic
, attrs
.iter(), item_sp
);
538 for used
in used { mark_used(used) }
542 pub fn require_unique_names(diagnostic
: &SpanHandler
, metas
: &[P
<MetaItem
>]) {
543 let mut set
= HashSet
::new();
545 let name
= meta
.name();
547 if !set
.insert(name
.clone()) {
548 panic
!(diagnostic
.span_fatal(meta
.span
,
549 &format
!("duplicate meta item `{}`", name
)));
555 /// Parse #[repr(...)] forms.
557 /// Valid repr contents: any of the primitive integral type names (see
558 /// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
559 /// the same discriminant size that the corresponding C enum would or C
560 /// structure layout, and `packed` to remove padding.
561 pub fn find_repr_attrs(diagnostic
: &SpanHandler
, attr
: &Attribute
) -> Vec
<ReprAttr
> {
562 let mut acc
= Vec
::new();
563 match attr
.node
.value
.node
{
564 ast
::MetaList(ref s
, ref items
) if *s
== "repr" => {
568 ast
::MetaWord(ref word
) => {
569 let hint
= match &word
[..] {
570 // Can't use "extern" because it's not a lexical identifier.
571 "C" => Some(ReprExtern
),
572 "packed" => Some(ReprPacked
),
573 _
=> match int_type_of_word(&word
) {
574 Some(ity
) => Some(ReprInt(item
.span
, ity
)),
576 // Not a word we recognize
577 diagnostic
.span_err(item
.span
,
578 "unrecognized representation hint");
585 Some(h
) => acc
.push(h
),
590 _
=> diagnostic
.span_err(item
.span
, "unrecognized enum representation hint")
594 // Not a "repr" hint: ignore.
600 fn int_type_of_word(s
: &str) -> Option
<IntType
> {
602 "i8" => Some(SignedInt(ast
::TyI8
)),
603 "u8" => Some(UnsignedInt(ast
::TyU8
)),
604 "i16" => Some(SignedInt(ast
::TyI16
)),
605 "u16" => Some(UnsignedInt(ast
::TyU16
)),
606 "i32" => Some(SignedInt(ast
::TyI32
)),
607 "u32" => Some(UnsignedInt(ast
::TyU32
)),
608 "i64" => Some(SignedInt(ast
::TyI64
)),
609 "u64" => Some(UnsignedInt(ast
::TyU64
)),
610 "isize" => Some(SignedInt(ast
::TyIs
)),
611 "usize" => Some(UnsignedInt(ast
::TyUs
)),
616 #[derive(PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)]
619 ReprInt(Span
, IntType
),
625 pub fn is_ffi_safe(&self) -> bool
{
628 ReprInt(_sp
, ity
) => ity
.is_ffi_safe(),
635 #[derive(Eq, Hash, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)]
637 SignedInt(ast
::IntTy
),
638 UnsignedInt(ast
::UintTy
)
643 pub fn is_signed(self) -> bool
{
645 SignedInt(..) => true,
646 UnsignedInt(..) => false
649 fn is_ffi_safe(self) -> bool
{
651 SignedInt(ast
::TyI8
) | UnsignedInt(ast
::TyU8
) |
652 SignedInt(ast
::TyI16
) | UnsignedInt(ast
::TyU16
) |
653 SignedInt(ast
::TyI32
) | UnsignedInt(ast
::TyU32
) |
654 SignedInt(ast
::TyI64
) | UnsignedInt(ast
::TyU64
) => true,
655 SignedInt(ast
::TyIs
) | UnsignedInt(ast
::TyUs
) => false