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 pub fn starts_with(&self, segments: &[Symbol]) -> bool {
96 segments.len() < self.len() && self.segments().zip(segments).all(|(a, b)| a.name == *b)
97 }
98}
99
100impl Display for PathParser<'_> {
101 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102 match self {
103 PathParser::Ast(path) => write!(f, "{}", pprust::path_to_string(path)),
104 PathParser::Attr(attr_path) => write!(f, "{attr_path}"),
105 }
106 }
107}
108
109#[derive(Clone, Debug)]
110#[must_use]
111pub enum ArgParser<'a> {
112 NoArgs,
113 List(MetaItemListParser<'a>),
114 NameValue(NameValueParser),
115}
116
117impl<'a> ArgParser<'a> {
118 pub fn span(&self) -> Option<Span> {
119 match self {
120 Self::NoArgs => None,
121 Self::List(l) => Some(l.span),
122 Self::NameValue(n) => Some(n.value_span.with_lo(n.eq_span.lo())),
123 }
124 }
125
126 pub fn from_attr_args<'sess>(value: &'a AttrArgs, dcx: DiagCtxtHandle<'sess>) -> Self {
127 match value {
128 AttrArgs::Empty => Self::NoArgs,
129 AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {
130 Self::List(MetaItemListParser::new(args, dcx))
131 }
132 AttrArgs::Delimited(args) => {
133 Self::List(MetaItemListParser { sub_parsers: vec![], span: args.dspan.entire() })
134 }
135 AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser {
136 eq_span: *eq_span,
137 value: expr_to_lit(dcx, &expr, *eq_span),
138 value_span: expr.span,
139 }),
140 }
141 }
142
143 pub fn list(&self) -> Option<&MetaItemListParser<'a>> {
150 match self {
151 Self::List(l) => Some(l),
152 Self::NameValue(_) | Self::NoArgs => None,
153 }
154 }
155
156 pub fn name_value(&self) -> Option<&NameValueParser> {
166 match self {
167 Self::NameValue(n) => Some(n),
168 Self::List(_) | Self::NoArgs => None,
169 }
170 }
171
172 pub fn no_args(&self) -> Result<(), Span> {
176 match self {
177 Self::NoArgs => Ok(()),
178 Self::List(args) => Err(args.span),
179 Self::NameValue(args) => Err(args.eq_span.to(args.value_span)),
180 }
181 }
182}
183
184#[derive(Debug, Clone)]
189pub enum MetaItemOrLitParser<'a> {
190 MetaItemParser(MetaItemParser<'a>),
191 Lit(MetaItemLit),
192 Err(Span, ErrorGuaranteed),
193}
194
195impl<'a> MetaItemOrLitParser<'a> {
196 pub fn span(&self) -> Span {
197 match self {
198 MetaItemOrLitParser::MetaItemParser(generic_meta_item_parser) => {
199 generic_meta_item_parser.span()
200 }
201 MetaItemOrLitParser::Lit(meta_item_lit) => meta_item_lit.span,
202 MetaItemOrLitParser::Err(span, _) => *span,
203 }
204 }
205
206 pub fn lit(&self) -> Option<&MetaItemLit> {
207 match self {
208 MetaItemOrLitParser::Lit(meta_item_lit) => Some(meta_item_lit),
209 _ => None,
210 }
211 }
212
213 pub fn meta_item(&self) -> Option<&MetaItemParser<'a>> {
214 match self {
215 MetaItemOrLitParser::MetaItemParser(parser) => Some(parser),
216 _ => None,
217 }
218 }
219}
220
221#[derive(Clone)]
235pub struct MetaItemParser<'a> {
236 path: PathParser<'a>,
237 args: ArgParser<'a>,
238}
239
240impl<'a> Debug for MetaItemParser<'a> {
241 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
242 f.debug_struct("MetaItemParser")
243 .field("path", &self.path)
244 .field("args", &self.args)
245 .finish()
246 }
247}
248
249impl<'a> MetaItemParser<'a> {
250 pub fn from_attr<'sess>(attr: &'a NormalAttr, dcx: DiagCtxtHandle<'sess>) -> Self {
253 Self {
254 path: PathParser::Ast(&attr.item.path),
255 args: ArgParser::from_attr_args(&attr.item.args, dcx),
256 }
257 }
258}
259
260impl<'a> MetaItemParser<'a> {
261 pub fn span(&self) -> Span {
262 if let Some(other) = self.args.span() {
263 self.path.span().with_hi(other.hi())
264 } else {
265 self.path.span()
266 }
267 }
268
269 pub fn path(&self) -> &PathParser<'a> {
275 &self.path
276 }
277
278 pub fn args(&self) -> &ArgParser<'a> {
280 &self.args
281 }
282
283 pub fn word_is(&self, sym: Symbol) -> Option<&ArgParser<'a>> {
290 self.path().word_is(sym).then(|| self.args())
291 }
292}
293
294#[derive(Clone)]
295pub struct NameValueParser {
296 pub eq_span: Span,
297 value: MetaItemLit,
298 pub value_span: Span,
299}
300
301impl Debug for NameValueParser {
302 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
303 f.debug_struct("NameValueParser")
304 .field("eq_span", &self.eq_span)
305 .field("value", &self.value)
306 .field("value_span", &self.value_span)
307 .finish()
308 }
309}
310
311impl NameValueParser {
312 pub fn value_as_lit(&self) -> &MetaItemLit {
313 &self.value
314 }
315
316 pub fn value_as_str(&self) -> Option<Symbol> {
317 self.value_as_lit().kind.str()
318 }
319}
320
321fn expr_to_lit(dcx: DiagCtxtHandle<'_>, expr: &Expr, span: Span) -> MetaItemLit {
322 if let ExprKind::Lit(token_lit) = expr.kind
325 && let Ok(lit) = MetaItemLit::from_token_lit(token_lit, expr.span)
326 {
327 lit
328 } else {
329 let guar = dcx.span_delayed_bug(
330 span,
331 "expr in place where literal is expected (builtin attr parsing)",
332 );
333 MetaItemLit { symbol: sym::dummy, suffix: None, kind: LitKind::Err(guar), span }
334 }
335}
336
337struct MetaItemListParserContext<'a, 'sess> {
338 inside_delimiters: Peekable<TokenStreamIter<'a>>,
340 dcx: DiagCtxtHandle<'sess>,
341}
342
343impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
344 fn done(&mut self) -> bool {
345 self.inside_delimiters.peek().is_none()
346 }
347
348 fn next_path(&mut self) -> Option<AttrPath> {
349 let tt = self.inside_delimiters.next().map(|tt| TokenTree::uninterpolate(tt));
351
352 match tt.as_deref()? {
353 &TokenTree::Token(
354 Token { kind: ref kind @ (token::Ident(..) | token::PathSep), span },
355 _,
356 ) => {
357 let mut segments = if let &token::Ident(name, _) = kind {
360 if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
362 self.inside_delimiters.peek()
363 {
364 self.inside_delimiters.next();
365 vec![Ident::new(name, span)]
366 } else {
367 return Some(AttrPath {
369 segments: vec![Ident::new(name, span)].into_boxed_slice(),
370 span,
371 });
372 }
373 } else {
374 vec![Ident::new(kw::PathRoot, span)]
376 };
377
378 loop {
380 if let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
382 self.inside_delimiters
383 .next()
384 .map(|tt| TokenTree::uninterpolate(tt))
385 .as_deref()
386 {
387 segments.push(Ident::new(name, span));
388 } else {
389 return None;
390 }
391 if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
393 self.inside_delimiters.peek()
394 {
395 self.inside_delimiters.next();
396 } else {
397 break;
398 }
399 }
400 let span = span.with_hi(segments.last().unwrap().span.hi());
401 Some(AttrPath { segments: segments.into_boxed_slice(), span })
402 }
403 TokenTree::Token(Token { kind, .. }, _) if kind.is_delim() => None,
404 _ => {
405 None
408 }
409 }
410 }
411
412 fn value(&mut self) -> Option<MetaItemLit> {
413 match self.inside_delimiters.next() {
414 Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => {
415 MetaItemListParserContext {
416 inside_delimiters: inner_tokens.iter().peekable(),
417 dcx: self.dcx,
418 }
419 .value()
420 }
421 Some(TokenTree::Token(token, _)) => MetaItemLit::from_token(token),
422 _ => None,
423 }
424 }
425
426 fn next(&mut self) -> Option<MetaItemOrLitParser<'a>> {
438 if let Some(TokenTree::Token(token, _)) = self.inside_delimiters.peek()
440 && let Some(lit) = MetaItemLit::from_token(token)
441 {
442 self.inside_delimiters.next();
443 return Some(MetaItemOrLitParser::Lit(lit));
444 } else if let Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) =
445 self.inside_delimiters.peek()
446 {
447 self.inside_delimiters.next();
448 return MetaItemListParserContext {
449 inside_delimiters: inner_tokens.iter().peekable(),
450 dcx: self.dcx,
451 }
452 .next();
453 }
454
455 let path = self.next_path()?;
457
458 Some(MetaItemOrLitParser::MetaItemParser(match self.inside_delimiters.peek() {
463 Some(TokenTree::Delimited(dspan, _, Delimiter::Parenthesis, inner_tokens)) => {
464 self.inside_delimiters.next();
465
466 MetaItemParser {
467 path: PathParser::Attr(path),
468 args: ArgParser::List(MetaItemListParser::new_tts(
469 inner_tokens.iter(),
470 dspan.entire(),
471 self.dcx,
472 )),
473 }
474 }
475 Some(TokenTree::Delimited(_, ..)) => {
476 self.inside_delimiters.next();
477 return None;
479 }
480 Some(TokenTree::Token(Token { kind: token::Eq, span }, _)) => {
481 self.inside_delimiters.next();
482 let value = self.value()?;
483 MetaItemParser {
484 path: PathParser::Attr(path),
485 args: ArgParser::NameValue(NameValueParser {
486 eq_span: *span,
487 value_span: value.span,
488 value,
489 }),
490 }
491 }
492 _ => MetaItemParser { path: PathParser::Attr(path), args: ArgParser::NoArgs },
493 }))
494 }
495
496 fn parse(mut self, span: Span) -> MetaItemListParser<'a> {
497 let mut sub_parsers = Vec::new();
498
499 while !self.done() {
500 let Some(n) = self.next() else {
501 continue;
502 };
503 sub_parsers.push(n);
504
505 match self.inside_delimiters.peek() {
506 None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {
507 self.inside_delimiters.next();
508 }
509 Some(_) => {}
510 }
511 }
512
513 MetaItemListParser { sub_parsers, span }
514 }
515}
516
517#[derive(Debug, Clone)]
518pub struct MetaItemListParser<'a> {
519 sub_parsers: Vec<MetaItemOrLitParser<'a>>,
520 pub span: Span,
521}
522
523impl<'a> MetaItemListParser<'a> {
524 fn new<'sess>(delim: &'a DelimArgs, dcx: DiagCtxtHandle<'sess>) -> Self {
525 MetaItemListParser::new_tts(delim.tokens.iter(), delim.dspan.entire(), dcx)
526 }
527
528 fn new_tts<'sess>(tts: TokenStreamIter<'a>, span: Span, dcx: DiagCtxtHandle<'sess>) -> Self {
529 MetaItemListParserContext { inside_delimiters: tts.peekable(), dcx }.parse(span)
530 }
531
532 pub fn mixed(&self) -> impl Iterator<Item = &MetaItemOrLitParser<'a>> {
534 self.sub_parsers.iter()
535 }
536
537 pub fn len(&self) -> usize {
538 self.sub_parsers.len()
539 }
540
541 pub fn is_empty(&self) -> bool {
542 self.len() == 0
543 }
544
545 pub fn single(&self) -> Option<&MetaItemOrLitParser<'a>> {
549 let mut iter = self.mixed();
550 iter.next().filter(|_| iter.next().is_none())
551 }
552}