rustc_parse/parser/
attr.rs

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