]>
Commit | Line | Data |
---|---|---|
e1599b0c | 1 | //! Functions dealing with attributes and meta items. |
8faf50e0 | 2 | |
9fa01778 | 3 | use crate::ast; |
6a06907d XL |
4 | use crate::ast::{AttrId, AttrItem, AttrKind, AttrStyle, Attribute}; |
5 | use crate::ast::{Lit, LitKind}; | |
60c5eb7d | 6 | use crate::ast::{MacArgs, MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem}; |
f9f354fc | 7 | use crate::ast::{Path, PathSegment}; |
3dfed10e | 8 | use crate::token::{self, CommentKind, Token}; |
29967ef6 | 9 | use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream, TokenTree, TreeAndSpacing}; |
9fa01778 | 10 | |
74b04a01 | 11 | use rustc_index::bit_set::GrowableBitSet; |
6a06907d | 12 | use rustc_span::source_map::BytePos; |
f9f354fc | 13 | use rustc_span::symbol::{sym, Ident, Symbol}; |
dfeec247 | 14 | use rustc_span::Span; |
8faf50e0 XL |
15 | |
16 | use std::iter; | |
17 | ||
3dfed10e | 18 | pub struct MarkedAttrs(GrowableBitSet<AttrId>); |
74b04a01 | 19 | |
3dfed10e XL |
20 | impl MarkedAttrs { |
21 | // We have no idea how many attributes there will be, so just | |
22 | // initiate the vectors with 0 bits. We'll grow them as necessary. | |
23 | pub fn new() -> Self { | |
24 | MarkedAttrs(GrowableBitSet::new_empty()) | |
74b04a01 | 25 | } |
74b04a01 | 26 | |
3dfed10e XL |
27 | pub fn mark(&mut self, attr: &Attribute) { |
28 | self.0.insert(attr.id); | |
29 | } | |
8faf50e0 | 30 | |
3dfed10e XL |
31 | pub fn is_marked(&self, attr: &Attribute) -> bool { |
32 | self.0.contains(attr.id) | |
33 | } | |
8faf50e0 XL |
34 | } |
35 | ||
8faf50e0 | 36 | impl NestedMetaItem { |
e1599b0c | 37 | /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem`. |
8faf50e0 | 38 | pub fn meta_item(&self) -> Option<&MetaItem> { |
532ac7d7 XL |
39 | match *self { |
40 | NestedMetaItem::MetaItem(ref item) => Some(item), | |
dfeec247 | 41 | _ => None, |
8faf50e0 XL |
42 | } |
43 | } | |
44 | ||
e1599b0c | 45 | /// Returns the `Lit` if `self` is a `NestedMetaItem::Literal`s. |
8faf50e0 | 46 | pub fn literal(&self) -> Option<&Lit> { |
532ac7d7 XL |
47 | match *self { |
48 | NestedMetaItem::Literal(ref lit) => Some(lit), | |
dfeec247 | 49 | _ => None, |
8faf50e0 XL |
50 | } |
51 | } | |
52 | ||
9fa01778 | 53 | /// Returns `true` if this list item is a MetaItem with a name of `name`. |
3dfed10e XL |
54 | pub fn has_name(&self, name: Symbol) -> bool { |
55 | self.meta_item().map_or(false, |meta_item| meta_item.has_name(name)) | |
8faf50e0 XL |
56 | } |
57 | ||
e1599b0c | 58 | /// For a single-segment meta item, returns its name; otherwise, returns `None`. |
9fa01778 XL |
59 | pub fn ident(&self) -> Option<Ident> { |
60 | self.meta_item().and_then(|meta_item| meta_item.ident()) | |
61 | } | |
48663c56 | 62 | pub fn name_or_empty(&self) -> Symbol { |
29967ef6 | 63 | self.ident().unwrap_or_else(Ident::invalid).name |
8faf50e0 XL |
64 | } |
65 | ||
e1599b0c XL |
66 | /// Gets the string value if `self` is a `MetaItem` and the `MetaItem` is a |
67 | /// `MetaItemKind::NameValue` variant containing a string, otherwise `None`. | |
8faf50e0 XL |
68 | pub fn value_str(&self) -> Option<Symbol> { |
69 | self.meta_item().and_then(|meta_item| meta_item.value_str()) | |
70 | } | |
71 | ||
e1599b0c | 72 | /// Returns a name and single literal value tuple of the `MetaItem`. |
f9f354fc | 73 | pub fn name_value_literal(&self) -> Option<(Symbol, &Lit)> { |
dfeec247 XL |
74 | self.meta_item().and_then(|meta_item| { |
75 | meta_item.meta_item_list().and_then(|meta_item_list| { | |
76 | if meta_item_list.len() == 1 { | |
77 | if let Some(ident) = meta_item.ident() { | |
78 | if let Some(lit) = meta_item_list[0].literal() { | |
79 | return Some((ident.name, lit)); | |
8faf50e0 XL |
80 | } |
81 | } | |
dfeec247 XL |
82 | } |
83 | None | |
84 | }) | |
85 | }) | |
8faf50e0 XL |
86 | } |
87 | ||
e1599b0c | 88 | /// Gets a list of inner meta items from a list `MetaItem` type. |
8faf50e0 XL |
89 | pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> { |
90 | self.meta_item().and_then(|meta_item| meta_item.meta_item_list()) | |
91 | } | |
92 | ||
e1599b0c | 93 | /// Returns `true` if the variant is `MetaItem`. |
8faf50e0 XL |
94 | pub fn is_meta_item(&self) -> bool { |
95 | self.meta_item().is_some() | |
96 | } | |
97 | ||
e1599b0c | 98 | /// Returns `true` if `self` is a `MetaItem` and the meta item is a word. |
8faf50e0 | 99 | pub fn is_word(&self) -> bool { |
9fa01778 | 100 | self.meta_item().map_or(false, |meta_item| meta_item.is_word()) |
8faf50e0 XL |
101 | } |
102 | ||
e1599b0c | 103 | /// Returns `true` if `self` is a `MetaItem` and the meta item is a `ValueString`. |
8faf50e0 XL |
104 | pub fn is_value_str(&self) -> bool { |
105 | self.value_str().is_some() | |
106 | } | |
107 | ||
e1599b0c | 108 | /// Returns `true` if `self` is a `MetaItem` and the meta item is a list. |
8faf50e0 XL |
109 | pub fn is_meta_item_list(&self) -> bool { |
110 | self.meta_item_list().is_some() | |
111 | } | |
fc512014 XL |
112 | |
113 | pub fn name_value_literal_span(&self) -> Option<Span> { | |
114 | self.meta_item()?.name_value_literal_span() | |
115 | } | |
8faf50e0 XL |
116 | } |
117 | ||
8faf50e0 | 118 | impl Attribute { |
6a06907d | 119 | #[inline] |
60c5eb7d XL |
120 | pub fn has_name(&self, name: Symbol) -> bool { |
121 | match self.kind { | |
29967ef6 | 122 | AttrKind::Normal(ref item, _) => item.path == name, |
3dfed10e | 123 | AttrKind::DocComment(..) => false, |
8faf50e0 | 124 | } |
8faf50e0 XL |
125 | } |
126 | ||
e1599b0c | 127 | /// For a single-segment attribute, returns its name; otherwise, returns `None`. |
9fa01778 | 128 | pub fn ident(&self) -> Option<Ident> { |
60c5eb7d | 129 | match self.kind { |
29967ef6 | 130 | AttrKind::Normal(ref item, _) => { |
60c5eb7d XL |
131 | if item.path.segments.len() == 1 { |
132 | Some(item.path.segments[0].ident) | |
133 | } else { | |
134 | None | |
135 | } | |
136 | } | |
3dfed10e | 137 | AttrKind::DocComment(..) => None, |
9fa01778 XL |
138 | } |
139 | } | |
48663c56 | 140 | pub fn name_or_empty(&self) -> Symbol { |
29967ef6 | 141 | self.ident().unwrap_or_else(Ident::invalid).name |
8faf50e0 XL |
142 | } |
143 | ||
144 | pub fn value_str(&self) -> Option<Symbol> { | |
60c5eb7d | 145 | match self.kind { |
29967ef6 | 146 | AttrKind::Normal(ref item, _) => item.meta(self.span).and_then(|meta| meta.value_str()), |
dfeec247 | 147 | AttrKind::DocComment(..) => None, |
60c5eb7d | 148 | } |
8faf50e0 XL |
149 | } |
150 | ||
151 | pub fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> { | |
60c5eb7d | 152 | match self.kind { |
29967ef6 | 153 | AttrKind::Normal(ref item, _) => match item.meta(self.span) { |
dfeec247 XL |
154 | Some(MetaItem { kind: MetaItemKind::List(list), .. }) => Some(list), |
155 | _ => None, | |
156 | }, | |
3dfed10e | 157 | AttrKind::DocComment(..) => None, |
8faf50e0 XL |
158 | } |
159 | } | |
160 | ||
161 | pub fn is_word(&self) -> bool { | |
29967ef6 | 162 | if let AttrKind::Normal(item, _) = &self.kind { |
60c5eb7d XL |
163 | matches!(item.args, MacArgs::Empty) |
164 | } else { | |
165 | false | |
166 | } | |
8faf50e0 XL |
167 | } |
168 | ||
8faf50e0 XL |
169 | pub fn is_meta_item_list(&self) -> bool { |
170 | self.meta_item_list().is_some() | |
171 | } | |
172 | ||
e1599b0c | 173 | /// Indicates if the attribute is a `ValueString`. |
8faf50e0 XL |
174 | pub fn is_value_str(&self) -> bool { |
175 | self.value_str().is_some() | |
176 | } | |
fc512014 XL |
177 | |
178 | /// This is used in case you want the value span instead of the whole attribute. Example: | |
179 | /// | |
180 | /// ```text | |
181 | /// #[doc(alias = "foo")] | |
182 | /// ``` | |
183 | /// | |
184 | /// In here, it'll return a span for `"foo"`. | |
185 | pub fn name_value_literal_span(&self) -> Option<Span> { | |
186 | match self.kind { | |
187 | AttrKind::Normal(ref item, _) => { | |
188 | item.meta(self.span).and_then(|meta| meta.name_value_literal_span()) | |
189 | } | |
190 | AttrKind::DocComment(..) => None, | |
191 | } | |
192 | } | |
8faf50e0 XL |
193 | } |
194 | ||
195 | impl MetaItem { | |
e1599b0c | 196 | /// For a single-segment meta item, returns its name; otherwise, returns `None`. |
9fa01778 | 197 | pub fn ident(&self) -> Option<Ident> { |
dfeec247 | 198 | if self.path.segments.len() == 1 { Some(self.path.segments[0].ident) } else { None } |
9fa01778 | 199 | } |
48663c56 | 200 | pub fn name_or_empty(&self) -> Symbol { |
29967ef6 | 201 | self.ident().unwrap_or_else(Ident::invalid).name |
8faf50e0 XL |
202 | } |
203 | ||
e1599b0c XL |
204 | // Example: |
205 | // #[attribute(name = "value")] | |
206 | // ^^^^^^^^^^^^^^ | |
a1dfa0c6 | 207 | pub fn name_value_literal(&self) -> Option<&Lit> { |
e74abb32 | 208 | match &self.kind { |
a1dfa0c6 XL |
209 | MetaItemKind::NameValue(v) => Some(v), |
210 | _ => None, | |
211 | } | |
212 | } | |
213 | ||
8faf50e0 | 214 | pub fn value_str(&self) -> Option<Symbol> { |
e74abb32 | 215 | match self.kind { |
dfeec247 XL |
216 | MetaItemKind::NameValue(ref v) => match v.kind { |
217 | LitKind::Str(ref s, _) => Some(*s), | |
218 | _ => None, | |
8faf50e0 | 219 | }, |
dfeec247 | 220 | _ => None, |
8faf50e0 XL |
221 | } |
222 | } | |
223 | ||
224 | pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> { | |
e74abb32 | 225 | match self.kind { |
8faf50e0 | 226 | MetaItemKind::List(ref l) => Some(&l[..]), |
dfeec247 | 227 | _ => None, |
8faf50e0 XL |
228 | } |
229 | } | |
230 | ||
231 | pub fn is_word(&self) -> bool { | |
5869c6ff | 232 | matches!(self.kind, MetaItemKind::Word) |
8faf50e0 XL |
233 | } |
234 | ||
3dfed10e | 235 | pub fn has_name(&self, name: Symbol) -> bool { |
532ac7d7 | 236 | self.path == name |
8faf50e0 XL |
237 | } |
238 | ||
239 | pub fn is_value_str(&self) -> bool { | |
240 | self.value_str().is_some() | |
241 | } | |
fc512014 XL |
242 | |
243 | /// This is used in case you want the value span instead of the whole attribute. Example: | |
244 | /// | |
245 | /// ```text | |
246 | /// #[doc(alias = "foo")] | |
247 | /// ``` | |
248 | /// | |
249 | /// In here, it'll return a span for `"foo"`. | |
250 | pub fn name_value_literal_span(&self) -> Option<Span> { | |
251 | Some(self.name_value_literal()?.span) | |
252 | } | |
8faf50e0 XL |
253 | } |
254 | ||
e74abb32 | 255 | impl AttrItem { |
74b04a01 XL |
256 | pub fn span(&self) -> Span { |
257 | self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span)) | |
258 | } | |
259 | ||
60c5eb7d | 260 | pub fn meta(&self, span: Span) -> Option<MetaItem> { |
8faf50e0 | 261 | Some(MetaItem { |
532ac7d7 | 262 | path: self.path.clone(), |
60c5eb7d | 263 | kind: MetaItemKind::from_mac_args(&self.args)?, |
e74abb32 | 264 | span, |
8faf50e0 XL |
265 | }) |
266 | } | |
e74abb32 | 267 | } |
8faf50e0 | 268 | |
e74abb32 | 269 | impl Attribute { |
60c5eb7d XL |
270 | pub fn is_doc_comment(&self) -> bool { |
271 | match self.kind { | |
29967ef6 | 272 | AttrKind::Normal(..) => false, |
3dfed10e | 273 | AttrKind::DocComment(..) => true, |
60c5eb7d | 274 | } |
8faf50e0 XL |
275 | } |
276 | ||
dfeec247 XL |
277 | pub fn doc_str(&self) -> Option<Symbol> { |
278 | match self.kind { | |
3dfed10e | 279 | AttrKind::DocComment(.., data) => Some(data), |
29967ef6 | 280 | AttrKind::Normal(ref item, _) if item.path == sym::doc => { |
dfeec247 XL |
281 | item.meta(self.span).and_then(|meta| meta.value_str()) |
282 | } | |
283 | _ => None, | |
284 | } | |
285 | } | |
286 | ||
60c5eb7d XL |
287 | pub fn get_normal_item(&self) -> &AttrItem { |
288 | match self.kind { | |
29967ef6 | 289 | AttrKind::Normal(ref item, _) => item, |
3dfed10e | 290 | AttrKind::DocComment(..) => panic!("unexpected doc comment"), |
60c5eb7d XL |
291 | } |
292 | } | |
293 | ||
294 | pub fn unwrap_normal_item(self) -> AttrItem { | |
295 | match self.kind { | |
29967ef6 | 296 | AttrKind::Normal(item, _) => item, |
3dfed10e | 297 | AttrKind::DocComment(..) => panic!("unexpected doc comment"), |
60c5eb7d XL |
298 | } |
299 | } | |
300 | ||
301 | /// Extracts the MetaItem from inside this Attribute. | |
302 | pub fn meta(&self) -> Option<MetaItem> { | |
303 | match self.kind { | |
29967ef6 | 304 | AttrKind::Normal(ref item, _) => item.meta(self.span), |
dfeec247 | 305 | AttrKind::DocComment(..) => None, |
60c5eb7d | 306 | } |
8faf50e0 | 307 | } |
29967ef6 XL |
308 | |
309 | pub fn tokens(&self) -> TokenStream { | |
310 | match self.kind { | |
311 | AttrKind::Normal(_, ref tokens) => tokens | |
312 | .as_ref() | |
313 | .unwrap_or_else(|| panic!("attribute is missing tokens: {:?}", self)) | |
314 | .create_token_stream(), | |
315 | AttrKind::DocComment(comment_kind, data) => TokenStream::from(TokenTree::Token( | |
316 | Token::new(token::DocComment(comment_kind, self.style, data), self.span), | |
317 | )), | |
318 | } | |
319 | } | |
8faf50e0 XL |
320 | } |
321 | ||
322 | /* Constructors */ | |
323 | ||
e1599b0c XL |
324 | pub fn mk_name_value_item_str(ident: Ident, str: Symbol, str_span: Span) -> MetaItem { |
325 | let lit_kind = LitKind::Str(str, ast::StrStyle::Cooked); | |
326 | mk_name_value_item(ident, lit_kind, str_span) | |
8faf50e0 XL |
327 | } |
328 | ||
416331ca | 329 | pub fn mk_name_value_item(ident: Ident, lit_kind: LitKind, lit_span: Span) -> MetaItem { |
48663c56 | 330 | let lit = Lit::from_lit_kind(lit_kind, lit_span); |
416331ca | 331 | let span = ident.span.to(lit_span); |
e74abb32 | 332 | MetaItem { path: Path::from_ident(ident), span, kind: MetaItemKind::NameValue(lit) } |
8faf50e0 XL |
333 | } |
334 | ||
416331ca | 335 | pub fn mk_list_item(ident: Ident, items: Vec<NestedMetaItem>) -> MetaItem { |
e74abb32 | 336 | MetaItem { path: Path::from_ident(ident), span: ident.span, kind: MetaItemKind::List(items) } |
8faf50e0 XL |
337 | } |
338 | ||
339 | pub fn mk_word_item(ident: Ident) -> MetaItem { | |
e74abb32 | 340 | MetaItem { path: Path::from_ident(ident), span: ident.span, kind: MetaItemKind::Word } |
8faf50e0 XL |
341 | } |
342 | ||
343 | pub fn mk_nested_word_item(ident: Ident) -> NestedMetaItem { | |
532ac7d7 | 344 | NestedMetaItem::MetaItem(mk_word_item(ident)) |
8faf50e0 XL |
345 | } |
346 | ||
416331ca | 347 | crate fn mk_attr_id() -> AttrId { |
ba9703b0 | 348 | use std::sync::atomic::AtomicU32; |
8faf50e0 XL |
349 | use std::sync::atomic::Ordering; |
350 | ||
ba9703b0 | 351 | static NEXT_ATTR_ID: AtomicU32 = AtomicU32::new(0); |
8faf50e0 XL |
352 | |
353 | let id = NEXT_ATTR_ID.fetch_add(1, Ordering::SeqCst); | |
ba9703b0 XL |
354 | assert!(id != u32::MAX); |
355 | AttrId::from_u32(id) | |
8faf50e0 XL |
356 | } |
357 | ||
60c5eb7d | 358 | pub fn mk_attr(style: AttrStyle, path: Path, args: MacArgs, span: Span) -> Attribute { |
29967ef6 | 359 | mk_attr_from_item(AttrItem { path, args, tokens: None }, None, style, span) |
60c5eb7d XL |
360 | } |
361 | ||
29967ef6 XL |
362 | pub fn mk_attr_from_item( |
363 | item: AttrItem, | |
364 | tokens: Option<LazyTokenStream>, | |
365 | style: AttrStyle, | |
366 | span: Span, | |
367 | ) -> Attribute { | |
368 | Attribute { kind: AttrKind::Normal(item, tokens), id: mk_attr_id(), style, span } | |
8faf50e0 XL |
369 | } |
370 | ||
e1599b0c XL |
371 | /// Returns an inner attribute with the given value and span. |
372 | pub fn mk_attr_inner(item: MetaItem) -> Attribute { | |
60c5eb7d | 373 | mk_attr(AttrStyle::Inner, item.path, item.kind.mac_args(item.span), item.span) |
e1599b0c XL |
374 | } |
375 | ||
8faf50e0 | 376 | /// Returns an outer attribute with the given value and span. |
416331ca | 377 | pub fn mk_attr_outer(item: MetaItem) -> Attribute { |
60c5eb7d | 378 | mk_attr(AttrStyle::Outer, item.path, item.kind.mac_args(item.span), item.span) |
8faf50e0 XL |
379 | } |
380 | ||
3dfed10e XL |
381 | pub fn mk_doc_comment( |
382 | comment_kind: CommentKind, | |
383 | style: AttrStyle, | |
384 | data: Symbol, | |
385 | span: Span, | |
386 | ) -> Attribute { | |
387 | Attribute { kind: AttrKind::DocComment(comment_kind, data), id: mk_attr_id(), style, span } | |
8faf50e0 XL |
388 | } |
389 | ||
48663c56 | 390 | pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool { |
3dfed10e | 391 | items.iter().any(|item| item.has_name(name)) |
8faf50e0 XL |
392 | } |
393 | ||
394 | impl MetaItem { | |
1b1a35ee | 395 | fn token_trees_and_spacings(&self) -> Vec<TreeAndSpacing> { |
8faf50e0 | 396 | let mut idents = vec![]; |
29967ef6 | 397 | let mut last_pos = BytePos(0_u32); |
532ac7d7 | 398 | for (i, segment) in self.path.segments.iter().enumerate() { |
8faf50e0 XL |
399 | let is_first = i == 0; |
400 | if !is_first { | |
dfeec247 XL |
401 | let mod_sep_span = |
402 | Span::new(last_pos, segment.ident.span.lo(), segment.ident.span.ctxt()); | |
dc9dc135 | 403 | idents.push(TokenTree::token(token::ModSep, mod_sep_span).into()); |
8faf50e0 | 404 | } |
dc9dc135 | 405 | idents.push(TokenTree::Token(Token::from_ast_ident(segment.ident)).into()); |
8faf50e0 XL |
406 | last_pos = segment.ident.span.hi(); |
407 | } | |
1b1a35ee | 408 | idents.extend(self.kind.token_trees_and_spacings(self.span)); |
e74abb32 | 409 | idents |
8faf50e0 XL |
410 | } |
411 | ||
412 | fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem> | |
dfeec247 XL |
413 | where |
414 | I: Iterator<Item = TokenTree>, | |
8faf50e0 XL |
415 | { |
416 | // FIXME: Share code with `parse_path`. | |
74b04a01 | 417 | let path = match tokens.next().map(TokenTree::uninterpolate) { |
ba9703b0 XL |
418 | Some(TokenTree::Token(Token { |
419 | kind: kind @ (token::Ident(..) | token::ModSep), | |
420 | span, | |
421 | })) => 'arm: { | |
dc9dc135 | 422 | let mut segments = if let token::Ident(name, _) = kind { |
dfeec247 XL |
423 | if let Some(TokenTree::Token(Token { kind: token::ModSep, .. })) = tokens.peek() |
424 | { | |
9fa01778 | 425 | tokens.next(); |
dc9dc135 | 426 | vec![PathSegment::from_ident(Ident::new(name, span))] |
9fa01778 | 427 | } else { |
dc9dc135 | 428 | break 'arm Path::from_ident(Ident::new(name, span)); |
8faf50e0 | 429 | } |
8faf50e0 | 430 | } else { |
9fa01778 XL |
431 | vec![PathSegment::path_root(span)] |
432 | }; | |
433 | loop { | |
dfeec247 | 434 | if let Some(TokenTree::Token(Token { kind: token::Ident(name, _), span })) = |
74b04a01 | 435 | tokens.next().map(TokenTree::uninterpolate) |
dfeec247 | 436 | { |
dc9dc135 | 437 | segments.push(PathSegment::from_ident(Ident::new(name, span))); |
9fa01778 XL |
438 | } else { |
439 | return None; | |
440 | } | |
dfeec247 XL |
441 | if let Some(TokenTree::Token(Token { kind: token::ModSep, .. })) = tokens.peek() |
442 | { | |
9fa01778 XL |
443 | tokens.next(); |
444 | } else { | |
445 | break; | |
446 | } | |
8faf50e0 | 447 | } |
9fa01778 | 448 | let span = span.with_hi(segments.last().unwrap().ident.span.hi()); |
1b1a35ee | 449 | Path { span, segments, tokens: None } |
8faf50e0 | 450 | } |
dc9dc135 | 451 | Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. })) => match *nt { |
e74abb32 | 452 | token::Nonterminal::NtMeta(ref item) => return item.meta(item.path.span), |
8faf50e0 XL |
453 | token::Nonterminal::NtPath(ref path) => path.clone(), |
454 | _ => return None, | |
455 | }, | |
456 | _ => return None, | |
457 | }; | |
458 | let list_closing_paren_pos = tokens.peek().map(|tt| tt.span().hi()); | |
e74abb32 XL |
459 | let kind = MetaItemKind::from_tokens(tokens)?; |
460 | let hi = match kind { | |
8faf50e0 | 461 | MetaItemKind::NameValue(ref lit) => lit.span.hi(), |
532ac7d7 XL |
462 | MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(path.span.hi()), |
463 | _ => path.span.hi(), | |
8faf50e0 | 464 | }; |
532ac7d7 | 465 | let span = path.span.with_hi(hi); |
e74abb32 | 466 | Some(MetaItem { path, kind, span }) |
8faf50e0 XL |
467 | } |
468 | } | |
469 | ||
470 | impl MetaItemKind { | |
60c5eb7d XL |
471 | pub fn mac_args(&self, span: Span) -> MacArgs { |
472 | match self { | |
473 | MetaItemKind::Word => MacArgs::Empty, | |
5869c6ff | 474 | MetaItemKind::NameValue(lit) => MacArgs::Eq(span, lit.to_token()), |
60c5eb7d XL |
475 | MetaItemKind::List(list) => { |
476 | let mut tts = Vec::new(); | |
477 | for (i, item) in list.iter().enumerate() { | |
478 | if i > 0 { | |
479 | tts.push(TokenTree::token(token::Comma, span).into()); | |
480 | } | |
1b1a35ee | 481 | tts.extend(item.token_trees_and_spacings()) |
60c5eb7d XL |
482 | } |
483 | MacArgs::Delimited( | |
dfeec247 XL |
484 | DelimSpan::from_single(span), |
485 | MacDelimiter::Parenthesis, | |
486 | TokenStream::new(tts), | |
60c5eb7d XL |
487 | ) |
488 | } | |
489 | } | |
490 | } | |
491 | ||
1b1a35ee | 492 | fn token_trees_and_spacings(&self, span: Span) -> Vec<TreeAndSpacing> { |
8faf50e0 | 493 | match *self { |
e74abb32 | 494 | MetaItemKind::Word => vec![], |
8faf50e0 | 495 | MetaItemKind::NameValue(ref lit) => { |
5869c6ff XL |
496 | vec![ |
497 | TokenTree::token(token::Eq, span).into(), | |
498 | TokenTree::Token(lit.to_token()).into(), | |
499 | ] | |
8faf50e0 XL |
500 | } |
501 | MetaItemKind::List(ref list) => { | |
502 | let mut tokens = Vec::new(); | |
503 | for (i, item) in list.iter().enumerate() { | |
504 | if i > 0 { | |
dc9dc135 | 505 | tokens.push(TokenTree::token(token::Comma, span).into()); |
8faf50e0 | 506 | } |
1b1a35ee | 507 | tokens.extend(item.token_trees_and_spacings()) |
8faf50e0 | 508 | } |
e74abb32 XL |
509 | vec![ |
510 | TokenTree::Delimited( | |
511 | DelimSpan::from_single(span), | |
512 | token::Paren, | |
74b04a01 | 513 | TokenStream::new(tokens), |
dfeec247 XL |
514 | ) |
515 | .into(), | |
e74abb32 | 516 | ] |
8faf50e0 XL |
517 | } |
518 | } | |
519 | } | |
520 | ||
60c5eb7d XL |
521 | fn list_from_tokens(tokens: TokenStream) -> Option<MetaItemKind> { |
522 | let mut tokens = tokens.into_trees().peekable(); | |
8faf50e0 | 523 | let mut result = Vec::new(); |
5869c6ff | 524 | while tokens.peek().is_some() { |
532ac7d7 XL |
525 | let item = NestedMetaItem::from_tokens(&mut tokens)?; |
526 | result.push(item); | |
8faf50e0 | 527 | match tokens.next() { |
dc9dc135 | 528 | None | Some(TokenTree::Token(Token { kind: token::Comma, .. })) => {} |
8faf50e0 XL |
529 | _ => return None, |
530 | } | |
531 | } | |
532 | Some(MetaItemKind::List(result)) | |
533 | } | |
60c5eb7d XL |
534 | |
535 | fn name_value_from_tokens( | |
536 | tokens: &mut impl Iterator<Item = TokenTree>, | |
537 | ) -> Option<MetaItemKind> { | |
538 | match tokens.next() { | |
f035d41b XL |
539 | Some(TokenTree::Delimited(_, token::NoDelim, inner_tokens)) => { |
540 | MetaItemKind::name_value_from_tokens(&mut inner_tokens.trees()) | |
541 | } | |
dfeec247 XL |
542 | Some(TokenTree::Token(token)) => { |
543 | Lit::from_token(&token).ok().map(MetaItemKind::NameValue) | |
544 | } | |
60c5eb7d XL |
545 | _ => None, |
546 | } | |
547 | } | |
548 | ||
549 | fn from_mac_args(args: &MacArgs) -> Option<MetaItemKind> { | |
550 | match args { | |
dfeec247 XL |
551 | MacArgs::Delimited(_, MacDelimiter::Parenthesis, tokens) => { |
552 | MetaItemKind::list_from_tokens(tokens.clone()) | |
553 | } | |
60c5eb7d | 554 | MacArgs::Delimited(..) => None, |
5869c6ff | 555 | MacArgs::Eq(_, token) => Lit::from_token(token).ok().map(MetaItemKind::NameValue), |
60c5eb7d XL |
556 | MacArgs::Empty => Some(MetaItemKind::Word), |
557 | } | |
558 | } | |
559 | ||
560 | fn from_tokens( | |
561 | tokens: &mut iter::Peekable<impl Iterator<Item = TokenTree>>, | |
562 | ) -> Option<MetaItemKind> { | |
563 | match tokens.peek() { | |
564 | Some(TokenTree::Delimited(_, token::Paren, inner_tokens)) => { | |
565 | let inner_tokens = inner_tokens.clone(); | |
566 | tokens.next(); | |
567 | MetaItemKind::list_from_tokens(inner_tokens) | |
568 | } | |
569 | Some(TokenTree::Delimited(..)) => None, | |
570 | Some(TokenTree::Token(Token { kind: token::Eq, .. })) => { | |
571 | tokens.next(); | |
572 | MetaItemKind::name_value_from_tokens(tokens) | |
573 | } | |
574 | _ => Some(MetaItemKind::Word), | |
575 | } | |
576 | } | |
8faf50e0 XL |
577 | } |
578 | ||
532ac7d7 XL |
579 | impl NestedMetaItem { |
580 | pub fn span(&self) -> Span { | |
8faf50e0 | 581 | match *self { |
532ac7d7 XL |
582 | NestedMetaItem::MetaItem(ref item) => item.span, |
583 | NestedMetaItem::Literal(ref lit) => lit.span, | |
8faf50e0 XL |
584 | } |
585 | } | |
586 | ||
1b1a35ee | 587 | fn token_trees_and_spacings(&self) -> Vec<TreeAndSpacing> { |
8faf50e0 | 588 | match *self { |
1b1a35ee | 589 | NestedMetaItem::MetaItem(ref item) => item.token_trees_and_spacings(), |
5869c6ff | 590 | NestedMetaItem::Literal(ref lit) => vec![TokenTree::Token(lit.to_token()).into()], |
8faf50e0 XL |
591 | } |
592 | } | |
593 | ||
532ac7d7 | 594 | fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItem> |
dfeec247 XL |
595 | where |
596 | I: Iterator<Item = TokenTree>, | |
8faf50e0 | 597 | { |
f035d41b XL |
598 | match tokens.peek() { |
599 | Some(TokenTree::Token(token)) => { | |
600 | if let Ok(lit) = Lit::from_token(token) { | |
601 | tokens.next(); | |
602 | return Some(NestedMetaItem::Literal(lit)); | |
603 | } | |
604 | } | |
605 | Some(TokenTree::Delimited(_, token::NoDelim, inner_tokens)) => { | |
606 | let inner_tokens = inner_tokens.clone(); | |
8faf50e0 | 607 | tokens.next(); |
f035d41b | 608 | return NestedMetaItem::from_tokens(&mut inner_tokens.into_trees().peekable()); |
8faf50e0 | 609 | } |
f035d41b | 610 | _ => {} |
8faf50e0 | 611 | } |
532ac7d7 | 612 | MetaItem::from_tokens(tokens).map(NestedMetaItem::MetaItem) |
8faf50e0 XL |
613 | } |
614 | } |