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