]>
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; | |
cc61c64b | 18 | use ast::{AttrId, Attribute, Name, Ident}; |
9e0c209e | 19 | use ast::{MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind}; |
cc61c64b XL |
20 | use ast::{Lit, LitKind, Expr, ExprKind, Item, Local, Stmt, StmtKind}; |
21 | use codemap::{Spanned, respan, dummy_spanned}; | |
22 | use syntax_pos::{Span, DUMMY_SP}; | |
9cc50fc6 | 23 | use errors::Handler; |
3157f602 | 24 | use feature_gate::{Features, GatedCfg}; |
1a4d82fc | 25 | use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration}; |
cc61c64b XL |
26 | use parse::parser::Parser; |
27 | use parse::{self, ParseSess, PResult}; | |
28 | use parse::token::{self, Token}; | |
1a4d82fc | 29 | use ptr::P; |
476ff2be | 30 | use symbol::Symbol; |
cc61c64b | 31 | use tokenstream::{TokenStream, TokenTree, Delimited}; |
3157f602 | 32 | use util::ThinVec; |
0531ce1d | 33 | use GLOBALS; |
1a4d82fc | 34 | |
cc61c64b | 35 | use std::iter; |
1a4d82fc | 36 | |
3157f602 | 37 | enum AttrError { |
476ff2be SL |
38 | MultipleItem(Name), |
39 | UnknownMetaItem(Name), | |
3157f602 XL |
40 | MissingSince, |
41 | MissingFeature, | |
42 | MultipleStabilityLevels, | |
9e0c209e | 43 | UnsupportedLiteral |
3157f602 XL |
44 | } |
45 | ||
46 | fn handle_errors(diag: &Handler, span: Span, error: AttrError) { | |
47 | match error { | |
48 | AttrError::MultipleItem(item) => span_err!(diag, span, E0538, | |
49 | "multiple '{}' items", item), | |
50 | AttrError::UnknownMetaItem(item) => span_err!(diag, span, E0541, | |
51 | "unknown meta item '{}'", item), | |
52 | AttrError::MissingSince => span_err!(diag, span, E0542, "missing 'since'"), | |
53 | AttrError::MissingFeature => span_err!(diag, span, E0546, "missing 'feature'"), | |
54 | AttrError::MultipleStabilityLevels => span_err!(diag, span, E0544, | |
55 | "multiple stability levels"), | |
9e0c209e | 56 | AttrError::UnsupportedLiteral => span_err!(diag, span, E0565, "unsupported literal"), |
3157f602 XL |
57 | } |
58 | } | |
59 | ||
1a4d82fc | 60 | pub fn mark_used(attr: &Attribute) { |
9e0c209e | 61 | debug!("Marking {:?} as used.", attr); |
476ff2be | 62 | let AttrId(id) = attr.id; |
0531ce1d XL |
63 | GLOBALS.with(|globals| { |
64 | let mut slot = globals.used_attrs.lock(); | |
e9174d1e SL |
65 | let idx = (id / 64) as usize; |
66 | let shift = id % 64; | |
0531ce1d XL |
67 | if slot.len() <= idx { |
68 | slot.resize(idx + 1, 0); | |
e9174d1e | 69 | } |
0531ce1d | 70 | slot[idx] |= 1 << shift; |
e9174d1e | 71 | }); |
1a4d82fc | 72 | } |
223e47cc | 73 | |
1a4d82fc | 74 | pub fn is_used(attr: &Attribute) -> bool { |
476ff2be | 75 | let AttrId(id) = attr.id; |
0531ce1d XL |
76 | GLOBALS.with(|globals| { |
77 | let slot = globals.used_attrs.lock(); | |
e9174d1e SL |
78 | let idx = (id / 64) as usize; |
79 | let shift = id % 64; | |
0531ce1d | 80 | slot.get(idx).map(|bits| bits & (1 << shift) != 0) |
e9174d1e SL |
81 | .unwrap_or(false) |
82 | }) | |
1a4d82fc | 83 | } |
223e47cc | 84 | |
476ff2be SL |
85 | pub fn mark_known(attr: &Attribute) { |
86 | debug!("Marking {:?} as known.", attr); | |
87 | let AttrId(id) = attr.id; | |
0531ce1d XL |
88 | GLOBALS.with(|globals| { |
89 | let mut slot = globals.known_attrs.lock(); | |
476ff2be SL |
90 | let idx = (id / 64) as usize; |
91 | let shift = id % 64; | |
0531ce1d XL |
92 | if slot.len() <= idx { |
93 | slot.resize(idx + 1, 0); | |
476ff2be | 94 | } |
0531ce1d | 95 | slot[idx] |= 1 << shift; |
476ff2be SL |
96 | }); |
97 | } | |
98 | ||
99 | pub fn is_known(attr: &Attribute) -> bool { | |
100 | let AttrId(id) = attr.id; | |
0531ce1d XL |
101 | GLOBALS.with(|globals| { |
102 | let slot = globals.known_attrs.lock(); | |
476ff2be SL |
103 | let idx = (id / 64) as usize; |
104 | let shift = id % 64; | |
0531ce1d | 105 | slot.get(idx).map(|bits| bits & (1 << shift) != 0) |
476ff2be SL |
106 | .unwrap_or(false) |
107 | }) | |
108 | } | |
109 | ||
9e0c209e SL |
110 | impl NestedMetaItem { |
111 | /// Returns the MetaItem if self is a NestedMetaItemKind::MetaItem. | |
476ff2be | 112 | pub fn meta_item(&self) -> Option<&MetaItem> { |
9e0c209e | 113 | match self.node { |
7cac9316 | 114 | NestedMetaItemKind::MetaItem(ref item) => Some(item), |
9e0c209e SL |
115 | _ => None |
116 | } | |
117 | } | |
118 | ||
119 | /// Returns the Lit if self is a NestedMetaItemKind::Literal. | |
120 | pub fn literal(&self) -> Option<&Lit> { | |
121 | match self.node { | |
7cac9316 | 122 | NestedMetaItemKind::Literal(ref lit) => Some(lit), |
9e0c209e SL |
123 | _ => None |
124 | } | |
1a4d82fc JJ |
125 | } |
126 | ||
9e0c209e SL |
127 | /// Returns the Span for `self`. |
128 | pub fn span(&self) -> Span { | |
129 | self.span | |
130 | } | |
131 | ||
132 | /// Returns true if this list item is a MetaItem with a name of `name`. | |
133 | pub fn check_name(&self, name: &str) -> bool { | |
134 | self.meta_item().map_or(false, |meta_item| meta_item.check_name(name)) | |
135 | } | |
136 | ||
137 | /// Returns the name of the meta item, e.g. `foo` in `#[foo]`, | |
138 | /// `#[foo="bar"]` and `#[foo(bar)]`, if self is a MetaItem | |
476ff2be | 139 | pub fn name(&self) -> Option<Name> { |
9e0c209e SL |
140 | self.meta_item().and_then(|meta_item| Some(meta_item.name())) |
141 | } | |
142 | ||
143 | /// Gets the string value if self is a MetaItem and the MetaItem is a | |
144 | /// MetaItemKind::NameValue variant containing a string, otherwise None. | |
476ff2be | 145 | pub fn value_str(&self) -> Option<Symbol> { |
9e0c209e SL |
146 | self.meta_item().and_then(|meta_item| meta_item.value_str()) |
147 | } | |
148 | ||
cc61c64b XL |
149 | /// Returns a name and single literal value tuple of the MetaItem. |
150 | pub fn name_value_literal(&self) -> Option<(Name, &Lit)> { | |
151 | self.meta_item().and_then( | |
152 | |meta_item| meta_item.meta_item_list().and_then( | |
153 | |meta_item_list| { | |
154 | if meta_item_list.len() == 1 { | |
155 | let nested_item = &meta_item_list[0]; | |
156 | if nested_item.is_literal() { | |
157 | Some((meta_item.name(), nested_item.literal().unwrap())) | |
158 | } else { | |
159 | None | |
160 | } | |
161 | } | |
162 | else { | |
163 | None | |
164 | }})) | |
165 | } | |
166 | ||
9e0c209e | 167 | /// Returns a MetaItem if self is a MetaItem with Kind Word. |
476ff2be | 168 | pub fn word(&self) -> Option<&MetaItem> { |
9e0c209e SL |
169 | self.meta_item().and_then(|meta_item| if meta_item.is_word() { |
170 | Some(meta_item) | |
171 | } else { | |
172 | None | |
173 | }) | |
174 | } | |
1a4d82fc | 175 | |
1a4d82fc | 176 | /// Gets a list of inner meta items from a list MetaItem type. |
9e0c209e SL |
177 | pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> { |
178 | self.meta_item().and_then(|meta_item| meta_item.meta_item_list()) | |
179 | } | |
85aaf69f | 180 | |
9e0c209e SL |
181 | /// Returns `true` if the variant is MetaItem. |
182 | pub fn is_meta_item(&self) -> bool { | |
183 | self.meta_item().is_some() | |
184 | } | |
5bcae85e | 185 | |
9e0c209e SL |
186 | /// Returns `true` if the variant is Literal. |
187 | pub fn is_literal(&self) -> bool { | |
188 | self.literal().is_some() | |
189 | } | |
190 | ||
191 | /// Returns `true` if self is a MetaItem and the meta item is a word. | |
192 | pub fn is_word(&self) -> bool { | |
193 | self.word().is_some() | |
194 | } | |
195 | ||
196 | /// Returns `true` if self is a MetaItem and the meta item is a ValueString. | |
197 | pub fn is_value_str(&self) -> bool { | |
5bcae85e SL |
198 | self.value_str().is_some() |
199 | } | |
200 | ||
9e0c209e SL |
201 | /// Returns `true` if self is a MetaItem and the meta item is a list. |
202 | pub fn is_meta_item_list(&self) -> bool { | |
5bcae85e SL |
203 | self.meta_item_list().is_some() |
204 | } | |
223e47cc LB |
205 | } |
206 | ||
9e0c209e SL |
207 | impl Attribute { |
208 | pub fn check_name(&self, name: &str) -> bool { | |
cc61c64b | 209 | let matches = self.path == name; |
1a4d82fc JJ |
210 | if matches { |
211 | mark_used(self); | |
212 | } | |
213 | matches | |
214 | } | |
9e0c209e | 215 | |
cc61c64b XL |
216 | pub fn name(&self) -> Option<Name> { |
217 | match self.path.segments.len() { | |
218 | 1 => Some(self.path.segments[0].identifier.name), | |
219 | _ => None, | |
220 | } | |
221 | } | |
9e0c209e | 222 | |
476ff2be | 223 | pub fn value_str(&self) -> Option<Symbol> { |
cc61c64b | 224 | self.meta().and_then(|meta| meta.value_str()) |
1a4d82fc | 225 | } |
9e0c209e | 226 | |
cc61c64b XL |
227 | pub fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> { |
228 | match self.meta() { | |
229 | Some(MetaItem { node: MetaItemKind::List(list), .. }) => Some(list), | |
230 | _ => None | |
231 | } | |
1a4d82fc | 232 | } |
5bcae85e | 233 | |
cc61c64b XL |
234 | pub fn is_word(&self) -> bool { |
235 | self.path.segments.len() == 1 && self.tokens.is_empty() | |
236 | } | |
9e0c209e | 237 | |
cc61c64b XL |
238 | pub fn span(&self) -> Span { |
239 | self.span | |
240 | } | |
9e0c209e SL |
241 | |
242 | pub fn is_meta_item_list(&self) -> bool { | |
243 | self.meta_item_list().is_some() | |
244 | } | |
5bcae85e | 245 | |
9e0c209e SL |
246 | /// Indicates if the attribute is a Value String. |
247 | pub fn is_value_str(&self) -> bool { | |
248 | self.value_str().is_some() | |
249 | } | |
223e47cc LB |
250 | } |
251 | ||
9e0c209e | 252 | impl MetaItem { |
476ff2be SL |
253 | pub fn name(&self) -> Name { |
254 | self.name | |
1a4d82fc JJ |
255 | } |
256 | ||
476ff2be | 257 | pub fn value_str(&self) -> Option<Symbol> { |
1a4d82fc | 258 | match self.node { |
476ff2be | 259 | MetaItemKind::NameValue(ref v) => { |
1a4d82fc | 260 | match v.node { |
7cac9316 | 261 | LitKind::Str(ref s, _) => Some(*s), |
1a4d82fc JJ |
262 | _ => None, |
263 | } | |
264 | }, | |
265 | _ => None | |
266 | } | |
267 | } | |
268 | ||
9e0c209e | 269 | pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> { |
1a4d82fc | 270 | match self.node { |
476ff2be | 271 | MetaItemKind::List(ref l) => Some(&l[..]), |
1a4d82fc JJ |
272 | _ => None |
273 | } | |
274 | } | |
5bcae85e | 275 | |
9e0c209e | 276 | pub fn is_word(&self) -> bool { |
5bcae85e | 277 | match self.node { |
476ff2be | 278 | MetaItemKind::Word => true, |
5bcae85e SL |
279 | _ => false, |
280 | } | |
281 | } | |
282 | ||
9e0c209e | 283 | pub fn span(&self) -> Span { self.span } |
223e47cc | 284 | |
9e0c209e | 285 | pub fn check_name(&self, name: &str) -> bool { |
476ff2be | 286 | self.name() == name |
1a4d82fc | 287 | } |
223e47cc | 288 | |
9e0c209e SL |
289 | pub fn is_value_str(&self) -> bool { |
290 | self.value_str().is_some() | |
291 | } | |
1a4d82fc | 292 | |
9e0c209e SL |
293 | pub fn is_meta_item_list(&self) -> bool { |
294 | self.meta_item_list().is_some() | |
295 | } | |
223e47cc LB |
296 | } |
297 | ||
9e0c209e | 298 | impl Attribute { |
1a4d82fc | 299 | /// Extract the MetaItem from inside this Attribute. |
cc61c64b XL |
300 | pub fn meta(&self) -> Option<MetaItem> { |
301 | let mut tokens = self.tokens.trees().peekable(); | |
302 | Some(MetaItem { | |
303 | name: match self.path.segments.len() { | |
304 | 1 => self.path.segments[0].identifier.name, | |
305 | _ => return None, | |
306 | }, | |
307 | node: if let Some(node) = MetaItemKind::from_tokens(&mut tokens) { | |
308 | if tokens.peek().is_some() { | |
309 | return None; | |
310 | } | |
311 | node | |
312 | } else { | |
313 | return None; | |
314 | }, | |
315 | span: self.span, | |
316 | }) | |
317 | } | |
318 | ||
319 | pub fn parse<'a, T, F>(&self, sess: &'a ParseSess, mut f: F) -> PResult<'a, T> | |
320 | where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, | |
321 | { | |
7cac9316 | 322 | let mut parser = Parser::new(sess, self.tokens.clone(), None, false, false); |
cc61c64b XL |
323 | let result = f(&mut parser)?; |
324 | if parser.token != token::Eof { | |
325 | parser.unexpected()?; | |
326 | } | |
327 | Ok(result) | |
328 | } | |
329 | ||
330 | pub fn parse_list<'a, T, F>(&self, sess: &'a ParseSess, mut f: F) -> PResult<'a, Vec<T>> | |
331 | where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, | |
332 | { | |
333 | if self.tokens.is_empty() { | |
334 | return Ok(Vec::new()); | |
335 | } | |
336 | self.parse(sess, |parser| { | |
337 | parser.expect(&token::OpenDelim(token::Paren))?; | |
338 | let mut list = Vec::new(); | |
339 | while !parser.eat(&token::CloseDelim(token::Paren)) { | |
340 | list.push(f(parser)?); | |
341 | if !parser.eat(&token::Comma) { | |
342 | parser.expect(&token::CloseDelim(token::Paren))?; | |
343 | break | |
344 | } | |
345 | } | |
346 | Ok(list) | |
347 | }) | |
348 | } | |
349 | ||
350 | pub fn parse_meta<'a>(&self, sess: &'a ParseSess) -> PResult<'a, MetaItem> { | |
351 | if self.path.segments.len() > 1 { | |
352 | sess.span_diagnostic.span_err(self.path.span, "expected ident, found path"); | |
353 | } | |
354 | ||
355 | Ok(MetaItem { | |
356 | name: self.path.segments.last().unwrap().identifier.name, | |
357 | node: self.parse(sess, |parser| parser.parse_meta_item_kind())?, | |
358 | span: self.span, | |
359 | }) | |
1a4d82fc JJ |
360 | } |
361 | ||
362 | /// Convert self to a normal #[doc="foo"] comment, if it is a | |
363 | /// comment like `///` or `/** */`. (Returns self unchanged for | |
364 | /// non-sugared doc attributes.) | |
9e0c209e | 365 | pub fn with_desugared_doc<T, F>(&self, f: F) -> T where |
1a4d82fc JJ |
366 | F: FnOnce(&Attribute) -> T, |
367 | { | |
476ff2be | 368 | if self.is_sugared_doc { |
1a4d82fc JJ |
369 | let comment = self.value_str().unwrap(); |
370 | let meta = mk_name_value_item_str( | |
476ff2be SL |
371 | Symbol::intern("doc"), |
372 | Symbol::intern(&strip_doc_comment_decoration(&comment.as_str()))); | |
ff7c6d11 XL |
373 | let mut attr = if self.style == ast::AttrStyle::Outer { |
374 | mk_attr_outer(self.span, self.id, meta) | |
1a4d82fc | 375 | } else { |
ff7c6d11 XL |
376 | mk_attr_inner(self.span, self.id, meta) |
377 | }; | |
378 | attr.is_sugared_doc = true; | |
379 | f(&attr) | |
1a4d82fc JJ |
380 | } else { |
381 | f(self) | |
382 | } | |
383 | } | |
223e47cc LB |
384 | } |
385 | ||
1a4d82fc JJ |
386 | /* Constructors */ |
387 | ||
476ff2be | 388 | pub fn mk_name_value_item_str(name: Name, value: Symbol) -> MetaItem { |
cc61c64b | 389 | let value_lit = dummy_spanned(LitKind::Str(value, ast::StrStyle::Cooked)); |
5bcae85e | 390 | mk_spanned_name_value_item(DUMMY_SP, name, value_lit) |
1a4d82fc | 391 | } |
223e47cc | 392 | |
476ff2be | 393 | pub fn mk_name_value_item(name: Name, value: ast::Lit) -> MetaItem { |
5bcae85e | 394 | mk_spanned_name_value_item(DUMMY_SP, name, value) |
223e47cc LB |
395 | } |
396 | ||
476ff2be | 397 | pub fn mk_list_item(name: Name, items: Vec<NestedMetaItem>) -> MetaItem { |
5bcae85e | 398 | mk_spanned_list_item(DUMMY_SP, name, items) |
223e47cc LB |
399 | } |
400 | ||
476ff2be | 401 | pub fn mk_list_word_item(name: Name) -> ast::NestedMetaItem { |
9e0c209e SL |
402 | dummy_spanned(NestedMetaItemKind::MetaItem(mk_spanned_word_item(DUMMY_SP, name))) |
403 | } | |
404 | ||
476ff2be | 405 | pub fn mk_word_item(name: Name) -> MetaItem { |
5bcae85e SL |
406 | mk_spanned_word_item(DUMMY_SP, name) |
407 | } | |
408 | ||
476ff2be SL |
409 | pub fn mk_spanned_name_value_item(sp: Span, name: Name, value: ast::Lit) -> MetaItem { |
410 | MetaItem { span: sp, name: name, node: MetaItemKind::NameValue(value) } | |
5bcae85e SL |
411 | } |
412 | ||
476ff2be SL |
413 | pub fn mk_spanned_list_item(sp: Span, name: Name, items: Vec<NestedMetaItem>) -> MetaItem { |
414 | MetaItem { span: sp, name: name, node: MetaItemKind::List(items) } | |
5bcae85e SL |
415 | } |
416 | ||
476ff2be SL |
417 | pub fn mk_spanned_word_item(sp: Span, name: Name) -> MetaItem { |
418 | MetaItem { span: sp, name: name, node: MetaItemKind::Word } | |
223e47cc LB |
419 | } |
420 | ||
ff7c6d11 XL |
421 | pub fn mk_attr_id() -> AttrId { |
422 | use std::sync::atomic::AtomicUsize; | |
423 | use std::sync::atomic::Ordering; | |
5bcae85e | 424 | |
ff7c6d11 | 425 | static NEXT_ATTR_ID: AtomicUsize = AtomicUsize::new(0); |
5bcae85e | 426 | |
ff7c6d11 XL |
427 | let id = NEXT_ATTR_ID.fetch_add(1, Ordering::SeqCst); |
428 | assert!(id != ::std::usize::MAX); | |
1a4d82fc | 429 | AttrId(id) |
223e47cc LB |
430 | } |
431 | ||
1a4d82fc | 432 | /// Returns an inner attribute with the given value. |
8bb4bdeb XL |
433 | pub fn mk_attr_inner(span: Span, id: AttrId, item: MetaItem) -> Attribute { |
434 | mk_spanned_attr_inner(span, id, item) | |
5bcae85e SL |
435 | } |
436 | ||
3b2f2976 | 437 | /// Returns an inner attribute with the given value and span. |
476ff2be SL |
438 | pub fn mk_spanned_attr_inner(sp: Span, id: AttrId, item: MetaItem) -> Attribute { |
439 | Attribute { | |
3b2f2976 | 440 | id, |
476ff2be | 441 | style: ast::AttrStyle::Inner, |
cc61c64b XL |
442 | path: ast::Path::from_ident(item.span, ast::Ident::with_empty_ctxt(item.name)), |
443 | tokens: item.node.tokens(item.span), | |
476ff2be SL |
444 | is_sugared_doc: false, |
445 | span: sp, | |
446 | } | |
223e47cc LB |
447 | } |
448 | ||
5bcae85e | 449 | |
1a4d82fc | 450 | /// Returns an outer attribute with the given value. |
8bb4bdeb XL |
451 | pub fn mk_attr_outer(span: Span, id: AttrId, item: MetaItem) -> Attribute { |
452 | mk_spanned_attr_outer(span, id, item) | |
5bcae85e SL |
453 | } |
454 | ||
455 | /// Returns an outer attribute with the given value and span. | |
476ff2be SL |
456 | pub fn mk_spanned_attr_outer(sp: Span, id: AttrId, item: MetaItem) -> Attribute { |
457 | Attribute { | |
3b2f2976 | 458 | id, |
476ff2be | 459 | style: ast::AttrStyle::Outer, |
cc61c64b XL |
460 | path: ast::Path::from_ident(item.span, ast::Ident::with_empty_ctxt(item.name)), |
461 | tokens: item.node.tokens(item.span), | |
476ff2be SL |
462 | is_sugared_doc: false, |
463 | span: sp, | |
464 | } | |
465 | } | |
466 | ||
cc61c64b | 467 | pub fn mk_sugared_doc_attr(id: AttrId, text: Symbol, span: Span) -> Attribute { |
476ff2be | 468 | let style = doc_comment_style(&text.as_str()); |
cc61c64b | 469 | let lit = respan(span, LitKind::Str(text, ast::StrStyle::Cooked)); |
476ff2be | 470 | Attribute { |
3b2f2976 XL |
471 | id, |
472 | style, | |
cc61c64b XL |
473 | path: ast::Path::from_ident(span, ast::Ident::from_str("doc")), |
474 | tokens: MetaItemKind::NameValue(lit).tokens(span), | |
476ff2be | 475 | is_sugared_doc: true, |
3b2f2976 | 476 | span, |
476ff2be | 477 | } |
223e47cc LB |
478 | } |
479 | ||
9e0c209e | 480 | pub fn list_contains_name(items: &[NestedMetaItem], name: &str) -> bool { |
9e0c209e | 481 | items.iter().any(|item| { |
9e0c209e SL |
482 | item.check_name(name) |
483 | }) | |
484 | } | |
485 | ||
486 | pub fn contains_name(attrs: &[Attribute], name: &str) -> bool { | |
9e0c209e | 487 | attrs.iter().any(|item| { |
1a4d82fc JJ |
488 | item.check_name(name) |
489 | }) | |
490 | } | |
223e47cc | 491 | |
ea8adc8c XL |
492 | pub fn find_by_name<'a>(attrs: &'a [Attribute], name: &str) -> Option<&'a Attribute> { |
493 | attrs.iter().find(|attr| attr.check_name(name)) | |
494 | } | |
495 | ||
476ff2be | 496 | pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: &str) -> Option<Symbol> { |
1a4d82fc JJ |
497 | attrs.iter() |
498 | .find(|at| at.check_name(name)) | |
499 | .and_then(|at| at.value_str()) | |
500 | } | |
223e47cc | 501 | |
ea8adc8c XL |
502 | /// Check if `attrs` contains an attribute like `#![feature(feature_name)]`. |
503 | /// This will not perform any "sanity checks" on the form of the attributes. | |
504 | pub fn contains_feature_attr(attrs: &[Attribute], feature_name: &str) -> bool { | |
505 | attrs.iter().any(|item| { | |
506 | item.check_name("feature") && | |
507 | item.meta_item_list().map(|list| { | |
508 | list.iter().any(|mi| { | |
509 | mi.word().map(|w| w.name() == feature_name) | |
510 | .unwrap_or(false) | |
511 | }) | |
512 | }).unwrap_or(false) | |
513 | }) | |
514 | } | |
515 | ||
1a4d82fc JJ |
516 | /* Higher-level applications */ |
517 | ||
476ff2be | 518 | pub fn find_crate_name(attrs: &[Attribute]) -> Option<Symbol> { |
1a4d82fc | 519 | first_attr_value_str_by_name(attrs, "crate_name") |
223e47cc LB |
520 | } |
521 | ||
0531ce1d | 522 | #[derive(Copy, Clone, Hash, PartialEq, RustcEncodable, RustcDecodable)] |
1a4d82fc | 523 | pub enum InlineAttr { |
c34b1796 AL |
524 | None, |
525 | Hint, | |
526 | Always, | |
527 | Never, | |
1a4d82fc JJ |
528 | } |
529 | ||
0531ce1d XL |
530 | #[derive(Copy, Clone, PartialEq)] |
531 | pub enum UnwindAttr { | |
532 | Allowed, | |
533 | Aborts, | |
534 | } | |
535 | ||
536 | /// Determine what `#[unwind]` attribute is present in `attrs`, if any. | |
537 | pub fn find_unwind_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> Option<UnwindAttr> { | |
538 | let syntax_error = |attr: &Attribute| { | |
539 | mark_used(attr); | |
540 | diagnostic.map(|d| { | |
541 | span_err!(d, attr.span, E0633, "malformed `#[unwind]` attribute"); | |
542 | }); | |
543 | None | |
544 | }; | |
545 | ||
546 | attrs.iter().fold(None, |ia, attr| { | |
547 | if attr.path != "unwind" { | |
cc61c64b XL |
548 | return ia; |
549 | } | |
550 | let meta = match attr.meta() { | |
551 | Some(meta) => meta.node, | |
552 | None => return ia, | |
553 | }; | |
554 | match meta { | |
476ff2be | 555 | MetaItemKind::Word => { |
0531ce1d | 556 | syntax_error(attr) |
223e47cc | 557 | } |
476ff2be | 558 | MetaItemKind::List(ref items) => { |
1a4d82fc | 559 | mark_used(attr); |
c34b1796 | 560 | if items.len() != 1 { |
0531ce1d XL |
561 | syntax_error(attr) |
562 | } else if list_contains_name(&items[..], "allowed") { | |
563 | Some(UnwindAttr::Allowed) | |
564 | } else if list_contains_name(&items[..], "aborts") { | |
565 | Some(UnwindAttr::Aborts) | |
1a4d82fc | 566 | } else { |
0531ce1d | 567 | syntax_error(attr) |
223e47cc | 568 | } |
223e47cc | 569 | } |
5bcae85e | 570 | _ => ia, |
223e47cc | 571 | } |
1a4d82fc | 572 | }) |
223e47cc LB |
573 | } |
574 | ||
223e47cc | 575 | |
1a4d82fc | 576 | /// Tests if a cfg-pattern matches the cfg set |
c30ab7b3 | 577 | pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) -> bool { |
ea8adc8c XL |
578 | eval_condition(cfg, sess, &mut |cfg| { |
579 | if let (Some(feats), Some(gated_cfg)) = (features, GatedCfg::gate(cfg)) { | |
580 | gated_cfg.check_and_emit(sess, feats); | |
581 | } | |
582 | sess.config.contains(&(cfg.name(), cfg.value_str())) | |
583 | }) | |
584 | } | |
585 | ||
586 | /// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to | |
587 | /// evaluate individual items. | |
588 | pub fn eval_condition<F>(cfg: &ast::MetaItem, sess: &ParseSess, eval: &mut F) | |
589 | -> bool | |
590 | where F: FnMut(&ast::MetaItem) -> bool | |
591 | { | |
1a4d82fc | 592 | match cfg.node { |
476ff2be | 593 | ast::MetaItemKind::List(ref mis) => { |
9e0c209e SL |
594 | for mi in mis.iter() { |
595 | if !mi.is_meta_item() { | |
596 | handle_errors(&sess.span_diagnostic, mi.span, AttrError::UnsupportedLiteral); | |
597 | return false; | |
598 | } | |
599 | } | |
600 | ||
601 | // The unwraps below may look dangerous, but we've already asserted | |
602 | // that they won't fail with the loop above. | |
476ff2be | 603 | match &*cfg.name.as_str() { |
9e0c209e | 604 | "any" => mis.iter().any(|mi| { |
ea8adc8c | 605 | eval_condition(mi.meta_item().unwrap(), sess, eval) |
9e0c209e SL |
606 | }), |
607 | "all" => mis.iter().all(|mi| { | |
ea8adc8c | 608 | eval_condition(mi.meta_item().unwrap(), sess, eval) |
9e0c209e SL |
609 | }), |
610 | "not" => { | |
611 | if mis.len() != 1 { | |
612 | span_err!(sess.span_diagnostic, cfg.span, E0536, "expected 1 cfg-pattern"); | |
613 | return false; | |
614 | } | |
615 | ||
ea8adc8c | 616 | !eval_condition(mis[0].meta_item().unwrap(), sess, eval) |
9e0c209e SL |
617 | }, |
618 | p => { | |
619 | span_err!(sess.span_diagnostic, cfg.span, E0537, "invalid predicate `{}`", p); | |
620 | false | |
621 | } | |
1a4d82fc | 622 | } |
1a4d82fc | 623 | }, |
476ff2be | 624 | ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => { |
ea8adc8c | 625 | eval(cfg) |
e9174d1e | 626 | } |
223e47cc LB |
627 | } |
628 | } | |
629 | ||
ea8adc8c | 630 | /// Represents the #[stable], #[unstable], #[rustc_{deprecated,const_unstable}] attributes. |
62682a34 | 631 | #[derive(RustcEncodable, RustcDecodable, Clone, Debug, PartialEq, Eq, Hash)] |
1a4d82fc JJ |
632 | pub struct Stability { |
633 | pub level: StabilityLevel, | |
476ff2be | 634 | pub feature: Symbol, |
9cc50fc6 | 635 | pub rustc_depr: Option<RustcDeprecation>, |
ea8adc8c | 636 | pub rustc_const_unstable: Option<RustcConstUnstable>, |
223e47cc LB |
637 | } |
638 | ||
1a4d82fc | 639 | /// The available stability levels. |
b039eaaf | 640 | #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)] |
1a4d82fc | 641 | pub enum StabilityLevel { |
b039eaaf | 642 | // Reason for the current stability level and the relevant rust-lang issue |
476ff2be SL |
643 | Unstable { reason: Option<Symbol>, issue: u32 }, |
644 | Stable { since: Symbol }, | |
1a4d82fc | 645 | } |
223e47cc | 646 | |
b039eaaf | 647 | #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)] |
9cc50fc6 | 648 | pub struct RustcDeprecation { |
476ff2be SL |
649 | pub since: Symbol, |
650 | pub reason: Symbol, | |
223e47cc LB |
651 | } |
652 | ||
ea8adc8c XL |
653 | #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)] |
654 | pub struct RustcConstUnstable { | |
655 | pub feature: Symbol, | |
656 | } | |
657 | ||
9cc50fc6 SL |
658 | #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)] |
659 | pub struct Deprecation { | |
476ff2be SL |
660 | pub since: Option<Symbol>, |
661 | pub note: Option<Symbol>, | |
9cc50fc6 SL |
662 | } |
663 | ||
b039eaaf SL |
664 | impl StabilityLevel { |
665 | pub fn is_unstable(&self) -> bool { if let Unstable {..} = *self { true } else { false }} | |
666 | pub fn is_stable(&self) -> bool { if let Stable {..} = *self { true } else { false }} | |
667 | } | |
85aaf69f | 668 | |
9cc50fc6 | 669 | fn find_stability_generic<'a, I>(diagnostic: &Handler, |
b039eaaf SL |
670 | attrs_iter: I, |
671 | item_sp: Span) | |
672 | -> Option<Stability> | |
673 | where I: Iterator<Item = &'a Attribute> | |
674 | { | |
85aaf69f | 675 | let mut stab: Option<Stability> = None; |
9cc50fc6 | 676 | let mut rustc_depr: Option<RustcDeprecation> = None; |
ea8adc8c | 677 | let mut rustc_const_unstable: Option<RustcConstUnstable> = None; |
85aaf69f | 678 | |
b039eaaf | 679 | 'outer: for attr in attrs_iter { |
ea8adc8c XL |
680 | if ![ |
681 | "rustc_deprecated", | |
682 | "rustc_const_unstable", | |
683 | "unstable", | |
684 | "stable", | |
685 | ].iter().any(|&s| attr.path == s) { | |
85aaf69f SL |
686 | continue // not a stability level |
687 | } | |
688 | ||
b039eaaf SL |
689 | mark_used(attr); |
690 | ||
cc61c64b XL |
691 | let meta = attr.meta(); |
692 | if let Some(MetaItem { node: MetaItemKind::List(ref metas), .. }) = meta { | |
693 | let meta = meta.as_ref().unwrap(); | |
476ff2be | 694 | let get = |meta: &MetaItem, item: &mut Option<Symbol>| { |
b039eaaf | 695 | if item.is_some() { |
3157f602 | 696 | handle_errors(diagnostic, meta.span, AttrError::MultipleItem(meta.name())); |
b039eaaf SL |
697 | return false |
698 | } | |
699 | if let Some(v) = meta.value_str() { | |
700 | *item = Some(v); | |
701 | true | |
702 | } else { | |
3157f602 | 703 | span_err!(diagnostic, meta.span, E0539, "incorrect meta item"); |
b039eaaf SL |
704 | false |
705 | } | |
706 | }; | |
707 | ||
ea8adc8c XL |
708 | macro_rules! get_meta { |
709 | ($($name:ident),+) => { | |
710 | $( | |
711 | let mut $name = None; | |
712 | )+ | |
b039eaaf | 713 | for meta in metas { |
9e0c209e | 714 | if let Some(mi) = meta.meta_item() { |
476ff2be | 715 | match &*mi.name().as_str() { |
ea8adc8c XL |
716 | $( |
717 | stringify!($name) | |
718 | => if !get(mi, &mut $name) { continue 'outer }, | |
719 | )+ | |
9e0c209e SL |
720 | _ => { |
721 | handle_errors(diagnostic, mi.span, | |
722 | AttrError::UnknownMetaItem(mi.name())); | |
723 | continue 'outer | |
724 | } | |
85aaf69f | 725 | } |
9e0c209e SL |
726 | } else { |
727 | handle_errors(diagnostic, meta.span, AttrError::UnsupportedLiteral); | |
728 | continue 'outer | |
85aaf69f | 729 | } |
b039eaaf | 730 | } |
ea8adc8c XL |
731 | } |
732 | } | |
733 | ||
734 | match &*meta.name.as_str() { | |
735 | "rustc_deprecated" => { | |
736 | if rustc_depr.is_some() { | |
737 | span_err!(diagnostic, item_sp, E0540, | |
738 | "multiple rustc_deprecated attributes"); | |
739 | continue 'outer | |
740 | } | |
741 | ||
742 | get_meta!(since, reason); | |
b039eaaf SL |
743 | |
744 | match (since, reason) { | |
745 | (Some(since), Some(reason)) => { | |
9cc50fc6 | 746 | rustc_depr = Some(RustcDeprecation { |
3b2f2976 XL |
747 | since, |
748 | reason, | |
b039eaaf | 749 | }) |
85aaf69f | 750 | } |
b039eaaf | 751 | (None, _) => { |
3157f602 | 752 | handle_errors(diagnostic, attr.span(), AttrError::MissingSince); |
b039eaaf | 753 | continue |
c1a9b12d | 754 | } |
b039eaaf | 755 | _ => { |
3157f602 | 756 | span_err!(diagnostic, attr.span(), E0543, "missing 'reason'"); |
b039eaaf | 757 | continue |
85aaf69f SL |
758 | } |
759 | } | |
760 | } | |
ea8adc8c XL |
761 | "rustc_const_unstable" => { |
762 | if rustc_const_unstable.is_some() { | |
763 | span_err!(diagnostic, item_sp, E0553, | |
764 | "multiple rustc_const_unstable attributes"); | |
765 | continue 'outer | |
766 | } | |
767 | ||
768 | get_meta!(feature); | |
769 | if let Some(feature) = feature { | |
770 | rustc_const_unstable = Some(RustcConstUnstable { | |
771 | feature | |
772 | }); | |
773 | } else { | |
774 | span_err!(diagnostic, attr.span(), E0629, "missing 'feature'"); | |
775 | continue | |
776 | } | |
777 | } | |
b039eaaf SL |
778 | "unstable" => { |
779 | if stab.is_some() { | |
3157f602 | 780 | handle_errors(diagnostic, attr.span(), AttrError::MultipleStabilityLevels); |
b039eaaf SL |
781 | break |
782 | } | |
1a4d82fc | 783 | |
b039eaaf SL |
784 | let mut feature = None; |
785 | let mut reason = None; | |
786 | let mut issue = None; | |
787 | for meta in metas { | |
9e0c209e | 788 | if let Some(mi) = meta.meta_item() { |
476ff2be | 789 | match &*mi.name().as_str() { |
9e0c209e SL |
790 | "feature" => if !get(mi, &mut feature) { continue 'outer }, |
791 | "reason" => if !get(mi, &mut reason) { continue 'outer }, | |
792 | "issue" => if !get(mi, &mut issue) { continue 'outer }, | |
793 | _ => { | |
794 | handle_errors(diagnostic, meta.span, | |
795 | AttrError::UnknownMetaItem(mi.name())); | |
796 | continue 'outer | |
797 | } | |
b039eaaf | 798 | } |
9e0c209e SL |
799 | } else { |
800 | handle_errors(diagnostic, meta.span, AttrError::UnsupportedLiteral); | |
801 | continue 'outer | |
b039eaaf SL |
802 | } |
803 | } | |
85aaf69f | 804 | |
b039eaaf SL |
805 | match (feature, reason, issue) { |
806 | (Some(feature), reason, Some(issue)) => { | |
807 | stab = Some(Stability { | |
808 | level: Unstable { | |
3b2f2976 | 809 | reason, |
b039eaaf | 810 | issue: { |
476ff2be | 811 | if let Ok(issue) = issue.as_str().parse() { |
b039eaaf SL |
812 | issue |
813 | } else { | |
3157f602 XL |
814 | span_err!(diagnostic, attr.span(), E0545, |
815 | "incorrect 'issue'"); | |
b039eaaf SL |
816 | continue |
817 | } | |
818 | } | |
819 | }, | |
3b2f2976 | 820 | feature, |
9cc50fc6 | 821 | rustc_depr: None, |
ea8adc8c | 822 | rustc_const_unstable: None, |
b039eaaf SL |
823 | }) |
824 | } | |
825 | (None, _, _) => { | |
3157f602 | 826 | handle_errors(diagnostic, attr.span(), AttrError::MissingFeature); |
b039eaaf SL |
827 | continue |
828 | } | |
829 | _ => { | |
3157f602 | 830 | span_err!(diagnostic, attr.span(), E0547, "missing 'issue'"); |
b039eaaf SL |
831 | continue |
832 | } | |
833 | } | |
834 | } | |
835 | "stable" => { | |
836 | if stab.is_some() { | |
3157f602 | 837 | handle_errors(diagnostic, attr.span(), AttrError::MultipleStabilityLevels); |
b039eaaf SL |
838 | break |
839 | } | |
85aaf69f | 840 | |
b039eaaf SL |
841 | let mut feature = None; |
842 | let mut since = None; | |
843 | for meta in metas { | |
9e0c209e | 844 | if let NestedMetaItemKind::MetaItem(ref mi) = meta.node { |
476ff2be | 845 | match &*mi.name().as_str() { |
9e0c209e SL |
846 | "feature" => if !get(mi, &mut feature) { continue 'outer }, |
847 | "since" => if !get(mi, &mut since) { continue 'outer }, | |
848 | _ => { | |
849 | handle_errors(diagnostic, meta.span, | |
850 | AttrError::UnknownMetaItem(mi.name())); | |
851 | continue 'outer | |
852 | } | |
b039eaaf | 853 | } |
9e0c209e SL |
854 | } else { |
855 | handle_errors(diagnostic, meta.span, AttrError::UnsupportedLiteral); | |
856 | continue 'outer | |
b039eaaf SL |
857 | } |
858 | } | |
85aaf69f | 859 | |
b039eaaf SL |
860 | match (feature, since) { |
861 | (Some(feature), Some(since)) => { | |
862 | stab = Some(Stability { | |
863 | level: Stable { | |
3b2f2976 | 864 | since, |
b039eaaf | 865 | }, |
3b2f2976 | 866 | feature, |
9cc50fc6 | 867 | rustc_depr: None, |
ea8adc8c | 868 | rustc_const_unstable: None, |
b039eaaf SL |
869 | }) |
870 | } | |
871 | (None, _) => { | |
3157f602 | 872 | handle_errors(diagnostic, attr.span(), AttrError::MissingFeature); |
b039eaaf SL |
873 | continue |
874 | } | |
875 | _ => { | |
3157f602 | 876 | handle_errors(diagnostic, attr.span(), AttrError::MissingSince); |
b039eaaf SL |
877 | continue |
878 | } | |
879 | } | |
880 | } | |
85aaf69f | 881 | _ => unreachable!() |
85aaf69f | 882 | } |
b039eaaf | 883 | } else { |
3157f602 | 884 | span_err!(diagnostic, attr.span(), E0548, "incorrect stability attribute type"); |
b039eaaf | 885 | continue |
85aaf69f | 886 | } |
223e47cc | 887 | } |
85aaf69f SL |
888 | |
889 | // Merge the deprecation info into the stability info | |
9cc50fc6 | 890 | if let Some(rustc_depr) = rustc_depr { |
b039eaaf | 891 | if let Some(ref mut stab) = stab { |
9cc50fc6 | 892 | stab.rustc_depr = Some(rustc_depr); |
b039eaaf | 893 | } else { |
3157f602 XL |
894 | span_err!(diagnostic, item_sp, E0549, |
895 | "rustc_deprecated attribute must be paired with \ | |
896 | either stable or unstable attribute"); | |
85aaf69f SL |
897 | } |
898 | } | |
899 | ||
ea8adc8c XL |
900 | // Merge the const-unstable info into the stability info |
901 | if let Some(rustc_const_unstable) = rustc_const_unstable { | |
902 | if let Some(ref mut stab) = stab { | |
903 | stab.rustc_const_unstable = Some(rustc_const_unstable); | |
904 | } else { | |
905 | span_err!(diagnostic, item_sp, E0630, | |
906 | "rustc_const_unstable attribute must be paired with \ | |
907 | either stable or unstable attribute"); | |
908 | } | |
909 | } | |
910 | ||
b039eaaf | 911 | stab |
223e47cc LB |
912 | } |
913 | ||
9cc50fc6 SL |
914 | fn find_deprecation_generic<'a, I>(diagnostic: &Handler, |
915 | attrs_iter: I, | |
916 | item_sp: Span) | |
917 | -> Option<Deprecation> | |
918 | where I: Iterator<Item = &'a Attribute> | |
919 | { | |
920 | let mut depr: Option<Deprecation> = None; | |
921 | ||
922 | 'outer: for attr in attrs_iter { | |
cc61c64b | 923 | if attr.path != "deprecated" { |
9cc50fc6 SL |
924 | continue |
925 | } | |
926 | ||
927 | mark_used(attr); | |
928 | ||
929 | if depr.is_some() { | |
3157f602 | 930 | span_err!(diagnostic, item_sp, E0550, "multiple deprecated attributes"); |
9cc50fc6 SL |
931 | break |
932 | } | |
933 | ||
934 | depr = if let Some(metas) = attr.meta_item_list() { | |
476ff2be | 935 | let get = |meta: &MetaItem, item: &mut Option<Symbol>| { |
9cc50fc6 | 936 | if item.is_some() { |
3157f602 | 937 | handle_errors(diagnostic, meta.span, AttrError::MultipleItem(meta.name())); |
9cc50fc6 SL |
938 | return false |
939 | } | |
940 | if let Some(v) = meta.value_str() { | |
941 | *item = Some(v); | |
942 | true | |
943 | } else { | |
3157f602 | 944 | span_err!(diagnostic, meta.span, E0551, "incorrect meta item"); |
9cc50fc6 SL |
945 | false |
946 | } | |
947 | }; | |
948 | ||
949 | let mut since = None; | |
950 | let mut note = None; | |
951 | for meta in metas { | |
9e0c209e | 952 | if let NestedMetaItemKind::MetaItem(ref mi) = meta.node { |
476ff2be | 953 | match &*mi.name().as_str() { |
9e0c209e SL |
954 | "since" => if !get(mi, &mut since) { continue 'outer }, |
955 | "note" => if !get(mi, &mut note) { continue 'outer }, | |
956 | _ => { | |
957 | handle_errors(diagnostic, meta.span, | |
958 | AttrError::UnknownMetaItem(mi.name())); | |
959 | continue 'outer | |
960 | } | |
9cc50fc6 | 961 | } |
9e0c209e SL |
962 | } else { |
963 | handle_errors(diagnostic, meta.span, AttrError::UnsupportedLiteral); | |
964 | continue 'outer | |
9cc50fc6 SL |
965 | } |
966 | } | |
967 | ||
968 | Some(Deprecation {since: since, note: note}) | |
969 | } else { | |
970 | Some(Deprecation{since: None, note: None}) | |
971 | } | |
972 | } | |
973 | ||
974 | depr | |
975 | } | |
976 | ||
1a4d82fc | 977 | /// Find the first stability attribute. `None` if none exists. |
9cc50fc6 | 978 | pub fn find_stability(diagnostic: &Handler, attrs: &[Attribute], |
85aaf69f | 979 | item_sp: Span) -> Option<Stability> { |
b039eaaf | 980 | find_stability_generic(diagnostic, attrs.iter(), item_sp) |
1a4d82fc | 981 | } |
223e47cc | 982 | |
9cc50fc6 SL |
983 | /// Find the deprecation attribute. `None` if none exists. |
984 | pub fn find_deprecation(diagnostic: &Handler, attrs: &[Attribute], | |
985 | item_sp: Span) -> Option<Deprecation> { | |
986 | find_deprecation_generic(diagnostic, attrs.iter(), item_sp) | |
987 | } | |
988 | ||
1a4d82fc JJ |
989 | |
990 | /// Parse #[repr(...)] forms. | |
991 | /// | |
992 | /// Valid repr contents: any of the primitive integral type names (see | |
993 | /// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use | |
994 | /// the same discriminant size that the corresponding C enum would or C | |
2c00a5a8 XL |
995 | /// structure layout, `packed` to remove padding, and `transparent` to elegate representation |
996 | /// concerns to the only non-ZST field. | |
9cc50fc6 | 997 | pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr> { |
1a4d82fc | 998 | let mut acc = Vec::new(); |
cc61c64b XL |
999 | if attr.path == "repr" { |
1000 | if let Some(items) = attr.meta_item_list() { | |
1a4d82fc | 1001 | mark_used(attr); |
85aaf69f | 1002 | for item in items { |
9e0c209e SL |
1003 | if !item.is_meta_item() { |
1004 | handle_errors(diagnostic, item.span, AttrError::UnsupportedLiteral); | |
1005 | continue | |
1006 | } | |
1a4d82fc | 1007 | |
cc61c64b | 1008 | let mut recognised = false; |
9e0c209e | 1009 | if let Some(mi) = item.word() { |
476ff2be | 1010 | let word = &*mi.name().as_str(); |
9e0c209e | 1011 | let hint = match word { |
2c00a5a8 | 1012 | "C" => Some(ReprC), |
9e0c209e SL |
1013 | "packed" => Some(ReprPacked), |
1014 | "simd" => Some(ReprSimd), | |
2c00a5a8 | 1015 | "transparent" => Some(ReprTransparent), |
9e0c209e SL |
1016 | _ => match int_type_of_word(word) { |
1017 | Some(ity) => Some(ReprInt(ity)), | |
1018 | None => { | |
9e0c209e SL |
1019 | None |
1020 | } | |
1a4d82fc | 1021 | } |
9e0c209e SL |
1022 | }; |
1023 | ||
1024 | if let Some(h) = hint { | |
cc61c64b | 1025 | recognised = true; |
9e0c209e | 1026 | acc.push(h); |
1a4d82fc | 1027 | } |
cc61c64b XL |
1028 | } else if let Some((name, value)) = item.name_value_literal() { |
1029 | if name == "align" { | |
1030 | recognised = true; | |
1031 | let mut align_error = None; | |
1032 | if let ast::LitKind::Int(align, ast::LitIntType::Unsuffixed) = value.node { | |
1033 | if align.is_power_of_two() { | |
041b39d2 XL |
1034 | // rustc::ty::layout::Align restricts align to <= 2147483647 |
1035 | if align <= 2147483647 { | |
1036 | acc.push(ReprAlign(align as u32)); | |
cc61c64b | 1037 | } else { |
041b39d2 | 1038 | align_error = Some("larger than 2147483647"); |
cc61c64b XL |
1039 | } |
1040 | } else { | |
1041 | align_error = Some("not a power of two"); | |
1042 | } | |
1043 | } else { | |
1044 | align_error = Some("not an unsuffixed integer"); | |
1045 | } | |
1046 | if let Some(align_error) = align_error { | |
1047 | span_err!(diagnostic, item.span, E0589, | |
1048 | "invalid `repr(align)` attribute: {}", align_error); | |
1049 | } | |
1050 | } | |
1051 | } | |
1052 | if !recognised { | |
1053 | // Not a word we recognize | |
1054 | span_err!(diagnostic, item.span, E0552, | |
1055 | "unrecognized representation hint"); | |
223e47cc LB |
1056 | } |
1057 | } | |
223e47cc LB |
1058 | } |
1059 | } | |
1a4d82fc | 1060 | acc |
223e47cc LB |
1061 | } |
1062 | ||
1a4d82fc JJ |
1063 | fn int_type_of_word(s: &str) -> Option<IntType> { |
1064 | match s { | |
7453a54e SL |
1065 | "i8" => Some(SignedInt(ast::IntTy::I8)), |
1066 | "u8" => Some(UnsignedInt(ast::UintTy::U8)), | |
1067 | "i16" => Some(SignedInt(ast::IntTy::I16)), | |
1068 | "u16" => Some(UnsignedInt(ast::UintTy::U16)), | |
1069 | "i32" => Some(SignedInt(ast::IntTy::I32)), | |
1070 | "u32" => Some(UnsignedInt(ast::UintTy::U32)), | |
1071 | "i64" => Some(SignedInt(ast::IntTy::I64)), | |
1072 | "u64" => Some(UnsignedInt(ast::UintTy::U64)), | |
32a655c1 SL |
1073 | "i128" => Some(SignedInt(ast::IntTy::I128)), |
1074 | "u128" => Some(UnsignedInt(ast::UintTy::U128)), | |
2c00a5a8 XL |
1075 | "isize" => Some(SignedInt(ast::IntTy::Isize)), |
1076 | "usize" => Some(UnsignedInt(ast::UintTy::Usize)), | |
1a4d82fc JJ |
1077 | _ => None |
1078 | } | |
1079 | } | |
223e47cc | 1080 | |
c34b1796 | 1081 | #[derive(PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)] |
1a4d82fc | 1082 | pub enum ReprAttr { |
9e0c209e | 1083 | ReprInt(IntType), |
2c00a5a8 | 1084 | ReprC, |
1a4d82fc | 1085 | ReprPacked, |
e9174d1e | 1086 | ReprSimd, |
2c00a5a8 | 1087 | ReprTransparent, |
041b39d2 | 1088 | ReprAlign(u32), |
223e47cc LB |
1089 | } |
1090 | ||
c34b1796 | 1091 | #[derive(Eq, Hash, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)] |
1a4d82fc JJ |
1092 | pub enum IntType { |
1093 | SignedInt(ast::IntTy), | |
1094 | UnsignedInt(ast::UintTy) | |
223e47cc LB |
1095 | } |
1096 | ||
1a4d82fc JJ |
1097 | impl IntType { |
1098 | #[inline] | |
1099 | pub fn is_signed(self) -> bool { | |
1100 | match self { | |
1101 | SignedInt(..) => true, | |
1102 | UnsignedInt(..) => false | |
223e47cc LB |
1103 | } |
1104 | } | |
223e47cc | 1105 | } |
92a42be0 | 1106 | |
cc61c64b XL |
1107 | impl MetaItem { |
1108 | fn tokens(&self) -> TokenStream { | |
0531ce1d XL |
1109 | let ident = TokenTree::Token(self.span, |
1110 | Token::from_ast_ident(Ident::with_empty_ctxt(self.name))); | |
cc61c64b XL |
1111 | TokenStream::concat(vec![ident.into(), self.node.tokens(self.span)]) |
1112 | } | |
1113 | ||
1114 | fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem> | |
1115 | where I: Iterator<Item = TokenTree>, | |
1116 | { | |
ea8adc8c | 1117 | let (span, name) = match tokens.next() { |
0531ce1d | 1118 | Some(TokenTree::Token(span, Token::Ident(ident, _))) => (span, ident.name), |
041b39d2 | 1119 | Some(TokenTree::Token(_, Token::Interpolated(ref nt))) => match nt.0 { |
0531ce1d | 1120 | token::Nonterminal::NtIdent(ident, _) => (ident.span, ident.node.name), |
cc61c64b XL |
1121 | token::Nonterminal::NtMeta(ref meta) => return Some(meta.clone()), |
1122 | _ => return None, | |
1123 | }, | |
1124 | _ => return None, | |
1125 | }; | |
ea8adc8c | 1126 | let list_closing_paren_pos = tokens.peek().map(|tt| tt.span().hi()); |
ff7c6d11 | 1127 | let node = MetaItemKind::from_tokens(tokens)?; |
ea8adc8c XL |
1128 | let hi = match node { |
1129 | MetaItemKind::NameValue(ref lit) => lit.span.hi(), | |
1130 | MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(span.hi()), | |
1131 | _ => span.hi(), | |
3b2f2976 | 1132 | }; |
ea8adc8c | 1133 | Some(MetaItem { name, node, span: span.with_hi(hi) }) |
cc61c64b XL |
1134 | } |
1135 | } | |
1136 | ||
1137 | impl MetaItemKind { | |
cc61c64b XL |
1138 | pub fn tokens(&self, span: Span) -> TokenStream { |
1139 | match *self { | |
1140 | MetaItemKind::Word => TokenStream::empty(), | |
1141 | MetaItemKind::NameValue(ref lit) => { | |
1142 | TokenStream::concat(vec![TokenTree::Token(span, Token::Eq).into(), lit.tokens()]) | |
1143 | } | |
1144 | MetaItemKind::List(ref list) => { | |
1145 | let mut tokens = Vec::new(); | |
1146 | for (i, item) in list.iter().enumerate() { | |
1147 | if i > 0 { | |
1148 | tokens.push(TokenTree::Token(span, Token::Comma).into()); | |
1149 | } | |
1150 | tokens.push(item.node.tokens()); | |
1151 | } | |
1152 | TokenTree::Delimited(span, Delimited { | |
1153 | delim: token::Paren, | |
1154 | tts: TokenStream::concat(tokens).into(), | |
1155 | }).into() | |
1156 | } | |
1157 | } | |
1158 | } | |
1159 | ||
1160 | fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItemKind> | |
1161 | where I: Iterator<Item = TokenTree>, | |
1162 | { | |
1163 | let delimited = match tokens.peek().cloned() { | |
1164 | Some(TokenTree::Token(_, token::Eq)) => { | |
1165 | tokens.next(); | |
1166 | return if let Some(TokenTree::Token(span, token)) = tokens.next() { | |
1167 | LitKind::from_token(token) | |
1168 | .map(|lit| MetaItemKind::NameValue(Spanned { node: lit, span: span })) | |
1169 | } else { | |
1170 | None | |
1171 | }; | |
1172 | } | |
1173 | Some(TokenTree::Delimited(_, ref delimited)) if delimited.delim == token::Paren => { | |
1174 | tokens.next(); | |
1175 | delimited.stream() | |
1176 | } | |
1177 | _ => return Some(MetaItemKind::Word), | |
1178 | }; | |
1179 | ||
1180 | let mut tokens = delimited.into_trees().peekable(); | |
1181 | let mut result = Vec::new(); | |
1182 | while let Some(..) = tokens.peek() { | |
ff7c6d11 XL |
1183 | let item = NestedMetaItemKind::from_tokens(&mut tokens)?; |
1184 | result.push(respan(item.span(), item)); | |
cc61c64b XL |
1185 | match tokens.next() { |
1186 | None | Some(TokenTree::Token(_, Token::Comma)) => {} | |
1187 | _ => return None, | |
1188 | } | |
1189 | } | |
1190 | Some(MetaItemKind::List(result)) | |
1191 | } | |
1192 | } | |
1193 | ||
1194 | impl NestedMetaItemKind { | |
1195 | fn span(&self) -> Span { | |
1196 | match *self { | |
1197 | NestedMetaItemKind::MetaItem(ref item) => item.span, | |
1198 | NestedMetaItemKind::Literal(ref lit) => lit.span, | |
1199 | } | |
1200 | } | |
1201 | ||
1202 | fn tokens(&self) -> TokenStream { | |
1203 | match *self { | |
1204 | NestedMetaItemKind::MetaItem(ref item) => item.tokens(), | |
1205 | NestedMetaItemKind::Literal(ref lit) => lit.tokens(), | |
1206 | } | |
1207 | } | |
1208 | ||
1209 | fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItemKind> | |
1210 | where I: Iterator<Item = TokenTree>, | |
1211 | { | |
1212 | if let Some(TokenTree::Token(span, token)) = tokens.peek().cloned() { | |
1213 | if let Some(node) = LitKind::from_token(token) { | |
1214 | tokens.next(); | |
3b2f2976 | 1215 | return Some(NestedMetaItemKind::Literal(respan(span, node))); |
cc61c64b XL |
1216 | } |
1217 | } | |
1218 | ||
1219 | MetaItem::from_tokens(tokens).map(NestedMetaItemKind::MetaItem) | |
1220 | } | |
1221 | } | |
1222 | ||
1223 | impl Lit { | |
1224 | fn tokens(&self) -> TokenStream { | |
1225 | TokenTree::Token(self.span, self.node.token()).into() | |
1226 | } | |
1227 | } | |
1228 | ||
1229 | impl LitKind { | |
1230 | fn token(&self) -> Token { | |
1231 | use std::ascii; | |
1232 | ||
1233 | match *self { | |
1234 | LitKind::Str(string, ast::StrStyle::Cooked) => { | |
1235 | let mut escaped = String::new(); | |
1236 | for ch in string.as_str().chars() { | |
1237 | escaped.extend(ch.escape_unicode()); | |
1238 | } | |
1239 | Token::Literal(token::Lit::Str_(Symbol::intern(&escaped)), None) | |
1240 | } | |
1241 | LitKind::Str(string, ast::StrStyle::Raw(n)) => { | |
1242 | Token::Literal(token::Lit::StrRaw(string, n), None) | |
1243 | } | |
1244 | LitKind::ByteStr(ref bytes) => { | |
1245 | let string = bytes.iter().cloned().flat_map(ascii::escape_default) | |
1246 | .map(Into::<char>::into).collect::<String>(); | |
1247 | Token::Literal(token::Lit::ByteStr(Symbol::intern(&string)), None) | |
1248 | } | |
1249 | LitKind::Byte(byte) => { | |
1250 | let string: String = ascii::escape_default(byte).map(Into::<char>::into).collect(); | |
1251 | Token::Literal(token::Lit::Byte(Symbol::intern(&string)), None) | |
1252 | } | |
1253 | LitKind::Char(ch) => { | |
1254 | let string: String = ch.escape_default().map(Into::<char>::into).collect(); | |
1255 | Token::Literal(token::Lit::Char(Symbol::intern(&string)), None) | |
1256 | } | |
1257 | LitKind::Int(n, ty) => { | |
1258 | let suffix = match ty { | |
1259 | ast::LitIntType::Unsigned(ty) => Some(Symbol::intern(ty.ty_to_string())), | |
1260 | ast::LitIntType::Signed(ty) => Some(Symbol::intern(ty.ty_to_string())), | |
1261 | ast::LitIntType::Unsuffixed => None, | |
1262 | }; | |
1263 | Token::Literal(token::Lit::Integer(Symbol::intern(&n.to_string())), suffix) | |
1264 | } | |
1265 | LitKind::Float(symbol, ty) => { | |
1266 | Token::Literal(token::Lit::Float(symbol), Some(Symbol::intern(ty.ty_to_string()))) | |
1267 | } | |
1268 | LitKind::FloatUnsuffixed(symbol) => Token::Literal(token::Lit::Float(symbol), None), | |
7cac9316 XL |
1269 | LitKind::Bool(value) => Token::Ident(Ident::with_empty_ctxt(Symbol::intern(if value { |
1270 | "true" | |
1271 | } else { | |
1272 | "false" | |
0531ce1d | 1273 | })), false), |
cc61c64b XL |
1274 | } |
1275 | } | |
1276 | ||
1277 | fn from_token(token: Token) -> Option<LitKind> { | |
1278 | match token { | |
0531ce1d XL |
1279 | Token::Ident(ident, false) if ident.name == "true" => Some(LitKind::Bool(true)), |
1280 | Token::Ident(ident, false) if ident.name == "false" => Some(LitKind::Bool(false)), | |
041b39d2 | 1281 | Token::Interpolated(ref nt) => match nt.0 { |
cc61c64b XL |
1282 | token::NtExpr(ref v) => match v.node { |
1283 | ExprKind::Lit(ref lit) => Some(lit.node.clone()), | |
1284 | _ => None, | |
1285 | }, | |
1286 | _ => None, | |
1287 | }, | |
1288 | Token::Literal(lit, suf) => { | |
1289 | let (suffix_illegal, result) = parse::lit_token(lit, suf, None); | |
1290 | if suffix_illegal && suf.is_some() { | |
1291 | return None; | |
1292 | } | |
1293 | result | |
1294 | } | |
1295 | _ => None, | |
1296 | } | |
1297 | } | |
1298 | } | |
1299 | ||
3157f602 XL |
1300 | pub trait HasAttrs: Sized { |
1301 | fn attrs(&self) -> &[ast::Attribute]; | |
1302 | fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self; | |
92a42be0 SL |
1303 | } |
1304 | ||
32a655c1 SL |
1305 | impl<T: HasAttrs> HasAttrs for Spanned<T> { |
1306 | fn attrs(&self) -> &[ast::Attribute] { self.node.attrs() } | |
1307 | fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self { | |
3b2f2976 | 1308 | respan(self.span, self.node.map_attrs(f)) |
32a655c1 SL |
1309 | } |
1310 | } | |
1311 | ||
3157f602 XL |
1312 | impl HasAttrs for Vec<Attribute> { |
1313 | fn attrs(&self) -> &[Attribute] { | |
7cac9316 | 1314 | self |
92a42be0 | 1315 | } |
3157f602 XL |
1316 | fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self { |
1317 | f(self) | |
92a42be0 | 1318 | } |
3157f602 | 1319 | } |
92a42be0 | 1320 | |
3157f602 XL |
1321 | impl HasAttrs for ThinVec<Attribute> { |
1322 | fn attrs(&self) -> &[Attribute] { | |
7cac9316 | 1323 | self |
92a42be0 | 1324 | } |
3157f602 XL |
1325 | fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self { |
1326 | f(self.into()).into() | |
92a42be0 | 1327 | } |
3157f602 | 1328 | } |
92a42be0 | 1329 | |
3157f602 XL |
1330 | impl<T: HasAttrs + 'static> HasAttrs for P<T> { |
1331 | fn attrs(&self) -> &[Attribute] { | |
1332 | (**self).attrs() | |
92a42be0 | 1333 | } |
3157f602 XL |
1334 | fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self { |
1335 | self.map(|t| t.map_attrs(f)) | |
92a42be0 SL |
1336 | } |
1337 | } | |
1338 | ||
3157f602 XL |
1339 | impl HasAttrs for StmtKind { |
1340 | fn attrs(&self) -> &[Attribute] { | |
1341 | match *self { | |
1342 | StmtKind::Local(ref local) => local.attrs(), | |
1343 | StmtKind::Item(..) => &[], | |
1344 | StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.attrs(), | |
1345 | StmtKind::Mac(ref mac) => { | |
1346 | let (_, _, ref attrs) = **mac; | |
1347 | attrs.attrs() | |
1348 | } | |
92a42be0 SL |
1349 | } |
1350 | } | |
92a42be0 | 1351 | |
3157f602 XL |
1352 | fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self { |
1353 | match self { | |
1354 | StmtKind::Local(local) => StmtKind::Local(local.map_attrs(f)), | |
1355 | StmtKind::Item(..) => self, | |
1356 | StmtKind::Expr(expr) => StmtKind::Expr(expr.map_attrs(f)), | |
1357 | StmtKind::Semi(expr) => StmtKind::Semi(expr.map_attrs(f)), | |
1358 | StmtKind::Mac(mac) => StmtKind::Mac(mac.map(|(mac, style, attrs)| { | |
1359 | (mac, style, attrs.map_attrs(f)) | |
1360 | })), | |
1361 | } | |
92a42be0 SL |
1362 | } |
1363 | } | |
1364 | ||
32a655c1 SL |
1365 | impl HasAttrs for Stmt { |
1366 | fn attrs(&self) -> &[ast::Attribute] { self.node.attrs() } | |
1367 | fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self { | |
1368 | Stmt { id: self.id, node: self.node.map_attrs(f), span: self.span } | |
1369 | } | |
1370 | } | |
1371 | ||
1372 | macro_rules! derive_has_attrs { | |
1373 | ($($ty:path),*) => { $( | |
3157f602 XL |
1374 | impl HasAttrs for $ty { |
1375 | fn attrs(&self) -> &[Attribute] { | |
32a655c1 | 1376 | &self.attrs |
92a42be0 | 1377 | } |
92a42be0 | 1378 | |
3157f602 XL |
1379 | fn map_attrs<F>(mut self, f: F) -> Self |
1380 | where F: FnOnce(Vec<Attribute>) -> Vec<Attribute>, | |
1381 | { | |
32a655c1 | 1382 | self.attrs = self.attrs.map_attrs(f); |
3157f602 | 1383 | self |
92a42be0 | 1384 | } |
3157f602 XL |
1385 | } |
1386 | )* } | |
92a42be0 SL |
1387 | } |
1388 | ||
32a655c1 SL |
1389 | derive_has_attrs! { |
1390 | Item, Expr, Local, ast::ForeignItem, ast::StructField, ast::ImplItem, ast::TraitItem, ast::Arm, | |
1391 | ast::Field, ast::FieldPat, ast::Variant_ | |
92a42be0 | 1392 | } |