Skip to main content

rustc_attr_parsing/
parser.rs

1//! This is in essence an (improved) duplicate of `rustc_ast/attr/mod.rs`.
2//! That module is intended to be deleted in its entirety.
3//!
4//! FIXME(jdonszelmann): delete `rustc_ast/attr/mod.rs`
5
6use 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::{Applicability, Diag, PResult};
16use rustc_hir::{self as hir, AttrPath};
17use rustc_parse::exp;
18use rustc_parse::parser::{ForceCollect, Parser, PathStyle, Recovery, token_descr};
19use rustc_session::errors::create_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    /// Asserts that this MetaItem is some specific word.
69    ///
70    /// See [`word`](Self::word) for examples of what a word is.
71    pub fn word_is(&self, sym: Symbol) -> bool {
72        self.word().map(|i| i.name == sym).unwrap_or(false)
73    }
74
75    /// Checks whether the first segments match the givens.
76    ///
77    /// Unlike [`segments_is`](Self::segments_is),
78    /// `self` may contain more segments than the number matched  against.
79    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        allow_expr_metavar: AllowExprMetavar,
113    ) -> Option<Self> {
114        Some(match value {
115            AttrArgs::Empty => Self::NoArgs,
116            AttrArgs::Delimited(args) => {
117                // Diagnostic attributes can't error if they encounter non meta item syntax.
118                // However, the current syntax for diagnostic attributes is meta item syntax.
119                // Therefore we can substitute with a dummy value on invalid syntax.
120                if #[allow(non_exhaustive_omitted_patterns)] match parts {
    [sym::rustc_dummy] | [sym::diagnostic, ..] => true,
    _ => false,
}matches!(parts, [sym::rustc_dummy] | [sym::diagnostic, ..]) {
121                    match MetaItemListParser::new(
122                        &args.tokens,
123                        args.dspan.entire(),
124                        psess,
125                        ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden },
126                        allow_expr_metavar,
127                    ) {
128                        Ok(p) => return Some(ArgParser::List(p)),
129                        Err(e) => {
130                            // We can just dispose of the diagnostic and not bother with a lint,
131                            // because this will look like `#[diagnostic::attr()]` was used. This
132                            // is invalid for all diagnostic attrs, so a lint explaining the proper
133                            // form will be issued later.
134                            e.cancel();
135                            return Some(ArgParser::List(MetaItemListParser {
136                                sub_parsers: ThinVec::new(),
137                                span: args.dspan.entire(),
138                            }));
139                        }
140                    }
141                }
142
143                if args.delim != Delimiter::Parenthesis {
144                    should_emit.emit_err(psess.dcx().create_err(MetaBadDelim {
145                        span: args.dspan.entire(),
146                        sugg: MetaBadDelimSugg { open: args.dspan.open, close: args.dspan.close },
147                    }));
148                    return None;
149                }
150
151                Self::List(
152                    MetaItemListParser::new(
153                        &args.tokens,
154                        args.dspan.entire(),
155                        psess,
156                        should_emit,
157                        allow_expr_metavar,
158                    )
159                    .map_err(|e| should_emit.emit_err(e))
160                    .ok()?,
161                )
162            }
163            AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser {
164                eq_span: *eq_span,
165                value: expr_to_lit(psess, &expr, expr.span, should_emit)
166                    .map_err(|e| should_emit.emit_err(e))
167                    .ok()??,
168                value_span: expr.span,
169            }),
170        })
171    }
172
173    /// Asserts that this MetaItem is a list
174    ///
175    /// Some examples:
176    ///
177    /// - `#[allow(clippy::complexity)]`: `(clippy::complexity)` is a list
178    /// - `#[rustfmt::skip::macros(target_macro_name)]`: `(target_macro_name)` is a list
179    pub fn as_list(&self) -> Option<&MetaItemListParser> {
180        match self {
181            Self::List(l) => Some(l),
182            Self::NameValue(_) | Self::NoArgs => None,
183        }
184    }
185
186    /// Asserts that this MetaItem is a name-value pair.
187    ///
188    /// Some examples:
189    ///
190    /// - `#[clippy::cyclomatic_complexity = "100"]`: `clippy::cyclomatic_complexity = "100"` is a name value pair,
191    ///   where the name is a path (`clippy::cyclomatic_complexity`). You already checked the path
192    ///   to get an `ArgParser`, so this method will effectively only assert that the `= "100"` is
193    ///   there
194    /// - `#[doc = "hello"]`: `doc = "hello`  is also a name value pair
195    pub fn as_name_value(&self) -> Option<&NameValueParser> {
196        match self {
197            Self::NameValue(n) => Some(n),
198            Self::List(_) | Self::NoArgs => None,
199        }
200    }
201
202    /// Assert that there were no args.
203    /// If there were, get a span to the arguments
204    /// (to pass to [`AttributeDiagnosticContext::expected_no_args`](crate::context::AttributeDiagnosticContext::expected_no_args)).
205    pub fn as_no_args(&self) -> Result<(), Span> {
206        match self {
207            Self::NoArgs => Ok(()),
208            Self::List(args) => Err(args.span),
209            Self::NameValue(args) => Err(args.args_span()),
210        }
211    }
212}
213
214/// Inside lists, values could be either literals, or more deeply nested meta items.
215/// This enum represents that.
216///
217/// Choose which one you want using the provided methods.
218#[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)]
219pub enum MetaItemOrLitParser {
220    MetaItemParser(MetaItemParser),
221    Lit(MetaItemLit),
222}
223
224impl MetaItemOrLitParser {
225    pub fn parse_single<'sess>(
226        parser: &mut Parser<'sess>,
227        should_emit: ShouldEmit,
228        allow_expr_metavar: AllowExprMetavar,
229    ) -> PResult<'sess, MetaItemOrLitParser> {
230        let mut this = MetaItemListParserContext { parser, should_emit, allow_expr_metavar };
231        this.parse_meta_item_inner()
232    }
233
234    pub fn span(&self) -> Span {
235        match self {
236            MetaItemOrLitParser::MetaItemParser(generic_meta_item_parser) => {
237                generic_meta_item_parser.span()
238            }
239            MetaItemOrLitParser::Lit(meta_item_lit) => meta_item_lit.span,
240        }
241    }
242
243    pub fn as_lit(&self) -> Option<&MetaItemLit> {
244        match self {
245            MetaItemOrLitParser::Lit(meta_item_lit) => Some(meta_item_lit),
246            MetaItemOrLitParser::MetaItemParser(_) => None,
247        }
248    }
249
250    pub fn meta_item(&self) -> Option<&MetaItemParser> {
251        match self {
252            MetaItemOrLitParser::MetaItemParser(parser) => Some(parser),
253            MetaItemOrLitParser::Lit(_) => None,
254        }
255    }
256}
257
258// FIXME(scrabsha): once #155696 is merged, update this and mention the higher-level APIs.
259/// Utility that deconstructs a MetaItem into usable parts.
260///
261/// MetaItems are syntactically extremely flexible, but specific attributes want to parse
262/// them in custom, more restricted ways. This can be done using this struct.
263///
264/// MetaItems consist of some path, and some args. The args could be empty. In other words:
265///
266/// - `name` -> args are empty
267/// - `name(...)` -> args are a [`list`](ArgParser::as_list), which is the bit between the
268///   parentheses
269/// - `name = value`-> arg is [`name_value`](ArgParser::as_name_value), where the argument is the
270///   `= value` part
271///
272/// The syntax of MetaItems can be found at <https://doc.rust-lang.org/reference/attributes.html>
273#[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)]
274pub struct MetaItemParser {
275    path: OwnedPathParser,
276    args: ArgParser,
277}
278
279impl Debug for MetaItemParser {
280    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281        f.debug_struct("MetaItemParser")
282            .field("path", &self.path)
283            .field("args", &self.args)
284            .finish()
285    }
286}
287
288impl MetaItemParser {
289    /// For a single-segment meta item, returns its name; otherwise, returns `None`.
290    pub fn ident(&self) -> Option<Ident> {
291        if let [PathSegment { ident, .. }] = self.path.0.segments[..] { Some(ident) } else { None }
292    }
293
294    pub fn span(&self) -> Span {
295        if let Some(other) = self.args.span() {
296            self.path.borrow().span().with_hi(other.hi())
297        } else {
298            self.path.borrow().span()
299        }
300    }
301
302    /// Gets just the path, without the args. Some examples:
303    ///
304    /// - `#[rustfmt::skip]`: `rustfmt::skip` is a path
305    /// - `#[allow(clippy::complexity)]`: `clippy::complexity` is a path
306    /// - `#[inline]`: `inline` is a single segment path
307    pub fn path(&self) -> &OwnedPathParser {
308        &self.path
309    }
310
311    /// Gets just the args parser, without caring about the path.
312    pub fn args(&self) -> &ArgParser {
313        &self.args
314    }
315
316    /// Asserts that this MetaItem starts with a word, or single segment path.
317    ///
318    /// Some examples:
319    /// - `#[inline]`: `inline` is a word
320    /// - `#[rustfmt::skip]`: `rustfmt::skip` is a path,
321    ///   and not a word and should instead be parsed using [`path`](Self::path)
322    pub fn word_is(&self, sym: Symbol) -> Option<&ArgParser> {
323        self.path().word_is(sym).then(|| self.args())
324    }
325}
326
327#[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)]
328pub struct NameValueParser {
329    pub eq_span: Span,
330    value: MetaItemLit,
331    pub value_span: Span,
332}
333
334impl Debug for NameValueParser {
335    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
336        f.debug_struct("NameValueParser")
337            .field("eq_span", &self.eq_span)
338            .field("value", &self.value)
339            .field("value_span", &self.value_span)
340            .finish()
341    }
342}
343
344impl NameValueParser {
345    pub fn value_as_lit(&self) -> &MetaItemLit {
346        &self.value
347    }
348
349    pub fn value_as_str(&self) -> Option<Symbol> {
350        self.value_as_lit().kind.str()
351    }
352
353    /// If the value is a string literal, it will return its value associated with its span (an
354    /// `Ident` in short).
355    pub fn value_as_ident(&self) -> Option<Ident> {
356        let meta_item = self.value_as_lit();
357        meta_item.kind.str().map(|name| Ident { name, span: meta_item.span })
358    }
359
360    pub fn args_span(&self) -> Span {
361        self.eq_span.to(self.value_span)
362    }
363}
364
365fn expr_to_lit<'sess>(
366    psess: &'sess ParseSess,
367    expr: &Expr,
368    span: Span,
369    should_emit: ShouldEmit,
370) -> PResult<'sess, Option<MetaItemLit>> {
371    if let ExprKind::Lit(token_lit) = expr.kind {
372        let res = MetaItemLit::from_token_lit(token_lit, expr.span);
373        match res {
374            Ok(lit) => {
375                if token_lit.suffix.is_some() {
376                    Err(psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }))
377                } else {
378                    if lit.kind.is_unsuffixed() {
379                        Ok(Some(lit))
380                    } else {
381                        Err(psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }))
382                    }
383                }
384            }
385            Err(err) => {
386                let err = create_lit_error(psess, err, token_lit, expr.span);
387                if #[allow(non_exhaustive_omitted_patterns)] match should_emit {
    ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden } => true,
    _ => false,
}matches!(
388                    should_emit,
389                    ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden }
390                ) {
391                    Err(err)
392                } else {
393                    let lit = MetaItemLit {
394                        symbol: token_lit.symbol,
395                        suffix: token_lit.suffix,
396                        kind: LitKind::Err(err.emit()),
397                        span: expr.span,
398                    };
399                    Ok(Some(lit))
400                }
401            }
402        }
403    } else {
404        if #[allow(non_exhaustive_omitted_patterns)] match should_emit {
    ShouldEmit::Nothing => true,
    _ => false,
}matches!(should_emit, ShouldEmit::Nothing) || #[allow(non_exhaustive_omitted_patterns)] match expr.kind {
    ExprKind::Err(_) => true,
    _ => false,
}matches!(expr.kind, ExprKind::Err(_)) {
405            return Ok(None);
406        }
407
408        // Example cases:
409        // - `#[foo = 1+1]`: results in `ast::ExprKind::BinOp`.
410        // - `#[foo = include_str!("nonexistent-file.rs")]`:
411        //   results in `ast::ExprKind::Err`.
412        let msg = "attribute value must be a literal";
413        let mut err = psess.dcx().struct_span_err(span, msg);
414
415        // Suggest adding quotation marks to turn an identifier into a string literal
416        if let ExprKind::Path(None, ref path) = expr.kind
417            && let [segment] = path.segments.as_slice()
418        {
419            err.span_suggestion(
420                expr.span,
421                "try adding quotation marks",
422                &::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("\"{0}\"", segment.ident))
    })format!("\"{}\"", segment.ident),
423                Applicability::MaybeIncorrect,
424            );
425        }
426
427        Err(err)
428    }
429}
430
431/// Whether expansions of `expr` metavariables from decrarative macros
432/// are permitted. Used when parsing meta items; currently, only `cfg` predicates
433/// enable this option
434#[derive(#[automatically_derived]
impl ::core::clone::Clone for AllowExprMetavar {
    #[inline]
    fn clone(&self) -> AllowExprMetavar { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for AllowExprMetavar { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for AllowExprMetavar {
    #[inline]
    fn eq(&self, other: &AllowExprMetavar) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for AllowExprMetavar {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {}
}Eq)]
435pub enum AllowExprMetavar {
436    No,
437    Yes,
438}
439
440struct MetaItemListParserContext<'a, 'sess> {
441    parser: &'a mut Parser<'sess>,
442    should_emit: ShouldEmit,
443    allow_expr_metavar: AllowExprMetavar,
444}
445
446impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
447    fn parse_unsuffixed_meta_item_lit(&mut self) -> PResult<'sess, MetaItemLit> {
448        let Some(token_lit) = self.parser.eat_token_lit() else { return Err(self.expected_lit()) };
449        self.unsuffixed_meta_item_from_lit(token_lit)
450    }
451
452    fn unsuffixed_meta_item_from_lit(
453        &mut self,
454        token_lit: token::Lit,
455    ) -> PResult<'sess, MetaItemLit> {
456        let lit = match MetaItemLit::from_token_lit(token_lit, self.parser.prev_token.span) {
457            Ok(lit) => lit,
458            Err(err) => {
459                return Err(create_lit_error(
460                    &self.parser.psess,
461                    err,
462                    token_lit,
463                    self.parser.prev_token_uninterpolated_span(),
464                ));
465            }
466        };
467
468        if !lit.kind.is_unsuffixed() {
469            // Emit error and continue, we can still parse the attribute as if the suffix isn't there
470            let err = self.parser.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span });
471            if #[allow(non_exhaustive_omitted_patterns)] match self.should_emit {
    ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden } => true,
    _ => false,
}matches!(
472                self.should_emit,
473                ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden }
474            ) {
475                return Err(err);
476            } else {
477                self.should_emit.emit_err(err)
478            };
479        }
480
481        Ok(lit)
482    }
483
484    fn parse_meta_item(&mut self) -> PResult<'sess, MetaItemParser> {
485        if let Some(metavar) = self.parser.token.is_metavar_seq() {
486            match (metavar, self.allow_expr_metavar) {
487                (kind @ MetaVarKind::Expr { .. }, AllowExprMetavar::Yes) => {
488                    return self
489                        .parser
490                        .eat_metavar_seq(kind, |this| {
491                            MetaItemListParserContext {
492                                parser: this,
493                                should_emit: self.should_emit,
494                                allow_expr_metavar: AllowExprMetavar::Yes,
495                            }
496                            .parse_meta_item()
497                        })
498                        .ok_or_else(|| {
499                            self.parser.unexpected_any::<core::convert::Infallible>().unwrap_err()
500                        });
501                }
502                (MetaVarKind::Meta { has_meta_form }, _) => {
503                    return if has_meta_form {
504                        let attr_item = self
505                            .parser
506                            .eat_metavar_seq(MetaVarKind::Meta { has_meta_form: true }, |this| {
507                                MetaItemListParserContext {
508                                    parser: this,
509                                    should_emit: self.should_emit,
510                                    allow_expr_metavar: self.allow_expr_metavar,
511                                }
512                                .parse_meta_item()
513                            })
514                            .unwrap();
515                        Ok(attr_item)
516                    } else {
517                        self.parser.unexpected_any()
518                    };
519                }
520                _ => {}
521            }
522        }
523
524        let path = self.parser.parse_path(PathStyle::Mod)?;
525
526        // Check style of arguments that this meta item has
527        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)) {
528            let start = self.parser.token.span;
529            let (sub_parsers, _) = self.parser.parse_paren_comma_seq(|parser| {
530                MetaItemListParserContext {
531                    parser,
532                    should_emit: self.should_emit,
533                    allow_expr_metavar: self.allow_expr_metavar,
534                }
535                .parse_meta_item_inner()
536            })?;
537            let end = self.parser.prev_token.span;
538            ArgParser::List(MetaItemListParser { sub_parsers, span: start.with_hi(end.hi()) })
539        } 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)) {
540            let eq_span = self.parser.prev_token.span;
541            let value = self.parse_unsuffixed_meta_item_lit()?;
542
543            ArgParser::NameValue(NameValueParser { eq_span, value, value_span: value.span })
544        } else {
545            ArgParser::NoArgs
546        };
547
548        Ok(MetaItemParser { path: PathParser(path), args })
549    }
550
551    fn parse_meta_item_inner(&mut self) -> PResult<'sess, MetaItemOrLitParser> {
552        if let Some(token_lit) = self.parser.eat_token_lit() {
553            // If a literal token is parsed, we commit to parsing a MetaItemLit for better errors
554            Ok(MetaItemOrLitParser::Lit(self.unsuffixed_meta_item_from_lit(token_lit)?))
555        } else {
556            let prev_pros = self.parser.approx_token_stream_pos();
557            match self.parse_meta_item() {
558                Ok(item) => Ok(MetaItemOrLitParser::MetaItemParser(item)),
559                Err(err) => {
560                    // If `parse_attr_item` made any progress, it likely has a more precise error we should prefer
561                    // If it didn't make progress we use the `expected_lit` from below
562                    if self.parser.approx_token_stream_pos() != prev_pros {
563                        Err(err)
564                    } else {
565                        err.cancel();
566                        Err(self.expected_lit())
567                    }
568                }
569            }
570        }
571    }
572
573    fn expected_lit(&mut self) -> Diag<'sess> {
574        let mut err = InvalidMetaItem {
575            span: self.parser.token.span,
576            descr: token_descr(&self.parser.token),
577            quote_ident_sugg: None,
578            remove_neg_sugg: None,
579            label: None,
580        };
581
582        if let token::OpenInvisible(_) = self.parser.token.kind {
583            // Do not attempt to suggest anything when encountered as part of a macro expansion.
584            return self.parser.dcx().create_err(err);
585        }
586
587        if let ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden } = self.should_emit {
588            // Do not attempt to suggest anything in `Recovery::Forbidden` mode.
589            // Malformed diagnostic-attr arguments that start with an `if` expression can lead to
590            // an ICE (https://github.com/rust-lang/rust/issues/152744), because callers may cancel the `InvalidMetaItem` error.
591            return self.parser.dcx().create_err(err);
592        }
593
594        // Suggest quoting idents, e.g. in `#[cfg(key = value)]`. We don't use `Token::ident` and
595        // don't `uninterpolate` the token to avoid suggesting anything butchered or questionable
596        // when macro metavariables are involved.
597        let snapshot = self.parser.create_snapshot_for_diagnostic();
598        let stmt = self.parser.parse_stmt_without_recovery(false, ForceCollect::No, false);
599        match stmt {
600            Ok(Some(stmt)) => {
601                // The user tried to write something like
602                // `#[deprecated(note = concat!("a", "b"))]`.
603                err.descr = stmt.kind.descr().to_string();
604                err.label = Some(stmt.span);
605                err.span = stmt.span;
606                if let StmtKind::Expr(expr) = &stmt.kind
607                    && let ExprKind::Unary(UnOp::Neg, val) = &expr.kind
608                    && let ExprKind::Lit(_) = val.kind
609                {
610                    err.remove_neg_sugg = Some(InvalidMetaItemRemoveNegSugg {
611                        negative_sign: expr.span.until(val.span),
612                    });
613                } else if let StmtKind::Expr(expr) = &stmt.kind
614                    && let ExprKind::Path(None, Path { segments, .. }) = &expr.kind
615                    && segments.len() == 1
616                {
617                    while let token::Ident(..) | token::Literal(_) | token::Dot =
618                        self.parser.token.kind
619                    {
620                        // We've got a word, so we try to consume the rest of a potential sentence.
621                        // We include `.` to correctly handle things like `A sentence here.`.
622                        self.parser.bump();
623                    }
624                    err.quote_ident_sugg = Some(InvalidMetaItemQuoteIdentSugg {
625                        before: expr.span.shrink_to_lo(),
626                        after: self.parser.prev_token.span.shrink_to_hi(),
627                    });
628                }
629            }
630            Ok(None) => {}
631            Err(e) => {
632                e.cancel();
633                self.parser.restore_snapshot(snapshot);
634            }
635        }
636
637        self.parser.dcx().create_err(err)
638    }
639
640    fn parse(
641        tokens: TokenStream,
642        psess: &'sess ParseSess,
643        span: Span,
644        should_emit: ShouldEmit,
645        allow_expr_metavar: AllowExprMetavar,
646    ) -> PResult<'sess, MetaItemListParser> {
647        let mut parser = Parser::new(psess, tokens, None);
648        if let ShouldEmit::ErrorsAndLints { recovery } = should_emit {
649            parser = parser.recovery(recovery);
650        }
651
652        let mut this =
653            MetaItemListParserContext { parser: &mut parser, should_emit, allow_expr_metavar };
654
655        // Presumably, the majority of the time there will only be one attr.
656        let mut sub_parsers = ThinVec::with_capacity(1);
657        while this.parser.token != token::Eof {
658            sub_parsers.push(this.parse_meta_item_inner()?);
659
660            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)) {
661                break;
662            }
663        }
664
665        if parser.token != token::Eof {
666            parser.unexpected()?;
667        }
668
669        Ok(MetaItemListParser { sub_parsers, span })
670    }
671}
672
673#[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)]
674pub struct MetaItemListParser {
675    sub_parsers: ThinVec<MetaItemOrLitParser>,
676    pub span: Span,
677}
678
679impl MetaItemListParser {
680    pub(crate) fn new<'sess>(
681        tokens: &TokenStream,
682        span: Span,
683        psess: &'sess ParseSess,
684        should_emit: ShouldEmit,
685        allow_expr_metavar: AllowExprMetavar,
686    ) -> Result<Self, Diag<'sess>> {
687        MetaItemListParserContext::parse(
688            tokens.clone(),
689            psess,
690            span,
691            should_emit,
692            allow_expr_metavar,
693        )
694    }
695
696    /// Lets you pick and choose as what you want to parse each element in the list
697    pub fn mixed(&self) -> impl Iterator<Item = &MetaItemOrLitParser> {
698        self.sub_parsers.iter()
699    }
700
701    pub fn len(&self) -> usize {
702        self.sub_parsers.len()
703    }
704
705    pub fn is_empty(&self) -> bool {
706        self.len() == 0
707    }
708
709    /// Returns Some if the list contains only a single element.
710    ///
711    /// Inside the Some is the parser to parse this single element.
712    pub fn as_single(&self) -> Option<&MetaItemOrLitParser> {
713        let mut iter = self.mixed();
714        iter.next().filter(|_| iter.next().is_none())
715    }
716}