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