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

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy