1//! Functions dealing with attributes and meta items.23pub mod data_structures;4pub mod version;56use std::fmt::Debug;7use std::sync::atomic::{AtomicU32, Ordering};89use rustc_index::bit_set::GrowableBitSet;10use rustc_span::{Ident, Span, Symbol, sym};11use smallvec::{SmallVec, smallvec};12use thin_vec::{ThinVec, thin_vec};1314use crate::AttrItemKind;15use crate::ast::{16 AttrArgs, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute, DUMMY_NODE_ID, DelimArgs,17 Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NormalAttr, Path,18 PathSegment, Safety,19};20use crate::token::{21 self, CommentKind, Delimiter, DocFragmentKind, InvisibleOrigin, MetaVarKind, Token,22};23use crate::tokenstream::{24 DelimSpan, LazyAttrTokenStream, Spacing, TokenStream, TokenStreamIter, TokenTree,25};26use crate::util::comments;27use crate::util::literal::escape_string_symbol;2829pub struct MarkedAttrs(GrowableBitSet<AttrId>);3031impl MarkedAttrs {32 pub fn new() -> Self {33 // We have no idea how many attributes there will be, so just34 // initiate the vectors with 0 bits. We'll grow them as necessary.35 MarkedAttrs(GrowableBitSet::new_empty())36 }3738 pub fn mark(&mut self, attr: &Attribute) {39 self.0.insert(attr.id);40 }4142 pub fn is_marked(&self, attr: &Attribute) -> bool {43 self.0.contains(attr.id)44 }45}4647pub struct AttrIdGenerator(AtomicU32);4849impl AttrIdGenerator {50 pub fn new() -> Self {51 AttrIdGenerator(AtomicU32::new(0))52 }5354 pub fn mk_attr_id(&self) -> AttrId {55 let id = self.0.fetch_add(1, Ordering::Relaxed);56 assert!(id != u32::MAX);57 AttrId::from_u32(id)58 }59}6061impl Attribute {62 pub fn get_normal_item(&self) -> &AttrItem {63 match &self.kind {64 AttrKind::Normal(normal) => &normal.item,65 AttrKind::DocComment(..) => panic!("unexpected doc comment"),66 }67 }6869 /// Replaces the arguments of this attribute with new arguments `AttrItemKind`.70 /// This is useful for making this attribute into a trace attribute, and should otherwise be avoided.71 pub fn replace_args(&mut self, new_args: AttrItemKind) {72 match &mut self.kind {73 AttrKind::Normal(normal) => normal.item.args = new_args,74 AttrKind::DocComment(..) => panic!("unexpected doc comment"),75 }76 }7778 pub fn unwrap_normal_item(self) -> AttrItem {79 match self.kind {80 AttrKind::Normal(normal) => normal.item,81 AttrKind::DocComment(..) => panic!("unexpected doc comment"),82 }83 }84}8586impl AttributeExt for Attribute {87 fn id(&self) -> AttrId {88 self.id89 }9091 fn value_span(&self) -> Option<Span> {92 match &self.kind {93 AttrKind::Normal(normal) => match &normal.item.args.unparsed_ref()? {94 AttrArgs::Eq { expr, .. } => Some(expr.span),95 _ => None,96 },97 AttrKind::DocComment(..) => None,98 }99 }100101 /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).102 /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not103 /// a doc comment) will return `false`.104 fn is_doc_comment(&self) -> Option<Span> {105 match self.kind {106 AttrKind::Normal(..) => None,107 AttrKind::DocComment(..) => Some(self.span),108 }109 }110111 /// For a single-segment attribute, returns its name; otherwise, returns `None`.112 fn name(&self) -> Option<Symbol> {113 match &self.kind {114 AttrKind::Normal(normal) => {115 if let [ident] = &*normal.item.path.segments {116 Some(ident.ident.name)117 } else {118 None119 }120 }121 AttrKind::DocComment(..) => None,122 }123 }124125 fn symbol_path(&self) -> Option<SmallVec<[Symbol; 1]>> {126 match &self.kind {127 AttrKind::Normal(p) => {128 Some(p.item.path.segments.iter().map(|i| i.ident.name).collect())129 }130 AttrKind::DocComment(_, _) => None,131 }132 }133134 fn path_span(&self) -> Option<Span> {135 match &self.kind {136 AttrKind::Normal(attr) => Some(attr.item.path.span),137 AttrKind::DocComment(_, _) => None,138 }139 }140141 fn path_matches(&self, name: &[Symbol]) -> bool {142 match &self.kind {143 AttrKind::Normal(normal) => {144 normal.item.path.segments.len() == name.len()145 && normal146 .item147 .path148 .segments149 .iter()150 .zip(name)151 .all(|(s, n)| s.args.is_none() && s.ident.name == *n)152 }153 AttrKind::DocComment(..) => false,154 }155 }156157 fn span(&self) -> Span {158 self.span159 }160161 fn is_word(&self) -> bool {162 if let AttrKind::Normal(normal) = &self.kind {163 matches!(normal.item.args, AttrItemKind::Unparsed(AttrArgs::Empty))164 } else {165 false166 }167 }168169 /// Returns a list of meta items if the attribute is delimited with parenthesis:170 ///171 /// ```text172 /// #[attr(a, b = "c")] // Returns `Some()`.173 /// #[attr = ""] // Returns `None`.174 /// #[attr] // Returns `None`.175 /// ```176 fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {177 match &self.kind {178 AttrKind::Normal(normal) => normal.item.meta_item_list(),179 AttrKind::DocComment(..) => None,180 }181 }182183 /// Returns the string value in:184 ///185 /// ```text186 /// #[attribute = "value"]187 /// ^^^^^^^188 /// ```189 ///190 /// It returns `None` in any other cases, including doc comments if they191 /// are not under the form `#[doc = "..."]`.192 ///193 /// It also returns `None` for:194 ///195 /// ```text196 /// #[attr("value")]197 /// ```198 fn value_str(&self) -> Option<Symbol> {199 match &self.kind {200 AttrKind::Normal(normal) => normal.item.value_str(),201 AttrKind::DocComment(..) => None,202 }203 }204205 /// Returns the documentation and its kind if this is a doc comment or a sugared doc comment.206 /// * `///doc` returns `Some(("doc", DocFragmentKind::Sugared(CommentKind::Line)))`.207 /// * `/** doc */` returns `Some(("doc", DocFragmentKind::Sugared(CommentKind::Block)))`.208 /// * `#[doc = "doc"]` returns `Some(("doc", DocFragmentKind::Raw))`.209 /// * `#[doc(...)]` returns `None`.210 fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)> {211 match &self.kind {212 AttrKind::DocComment(kind, data) => Some((*data, DocFragmentKind::Sugared(*kind))),213 AttrKind::Normal(normal) if normal.item.path == sym::doc => {214 if let Some(value) = normal.item.value_str()215 && let Some(value_span) = normal.item.value_span()216 {217 Some((value, DocFragmentKind::Raw(value_span)))218 } else {219 None220 }221 }222 _ => None,223 }224 }225226 /// Returns the documentation if this is a doc comment or a sugared doc comment.227 /// * `///doc` returns `Some("doc")`.228 /// * `#[doc = "doc"]` returns `Some("doc")`.229 /// * `#[doc(...)]` returns `None`.230 fn doc_str(&self) -> Option<Symbol> {231 match &self.kind {232 AttrKind::DocComment(.., data) => Some(*data),233 AttrKind::Normal(normal) if normal.item.path == sym::doc => normal.item.value_str(),234 _ => None,235 }236 }237238 fn doc_resolution_scope(&self) -> Option<AttrStyle> {239 match &self.kind {240 AttrKind::DocComment(..) => Some(self.style),241 AttrKind::Normal(normal)242 if normal.item.path == sym::doc && normal.item.value_str().is_some() =>243 {244 Some(self.style)245 }246 _ => None,247 }248 }249250 fn is_automatically_derived_attr(&self) -> bool {251 self.has_name(sym::automatically_derived)252 }253254 fn is_doc_hidden(&self) -> bool {255 self.has_name(sym::doc)256 && self.meta_item_list().is_some_and(|l| list_contains_name(&l, sym::hidden))257 }258259 fn is_doc_keyword_or_attribute(&self) -> bool {260 if self.has_name(sym::doc)261 && let Some(items) = self.meta_item_list()262 {263 for item in items {264 if item.has_name(sym::keyword) || item.has_name(sym::attribute) {265 return true;266 }267 }268 }269 false270 }271272 fn is_rustc_doc_primitive(&self) -> bool {273 self.has_name(sym::rustc_doc_primitive)274 }275}276277impl Attribute {278 pub fn style(&self) -> AttrStyle {279 self.style280 }281282 pub fn may_have_doc_links(&self) -> bool {283 self.doc_str().is_some_and(|s| comments::may_have_doc_links(s.as_str()))284 || self.deprecation_note().is_some_and(|s| comments::may_have_doc_links(s.as_str()))285 }286287 /// Extracts the MetaItem from inside this Attribute.288 pub fn meta(&self) -> Option<MetaItem> {289 match &self.kind {290 AttrKind::Normal(normal) => normal.item.meta(self.span),291 AttrKind::DocComment(..) => None,292 }293 }294295 pub fn meta_kind(&self) -> Option<MetaItemKind> {296 match &self.kind {297 AttrKind::Normal(normal) => normal.item.meta_kind(),298 AttrKind::DocComment(..) => None,299 }300 }301302 pub fn token_trees(&self) -> Vec<TokenTree> {303 match self.kind {304 AttrKind::Normal(ref normal) => normal305 .tokens306 .as_ref()307 .unwrap_or_else(|| panic!("attribute is missing tokens: {self:?}"))308 .to_attr_token_stream()309 .to_token_trees(),310 AttrKind::DocComment(comment_kind, data) => vec![TokenTree::token_alone(311 token::DocComment(comment_kind, self.style, data),312 self.span,313 )],314 }315 }316317 pub fn deprecation_note(&self) -> Option<Ident> {318 match &self.kind {319 AttrKind::Normal(normal) if normal.item.path == sym::deprecated => {320 let meta = &normal.item;321322 // #[deprecated = "..."]323 if let Some(s) = meta.value_str() {324 return Some(Ident { name: s, span: meta.span() });325 }326327 // #[deprecated(note = "...")]328 if let Some(list) = meta.meta_item_list() {329 for nested in list {330 if let Some(mi) = nested.meta_item()331 && mi.path == sym::note332 && let Some(s) = mi.value_str()333 {334 return Some(Ident { name: s, span: mi.span });335 }336 }337 }338339 None340 }341 _ => None,342 }343 }344}345346impl AttrItem {347 pub fn span(&self) -> Span {348 self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span))349 }350351 pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {352 match &self.args.unparsed_ref()? {353 AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {354 MetaItemKind::list_from_tokens(args.tokens.clone())355 }356 AttrArgs::Delimited(_) | AttrArgs::Eq { .. } | AttrArgs::Empty => None,357 }358 }359360 /// Returns the string value in:361 ///362 /// ```text363 /// #[attribute = "value"]364 /// ^^^^^^^365 /// ```366 ///367 /// It returns `None` in any other cases like:368 ///369 /// ```text370 /// #[attr("value")]371 /// ```372 fn value_str(&self) -> Option<Symbol> {373 match &self.args.unparsed_ref()? {374 AttrArgs::Eq { expr, .. } => match expr.kind {375 ExprKind::Lit(token_lit) => {376 LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str())377 }378 _ => None,379 },380 AttrArgs::Delimited(_) | AttrArgs::Empty => None,381 }382 }383384 /// Returns the span in:385 ///386 /// ```text387 /// #[attribute = "value"]388 /// ^^^^^^^389 /// ```390 ///391 /// It returns `None` in any other cases like:392 ///393 /// ```text394 /// #[attr("value")]395 /// ```396 fn value_span(&self) -> Option<Span> {397 match &self.args.unparsed_ref()? {398 AttrArgs::Eq { expr, .. } => Some(expr.span),399 AttrArgs::Delimited(_) | AttrArgs::Empty => None,400 }401 }402403 pub fn meta(&self, span: Span) -> Option<MetaItem> {404 Some(MetaItem {405 unsafety: Safety::Default,406 path: self.path.clone(),407 kind: self.meta_kind()?,408 span,409 })410 }411412 pub fn meta_kind(&self) -> Option<MetaItemKind> {413 MetaItemKind::from_attr_args(self.args.unparsed_ref()?)414 }415}416417impl MetaItem {418 /// For a single-segment meta item, returns its name; otherwise, returns `None`.419 pub fn ident(&self) -> Option<Ident> {420 if let [PathSegment { ident, .. }] = self.path.segments[..] { Some(ident) } else { None }421 }422423 pub fn name(&self) -> Option<Symbol> {424 self.ident().map(|ident| ident.name)425 }426427 pub fn has_name(&self, name: Symbol) -> bool {428 self.path == name429 }430431 pub fn is_word(&self) -> bool {432 matches!(self.kind, MetaItemKind::Word)433 }434435 pub fn meta_item_list(&self) -> Option<&[MetaItemInner]> {436 match &self.kind {437 MetaItemKind::List(l) => Some(&**l),438 _ => None,439 }440 }441442 /// ```text443 /// Example:444 /// #[attribute(name = "value")]445 /// ^^^^^^^^^^^^^^446 /// ```447 pub fn name_value_literal(&self) -> Option<&MetaItemLit> {448 match &self.kind {449 MetaItemKind::NameValue(v) => Some(v),450 _ => None,451 }452 }453454 /// This is used in case you want the value span instead of the whole attribute. Example:455 ///456 /// ```text457 /// #[doc(alias = "foo")]458 /// ```459 ///460 /// In here, it'll return a span for `"foo"`.461 pub fn name_value_literal_span(&self) -> Option<Span> {462 Some(self.name_value_literal()?.span)463 }464465 /// Returns the string value in:466 ///467 /// ```text468 /// #[attribute = "value"]469 /// ^^^^^^^470 /// ```471 ///472 /// It returns `None` in any other cases like:473 ///474 /// ```text475 /// #[attr("value")]476 /// ```477 pub fn value_str(&self) -> Option<Symbol> {478 match &self.kind {479 MetaItemKind::NameValue(v) => v.kind.str(),480 _ => None,481 }482 }483484 fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItem> {485 // FIXME: Share code with `parse_path`.486 let tt = iter.next().map(|tt| TokenTree::uninterpolate(tt));487 let path = match tt.as_deref() {488 Some(&TokenTree::Token(489 Token { kind: ref kind @ (token::Ident(..) | token::PathSep), span },490 _,491 )) => 'arm: {492 let mut segments = if let &token::Ident(name, _) = kind {493 if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =494 iter.peek()495 {496 iter.next();497 thin_vec![PathSegment::from_ident(Ident::new(name, span))]498 } else {499 break 'arm Path::from_ident(Ident::new(name, span));500 }501 } else {502 thin_vec![PathSegment::path_root(span)]503 };504 loop {505 let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =506 iter.next().map(|tt| TokenTree::uninterpolate(tt)).as_deref()507 else {508 return None;509 };510 segments.push(PathSegment::from_ident(Ident::new(name, span)));511 let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) = iter.peek()512 else {513 break;514 };515 iter.next();516 }517 let span = span.with_hi(segments.last().unwrap().ident.span.hi());518 Path { span, segments, tokens: None }519 }520 Some(TokenTree::Delimited(521 _span,522 _spacing,523 Delimiter::Invisible(InvisibleOrigin::MetaVar(524 MetaVarKind::Meta { .. } | MetaVarKind::Path,525 )),526 _stream,527 )) => {528 // This path is currently unreachable in the test suite.529 unreachable!()530 }531 Some(TokenTree::Token(Token { kind, .. }, _)) if kind.is_delim() => {532 panic!("Should be `AttrTokenTree::Delimited`, not delim tokens: {:?}", tt);533 }534 _ => return None,535 };536 let list_closing_paren_pos = iter.peek().map(|tt| tt.span().hi());537 let kind = MetaItemKind::from_tokens(iter)?;538 let hi = match &kind {539 MetaItemKind::NameValue(lit) => lit.span.hi(),540 MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(path.span.hi()),541 _ => path.span.hi(),542 };543 let span = path.span.with_hi(hi);544 // FIXME: This parses `unsafe()` not as unsafe attribute syntax in `MetaItem`,545 // but as a parenthesized list. This (and likely `MetaItem`) should be changed in546 // such a way that builtin macros don't accept extraneous `unsafe()`.547 Some(MetaItem { unsafety: Safety::Default, path, kind, span })548 }549}550551impl MetaItemKind {552 // public because it can be called in the hir553 pub fn list_from_tokens(tokens: TokenStream) -> Option<ThinVec<MetaItemInner>> {554 let mut iter = tokens.iter();555 let mut result = ThinVec::new();556 while iter.peek().is_some() {557 let item = MetaItemInner::from_tokens(&mut iter)?;558 result.push(item);559 match iter.next() {560 None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {}561 _ => return None,562 }563 }564 Some(result)565 }566567 fn name_value_from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItemKind> {568 match iter.next() {569 Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => {570 MetaItemKind::name_value_from_tokens(&mut inner_tokens.iter())571 }572 Some(TokenTree::Token(token, _)) => {573 MetaItemLit::from_token(token).map(MetaItemKind::NameValue)574 }575 _ => None,576 }577 }578579 fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItemKind> {580 match iter.peek() {581 Some(TokenTree::Delimited(.., Delimiter::Parenthesis, inner_tokens)) => {582 let inner_tokens = inner_tokens.clone();583 iter.next();584 MetaItemKind::list_from_tokens(inner_tokens).map(MetaItemKind::List)585 }586 Some(TokenTree::Delimited(..)) => None,587 Some(TokenTree::Token(Token { kind: token::Eq, .. }, _)) => {588 iter.next();589 MetaItemKind::name_value_from_tokens(iter)590 }591 _ => Some(MetaItemKind::Word),592 }593 }594595 fn from_attr_args(args: &AttrArgs) -> Option<MetaItemKind> {596 match args {597 AttrArgs::Empty => Some(MetaItemKind::Word),598 AttrArgs::Delimited(DelimArgs { dspan: _, delim: Delimiter::Parenthesis, tokens }) => {599 MetaItemKind::list_from_tokens(tokens.clone()).map(MetaItemKind::List)600 }601 AttrArgs::Delimited(..) => None,602 AttrArgs::Eq { expr, .. } => match expr.kind {603 ExprKind::Lit(token_lit) => {604 // Turn failures to `None`, we'll get parse errors elsewhere.605 MetaItemLit::from_token_lit(token_lit, expr.span)606 .ok()607 .map(|lit| MetaItemKind::NameValue(lit))608 }609 _ => None,610 },611 }612 }613}614615impl MetaItemInner {616 pub fn span(&self) -> Span {617 match self {618 MetaItemInner::MetaItem(item) => item.span,619 MetaItemInner::Lit(lit) => lit.span,620 }621 }622623 /// For a single-segment meta item, returns its identifier; otherwise, returns `None`.624 pub fn ident(&self) -> Option<Ident> {625 self.meta_item().and_then(|meta_item| meta_item.ident())626 }627628 /// For a single-segment meta item, returns its name; otherwise, returns `None`.629 pub fn name(&self) -> Option<Symbol> {630 self.ident().map(|ident| ident.name)631 }632633 /// Returns `true` if this list item is a MetaItem with a name of `name`.634 pub fn has_name(&self, name: Symbol) -> bool {635 self.meta_item().is_some_and(|meta_item| meta_item.has_name(name))636 }637638 /// Returns `true` if `self` is a `MetaItem` and the meta item is a word.639 pub fn is_word(&self) -> bool {640 self.meta_item().is_some_and(|meta_item| meta_item.is_word())641 }642643 /// Gets a list of inner meta items from a list `MetaItem` type.644 pub fn meta_item_list(&self) -> Option<&[MetaItemInner]> {645 self.meta_item().and_then(|meta_item| meta_item.meta_item_list())646 }647648 /// If it's a singleton list of the form `foo(lit)`, returns the `foo` and649 /// the `lit`.650 pub fn singleton_lit_list(&self) -> Option<(Symbol, &MetaItemLit)> {651 self.meta_item().and_then(|meta_item| {652 meta_item.meta_item_list().and_then(|meta_item_list| {653 if meta_item_list.len() == 1654 && let Some(ident) = meta_item.ident()655 && let Some(lit) = meta_item_list[0].lit()656 {657 return Some((ident.name, lit));658 }659 None660 })661 })662 }663664 /// See [`MetaItem::name_value_literal_span`].665 pub fn name_value_literal_span(&self) -> Option<Span> {666 self.meta_item()?.name_value_literal_span()667 }668669 /// Gets the string value if `self` is a `MetaItem` and the `MetaItem` is a670 /// `MetaItemKind::NameValue` variant containing a string, otherwise `None`.671 pub fn value_str(&self) -> Option<Symbol> {672 self.meta_item().and_then(|meta_item| meta_item.value_str())673 }674675 /// Returns the `MetaItemLit` if `self` is a `MetaItemInner::Literal`s.676 pub fn lit(&self) -> Option<&MetaItemLit> {677 match self {678 MetaItemInner::Lit(lit) => Some(lit),679 _ => None,680 }681 }682683 /// Returns the bool if `self` is a boolean `MetaItemInner::Literal`.684 pub fn boolean_literal(&self) -> Option<bool> {685 match self {686 MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => Some(*b),687 _ => None,688 }689 }690691 /// Returns the `MetaItem` if `self` is a `MetaItemInner::MetaItem` or if it's692 /// `MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(_), .. })`.693 pub fn meta_item_or_bool(&self) -> Option<&MetaItemInner> {694 match self {695 MetaItemInner::MetaItem(_item) => Some(self),696 MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(_), .. }) => Some(self),697 _ => None,698 }699 }700701 /// Returns the `MetaItem` if `self` is a `MetaItemInner::MetaItem`.702 pub fn meta_item(&self) -> Option<&MetaItem> {703 match self {704 MetaItemInner::MetaItem(item) => Some(item),705 _ => None,706 }707 }708709 /// Returns `true` if the variant is `MetaItem`.710 pub fn is_meta_item(&self) -> bool {711 self.meta_item().is_some()712 }713714 fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItemInner> {715 match iter.peek() {716 Some(TokenTree::Token(token, _)) if let Some(lit) = MetaItemLit::from_token(token) => {717 iter.next();718 return Some(MetaItemInner::Lit(lit));719 }720 Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => {721 iter.next();722 return MetaItemInner::from_tokens(&mut inner_tokens.iter());723 }724 _ => {}725 }726 MetaItem::from_tokens(iter).map(MetaItemInner::MetaItem)727 }728}729730pub fn mk_doc_comment(731 g: &AttrIdGenerator,732 comment_kind: CommentKind,733 style: AttrStyle,734 data: Symbol,735 span: Span,736) -> Attribute {737 Attribute { kind: AttrKind::DocComment(comment_kind, data), id: g.mk_attr_id(), style, span }738}739740fn mk_attr(741 g: &AttrIdGenerator,742 style: AttrStyle,743 unsafety: Safety,744 path: Path,745 args: AttrArgs,746 span: Span,747) -> Attribute {748 mk_attr_from_item(749 g,750 AttrItem { unsafety, path, args: AttrItemKind::Unparsed(args), tokens: None },751 None,752 style,753 span,754 )755}756757pub fn mk_attr_from_item(758 g: &AttrIdGenerator,759 item: AttrItem,760 tokens: Option<LazyAttrTokenStream>,761 style: AttrStyle,762 span: Span,763) -> Attribute {764 Attribute {765 kind: AttrKind::Normal(Box::new(NormalAttr { item, tokens })),766 id: g.mk_attr_id(),767 style,768 span,769 }770}771772pub fn mk_attr_word(773 g: &AttrIdGenerator,774 style: AttrStyle,775 unsafety: Safety,776 name: Symbol,777 span: Span,778) -> Attribute {779 let path = Path::from_ident(Ident::new(name, span));780 let args = AttrArgs::Empty;781 mk_attr(g, style, unsafety, path, args, span)782}783784pub fn mk_attr_nested_word(785 g: &AttrIdGenerator,786 style: AttrStyle,787 unsafety: Safety,788 outer: Symbol,789 inner: Symbol,790 span: Span,791) -> Attribute {792 let inner_tokens = TokenStream::new(vec![TokenTree::Token(793 Token::from_ast_ident(Ident::new(inner, span)),794 Spacing::Alone,795 )]);796 let outer_ident = Ident::new(outer, span);797 let path = Path::from_ident(outer_ident);798 let attr_args = AttrArgs::Delimited(DelimArgs {799 dspan: DelimSpan::from_single(span),800 delim: Delimiter::Parenthesis,801 tokens: inner_tokens,802 });803 mk_attr(g, style, unsafety, path, attr_args, span)804}805806pub fn mk_attr_name_value_str(807 g: &AttrIdGenerator,808 style: AttrStyle,809 unsafety: Safety,810 name: Symbol,811 val: Symbol,812 span: Span,813) -> Attribute {814 let lit = token::Lit::new(token::Str, escape_string_symbol(val), None);815 let expr = Box::new(Expr {816 id: DUMMY_NODE_ID,817 kind: ExprKind::Lit(lit),818 span,819 attrs: AttrVec::new(),820 tokens: None,821 });822 let path = Path::from_ident(Ident::new(name, span));823 let args = AttrArgs::Eq { eq_span: span, expr };824 mk_attr(g, style, unsafety, path, args, span)825}826827pub fn filter_by_name(attrs: &[Attribute], name: Symbol) -> impl Iterator<Item = &Attribute> {828 attrs.iter().filter(move |attr| attr.has_name(name))829}830831pub fn find_by_name(attrs: &[Attribute], name: Symbol) -> Option<&Attribute> {832 filter_by_name(attrs, name).next()833}834835pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: Symbol) -> Option<Symbol> {836 find_by_name(attrs, name).and_then(|attr| attr.value_str())837}838839pub fn contains_name(attrs: &[Attribute], name: Symbol) -> bool {840 find_by_name(attrs, name).is_some()841}842843pub fn list_contains_name(items: &[MetaItemInner], name: Symbol) -> bool {844 items.iter().any(|item| item.has_name(name))845}846847impl MetaItemLit {848 pub fn value_str(&self) -> Option<Symbol> {849 LitKind::from_token_lit(self.as_token_lit()).ok().and_then(|lit| lit.str())850 }851}852853pub trait AttributeExt: Debug {854 fn id(&self) -> AttrId;855856 /// For a single-segment attribute (i.e., `#[attr]` and not `#[path::atrr]`),857 /// return the name of the attribute; otherwise, returns `None`.858 fn name(&self) -> Option<Symbol>;859860 /// Get the meta item list, `#[attr(meta item list)]`861 fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>>;862863 /// Gets the value literal, as string, when using `#[attr = value]`864 fn value_str(&self) -> Option<Symbol>;865866 /// Gets the span of the value literal, as string, when using `#[attr = value]`867 fn value_span(&self) -> Option<Span>;868869 /// Checks whether the path of this attribute matches the name.870 ///871 /// Matches one segment of the path to each element in `name`872 fn path_matches(&self, name: &[Symbol]) -> bool;873874 /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).875 /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not876 /// a doc comment) will return `false`.877 fn is_doc_comment(&self) -> Option<Span>;878879 /// Returns true if the attribute's first *and only* path segment is equal to the passed-in880 /// symbol.881 #[inline]882 fn has_name(&self, name: Symbol) -> bool {883 self.name().map(|x| x == name).unwrap_or(false)884 }885886 /// Returns true if the attribute's first *and only* path segment is any of the passed-in887 /// symbols.888 #[inline]889 fn has_any_name(&self, names: &[Symbol]) -> bool {890 names.iter().any(|&name| self.has_name(name))891 }892893 /// get the span of the entire attribute894 fn span(&self) -> Span;895896 /// Returns whether the attribute is a path, without any arguments.897 fn is_word(&self) -> bool;898899 fn path(&self) -> SmallVec<[Symbol; 1]> {900 self.symbol_path().unwrap_or(smallvec![sym::doc])901 }902903 fn path_span(&self) -> Option<Span>;904905 /// Returns None for doc comments906 fn symbol_path(&self) -> Option<SmallVec<[Symbol; 1]>>;907908 /// Returns the documentation if this is a doc comment or a sugared doc comment.909 /// * `///doc` returns `Some("doc")`.910 /// * `#[doc = "doc"]` returns `Some("doc")`.911 /// * `#[doc(...)]` returns `None`.912 fn doc_str(&self) -> Option<Symbol>;913914 /// Returns whether this attribute is any of the proc macro attributes.915 /// i.e. `proc_macro`, `proc_macro_attribute` or `proc_macro_derive`.916 fn is_proc_macro_attr(&self) -> bool {917 [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]918 .iter()919 .any(|kind| self.has_name(*kind))920 }921 /// Returns true if this attribute is `#[automatically_deived]`.922 fn is_automatically_derived_attr(&self) -> bool;923924 /// Returns the documentation and its kind if this is a doc comment or a sugared doc comment.925 /// * `///doc` returns `Some(("doc", CommentKind::Line))`.926 /// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.927 /// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.928 /// * `#[doc(...)]` returns `None`.929 fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)>;930931 /// Returns outer or inner if this is a doc attribute or a sugared doc932 /// comment, otherwise None.933 ///934 /// This is used in the case of doc comments on modules, to decide whether935 /// to resolve intra-doc links against the symbols in scope within the936 /// commented module (for inner doc) vs within its parent module (for outer937 /// doc).938 fn doc_resolution_scope(&self) -> Option<AttrStyle>;939940 /// Returns `true` if this attribute contains `doc(hidden)`.941 fn is_doc_hidden(&self) -> bool;942943 /// Returns `true` is this attribute contains `doc(keyword)` or `doc(attribute)`.944 fn is_doc_keyword_or_attribute(&self) -> bool;945946 /// Returns `true` if this is a `#[rustc_doc_primitive]` attribute.947 fn is_rustc_doc_primitive(&self) -> bool;948}949950// FIXME(fn_delegation): use function delegation instead of manually forwarding951952impl Attribute {953 pub fn id(&self) -> AttrId {954 AttributeExt::id(self)955 }956957 pub fn name(&self) -> Option<Symbol> {958 AttributeExt::name(self)959 }960961 pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {962 AttributeExt::meta_item_list(self)963 }964965 pub fn value_str(&self) -> Option<Symbol> {966 AttributeExt::value_str(self)967 }968969 pub fn value_span(&self) -> Option<Span> {970 AttributeExt::value_span(self)971 }972973 pub fn path_matches(&self, name: &[Symbol]) -> bool {974 AttributeExt::path_matches(self, name)975 }976977 // on ast attributes we return a bool since that's what most code already expects978 pub fn is_doc_comment(&self) -> bool {979 AttributeExt::is_doc_comment(self).is_some()980 }981982 #[inline]983 pub fn has_name(&self, name: Symbol) -> bool {984 AttributeExt::has_name(self, name)985 }986987 #[inline]988 pub fn has_any_name(&self, names: &[Symbol]) -> bool {989 AttributeExt::has_any_name(self, names)990 }991992 pub fn span(&self) -> Span {993 AttributeExt::span(self)994 }995996 pub fn is_word(&self) -> bool {997 AttributeExt::is_word(self)998 }9991000 pub fn path(&self) -> SmallVec<[Symbol; 1]> {1001 AttributeExt::path(self)1002 }10031004 pub fn doc_str(&self) -> Option<Symbol> {1005 AttributeExt::doc_str(self)1006 }10071008 pub fn is_proc_macro_attr(&self) -> bool {1009 AttributeExt::is_proc_macro_attr(self)1010 }10111012 pub fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)> {1013 AttributeExt::doc_str_and_fragment_kind(self)1014 }1015}