(index<- )        ./libsyntax/attr.rs

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


libsyntax/attr.rs:477:22-477:22 -enum- definition:
pub enum IntType {
    SignedInt(ast::IntTy),
    UnsignedInt(ast::UintTy)
references:- 7
478: pub enum IntType {
--
483: impl IntType {
484:     #[inline]


libsyntax/attr.rs:190:1-190:1 -fn- definition:
pub fn contains_name<AM: AttrMetaMethods>(metas: &[AM], name: &str) -> bool {
    debug!("attr::contains_name (name={})", name);
    metas.iter().any(|item| {
references:- 6
276:           MetaList(ref n, ref items) if n.equiv(&("inline")) => {
277:             if contains_name(items.as_slice(), "always") {
278:                 InlineAlways
279:             } else if contains_name(items.as_slice(), "never") {
280:                 InlineNever
libsyntax/ext/expand.rs:
436:             fld.extsbox.insert(intern(name.as_slice()), ext);
437:             if attr::contains_name(it.attrs.as_slice(), "macro_export") {
438:                 SmallVector::one(it)
--
472:                     attr.meta_item_list().map_or(false, |phases| {
473:                         attr::contains_name(phases, "syntax")
474:                     })
libsyntax/ext/registrar.rs:
25:             ast::ItemFn(..) => {
26:                 if attr::contains_name(item.attrs.as_slice(),
27:                                        "macro_registrar") {
libsyntax/ext/expand.rs:
354: pub fn contains_macro_escape(attrs: &[ast::Attribute]) -> bool {
355:     attr::contains_name(attrs, "macro_escape")
356: }


libsyntax/attr.rs:164:1-164:1 -fn- definition:
pub fn mk_sugared_doc_attr(text: InternedString, lo: BytePos, hi: BytePos)
                           -> Attribute {
    let style = doc_comment_style(text.get());
references:- 2
libsyntax/parse/attr.rs:
40:               token::DOC_COMMENT(s) => {
41:                 let attr = ::attr::mk_sugared_doc_attr(
42:                     self.id_to_interned_str(s),
--
132:                     self.bump();
133:                     ::attr::mk_sugared_doc_attr(self.id_to_interned_str(s),
134:                                                 self.span.lo,


libsyntax/attr.rs:262:16-262:16 -enum- definition:
pub enum InlineAttr {
    InlineNone,
    InlineHint,
references:- 4
270: /// True if something like #[inline] is found in the list of attrs.
271: pub fn find_inline_attr(attrs: &[Attribute]) -> InlineAttr {
272:     // FIXME (#2809)---validate the usage of #[inline] and #[inline]


libsyntax/attr.rs:198:1-198:1 -fn- definition:
pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: &str)
                                 -> Option<InternedString> {
    attrs.iter()
references:- 3
libsyntax/parse/parser.rs:
4147:         let default_path = self.id_to_interned_str(id);
4148:         let file_path = match ::attr::first_attr_value_str_by_name(attrs,
4149:                                                                    "path") {
--
4169:         let dir_path = prefix.join(&mod_path);
4170:         let file_path = match ::attr::first_attr_value_str_by_name(
4171:                 outer_attrs, "path") {
libsyntax/attr.rs:
255: pub fn find_crateid(attrs: &[Attribute]) -> Option<CrateId> {
256:     match first_attr_value_str_by_name(attrs, "crate_id") {
257:         None => None,


libsyntax/attr.rs:339:57-339:57 -struct- definition:
/// Represents the #[deprecated="foo"] (etc) attributes.
pub struct Stability {
    pub level: StabilityLevel,
references:- 2
370:         return Some(Stability {
371:                 level: level,


libsyntax/attr.rs:460:22-460:22 -enum- definition:
pub enum ReprAttr {
    ReprAny,
    ReprInt(Span, IntType),
references:- 7
461: pub enum ReprAttr {
--
467: impl ReprAttr {
468:     pub fn is_ffi_safe(&self) -> bool {


libsyntax/attr.rs:181:59-181:59 -fn- definition:
/// span included in the `==` comparison a plain MetaItem.
pub fn contains(haystack: &[@ast::MetaItem],
                needle: @ast::MetaItem) -> bool {
references:- 2
321:                                     debug!("cfg(not({}[...]))", mi.name());
322:                                     contains(cfg, *mi)
323:                                 })
324:                             }
325:                             _ => contains(cfg, *cfg_mi)
326:                         }


libsyntax/attr.rs:24:1-24:1 -trait- definition:
pub trait AttrMetaMethods {
    // This could be changed to `fn check_name(&self, name: InternedString) ->
    // bool` which would facilitate a side table recording which
references:- 6
62: impl AttrMetaMethods for MetaItem {
63:     fn name(&self) -> InternedString {
--
295: /// test_cfg(`[foo="a", bar]`, `[cfg(bar, foo="b")]`) == false
296: pub fn test_cfg<AM: AttrMetaMethods, It: Iterator<AM>>
297:     (cfg: &[@MetaItem], mut metas: It) -> bool {
--
356: /// Find the first stability attribute. `None` if none exists.
357: pub fn find_stability<AM: AttrMetaMethods, It: Iterator<AM>>(mut metas: It)
358:                       -> Option<Stability> {


libsyntax/attr.rs:346:31-346:31 -enum- definition:
pub enum StabilityLevel {
    Deprecated,
    Experimental,
references:- 12
345: /// The available stability levels.
347: pub enum StabilityLevel {