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(&self) -> &PathParser<'a> {
261 &self.path
262 }
263
264 pub fn args(&self) -> &ArgParser<'a> {
266 &self.args
267 }
268
269 pub fn word_is(&self, sym: Symbol) -> Option<&ArgParser<'a>> {
276 self.path().word_is(sym).then(|| self.args())
277 }
278}
279
280#[derive(Clone)]
281pub struct NameValueParser {
282 pub eq_span: Span,
283 value: MetaItemLit,
284 pub value_span: Span,
285}
286
287impl Debug for NameValueParser {
288 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
289 f.debug_struct("NameValueParser")
290 .field("eq_span", &self.eq_span)
291 .field("value", &self.value)
292 .field("value_span", &self.value_span)
293 .finish()
294 }
295}
296
297impl NameValueParser {
298 pub fn value_as_lit(&self) -> &MetaItemLit {
299 &self.value
300 }
301
302 pub fn value_as_str(&self) -> Option<Symbol> {
303 self.value_as_lit().kind.str()
304 }
305}
306
307fn expr_to_lit(dcx: DiagCtxtHandle<'_>, expr: &Expr, span: Span) -> MetaItemLit {
308 if let ExprKind::Lit(token_lit) = expr.kind
311 && let Ok(lit) = MetaItemLit::from_token_lit(token_lit, expr.span)
312 {
313 lit
314 } else {
315 let guar = dcx.span_delayed_bug(
316 span,
317 "expr in place where literal is expected (builtin attr parsing)",
318 );
319 MetaItemLit { symbol: sym::dummy, suffix: None, kind: LitKind::Err(guar), span }
320 }
321}
322
323struct MetaItemListParserContext<'a> {
324 inside_delimiters: Peekable<TokenStreamIter<'a>>,
326 dcx: DiagCtxtHandle<'a>,
327}
328
329impl<'a> MetaItemListParserContext<'a> {
330 fn done(&mut self) -> bool {
331 self.inside_delimiters.peek().is_none()
332 }
333
334 fn next_path(&mut self) -> Option<AttrPath> {
335 let tt = self.inside_delimiters.next().map(|tt| TokenTree::uninterpolate(tt));
337
338 match tt.as_deref()? {
339 &TokenTree::Token(
340 Token { kind: ref kind @ (token::Ident(..) | token::PathSep), span },
341 _,
342 ) => {
343 let mut segments = if let &token::Ident(name, _) = kind {
346 if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
348 self.inside_delimiters.peek()
349 {
350 self.inside_delimiters.next();
351 vec![Ident::new(name, span)]
352 } else {
353 return Some(AttrPath {
355 segments: vec![Ident::new(name, span)].into_boxed_slice(),
356 span,
357 });
358 }
359 } else {
360 vec![Ident::new(kw::PathRoot, span)]
362 };
363
364 loop {
366 if let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
368 self.inside_delimiters
369 .next()
370 .map(|tt| TokenTree::uninterpolate(tt))
371 .as_deref()
372 {
373 segments.push(Ident::new(name, span));
374 } else {
375 return None;
376 }
377 if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
379 self.inside_delimiters.peek()
380 {
381 self.inside_delimiters.next();
382 } else {
383 break;
384 }
385 }
386 let span = span.with_hi(segments.last().unwrap().span.hi());
387 Some(AttrPath { segments: segments.into_boxed_slice(), span })
388 }
389 TokenTree::Token(Token { kind, .. }, _) if kind.is_delim() => None,
390 _ => {
391 None
394 }
395 }
396 }
397
398 fn value(&mut self) -> Option<MetaItemLit> {
399 match self.inside_delimiters.next() {
400 Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => {
401 MetaItemListParserContext {
402 inside_delimiters: inner_tokens.iter().peekable(),
403 dcx: self.dcx,
404 }
405 .value()
406 }
407 Some(TokenTree::Token(token, _)) => MetaItemLit::from_token(token),
408 _ => None,
409 }
410 }
411
412 fn next(&mut self) -> Option<MetaItemOrLitParser<'a>> {
424 if let Some(TokenTree::Token(token, _)) = self.inside_delimiters.peek()
426 && let Some(lit) = MetaItemLit::from_token(token)
427 {
428 self.inside_delimiters.next();
429 return Some(MetaItemOrLitParser::Lit(lit));
430 } else if let Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) =
431 self.inside_delimiters.peek()
432 {
433 self.inside_delimiters.next();
434 return MetaItemListParserContext {
435 inside_delimiters: inner_tokens.iter().peekable(),
436 dcx: self.dcx,
437 }
438 .next();
439 }
440
441 let path = self.next_path()?;
443
444 Some(MetaItemOrLitParser::MetaItemParser(match self.inside_delimiters.peek() {
449 Some(TokenTree::Delimited(dspan, _, Delimiter::Parenthesis, inner_tokens)) => {
450 self.inside_delimiters.next();
451
452 MetaItemParser {
453 path: PathParser::Attr(path),
454 args: ArgParser::List(MetaItemListParser::new_tts(
455 inner_tokens.iter(),
456 dspan.entire(),
457 self.dcx,
458 )),
459 }
460 }
461 Some(TokenTree::Delimited(_, ..)) => {
462 self.inside_delimiters.next();
463 return None;
465 }
466 Some(TokenTree::Token(Token { kind: token::Eq, span }, _)) => {
467 self.inside_delimiters.next();
468 let value = self.value()?;
469 MetaItemParser {
470 path: PathParser::Attr(path),
471 args: ArgParser::NameValue(NameValueParser {
472 eq_span: *span,
473 value_span: value.span,
474 value,
475 }),
476 }
477 }
478 _ => MetaItemParser { path: PathParser::Attr(path), args: ArgParser::NoArgs },
479 }))
480 }
481
482 fn parse(mut self, span: Span) -> MetaItemListParser<'a> {
483 let mut sub_parsers = Vec::new();
484
485 while !self.done() {
486 let Some(n) = self.next() else {
487 continue;
488 };
489 sub_parsers.push(n);
490
491 match self.inside_delimiters.peek() {
492 None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {
493 self.inside_delimiters.next();
494 }
495 Some(_) => {}
496 }
497 }
498
499 MetaItemListParser { sub_parsers, span }
500 }
501}
502
503#[derive(Debug, Clone)]
504pub struct MetaItemListParser<'a> {
505 sub_parsers: Vec<MetaItemOrLitParser<'a>>,
506 pub span: Span,
507}
508
509impl<'a> MetaItemListParser<'a> {
510 fn new(delim: &'a DelimArgs, dcx: DiagCtxtHandle<'a>) -> MetaItemListParser<'a> {
511 MetaItemListParser::new_tts(delim.tokens.iter(), delim.dspan.entire(), dcx)
512 }
513
514 fn new_tts(tts: TokenStreamIter<'a>, span: Span, dcx: DiagCtxtHandle<'a>) -> Self {
515 MetaItemListParserContext { inside_delimiters: tts.peekable(), dcx }.parse(span)
516 }
517
518 pub fn mixed(&self) -> impl Iterator<Item = &MetaItemOrLitParser<'a>> {
520 self.sub_parsers.iter()
521 }
522
523 pub fn len(&self) -> usize {
524 self.sub_parsers.len()
525 }
526
527 pub fn is_empty(&self) -> bool {
528 self.len() == 0
529 }
530
531 pub fn single(&self) -> Option<&MetaItemOrLitParser<'a>> {
535 let mut iter = self.mixed();
536 iter.next().filter(|_| iter.next().is_none())
537 }
538}