]> git.proxmox.com Git - rustc.git/blobdiff - compiler/rustc_ast/src/attr/mod.rs
New upstream version 1.69.0+dfsg1
[rustc.git] / compiler / rustc_ast / src / attr / mod.rs
index c6b6207b3186e85442b1c388c221b6974b0306b3..2e83b3e623f075c4f3ace79abb5f2c591e9eb8aa 100644 (file)
@@ -20,7 +20,7 @@ use std::iter;
 use std::ops::BitXor;
 #[cfg(debug_assertions)]
 use std::sync::atomic::{AtomicU32, Ordering};
-use thin_vec::thin_vec;
+use thin_vec::{thin_vec, ThinVec};
 
 pub struct MarkedAttrs(GrowableBitSet<AttrId>);
 
@@ -40,84 +40,65 @@ impl MarkedAttrs {
     }
 }
 
-impl NestedMetaItem {
-    /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem`.
-    pub fn meta_item(&self) -> Option<&MetaItem> {
-        match self {
-            NestedMetaItem::MetaItem(item) => Some(item),
-            _ => None,
-        }
-    }
+pub struct AttrIdGenerator(WorkerLocal<Cell<u32>>);
 
-    /// Returns the `MetaItemLit` if `self` is a `NestedMetaItem::Literal`s.
-    pub fn lit(&self) -> Option<&MetaItemLit> {
-        match self {
-            NestedMetaItem::Lit(lit) => Some(lit),
-            _ => None,
-        }
-    }
+#[cfg(debug_assertions)]
+static MAX_ATTR_ID: AtomicU32 = AtomicU32::new(u32::MAX);
 
-    /// Returns `true` if this list item is a MetaItem with a name of `name`.
-    pub fn has_name(&self, name: Symbol) -> bool {
-        self.meta_item().map_or(false, |meta_item| meta_item.has_name(name))
-    }
+impl AttrIdGenerator {
+    pub fn new() -> Self {
+        // We use `(index as u32).reverse_bits()` to initialize the
+        // starting value of AttrId in each worker thread.
+        // The `index` is the index of the worker thread.
+        // This ensures that the AttrId generated in each thread is unique.
+        AttrIdGenerator(WorkerLocal::new(|index| {
+            let index: u32 = index.try_into().unwrap();
 
-    /// For a single-segment meta item, returns its name; otherwise, returns `None`.
-    pub fn ident(&self) -> Option<Ident> {
-        self.meta_item().and_then(|meta_item| meta_item.ident())
-    }
-    pub fn name_or_empty(&self) -> Symbol {
-        self.ident().unwrap_or_else(Ident::empty).name
-    }
+            #[cfg(debug_assertions)]
+            {
+                let max_id = ((index + 1).next_power_of_two() - 1).bitxor(u32::MAX).reverse_bits();
+                MAX_ATTR_ID.fetch_min(max_id, Ordering::Release);
+            }
 
-    /// Gets the string value if `self` is a `MetaItem` and the `MetaItem` is a
-    /// `MetaItemKind::NameValue` variant containing a string, otherwise `None`.
-    pub fn value_str(&self) -> Option<Symbol> {
-        self.meta_item().and_then(|meta_item| meta_item.value_str())
+            Cell::new(index.reverse_bits())
+        }))
     }
 
-    /// Returns a name and single literal value tuple of the `MetaItem`.
-    pub fn name_value_literal(&self) -> Option<(Symbol, &MetaItemLit)> {
-        self.meta_item().and_then(|meta_item| {
-            meta_item.meta_item_list().and_then(|meta_item_list| {
-                if meta_item_list.len() == 1
-                    && let Some(ident) = meta_item.ident()
-                    && let Some(lit) = meta_item_list[0].lit()
-                {
-                    return Some((ident.name, lit));
-                }
-                None
-            })
-        })
-    }
+    pub fn mk_attr_id(&self) -> AttrId {
+        let id = self.0.get();
 
-    /// Gets a list of inner meta items from a list `MetaItem` type.
-    pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
-        self.meta_item().and_then(|meta_item| meta_item.meta_item_list())
-    }
+        // Ensure the assigned attr_id does not overlap the bits
+        // representing the number of threads.
+        #[cfg(debug_assertions)]
+        assert!(id <= MAX_ATTR_ID.load(Ordering::Acquire));
 
-    /// Returns `true` if the variant is `MetaItem`.
-    pub fn is_meta_item(&self) -> bool {
-        self.meta_item().is_some()
+        self.0.set(id + 1);
+        AttrId::from_u32(id)
     }
+}
 
-    /// Returns `true` if `self` is a `MetaItem` and the meta item is a word.
-    pub fn is_word(&self) -> bool {
-        self.meta_item().map_or(false, |meta_item| meta_item.is_word())
+impl Attribute {
+    pub fn get_normal_item(&self) -> &AttrItem {
+        match &self.kind {
+            AttrKind::Normal(normal) => &normal.item,
+            AttrKind::DocComment(..) => panic!("unexpected doc comment"),
+        }
     }
 
-    /// See [`MetaItem::name_value_literal_span`].
-    pub fn name_value_literal_span(&self) -> Option<Span> {
-        self.meta_item()?.name_value_literal_span()
+    pub fn unwrap_normal_item(self) -> AttrItem {
+        match self.kind {
+            AttrKind::Normal(normal) => normal.into_inner().item,
+            AttrKind::DocComment(..) => panic!("unexpected doc comment"),
+        }
     }
-}
 
-impl Attribute {
-    #[inline]
-    pub fn has_name(&self, name: Symbol) -> bool {
-        match &self.kind {
-            AttrKind::Normal(normal) => normal.item.path == name,
-            AttrKind::DocComment(..) => false,
+    /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).
+    /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not
+    /// a doc comment) will return `false`.
+    pub fn is_doc_comment(&self) -> bool {
+        match self.kind {
+            AttrKind::Normal(..) => false,
+            AttrKind::DocComment(..) => true,
         }
     }
 
@@ -138,20 +119,11 @@ impl Attribute {
         self.ident().unwrap_or_else(Ident::empty).name
     }
 
-    pub fn value_str(&self) -> Option<Symbol> {
-        match &self.kind {
-            AttrKind::Normal(normal) => normal.item.meta_kind().and_then(|kind| kind.value_str()),
-            AttrKind::DocComment(..) => None,
-        }
-    }
-
-    pub fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> {
+    #[inline]
+    pub fn has_name(&self, name: Symbol) -> bool {
         match &self.kind {
-            AttrKind::Normal(normal) => match normal.item.meta_kind() {
-                Some(MetaItemKind::List(list)) => Some(list),
-                _ => None,
-            },
-            AttrKind::DocComment(..) => None,
+            AttrKind::Normal(normal) => normal.item.path == name,
+            AttrKind::DocComment(..) => false,
         }
     }
 
@@ -162,82 +134,18 @@ impl Attribute {
             false
         }
     }
-}
-
-impl MetaItem {
-    /// For a single-segment meta item, returns its name; otherwise, returns `None`.
-    pub fn ident(&self) -> Option<Ident> {
-        if self.path.segments.len() == 1 { Some(self.path.segments[0].ident) } else { None }
-    }
-    pub fn name_or_empty(&self) -> Symbol {
-        self.ident().unwrap_or_else(Ident::empty).name
-    }
 
-    /// ```text
-    /// Example:
-    ///     #[attribute(name = "value")]
-    ///                 ^^^^^^^^^^^^^^
-    /// ```
-    pub fn name_value_literal(&self) -> Option<&MetaItemLit> {
+    pub fn meta_item_list(&self) -> Option<ThinVec<NestedMetaItem>> {
         match &self.kind {
-            MetaItemKind::NameValue(v) => Some(v),
-            _ => None,
+            AttrKind::Normal(normal) => normal.item.meta_item_list(),
+            AttrKind::DocComment(..) => None,
         }
     }
 
     pub fn value_str(&self) -> Option<Symbol> {
-        self.kind.value_str()
-    }
-
-    pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
         match &self.kind {
-            MetaItemKind::List(l) => Some(&**l),
-            _ => None,
-        }
-    }
-
-    pub fn is_word(&self) -> bool {
-        matches!(self.kind, MetaItemKind::Word)
-    }
-
-    pub fn has_name(&self, name: Symbol) -> bool {
-        self.path == name
-    }
-
-    /// This is used in case you want the value span instead of the whole attribute. Example:
-    ///
-    /// ```text
-    /// #[doc(alias = "foo")]
-    /// ```
-    ///
-    /// In here, it'll return a span for `"foo"`.
-    pub fn name_value_literal_span(&self) -> Option<Span> {
-        Some(self.name_value_literal()?.span)
-    }
-}
-
-impl AttrItem {
-    pub fn span(&self) -> Span {
-        self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span))
-    }
-
-    pub fn meta(&self, span: Span) -> Option<MetaItem> {
-        Some(MetaItem { path: self.path.clone(), kind: self.meta_kind()?, span })
-    }
-
-    pub fn meta_kind(&self) -> Option<MetaItemKind> {
-        MetaItemKind::from_attr_args(&self.args)
-    }
-}
-
-impl Attribute {
-    /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).
-    /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not
-    /// a doc comment) will return `false`.
-    pub fn is_doc_comment(&self) -> bool {
-        match self.kind {
-            AttrKind::Normal(..) => false,
-            AttrKind::DocComment(..) => true,
+            AttrKind::Normal(normal) => normal.item.value_str(),
+            AttrKind::DocComment(..) => None,
         }
     }
 
@@ -247,13 +155,11 @@ impl Attribute {
     /// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
     /// * `#[doc(...)]` returns `None`.
     pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
-        match self.kind {
-            AttrKind::DocComment(kind, data) => Some((data, kind)),
-            AttrKind::Normal(ref normal) if normal.item.path == sym::doc => normal
-                .item
-                .meta_kind()
-                .and_then(|kind| kind.value_str())
-                .map(|data| (data, CommentKind::Line)),
+        match &self.kind {
+            AttrKind::DocComment(kind, data) => Some((*data, *kind)),
+            AttrKind::Normal(normal) if normal.item.path == sym::doc => {
+                normal.item.value_str().map(|s| (s, CommentKind::Line))
+            }
             _ => None,
         }
     }
@@ -265,9 +171,7 @@ impl Attribute {
     pub fn doc_str(&self) -> Option<Symbol> {
         match &self.kind {
             AttrKind::DocComment(.., data) => Some(*data),
-            AttrKind::Normal(normal) if normal.item.path == sym::doc => {
-                normal.item.meta_kind().and_then(|kind| kind.value_str())
-            }
+            AttrKind::Normal(normal) if normal.item.path == sym::doc => normal.item.value_str(),
             _ => None,
         }
     }
@@ -276,20 +180,6 @@ impl Attribute {
         self.doc_str().map_or(false, |s| comments::may_have_doc_links(s.as_str()))
     }
 
-    pub fn get_normal_item(&self) -> &AttrItem {
-        match &self.kind {
-            AttrKind::Normal(normal) => &normal.item,
-            AttrKind::DocComment(..) => panic!("unexpected doc comment"),
-        }
-    }
-
-    pub fn unwrap_normal_item(self) -> AttrItem {
-        match self.kind {
-            AttrKind::Normal(normal) => normal.into_inner().item,
-            AttrKind::DocComment(..) => panic!("unexpected doc comment"),
-        }
-    }
-
     /// Extracts the MetaItem from inside this Attribute.
     pub fn meta(&self) -> Option<MetaItem> {
         match &self.kind {
@@ -321,130 +211,102 @@ impl Attribute {
     }
 }
 
-pub struct AttrIdGenerator(WorkerLocal<Cell<u32>>);
-
-#[cfg(debug_assertions)]
-static MAX_ATTR_ID: AtomicU32 = AtomicU32::new(u32::MAX);
-
-impl AttrIdGenerator {
-    pub fn new() -> Self {
-        // We use `(index as u32).reverse_bits()` to initialize the
-        // starting value of AttrId in each worker thread.
-        // The `index` is the index of the worker thread.
-        // This ensures that the AttrId generated in each thread is unique.
-        AttrIdGenerator(WorkerLocal::new(|index| {
-            let index: u32 = index.try_into().unwrap();
+impl AttrItem {
+    pub fn span(&self) -> Span {
+        self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span))
+    }
 
-            #[cfg(debug_assertions)]
-            {
-                let max_id = ((index + 1).next_power_of_two() - 1).bitxor(u32::MAX).reverse_bits();
-                MAX_ATTR_ID.fetch_min(max_id, Ordering::Release);
+    fn meta_item_list(&self) -> Option<ThinVec<NestedMetaItem>> {
+        match &self.args {
+            AttrArgs::Delimited(args) if args.delim == MacDelimiter::Parenthesis => {
+                MetaItemKind::list_from_tokens(args.tokens.clone())
             }
-
-            Cell::new(index.reverse_bits())
-        }))
+            AttrArgs::Delimited(_) | AttrArgs::Eq(..) | AttrArgs::Empty => None,
+        }
     }
 
-    pub fn mk_attr_id(&self) -> AttrId {
-        let id = self.0.get();
-
-        // Ensure the assigned attr_id does not overlap the bits
-        // representing the number of threads.
-        #[cfg(debug_assertions)]
-        assert!(id <= MAX_ATTR_ID.load(Ordering::Acquire));
-
-        self.0.set(id + 1);
-        AttrId::from_u32(id)
+    fn value_str(&self) -> Option<Symbol> {
+        match &self.args {
+            AttrArgs::Eq(_, args) => args.value_str(),
+            AttrArgs::Delimited(_) | AttrArgs::Empty => None,
+        }
     }
-}
 
-pub fn mk_attr(
-    g: &AttrIdGenerator,
-    style: AttrStyle,
-    path: Path,
-    args: AttrArgs,
-    span: Span,
-) -> Attribute {
-    mk_attr_from_item(g, AttrItem { path, args, tokens: None }, None, style, span)
-}
-
-pub fn mk_attr_from_item(
-    g: &AttrIdGenerator,
-    item: AttrItem,
-    tokens: Option<LazyAttrTokenStream>,
-    style: AttrStyle,
-    span: Span,
-) -> Attribute {
-    Attribute {
-        kind: AttrKind::Normal(P(NormalAttr { item, tokens })),
-        id: g.mk_attr_id(),
-        style,
-        span,
+    pub fn meta(&self, span: Span) -> Option<MetaItem> {
+        Some(MetaItem { path: self.path.clone(), kind: self.meta_kind()?, span })
     }
-}
-
-pub fn mk_attr_word(g: &AttrIdGenerator, style: AttrStyle, name: Symbol, span: Span) -> Attribute {
-    let path = Path::from_ident(Ident::new(name, span));
-    let args = AttrArgs::Empty;
-    mk_attr(g, style, path, args, span)
-}
-
-pub fn mk_attr_name_value_str(
-    g: &AttrIdGenerator,
-    style: AttrStyle,
-    name: Symbol,
-    val: Symbol,
-    span: Span,
-) -> Attribute {
-    let lit = token::Lit::new(token::Str, escape_string_symbol(val), None);
-    let expr = P(Expr {
-        id: DUMMY_NODE_ID,
-        kind: ExprKind::Lit(lit),
-        span,
-        attrs: AttrVec::new(),
-        tokens: None,
-    });
-    let path = Path::from_ident(Ident::new(name, span));
-    let args = AttrArgs::Eq(span, AttrArgsEq::Ast(expr));
-    mk_attr(g, style, path, args, span)
-}
 
-pub fn mk_attr_nested_word(
-    g: &AttrIdGenerator,
-    style: AttrStyle,
-    outer: Symbol,
-    inner: Symbol,
-    span: Span,
-) -> Attribute {
-    let inner_tokens = TokenStream::new(vec![TokenTree::Token(
-        Token::from_ast_ident(Ident::new(inner, span)),
-        Spacing::Alone,
-    )]);
-    let outer_ident = Ident::new(outer, span);
-    let path = Path::from_ident(outer_ident);
-    let attr_args = AttrArgs::Delimited(DelimArgs {
-        dspan: DelimSpan::from_single(span),
-        delim: MacDelimiter::Parenthesis,
-        tokens: inner_tokens,
-    });
-    mk_attr(g, style, path, attr_args, span)
-}
-
-pub fn mk_doc_comment(
-    g: &AttrIdGenerator,
-    comment_kind: CommentKind,
-    style: AttrStyle,
-    data: Symbol,
-    span: Span,
-) -> Attribute {
-    Attribute { kind: AttrKind::DocComment(comment_kind, data), id: g.mk_attr_id(), style, span }
+    pub fn meta_kind(&self) -> Option<MetaItemKind> {
+        MetaItemKind::from_attr_args(&self.args)
+    }
 }
 
-pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
-    items.iter().any(|item| item.has_name(name))
+impl AttrArgsEq {
+    fn value_str(&self) -> Option<Symbol> {
+        match self {
+            AttrArgsEq::Ast(expr) => match expr.kind {
+                ExprKind::Lit(token_lit) => {
+                    LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str())
+                }
+                _ => None,
+            },
+            AttrArgsEq::Hir(lit) => lit.kind.str(),
+        }
+    }
 }
 
 impl MetaItem {
+    /// For a single-segment meta item, returns its name; otherwise, returns `None`.
+    pub fn ident(&self) -> Option<Ident> {
+        if self.path.segments.len() == 1 { Some(self.path.segments[0].ident) } else { None }
+    }
+
+    pub fn name_or_empty(&self) -> Symbol {
+        self.ident().unwrap_or_else(Ident::empty).name
+    }
+
+    pub fn has_name(&self, name: Symbol) -> bool {
+        self.path == name
+    }
+
+    pub fn is_word(&self) -> bool {
+        matches!(self.kind, MetaItemKind::Word)
+    }
+
+    pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
+        match &self.kind {
+            MetaItemKind::List(l) => Some(&**l),
+            _ => None,
+        }
+    }
+
+    /// ```text
+    /// Example:
+    ///     #[attribute(name = "value")]
+    ///                 ^^^^^^^^^^^^^^
+    /// ```
+    pub fn name_value_literal(&self) -> Option<&MetaItemLit> {
+        match &self.kind {
+            MetaItemKind::NameValue(v) => Some(v),
+            _ => None,
+        }
+    }
+
+    /// This is used in case you want the value span instead of the whole attribute. Example:
+    ///
+    /// ```text
+    /// #[doc(alias = "foo")]
+    /// ```
+    ///
+    /// In here, it'll return a span for `"foo"`.
+    pub fn name_value_literal_span(&self) -> Option<Span> {
+        Some(self.name_value_literal()?.span)
+    }
+
+    pub fn value_str(&self) -> Option<Symbol> {
+        self.kind.value_str()
+    }
+
     fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
     where
         I: Iterator<Item = TokenTree>,
@@ -508,17 +370,14 @@ impl MetaItem {
 impl MetaItemKind {
     pub fn value_str(&self) -> Option<Symbol> {
         match self {
-            MetaItemKind::NameValue(v) => match v.kind {
-                LitKind::Str(s, _) => Some(s),
-                _ => None,
-            },
+            MetaItemKind::NameValue(v) => v.kind.str(),
             _ => None,
         }
     }
 
-    fn list_from_tokens(tokens: TokenStream) -> Option<MetaItemKind> {
+    fn list_from_tokens(tokens: TokenStream) -> Option<ThinVec<NestedMetaItem>> {
         let mut tokens = tokens.into_trees().peekable();
-        let mut result = Vec::new();
+        let mut result = ThinVec::new();
         while tokens.peek().is_some() {
             let item = NestedMetaItem::from_tokens(&mut tokens)?;
             result.push(item);
@@ -527,7 +386,7 @@ impl MetaItemKind {
                 _ => return None,
             }
         }
-        Some(MetaItemKind::List(result))
+        Some(result)
     }
 
     fn name_value_from_tokens(
@@ -544,6 +403,24 @@ impl MetaItemKind {
         }
     }
 
+    fn from_tokens(
+        tokens: &mut iter::Peekable<impl Iterator<Item = TokenTree>>,
+    ) -> Option<MetaItemKind> {
+        match tokens.peek() {
+            Some(TokenTree::Delimited(_, Delimiter::Parenthesis, inner_tokens)) => {
+                let inner_tokens = inner_tokens.clone();
+                tokens.next();
+                MetaItemKind::list_from_tokens(inner_tokens).map(MetaItemKind::List)
+            }
+            Some(TokenTree::Delimited(..)) => None,
+            Some(TokenTree::Token(Token { kind: token::Eq, .. }, _)) => {
+                tokens.next();
+                MetaItemKind::name_value_from_tokens(tokens)
+            }
+            _ => Some(MetaItemKind::Word),
+        }
+    }
+
     fn from_attr_args(args: &AttrArgs) -> Option<MetaItemKind> {
         match args {
             AttrArgs::Empty => Some(MetaItemKind::Word),
@@ -551,7 +428,7 @@ impl MetaItemKind {
                 dspan: _,
                 delim: MacDelimiter::Parenthesis,
                 tokens,
-            }) => MetaItemKind::list_from_tokens(tokens.clone()),
+            }) => MetaItemKind::list_from_tokens(tokens.clone()).map(MetaItemKind::List),
             AttrArgs::Delimited(..) => None,
             AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => match expr.kind {
                 ExprKind::Lit(token_lit) => {
@@ -565,24 +442,6 @@ impl MetaItemKind {
             AttrArgs::Eq(_, AttrArgsEq::Hir(lit)) => Some(MetaItemKind::NameValue(lit.clone())),
         }
     }
-
-    fn from_tokens(
-        tokens: &mut iter::Peekable<impl Iterator<Item = TokenTree>>,
-    ) -> Option<MetaItemKind> {
-        match tokens.peek() {
-            Some(TokenTree::Delimited(_, Delimiter::Parenthesis, inner_tokens)) => {
-                let inner_tokens = inner_tokens.clone();
-                tokens.next();
-                MetaItemKind::list_from_tokens(inner_tokens)
-            }
-            Some(TokenTree::Delimited(..)) => None,
-            Some(TokenTree::Token(Token { kind: token::Eq, .. }, _)) => {
-                tokens.next();
-                MetaItemKind::name_value_from_tokens(tokens)
-            }
-            _ => Some(MetaItemKind::Word),
-        }
-    }
 }
 
 impl NestedMetaItem {
@@ -593,6 +452,77 @@ impl NestedMetaItem {
         }
     }
 
+    /// For a single-segment meta item, returns its name; otherwise, returns `None`.
+    pub fn ident(&self) -> Option<Ident> {
+        self.meta_item().and_then(|meta_item| meta_item.ident())
+    }
+
+    pub fn name_or_empty(&self) -> Symbol {
+        self.ident().unwrap_or_else(Ident::empty).name
+    }
+
+    /// Returns `true` if this list item is a MetaItem with a name of `name`.
+    pub fn has_name(&self, name: Symbol) -> bool {
+        self.meta_item().map_or(false, |meta_item| meta_item.has_name(name))
+    }
+
+    /// Returns `true` if `self` is a `MetaItem` and the meta item is a word.
+    pub fn is_word(&self) -> bool {
+        self.meta_item().map_or(false, |meta_item| meta_item.is_word())
+    }
+
+    /// Gets a list of inner meta items from a list `MetaItem` type.
+    pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
+        self.meta_item().and_then(|meta_item| meta_item.meta_item_list())
+    }
+
+    /// Returns a name and single literal value tuple of the `MetaItem`.
+    pub fn name_value_literal(&self) -> Option<(Symbol, &MetaItemLit)> {
+        self.meta_item().and_then(|meta_item| {
+            meta_item.meta_item_list().and_then(|meta_item_list| {
+                if meta_item_list.len() == 1
+                    && let Some(ident) = meta_item.ident()
+                    && let Some(lit) = meta_item_list[0].lit()
+                {
+                    return Some((ident.name, lit));
+                }
+                None
+            })
+        })
+    }
+
+    /// See [`MetaItem::name_value_literal_span`].
+    pub fn name_value_literal_span(&self) -> Option<Span> {
+        self.meta_item()?.name_value_literal_span()
+    }
+
+    /// Gets the string value if `self` is a `MetaItem` and the `MetaItem` is a
+    /// `MetaItemKind::NameValue` variant containing a string, otherwise `None`.
+    pub fn value_str(&self) -> Option<Symbol> {
+        self.meta_item().and_then(|meta_item| meta_item.value_str())
+    }
+
+    /// Returns the `MetaItemLit` if `self` is a `NestedMetaItem::Literal`s.
+    pub fn lit(&self) -> Option<&MetaItemLit> {
+        match self {
+            NestedMetaItem::Lit(lit) => Some(lit),
+            _ => None,
+        }
+    }
+
+    /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem`.
+    pub fn meta_item(&self) -> Option<&MetaItem> {
+        match self {
+            NestedMetaItem::MetaItem(item) => Some(item),
+            _ => None,
+        }
+    }
+
+    /// Returns `true` if the variant is `MetaItem`.
+    pub fn is_meta_item(&self) -> bool {
+        self.meta_item().is_some()
+    }
+
     fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItem>
     where
         I: Iterator<Item = TokenTree>,
@@ -614,3 +544,89 @@ impl NestedMetaItem {
         MetaItem::from_tokens(tokens).map(NestedMetaItem::MetaItem)
     }
 }
+
+pub fn mk_doc_comment(
+    g: &AttrIdGenerator,
+    comment_kind: CommentKind,
+    style: AttrStyle,
+    data: Symbol,
+    span: Span,
+) -> Attribute {
+    Attribute { kind: AttrKind::DocComment(comment_kind, data), id: g.mk_attr_id(), style, span }
+}
+
+pub fn mk_attr(
+    g: &AttrIdGenerator,
+    style: AttrStyle,
+    path: Path,
+    args: AttrArgs,
+    span: Span,
+) -> Attribute {
+    mk_attr_from_item(g, AttrItem { path, args, tokens: None }, None, style, span)
+}
+
+pub fn mk_attr_from_item(
+    g: &AttrIdGenerator,
+    item: AttrItem,
+    tokens: Option<LazyAttrTokenStream>,
+    style: AttrStyle,
+    span: Span,
+) -> Attribute {
+    Attribute {
+        kind: AttrKind::Normal(P(NormalAttr { item, tokens })),
+        id: g.mk_attr_id(),
+        style,
+        span,
+    }
+}
+
+pub fn mk_attr_word(g: &AttrIdGenerator, style: AttrStyle, name: Symbol, span: Span) -> Attribute {
+    let path = Path::from_ident(Ident::new(name, span));
+    let args = AttrArgs::Empty;
+    mk_attr(g, style, path, args, span)
+}
+
+pub fn mk_attr_nested_word(
+    g: &AttrIdGenerator,
+    style: AttrStyle,
+    outer: Symbol,
+    inner: Symbol,
+    span: Span,
+) -> Attribute {
+    let inner_tokens = TokenStream::new(vec![TokenTree::Token(
+        Token::from_ast_ident(Ident::new(inner, span)),
+        Spacing::Alone,
+    )]);
+    let outer_ident = Ident::new(outer, span);
+    let path = Path::from_ident(outer_ident);
+    let attr_args = AttrArgs::Delimited(DelimArgs {
+        dspan: DelimSpan::from_single(span),
+        delim: MacDelimiter::Parenthesis,
+        tokens: inner_tokens,
+    });
+    mk_attr(g, style, path, attr_args, span)
+}
+
+pub fn mk_attr_name_value_str(
+    g: &AttrIdGenerator,
+    style: AttrStyle,
+    name: Symbol,
+    val: Symbol,
+    span: Span,
+) -> Attribute {
+    let lit = token::Lit::new(token::Str, escape_string_symbol(val), None);
+    let expr = P(Expr {
+        id: DUMMY_NODE_ID,
+        kind: ExprKind::Lit(lit),
+        span,
+        attrs: AttrVec::new(),
+        tokens: None,
+    });
+    let path = Path::from_ident(Ident::new(name, span));
+    let args = AttrArgs::Eq(span, AttrArgsEq::Ast(expr));
+    mk_attr(g, style, path, args, span)
+}
+
+pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
+    items.iter().any(|item| item.has_name(name))
+}