1use std::borrow::Borrow;
7use std::fmt::{Debug, Display};
8
9use rustc_ast::token::{self, Delimiter, MetaVarKind};
10use rustc_ast::tokenstream::TokenStream;
11use rustc_ast::{
12 AttrArgs, Expr, ExprKind, LitKind, MetaItemLit, Path, PathSegment, StmtKind, UnOp,
13};
14use rustc_ast_pretty::pprust;
15use rustc_errors::{Diag, PResult};
16use rustc_hir::{self as hir, AttrPath};
17use rustc_parse::exp;
18use rustc_parse::parser::{ForceCollect, Parser, PathStyle, token_descr};
19use rustc_session::errors::{create_lit_error, report_lit_error};
20use rustc_session::parse::ParseSess;
21use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, sym};
22use thin_vec::ThinVec;
23
24use crate::ShouldEmit;
25use crate::session_diagnostics::{
26 InvalidMetaItem, InvalidMetaItemQuoteIdentSugg, InvalidMetaItemRemoveNegSugg, MetaBadDelim,
27 MetaBadDelimSugg, SuffixedLiteralInAttribute,
28};
29
30#[derive(Clone, Debug)]
31pub struct PathParser<P: Borrow<Path>>(pub P);
32
33pub type OwnedPathParser = PathParser<Path>;
34pub type RefPathParser<'p> = PathParser<&'p Path>;
35
36impl<P: Borrow<Path>> PathParser<P> {
37 pub fn get_attribute_path(&self) -> hir::AttrPath {
38 AttrPath {
39 segments: self.segments().copied().collect::<Vec<_>>().into_boxed_slice(),
40 span: self.span(),
41 }
42 }
43
44 pub fn segments(&self) -> impl Iterator<Item = &Ident> {
45 self.0.borrow().segments.iter().map(|seg| &seg.ident)
46 }
47
48 pub fn span(&self) -> Span {
49 self.0.borrow().span
50 }
51
52 pub fn len(&self) -> usize {
53 self.0.borrow().segments.len()
54 }
55
56 pub fn segments_is(&self, segments: &[Symbol]) -> bool {
57 self.segments().map(|segment| &segment.name).eq(segments)
58 }
59
60 pub fn word(&self) -> Option<Ident> {
61 (self.len() == 1).then(|| **self.segments().next().as_ref().unwrap())
62 }
63
64 pub fn word_sym(&self) -> Option<Symbol> {
65 self.word().map(|ident| ident.name)
66 }
67
68 pub fn word_is(&self, sym: Symbol) -> bool {
72 self.word().map(|i| i.name == sym).unwrap_or(false)
73 }
74
75 pub fn starts_with(&self, segments: &[Symbol]) -> bool {
80 segments.len() < self.len() && self.segments().zip(segments).all(|(a, b)| a.name == *b)
81 }
82}
83
84impl<P: Borrow<Path>> Display for PathParser<P> {
85 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 write!(f, "{}", pprust::path_to_string(self.0.borrow()))
87 }
88}
89
90#[derive(Clone, Debug)]
91#[must_use]
92pub enum ArgParser {
93 NoArgs,
94 List(MetaItemListParser),
95 NameValue(NameValueParser),
96}
97
98impl ArgParser {
99 pub fn span(&self) -> Option<Span> {
100 match self {
101 Self::NoArgs => None,
102 Self::List(l) => Some(l.span),
103 Self::NameValue(n) => Some(n.value_span.with_lo(n.eq_span.lo())),
104 }
105 }
106
107 pub fn from_attr_args<'sess>(
108 value: &AttrArgs,
109 parts: &[Symbol],
110 psess: &'sess ParseSess,
111 should_emit: ShouldEmit,
112 ) -> Option<Self> {
113 Some(match value {
114 AttrArgs::Empty => Self::NoArgs,
115 AttrArgs::Delimited(args) => {
116 if parts == &[sym::rustc_dummy] {
118 return Some(ArgParser::List(MetaItemListParser {
119 sub_parsers: ThinVec::new(),
120 span: args.dspan.entire(),
121 }));
122 }
123
124 if args.delim != Delimiter::Parenthesis {
125 psess.dcx().emit_err(MetaBadDelim {
126 span: args.dspan.entire(),
127 sugg: MetaBadDelimSugg { open: args.dspan.open, close: args.dspan.close },
128 });
129 return None;
130 }
131
132 Self::List(
133 MetaItemListParser::new(&args.tokens, args.dspan.entire(), psess, should_emit)
134 .map_err(|e| should_emit.emit_err(e))
135 .ok()?,
136 )
137 }
138 AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser {
139 eq_span: *eq_span,
140 value: expr_to_lit(psess, &expr, expr.span, should_emit)?,
141 value_span: expr.span,
142 }),
143 })
144 }
145
146 pub fn list(&self) -> Option<&MetaItemListParser> {
153 match self {
154 Self::List(l) => Some(l),
155 Self::NameValue(_) | Self::NoArgs => None,
156 }
157 }
158
159 pub fn name_value(&self) -> Option<&NameValueParser> {
169 match self {
170 Self::NameValue(n) => Some(n),
171 Self::List(_) | Self::NoArgs => None,
172 }
173 }
174
175 pub fn no_args(&self) -> Result<(), Span> {
179 match self {
180 Self::NoArgs => Ok(()),
181 Self::List(args) => Err(args.span),
182 Self::NameValue(args) => Err(args.args_span()),
183 }
184 }
185}
186
187#[derive(Debug, Clone)]
192pub enum MetaItemOrLitParser {
193 MetaItemParser(MetaItemParser),
194 Lit(MetaItemLit),
195 Err(Span, ErrorGuaranteed),
196}
197
198impl MetaItemOrLitParser {
199 pub fn parse_single<'sess>(
200 parser: &mut Parser<'sess>,
201 should_emit: ShouldEmit,
202 ) -> PResult<'sess, MetaItemOrLitParser> {
203 let mut this = MetaItemListParserContext { parser, should_emit };
204 this.parse_meta_item_inner()
205 }
206
207 pub fn span(&self) -> Span {
208 match self {
209 MetaItemOrLitParser::MetaItemParser(generic_meta_item_parser) => {
210 generic_meta_item_parser.span()
211 }
212 MetaItemOrLitParser::Lit(meta_item_lit) => meta_item_lit.span,
213 MetaItemOrLitParser::Err(span, _) => *span,
214 }
215 }
216
217 pub fn lit(&self) -> Option<&MetaItemLit> {
218 match self {
219 MetaItemOrLitParser::Lit(meta_item_lit) => Some(meta_item_lit),
220 _ => None,
221 }
222 }
223
224 pub fn meta_item(&self) -> Option<&MetaItemParser> {
225 match self {
226 MetaItemOrLitParser::MetaItemParser(parser) => Some(parser),
227 _ => None,
228 }
229 }
230}
231
232#[derive(Clone)]
246pub struct MetaItemParser {
247 path: OwnedPathParser,
248 args: ArgParser,
249}
250
251impl Debug for MetaItemParser {
252 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
253 f.debug_struct("MetaItemParser")
254 .field("path", &self.path)
255 .field("args", &self.args)
256 .finish()
257 }
258}
259
260impl MetaItemParser {
261 pub fn ident(&self) -> Option<Ident> {
263 if let [PathSegment { ident, .. }] = self.path.0.segments[..] { Some(ident) } else { None }
264 }
265
266 pub fn span(&self) -> Span {
267 if let Some(other) = self.args.span() {
268 self.path.borrow().span().with_hi(other.hi())
269 } else {
270 self.path.borrow().span()
271 }
272 }
273
274 pub fn path(&self) -> &OwnedPathParser {
280 &self.path
281 }
282
283 pub fn args(&self) -> &ArgParser {
285 &self.args
286 }
287
288 pub fn word_is(&self, sym: Symbol) -> Option<&ArgParser> {
295 self.path().word_is(sym).then(|| self.args())
296 }
297}
298
299#[derive(Clone)]
300pub struct NameValueParser {
301 pub eq_span: Span,
302 value: MetaItemLit,
303 pub value_span: Span,
304}
305
306impl Debug for NameValueParser {
307 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
308 f.debug_struct("NameValueParser")
309 .field("eq_span", &self.eq_span)
310 .field("value", &self.value)
311 .field("value_span", &self.value_span)
312 .finish()
313 }
314}
315
316impl NameValueParser {
317 pub fn value_as_lit(&self) -> &MetaItemLit {
318 &self.value
319 }
320
321 pub fn value_as_str(&self) -> Option<Symbol> {
322 self.value_as_lit().kind.str()
323 }
324
325 pub fn args_span(&self) -> Span {
326 self.eq_span.to(self.value_span)
327 }
328}
329
330fn expr_to_lit(
331 psess: &ParseSess,
332 expr: &Expr,
333 span: Span,
334 should_emit: ShouldEmit,
335) -> Option<MetaItemLit> {
336 if let ExprKind::Lit(token_lit) = expr.kind {
337 let res = MetaItemLit::from_token_lit(token_lit, expr.span);
338 match res {
339 Ok(lit) => {
340 if token_lit.suffix.is_some() {
341 should_emit.emit_err(
342 psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
343 );
344 None
345 } else {
346 if !lit.kind.is_unsuffixed() {
347 should_emit.emit_err(
349 psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
350 );
351 }
352
353 Some(lit)
354 }
355 }
356 Err(err) => {
357 let guar = report_lit_error(psess, err, token_lit, expr.span);
358 let lit = MetaItemLit {
359 symbol: token_lit.symbol,
360 suffix: token_lit.suffix,
361 kind: LitKind::Err(guar),
362 span: expr.span,
363 };
364 Some(lit)
365 }
366 }
367 } else {
368 if matches!(should_emit, ShouldEmit::Nothing) {
369 return None;
370 }
371
372 let msg = "attribute value must be a literal";
379 let err = psess.dcx().struct_span_err(span, msg);
380 should_emit.emit_err(err);
381 None
382 }
383}
384
385struct MetaItemListParserContext<'a, 'sess> {
386 parser: &'a mut Parser<'sess>,
387 should_emit: ShouldEmit,
388}
389
390impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
391 fn parse_unsuffixed_meta_item_lit(&mut self) -> PResult<'sess, MetaItemLit> {
392 let Some(token_lit) = self.parser.eat_token_lit() else { return Err(self.expected_lit()) };
393 self.unsuffixed_meta_item_from_lit(token_lit)
394 }
395
396 fn unsuffixed_meta_item_from_lit(
397 &mut self,
398 token_lit: token::Lit,
399 ) -> PResult<'sess, MetaItemLit> {
400 let lit = match MetaItemLit::from_token_lit(token_lit, self.parser.prev_token.span) {
401 Ok(lit) => lit,
402 Err(err) => {
403 return Err(create_lit_error(
404 &self.parser.psess,
405 err,
406 token_lit,
407 self.parser.prev_token_uninterpolated_span(),
408 ));
409 }
410 };
411
412 if !lit.kind.is_unsuffixed() {
413 self.should_emit.emit_err(
415 self.parser.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
416 );
417 }
418
419 Ok(lit)
420 }
421
422 fn parse_attr_item(&mut self) -> PResult<'sess, MetaItemParser> {
423 if let Some(MetaVarKind::Meta { has_meta_form }) = self.parser.token.is_metavar_seq() {
424 return if has_meta_form {
425 let attr_item = self
426 .parser
427 .eat_metavar_seq(MetaVarKind::Meta { has_meta_form: true }, |this| {
428 MetaItemListParserContext { parser: this, should_emit: self.should_emit }
429 .parse_attr_item()
430 })
431 .unwrap();
432 Ok(attr_item)
433 } else {
434 self.parser.unexpected_any()
435 };
436 }
437
438 let path = self.parser.parse_path(PathStyle::Mod)?;
439
440 let args = if self.parser.check(exp!(OpenParen)) {
442 let start = self.parser.token.span;
443 let (sub_parsers, _) = self.parser.parse_paren_comma_seq(|parser| {
444 MetaItemListParserContext { parser, should_emit: self.should_emit }
445 .parse_meta_item_inner()
446 })?;
447 let end = self.parser.prev_token.span;
448 ArgParser::List(MetaItemListParser { sub_parsers, span: start.with_hi(end.hi()) })
449 } else if self.parser.eat(exp!(Eq)) {
450 let eq_span = self.parser.prev_token.span;
451 let value = self.parse_unsuffixed_meta_item_lit()?;
452
453 ArgParser::NameValue(NameValueParser { eq_span, value, value_span: value.span })
454 } else {
455 ArgParser::NoArgs
456 };
457
458 Ok(MetaItemParser { path: PathParser(path), args })
459 }
460
461 fn parse_meta_item_inner(&mut self) -> PResult<'sess, MetaItemOrLitParser> {
462 if let Some(token_lit) = self.parser.eat_token_lit() {
463 Ok(MetaItemOrLitParser::Lit(self.unsuffixed_meta_item_from_lit(token_lit)?))
465 } else {
466 let prev_pros = self.parser.approx_token_stream_pos();
467 match self.parse_attr_item() {
468 Ok(item) => Ok(MetaItemOrLitParser::MetaItemParser(item)),
469 Err(err) => {
470 if self.parser.approx_token_stream_pos() != prev_pros {
473 Err(err)
474 } else {
475 err.cancel();
476 Err(self.expected_lit())
477 }
478 }
479 }
480 }
481 }
482
483 fn expected_lit(&mut self) -> Diag<'sess> {
484 let mut err = InvalidMetaItem {
485 span: self.parser.token.span,
486 descr: token_descr(&self.parser.token),
487 quote_ident_sugg: None,
488 remove_neg_sugg: None,
489 label: None,
490 };
491
492 if let token::OpenInvisible(_) = self.parser.token.kind {
493 return self.parser.dcx().create_err(err);
495 }
496
497 let snapshot = self.parser.create_snapshot_for_diagnostic();
501 let stmt = self.parser.parse_stmt_without_recovery(false, ForceCollect::No, false);
502 match stmt {
503 Ok(Some(stmt)) => {
504 err.descr = stmt.kind.descr().to_string();
507 err.label = Some(stmt.span);
508 err.span = stmt.span;
509 if let StmtKind::Expr(expr) = &stmt.kind
510 && let ExprKind::Unary(UnOp::Neg, val) = &expr.kind
511 && let ExprKind::Lit(_) = val.kind
512 {
513 err.remove_neg_sugg = Some(InvalidMetaItemRemoveNegSugg {
514 negative_sign: expr.span.until(val.span),
515 });
516 } else if let StmtKind::Expr(expr) = &stmt.kind
517 && let ExprKind::Path(None, Path { segments, .. }) = &expr.kind
518 && segments.len() == 1
519 {
520 while let token::Ident(..) | token::Literal(_) | token::Dot =
521 self.parser.token.kind
522 {
523 self.parser.bump();
526 }
527 err.quote_ident_sugg = Some(InvalidMetaItemQuoteIdentSugg {
528 before: expr.span.shrink_to_lo(),
529 after: self.parser.prev_token.span.shrink_to_hi(),
530 });
531 }
532 }
533 Ok(None) => {}
534 Err(e) => {
535 e.cancel();
536 self.parser.restore_snapshot(snapshot);
537 }
538 }
539
540 self.parser.dcx().create_err(err)
541 }
542
543 fn parse(
544 tokens: TokenStream,
545 psess: &'sess ParseSess,
546 span: Span,
547 should_emit: ShouldEmit,
548 ) -> PResult<'sess, MetaItemListParser> {
549 let mut parser = Parser::new(psess, tokens, None);
550 let mut this = MetaItemListParserContext { parser: &mut parser, should_emit };
551
552 let mut sub_parsers = ThinVec::with_capacity(1);
554 while this.parser.token != token::Eof {
555 sub_parsers.push(this.parse_meta_item_inner()?);
556
557 if !this.parser.eat(exp!(Comma)) {
558 break;
559 }
560 }
561
562 if parser.token != token::Eof {
563 parser.unexpected()?;
564 }
565
566 Ok(MetaItemListParser { sub_parsers, span })
567 }
568}
569
570#[derive(Debug, Clone)]
571pub struct MetaItemListParser {
572 sub_parsers: ThinVec<MetaItemOrLitParser>,
573 pub span: Span,
574}
575
576impl MetaItemListParser {
577 pub(crate) fn new<'sess>(
578 tokens: &TokenStream,
579 span: Span,
580 psess: &'sess ParseSess,
581 should_emit: ShouldEmit,
582 ) -> Result<Self, Diag<'sess>> {
583 MetaItemListParserContext::parse(tokens.clone(), psess, span, should_emit)
584 }
585
586 pub fn mixed(&self) -> impl Iterator<Item = &MetaItemOrLitParser> {
588 self.sub_parsers.iter()
589 }
590
591 pub fn len(&self) -> usize {
592 self.sub_parsers.len()
593 }
594
595 pub fn is_empty(&self) -> bool {
596 self.len() == 0
597 }
598
599 pub fn single(&self) -> Option<&MetaItemOrLitParser> {
603 let mut iter = self.mixed();
604 iter.next().filter(|_| iter.next().is_none())
605 }
606}