• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Functions dealing with attributes and meta items.
2 
3 use crate::ast::{AttrArgs, AttrArgsEq, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute};
4 use crate::ast::{DelimArgs, Expr, ExprKind, LitKind, MetaItemLit};
5 use crate::ast::{MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem, NormalAttr};
6 use crate::ast::{Path, PathSegment, DUMMY_NODE_ID};
7 use crate::ptr::P;
8 use crate::token::{self, CommentKind, Delimiter, Token};
9 use crate::tokenstream::{DelimSpan, Spacing, TokenTree};
10 use crate::tokenstream::{LazyAttrTokenStream, TokenStream};
11 use crate::util::comments;
12 use crate::util::literal::escape_string_symbol;
13 use rustc_index::bit_set::GrowableBitSet;
14 use rustc_span::symbol::{sym, Ident, Symbol};
15 use rustc_span::Span;
16 use std::iter;
17 use std::sync::atomic::{AtomicU32, Ordering};
18 use thin_vec::{thin_vec, ThinVec};
19 
20 pub struct MarkedAttrs(GrowableBitSet<AttrId>);
21 
22 impl MarkedAttrs {
new() -> Self23     pub fn new() -> Self {
24         // We have no idea how many attributes there will be, so just
25         // initiate the vectors with 0 bits. We'll grow them as necessary.
26         MarkedAttrs(GrowableBitSet::new_empty())
27     }
28 
mark(&mut self, attr: &Attribute)29     pub fn mark(&mut self, attr: &Attribute) {
30         self.0.insert(attr.id);
31     }
32 
is_marked(&self, attr: &Attribute) -> bool33     pub fn is_marked(&self, attr: &Attribute) -> bool {
34         self.0.contains(attr.id)
35     }
36 }
37 
38 pub struct AttrIdGenerator(AtomicU32);
39 
40 impl AttrIdGenerator {
new() -> Self41     pub fn new() -> Self {
42         AttrIdGenerator(AtomicU32::new(0))
43     }
44 
mk_attr_id(&self) -> AttrId45     pub fn mk_attr_id(&self) -> AttrId {
46         let id = self.0.fetch_add(1, Ordering::Relaxed);
47         assert!(id != u32::MAX);
48         AttrId::from_u32(id)
49     }
50 }
51 
52 impl Attribute {
get_normal_item(&self) -> &AttrItem53     pub fn get_normal_item(&self) -> &AttrItem {
54         match &self.kind {
55             AttrKind::Normal(normal) => &normal.item,
56             AttrKind::DocComment(..) => panic!("unexpected doc comment"),
57         }
58     }
59 
unwrap_normal_item(self) -> AttrItem60     pub fn unwrap_normal_item(self) -> AttrItem {
61         match self.kind {
62             AttrKind::Normal(normal) => normal.into_inner().item,
63             AttrKind::DocComment(..) => panic!("unexpected doc comment"),
64         }
65     }
66 
67     /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).
68     /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not
69     /// a doc comment) will return `false`.
is_doc_comment(&self) -> bool70     pub fn is_doc_comment(&self) -> bool {
71         match self.kind {
72             AttrKind::Normal(..) => false,
73             AttrKind::DocComment(..) => true,
74         }
75     }
76 
77     /// For a single-segment attribute, returns its name; otherwise, returns `None`.
ident(&self) -> Option<Ident>78     pub fn ident(&self) -> Option<Ident> {
79         match &self.kind {
80             AttrKind::Normal(normal) => {
81                 if let [ident] = &*normal.item.path.segments {
82                     Some(ident.ident)
83                 } else {
84                     None
85                 }
86             }
87             AttrKind::DocComment(..) => None,
88         }
89     }
name_or_empty(&self) -> Symbol90     pub fn name_or_empty(&self) -> Symbol {
91         self.ident().unwrap_or_else(Ident::empty).name
92     }
93 
94     #[inline]
has_name(&self, name: Symbol) -> bool95     pub fn has_name(&self, name: Symbol) -> bool {
96         match &self.kind {
97             AttrKind::Normal(normal) => normal.item.path == name,
98             AttrKind::DocComment(..) => false,
99         }
100     }
101 
is_word(&self) -> bool102     pub fn is_word(&self) -> bool {
103         if let AttrKind::Normal(normal) = &self.kind {
104             matches!(normal.item.args, AttrArgs::Empty)
105         } else {
106             false
107         }
108     }
109 
meta_item_list(&self) -> Option<ThinVec<NestedMetaItem>>110     pub fn meta_item_list(&self) -> Option<ThinVec<NestedMetaItem>> {
111         match &self.kind {
112             AttrKind::Normal(normal) => normal.item.meta_item_list(),
113             AttrKind::DocComment(..) => None,
114         }
115     }
116 
value_str(&self) -> Option<Symbol>117     pub fn value_str(&self) -> Option<Symbol> {
118         match &self.kind {
119             AttrKind::Normal(normal) => normal.item.value_str(),
120             AttrKind::DocComment(..) => None,
121         }
122     }
123 
124     /// Returns the documentation and its kind if this is a doc comment or a sugared doc comment.
125     /// * `///doc` returns `Some(("doc", CommentKind::Line))`.
126     /// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
127     /// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
128     /// * `#[doc(...)]` returns `None`.
doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)>129     pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
130         match &self.kind {
131             AttrKind::DocComment(kind, data) => Some((*data, *kind)),
132             AttrKind::Normal(normal) if normal.item.path == sym::doc => {
133                 normal.item.value_str().map(|s| (s, CommentKind::Line))
134             }
135             _ => None,
136         }
137     }
138 
139     /// Returns the documentation if this is a doc comment or a sugared doc comment.
140     /// * `///doc` returns `Some("doc")`.
141     /// * `#[doc = "doc"]` returns `Some("doc")`.
142     /// * `#[doc(...)]` returns `None`.
doc_str(&self) -> Option<Symbol>143     pub fn doc_str(&self) -> Option<Symbol> {
144         match &self.kind {
145             AttrKind::DocComment(.., data) => Some(*data),
146             AttrKind::Normal(normal) if normal.item.path == sym::doc => normal.item.value_str(),
147             _ => None,
148         }
149     }
150 
may_have_doc_links(&self) -> bool151     pub fn may_have_doc_links(&self) -> bool {
152         self.doc_str().is_some_and(|s| comments::may_have_doc_links(s.as_str()))
153     }
154 
is_proc_macro_attr(&self) -> bool155     pub fn is_proc_macro_attr(&self) -> bool {
156         [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
157             .iter()
158             .any(|kind| self.has_name(*kind))
159     }
160 
161     /// Extracts the MetaItem from inside this Attribute.
meta(&self) -> Option<MetaItem>162     pub fn meta(&self) -> Option<MetaItem> {
163         match &self.kind {
164             AttrKind::Normal(normal) => normal.item.meta(self.span),
165             AttrKind::DocComment(..) => None,
166         }
167     }
168 
meta_kind(&self) -> Option<MetaItemKind>169     pub fn meta_kind(&self) -> Option<MetaItemKind> {
170         match &self.kind {
171             AttrKind::Normal(normal) => normal.item.meta_kind(),
172             AttrKind::DocComment(..) => None,
173         }
174     }
175 
tokens(&self) -> TokenStream176     pub fn tokens(&self) -> TokenStream {
177         match &self.kind {
178             AttrKind::Normal(normal) => normal
179                 .tokens
180                 .as_ref()
181                 .unwrap_or_else(|| panic!("attribute is missing tokens: {self:?}"))
182                 .to_attr_token_stream()
183                 .to_tokenstream(),
184             &AttrKind::DocComment(comment_kind, data) => TokenStream::new(vec![TokenTree::Token(
185                 Token::new(token::DocComment(comment_kind, self.style, data), self.span),
186                 Spacing::Alone,
187             )]),
188         }
189     }
190 }
191 
192 impl AttrItem {
span(&self) -> Span193     pub fn span(&self) -> Span {
194         self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span))
195     }
196 
meta_item_list(&self) -> Option<ThinVec<NestedMetaItem>>197     fn meta_item_list(&self) -> Option<ThinVec<NestedMetaItem>> {
198         match &self.args {
199             AttrArgs::Delimited(args) if args.delim == MacDelimiter::Parenthesis => {
200                 MetaItemKind::list_from_tokens(args.tokens.clone())
201             }
202             AttrArgs::Delimited(_) | AttrArgs::Eq(..) | AttrArgs::Empty => None,
203         }
204     }
205 
value_str(&self) -> Option<Symbol>206     fn value_str(&self) -> Option<Symbol> {
207         match &self.args {
208             AttrArgs::Eq(_, args) => args.value_str(),
209             AttrArgs::Delimited(_) | AttrArgs::Empty => None,
210         }
211     }
212 
meta(&self, span: Span) -> Option<MetaItem>213     pub fn meta(&self, span: Span) -> Option<MetaItem> {
214         Some(MetaItem { path: self.path.clone(), kind: self.meta_kind()?, span })
215     }
216 
meta_kind(&self) -> Option<MetaItemKind>217     pub fn meta_kind(&self) -> Option<MetaItemKind> {
218         MetaItemKind::from_attr_args(&self.args)
219     }
220 }
221 
222 impl AttrArgsEq {
value_str(&self) -> Option<Symbol>223     fn value_str(&self) -> Option<Symbol> {
224         match self {
225             AttrArgsEq::Ast(expr) => match expr.kind {
226                 ExprKind::Lit(token_lit) => {
227                     LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str())
228                 }
229                 _ => None,
230             },
231             AttrArgsEq::Hir(lit) => lit.kind.str(),
232         }
233     }
234 }
235 
236 impl MetaItem {
237     /// For a single-segment meta item, returns its name; otherwise, returns `None`.
ident(&self) -> Option<Ident>238     pub fn ident(&self) -> Option<Ident> {
239         if self.path.segments.len() == 1 { Some(self.path.segments[0].ident) } else { None }
240     }
241 
name_or_empty(&self) -> Symbol242     pub fn name_or_empty(&self) -> Symbol {
243         self.ident().unwrap_or_else(Ident::empty).name
244     }
245 
has_name(&self, name: Symbol) -> bool246     pub fn has_name(&self, name: Symbol) -> bool {
247         self.path == name
248     }
249 
is_word(&self) -> bool250     pub fn is_word(&self) -> bool {
251         matches!(self.kind, MetaItemKind::Word)
252     }
253 
meta_item_list(&self) -> Option<&[NestedMetaItem]>254     pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
255         match &self.kind {
256             MetaItemKind::List(l) => Some(&**l),
257             _ => None,
258         }
259     }
260 
261     /// ```text
262     /// Example:
263     ///     #[attribute(name = "value")]
264     ///                 ^^^^^^^^^^^^^^
265     /// ```
name_value_literal(&self) -> Option<&MetaItemLit>266     pub fn name_value_literal(&self) -> Option<&MetaItemLit> {
267         match &self.kind {
268             MetaItemKind::NameValue(v) => Some(v),
269             _ => None,
270         }
271     }
272 
273     /// This is used in case you want the value span instead of the whole attribute. Example:
274     ///
275     /// ```text
276     /// #[doc(alias = "foo")]
277     /// ```
278     ///
279     /// In here, it'll return a span for `"foo"`.
name_value_literal_span(&self) -> Option<Span>280     pub fn name_value_literal_span(&self) -> Option<Span> {
281         Some(self.name_value_literal()?.span)
282     }
283 
value_str(&self) -> Option<Symbol>284     pub fn value_str(&self) -> Option<Symbol> {
285         self.kind.value_str()
286     }
287 
from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem> where I: Iterator<Item = TokenTree>,288     fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
289     where
290         I: Iterator<Item = TokenTree>,
291     {
292         // FIXME: Share code with `parse_path`.
293         let path = match tokens.next().map(TokenTree::uninterpolate) {
294             Some(TokenTree::Token(
295                 Token { kind: kind @ (token::Ident(..) | token::ModSep), span },
296                 _,
297             )) => 'arm: {
298                 let mut segments = if let token::Ident(name, _) = kind {
299                     if let Some(TokenTree::Token(Token { kind: token::ModSep, .. }, _)) =
300                         tokens.peek()
301                     {
302                         tokens.next();
303                         thin_vec![PathSegment::from_ident(Ident::new(name, span))]
304                     } else {
305                         break 'arm Path::from_ident(Ident::new(name, span));
306                     }
307                 } else {
308                     thin_vec![PathSegment::path_root(span)]
309                 };
310                 loop {
311                     if let Some(TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
312                         tokens.next().map(TokenTree::uninterpolate)
313                     {
314                         segments.push(PathSegment::from_ident(Ident::new(name, span)));
315                     } else {
316                         return None;
317                     }
318                     if let Some(TokenTree::Token(Token { kind: token::ModSep, .. }, _)) =
319                         tokens.peek()
320                     {
321                         tokens.next();
322                     } else {
323                         break;
324                     }
325                 }
326                 let span = span.with_hi(segments.last().unwrap().ident.span.hi());
327                 Path { span, segments, tokens: None }
328             }
329             Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. }, _)) => match &*nt {
330                 token::Nonterminal::NtMeta(item) => return item.meta(item.path.span),
331                 token::Nonterminal::NtPath(path) => (**path).clone(),
332                 _ => return None,
333             },
334             _ => return None,
335         };
336         let list_closing_paren_pos = tokens.peek().map(|tt| tt.span().hi());
337         let kind = MetaItemKind::from_tokens(tokens)?;
338         let hi = match &kind {
339             MetaItemKind::NameValue(lit) => lit.span.hi(),
340             MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(path.span.hi()),
341             _ => path.span.hi(),
342         };
343         let span = path.span.with_hi(hi);
344         Some(MetaItem { path, kind, span })
345     }
346 }
347 
348 impl MetaItemKind {
value_str(&self) -> Option<Symbol>349     pub fn value_str(&self) -> Option<Symbol> {
350         match self {
351             MetaItemKind::NameValue(v) => v.kind.str(),
352             _ => None,
353         }
354     }
355 
list_from_tokens(tokens: TokenStream) -> Option<ThinVec<NestedMetaItem>>356     fn list_from_tokens(tokens: TokenStream) -> Option<ThinVec<NestedMetaItem>> {
357         let mut tokens = tokens.into_trees().peekable();
358         let mut result = ThinVec::new();
359         while tokens.peek().is_some() {
360             let item = NestedMetaItem::from_tokens(&mut tokens)?;
361             result.push(item);
362             match tokens.next() {
363                 None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {}
364                 _ => return None,
365             }
366         }
367         Some(result)
368     }
369 
name_value_from_tokens( tokens: &mut impl Iterator<Item = TokenTree>, ) -> Option<MetaItemKind>370     fn name_value_from_tokens(
371         tokens: &mut impl Iterator<Item = TokenTree>,
372     ) -> Option<MetaItemKind> {
373         match tokens.next() {
374             Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => {
375                 MetaItemKind::name_value_from_tokens(&mut inner_tokens.into_trees())
376             }
377             Some(TokenTree::Token(token, _)) => {
378                 MetaItemLit::from_token(&token).map(MetaItemKind::NameValue)
379             }
380             _ => None,
381         }
382     }
383 
from_tokens( tokens: &mut iter::Peekable<impl Iterator<Item = TokenTree>>, ) -> Option<MetaItemKind>384     fn from_tokens(
385         tokens: &mut iter::Peekable<impl Iterator<Item = TokenTree>>,
386     ) -> Option<MetaItemKind> {
387         match tokens.peek() {
388             Some(TokenTree::Delimited(_, Delimiter::Parenthesis, inner_tokens)) => {
389                 let inner_tokens = inner_tokens.clone();
390                 tokens.next();
391                 MetaItemKind::list_from_tokens(inner_tokens).map(MetaItemKind::List)
392             }
393             Some(TokenTree::Delimited(..)) => None,
394             Some(TokenTree::Token(Token { kind: token::Eq, .. }, _)) => {
395                 tokens.next();
396                 MetaItemKind::name_value_from_tokens(tokens)
397             }
398             _ => Some(MetaItemKind::Word),
399         }
400     }
401 
from_attr_args(args: &AttrArgs) -> Option<MetaItemKind>402     fn from_attr_args(args: &AttrArgs) -> Option<MetaItemKind> {
403         match args {
404             AttrArgs::Empty => Some(MetaItemKind::Word),
405             AttrArgs::Delimited(DelimArgs {
406                 dspan: _,
407                 delim: MacDelimiter::Parenthesis,
408                 tokens,
409             }) => MetaItemKind::list_from_tokens(tokens.clone()).map(MetaItemKind::List),
410             AttrArgs::Delimited(..) => None,
411             AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => match expr.kind {
412                 ExprKind::Lit(token_lit) => {
413                     // Turn failures to `None`, we'll get parse errors elsewhere.
414                     MetaItemLit::from_token_lit(token_lit, expr.span)
415                         .ok()
416                         .map(|lit| MetaItemKind::NameValue(lit))
417                 }
418                 _ => None,
419             },
420             AttrArgs::Eq(_, AttrArgsEq::Hir(lit)) => Some(MetaItemKind::NameValue(lit.clone())),
421         }
422     }
423 }
424 
425 impl NestedMetaItem {
span(&self) -> Span426     pub fn span(&self) -> Span {
427         match self {
428             NestedMetaItem::MetaItem(item) => item.span,
429             NestedMetaItem::Lit(lit) => lit.span,
430         }
431     }
432 
433     /// For a single-segment meta item, returns its name; otherwise, returns `None`.
ident(&self) -> Option<Ident>434     pub fn ident(&self) -> Option<Ident> {
435         self.meta_item().and_then(|meta_item| meta_item.ident())
436     }
437 
name_or_empty(&self) -> Symbol438     pub fn name_or_empty(&self) -> Symbol {
439         self.ident().unwrap_or_else(Ident::empty).name
440     }
441 
442     /// Returns `true` if this list item is a MetaItem with a name of `name`.
has_name(&self, name: Symbol) -> bool443     pub fn has_name(&self, name: Symbol) -> bool {
444         self.meta_item().is_some_and(|meta_item| meta_item.has_name(name))
445     }
446 
447     /// Returns `true` if `self` is a `MetaItem` and the meta item is a word.
is_word(&self) -> bool448     pub fn is_word(&self) -> bool {
449         self.meta_item().is_some_and(|meta_item| meta_item.is_word())
450     }
451 
452     /// Gets a list of inner meta items from a list `MetaItem` type.
meta_item_list(&self) -> Option<&[NestedMetaItem]>453     pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
454         self.meta_item().and_then(|meta_item| meta_item.meta_item_list())
455     }
456 
457     /// Returns a name and single literal value tuple of the `MetaItem`.
name_value_literal(&self) -> Option<(Symbol, &MetaItemLit)>458     pub fn name_value_literal(&self) -> Option<(Symbol, &MetaItemLit)> {
459         self.meta_item().and_then(|meta_item| {
460             meta_item.meta_item_list().and_then(|meta_item_list| {
461                 if meta_item_list.len() == 1
462                     && let Some(ident) = meta_item.ident()
463                     && let Some(lit) = meta_item_list[0].lit()
464                 {
465                     return Some((ident.name, lit));
466                 }
467                 None
468             })
469         })
470     }
471 
472     /// See [`MetaItem::name_value_literal_span`].
name_value_literal_span(&self) -> Option<Span>473     pub fn name_value_literal_span(&self) -> Option<Span> {
474         self.meta_item()?.name_value_literal_span()
475     }
476 
477     /// Gets the string value if `self` is a `MetaItem` and the `MetaItem` is a
478     /// `MetaItemKind::NameValue` variant containing a string, otherwise `None`.
value_str(&self) -> Option<Symbol>479     pub fn value_str(&self) -> Option<Symbol> {
480         self.meta_item().and_then(|meta_item| meta_item.value_str())
481     }
482 
483     /// Returns the `MetaItemLit` if `self` is a `NestedMetaItem::Literal`s.
lit(&self) -> Option<&MetaItemLit>484     pub fn lit(&self) -> Option<&MetaItemLit> {
485         match self {
486             NestedMetaItem::Lit(lit) => Some(lit),
487             _ => None,
488         }
489     }
490 
491     /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem`.
meta_item(&self) -> Option<&MetaItem>492     pub fn meta_item(&self) -> Option<&MetaItem> {
493         match self {
494             NestedMetaItem::MetaItem(item) => Some(item),
495             _ => None,
496         }
497     }
498 
499     /// Returns `true` if the variant is `MetaItem`.
is_meta_item(&self) -> bool500     pub fn is_meta_item(&self) -> bool {
501         self.meta_item().is_some()
502     }
503 
from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItem> where I: Iterator<Item = TokenTree>,504     fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItem>
505     where
506         I: Iterator<Item = TokenTree>,
507     {
508         match tokens.peek() {
509             Some(TokenTree::Token(token, _))
510                 if let Some(lit) = MetaItemLit::from_token(token) =>
511             {
512                 tokens.next();
513                 return Some(NestedMetaItem::Lit(lit));
514             }
515             Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => {
516                 let inner_tokens = inner_tokens.clone();
517                 tokens.next();
518                 return NestedMetaItem::from_tokens(&mut inner_tokens.into_trees().peekable());
519             }
520             _ => {}
521         }
522         MetaItem::from_tokens(tokens).map(NestedMetaItem::MetaItem)
523     }
524 }
525 
mk_doc_comment( g: &AttrIdGenerator, comment_kind: CommentKind, style: AttrStyle, data: Symbol, span: Span, ) -> Attribute526 pub fn mk_doc_comment(
527     g: &AttrIdGenerator,
528     comment_kind: CommentKind,
529     style: AttrStyle,
530     data: Symbol,
531     span: Span,
532 ) -> Attribute {
533     Attribute { kind: AttrKind::DocComment(comment_kind, data), id: g.mk_attr_id(), style, span }
534 }
535 
mk_attr( g: &AttrIdGenerator, style: AttrStyle, path: Path, args: AttrArgs, span: Span, ) -> Attribute536 pub fn mk_attr(
537     g: &AttrIdGenerator,
538     style: AttrStyle,
539     path: Path,
540     args: AttrArgs,
541     span: Span,
542 ) -> Attribute {
543     mk_attr_from_item(g, AttrItem { path, args, tokens: None }, None, style, span)
544 }
545 
mk_attr_from_item( g: &AttrIdGenerator, item: AttrItem, tokens: Option<LazyAttrTokenStream>, style: AttrStyle, span: Span, ) -> Attribute546 pub fn mk_attr_from_item(
547     g: &AttrIdGenerator,
548     item: AttrItem,
549     tokens: Option<LazyAttrTokenStream>,
550     style: AttrStyle,
551     span: Span,
552 ) -> Attribute {
553     Attribute {
554         kind: AttrKind::Normal(P(NormalAttr { item, tokens })),
555         id: g.mk_attr_id(),
556         style,
557         span,
558     }
559 }
560 
mk_attr_word(g: &AttrIdGenerator, style: AttrStyle, name: Symbol, span: Span) -> Attribute561 pub fn mk_attr_word(g: &AttrIdGenerator, style: AttrStyle, name: Symbol, span: Span) -> Attribute {
562     let path = Path::from_ident(Ident::new(name, span));
563     let args = AttrArgs::Empty;
564     mk_attr(g, style, path, args, span)
565 }
566 
mk_attr_nested_word( g: &AttrIdGenerator, style: AttrStyle, outer: Symbol, inner: Symbol, span: Span, ) -> Attribute567 pub fn mk_attr_nested_word(
568     g: &AttrIdGenerator,
569     style: AttrStyle,
570     outer: Symbol,
571     inner: Symbol,
572     span: Span,
573 ) -> Attribute {
574     let inner_tokens = TokenStream::new(vec![TokenTree::Token(
575         Token::from_ast_ident(Ident::new(inner, span)),
576         Spacing::Alone,
577     )]);
578     let outer_ident = Ident::new(outer, span);
579     let path = Path::from_ident(outer_ident);
580     let attr_args = AttrArgs::Delimited(DelimArgs {
581         dspan: DelimSpan::from_single(span),
582         delim: MacDelimiter::Parenthesis,
583         tokens: inner_tokens,
584     });
585     mk_attr(g, style, path, attr_args, span)
586 }
587 
mk_attr_name_value_str( g: &AttrIdGenerator, style: AttrStyle, name: Symbol, val: Symbol, span: Span, ) -> Attribute588 pub fn mk_attr_name_value_str(
589     g: &AttrIdGenerator,
590     style: AttrStyle,
591     name: Symbol,
592     val: Symbol,
593     span: Span,
594 ) -> Attribute {
595     let lit = token::Lit::new(token::Str, escape_string_symbol(val), None);
596     let expr = P(Expr {
597         id: DUMMY_NODE_ID,
598         kind: ExprKind::Lit(lit),
599         span,
600         attrs: AttrVec::new(),
601         tokens: None,
602     });
603     let path = Path::from_ident(Ident::new(name, span));
604     let args = AttrArgs::Eq(span, AttrArgsEq::Ast(expr));
605     mk_attr(g, style, path, args, span)
606 }
607 
filter_by_name(attrs: &[Attribute], name: Symbol) -> impl Iterator<Item = &Attribute>608 pub fn filter_by_name(attrs: &[Attribute], name: Symbol) -> impl Iterator<Item = &Attribute> {
609     attrs.iter().filter(move |attr| attr.has_name(name))
610 }
611 
find_by_name(attrs: &[Attribute], name: Symbol) -> Option<&Attribute>612 pub fn find_by_name(attrs: &[Attribute], name: Symbol) -> Option<&Attribute> {
613     filter_by_name(attrs, name).next()
614 }
615 
first_attr_value_str_by_name(attrs: &[Attribute], name: Symbol) -> Option<Symbol>616 pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: Symbol) -> Option<Symbol> {
617     find_by_name(attrs, name).and_then(|attr| attr.value_str())
618 }
619 
contains_name(attrs: &[Attribute], name: Symbol) -> bool620 pub fn contains_name(attrs: &[Attribute], name: Symbol) -> bool {
621     find_by_name(attrs, name).is_some()
622 }
623 
list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool624 pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
625     items.iter().any(|item| item.has_name(name))
626 }
627