Skip to main content

rustc_attr_parsing/
parser.rs

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