Skip to main content

rustc_parse/parser/
attr.rs

1use rustc_ast as ast;
2use rustc_ast::token::{self, MetaVarKind};
3use rustc_ast::tokenstream::{ParserRange, WithTokens};
4use rustc_ast::{Attribute, attr};
5use rustc_errors::codes::*;
6use rustc_errors::{Diag, PResult, msg};
7use rustc_span::{BytePos, Span};
8use thin_vec::ThinVec;
9use tracing::debug;
10
11use super::{
12    AllowConstBlockItems, AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle,
13    Trailing, UsePreAttrPos,
14};
15use crate::parser::FnContext;
16use crate::{diagnostics, exp};
17
18// Public for rustfmt usage
19#[derive(#[automatically_derived]
impl ::core::fmt::Debug for InnerAttrPolicy {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            InnerAttrPolicy::Permitted =>
                ::core::fmt::Formatter::write_str(f, "Permitted"),
            InnerAttrPolicy::Forbidden(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "Forbidden", &__self_0),
        }
    }
}Debug)]
20pub enum InnerAttrPolicy {
21    Permitted,
22    Forbidden(Option<InnerAttrForbiddenReason>),
23}
24
25#[derive(#[automatically_derived]
impl ::core::clone::Clone for InnerAttrForbiddenReason {
    #[inline]
    fn clone(&self) -> InnerAttrForbiddenReason {
        let _: ::core::clone::AssertParamIsClone<Span>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for InnerAttrForbiddenReason { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for InnerAttrForbiddenReason {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            InnerAttrForbiddenReason::InCodeBlock =>
                ::core::fmt::Formatter::write_str(f, "InCodeBlock"),
            InnerAttrForbiddenReason::AfterOuterDocComment {
                prev_doc_comment_span: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "AfterOuterDocComment", "prev_doc_comment_span", &__self_0),
            InnerAttrForbiddenReason::AfterOuterAttribute {
                prev_outer_attr_sp: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "AfterOuterAttribute", "prev_outer_attr_sp", &__self_0),
        }
    }
}Debug)]
26pub enum InnerAttrForbiddenReason {
27    InCodeBlock,
28    AfterOuterDocComment { prev_doc_comment_span: Span },
29    AfterOuterAttribute { prev_outer_attr_sp: Span },
30}
31
32enum OuterAttributeType {
33    DocComment,
34    DocBlockComment,
35    Attribute,
36}
37
38impl<'a> Parser<'a> {
39    /// Parses attributes that appear before an item.
40    pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, AttrWrapper> {
41        let mut outer_attrs = ast::AttrVec::new();
42        let mut just_parsed_doc_comment = false;
43        let start_pos = self.num_bump_calls;
44        loop {
45            let attr = if self.check(crate::parser::token_type::ExpTokenPair {
    tok: rustc_ast::token::Pound,
    token_type: crate::parser::token_type::TokenType::Pound,
}exp!(Pound)) {
46                let prev_outer_attr_sp = outer_attrs.last().map(|attr: &Attribute| attr.span);
47
48                let inner_error_reason = if just_parsed_doc_comment {
49                    Some(InnerAttrForbiddenReason::AfterOuterDocComment {
50                        prev_doc_comment_span: prev_outer_attr_sp.unwrap(),
51                    })
52                } else {
53                    prev_outer_attr_sp.map(|prev_outer_attr_sp| {
54                        InnerAttrForbiddenReason::AfterOuterAttribute { prev_outer_attr_sp }
55                    })
56                };
57                let inner_parse_policy = InnerAttrPolicy::Forbidden(inner_error_reason);
58                just_parsed_doc_comment = false;
59                Some(self.parse_attribute(inner_parse_policy)?)
60            } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
61                if attr_style != ast::AttrStyle::Outer {
62                    let span = self.token.span;
63                    let mut err =
64                        self.dcx().struct_span_err(span, rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("expected outer doc comment"))msg!("expected outer doc comment"));
65                    err.code(E0753);
66                    if let Some(replacement_span) = self.annotate_following_item_if_applicable(
67                        &mut err,
68                        span,
69                        match comment_kind {
70                            token::CommentKind::Line => OuterAttributeType::DocComment,
71                            token::CommentKind::Block => OuterAttributeType::DocBlockComment,
72                        },
73                        true,
74                    ) {
75                        err.note(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("inner doc comments like this (starting with `//!` or `/*!`) can only appear before items"))msg!(
76                            "inner doc comments like this (starting with `//!` or `/*!`) can only appear before items"
77                        ));
78                        err.span_suggestion_verbose(
79                            replacement_span,
80                            rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("you might have meant to write a regular comment"))msg!("you might have meant to write a regular comment"),
81                            "",
82                            rustc_errors::Applicability::MachineApplicable,
83                        );
84                    }
85                    err.emit();
86                }
87                self.bump();
88                just_parsed_doc_comment = true;
89                // Always make an outer attribute - this allows us to recover from a misplaced
90                // inner attribute.
91                Some(attr::mk_doc_comment(
92                    &self.psess.attr_id_generator,
93                    comment_kind,
94                    ast::AttrStyle::Outer,
95                    data,
96                    self.prev_token.span,
97                ))
98            } else {
99                None
100            };
101
102            if let Some(attr) = attr {
103                if attr.style == ast::AttrStyle::Outer {
104                    outer_attrs.push(attr);
105                }
106            } else {
107                break;
108            }
109        }
110        Ok(AttrWrapper::new(outer_attrs, start_pos))
111    }
112
113    /// Matches `attribute = # ! [ meta_item ]`.
114    /// `inner_parse_policy` prescribes how to handle inner attributes.
115    // Public for rustfmt usage.
116    pub fn parse_attribute(
117        &mut self,
118        inner_parse_policy: InnerAttrPolicy,
119    ) -> PResult<'a, ast::Attribute> {
120        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_parse/src/parser/attr.rs:120",
                        "rustc_parse::parser::attr", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_parse/src/parser/attr.rs"),
                        ::tracing_core::__macro_support::Option::Some(120u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_parse::parser::attr"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("parse_attribute: inner_parse_policy={0:?} self.token={1:?}",
                                                    inner_parse_policy, self.token) as
                                            &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!(
121            "parse_attribute: inner_parse_policy={:?} self.token={:?}",
122            inner_parse_policy, self.token
123        );
124        let lo = self.token.span;
125        // Attributes can't have attributes of their own [Editor's note: not with that attitude]
126        self.collect_tokens_no_attrs(|this| {
127            let pound_hi = this.token.span.hi();
128            if !this.eat(crate::parser::token_type::ExpTokenPair {
                tok: rustc_ast::token::Pound,
                token_type: crate::parser::token_type::TokenType::Pound,
            }) {
    {
        ::core::panicking::panic_fmt(format_args!("parse_attribute called in non-attribute position"));
    }
};assert!(this.eat(exp!(Pound)), "parse_attribute called in non-attribute position");
129
130            let not_lo = this.token.span.lo();
131            let style =
132                if this.eat(crate::parser::token_type::ExpTokenPair {
    tok: rustc_ast::token::Bang,
    token_type: crate::parser::token_type::TokenType::Bang,
}exp!(Bang)) { ast::AttrStyle::Inner } else { ast::AttrStyle::Outer };
133
134            let mut bracket_res = this.expect(crate::parser::token_type::ExpTokenPair {
    tok: rustc_ast::token::OpenBracket,
    token_type: crate::parser::token_type::TokenType::OpenBracket,
}exp!(OpenBracket));
135            // If `#!` is not followed by `[`
136            if let Err(err) = &mut bracket_res
137                && style == ast::AttrStyle::Inner
138                && pound_hi == not_lo
139            {
140                err.note(
141                    "the token sequence `#!` here looks like the start of \
142                    a shebang interpreter directive but it is not",
143                );
144                err.help(
145                    "if you meant this to be a shebang interpreter directive, \
146                    move it to the very start of the file",
147                );
148            }
149            bracket_res?;
150
151            let attr_item = this.parse_attr_item(ForceCollect::No)?;
152            // `attr_item` will never have tokens: within `parse_attr_item`, `collect_tokens`
153            // attaches tokens only if:
154            // - `ForceCollect::Yes` is passed (not true), or
155            // - attributes on the parsed node require tokens (not true, because attr items can't
156            //   have attributes of their own, hence the empty `HasAttrs` impl for `AttrItem`).
157            if !attr_item.tokens.is_none() {
    ::core::panicking::panic("assertion failed: attr_item.tokens.is_none()")
};assert!(attr_item.tokens.is_none());
158
159            this.expect(crate::parser::token_type::ExpTokenPair {
    tok: rustc_ast::token::CloseBracket,
    token_type: crate::parser::token_type::TokenType::CloseBracket,
}exp!(CloseBracket))?;
160            let attr_sp = lo.to(this.prev_token.span);
161
162            // Emit error if inner attribute is encountered and forbidden.
163            if style == ast::AttrStyle::Inner {
164                this.error_on_forbidden_inner_attr(
165                    attr_sp,
166                    inner_parse_policy,
167                    attr_item.node.is_valid_for_outer_style(),
168                );
169            }
170
171            Ok(attr::mk_attr_from_item(
172                &self.psess.attr_id_generator,
173                attr_item.node,
174                None,
175                style,
176                attr_sp,
177            ))
178        })
179    }
180
181    fn annotate_following_item_if_applicable(
182        &self,
183        err: &mut Diag<'_>,
184        span: Span,
185        attr_type: OuterAttributeType,
186        suggest_to_outer: bool,
187    ) -> Option<Span> {
188        let mut snapshot = self.create_snapshot_for_diagnostic();
189        let lo = span.lo()
190            + BytePos(match attr_type {
191                OuterAttributeType::Attribute => 1,
192                _ => 2,
193            });
194        let hi = lo + BytePos(1);
195        let replacement_span = span.with_lo(lo).with_hi(hi);
196        if let OuterAttributeType::DocBlockComment | OuterAttributeType::DocComment = attr_type {
197            snapshot.bump();
198        }
199        loop {
200            // skip any other attributes, we want the item
201            if snapshot.token == token::Pound {
202                if let Err(err) = snapshot.parse_attribute(InnerAttrPolicy::Permitted) {
203                    err.cancel();
204                    return Some(replacement_span);
205                }
206            } else {
207                break;
208            }
209        }
210        match snapshot.parse_item_common(
211            AttrWrapper::empty(),
212            true,
213            false,
214            FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true },
215            ForceCollect::No,
216            AllowConstBlockItems::Yes,
217        ) {
218            Ok(Some(item)) => {
219                err.arg("item", item.kind.descr());
220                err.span_label(
221                    item.span,
222                    match attr_type {
223                        OuterAttributeType::Attribute => {
224                            rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("the inner attribute doesn't annotate this {$item}"))msg!("the inner attribute doesn't annotate this {$item}")
225                        }
226                        OuterAttributeType::DocComment | OuterAttributeType::DocBlockComment => {
227                            rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("the inner doc comment doesn't annotate this {$item}"))msg!("the inner doc comment doesn't annotate this {$item}")
228                        }
229                    },
230                );
231                if suggest_to_outer {
232                    err.span_suggestion_verbose(
233                        replacement_span,
234                        match attr_type {
235                            OuterAttributeType::Attribute =>  rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("to annotate the {$item}, change the attribute from inner to outer style"))msg!("to annotate the {$item}, change the attribute from inner to outer style"),
236                            OuterAttributeType::DocComment | OuterAttributeType::DocBlockComment =>  rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("to annotate the {$item}, change the doc comment from inner to outer style"))msg!("to annotate the {$item}, change the doc comment from inner to outer style"),
237                        },
238                        match attr_type {
239                            OuterAttributeType::Attribute => "",
240                            OuterAttributeType::DocBlockComment => "*",
241                            OuterAttributeType::DocComment => "/",
242                        },
243                        rustc_errors::Applicability::MachineApplicable,
244                    );
245                }
246                return None;
247            }
248            Err(item_err) => {
249                item_err.cancel();
250            }
251            Ok(None) => {}
252        }
253        Some(replacement_span)
254    }
255
256    pub(super) fn error_on_forbidden_inner_attr(
257        &self,
258        attr_sp: Span,
259        policy: InnerAttrPolicy,
260        suggest_to_outer: bool,
261    ) {
262        if let InnerAttrPolicy::Forbidden(reason) = policy {
263            let mut diag = match reason.as_ref().copied() {
264                Some(InnerAttrForbiddenReason::AfterOuterDocComment { prev_doc_comment_span }) => {
265                    self.dcx()
266                        .struct_span_err(
267                            attr_sp,
268                            rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("an inner attribute is not permitted following an outer doc comment"))msg!(
269                                "an inner attribute is not permitted following an outer doc comment"
270                            ),
271                        )
272                        .with_span_label(
273                            attr_sp,
274                            rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("not permitted following an outer doc comment"))msg!("not permitted following an outer doc comment"),
275                        )
276                        .with_span_label(prev_doc_comment_span, rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("previous doc comment"))msg!("previous doc comment"))
277                }
278                Some(InnerAttrForbiddenReason::AfterOuterAttribute { prev_outer_attr_sp }) => self
279                    .dcx()
280                    .struct_span_err(
281                        attr_sp,
282                        rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("an inner attribute is not permitted following an outer attribute"))msg!("an inner attribute is not permitted following an outer attribute"),
283                    )
284                    .with_span_label(attr_sp, rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("not permitted following an outer attribute"))msg!("not permitted following an outer attribute"))
285                    .with_span_label(prev_outer_attr_sp, rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("previous outer attribute"))msg!("previous outer attribute")),
286                Some(InnerAttrForbiddenReason::InCodeBlock) | None => self.dcx().struct_span_err(
287                    attr_sp,
288                    rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("an inner attribute is not permitted in this context"))msg!("an inner attribute is not permitted in this context"),
289                ),
290            };
291
292            diag.note(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files"))msg!("inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files"));
293            if self
294                .annotate_following_item_if_applicable(
295                    &mut diag,
296                    attr_sp,
297                    OuterAttributeType::Attribute,
298                    suggest_to_outer,
299                )
300                .is_some()
301            {
302                diag.note(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("outer attributes, like `#[test]`, annotate the item following them"))msg!(
303                    "outer attributes, like `#[test]`, annotate the item following them"
304                ));
305            };
306            diag.emit();
307        }
308    }
309
310    /// Parses an inner part of an attribute (the path and following tokens).
311    /// The tokens must be either a delimited token stream, or empty token stream,
312    /// or the "legacy" key-value form.
313    ///     PATH `(` TOKEN_STREAM `)`
314    ///     PATH `[` TOKEN_STREAM `]`
315    ///     PATH `{` TOKEN_STREAM `}`
316    ///     PATH
317    ///     PATH `=` UNSUFFIXED_LIT
318    /// The delimiters or `=` are still put into the resulting token stream.
319    pub fn parse_attr_item(
320        &mut self,
321        force_collect: ForceCollect,
322    ) -> PResult<'a, WithTokens<ast::AttrItem>> {
323        if let Some(item) = self.eat_metavar_seq_with_matcher(
324            |mv_kind| #[allow(non_exhaustive_omitted_patterns)] match mv_kind {
    MetaVarKind::Meta { .. } => true,
    _ => false,
}matches!(mv_kind, MetaVarKind::Meta { .. }),
325            |this| this.parse_attr_item(force_collect),
326        ) {
327            return Ok(item);
328        }
329
330        // Attr items don't have attributes.
331        self.collect_tokens(None, AttrWrapper::empty(), force_collect, |this, _empty_attrs| {
332            let lo = this.token.span;
333            let is_unsafe = this.eat_keyword(crate::parser::token_type::ExpKeywordPair {
    kw: rustc_span::symbol::kw::Unsafe,
    token_type: crate::parser::token_type::TokenType::KwUnsafe,
}exp!(Unsafe));
334            let unsafety = if is_unsafe {
335                let unsafe_span = this.prev_token.span;
336                this.expect(crate::parser::token_type::ExpTokenPair {
    tok: rustc_ast::token::OpenParen,
    token_type: crate::parser::token_type::TokenType::OpenParen,
}exp!(OpenParen))?;
337                ast::Safety::Unsafe(unsafe_span)
338            } else {
339                ast::Safety::Default
340            };
341
342            let path = this.parse_path(PathStyle::Mod)?;
343            let args = this.parse_attr_args()?;
344            if is_unsafe {
345                this.expect(crate::parser::token_type::ExpTokenPair {
    tok: rustc_ast::token::CloseParen,
    token_type: crate::parser::token_type::TokenType::CloseParen,
}exp!(CloseParen))?;
346            }
347            let span = lo.to(this.prev_token.span);
348            Ok((
349                WithTokens::new(ast::AttrItem { unsafety, path, args, span }),
350                Trailing::No,
351                UsePreAttrPos::No,
352            ))
353        })
354    }
355
356    /// Parses attributes that appear after the opening of an item. These should
357    /// be preceded by an exclamation mark, but we accept and warn about one
358    /// terminated by a semicolon.
359    ///
360    /// Matches `inner_attrs*`.
361    pub fn parse_inner_attributes(&mut self) -> PResult<'a, ast::AttrVec> {
362        let mut attrs = ast::AttrVec::new();
363        loop {
364            let start_pos = self.num_bump_calls;
365            // Only try to parse if it is an inner attribute (has `!`).
366            let attr = if self.check(crate::parser::token_type::ExpTokenPair {
    tok: rustc_ast::token::Pound,
    token_type: crate::parser::token_type::TokenType::Pound,
}exp!(Pound)) && self.look_ahead(1, |t| t == &token::Bang) {
367                Some(self.parse_attribute(InnerAttrPolicy::Permitted)?)
368            } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
369                if attr_style == ast::AttrStyle::Inner {
370                    self.bump();
371                    Some(attr::mk_doc_comment(
372                        &self.psess.attr_id_generator,
373                        comment_kind,
374                        attr_style,
375                        data,
376                        self.prev_token.span,
377                    ))
378                } else {
379                    None
380                }
381            } else {
382                None
383            };
384            if let Some(attr) = attr {
385                // If we are currently capturing tokens (i.e. we are within a call to
386                // `Parser::collect_tokens`) record the token positions of this inner attribute,
387                // for possible later processing in a `LazyAttrTokenStream`.
388                if let Capturing::Yes = self.capture_state.capturing {
389                    let end_pos = self.num_bump_calls;
390                    let parser_range = ParserRange(start_pos..end_pos);
391                    self.capture_state.inner_attr_parser_ranges.insert(attr.id, parser_range);
392                }
393                attrs.push(attr);
394            } else {
395                break;
396            }
397        }
398        Ok(attrs)
399    }
400
401    // Note: must be unsuffixed.
402    pub(crate) fn parse_unsuffixed_meta_item_lit(&mut self) -> PResult<'a, ast::MetaItemLit> {
403        let lit = self.parse_meta_item_lit()?;
404        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_parse/src/parser/attr.rs:404",
                        "rustc_parse::parser::attr", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_parse/src/parser/attr.rs"),
                        ::tracing_core::__macro_support::Option::Some(404u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_parse::parser::attr"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("checking if {0:?} is unsuffixed",
                                                    lit) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("checking if {:?} is unsuffixed", lit);
405
406        if !lit.kind.is_unsuffixed() {
407            self.dcx().emit_err(diagnostics::SuffixedLiteralInAttribute { span: lit.span });
408        }
409
410        Ok(lit)
411    }
412
413    /// Matches `COMMASEP(meta_item_inner)`.
414    pub fn parse_meta_seq_top(&mut self) -> PResult<'a, ThinVec<ast::MetaItemInner>> {
415        // Presumably, the majority of the time there will only be one attr.
416        let mut nmis = ThinVec::with_capacity(1);
417        while self.token != token::Eof {
418            nmis.push(self.parse_meta_item_inner()?);
419            if !self.eat(crate::parser::token_type::ExpTokenPair {
    tok: rustc_ast::token::Comma,
    token_type: crate::parser::token_type::TokenType::Comma,
}exp!(Comma)) {
420                break;
421            }
422        }
423        Ok(nmis)
424    }
425
426    /// Parse a meta item per RFC 1559.
427    ///
428    /// ```ebnf
429    /// MetaItem = SimplePath ( '=' UNSUFFIXED_LIT | '(' MetaSeq? ')' )? ;
430    /// MetaSeq = MetaItemInner (',' MetaItemInner)* ','? ;
431    /// ```
432    pub fn parse_meta_item(&mut self) -> PResult<'a, ast::MetaItem> {
433        if let Some(MetaVarKind::Meta { has_meta_form }) = self.token.is_metavar_seq() {
434            return if has_meta_form {
435                let attr_item = self
436                    .eat_metavar_seq(MetaVarKind::Meta { has_meta_form: true }, |this| {
437                        this.parse_attr_item(ForceCollect::No)
438                    })
439                    .unwrap()
440                    .node;
441                Ok(attr_item.meta(attr_item.path.span).unwrap())
442            } else {
443                self.unexpected_any()
444            };
445        }
446        let lo = self.token.span;
447
448        let path = self.parse_path(PathStyle::Mod)?;
449        let kind = self.parse_meta_item_kind()?;
450        let span = lo.to(self.prev_token.span);
451
452        Ok(ast::MetaItem { unsafety: ast::Safety::Default, path, kind, span })
453    }
454
455    pub(crate) fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> {
456        Ok(if self.eat(crate::parser::token_type::ExpTokenPair {
    tok: rustc_ast::token::Eq,
    token_type: crate::parser::token_type::TokenType::Eq,
}exp!(Eq)) {
457            ast::MetaItemKind::NameValue(self.parse_unsuffixed_meta_item_lit()?)
458        } else if self.check(crate::parser::token_type::ExpTokenPair {
    tok: rustc_ast::token::OpenParen,
    token_type: crate::parser::token_type::TokenType::OpenParen,
}exp!(OpenParen)) {
459            let (list, _) = self.parse_paren_comma_seq(|p| p.parse_meta_item_inner())?;
460            ast::MetaItemKind::List(list)
461        } else {
462            ast::MetaItemKind::Word
463        })
464    }
465
466    /// Parse an inner meta item per RFC 1559.
467    ///
468    /// ```ebnf
469    /// MetaItemInner = UNSUFFIXED_LIT | MetaItem ;
470    /// ```
471    pub fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::MetaItemInner> {
472        match self.parse_unsuffixed_meta_item_lit() {
473            Ok(lit) => return Ok(ast::MetaItemInner::Lit(lit)),
474            Err(err) => err.cancel(), // we provide a better error below
475        }
476
477        match self.parse_meta_item() {
478            Ok(mi) => return Ok(ast::MetaItemInner::MetaItem(mi)),
479            Err(err) => err.cancel(), // we provide a better error below
480        }
481
482        let mut err = diagnostics::InvalidMetaItem {
483            span: self.token.span,
484            descr: super::token_descr(&self.token),
485            quote_ident_sugg: None,
486        };
487
488        // Suggest quoting idents, e.g. in `#[cfg(key = value)]`. We don't use `Token::ident` and
489        // don't `uninterpolate` the token to avoid suggesting anything butchered or questionable
490        // when macro metavariables are involved.
491        if self.prev_token == token::Eq
492            && let token::Ident(..) = self.token.kind
493        {
494            let before = self.token.span.shrink_to_lo();
495            while let token::Ident(..) = self.token.kind {
496                self.bump();
497            }
498            err.quote_ident_sugg = Some(diagnostics::InvalidMetaItemQuoteIdentSugg {
499                before,
500                after: self.prev_token.span.shrink_to_hi(),
501            });
502        }
503
504        Err(self.dcx().create_err(err))
505    }
506}