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::{Diag, PResult};
16use rustc_hir::{self as hir, AttrPath};
17use rustc_parse::exp;
18use rustc_parse::parser::{ForceCollect, Parser, PathStyle, token_descr};
19use rustc_session::errors::{create_lit_error, report_lit_error};
20use rustc_session::parse::ParseSess;
21use rustc_span::{Ident, Span, Symbol, sym};
22use thin_vec::ThinVec;
23
24use crate::ShouldEmit;
25use crate::session_diagnostics::{
26    InvalidMetaItem, InvalidMetaItemQuoteIdentSugg, InvalidMetaItemRemoveNegSugg, MetaBadDelim,
27    MetaBadDelimSugg, SuffixedLiteralInAttribute,
28};
29
30#[derive(#[automatically_derived]
impl<P: ::core::clone::Clone + Borrow<Path>> ::core::clone::Clone for
    PathParser<P> {
    #[inline]
    fn clone(&self) -> PathParser<P> {
        PathParser(::core::clone::Clone::clone(&self.0))
    }
}Clone, #[automatically_derived]
impl<P: ::core::fmt::Debug + Borrow<Path>> ::core::fmt::Debug for
    PathParser<P> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "PathParser",
            &&self.0)
    }
}Debug)]
31pub struct PathParser<P: Borrow<Path>>(pub P);
32
33pub type OwnedPathParser = PathParser<Path>;
34pub type RefPathParser<'p> = PathParser<&'p Path>;
35
36impl<P: Borrow<Path>> PathParser<P> {
37    pub fn get_attribute_path(&self) -> hir::AttrPath {
38        AttrPath {
39            segments: self.segments().map(|s| s.name).collect::<Vec<_>>().into_boxed_slice(),
40            span: self.span(),
41        }
42    }
43
44    pub fn segments(&self) -> impl Iterator<Item = &Ident> {
45        self.0.borrow().segments.iter().map(|seg| &seg.ident)
46    }
47
48    pub fn span(&self) -> Span {
49        self.0.borrow().span
50    }
51
52    pub fn len(&self) -> usize {
53        self.0.borrow().segments.len()
54    }
55
56    pub fn segments_is(&self, segments: &[Symbol]) -> bool {
57        self.segments().map(|segment| &segment.name).eq(segments)
58    }
59
60    pub fn word(&self) -> Option<Ident> {
61        (self.len() == 1).then(|| **self.segments().next().as_ref().unwrap())
62    }
63
64    pub fn word_sym(&self) -> Option<Symbol> {
65        self.word().map(|ident| ident.name)
66    }
67
68    /// 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    ) -> Option<Self> {
113        Some(match value {
114            AttrArgs::Empty => Self::NoArgs,
115            AttrArgs::Delimited(args) => {
116                // The arguments of rustc_dummy and diagnostic::do_not_recommend are not validated
117                // if the arguments are delimited.
118                // See https://doc.rust-lang.org/reference/attributes/diagnostics.html#r-attributes.diagnostic.namespace.unknown-invalid-syntax
119                if parts == &[sym::rustc_dummy]
120                    || parts == &[sym::diagnostic, sym::do_not_recommend]
121                {
122                    return Some(ArgParser::List(MetaItemListParser {
123                        sub_parsers: ThinVec::new(),
124                        span: args.dspan.entire(),
125                    }));
126                }
127
128                if args.delim != Delimiter::Parenthesis {
129                    should_emit.emit_err(psess.dcx().create_err(MetaBadDelim {
130                        span: args.dspan.entire(),
131                        sugg: MetaBadDelimSugg { open: args.dspan.open, close: args.dspan.close },
132                    }));
133                    return None;
134                }
135
136                Self::List(
137                    MetaItemListParser::new(&args.tokens, args.dspan.entire(), psess, should_emit)
138                        .map_err(|e| should_emit.emit_err(e))
139                        .ok()?,
140                )
141            }
142            AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser {
143                eq_span: *eq_span,
144                value: expr_to_lit(psess, &expr, expr.span, should_emit)?,
145                value_span: expr.span,
146            }),
147        })
148    }
149
150    /// Asserts that this MetaItem is a list
151    ///
152    /// Some examples:
153    ///
154    /// - `#[allow(clippy::complexity)]`: `(clippy::complexity)` is a list
155    /// - `#[rustfmt::skip::macros(target_macro_name)]`: `(target_macro_name)` is a list
156    pub fn list(&self) -> Option<&MetaItemListParser> {
157        match self {
158            Self::List(l) => Some(l),
159            Self::NameValue(_) | Self::NoArgs => None,
160        }
161    }
162
163    /// Asserts that this MetaItem is a name-value pair.
164    ///
165    /// Some examples:
166    ///
167    /// - `#[clippy::cyclomatic_complexity = "100"]`: `clippy::cyclomatic_complexity = "100"` is a name value pair,
168    ///   where the name is a path (`clippy::cyclomatic_complexity`). You already checked the path
169    ///   to get an `ArgParser`, so this method will effectively only assert that the `= "100"` is
170    ///   there
171    /// - `#[doc = "hello"]`: `doc = "hello`  is also a name value pair
172    pub fn name_value(&self) -> Option<&NameValueParser> {
173        match self {
174            Self::NameValue(n) => Some(n),
175            Self::List(_) | Self::NoArgs => None,
176        }
177    }
178
179    /// Assert that there were no args.
180    /// If there were, get a span to the arguments
181    /// (to pass to [`AcceptContext::expected_no_args`](crate::context::AcceptContext::expected_no_args)).
182    pub fn no_args(&self) -> Result<(), Span> {
183        match self {
184            Self::NoArgs => Ok(()),
185            Self::List(args) => Err(args.span),
186            Self::NameValue(args) => Err(args.args_span()),
187        }
188    }
189}
190
191/// Inside lists, values could be either literals, or more deeply nested meta items.
192/// This enum represents that.
193///
194/// Choose which one you want using the provided methods.
195#[derive(#[automatically_derived]
impl ::core::fmt::Debug for MetaItemOrLitParser {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            MetaItemOrLitParser::MetaItemParser(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "MetaItemParser", &__self_0),
            MetaItemOrLitParser::Lit(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Lit",
                    &__self_0),
        }
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for MetaItemOrLitParser {
    #[inline]
    fn clone(&self) -> MetaItemOrLitParser {
        match self {
            MetaItemOrLitParser::MetaItemParser(__self_0) =>
                MetaItemOrLitParser::MetaItemParser(::core::clone::Clone::clone(__self_0)),
            MetaItemOrLitParser::Lit(__self_0) =>
                MetaItemOrLitParser::Lit(::core::clone::Clone::clone(__self_0)),
        }
    }
}Clone)]
196pub enum MetaItemOrLitParser {
197    MetaItemParser(MetaItemParser),
198    Lit(MetaItemLit),
199}
200
201impl MetaItemOrLitParser {
202    pub fn parse_single<'sess>(
203        parser: &mut Parser<'sess>,
204        should_emit: ShouldEmit,
205    ) -> PResult<'sess, MetaItemOrLitParser> {
206        let mut this = MetaItemListParserContext { parser, should_emit };
207        this.parse_meta_item_inner()
208    }
209
210    pub fn span(&self) -> Span {
211        match self {
212            MetaItemOrLitParser::MetaItemParser(generic_meta_item_parser) => {
213                generic_meta_item_parser.span()
214            }
215            MetaItemOrLitParser::Lit(meta_item_lit) => meta_item_lit.span,
216        }
217    }
218
219    pub fn lit(&self) -> Option<&MetaItemLit> {
220        match self {
221            MetaItemOrLitParser::Lit(meta_item_lit) => Some(meta_item_lit),
222            MetaItemOrLitParser::MetaItemParser(_) => None,
223        }
224    }
225
226    pub fn meta_item(&self) -> Option<&MetaItemParser> {
227        match self {
228            MetaItemOrLitParser::MetaItemParser(parser) => Some(parser),
229            MetaItemOrLitParser::Lit(_) => None,
230        }
231    }
232}
233
234/// Utility that deconstructs a MetaItem into usable parts.
235///
236/// MetaItems are syntactically extremely flexible, but specific attributes want to parse
237/// them in custom, more restricted ways. This can be done using this struct.
238///
239/// MetaItems consist of some path, and some args. The args could be empty. In other words:
240///
241/// - `name` -> args are empty
242/// - `name(...)` -> args are a [`list`](ArgParser::list), which is the bit between the parentheses
243/// - `name = value`-> arg is [`name_value`](ArgParser::name_value), where the argument is the
244///   `= value` part
245///
246/// The syntax of MetaItems can be found at <https://doc.rust-lang.org/reference/attributes.html>
247#[derive(#[automatically_derived]
impl ::core::clone::Clone for MetaItemParser {
    #[inline]
    fn clone(&self) -> MetaItemParser {
        MetaItemParser {
            path: ::core::clone::Clone::clone(&self.path),
            args: ::core::clone::Clone::clone(&self.args),
        }
    }
}Clone)]
248pub struct MetaItemParser {
249    path: OwnedPathParser,
250    args: ArgParser,
251}
252
253impl Debug for MetaItemParser {
254    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
255        f.debug_struct("MetaItemParser")
256            .field("path", &self.path)
257            .field("args", &self.args)
258            .finish()
259    }
260}
261
262impl MetaItemParser {
263    /// For a single-segment meta item, returns its name; otherwise, returns `None`.
264    pub fn ident(&self) -> Option<Ident> {
265        if let [PathSegment { ident, .. }] = self.path.0.segments[..] { Some(ident) } else { None }
266    }
267
268    pub fn span(&self) -> Span {
269        if let Some(other) = self.args.span() {
270            self.path.borrow().span().with_hi(other.hi())
271        } else {
272            self.path.borrow().span()
273        }
274    }
275
276    /// Gets just the path, without the args. Some examples:
277    ///
278    /// - `#[rustfmt::skip]`: `rustfmt::skip` is a path
279    /// - `#[allow(clippy::complexity)]`: `clippy::complexity` is a path
280    /// - `#[inline]`: `inline` is a single segment path
281    pub fn path(&self) -> &OwnedPathParser {
282        &self.path
283    }
284
285    /// Gets just the args parser, without caring about the path.
286    pub fn args(&self) -> &ArgParser {
287        &self.args
288    }
289
290    /// Asserts that this MetaItem starts with a word, or single segment path.
291    ///
292    /// Some examples:
293    /// - `#[inline]`: `inline` is a word
294    /// - `#[rustfmt::skip]`: `rustfmt::skip` is a path,
295    ///   and not a word and should instead be parsed using [`path`](Self::path)
296    pub fn word_is(&self, sym: Symbol) -> Option<&ArgParser> {
297        self.path().word_is(sym).then(|| self.args())
298    }
299}
300
301#[derive(#[automatically_derived]
impl ::core::clone::Clone for NameValueParser {
    #[inline]
    fn clone(&self) -> NameValueParser {
        NameValueParser {
            eq_span: ::core::clone::Clone::clone(&self.eq_span),
            value: ::core::clone::Clone::clone(&self.value),
            value_span: ::core::clone::Clone::clone(&self.value_span),
        }
    }
}Clone)]
302pub struct NameValueParser {
303    pub eq_span: Span,
304    value: MetaItemLit,
305    pub value_span: Span,
306}
307
308impl Debug for NameValueParser {
309    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
310        f.debug_struct("NameValueParser")
311            .field("eq_span", &self.eq_span)
312            .field("value", &self.value)
313            .field("value_span", &self.value_span)
314            .finish()
315    }
316}
317
318impl NameValueParser {
319    pub fn value_as_lit(&self) -> &MetaItemLit {
320        &self.value
321    }
322
323    pub fn value_as_str(&self) -> Option<Symbol> {
324        self.value_as_lit().kind.str()
325    }
326
327    /// If the value is a string literal, it will return its value associated with its span (an
328    /// `Ident` in short).
329    pub fn value_as_ident(&self) -> Option<Ident> {
330        let meta_item = self.value_as_lit();
331        meta_item.kind.str().map(|name| Ident { name, span: meta_item.span })
332    }
333
334    pub fn args_span(&self) -> Span {
335        self.eq_span.to(self.value_span)
336    }
337}
338
339fn expr_to_lit(
340    psess: &ParseSess,
341    expr: &Expr,
342    span: Span,
343    should_emit: ShouldEmit,
344) -> Option<MetaItemLit> {
345    if let ExprKind::Lit(token_lit) = expr.kind {
346        let res = MetaItemLit::from_token_lit(token_lit, expr.span);
347        match res {
348            Ok(lit) => {
349                if token_lit.suffix.is_some() {
350                    should_emit.emit_err(
351                        psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
352                    );
353                    None
354                } else {
355                    if !lit.kind.is_unsuffixed() {
356                        // Emit error and continue, we can still parse the attribute as if the suffix isn't there
357                        should_emit.emit_err(
358                            psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
359                        );
360                    }
361
362                    Some(lit)
363                }
364            }
365            Err(err) => {
366                let guar = report_lit_error(psess, err, token_lit, expr.span);
367                let lit = MetaItemLit {
368                    symbol: token_lit.symbol,
369                    suffix: token_lit.suffix,
370                    kind: LitKind::Err(guar),
371                    span: expr.span,
372                };
373                Some(lit)
374            }
375        }
376    } else {
377        if #[allow(non_exhaustive_omitted_patterns)] match should_emit {
    ShouldEmit::Nothing => true,
    _ => false,
}matches!(should_emit, ShouldEmit::Nothing) {
378            return None;
379        }
380
381        // Example cases:
382        // - `#[foo = 1+1]`: results in `ast::ExprKind::BinOp`.
383        // - `#[foo = include_str!("nonexistent-file.rs")]`:
384        //   results in `ast::ExprKind::Err`. In that case we delay
385        //   the error because an earlier error will have already
386        //   been reported.
387        let msg = "attribute value must be a literal";
388        let err = psess.dcx().struct_span_err(span, msg);
389        should_emit.emit_err(err);
390        None
391    }
392}
393
394struct MetaItemListParserContext<'a, 'sess> {
395    parser: &'a mut Parser<'sess>,
396    should_emit: ShouldEmit,
397}
398
399impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
400    fn parse_unsuffixed_meta_item_lit(&mut self) -> PResult<'sess, MetaItemLit> {
401        let Some(token_lit) = self.parser.eat_token_lit() else { return Err(self.expected_lit()) };
402        self.unsuffixed_meta_item_from_lit(token_lit)
403    }
404
405    fn unsuffixed_meta_item_from_lit(
406        &mut self,
407        token_lit: token::Lit,
408    ) -> PResult<'sess, MetaItemLit> {
409        let lit = match MetaItemLit::from_token_lit(token_lit, self.parser.prev_token.span) {
410            Ok(lit) => lit,
411            Err(err) => {
412                return Err(create_lit_error(
413                    &self.parser.psess,
414                    err,
415                    token_lit,
416                    self.parser.prev_token_uninterpolated_span(),
417                ));
418            }
419        };
420
421        if !lit.kind.is_unsuffixed() {
422            // Emit error and continue, we can still parse the attribute as if the suffix isn't there
423            self.should_emit.emit_err(
424                self.parser.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
425            );
426        }
427
428        Ok(lit)
429    }
430
431    fn parse_attr_item(&mut self) -> PResult<'sess, MetaItemParser> {
432        if let Some(MetaVarKind::Meta { has_meta_form }) = self.parser.token.is_metavar_seq() {
433            return if has_meta_form {
434                let attr_item = self
435                    .parser
436                    .eat_metavar_seq(MetaVarKind::Meta { has_meta_form: true }, |this| {
437                        MetaItemListParserContext { parser: this, should_emit: self.should_emit }
438                            .parse_attr_item()
439                    })
440                    .unwrap();
441                Ok(attr_item)
442            } else {
443                self.parser.unexpected_any()
444            };
445        }
446
447        let path = self.parser.parse_path(PathStyle::Mod)?;
448
449        // Check style of arguments that this meta item has
450        let args = if self.parser.check(::rustc_parse::parser::token_type::ExpTokenPair {
    tok: rustc_ast::token::OpenParen,
    token_type: ::rustc_parse::parser::token_type::TokenType::OpenParen,
}exp!(OpenParen)) {
451            let start = self.parser.token.span;
452            let (sub_parsers, _) = self.parser.parse_paren_comma_seq(|parser| {
453                MetaItemListParserContext { parser, should_emit: self.should_emit }
454                    .parse_meta_item_inner()
455            })?;
456            let end = self.parser.prev_token.span;
457            ArgParser::List(MetaItemListParser { sub_parsers, span: start.with_hi(end.hi()) })
458        } else if self.parser.eat(::rustc_parse::parser::token_type::ExpTokenPair {
    tok: rustc_ast::token::Eq,
    token_type: ::rustc_parse::parser::token_type::TokenType::Eq,
}exp!(Eq)) {
459            let eq_span = self.parser.prev_token.span;
460            let value = self.parse_unsuffixed_meta_item_lit()?;
461
462            ArgParser::NameValue(NameValueParser { eq_span, value, value_span: value.span })
463        } else {
464            ArgParser::NoArgs
465        };
466
467        Ok(MetaItemParser { path: PathParser(path), args })
468    }
469
470    fn parse_meta_item_inner(&mut self) -> PResult<'sess, MetaItemOrLitParser> {
471        if let Some(token_lit) = self.parser.eat_token_lit() {
472            // If a literal token is parsed, we commit to parsing a MetaItemLit for better errors
473            Ok(MetaItemOrLitParser::Lit(self.unsuffixed_meta_item_from_lit(token_lit)?))
474        } else {
475            let prev_pros = self.parser.approx_token_stream_pos();
476            match self.parse_attr_item() {
477                Ok(item) => Ok(MetaItemOrLitParser::MetaItemParser(item)),
478                Err(err) => {
479                    // If `parse_attr_item` made any progress, it likely has a more precise error we should prefer
480                    // If it didn't make progress we use the `expected_lit` from below
481                    if self.parser.approx_token_stream_pos() != prev_pros {
482                        Err(err)
483                    } else {
484                        err.cancel();
485                        Err(self.expected_lit())
486                    }
487                }
488            }
489        }
490    }
491
492    fn expected_lit(&mut self) -> Diag<'sess> {
493        let mut err = InvalidMetaItem {
494            span: self.parser.token.span,
495            descr: token_descr(&self.parser.token),
496            quote_ident_sugg: None,
497            remove_neg_sugg: None,
498            label: None,
499        };
500
501        if let token::OpenInvisible(_) = self.parser.token.kind {
502            // Do not attempt to suggest anything when encountered as part of a macro expansion.
503            return self.parser.dcx().create_err(err);
504        }
505
506        // Suggest quoting idents, e.g. in `#[cfg(key = value)]`. We don't use `Token::ident` and
507        // don't `uninterpolate` the token to avoid suggesting anything butchered or questionable
508        // when macro metavariables are involved.
509        let snapshot = self.parser.create_snapshot_for_diagnostic();
510        let stmt = self.parser.parse_stmt_without_recovery(false, ForceCollect::No, false);
511        match stmt {
512            Ok(Some(stmt)) => {
513                // The user tried to write something like
514                // `#[deprecated(note = concat!("a", "b"))]`.
515                err.descr = stmt.kind.descr().to_string();
516                err.label = Some(stmt.span);
517                err.span = stmt.span;
518                if let StmtKind::Expr(expr) = &stmt.kind
519                    && let ExprKind::Unary(UnOp::Neg, val) = &expr.kind
520                    && let ExprKind::Lit(_) = val.kind
521                {
522                    err.remove_neg_sugg = Some(InvalidMetaItemRemoveNegSugg {
523                        negative_sign: expr.span.until(val.span),
524                    });
525                } else if let StmtKind::Expr(expr) = &stmt.kind
526                    && let ExprKind::Path(None, Path { segments, .. }) = &expr.kind
527                    && segments.len() == 1
528                {
529                    while let token::Ident(..) | token::Literal(_) | token::Dot =
530                        self.parser.token.kind
531                    {
532                        // We've got a word, so we try to consume the rest of a potential sentence.
533                        // We include `.` to correctly handle things like `A sentence here.`.
534                        self.parser.bump();
535                    }
536                    err.quote_ident_sugg = Some(InvalidMetaItemQuoteIdentSugg {
537                        before: expr.span.shrink_to_lo(),
538                        after: self.parser.prev_token.span.shrink_to_hi(),
539                    });
540                }
541            }
542            Ok(None) => {}
543            Err(e) => {
544                e.cancel();
545                self.parser.restore_snapshot(snapshot);
546            }
547        }
548
549        self.parser.dcx().create_err(err)
550    }
551
552    fn parse(
553        tokens: TokenStream,
554        psess: &'sess ParseSess,
555        span: Span,
556        should_emit: ShouldEmit,
557    ) -> PResult<'sess, MetaItemListParser> {
558        let mut parser = Parser::new(psess, tokens, None);
559        let mut this = MetaItemListParserContext { parser: &mut parser, should_emit };
560
561        // Presumably, the majority of the time there will only be one attr.
562        let mut sub_parsers = ThinVec::with_capacity(1);
563        while this.parser.token != token::Eof {
564            sub_parsers.push(this.parse_meta_item_inner()?);
565
566            if !this.parser.eat(::rustc_parse::parser::token_type::ExpTokenPair {
    tok: rustc_ast::token::Comma,
    token_type: ::rustc_parse::parser::token_type::TokenType::Comma,
}exp!(Comma)) {
567                break;
568            }
569        }
570
571        if parser.token != token::Eof {
572            parser.unexpected()?;
573        }
574
575        Ok(MetaItemListParser { sub_parsers, span })
576    }
577}
578
579#[derive(#[automatically_derived]
impl ::core::fmt::Debug for MetaItemListParser {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f,
            "MetaItemListParser", "sub_parsers", &self.sub_parsers, "span",
            &&self.span)
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for MetaItemListParser {
    #[inline]
    fn clone(&self) -> MetaItemListParser {
        MetaItemListParser {
            sub_parsers: ::core::clone::Clone::clone(&self.sub_parsers),
            span: ::core::clone::Clone::clone(&self.span),
        }
    }
}Clone)]
580pub struct MetaItemListParser {
581    sub_parsers: ThinVec<MetaItemOrLitParser>,
582    pub span: Span,
583}
584
585impl MetaItemListParser {
586    pub(crate) fn new<'sess>(
587        tokens: &TokenStream,
588        span: Span,
589        psess: &'sess ParseSess,
590        should_emit: ShouldEmit,
591    ) -> Result<Self, Diag<'sess>> {
592        MetaItemListParserContext::parse(tokens.clone(), psess, span, should_emit)
593    }
594
595    /// Lets you pick and choose as what you want to parse each element in the list
596    pub fn mixed(&self) -> impl Iterator<Item = &MetaItemOrLitParser> {
597        self.sub_parsers.iter()
598    }
599
600    pub fn len(&self) -> usize {
601        self.sub_parsers.len()
602    }
603
604    pub fn is_empty(&self) -> bool {
605        self.len() == 0
606    }
607
608    /// Returns Some if the list contains only a single element.
609    ///
610    /// Inside the Some is the parser to parse this single element.
611    pub fn single(&self) -> Option<&MetaItemOrLitParser> {
612        let mut iter = self.mixed();
613        iter.next().filter(|_| iter.next().is_none())
614    }
615}