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