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