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