Skip to content

Commit 2c3eb76

Browse files
committed
add extension trait for attributes to make hir::Attribute a drop in replacement for most places currently accepting ast::Attribute
1 parent 4e426da commit 2c3eb76

File tree

1 file changed

+150
-72
lines changed
  • compiler/rustc_ast/src/attr

1 file changed

+150
-72
lines changed

compiler/rustc_ast/src/attr/mod.rs

Lines changed: 150 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -53,33 +53,30 @@ impl AttrIdGenerator {
5353
}
5454
}
5555

56-
impl Attribute {
57-
pub fn get_normal_item(&self) -> &AttrItem {
58-
match &self.kind {
59-
AttrKind::Normal(normal) => &normal.item,
60-
AttrKind::DocComment(..) => panic!("unexpected doc comment"),
61-
}
56+
impl AttributeExt for Attribute {
57+
fn id(&self) -> AttrId {
58+
self.id
6259
}
6360

64-
pub fn unwrap_normal_item(self) -> AttrItem {
65-
match self.kind {
66-
AttrKind::Normal(normal) => normal.into_inner().item,
67-
AttrKind::DocComment(..) => panic!("unexpected doc comment"),
61+
fn value_str(&self) -> Option<Symbol> {
62+
match &self.kind {
63+
AttrKind::Normal(normal) => normal.item.value_str(),
64+
AttrKind::DocComment(..) => None,
6865
}
6966
}
7067

71-
/// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).
72-
/// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not
73-
/// a doc comment) will return `false`.
74-
pub fn is_doc_comment(&self) -> bool {
75-
match self.kind {
76-
AttrKind::Normal(..) => false,
77-
AttrKind::DocComment(..) => true,
68+
fn value_span(&self) -> Option<Span> {
69+
match &self.kind {
70+
AttrKind::Normal(normal) => match &normal.item.args {
71+
AttrArgs::Eq(_, l) => Some(l.span),
72+
_ => None,
73+
},
74+
AttrKind::DocComment(..) => None,
7875
}
7976
}
8077

8178
/// For a single-segment attribute, returns its name; otherwise, returns `None`.
82-
pub fn ident(&self) -> Option<Ident> {
79+
fn ident(&self) -> Option<Ident> {
8380
match &self.kind {
8481
AttrKind::Normal(normal) => {
8582
if let [ident] = &*normal.item.path.segments {
@@ -92,28 +89,7 @@ impl Attribute {
9289
}
9390
}
9491

95-
pub fn name_or_empty(&self) -> Symbol {
96-
self.ident().unwrap_or_else(Ident::empty).name
97-
}
98-
99-
pub fn path(&self) -> SmallVec<[Symbol; 1]> {
100-
match &self.kind {
101-
AttrKind::Normal(normal) => {
102-
normal.item.path.segments.iter().map(|s| s.ident.name).collect()
103-
}
104-
AttrKind::DocComment(..) => smallvec![sym::doc],
105-
}
106-
}
107-
108-
#[inline]
109-
pub fn has_name(&self, name: Symbol) -> bool {
110-
match &self.kind {
111-
AttrKind::Normal(normal) => normal.item.path == name,
112-
AttrKind::DocComment(..) => false,
113-
}
114-
}
115-
116-
pub fn path_matches(&self, name: &[Symbol]) -> bool {
92+
fn path_matches(&self, name: &[Symbol]) -> bool {
11793
match &self.kind {
11894
AttrKind::Normal(normal) => {
11995
normal.item.path.segments.len() == name.len()
@@ -129,34 +105,45 @@ impl Attribute {
129105
}
130106
}
131107

132-
pub fn is_word(&self) -> bool {
108+
fn is_doc_comment(&self) -> bool {
109+
match self.kind {
110+
AttrKind::Normal(..) => false,
111+
AttrKind::DocComment(..) => true,
112+
}
113+
}
114+
115+
fn span(&self) -> Span {
116+
self.span
117+
}
118+
119+
fn is_word(&self) -> bool {
133120
if let AttrKind::Normal(normal) = &self.kind {
134121
matches!(normal.item.args, AttrArgs::Empty)
135122
} else {
136123
false
137124
}
138125
}
139126

140-
pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
127+
fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
141128
match &self.kind {
142129
AttrKind::Normal(normal) => normal.item.meta_item_list(),
143130
AttrKind::DocComment(..) => None,
144131
}
145132
}
146133

147-
pub fn value_str(&self) -> Option<Symbol> {
134+
/// Returns the documentation if this is a doc comment or a sugared doc comment.
135+
/// * `///doc` returns `Some("doc")`.
136+
/// * `#[doc = "doc"]` returns `Some("doc")`.
137+
/// * `#[doc(...)]` returns `None`.
138+
fn doc_str(&self) -> Option<Symbol> {
148139
match &self.kind {
149-
AttrKind::Normal(normal) => normal.item.value_str(),
150-
AttrKind::DocComment(..) => None,
140+
AttrKind::DocComment(.., data) => Some(*data),
141+
AttrKind::Normal(normal) if normal.item.path == sym::doc => normal.item.value_str(),
142+
_ => None,
151143
}
152144
}
153145

154-
/// Returns the documentation and its kind if this is a doc comment or a sugared doc comment.
155-
/// * `///doc` returns `Some(("doc", CommentKind::Line))`.
156-
/// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
157-
/// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
158-
/// * `#[doc(...)]` returns `None`.
159-
pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
146+
fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
160147
match &self.kind {
161148
AttrKind::DocComment(kind, data) => Some((*data, *kind)),
162149
AttrKind::Normal(normal) if normal.item.path == sym::doc => {
@@ -166,26 +153,35 @@ impl Attribute {
166153
}
167154
}
168155

169-
/// Returns the documentation if this is a doc comment or a sugared doc comment.
170-
/// * `///doc` returns `Some("doc")`.
171-
/// * `#[doc = "doc"]` returns `Some("doc")`.
172-
/// * `#[doc(...)]` returns `None`.
173-
pub fn doc_str(&self) -> Option<Symbol> {
156+
fn style(&self) -> AttrStyle {
157+
self.style
158+
}
159+
160+
fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> {
174161
match &self.kind {
175-
AttrKind::DocComment(.., data) => Some(*data),
176-
AttrKind::Normal(normal) if normal.item.path == sym::doc => normal.item.value_str(),
177-
_ => None,
162+
AttrKind::Normal(p) => Some(p.item.path.segments.iter().map(|i| i.ident).collect()),
163+
AttrKind::DocComment(_, _) => None,
178164
}
179165
}
166+
}
180167

181-
pub fn may_have_doc_links(&self) -> bool {
182-
self.doc_str().is_some_and(|s| comments::may_have_doc_links(s.as_str()))
168+
impl Attribute {
169+
pub fn get_normal_item(&self) -> &AttrItem {
170+
match &self.kind {
171+
AttrKind::Normal(normal) => &normal.item,
172+
AttrKind::DocComment(..) => panic!("unexpected doc comment"),
173+
}
183174
}
184175

185-
pub fn is_proc_macro_attr(&self) -> bool {
186-
[sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
187-
.iter()
188-
.any(|kind| self.has_name(*kind))
176+
pub fn unwrap_normal_item(self) -> AttrItem {
177+
match self.kind {
178+
AttrKind::Normal(normal) => normal.into_inner().item,
179+
AttrKind::DocComment(..) => panic!("unexpected doc comment"),
180+
}
181+
}
182+
183+
pub fn may_have_doc_links(&self) -> bool {
184+
self.doc_str().is_some_and(|s| comments::may_have_doc_links(s.as_str()))
189185
}
190186

191187
/// Extracts the MetaItem from inside this Attribute.
@@ -235,7 +231,12 @@ impl AttrItem {
235231

236232
fn value_str(&self) -> Option<Symbol> {
237233
match &self.args {
238-
AttrArgs::Eq(_, args) => args.value_str(),
234+
AttrArgs::Eq(_, expr) => match expr.kind {
235+
ExprKind::Lit(token_lit) => {
236+
LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str())
237+
}
238+
_ => None,
239+
},
239240
AttrArgs::Delimited(_) | AttrArgs::Empty => None,
240241
}
241242
}
@@ -380,7 +381,8 @@ impl MetaItem {
380381
}
381382

382383
impl MetaItemKind {
383-
fn list_from_tokens(tokens: TokenStream) -> Option<ThinVec<MetaItemInner>> {
384+
// public because it can be called in the hir
385+
pub fn list_from_tokens(tokens: TokenStream) -> Option<ThinVec<MetaItemInner>> {
384386
let mut tokens = tokens.trees().peekable();
385387
let mut result = ThinVec::new();
386388
while tokens.peek().is_some() {
@@ -642,26 +644,102 @@ pub fn mk_attr_name_value_str(
642644
tokens: None,
643645
});
644646
let path = Path::from_ident(Ident::new(name, span));
645-
let args = AttrArgs::Eq(span, AttrArgsEq::Ast(expr));
647+
let args = AttrArgs::Eq(span, expr);
646648
mk_attr(g, style, unsafety, path, args, span)
647649
}
648650

649-
pub fn filter_by_name(attrs: &[Attribute], name: Symbol) -> impl Iterator<Item = &Attribute> {
651+
pub fn filter_by_name<A: AttributeExt>(attrs: &[A], name: Symbol) -> impl Iterator<Item = &A> {
650652
attrs.iter().filter(move |attr| attr.has_name(name))
651653
}
652654

653-
pub fn find_by_name(attrs: &[Attribute], name: Symbol) -> Option<&Attribute> {
655+
pub fn find_by_name<A: AttributeExt>(attrs: &[A], name: Symbol) -> Option<&A> {
654656
filter_by_name(attrs, name).next()
655657
}
656658

657-
pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: Symbol) -> Option<Symbol> {
659+
pub fn first_attr_value_str_by_name(attrs: &[impl AttributeExt], name: Symbol) -> Option<Symbol> {
658660
find_by_name(attrs, name).and_then(|attr| attr.value_str())
659661
}
660662

661-
pub fn contains_name(attrs: &[Attribute], name: Symbol) -> bool {
663+
pub fn contains_name(attrs: &[impl AttributeExt], name: Symbol) -> bool {
662664
find_by_name(attrs, name).is_some()
663665
}
664666

665667
pub fn list_contains_name(items: &[MetaItemInner], name: Symbol) -> bool {
666668
items.iter().any(|item| item.has_name(name))
667669
}
670+
671+
impl MetaItemLit {
672+
pub fn value_str(&self) -> Option<Symbol> {
673+
LitKind::from_token_lit(self.as_token_lit()).ok().and_then(|lit| lit.str())
674+
}
675+
}
676+
677+
pub trait AttributeExt: Debug {
678+
fn id(&self) -> AttrId;
679+
680+
fn name_or_empty(&self) -> Symbol {
681+
self.ident().unwrap_or_else(Ident::empty).name
682+
}
683+
684+
/// Get the meta item list, `#[attr(meta item list)]`
685+
fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>>;
686+
687+
/// Gets the value literal, as string, when using `#[attr = value]`
688+
fn value_str(&self) -> Option<Symbol>;
689+
690+
/// Gets the span of the value literal, as string, when using `#[attr = value]`
691+
fn value_span(&self) -> Option<Span>;
692+
693+
/// For a single-segment attribute, returns its name; otherwise, returns `None`.
694+
fn ident(&self) -> Option<Ident>;
695+
696+
/// Checks whether the path of this attribute matches the name.
697+
///
698+
/// Matches one segment of the path to each element in `name`
699+
fn path_matches(&self, name: &[Symbol]) -> bool;
700+
701+
/// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).
702+
/// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not
703+
/// a doc comment) will return `false`.
704+
fn is_doc_comment(&self) -> bool;
705+
706+
#[inline]
707+
fn has_name(&self, name: Symbol) -> bool {
708+
self.ident().map(|x| x.name == name).unwrap_or(false)
709+
}
710+
711+
/// get the span of the entire attribute
712+
fn span(&self) -> Span;
713+
714+
fn is_word(&self) -> bool;
715+
716+
fn path(&self) -> SmallVec<[Symbol; 1]> {
717+
self.ident_path()
718+
.map(|i| i.into_iter().map(|i| i.name).collect())
719+
.unwrap_or(smallvec![sym::doc])
720+
}
721+
722+
/// Returns None for doc comments
723+
fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>>;
724+
725+
/// Returns the documentation if this is a doc comment or a sugared doc comment.
726+
/// * `///doc` returns `Some("doc")`.
727+
/// * `#[doc = "doc"]` returns `Some("doc")`.
728+
/// * `#[doc(...)]` returns `None`.
729+
fn doc_str(&self) -> Option<Symbol>;
730+
731+
fn is_proc_macro_attr(&self) -> bool {
732+
[sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
733+
.iter()
734+
.any(|kind| self.has_name(*kind))
735+
}
736+
737+
/// Returns the documentation and its kind if this is a doc comment or a sugared doc comment.
738+
/// * `///doc` returns `Some(("doc", CommentKind::Line))`.
739+
/// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
740+
/// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
741+
/// * `#[doc(...)]` returns `None`.
742+
fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)>;
743+
744+
fn style(&self) -> AttrStyle;
745+
}

0 commit comments

Comments
 (0)