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, DelimArgs, 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.len() == segments.len() && self.segments().zip(segments).all(|(a, b)| a.name == *b)
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(MetaItemListParser::new(args, psess, should_emit)?)
128 }
129 AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser {
130 eq_span: *eq_span,
131 value: expr_to_lit(psess, &expr, expr.span, should_emit)?,
132 value_span: expr.span,
133 }),
134 })
135 }
136
137 pub fn list(&self) -> Option<&MetaItemListParser<'a>> {
144 match self {
145 Self::List(l) => Some(l),
146 Self::NameValue(_) | Self::NoArgs => None,
147 }
148 }
149
150 pub fn name_value(&self) -> Option<&NameValueParser> {
160 match self {
161 Self::NameValue(n) => Some(n),
162 Self::List(_) | Self::NoArgs => None,
163 }
164 }
165
166 pub fn no_args(&self) -> Result<(), Span> {
170 match self {
171 Self::NoArgs => Ok(()),
172 Self::List(args) => Err(args.span),
173 Self::NameValue(args) => Err(args.eq_span.to(args.value_span)),
174 }
175 }
176}
177
178#[derive(Debug, Clone)]
183pub enum MetaItemOrLitParser<'a> {
184 MetaItemParser(MetaItemParser<'a>),
185 Lit(MetaItemLit),
186 Err(Span, ErrorGuaranteed),
187}
188
189impl<'a> MetaItemOrLitParser<'a> {
190 pub fn span(&self) -> Span {
191 match self {
192 MetaItemOrLitParser::MetaItemParser(generic_meta_item_parser) => {
193 generic_meta_item_parser.span()
194 }
195 MetaItemOrLitParser::Lit(meta_item_lit) => meta_item_lit.span,
196 MetaItemOrLitParser::Err(span, _) => *span,
197 }
198 }
199
200 pub fn lit(&self) -> Option<&MetaItemLit> {
201 match self {
202 MetaItemOrLitParser::Lit(meta_item_lit) => Some(meta_item_lit),
203 _ => None,
204 }
205 }
206
207 pub fn meta_item(&self) -> Option<&MetaItemParser<'a>> {
208 match self {
209 MetaItemOrLitParser::MetaItemParser(parser) => Some(parser),
210 _ => None,
211 }
212 }
213}
214
215#[derive(Clone)]
229pub struct MetaItemParser<'a> {
230 path: PathParser<'a>,
231 args: ArgParser<'a>,
232}
233
234impl<'a> Debug for MetaItemParser<'a> {
235 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
236 f.debug_struct("MetaItemParser")
237 .field("path", &self.path)
238 .field("args", &self.args)
239 .finish()
240 }
241}
242
243impl<'a> MetaItemParser<'a> {
244 pub fn from_attr<'sess>(
247 attr: &'a NormalAttr,
248 parts: &[Symbol],
249 psess: &'sess ParseSess,
250 should_emit: ShouldEmit,
251 ) -> Option<Self> {
252 Some(Self {
253 path: PathParser(Cow::Borrowed(&attr.item.path)),
254 args: ArgParser::from_attr_args(&attr.item.args, parts, psess, should_emit)?,
255 })
256 }
257}
258
259impl<'a> MetaItemParser<'a> {
260 pub fn span(&self) -> Span {
261 if let Some(other) = self.args.span() {
262 self.path.span().with_hi(other.hi())
263 } else {
264 self.path.span()
265 }
266 }
267
268 pub fn path(&self) -> &PathParser<'a> {
274 &self.path
275 }
276
277 pub fn args(&self) -> &ArgParser<'a> {
279 &self.args
280 }
281
282 pub fn word_is(&self, sym: Symbol) -> Option<&ArgParser<'a>> {
289 self.path().word_is(sym).then(|| self.args())
290 }
291}
292
293#[derive(Clone)]
294pub struct NameValueParser {
295 pub eq_span: Span,
296 value: MetaItemLit,
297 pub value_span: Span,
298}
299
300impl Debug for NameValueParser {
301 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
302 f.debug_struct("NameValueParser")
303 .field("eq_span", &self.eq_span)
304 .field("value", &self.value)
305 .field("value_span", &self.value_span)
306 .finish()
307 }
308}
309
310impl NameValueParser {
311 pub fn value_as_lit(&self) -> &MetaItemLit {
312 &self.value
313 }
314
315 pub fn value_as_str(&self) -> Option<Symbol> {
316 self.value_as_lit().kind.str()
317 }
318}
319
320fn expr_to_lit(
321 psess: &ParseSess,
322 expr: &Expr,
323 span: Span,
324 should_emit: ShouldEmit,
325) -> Option<MetaItemLit> {
326 if let ExprKind::Lit(token_lit) = expr.kind {
327 let res = MetaItemLit::from_token_lit(token_lit, expr.span);
328 match res {
329 Ok(lit) => {
330 if token_lit.suffix.is_some() {
331 should_emit.emit_err(
332 psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
333 );
334 None
335 } else {
336 if !lit.kind.is_unsuffixed() {
337 should_emit.emit_err(
339 psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
340 );
341 }
342
343 Some(lit)
344 }
345 }
346 Err(err) => {
347 let guar = report_lit_error(psess, err, token_lit, expr.span);
348 let lit = MetaItemLit {
349 symbol: token_lit.symbol,
350 suffix: token_lit.suffix,
351 kind: LitKind::Err(guar),
352 span: expr.span,
353 };
354 Some(lit)
355 }
356 }
357 } else {
358 if matches!(should_emit, ShouldEmit::Nothing) {
359 return None;
360 }
361
362 let msg = "attribute value must be a literal";
369 let err = psess.dcx().struct_span_err(span, msg);
370 should_emit.emit_err(err);
371 None
372 }
373}
374
375struct MetaItemListParserContext<'a, 'sess> {
376 parser: &'a mut Parser<'sess>,
377 should_emit: ShouldEmit,
378}
379
380impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
381 fn parse_unsuffixed_meta_item_lit(&mut self) -> PResult<'sess, MetaItemLit> {
382 let Some(token_lit) = self.parser.eat_token_lit() else { return Err(self.expected_lit()) };
383 self.unsuffixed_meta_item_from_lit(token_lit)
384 }
385
386 fn unsuffixed_meta_item_from_lit(
387 &mut self,
388 token_lit: token::Lit,
389 ) -> PResult<'sess, MetaItemLit> {
390 let lit = match MetaItemLit::from_token_lit(token_lit, self.parser.prev_token.span) {
391 Ok(lit) => lit,
392 Err(err) => {
393 return Err(create_lit_error(
394 &self.parser.psess,
395 err,
396 token_lit,
397 self.parser.prev_token_uninterpolated_span(),
398 ));
399 }
400 };
401
402 if !lit.kind.is_unsuffixed() {
403 self.should_emit.emit_err(
405 self.parser.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
406 );
407 }
408
409 Ok(lit)
410 }
411
412 fn parse_attr_item(&mut self) -> PResult<'sess, MetaItemParser<'static>> {
413 if let Some(MetaVarKind::Meta { has_meta_form }) = self.parser.token.is_metavar_seq() {
414 return if has_meta_form {
415 let attr_item = self
416 .parser
417 .eat_metavar_seq(MetaVarKind::Meta { has_meta_form: true }, |this| {
418 MetaItemListParserContext { parser: this, should_emit: self.should_emit }
419 .parse_attr_item()
420 })
421 .unwrap();
422 Ok(attr_item)
423 } else {
424 self.parser.unexpected_any()
425 };
426 }
427
428 let path = self.parser.parse_path(PathStyle::Mod)?;
429
430 let args = if self.parser.check(exp!(OpenParen)) {
432 let start = self.parser.token.span;
433 let (sub_parsers, _) = self.parser.parse_paren_comma_seq(|parser| {
434 MetaItemListParserContext { parser, should_emit: self.should_emit }
435 .parse_meta_item_inner()
436 })?;
437 let end = self.parser.prev_token.span;
438 ArgParser::List(MetaItemListParser { sub_parsers, span: start.with_hi(end.hi()) })
439 } else if self.parser.eat(exp!(Eq)) {
440 let eq_span = self.parser.prev_token.span;
441 let value = self.parse_unsuffixed_meta_item_lit()?;
442
443 ArgParser::NameValue(NameValueParser { eq_span, value, value_span: value.span })
444 } else {
445 ArgParser::NoArgs
446 };
447
448 Ok(MetaItemParser { path: PathParser(Cow::Owned(path)), args })
449 }
450
451 fn parse_meta_item_inner(&mut self) -> PResult<'sess, MetaItemOrLitParser<'static>> {
452 if let Some(token_lit) = self.parser.eat_token_lit() {
453 Ok(MetaItemOrLitParser::Lit(self.unsuffixed_meta_item_from_lit(token_lit)?))
455 } else {
456 let prev_pros = self.parser.approx_token_stream_pos();
457 match self.parse_attr_item() {
458 Ok(item) => Ok(MetaItemOrLitParser::MetaItemParser(item)),
459 Err(err) => {
460 if self.parser.approx_token_stream_pos() != prev_pros {
463 Err(err)
464 } else {
465 err.cancel();
466 Err(self.expected_lit())
467 }
468 }
469 }
470 }
471 }
472
473 fn expected_lit(&mut self) -> Diag<'sess> {
474 let mut err = InvalidMetaItem {
475 span: self.parser.token.span,
476 descr: token_descr(&self.parser.token),
477 quote_ident_sugg: None,
478 remove_neg_sugg: None,
479 };
480
481 if self.parser.prev_token == token::Eq
485 && let token::Ident(..) = self.parser.token.kind
486 {
487 let before = self.parser.token.span.shrink_to_lo();
488 while let token::Ident(..) = self.parser.token.kind {
489 self.parser.bump();
490 }
491 err.quote_ident_sugg = Some(InvalidMetaItemQuoteIdentSugg {
492 before,
493 after: self.parser.prev_token.span.shrink_to_hi(),
494 });
495 }
496
497 if self.parser.token == token::Minus
498 && self
499 .parser
500 .look_ahead(1, |t| matches!(t.kind, rustc_ast::token::TokenKind::Literal { .. }))
501 {
502 err.remove_neg_sugg =
503 Some(InvalidMetaItemRemoveNegSugg { negative_sign: self.parser.token.span });
504 self.parser.bump();
505 self.parser.bump();
506 }
507
508 self.parser.dcx().create_err(err)
509 }
510
511 fn parse(
512 tokens: TokenStream,
513 psess: &'sess ParseSess,
514 span: Span,
515 should_emit: ShouldEmit,
516 ) -> PResult<'sess, MetaItemListParser<'static>> {
517 let mut parser = Parser::new(psess, tokens, None);
518 let mut this = MetaItemListParserContext { parser: &mut parser, should_emit };
519
520 let mut sub_parsers = ThinVec::with_capacity(1);
522 while this.parser.token != token::Eof {
523 sub_parsers.push(this.parse_meta_item_inner()?);
524
525 if !this.parser.eat(exp!(Comma)) {
526 break;
527 }
528 }
529
530 if parser.token != token::Eof {
531 parser.unexpected()?;
532 }
533
534 Ok(MetaItemListParser { sub_parsers, span })
535 }
536}
537
538#[derive(Debug, Clone)]
539pub struct MetaItemListParser<'a> {
540 sub_parsers: ThinVec<MetaItemOrLitParser<'a>>,
541 pub span: Span,
542}
543
544impl<'a> MetaItemListParser<'a> {
545 fn new<'sess>(
546 delim: &'a DelimArgs,
547 psess: &'sess ParseSess,
548 should_emit: ShouldEmit,
549 ) -> Option<Self> {
550 match MetaItemListParserContext::parse(
551 delim.tokens.clone(),
552 psess,
553 delim.dspan.entire(),
554 should_emit,
555 ) {
556 Ok(s) => Some(s),
557 Err(e) => {
558 should_emit.emit_err(e);
559 None
560 }
561 }
562 }
563
564 pub fn mixed(&self) -> impl Iterator<Item = &MetaItemOrLitParser<'a>> {
566 self.sub_parsers.iter()
567 }
568
569 pub fn len(&self) -> usize {
570 self.sub_parsers.len()
571 }
572
573 pub fn is_empty(&self) -> bool {
574 self.len() == 0
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}