(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(name: InternedString, value: InternedString)
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(name: InternedString, value: ast::Lit)
145 -> @MetaItem {
146 @dummy_spanned(MetaNameValue(name, value))
147 }
148
149 pub fn mk_list_item(name: InternedString, items: Vec<@MetaItem> ) -> @MetaItem {
150 @dummy_spanned(MetaList(name, items))
151 }
152
153 pub fn mk_word_item(name: InternedString) -> @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(text: InternedString, lo: BytePos, hi: BytePos)
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 metas: It) -> 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 metas: It)
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, acc: ReprAttr)
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:- 7478: 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:- 6276: 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:- 2libsyntax/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:- 4270: /// 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:- 3libsyntax/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:- 2370: return Some(Stability {
371: level: level,
libsyntax/attr.rs:460:22-460:22 -enum- definition:
pub enum ReprAttr {
ReprAny,
ReprInt(Span, IntType),
references:- 7461: 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:- 2321: 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:- 662: 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:- 12345: /// The available stability levels.
347: pub enum StabilityLevel {