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