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::{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(#[automatically_derived]
impl<P: ::core::clone::Clone + Borrow<Path>> ::core::clone::Clone for
PathParser<P> {
#[inline]
fn clone(&self) -> PathParser<P> {
PathParser(::core::clone::Clone::clone(&self.0))
}
}Clone, #[automatically_derived]
impl<P: ::core::fmt::Debug + Borrow<Path>> ::core::fmt::Debug for
PathParser<P> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f, "PathParser",
&&self.0)
}
}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().map(|s| s.name).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 f.write_fmt(format_args!("{0}", pprust::path_to_string(self.0.borrow())))write!(f, "{}", pprust::path_to_string(self.0.borrow()))
87 }
88}
89
90#[derive(#[automatically_derived]
impl ::core::clone::Clone for ArgParser {
#[inline]
fn clone(&self) -> ArgParser {
match self {
ArgParser::NoArgs => ArgParser::NoArgs,
ArgParser::List(__self_0) =>
ArgParser::List(::core::clone::Clone::clone(__self_0)),
ArgParser::NameValue(__self_0) =>
ArgParser::NameValue(::core::clone::Clone::clone(__self_0)),
}
}
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for ArgParser {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
ArgParser::NoArgs =>
::core::fmt::Formatter::write_str(f, "NoArgs"),
ArgParser::List(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "List",
&__self_0),
ArgParser::NameValue(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"NameValue", &__self_0),
}
}
}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]
120 || parts == &[sym::diagnostic, sym::do_not_recommend]
121 {
122 return Some(ArgParser::List(MetaItemListParser {
123 sub_parsers: ThinVec::new(),
124 span: args.dspan.entire(),
125 }));
126 }
127
128 if args.delim != Delimiter::Parenthesis {
129 should_emit.emit_err(psess.dcx().create_err(MetaBadDelim {
130 span: args.dspan.entire(),
131 sugg: MetaBadDelimSugg { open: args.dspan.open, close: args.dspan.close },
132 }));
133 return None;
134 }
135
136 Self::List(
137 MetaItemListParser::new(&args.tokens, args.dspan.entire(), psess, should_emit)
138 .map_err(|e| should_emit.emit_err(e))
139 .ok()?,
140 )
141 }
142 AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser {
143 eq_span: *eq_span,
144 value: expr_to_lit(psess, &expr, expr.span, should_emit)?,
145 value_span: expr.span,
146 }),
147 })
148 }
149
150 pub fn list(&self) -> Option<&MetaItemListParser> {
157 match self {
158 Self::List(l) => Some(l),
159 Self::NameValue(_) | Self::NoArgs => None,
160 }
161 }
162
163 pub fn name_value(&self) -> Option<&NameValueParser> {
173 match self {
174 Self::NameValue(n) => Some(n),
175 Self::List(_) | Self::NoArgs => None,
176 }
177 }
178
179 pub fn no_args(&self) -> Result<(), Span> {
183 match self {
184 Self::NoArgs => Ok(()),
185 Self::List(args) => Err(args.span),
186 Self::NameValue(args) => Err(args.args_span()),
187 }
188 }
189}
190
191#[derive(#[automatically_derived]
impl ::core::fmt::Debug for MetaItemOrLitParser {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
MetaItemOrLitParser::MetaItemParser(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"MetaItemParser", &__self_0),
MetaItemOrLitParser::Lit(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Lit",
&__self_0),
}
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for MetaItemOrLitParser {
#[inline]
fn clone(&self) -> MetaItemOrLitParser {
match self {
MetaItemOrLitParser::MetaItemParser(__self_0) =>
MetaItemOrLitParser::MetaItemParser(::core::clone::Clone::clone(__self_0)),
MetaItemOrLitParser::Lit(__self_0) =>
MetaItemOrLitParser::Lit(::core::clone::Clone::clone(__self_0)),
}
}
}Clone)]
196pub enum MetaItemOrLitParser {
197 MetaItemParser(MetaItemParser),
198 Lit(MetaItemLit),
199}
200
201impl MetaItemOrLitParser {
202 pub fn parse_single<'sess>(
203 parser: &mut Parser<'sess>,
204 should_emit: ShouldEmit,
205 ) -> PResult<'sess, MetaItemOrLitParser> {
206 let mut this = MetaItemListParserContext { parser, should_emit };
207 this.parse_meta_item_inner()
208 }
209
210 pub fn span(&self) -> Span {
211 match self {
212 MetaItemOrLitParser::MetaItemParser(generic_meta_item_parser) => {
213 generic_meta_item_parser.span()
214 }
215 MetaItemOrLitParser::Lit(meta_item_lit) => meta_item_lit.span,
216 }
217 }
218
219 pub fn lit(&self) -> Option<&MetaItemLit> {
220 match self {
221 MetaItemOrLitParser::Lit(meta_item_lit) => Some(meta_item_lit),
222 MetaItemOrLitParser::MetaItemParser(_) => None,
223 }
224 }
225
226 pub fn meta_item(&self) -> Option<&MetaItemParser> {
227 match self {
228 MetaItemOrLitParser::MetaItemParser(parser) => Some(parser),
229 MetaItemOrLitParser::Lit(_) => None,
230 }
231 }
232}
233
234#[derive(#[automatically_derived]
impl ::core::clone::Clone for MetaItemParser {
#[inline]
fn clone(&self) -> MetaItemParser {
MetaItemParser {
path: ::core::clone::Clone::clone(&self.path),
args: ::core::clone::Clone::clone(&self.args),
}
}
}Clone)]
248pub struct MetaItemParser {
249 path: OwnedPathParser,
250 args: ArgParser,
251}
252
253impl Debug for MetaItemParser {
254 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
255 f.debug_struct("MetaItemParser")
256 .field("path", &self.path)
257 .field("args", &self.args)
258 .finish()
259 }
260}
261
262impl MetaItemParser {
263 pub fn ident(&self) -> Option<Ident> {
265 if let [PathSegment { ident, .. }] = self.path.0.segments[..] { Some(ident) } else { None }
266 }
267
268 pub fn span(&self) -> Span {
269 if let Some(other) = self.args.span() {
270 self.path.borrow().span().with_hi(other.hi())
271 } else {
272 self.path.borrow().span()
273 }
274 }
275
276 pub fn path(&self) -> &OwnedPathParser {
282 &self.path
283 }
284
285 pub fn args(&self) -> &ArgParser {
287 &self.args
288 }
289
290 pub fn word_is(&self, sym: Symbol) -> Option<&ArgParser> {
297 self.path().word_is(sym).then(|| self.args())
298 }
299}
300
301#[derive(#[automatically_derived]
impl ::core::clone::Clone for NameValueParser {
#[inline]
fn clone(&self) -> NameValueParser {
NameValueParser {
eq_span: ::core::clone::Clone::clone(&self.eq_span),
value: ::core::clone::Clone::clone(&self.value),
value_span: ::core::clone::Clone::clone(&self.value_span),
}
}
}Clone)]
302pub struct NameValueParser {
303 pub eq_span: Span,
304 value: MetaItemLit,
305 pub value_span: Span,
306}
307
308impl Debug for NameValueParser {
309 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
310 f.debug_struct("NameValueParser")
311 .field("eq_span", &self.eq_span)
312 .field("value", &self.value)
313 .field("value_span", &self.value_span)
314 .finish()
315 }
316}
317
318impl NameValueParser {
319 pub fn value_as_lit(&self) -> &MetaItemLit {
320 &self.value
321 }
322
323 pub fn value_as_str(&self) -> Option<Symbol> {
324 self.value_as_lit().kind.str()
325 }
326
327 pub fn value_as_ident(&self) -> Option<Ident> {
330 let meta_item = self.value_as_lit();
331 meta_item.kind.str().map(|name| Ident { name, span: meta_item.span })
332 }
333
334 pub fn args_span(&self) -> Span {
335 self.eq_span.to(self.value_span)
336 }
337}
338
339fn expr_to_lit(
340 psess: &ParseSess,
341 expr: &Expr,
342 span: Span,
343 should_emit: ShouldEmit,
344) -> Option<MetaItemLit> {
345 if let ExprKind::Lit(token_lit) = expr.kind {
346 let res = MetaItemLit::from_token_lit(token_lit, expr.span);
347 match res {
348 Ok(lit) => {
349 if token_lit.suffix.is_some() {
350 should_emit.emit_err(
351 psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
352 );
353 None
354 } else {
355 if !lit.kind.is_unsuffixed() {
356 should_emit.emit_err(
358 psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
359 );
360 }
361
362 Some(lit)
363 }
364 }
365 Err(err) => {
366 let guar = report_lit_error(psess, err, token_lit, expr.span);
367 let lit = MetaItemLit {
368 symbol: token_lit.symbol,
369 suffix: token_lit.suffix,
370 kind: LitKind::Err(guar),
371 span: expr.span,
372 };
373 Some(lit)
374 }
375 }
376 } else {
377 if #[allow(non_exhaustive_omitted_patterns)] match should_emit {
ShouldEmit::Nothing => true,
_ => false,
}matches!(should_emit, ShouldEmit::Nothing) {
378 return None;
379 }
380
381 let msg = "attribute value must be a literal";
388 let err = psess.dcx().struct_span_err(span, msg);
389 should_emit.emit_err(err);
390 None
391 }
392}
393
394struct MetaItemListParserContext<'a, 'sess> {
395 parser: &'a mut Parser<'sess>,
396 should_emit: ShouldEmit,
397}
398
399impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
400 fn parse_unsuffixed_meta_item_lit(&mut self) -> PResult<'sess, MetaItemLit> {
401 let Some(token_lit) = self.parser.eat_token_lit() else { return Err(self.expected_lit()) };
402 self.unsuffixed_meta_item_from_lit(token_lit)
403 }
404
405 fn unsuffixed_meta_item_from_lit(
406 &mut self,
407 token_lit: token::Lit,
408 ) -> PResult<'sess, MetaItemLit> {
409 let lit = match MetaItemLit::from_token_lit(token_lit, self.parser.prev_token.span) {
410 Ok(lit) => lit,
411 Err(err) => {
412 return Err(create_lit_error(
413 &self.parser.psess,
414 err,
415 token_lit,
416 self.parser.prev_token_uninterpolated_span(),
417 ));
418 }
419 };
420
421 if !lit.kind.is_unsuffixed() {
422 self.should_emit.emit_err(
424 self.parser.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
425 );
426 }
427
428 Ok(lit)
429 }
430
431 fn parse_attr_item(&mut self) -> PResult<'sess, MetaItemParser> {
432 if let Some(MetaVarKind::Meta { has_meta_form }) = self.parser.token.is_metavar_seq() {
433 return if has_meta_form {
434 let attr_item = self
435 .parser
436 .eat_metavar_seq(MetaVarKind::Meta { has_meta_form: true }, |this| {
437 MetaItemListParserContext { parser: this, should_emit: self.should_emit }
438 .parse_attr_item()
439 })
440 .unwrap();
441 Ok(attr_item)
442 } else {
443 self.parser.unexpected_any()
444 };
445 }
446
447 let path = self.parser.parse_path(PathStyle::Mod)?;
448
449 let args = if self.parser.check(::rustc_parse::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::OpenParen,
token_type: ::rustc_parse::parser::token_type::TokenType::OpenParen,
}exp!(OpenParen)) {
451 let start = self.parser.token.span;
452 let (sub_parsers, _) = self.parser.parse_paren_comma_seq(|parser| {
453 MetaItemListParserContext { parser, should_emit: self.should_emit }
454 .parse_meta_item_inner()
455 })?;
456 let end = self.parser.prev_token.span;
457 ArgParser::List(MetaItemListParser { sub_parsers, span: start.with_hi(end.hi()) })
458 } else if self.parser.eat(::rustc_parse::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::Eq,
token_type: ::rustc_parse::parser::token_type::TokenType::Eq,
}exp!(Eq)) {
459 let eq_span = self.parser.prev_token.span;
460 let value = self.parse_unsuffixed_meta_item_lit()?;
461
462 ArgParser::NameValue(NameValueParser { eq_span, value, value_span: value.span })
463 } else {
464 ArgParser::NoArgs
465 };
466
467 Ok(MetaItemParser { path: PathParser(path), args })
468 }
469
470 fn parse_meta_item_inner(&mut self) -> PResult<'sess, MetaItemOrLitParser> {
471 if let Some(token_lit) = self.parser.eat_token_lit() {
472 Ok(MetaItemOrLitParser::Lit(self.unsuffixed_meta_item_from_lit(token_lit)?))
474 } else {
475 let prev_pros = self.parser.approx_token_stream_pos();
476 match self.parse_attr_item() {
477 Ok(item) => Ok(MetaItemOrLitParser::MetaItemParser(item)),
478 Err(err) => {
479 if self.parser.approx_token_stream_pos() != prev_pros {
482 Err(err)
483 } else {
484 err.cancel();
485 Err(self.expected_lit())
486 }
487 }
488 }
489 }
490 }
491
492 fn expected_lit(&mut self) -> Diag<'sess> {
493 let mut err = InvalidMetaItem {
494 span: self.parser.token.span,
495 descr: token_descr(&self.parser.token),
496 quote_ident_sugg: None,
497 remove_neg_sugg: None,
498 label: None,
499 };
500
501 if let token::OpenInvisible(_) = self.parser.token.kind {
502 return self.parser.dcx().create_err(err);
504 }
505
506 let snapshot = self.parser.create_snapshot_for_diagnostic();
510 let stmt = self.parser.parse_stmt_without_recovery(false, ForceCollect::No, false);
511 match stmt {
512 Ok(Some(stmt)) => {
513 err.descr = stmt.kind.descr().to_string();
516 err.label = Some(stmt.span);
517 err.span = stmt.span;
518 if let StmtKind::Expr(expr) = &stmt.kind
519 && let ExprKind::Unary(UnOp::Neg, val) = &expr.kind
520 && let ExprKind::Lit(_) = val.kind
521 {
522 err.remove_neg_sugg = Some(InvalidMetaItemRemoveNegSugg {
523 negative_sign: expr.span.until(val.span),
524 });
525 } else if let StmtKind::Expr(expr) = &stmt.kind
526 && let ExprKind::Path(None, Path { segments, .. }) = &expr.kind
527 && segments.len() == 1
528 {
529 while let token::Ident(..) | token::Literal(_) | token::Dot =
530 self.parser.token.kind
531 {
532 self.parser.bump();
535 }
536 err.quote_ident_sugg = Some(InvalidMetaItemQuoteIdentSugg {
537 before: expr.span.shrink_to_lo(),
538 after: self.parser.prev_token.span.shrink_to_hi(),
539 });
540 }
541 }
542 Ok(None) => {}
543 Err(e) => {
544 e.cancel();
545 self.parser.restore_snapshot(snapshot);
546 }
547 }
548
549 self.parser.dcx().create_err(err)
550 }
551
552 fn parse(
553 tokens: TokenStream,
554 psess: &'sess ParseSess,
555 span: Span,
556 should_emit: ShouldEmit,
557 ) -> PResult<'sess, MetaItemListParser> {
558 let mut parser = Parser::new(psess, tokens, None);
559 let mut this = MetaItemListParserContext { parser: &mut parser, should_emit };
560
561 let mut sub_parsers = ThinVec::with_capacity(1);
563 while this.parser.token != token::Eof {
564 sub_parsers.push(this.parse_meta_item_inner()?);
565
566 if !this.parser.eat(::rustc_parse::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::Comma,
token_type: ::rustc_parse::parser::token_type::TokenType::Comma,
}exp!(Comma)) {
567 break;
568 }
569 }
570
571 if parser.token != token::Eof {
572 parser.unexpected()?;
573 }
574
575 Ok(MetaItemListParser { sub_parsers, span })
576 }
577}
578
579#[derive(#[automatically_derived]
impl ::core::fmt::Debug for MetaItemListParser {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f,
"MetaItemListParser", "sub_parsers", &self.sub_parsers, "span",
&&self.span)
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for MetaItemListParser {
#[inline]
fn clone(&self) -> MetaItemListParser {
MetaItemListParser {
sub_parsers: ::core::clone::Clone::clone(&self.sub_parsers),
span: ::core::clone::Clone::clone(&self.span),
}
}
}Clone)]
580pub struct MetaItemListParser {
581 sub_parsers: ThinVec<MetaItemOrLitParser>,
582 pub span: Span,
583}
584
585impl MetaItemListParser {
586 pub(crate) fn new<'sess>(
587 tokens: &TokenStream,
588 span: Span,
589 psess: &'sess ParseSess,
590 should_emit: ShouldEmit,
591 ) -> Result<Self, Diag<'sess>> {
592 MetaItemListParserContext::parse(tokens.clone(), psess, span, should_emit)
593 }
594
595 pub fn mixed(&self) -> impl Iterator<Item = &MetaItemOrLitParser> {
597 self.sub_parsers.iter()
598 }
599
600 pub fn len(&self) -> usize {
601 self.sub_parsers.len()
602 }
603
604 pub fn is_empty(&self) -> bool {
605 self.len() == 0
606 }
607
608 pub fn single(&self) -> Option<&MetaItemOrLitParser> {
612 let mut iter = self.mixed();
613 iter.next().filter(|_| iter.next().is_none())
614 }
615}