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