]>
Commit | Line | Data |
---|---|---|
1a4d82fc | 1 | // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT |
223e47cc LB |
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 | ||
1a4d82fc | 11 | // Functions dealing with attributes and meta items |
223e47cc | 12 | |
1a4d82fc JJ |
13 | pub use self::StabilityLevel::*; |
14 | pub use self::ReprAttr::*; | |
15 | pub use self::IntType::*; | |
223e47cc LB |
16 | |
17 | use ast; | |
1a4d82fc JJ |
18 | use ast::{AttrId, Attribute, Attribute_, MetaItem, MetaWord, MetaNameValue, MetaList}; |
19 | use codemap::{Span, Spanned, spanned, dummy_spanned}; | |
223e47cc | 20 | use codemap::BytePos; |
1a4d82fc JJ |
21 | use diagnostic::SpanHandler; |
22 | use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration}; | |
85aaf69f | 23 | use parse::token::{InternedString, intern_and_get_ident}; |
1a4d82fc JJ |
24 | use parse::token; |
25 | use ptr::P; | |
26 | ||
27 | use std::cell::{RefCell, Cell}; | |
85aaf69f | 28 | use std::collections::BitSet; |
1a4d82fc JJ |
29 | use std::collections::HashSet; |
30 | use std::fmt; | |
31 | ||
85aaf69f | 32 | thread_local! { static USED_ATTRS: RefCell<BitSet> = RefCell::new(BitSet::new()) } |
1a4d82fc JJ |
33 | |
34 | pub fn mark_used(attr: &Attribute) { | |
35 | let AttrId(id) = attr.node.id; | |
36 | USED_ATTRS.with(|slot| slot.borrow_mut().insert(id)); | |
37 | } | |
223e47cc | 38 | |
1a4d82fc JJ |
39 | pub fn is_used(attr: &Attribute) -> bool { |
40 | let AttrId(id) = attr.node.id; | |
41 | USED_ATTRS.with(|slot| slot.borrow().contains(&id)) | |
42 | } | |
223e47cc | 43 | |
1a4d82fc JJ |
44 | pub trait AttrMetaMethods { |
45 | fn check_name(&self, name: &str) -> bool { | |
85aaf69f | 46 | name == &self.name()[..] |
1a4d82fc JJ |
47 | } |
48 | ||
49 | /// Retrieve the name of the meta item, e.g. `foo` in `#[foo]`, | |
50 | /// `#[foo="bar"]` and `#[foo(bar)]` | |
51 | fn name(&self) -> InternedString; | |
52 | ||
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>]>; | |
85aaf69f SL |
58 | |
59 | fn span(&self) -> Span; | |
223e47cc LB |
60 | } |
61 | ||
1a4d82fc JJ |
62 | impl AttrMetaMethods for Attribute { |
63 | fn check_name(&self, name: &str) -> bool { | |
85aaf69f | 64 | let matches = name == &self.name()[..]; |
1a4d82fc JJ |
65 | if matches { |
66 | mark_used(self); | |
67 | } | |
68 | matches | |
69 | } | |
70 | fn name(&self) -> InternedString { self.meta().name() } | |
71 | fn value_str(&self) -> Option<InternedString> { | |
72 | self.meta().value_str() | |
73 | } | |
74 | fn meta_item_list<'a>(&'a self) -> Option<&'a [P<MetaItem>]> { | |
75 | self.node.value.meta_item_list() | |
76 | } | |
85aaf69f | 77 | fn span(&self) -> Span { self.meta().span } |
223e47cc LB |
78 | } |
79 | ||
1a4d82fc JJ |
80 | impl AttrMetaMethods for MetaItem { |
81 | fn name(&self) -> InternedString { | |
82 | match self.node { | |
83 | MetaWord(ref n) => (*n).clone(), | |
84 | MetaNameValue(ref n, _) => (*n).clone(), | |
85 | MetaList(ref n, _) => (*n).clone(), | |
86 | } | |
87 | } | |
88 | ||
89 | fn value_str(&self) -> Option<InternedString> { | |
90 | match self.node { | |
91 | MetaNameValue(_, ref v) => { | |
92 | match v.node { | |
93 | ast::LitStr(ref s, _) => Some((*s).clone()), | |
94 | _ => None, | |
95 | } | |
96 | }, | |
97 | _ => None | |
98 | } | |
99 | } | |
100 | ||
101 | fn meta_item_list<'a>(&'a self) -> Option<&'a [P<MetaItem>]> { | |
102 | match self.node { | |
85aaf69f | 103 | MetaList(_, ref l) => Some(&l[..]), |
1a4d82fc JJ |
104 | _ => None |
105 | } | |
106 | } | |
85aaf69f | 107 | fn span(&self) -> Span { self.span } |
223e47cc LB |
108 | } |
109 | ||
1a4d82fc JJ |
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() | |
116 | } | |
85aaf69f | 117 | fn span(&self) -> Span { (**self).span() } |
223e47cc LB |
118 | } |
119 | ||
1a4d82fc JJ |
120 | |
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; | |
223e47cc LB |
125 | } |
126 | ||
1a4d82fc JJ |
127 | impl AttributeMethods for Attribute { |
128 | /// Extract the MetaItem from inside this Attribute. | |
129 | fn meta<'a>(&'a self) -> &'a MetaItem { | |
130 | &*self.node.value | |
131 | } | |
132 | ||
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, | |
138 | { | |
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( | |
85aaf69f | 144 | &comment))); |
1a4d82fc JJ |
145 | if self.node.style == ast::AttrOuter { |
146 | f(&mk_attr_outer(self.node.id, meta)) | |
147 | } else { | |
148 | f(&mk_attr_inner(self.node.id, meta)) | |
149 | } | |
150 | } else { | |
151 | f(self) | |
152 | } | |
153 | } | |
223e47cc LB |
154 | } |
155 | ||
1a4d82fc JJ |
156 | /* Constructors */ |
157 | ||
158 | pub fn mk_name_value_item_str(name: InternedString, value: InternedString) | |
159 | -> P<MetaItem> { | |
160 | let value_lit = dummy_spanned(ast::LitStr(value, ast::CookedStr)); | |
161 | mk_name_value_item(name, value_lit) | |
162 | } | |
223e47cc | 163 | |
1a4d82fc JJ |
164 | pub fn mk_name_value_item(name: InternedString, value: ast::Lit) |
165 | -> P<MetaItem> { | |
166 | P(dummy_spanned(MetaNameValue(name, value))) | |
223e47cc LB |
167 | } |
168 | ||
1a4d82fc JJ |
169 | pub fn mk_list_item(name: InternedString, items: Vec<P<MetaItem>>) -> P<MetaItem> { |
170 | P(dummy_spanned(MetaList(name, items))) | |
223e47cc LB |
171 | } |
172 | ||
1a4d82fc JJ |
173 | pub fn mk_word_item(name: InternedString) -> P<MetaItem> { |
174 | P(dummy_spanned(MetaWord(name))) | |
223e47cc LB |
175 | } |
176 | ||
85aaf69f | 177 | thread_local! { static NEXT_ATTR_ID: Cell<usize> = Cell::new(0) } |
223e47cc | 178 | |
1a4d82fc JJ |
179 | pub fn mk_attr_id() -> AttrId { |
180 | let id = NEXT_ATTR_ID.with(|slot| { | |
181 | let r = slot.get(); | |
182 | slot.set(r + 1); | |
183 | r | |
184 | }); | |
185 | AttrId(id) | |
223e47cc LB |
186 | } |
187 | ||
1a4d82fc JJ |
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_ { | |
191 | id: id, | |
192 | style: ast::AttrInner, | |
193 | value: item, | |
194 | is_sugared_doc: false, | |
195 | }) | |
223e47cc LB |
196 | } |
197 | ||
1a4d82fc JJ |
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_ { | |
201 | id: id, | |
202 | style: ast::AttrOuter, | |
203 | value: item, | |
204 | is_sugared_doc: false, | |
205 | }) | |
223e47cc LB |
206 | } |
207 | ||
1a4d82fc JJ |
208 | pub fn mk_sugared_doc_attr(id: AttrId, text: InternedString, lo: BytePos, |
209 | hi: BytePos) | |
210 | -> Attribute { | |
85aaf69f | 211 | let style = doc_comment_style(&text); |
1a4d82fc JJ |
212 | let lit = spanned(lo, hi, ast::LitStr(text, ast::CookedStr)); |
213 | let attr = Attribute_ { | |
214 | id: id, | |
215 | style: style, | |
216 | value: P(spanned(lo, hi, MetaNameValue(InternedString::new("doc"), | |
217 | lit))), | |
218 | is_sugared_doc: true | |
219 | }; | |
220 | spanned(lo, hi, attr) | |
223e47cc LB |
221 | } |
222 | ||
1a4d82fc JJ |
223 | /* Searching */ |
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 | |
232 | }) | |
223e47cc LB |
233 | } |
234 | ||
1a4d82fc JJ |
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) | |
240 | }) | |
241 | } | |
223e47cc | 242 | |
1a4d82fc JJ |
243 | pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: &str) |
244 | -> Option<InternedString> { | |
245 | attrs.iter() | |
246 | .find(|at| at.check_name(name)) | |
247 | .and_then(|at| at.value_str()) | |
248 | } | |
223e47cc | 249 | |
1a4d82fc JJ |
250 | pub fn last_meta_item_value_str_by_name(items: &[P<MetaItem>], name: &str) |
251 | -> Option<InternedString> { | |
252 | items.iter() | |
253 | .rev() | |
254 | .find(|mi| mi.check_name(name)) | |
255 | .and_then(|i| i.value_str()) | |
223e47cc LB |
256 | } |
257 | ||
1a4d82fc JJ |
258 | /* Higher-level applications */ |
259 | ||
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>)>>(); | |
266 | ||
267 | v.sort_by(|&(ref a, _), &(ref b, _)| a.cmp(b)); | |
268 | ||
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}| { | |
271 | Spanned { | |
272 | node: match node { | |
273 | MetaList(n, mis) => MetaList(n, sort_meta_items(mis)), | |
274 | _ => node | |
275 | }, | |
276 | span: span | |
223e47cc | 277 | } |
1a4d82fc | 278 | })).collect() |
223e47cc LB |
279 | } |
280 | ||
1a4d82fc JJ |
281 | pub fn find_crate_name(attrs: &[Attribute]) -> Option<InternedString> { |
282 | first_attr_value_str_by_name(attrs, "crate_name") | |
223e47cc LB |
283 | } |
284 | ||
9346a6ac AL |
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() { | |
290 | s | |
291 | } else { | |
292 | diag.span_err(attr.span, "export_name attribute has invalid format"); | |
293 | diag.handler.help("use #[export_name=\"*\"]"); | |
294 | None | |
295 | } | |
296 | } else { | |
297 | ia | |
298 | } | |
299 | }) | |
300 | } | |
301 | ||
c34b1796 | 302 | #[derive(Copy, Clone, PartialEq)] |
1a4d82fc | 303 | pub enum InlineAttr { |
c34b1796 AL |
304 | None, |
305 | Hint, | |
306 | Always, | |
307 | Never, | |
1a4d82fc JJ |
308 | } |
309 | ||
310 | /// Determine what `#[inline]` attribute is present in `attrs`, if any. | |
c34b1796 | 311 | pub fn find_inline_attr(diagnostic: Option<&SpanHandler>, attrs: &[Attribute]) -> InlineAttr { |
1a4d82fc | 312 | // FIXME (#2809)---validate the usage of #[inline] and #[inline] |
c34b1796 | 313 | attrs.iter().fold(InlineAttr::None, |ia,attr| { |
1a4d82fc JJ |
314 | match attr.node.value.node { |
315 | MetaWord(ref n) if *n == "inline" => { | |
316 | mark_used(attr); | |
c34b1796 | 317 | InlineAttr::Hint |
223e47cc | 318 | } |
1a4d82fc JJ |
319 | MetaList(ref n, ref items) if *n == "inline" => { |
320 | mark_used(attr); | |
c34b1796 AL |
321 | if items.len() != 1 { |
322 | diagnostic.map(|d|{ d.span_err(attr.span, "expected one argument"); }); | |
323 | InlineAttr::None | |
324 | } else if contains_name(&items[..], "always") { | |
325 | InlineAttr::Always | |
85aaf69f | 326 | } else if contains_name(&items[..], "never") { |
c34b1796 | 327 | InlineAttr::Never |
1a4d82fc | 328 | } else { |
c34b1796 AL |
329 | diagnostic.map(|d|{ d.span_err((*items[0]).span, "invalid argument"); }); |
330 | InlineAttr::None | |
223e47cc | 331 | } |
223e47cc | 332 | } |
1a4d82fc | 333 | _ => ia |
223e47cc | 334 | } |
1a4d82fc | 335 | }) |
223e47cc LB |
336 | } |
337 | ||
1a4d82fc JJ |
338 | /// True if `#[inline]` or `#[inline(always)]` is present in `attrs`. |
339 | pub fn requests_inline(attrs: &[Attribute]) -> bool { | |
c34b1796 AL |
340 | match find_inline_attr(None, attrs) { |
341 | InlineAttr::Hint | InlineAttr::Always => true, | |
342 | InlineAttr::None | InlineAttr::Never => false, | |
1a4d82fc | 343 | } |
223e47cc LB |
344 | } |
345 | ||
1a4d82fc JJ |
346 | /// Tests if a cfg-pattern matches the cfg set |
347 | pub fn cfg_matches(diagnostic: &SpanHandler, cfgs: &[P<MetaItem>], cfg: &ast::MetaItem) -> bool { | |
348 | match cfg.node { | |
85aaf69f | 349 | ast::MetaList(ref pred, ref mis) if &pred[..] == "any" => |
1a4d82fc | 350 | mis.iter().any(|mi| cfg_matches(diagnostic, cfgs, &**mi)), |
85aaf69f | 351 | ast::MetaList(ref pred, ref mis) if &pred[..] == "all" => |
1a4d82fc | 352 | mis.iter().all(|mi| cfg_matches(diagnostic, cfgs, &**mi)), |
85aaf69f | 353 | ast::MetaList(ref pred, ref mis) if &pred[..] == "not" => { |
1a4d82fc JJ |
354 | if mis.len() != 1 { |
355 | diagnostic.span_err(cfg.span, "expected 1 cfg-pattern"); | |
356 | return false; | |
357 | } | |
358 | !cfg_matches(diagnostic, cfgs, &*mis[0]) | |
359 | } | |
360 | ast::MetaList(ref pred, _) => { | |
85aaf69f | 361 | diagnostic.span_err(cfg.span, &format!("invalid predicate `{}`", pred)); |
1a4d82fc JJ |
362 | false |
363 | }, | |
364 | ast::MetaWord(_) | ast::MetaNameValue(..) => contains(cfgs, cfg), | |
223e47cc LB |
365 | } |
366 | } | |
367 | ||
85aaf69f | 368 | /// Represents the #[deprecated] and friends attributes. |
62682a34 | 369 | #[derive(RustcEncodable, RustcDecodable, Clone, Debug, PartialEq, Eq, Hash)] |
1a4d82fc JJ |
370 | pub struct Stability { |
371 | pub level: StabilityLevel, | |
85aaf69f SL |
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>, | |
223e47cc LB |
378 | } |
379 | ||
1a4d82fc | 380 | /// The available stability levels. |
62682a34 | 381 | #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Copy, Eq, Hash)] |
1a4d82fc | 382 | pub enum StabilityLevel { |
1a4d82fc JJ |
383 | Unstable, |
384 | Stable, | |
1a4d82fc | 385 | } |
223e47cc | 386 | |
85aaf69f | 387 | impl fmt::Display for StabilityLevel { |
1a4d82fc | 388 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
85aaf69f | 389 | fmt::Debug::fmt(self, f) |
223e47cc LB |
390 | } |
391 | } | |
392 | ||
85aaf69f | 393 | fn find_stability_generic<'a, |
1a4d82fc JJ |
394 | AM: AttrMetaMethods, |
395 | I: Iterator<Item=&'a AM>> | |
85aaf69f SL |
396 | (diagnostic: &SpanHandler, attrs: I, item_sp: Span) |
397 | -> (Option<Stability>, Vec<&'a AM>) { | |
398 | ||
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![]; | |
402 | ||
403 | 'outer: for attr in attrs { | |
404 | let tag = attr.name(); | |
405 | let tag = &tag[..]; | |
406 | if tag != "deprecated" && tag != "unstable" && tag != "stable" { | |
407 | continue // not a stability level | |
408 | } | |
409 | ||
410 | used_attrs.push(attr); | |
411 | ||
412 | let (feature, since, reason) = match attr.meta_item_list() { | |
413 | Some(metas) => { | |
414 | let mut feature = None; | |
415 | let mut since = None; | |
416 | let mut reason = None; | |
62682a34 | 417 | for meta in metas { |
85aaf69f SL |
418 | if meta.name() == "feature" { |
419 | match meta.value_str() { | |
420 | Some(v) => feature = Some(v), | |
421 | None => { | |
422 | diagnostic.span_err(meta.span, "incorrect meta item"); | |
423 | continue 'outer; | |
424 | } | |
425 | } | |
426 | } | |
427 | if &meta.name()[..] == "since" { | |
428 | match meta.value_str() { | |
429 | Some(v) => since = Some(v), | |
430 | None => { | |
431 | diagnostic.span_err(meta.span, "incorrect meta item"); | |
432 | continue 'outer; | |
433 | } | |
434 | } | |
435 | } | |
436 | if &meta.name()[..] == "reason" { | |
437 | match meta.value_str() { | |
438 | Some(v) => reason = Some(v), | |
439 | None => { | |
440 | diagnostic.span_err(meta.span, "incorrect meta item"); | |
441 | continue 'outer; | |
442 | } | |
443 | } | |
444 | } | |
445 | } | |
446 | (feature, since, reason) | |
447 | } | |
448 | None => { | |
449 | diagnostic.span_err(attr.span(), "incorrect stability attribute type"); | |
450 | continue | |
451 | } | |
1a4d82fc JJ |
452 | }; |
453 | ||
85aaf69f SL |
454 | // Deprecated tags don't require feature names |
455 | if feature == None && tag != "deprecated" { | |
456 | diagnostic.span_err(attr.span(), "missing 'feature'"); | |
457 | } | |
458 | ||
459 | // Unstable tags don't require a version | |
460 | if since == None && tag != "unstable" { | |
461 | diagnostic.span_err(attr.span(), "missing 'since'"); | |
462 | } | |
463 | ||
464 | if tag == "unstable" || tag == "stable" { | |
465 | if stab.is_some() { | |
466 | diagnostic.span_err(item_sp, "multiple stability levels"); | |
467 | } | |
468 | ||
469 | let level = match tag { | |
470 | "unstable" => Unstable, | |
471 | "stable" => Stable, | |
472 | _ => unreachable!() | |
473 | }; | |
474 | ||
475 | stab = Some(Stability { | |
476 | level: level, | |
477 | feature: feature.unwrap_or(intern_and_get_ident("bogus")), | |
478 | since: since, | |
479 | deprecated_since: None, | |
480 | reason: reason | |
481 | }); | |
482 | } else { // "deprecated" | |
483 | if deprecated.is_some() { | |
484 | diagnostic.span_err(item_sp, "multiple deprecated attributes"); | |
485 | } | |
486 | ||
487 | deprecated = Some((since.unwrap_or(intern_and_get_ident("bogus")), reason)); | |
488 | } | |
223e47cc | 489 | } |
85aaf69f SL |
490 | |
491 | // Merge the deprecation info into the stability info | |
492 | if deprecated.is_some() { | |
493 | match stab { | |
494 | Some(ref mut s) => { | |
495 | let (since, reason) = deprecated.unwrap(); | |
496 | s.deprecated_since = Some(since); | |
497 | s.reason = reason; | |
498 | } | |
499 | None => { | |
500 | diagnostic.span_err(item_sp, "deprecated attribute must be paired with \ | |
501 | either stable or unstable attribute"); | |
502 | } | |
503 | } | |
504 | } | |
505 | ||
506 | (stab, used_attrs) | |
223e47cc LB |
507 | } |
508 | ||
1a4d82fc | 509 | /// Find the first stability attribute. `None` if none exists. |
85aaf69f SL |
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) } | |
514 | return s; | |
1a4d82fc | 515 | } |
223e47cc | 516 | |
1a4d82fc JJ |
517 | pub fn require_unique_names(diagnostic: &SpanHandler, metas: &[P<MetaItem>]) { |
518 | let mut set = HashSet::new(); | |
85aaf69f | 519 | for meta in metas { |
1a4d82fc | 520 | let name = meta.name(); |
223e47cc | 521 | |
1a4d82fc | 522 | if !set.insert(name.clone()) { |
9346a6ac AL |
523 | panic!(diagnostic.span_fatal(meta.span, |
524 | &format!("duplicate meta item `{}`", name))); | |
1a4d82fc | 525 | } |
223e47cc | 526 | } |
1a4d82fc | 527 | } |
223e47cc | 528 | |
1a4d82fc JJ |
529 | |
530 | /// Parse #[repr(...)] forms. | |
531 | /// | |
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" => { | |
540 | mark_used(attr); | |
85aaf69f | 541 | for item in items { |
1a4d82fc JJ |
542 | match item.node { |
543 | ast::MetaWord(ref word) => { | |
85aaf69f | 544 | let hint = match &word[..] { |
1a4d82fc JJ |
545 | // Can't use "extern" because it's not a lexical identifier. |
546 | "C" => Some(ReprExtern), | |
547 | "packed" => Some(ReprPacked), | |
85aaf69f | 548 | _ => match int_type_of_word(&word) { |
1a4d82fc JJ |
549 | Some(ity) => Some(ReprInt(item.span, ity)), |
550 | None => { | |
551 | // Not a word we recognize | |
552 | diagnostic.span_err(item.span, | |
553 | "unrecognized representation hint"); | |
554 | None | |
555 | } | |
556 | } | |
557 | }; | |
558 | ||
559 | match hint { | |
560 | Some(h) => acc.push(h), | |
561 | None => { } | |
562 | } | |
563 | } | |
564 | // Not a word: | |
565 | _ => diagnostic.span_err(item.span, "unrecognized enum representation hint") | |
223e47cc LB |
566 | } |
567 | } | |
223e47cc | 568 | } |
1a4d82fc JJ |
569 | // Not a "repr" hint: ignore. |
570 | _ => { } | |
223e47cc | 571 | } |
1a4d82fc | 572 | acc |
223e47cc LB |
573 | } |
574 | ||
1a4d82fc JJ |
575 | fn int_type_of_word(s: &str) -> Option<IntType> { |
576 | match s { | |
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)), | |
c34b1796 AL |
585 | "isize" => Some(SignedInt(ast::TyIs)), |
586 | "usize" => Some(UnsignedInt(ast::TyUs)), | |
1a4d82fc JJ |
587 | _ => None |
588 | } | |
589 | } | |
223e47cc | 590 | |
c34b1796 | 591 | #[derive(PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)] |
1a4d82fc JJ |
592 | pub enum ReprAttr { |
593 | ReprAny, | |
594 | ReprInt(Span, IntType), | |
595 | ReprExtern, | |
596 | ReprPacked, | |
223e47cc LB |
597 | } |
598 | ||
1a4d82fc JJ |
599 | impl ReprAttr { |
600 | pub fn is_ffi_safe(&self) -> bool { | |
601 | match *self { | |
602 | ReprAny => false, | |
603 | ReprInt(_sp, ity) => ity.is_ffi_safe(), | |
604 | ReprExtern => true, | |
605 | ReprPacked => false | |
223e47cc LB |
606 | } |
607 | } | |
608 | } | |
609 | ||
c34b1796 | 610 | #[derive(Eq, Hash, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)] |
1a4d82fc JJ |
611 | pub enum IntType { |
612 | SignedInt(ast::IntTy), | |
613 | UnsignedInt(ast::UintTy) | |
223e47cc LB |
614 | } |
615 | ||
1a4d82fc JJ |
616 | impl IntType { |
617 | #[inline] | |
618 | pub fn is_signed(self) -> bool { | |
619 | match self { | |
620 | SignedInt(..) => true, | |
621 | UnsignedInt(..) => false | |
223e47cc LB |
622 | } |
623 | } | |
1a4d82fc JJ |
624 | fn is_ffi_safe(self) -> bool { |
625 | match self { | |
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, | |
c34b1796 | 630 | SignedInt(ast::TyIs) | UnsignedInt(ast::TyUs) => false |
223e47cc LB |
631 | } |
632 | } | |
633 | } |