]>
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 | 21 | use diagnostic::SpanHandler; |
e9174d1e | 22 | use feature_gate::GatedCfg; |
1a4d82fc | 23 | use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration}; |
85aaf69f | 24 | use parse::token::{InternedString, intern_and_get_ident}; |
1a4d82fc JJ |
25 | use parse::token; |
26 | use ptr::P; | |
27 | ||
28 | use std::cell::{RefCell, Cell}; | |
1a4d82fc JJ |
29 | use std::collections::HashSet; |
30 | use std::fmt; | |
31 | ||
e9174d1e SL |
32 | thread_local! { |
33 | static USED_ATTRS: RefCell<Vec<u64>> = RefCell::new(Vec::new()) | |
34 | } | |
1a4d82fc JJ |
35 | |
36 | pub fn mark_used(attr: &Attribute) { | |
37 | let AttrId(id) = attr.node.id; | |
e9174d1e SL |
38 | USED_ATTRS.with(|slot| { |
39 | let idx = (id / 64) as usize; | |
40 | let shift = id % 64; | |
41 | if slot.borrow().len() <= idx { | |
42 | slot.borrow_mut().resize(idx + 1, 0); | |
43 | } | |
44 | slot.borrow_mut()[idx] |= 1 << shift; | |
45 | }); | |
1a4d82fc | 46 | } |
223e47cc | 47 | |
1a4d82fc JJ |
48 | pub fn is_used(attr: &Attribute) -> bool { |
49 | let AttrId(id) = attr.node.id; | |
e9174d1e SL |
50 | USED_ATTRS.with(|slot| { |
51 | let idx = (id / 64) as usize; | |
52 | let shift = id % 64; | |
53 | slot.borrow().get(idx).map(|bits| bits & (1 << shift) != 0) | |
54 | .unwrap_or(false) | |
55 | }) | |
1a4d82fc | 56 | } |
223e47cc | 57 | |
1a4d82fc JJ |
58 | pub trait AttrMetaMethods { |
59 | fn check_name(&self, name: &str) -> bool { | |
85aaf69f | 60 | name == &self.name()[..] |
1a4d82fc JJ |
61 | } |
62 | ||
63 | /// Retrieve the name of the meta item, e.g. `foo` in `#[foo]`, | |
64 | /// `#[foo="bar"]` and `#[foo(bar)]` | |
65 | fn name(&self) -> InternedString; | |
66 | ||
67 | /// Gets the string value if self is a MetaNameValue variant | |
68 | /// containing a string, otherwise None. | |
69 | fn value_str(&self) -> Option<InternedString>; | |
70 | /// Gets a list of inner meta items from a list MetaItem type. | |
71 | fn meta_item_list<'a>(&'a self) -> Option<&'a [P<MetaItem>]>; | |
85aaf69f SL |
72 | |
73 | fn span(&self) -> Span; | |
223e47cc LB |
74 | } |
75 | ||
1a4d82fc JJ |
76 | impl AttrMetaMethods for Attribute { |
77 | fn check_name(&self, name: &str) -> bool { | |
85aaf69f | 78 | let matches = name == &self.name()[..]; |
1a4d82fc JJ |
79 | if matches { |
80 | mark_used(self); | |
81 | } | |
82 | matches | |
83 | } | |
84 | fn name(&self) -> InternedString { self.meta().name() } | |
85 | fn value_str(&self) -> Option<InternedString> { | |
86 | self.meta().value_str() | |
87 | } | |
88 | fn meta_item_list<'a>(&'a self) -> Option<&'a [P<MetaItem>]> { | |
89 | self.node.value.meta_item_list() | |
90 | } | |
85aaf69f | 91 | fn span(&self) -> Span { self.meta().span } |
223e47cc LB |
92 | } |
93 | ||
1a4d82fc JJ |
94 | impl AttrMetaMethods for MetaItem { |
95 | fn name(&self) -> InternedString { | |
96 | match self.node { | |
97 | MetaWord(ref n) => (*n).clone(), | |
98 | MetaNameValue(ref n, _) => (*n).clone(), | |
99 | MetaList(ref n, _) => (*n).clone(), | |
100 | } | |
101 | } | |
102 | ||
103 | fn value_str(&self) -> Option<InternedString> { | |
104 | match self.node { | |
105 | MetaNameValue(_, ref v) => { | |
106 | match v.node { | |
107 | ast::LitStr(ref s, _) => Some((*s).clone()), | |
108 | _ => None, | |
109 | } | |
110 | }, | |
111 | _ => None | |
112 | } | |
113 | } | |
114 | ||
115 | fn meta_item_list<'a>(&'a self) -> Option<&'a [P<MetaItem>]> { | |
116 | match self.node { | |
85aaf69f | 117 | MetaList(_, ref l) => Some(&l[..]), |
1a4d82fc JJ |
118 | _ => None |
119 | } | |
120 | } | |
85aaf69f | 121 | fn span(&self) -> Span { self.span } |
223e47cc LB |
122 | } |
123 | ||
1a4d82fc JJ |
124 | // Annoying, but required to get test_cfg to work |
125 | impl AttrMetaMethods for P<MetaItem> { | |
126 | fn name(&self) -> InternedString { (**self).name() } | |
127 | fn value_str(&self) -> Option<InternedString> { (**self).value_str() } | |
128 | fn meta_item_list<'a>(&'a self) -> Option<&'a [P<MetaItem>]> { | |
129 | (**self).meta_item_list() | |
130 | } | |
85aaf69f | 131 | fn span(&self) -> Span { (**self).span() } |
223e47cc LB |
132 | } |
133 | ||
1a4d82fc JJ |
134 | |
135 | pub trait AttributeMethods { | |
136 | fn meta<'a>(&'a self) -> &'a MetaItem; | |
137 | fn with_desugared_doc<T, F>(&self, f: F) -> T where | |
138 | F: FnOnce(&Attribute) -> T; | |
223e47cc LB |
139 | } |
140 | ||
1a4d82fc JJ |
141 | impl AttributeMethods for Attribute { |
142 | /// Extract the MetaItem from inside this Attribute. | |
143 | fn meta<'a>(&'a self) -> &'a MetaItem { | |
144 | &*self.node.value | |
145 | } | |
146 | ||
147 | /// Convert self to a normal #[doc="foo"] comment, if it is a | |
148 | /// comment like `///` or `/** */`. (Returns self unchanged for | |
149 | /// non-sugared doc attributes.) | |
150 | fn with_desugared_doc<T, F>(&self, f: F) -> T where | |
151 | F: FnOnce(&Attribute) -> T, | |
152 | { | |
153 | if self.node.is_sugared_doc { | |
154 | let comment = self.value_str().unwrap(); | |
155 | let meta = mk_name_value_item_str( | |
156 | InternedString::new("doc"), | |
157 | token::intern_and_get_ident(&strip_doc_comment_decoration( | |
85aaf69f | 158 | &comment))); |
1a4d82fc JJ |
159 | if self.node.style == ast::AttrOuter { |
160 | f(&mk_attr_outer(self.node.id, meta)) | |
161 | } else { | |
162 | f(&mk_attr_inner(self.node.id, meta)) | |
163 | } | |
164 | } else { | |
165 | f(self) | |
166 | } | |
167 | } | |
223e47cc LB |
168 | } |
169 | ||
1a4d82fc JJ |
170 | /* Constructors */ |
171 | ||
172 | pub fn mk_name_value_item_str(name: InternedString, value: InternedString) | |
173 | -> P<MetaItem> { | |
174 | let value_lit = dummy_spanned(ast::LitStr(value, ast::CookedStr)); | |
175 | mk_name_value_item(name, value_lit) | |
176 | } | |
223e47cc | 177 | |
1a4d82fc JJ |
178 | pub fn mk_name_value_item(name: InternedString, value: ast::Lit) |
179 | -> P<MetaItem> { | |
180 | P(dummy_spanned(MetaNameValue(name, value))) | |
223e47cc LB |
181 | } |
182 | ||
1a4d82fc JJ |
183 | pub fn mk_list_item(name: InternedString, items: Vec<P<MetaItem>>) -> P<MetaItem> { |
184 | P(dummy_spanned(MetaList(name, items))) | |
223e47cc LB |
185 | } |
186 | ||
1a4d82fc JJ |
187 | pub fn mk_word_item(name: InternedString) -> P<MetaItem> { |
188 | P(dummy_spanned(MetaWord(name))) | |
223e47cc LB |
189 | } |
190 | ||
85aaf69f | 191 | thread_local! { static NEXT_ATTR_ID: Cell<usize> = Cell::new(0) } |
223e47cc | 192 | |
1a4d82fc JJ |
193 | pub fn mk_attr_id() -> AttrId { |
194 | let id = NEXT_ATTR_ID.with(|slot| { | |
195 | let r = slot.get(); | |
196 | slot.set(r + 1); | |
197 | r | |
198 | }); | |
199 | AttrId(id) | |
223e47cc LB |
200 | } |
201 | ||
1a4d82fc JJ |
202 | /// Returns an inner attribute with the given value. |
203 | pub fn mk_attr_inner(id: AttrId, item: P<MetaItem>) -> Attribute { | |
204 | dummy_spanned(Attribute_ { | |
205 | id: id, | |
206 | style: ast::AttrInner, | |
207 | value: item, | |
208 | is_sugared_doc: false, | |
209 | }) | |
223e47cc LB |
210 | } |
211 | ||
1a4d82fc JJ |
212 | /// Returns an outer attribute with the given value. |
213 | pub fn mk_attr_outer(id: AttrId, item: P<MetaItem>) -> Attribute { | |
214 | dummy_spanned(Attribute_ { | |
215 | id: id, | |
216 | style: ast::AttrOuter, | |
217 | value: item, | |
218 | is_sugared_doc: false, | |
219 | }) | |
223e47cc LB |
220 | } |
221 | ||
1a4d82fc JJ |
222 | pub fn mk_sugared_doc_attr(id: AttrId, text: InternedString, lo: BytePos, |
223 | hi: BytePos) | |
224 | -> Attribute { | |
85aaf69f | 225 | let style = doc_comment_style(&text); |
1a4d82fc JJ |
226 | let lit = spanned(lo, hi, ast::LitStr(text, ast::CookedStr)); |
227 | let attr = Attribute_ { | |
228 | id: id, | |
229 | style: style, | |
230 | value: P(spanned(lo, hi, MetaNameValue(InternedString::new("doc"), | |
231 | lit))), | |
232 | is_sugared_doc: true | |
233 | }; | |
234 | spanned(lo, hi, attr) | |
223e47cc LB |
235 | } |
236 | ||
1a4d82fc JJ |
237 | /* Searching */ |
238 | /// Check if `needle` occurs in `haystack` by a structural | |
239 | /// comparison. This is slightly subtle, and relies on ignoring the | |
240 | /// span included in the `==` comparison a plain MetaItem. | |
241 | pub fn contains(haystack: &[P<MetaItem>], needle: &MetaItem) -> bool { | |
242 | debug!("attr::contains (name={})", needle.name()); | |
243 | haystack.iter().any(|item| { | |
244 | debug!(" testing: {}", item.name()); | |
245 | item.node == needle.node | |
246 | }) | |
223e47cc LB |
247 | } |
248 | ||
1a4d82fc JJ |
249 | pub fn contains_name<AM: AttrMetaMethods>(metas: &[AM], name: &str) -> bool { |
250 | debug!("attr::contains_name (name={})", name); | |
251 | metas.iter().any(|item| { | |
252 | debug!(" testing: {}", item.name()); | |
253 | item.check_name(name) | |
254 | }) | |
255 | } | |
223e47cc | 256 | |
1a4d82fc JJ |
257 | pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: &str) |
258 | -> Option<InternedString> { | |
259 | attrs.iter() | |
260 | .find(|at| at.check_name(name)) | |
261 | .and_then(|at| at.value_str()) | |
262 | } | |
223e47cc | 263 | |
1a4d82fc JJ |
264 | pub fn last_meta_item_value_str_by_name(items: &[P<MetaItem>], name: &str) |
265 | -> Option<InternedString> { | |
266 | items.iter() | |
267 | .rev() | |
268 | .find(|mi| mi.check_name(name)) | |
269 | .and_then(|i| i.value_str()) | |
223e47cc LB |
270 | } |
271 | ||
1a4d82fc JJ |
272 | /* Higher-level applications */ |
273 | ||
274 | pub fn sort_meta_items(items: Vec<P<MetaItem>>) -> Vec<P<MetaItem>> { | |
275 | // This is sort of stupid here, but we need to sort by | |
276 | // human-readable strings. | |
277 | let mut v = items.into_iter() | |
278 | .map(|mi| (mi.name(), mi)) | |
279 | .collect::<Vec<(InternedString, P<MetaItem>)>>(); | |
280 | ||
281 | v.sort_by(|&(ref a, _), &(ref b, _)| a.cmp(b)); | |
282 | ||
283 | // There doesn't seem to be a more optimal way to do this | |
284 | v.into_iter().map(|(_, m)| m.map(|Spanned {node, span}| { | |
285 | Spanned { | |
286 | node: match node { | |
287 | MetaList(n, mis) => MetaList(n, sort_meta_items(mis)), | |
288 | _ => node | |
289 | }, | |
290 | span: span | |
223e47cc | 291 | } |
1a4d82fc | 292 | })).collect() |
223e47cc LB |
293 | } |
294 | ||
1a4d82fc JJ |
295 | pub fn find_crate_name(attrs: &[Attribute]) -> Option<InternedString> { |
296 | first_attr_value_str_by_name(attrs, "crate_name") | |
223e47cc LB |
297 | } |
298 | ||
9346a6ac AL |
299 | /// Find the value of #[export_name=*] attribute and check its validity. |
300 | pub fn find_export_name_attr(diag: &SpanHandler, attrs: &[Attribute]) -> Option<InternedString> { | |
301 | attrs.iter().fold(None, |ia,attr| { | |
302 | if attr.check_name("export_name") { | |
303 | if let s@Some(_) = attr.value_str() { | |
304 | s | |
305 | } else { | |
306 | diag.span_err(attr.span, "export_name attribute has invalid format"); | |
307 | diag.handler.help("use #[export_name=\"*\"]"); | |
308 | None | |
309 | } | |
310 | } else { | |
311 | ia | |
312 | } | |
313 | }) | |
314 | } | |
315 | ||
c34b1796 | 316 | #[derive(Copy, Clone, PartialEq)] |
1a4d82fc | 317 | pub enum InlineAttr { |
c34b1796 AL |
318 | None, |
319 | Hint, | |
320 | Always, | |
321 | Never, | |
1a4d82fc JJ |
322 | } |
323 | ||
324 | /// Determine what `#[inline]` attribute is present in `attrs`, if any. | |
c34b1796 | 325 | pub fn find_inline_attr(diagnostic: Option<&SpanHandler>, attrs: &[Attribute]) -> InlineAttr { |
1a4d82fc | 326 | // FIXME (#2809)---validate the usage of #[inline] and #[inline] |
c34b1796 | 327 | attrs.iter().fold(InlineAttr::None, |ia,attr| { |
1a4d82fc JJ |
328 | match attr.node.value.node { |
329 | MetaWord(ref n) if *n == "inline" => { | |
330 | mark_used(attr); | |
c34b1796 | 331 | InlineAttr::Hint |
223e47cc | 332 | } |
1a4d82fc JJ |
333 | MetaList(ref n, ref items) if *n == "inline" => { |
334 | mark_used(attr); | |
c34b1796 AL |
335 | if items.len() != 1 { |
336 | diagnostic.map(|d|{ d.span_err(attr.span, "expected one argument"); }); | |
337 | InlineAttr::None | |
338 | } else if contains_name(&items[..], "always") { | |
339 | InlineAttr::Always | |
85aaf69f | 340 | } else if contains_name(&items[..], "never") { |
c34b1796 | 341 | InlineAttr::Never |
1a4d82fc | 342 | } else { |
c34b1796 AL |
343 | diagnostic.map(|d|{ d.span_err((*items[0]).span, "invalid argument"); }); |
344 | InlineAttr::None | |
223e47cc | 345 | } |
223e47cc | 346 | } |
1a4d82fc | 347 | _ => ia |
223e47cc | 348 | } |
1a4d82fc | 349 | }) |
223e47cc LB |
350 | } |
351 | ||
1a4d82fc JJ |
352 | /// True if `#[inline]` or `#[inline(always)]` is present in `attrs`. |
353 | pub fn requests_inline(attrs: &[Attribute]) -> bool { | |
c34b1796 AL |
354 | match find_inline_attr(None, attrs) { |
355 | InlineAttr::Hint | InlineAttr::Always => true, | |
356 | InlineAttr::None | InlineAttr::Never => false, | |
1a4d82fc | 357 | } |
223e47cc LB |
358 | } |
359 | ||
1a4d82fc | 360 | /// Tests if a cfg-pattern matches the cfg set |
e9174d1e SL |
361 | pub fn cfg_matches(diagnostic: &SpanHandler, cfgs: &[P<MetaItem>], cfg: &ast::MetaItem, |
362 | feature_gated_cfgs: &mut Vec<GatedCfg>) -> bool { | |
1a4d82fc | 363 | match cfg.node { |
85aaf69f | 364 | ast::MetaList(ref pred, ref mis) if &pred[..] == "any" => |
e9174d1e | 365 | mis.iter().any(|mi| cfg_matches(diagnostic, cfgs, &**mi, feature_gated_cfgs)), |
85aaf69f | 366 | ast::MetaList(ref pred, ref mis) if &pred[..] == "all" => |
e9174d1e | 367 | mis.iter().all(|mi| cfg_matches(diagnostic, cfgs, &**mi, feature_gated_cfgs)), |
85aaf69f | 368 | ast::MetaList(ref pred, ref mis) if &pred[..] == "not" => { |
1a4d82fc JJ |
369 | if mis.len() != 1 { |
370 | diagnostic.span_err(cfg.span, "expected 1 cfg-pattern"); | |
371 | return false; | |
372 | } | |
e9174d1e | 373 | !cfg_matches(diagnostic, cfgs, &*mis[0], feature_gated_cfgs) |
1a4d82fc JJ |
374 | } |
375 | ast::MetaList(ref pred, _) => { | |
85aaf69f | 376 | diagnostic.span_err(cfg.span, &format!("invalid predicate `{}`", pred)); |
1a4d82fc JJ |
377 | false |
378 | }, | |
e9174d1e SL |
379 | ast::MetaWord(_) | ast::MetaNameValue(..) => { |
380 | feature_gated_cfgs.extend(GatedCfg::gate(cfg)); | |
381 | contains(cfgs, cfg) | |
382 | } | |
223e47cc LB |
383 | } |
384 | } | |
385 | ||
85aaf69f | 386 | /// Represents the #[deprecated] and friends attributes. |
62682a34 | 387 | #[derive(RustcEncodable, RustcDecodable, Clone, Debug, PartialEq, Eq, Hash)] |
1a4d82fc JJ |
388 | pub struct Stability { |
389 | pub level: StabilityLevel, | |
85aaf69f SL |
390 | pub feature: InternedString, |
391 | pub since: Option<InternedString>, | |
392 | pub deprecated_since: Option<InternedString>, | |
393 | // The reason for the current stability level. If deprecated, the | |
394 | // reason for deprecation. | |
395 | pub reason: Option<InternedString>, | |
c1a9b12d SL |
396 | // The relevant rust-lang issue |
397 | pub issue: Option<u32> | |
223e47cc LB |
398 | } |
399 | ||
1a4d82fc | 400 | /// The available stability levels. |
62682a34 | 401 | #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Copy, Eq, Hash)] |
1a4d82fc | 402 | pub enum StabilityLevel { |
1a4d82fc JJ |
403 | Unstable, |
404 | Stable, | |
1a4d82fc | 405 | } |
223e47cc | 406 | |
85aaf69f | 407 | impl fmt::Display for StabilityLevel { |
1a4d82fc | 408 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
85aaf69f | 409 | fmt::Debug::fmt(self, f) |
223e47cc LB |
410 | } |
411 | } | |
412 | ||
85aaf69f | 413 | fn find_stability_generic<'a, |
1a4d82fc JJ |
414 | AM: AttrMetaMethods, |
415 | I: Iterator<Item=&'a AM>> | |
85aaf69f SL |
416 | (diagnostic: &SpanHandler, attrs: I, item_sp: Span) |
417 | -> (Option<Stability>, Vec<&'a AM>) { | |
418 | ||
419 | let mut stab: Option<Stability> = None; | |
c1a9b12d | 420 | let mut deprecated: Option<(Option<InternedString>, Option<InternedString>)> = None; |
85aaf69f SL |
421 | let mut used_attrs: Vec<&'a AM> = vec![]; |
422 | ||
423 | 'outer: for attr in attrs { | |
424 | let tag = attr.name(); | |
425 | let tag = &tag[..]; | |
426 | if tag != "deprecated" && tag != "unstable" && tag != "stable" { | |
427 | continue // not a stability level | |
428 | } | |
429 | ||
430 | used_attrs.push(attr); | |
431 | ||
c1a9b12d | 432 | let (feature, since, reason, issue) = match attr.meta_item_list() { |
85aaf69f SL |
433 | Some(metas) => { |
434 | let mut feature = None; | |
435 | let mut since = None; | |
436 | let mut reason = None; | |
c1a9b12d | 437 | let mut issue = None; |
62682a34 | 438 | for meta in metas { |
c1a9b12d SL |
439 | match &*meta.name() { |
440 | "feature" => { | |
441 | match meta.value_str() { | |
442 | Some(v) => feature = Some(v), | |
443 | None => { | |
444 | diagnostic.span_err(meta.span, "incorrect meta item"); | |
445 | continue 'outer; | |
446 | } | |
85aaf69f SL |
447 | } |
448 | } | |
c1a9b12d SL |
449 | "since" => { |
450 | match meta.value_str() { | |
451 | Some(v) => since = Some(v), | |
452 | None => { | |
453 | diagnostic.span_err(meta.span, "incorrect meta item"); | |
454 | continue 'outer; | |
455 | } | |
85aaf69f SL |
456 | } |
457 | } | |
c1a9b12d SL |
458 | "reason" => { |
459 | match meta.value_str() { | |
460 | Some(v) => reason = Some(v), | |
461 | None => { | |
462 | diagnostic.span_err(meta.span, "incorrect meta item"); | |
463 | continue 'outer; | |
464 | } | |
465 | } | |
466 | } | |
467 | "issue" => { | |
468 | match meta.value_str().and_then(|s| s.parse().ok()) { | |
469 | Some(v) => issue = Some(v), | |
470 | None => { | |
471 | diagnostic.span_err(meta.span, "incorrect meta item"); | |
472 | continue 'outer; | |
473 | } | |
85aaf69f SL |
474 | } |
475 | } | |
c1a9b12d | 476 | _ => {} |
85aaf69f SL |
477 | } |
478 | } | |
c1a9b12d | 479 | (feature, since, reason, issue) |
85aaf69f SL |
480 | } |
481 | None => { | |
482 | diagnostic.span_err(attr.span(), "incorrect stability attribute type"); | |
483 | continue | |
484 | } | |
1a4d82fc JJ |
485 | }; |
486 | ||
85aaf69f SL |
487 | // Deprecated tags don't require feature names |
488 | if feature == None && tag != "deprecated" { | |
489 | diagnostic.span_err(attr.span(), "missing 'feature'"); | |
490 | } | |
491 | ||
492 | // Unstable tags don't require a version | |
493 | if since == None && tag != "unstable" { | |
494 | diagnostic.span_err(attr.span(), "missing 'since'"); | |
495 | } | |
496 | ||
497 | if tag == "unstable" || tag == "stable" { | |
498 | if stab.is_some() { | |
499 | diagnostic.span_err(item_sp, "multiple stability levels"); | |
500 | } | |
501 | ||
502 | let level = match tag { | |
503 | "unstable" => Unstable, | |
504 | "stable" => Stable, | |
505 | _ => unreachable!() | |
506 | }; | |
507 | ||
508 | stab = Some(Stability { | |
509 | level: level, | |
510 | feature: feature.unwrap_or(intern_and_get_ident("bogus")), | |
511 | since: since, | |
512 | deprecated_since: None, | |
c1a9b12d SL |
513 | reason: reason, |
514 | issue: issue, | |
85aaf69f SL |
515 | }); |
516 | } else { // "deprecated" | |
517 | if deprecated.is_some() { | |
518 | diagnostic.span_err(item_sp, "multiple deprecated attributes"); | |
519 | } | |
520 | ||
c1a9b12d | 521 | deprecated = Some((since, reason)); |
85aaf69f | 522 | } |
223e47cc | 523 | } |
85aaf69f SL |
524 | |
525 | // Merge the deprecation info into the stability info | |
526 | if deprecated.is_some() { | |
527 | match stab { | |
528 | Some(ref mut s) => { | |
529 | let (since, reason) = deprecated.unwrap(); | |
c1a9b12d | 530 | s.deprecated_since = since; |
85aaf69f SL |
531 | s.reason = reason; |
532 | } | |
533 | None => { | |
534 | diagnostic.span_err(item_sp, "deprecated attribute must be paired with \ | |
535 | either stable or unstable attribute"); | |
536 | } | |
537 | } | |
c1a9b12d SL |
538 | } else if stab.as_ref().map_or(false, |s| s.level == Unstable && s.issue.is_none()) { |
539 | // non-deprecated unstable items need to point to issues. | |
e9174d1e SL |
540 | diagnostic.span_err(item_sp, |
541 | "non-deprecated unstable items need to point \ | |
542 | to an issue with `issue = \"NNN\"`"); | |
85aaf69f SL |
543 | } |
544 | ||
545 | (stab, used_attrs) | |
223e47cc LB |
546 | } |
547 | ||
1a4d82fc | 548 | /// Find the first stability attribute. `None` if none exists. |
85aaf69f SL |
549 | pub fn find_stability(diagnostic: &SpanHandler, attrs: &[Attribute], |
550 | item_sp: Span) -> Option<Stability> { | |
551 | let (s, used) = find_stability_generic(diagnostic, attrs.iter(), item_sp); | |
552 | for used in used { mark_used(used) } | |
553 | return s; | |
1a4d82fc | 554 | } |
223e47cc | 555 | |
1a4d82fc JJ |
556 | pub fn require_unique_names(diagnostic: &SpanHandler, metas: &[P<MetaItem>]) { |
557 | let mut set = HashSet::new(); | |
85aaf69f | 558 | for meta in metas { |
1a4d82fc | 559 | let name = meta.name(); |
223e47cc | 560 | |
1a4d82fc | 561 | if !set.insert(name.clone()) { |
9346a6ac AL |
562 | panic!(diagnostic.span_fatal(meta.span, |
563 | &format!("duplicate meta item `{}`", name))); | |
1a4d82fc | 564 | } |
223e47cc | 565 | } |
1a4d82fc | 566 | } |
223e47cc | 567 | |
1a4d82fc JJ |
568 | |
569 | /// Parse #[repr(...)] forms. | |
570 | /// | |
571 | /// Valid repr contents: any of the primitive integral type names (see | |
572 | /// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use | |
573 | /// the same discriminant size that the corresponding C enum would or C | |
574 | /// structure layout, and `packed` to remove padding. | |
575 | pub fn find_repr_attrs(diagnostic: &SpanHandler, attr: &Attribute) -> Vec<ReprAttr> { | |
576 | let mut acc = Vec::new(); | |
577 | match attr.node.value.node { | |
578 | ast::MetaList(ref s, ref items) if *s == "repr" => { | |
579 | mark_used(attr); | |
85aaf69f | 580 | for item in items { |
1a4d82fc JJ |
581 | match item.node { |
582 | ast::MetaWord(ref word) => { | |
85aaf69f | 583 | let hint = match &word[..] { |
1a4d82fc JJ |
584 | // Can't use "extern" because it's not a lexical identifier. |
585 | "C" => Some(ReprExtern), | |
586 | "packed" => Some(ReprPacked), | |
e9174d1e | 587 | "simd" => Some(ReprSimd), |
85aaf69f | 588 | _ => match int_type_of_word(&word) { |
1a4d82fc JJ |
589 | Some(ity) => Some(ReprInt(item.span, ity)), |
590 | None => { | |
591 | // Not a word we recognize | |
592 | diagnostic.span_err(item.span, | |
593 | "unrecognized representation hint"); | |
594 | None | |
595 | } | |
596 | } | |
597 | }; | |
598 | ||
599 | match hint { | |
600 | Some(h) => acc.push(h), | |
601 | None => { } | |
602 | } | |
603 | } | |
604 | // Not a word: | |
605 | _ => diagnostic.span_err(item.span, "unrecognized enum representation hint") | |
223e47cc LB |
606 | } |
607 | } | |
223e47cc | 608 | } |
1a4d82fc JJ |
609 | // Not a "repr" hint: ignore. |
610 | _ => { } | |
223e47cc | 611 | } |
1a4d82fc | 612 | acc |
223e47cc LB |
613 | } |
614 | ||
1a4d82fc JJ |
615 | fn int_type_of_word(s: &str) -> Option<IntType> { |
616 | match s { | |
617 | "i8" => Some(SignedInt(ast::TyI8)), | |
618 | "u8" => Some(UnsignedInt(ast::TyU8)), | |
619 | "i16" => Some(SignedInt(ast::TyI16)), | |
620 | "u16" => Some(UnsignedInt(ast::TyU16)), | |
621 | "i32" => Some(SignedInt(ast::TyI32)), | |
622 | "u32" => Some(UnsignedInt(ast::TyU32)), | |
623 | "i64" => Some(SignedInt(ast::TyI64)), | |
624 | "u64" => Some(UnsignedInt(ast::TyU64)), | |
c34b1796 AL |
625 | "isize" => Some(SignedInt(ast::TyIs)), |
626 | "usize" => Some(UnsignedInt(ast::TyUs)), | |
1a4d82fc JJ |
627 | _ => None |
628 | } | |
629 | } | |
223e47cc | 630 | |
c34b1796 | 631 | #[derive(PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)] |
1a4d82fc JJ |
632 | pub enum ReprAttr { |
633 | ReprAny, | |
634 | ReprInt(Span, IntType), | |
635 | ReprExtern, | |
636 | ReprPacked, | |
e9174d1e | 637 | ReprSimd, |
223e47cc LB |
638 | } |
639 | ||
1a4d82fc JJ |
640 | impl ReprAttr { |
641 | pub fn is_ffi_safe(&self) -> bool { | |
642 | match *self { | |
643 | ReprAny => false, | |
644 | ReprInt(_sp, ity) => ity.is_ffi_safe(), | |
645 | ReprExtern => true, | |
e9174d1e SL |
646 | ReprPacked => false, |
647 | ReprSimd => true, | |
223e47cc LB |
648 | } |
649 | } | |
650 | } | |
651 | ||
c34b1796 | 652 | #[derive(Eq, Hash, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)] |
1a4d82fc JJ |
653 | pub enum IntType { |
654 | SignedInt(ast::IntTy), | |
655 | UnsignedInt(ast::UintTy) | |
223e47cc LB |
656 | } |
657 | ||
1a4d82fc JJ |
658 | impl IntType { |
659 | #[inline] | |
660 | pub fn is_signed(self) -> bool { | |
661 | match self { | |
662 | SignedInt(..) => true, | |
663 | UnsignedInt(..) => false | |
223e47cc LB |
664 | } |
665 | } | |
1a4d82fc JJ |
666 | fn is_ffi_safe(self) -> bool { |
667 | match self { | |
668 | SignedInt(ast::TyI8) | UnsignedInt(ast::TyU8) | | |
669 | SignedInt(ast::TyI16) | UnsignedInt(ast::TyU16) | | |
670 | SignedInt(ast::TyI32) | UnsignedInt(ast::TyU32) | | |
671 | SignedInt(ast::TyI64) | UnsignedInt(ast::TyU64) => true, | |
c34b1796 | 672 | SignedInt(ast::TyIs) | UnsignedInt(ast::TyUs) => false |
223e47cc LB |
673 | } |
674 | } | |
675 | } |