1use std::fmt::{Debug, Display};
7use std::iter::Peekable;
8
9use rustc_ast::token::{self, Delimiter, Token};
10use rustc_ast::tokenstream::{TokenStreamIter, TokenTree};
11use rustc_ast::{AttrArgs, DelimArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path};
12use rustc_ast_pretty::pprust;
13use rustc_errors::DiagCtxtHandle;
14use rustc_hir::{self as hir, AttrPath};
15use rustc_span::symbol::{Ident, kw, sym};
16use rustc_span::{ErrorGuaranteed, Span, Symbol};
17
18pub struct SegmentIterator<'a> {
19 offset: usize,
20 path: &'a PathParser<'a>,
21}
22
23impl<'a> Iterator for SegmentIterator<'a> {
24 type Item = &'a Ident;
25
26 fn next(&mut self) -> Option<Self::Item> {
27 if self.offset >= self.path.len() {
28 return None;
29 }
30
31 let res = match self.path {
32 PathParser::Ast(ast_path) => &ast_path.segments[self.offset].ident,
33 PathParser::Attr(attr_path) => &attr_path.segments[self.offset],
34 };
35
36 self.offset += 1;
37 Some(res)
38 }
39}
40
41#[derive(Clone, Debug)]
42pub enum PathParser<'a> {
43 Ast(&'a Path),
44 Attr(AttrPath),
45}
46
47impl<'a> PathParser<'a> {
48 pub fn get_attribute_path(&self) -> hir::AttrPath {
49 AttrPath {
50 segments: self.segments().copied().collect::<Vec<_>>().into_boxed_slice(),
51 span: self.span(),
52 }
53 }
54
55 pub fn segments(&'a self) -> impl Iterator<Item = &'a Ident> {
56 SegmentIterator { offset: 0, path: self }
57 }
58
59 pub fn span(&self) -> Span {
60 match self {
61 PathParser::Ast(path) => path.span,
62 PathParser::Attr(attr_path) => attr_path.span,
63 }
64 }
65
66 pub fn len(&self) -> usize {
67 match self {
68 PathParser::Ast(path) => path.segments.len(),
69 PathParser::Attr(attr_path) => attr_path.segments.len(),
70 }
71 }
72
73 pub fn segments_is(&self, segments: &[Symbol]) -> bool {
74 self.len() == segments.len() && self.segments().zip(segments).all(|(a, b)| a.name == *b)
75 }
76
77 pub fn word(&self) -> Option<Ident> {
78 (self.len() == 1).then(|| **self.segments().next().as_ref().unwrap())
79 }
80
81 pub fn word_or_empty(&self) -> Ident {
82 self.word().unwrap_or_else(Ident::empty)
83 }
84
85 pub fn word_is(&self, sym: Symbol) -> bool {
89 self.word().map(|i| i.name == sym).unwrap_or(false)
90 }
91}
92
93impl Display for PathParser<'_> {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 match self {
96 PathParser::Ast(path) => write!(f, "{}", pprust::path_to_string(path)),
97 PathParser::Attr(attr_path) => write!(f, "{attr_path}"),
98 }
99 }
100}
101
102#[derive(Clone, Debug)]
103#[must_use]
104pub enum ArgParser<'a> {
105 NoArgs,
106 List(MetaItemListParser<'a>),
107 NameValue(NameValueParser),
108}
109
110impl<'a> ArgParser<'a> {
111 pub fn span(&self) -> Option<Span> {
112 match self {
113 Self::NoArgs => None,
114 Self::List(l) => Some(l.span),
115 Self::NameValue(n) => Some(n.value_span.with_lo(n.eq_span.lo())),
116 }
117 }
118
119 pub fn from_attr_args(value: &'a AttrArgs, dcx: DiagCtxtHandle<'a>) -> Self {
120 match value {
121 AttrArgs::Empty => Self::NoArgs,
122 AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {
123 Self::List(MetaItemListParser::new(args, dcx))
124 }
125 AttrArgs::Delimited(args) => {
126 Self::List(MetaItemListParser { sub_parsers: vec![], span: args.dspan.entire() })
127 }
128 AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser {
129 eq_span: *eq_span,
130 value: expr_to_lit(dcx, &expr, *eq_span),
131 value_span: expr.span,
132 }),
133 }
134 }
135
136 pub fn list(&self) -> Option<&MetaItemListParser<'a>> {
143 match self {
144 Self::List(l) => Some(l),
145 Self::NameValue(_) | Self::NoArgs => None,
146 }
147 }
148
149 pub fn name_value(&self) -> Option<&NameValueParser> {
159 match self {
160 Self::NameValue(n) => Some(n),
161 Self::List(_) | Self::NoArgs => None,
162 }
163 }
164
165 pub fn no_args(&self) -> bool {
167 matches!(self, Self::NoArgs)
168 }
169}
170
171#[derive(Debug, Clone)]
176pub enum MetaItemOrLitParser<'a> {
177 MetaItemParser(MetaItemParser<'a>),
178 Lit(MetaItemLit),
179 Err(Span, ErrorGuaranteed),
180}
181
182impl<'a> MetaItemOrLitParser<'a> {
183 pub fn span(&self) -> Span {
184 match self {
185 MetaItemOrLitParser::MetaItemParser(generic_meta_item_parser) => {
186 generic_meta_item_parser.span()
187 }
188 MetaItemOrLitParser::Lit(meta_item_lit) => meta_item_lit.span,
189 MetaItemOrLitParser::Err(span, _) => *span,
190 }
191 }
192
193 pub fn lit(&self) -> Option<&MetaItemLit> {
194 match self {
195 MetaItemOrLitParser::Lit(meta_item_lit) => Some(meta_item_lit),
196 _ => None,
197 }
198 }
199
200 pub fn meta_item(&self) -> Option<&MetaItemParser<'a>> {
201 match self {
202 MetaItemOrLitParser::MetaItemParser(parser) => Some(parser),
203 _ => None,
204 }
205 }
206}
207
208#[derive(Clone)]
222pub struct MetaItemParser<'a> {
223 path: PathParser<'a>,
224 args: ArgParser<'a>,
225}
226
227impl<'a> Debug for MetaItemParser<'a> {
228 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
229 f.debug_struct("MetaItemParser")
230 .field("path", &self.path)
231 .field("args", &self.args)
232 .finish()
233 }
234}
235
236impl<'a> MetaItemParser<'a> {
237 pub fn from_attr(attr: &'a NormalAttr, dcx: DiagCtxtHandle<'a>) -> Self {
240 Self {
241 path: PathParser::Ast(&attr.item.path),
242 args: ArgParser::from_attr_args(&attr.item.args, dcx),
243 }
244 }
245}
246
247impl<'a> MetaItemParser<'a> {
248 pub fn span(&self) -> Span {
249 if let Some(other) = self.args.span() {
250 self.path.span().with_hi(other.hi())
251 } else {
252 self.path.span()
253 }
254 }
255
256 pub fn path_without_args(&self) -> PathParser<'a> {
258 self.path.clone()
259 }
260
261 pub fn args(&self) -> &ArgParser<'a> {
263 &self.args
264 }
265
266 pub fn deconstruct(&self) -> (PathParser<'a>, &ArgParser<'a>) {
267 (self.path_without_args(), self.args())
268 }
269
270 pub fn path(&self) -> (PathParser<'a>, &ArgParser<'a>) {
276 self.deconstruct()
277 }
278
279 pub fn word_without_args(&self) -> Option<Ident> {
284 Some(self.word()?.0)
285 }
286
287 pub fn word_or_empty_without_args(&self) -> Ident {
289 self.word_or_empty().0
290 }
291
292 pub fn word(&self) -> Option<(Ident, &ArgParser<'a>)> {
299 let (path, args) = self.deconstruct();
300 Some((path.word()?, args))
301 }
302
303 pub fn word_or_empty(&self) -> (Ident, &ArgParser<'a>) {
305 let (path, args) = self.deconstruct();
306 (path.word().unwrap_or(Ident::empty()), args)
307 }
308
309 pub fn word_is(&self, sym: Symbol) -> Option<&ArgParser<'a>> {
313 self.path_without_args().word_is(sym).then(|| self.args())
314 }
315
316 pub fn path_is(&self, segments: &[Symbol]) -> Option<&ArgParser<'a>> {
320 self.path_without_args().segments_is(segments).then(|| self.args())
321 }
322}
323
324#[derive(Clone)]
325pub struct NameValueParser {
326 pub eq_span: Span,
327 value: MetaItemLit,
328 pub value_span: Span,
329}
330
331impl Debug for NameValueParser {
332 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
333 f.debug_struct("NameValueParser")
334 .field("eq_span", &self.eq_span)
335 .field("value", &self.value)
336 .field("value_span", &self.value_span)
337 .finish()
338 }
339}
340
341impl NameValueParser {
342 pub fn value_as_lit(&self) -> &MetaItemLit {
343 &self.value
344 }
345
346 pub fn value_as_str(&self) -> Option<Symbol> {
347 self.value_as_lit().kind.str()
348 }
349}
350
351fn expr_to_lit(dcx: DiagCtxtHandle<'_>, expr: &Expr, span: Span) -> MetaItemLit {
352 if let ExprKind::Lit(token_lit) = expr.kind
355 && let Ok(lit) = MetaItemLit::from_token_lit(token_lit, expr.span)
356 {
357 lit
358 } else {
359 let guar = dcx.span_delayed_bug(
360 span,
361 "expr in place where literal is expected (builtin attr parsing)",
362 );
363 MetaItemLit { symbol: sym::dummy, suffix: None, kind: LitKind::Err(guar), span }
364 }
365}
366
367struct MetaItemListParserContext<'a> {
368 inside_delimiters: Peekable<TokenStreamIter<'a>>,
370 dcx: DiagCtxtHandle<'a>,
371}
372
373impl<'a> MetaItemListParserContext<'a> {
374 fn done(&mut self) -> bool {
375 self.inside_delimiters.peek().is_none()
376 }
377
378 fn next_path(&mut self) -> Option<AttrPath> {
379 let tt = self.inside_delimiters.next().map(|tt| TokenTree::uninterpolate(tt));
381
382 match tt.as_deref()? {
383 &TokenTree::Token(
384 Token { kind: ref kind @ (token::Ident(..) | token::PathSep), span },
385 _,
386 ) => {
387 let mut segments = if let &token::Ident(name, _) = kind {
390 if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
392 self.inside_delimiters.peek()
393 {
394 self.inside_delimiters.next();
395 vec![Ident::new(name, span)]
396 } else {
397 return Some(AttrPath {
399 segments: vec![Ident::new(name, span)].into_boxed_slice(),
400 span,
401 });
402 }
403 } else {
404 vec![Ident::new(kw::PathRoot, span)]
406 };
407
408 loop {
410 if let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
412 self.inside_delimiters
413 .next()
414 .map(|tt| TokenTree::uninterpolate(tt))
415 .as_deref()
416 {
417 segments.push(Ident::new(name, span));
418 } else {
419 return None;
420 }
421 if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
423 self.inside_delimiters.peek()
424 {
425 self.inside_delimiters.next();
426 } else {
427 break;
428 }
429 }
430 let span = span.with_hi(segments.last().unwrap().span.hi());
431 Some(AttrPath { segments: segments.into_boxed_slice(), span })
432 }
433 TokenTree::Token(Token { kind: token::OpenDelim(_) | token::CloseDelim(_), .. }, _) => {
434 None
435 }
436 _ => {
437 None
440 }
441 }
442 }
443
444 fn value(&mut self) -> Option<MetaItemLit> {
445 match self.inside_delimiters.next() {
446 Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => {
447 MetaItemListParserContext {
448 inside_delimiters: inner_tokens.iter().peekable(),
449 dcx: self.dcx,
450 }
451 .value()
452 }
453 Some(TokenTree::Token(token, _)) => MetaItemLit::from_token(token),
454 _ => None,
455 }
456 }
457
458 fn next(&mut self) -> Option<MetaItemOrLitParser<'a>> {
470 if let Some(TokenTree::Token(token, _)) = self.inside_delimiters.peek()
472 && let Some(lit) = MetaItemLit::from_token(token)
473 {
474 self.inside_delimiters.next();
475 return Some(MetaItemOrLitParser::Lit(lit));
476 } else if let Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) =
477 self.inside_delimiters.peek()
478 {
479 self.inside_delimiters.next();
480 return MetaItemListParserContext {
481 inside_delimiters: inner_tokens.iter().peekable(),
482 dcx: self.dcx,
483 }
484 .next();
485 }
486
487 let path = self.next_path()?;
489
490 Some(MetaItemOrLitParser::MetaItemParser(match self.inside_delimiters.peek() {
495 Some(TokenTree::Delimited(dspan, _, Delimiter::Parenthesis, inner_tokens)) => {
496 self.inside_delimiters.next();
497
498 MetaItemParser {
499 path: PathParser::Attr(path),
500 args: ArgParser::List(MetaItemListParser::new_tts(
501 inner_tokens.iter(),
502 dspan.entire(),
503 self.dcx,
504 )),
505 }
506 }
507 Some(TokenTree::Delimited(_, ..)) => {
508 self.inside_delimiters.next();
509 return None;
511 }
512 Some(TokenTree::Token(Token { kind: token::Eq, span }, _)) => {
513 self.inside_delimiters.next();
514 let value = self.value()?;
515 MetaItemParser {
516 path: PathParser::Attr(path),
517 args: ArgParser::NameValue(NameValueParser {
518 eq_span: *span,
519 value_span: value.span,
520 value,
521 }),
522 }
523 }
524 _ => MetaItemParser { path: PathParser::Attr(path), args: ArgParser::NoArgs },
525 }))
526 }
527
528 fn parse(mut self, span: Span) -> MetaItemListParser<'a> {
529 let mut sub_parsers = Vec::new();
530
531 while !self.done() {
532 let Some(n) = self.next() else {
533 continue;
534 };
535 sub_parsers.push(n);
536
537 match self.inside_delimiters.peek() {
538 None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {
539 self.inside_delimiters.next();
540 }
541 Some(_) => {}
542 }
543 }
544
545 MetaItemListParser { sub_parsers, span }
546 }
547}
548
549#[derive(Debug, Clone)]
550pub struct MetaItemListParser<'a> {
551 sub_parsers: Vec<MetaItemOrLitParser<'a>>,
552 pub span: Span,
553}
554
555impl<'a> MetaItemListParser<'a> {
556 fn new(delim: &'a DelimArgs, dcx: DiagCtxtHandle<'a>) -> MetaItemListParser<'a> {
557 MetaItemListParser::new_tts(delim.tokens.iter(), delim.dspan.entire(), dcx)
558 }
559
560 fn new_tts(tts: TokenStreamIter<'a>, span: Span, dcx: DiagCtxtHandle<'a>) -> Self {
561 MetaItemListParserContext { inside_delimiters: tts.peekable(), dcx }.parse(span)
562 }
563
564 pub fn mixed<'s>(&'s self) -> impl Iterator<Item = &'s MetaItemOrLitParser<'a>> + 's {
566 self.sub_parsers.iter()
567 }
568
569 pub fn len(&self) -> usize {
570 self.sub_parsers.len()
571 }
572
573 pub fn is_empty(&self) -> bool {
574 self.len() == 0
575 }
576
577 pub fn all_word_list<'s>(&'s self) -> Option<Vec<(Ident, &'s ArgParser<'a>)>> {
581 self.mixed().map(|i| i.meta_item()?.word()).collect()
582 }
583
584 pub fn all_path_list<'s>(&'s self) -> Option<Vec<(PathParser<'a>, &'s ArgParser<'a>)>> {
588 self.mixed().map(|i| Some(i.meta_item()?.path())).collect()
589 }
590
591 pub fn single(&self) -> Option<&MetaItemOrLitParser<'a>> {
595 let mut iter = self.mixed();
596 iter.next().filter(|_| iter.next().is_none())
597 }
598}