rustc_parse/parser/
attr.rs

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