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