]> git.lizzy.rs Git - rust.git/blob - src/librustc_parse/parser/attr.rs
Rollup merge of #75485 - RalfJung:pin, r=nagisa
[rust.git] / src / librustc_parse / parser / attr.rs
1 use super::{Parser, PathStyle};
2 use rustc_ast::ast;
3 use rustc_ast::attr;
4 use rustc_ast::token::{self, Nonterminal};
5 use rustc_ast_pretty::pprust;
6 use rustc_errors::{error_code, PResult};
7 use rustc_span::Span;
8
9 use tracing::debug;
10
11 #[derive(Debug)]
12 pub(super) enum InnerAttrPolicy<'a> {
13     Permitted,
14     Forbidden { reason: &'a str, saw_doc_comment: bool, prev_attr_sp: Option<Span> },
15 }
16
17 const DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG: &str = "an inner attribute is not \
18                                                      permitted in this context";
19
20 pub(super) const DEFAULT_INNER_ATTR_FORBIDDEN: InnerAttrPolicy<'_> = InnerAttrPolicy::Forbidden {
21     reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG,
22     saw_doc_comment: false,
23     prev_attr_sp: None,
24 };
25
26 impl<'a> Parser<'a> {
27     /// Parses attributes that appear before an item.
28     pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
29         let mut attrs: Vec<ast::Attribute> = Vec::new();
30         let mut just_parsed_doc_comment = false;
31         loop {
32             debug!("parse_outer_attributes: self.token={:?}", self.token);
33             if self.check(&token::Pound) {
34                 let inner_error_reason = if just_parsed_doc_comment {
35                     "an inner attribute is not permitted following an outer doc comment"
36                 } else if !attrs.is_empty() {
37                     "an inner attribute is not permitted following an outer attribute"
38                 } else {
39                     DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG
40                 };
41                 let inner_parse_policy = InnerAttrPolicy::Forbidden {
42                     reason: inner_error_reason,
43                     saw_doc_comment: just_parsed_doc_comment,
44                     prev_attr_sp: attrs.last().map(|a| a.span),
45                 };
46                 let attr = self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?;
47                 attrs.push(attr);
48                 just_parsed_doc_comment = false;
49             } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
50                 let attr = attr::mk_doc_comment(comment_kind, attr_style, data, self.token.span);
51                 if attr.style != ast::AttrStyle::Outer {
52                     self.sess
53                         .span_diagnostic
54                         .struct_span_err_with_code(
55                             self.token.span,
56                             "expected outer doc comment",
57                             error_code!(E0753),
58                         )
59                         .note(
60                             "inner doc comments like this (starting with \
61                              `//!` or `/*!`) can only appear before items",
62                         )
63                         .emit();
64                 }
65                 attrs.push(attr);
66                 self.bump();
67                 just_parsed_doc_comment = true;
68             } else {
69                 break;
70             }
71         }
72         Ok(attrs)
73     }
74
75     /// Matches `attribute = # ! [ meta_item ]`.
76     ///
77     /// If `permit_inner` is `true`, then a leading `!` indicates an inner
78     /// attribute.
79     pub fn parse_attribute(&mut self, permit_inner: bool) -> PResult<'a, ast::Attribute> {
80         debug!("parse_attribute: permit_inner={:?} self.token={:?}", permit_inner, self.token);
81         let inner_parse_policy =
82             if permit_inner { InnerAttrPolicy::Permitted } else { DEFAULT_INNER_ATTR_FORBIDDEN };
83         self.parse_attribute_with_inner_parse_policy(inner_parse_policy)
84     }
85
86     /// The same as `parse_attribute`, except it takes in an `InnerAttrPolicy`
87     /// that prescribes how to handle inner attributes.
88     fn parse_attribute_with_inner_parse_policy(
89         &mut self,
90         inner_parse_policy: InnerAttrPolicy<'_>,
91     ) -> PResult<'a, ast::Attribute> {
92         debug!(
93             "parse_attribute_with_inner_parse_policy: inner_parse_policy={:?} self.token={:?}",
94             inner_parse_policy, self.token
95         );
96         let lo = self.token.span;
97         let (span, item, style) = if self.eat(&token::Pound) {
98             let style =
99                 if self.eat(&token::Not) { ast::AttrStyle::Inner } else { ast::AttrStyle::Outer };
100
101             self.expect(&token::OpenDelim(token::Bracket))?;
102             let item = self.parse_attr_item()?;
103             self.expect(&token::CloseDelim(token::Bracket))?;
104             let attr_sp = lo.to(self.prev_token.span);
105
106             // Emit error if inner attribute is encountered and forbidden.
107             if style == ast::AttrStyle::Inner {
108                 self.error_on_forbidden_inner_attr(attr_sp, inner_parse_policy);
109             }
110
111             (attr_sp, item, style)
112         } else {
113             let token_str = pprust::token_to_string(&self.token);
114             let msg = &format!("expected `#`, found `{}`", token_str);
115             return Err(self.struct_span_err(self.token.span, msg));
116         };
117
118         Ok(attr::mk_attr_from_item(style, item, span))
119     }
120
121     pub(super) fn error_on_forbidden_inner_attr(&self, attr_sp: Span, policy: InnerAttrPolicy<'_>) {
122         if let InnerAttrPolicy::Forbidden { reason, saw_doc_comment, prev_attr_sp } = policy {
123             let prev_attr_note =
124                 if saw_doc_comment { "previous doc comment" } else { "previous outer attribute" };
125
126             let mut diag = self.struct_span_err(attr_sp, reason);
127
128             if let Some(prev_attr_sp) = prev_attr_sp {
129                 diag.span_label(attr_sp, "not permitted following an outer attribute")
130                     .span_label(prev_attr_sp, prev_attr_note);
131             }
132
133             diag.note(
134                 "inner attributes, like `#![no_std]`, annotate the item enclosing them, \
135                 and are usually found at the beginning of source files. \
136                 Outer attributes, like `#[test]`, annotate the item following them.",
137             )
138             .emit();
139         }
140     }
141
142     /// Parses an inner part of an attribute (the path and following tokens).
143     /// The tokens must be either a delimited token stream, or empty token stream,
144     /// or the "legacy" key-value form.
145     ///     PATH `(` TOKEN_STREAM `)`
146     ///     PATH `[` TOKEN_STREAM `]`
147     ///     PATH `{` TOKEN_STREAM `}`
148     ///     PATH
149     ///     PATH `=` UNSUFFIXED_LIT
150     /// The delimiters or `=` are still put into the resulting token stream.
151     pub fn parse_attr_item(&mut self) -> PResult<'a, ast::AttrItem> {
152         let item = match self.token.kind {
153             token::Interpolated(ref nt) => match **nt {
154                 Nonterminal::NtMeta(ref item) => Some(item.clone().into_inner()),
155                 _ => None,
156             },
157             _ => None,
158         };
159         Ok(if let Some(item) = item {
160             self.bump();
161             item
162         } else {
163             let path = self.parse_path(PathStyle::Mod)?;
164             let args = self.parse_attr_args()?;
165             ast::AttrItem { path, args }
166         })
167     }
168
169     /// Parses attributes that appear after the opening of an item. These should
170     /// be preceded by an exclamation mark, but we accept and warn about one
171     /// terminated by a semicolon.
172     ///
173     /// Matches `inner_attrs*`.
174     crate fn parse_inner_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
175         let mut attrs: Vec<ast::Attribute> = vec![];
176         loop {
177             // Only try to parse if it is an inner attribute (has `!`).
178             if self.check(&token::Pound) && self.look_ahead(1, |t| t == &token::Not) {
179                 let attr = self.parse_attribute(true)?;
180                 assert_eq!(attr.style, ast::AttrStyle::Inner);
181                 attrs.push(attr);
182             } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
183                 // We need to get the position of this token before we bump.
184                 let attr = attr::mk_doc_comment(comment_kind, attr_style, data, self.token.span);
185                 if attr.style == ast::AttrStyle::Inner {
186                     attrs.push(attr);
187                     self.bump();
188                 } else {
189                     break;
190                 }
191             } else {
192                 break;
193             }
194         }
195         Ok(attrs)
196     }
197
198     crate fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> {
199         let lit = self.parse_lit()?;
200         debug!("checking if {:?} is unusuffixed", lit);
201
202         if !lit.kind.is_unsuffixed() {
203             self.struct_span_err(lit.span, "suffixed literals are not allowed in attributes")
204                 .help(
205                     "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \
206                     use an unsuffixed version (`1`, `1.0`, etc.)",
207                 )
208                 .emit();
209         }
210
211         Ok(lit)
212     }
213
214     /// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited.
215     pub fn parse_cfg_attr(&mut self) -> PResult<'a, (ast::MetaItem, Vec<(ast::AttrItem, Span)>)> {
216         let cfg_predicate = self.parse_meta_item()?;
217         self.expect(&token::Comma)?;
218
219         // Presumably, the majority of the time there will only be one attr.
220         let mut expanded_attrs = Vec::with_capacity(1);
221         while self.token.kind != token::Eof {
222             let lo = self.token.span;
223             let item = self.parse_attr_item()?;
224             expanded_attrs.push((item, lo.to(self.prev_token.span)));
225             if !self.eat(&token::Comma) {
226                 break;
227             }
228         }
229
230         Ok((cfg_predicate, expanded_attrs))
231     }
232
233     /// Matches `COMMASEP(meta_item_inner)`.
234     crate fn parse_meta_seq_top(&mut self) -> PResult<'a, Vec<ast::NestedMetaItem>> {
235         // Presumably, the majority of the time there will only be one attr.
236         let mut nmis = Vec::with_capacity(1);
237         while self.token.kind != token::Eof {
238             nmis.push(self.parse_meta_item_inner()?);
239             if !self.eat(&token::Comma) {
240                 break;
241             }
242         }
243         Ok(nmis)
244     }
245
246     /// Matches the following grammar (per RFC 1559).
247     ///
248     ///     meta_item : PATH ( '=' UNSUFFIXED_LIT | '(' meta_item_inner? ')' )? ;
249     ///     meta_item_inner : (meta_item | UNSUFFIXED_LIT) (',' meta_item_inner)? ;
250     pub fn parse_meta_item(&mut self) -> PResult<'a, ast::MetaItem> {
251         let nt_meta = match self.token.kind {
252             token::Interpolated(ref nt) => match **nt {
253                 token::NtMeta(ref e) => Some(e.clone()),
254                 _ => None,
255             },
256             _ => None,
257         };
258
259         if let Some(item) = nt_meta {
260             return match item.meta(item.path.span) {
261                 Some(meta) => {
262                     self.bump();
263                     Ok(meta)
264                 }
265                 None => self.unexpected(),
266             };
267         }
268
269         let lo = self.token.span;
270         let path = self.parse_path(PathStyle::Mod)?;
271         let kind = self.parse_meta_item_kind()?;
272         let span = lo.to(self.prev_token.span);
273         Ok(ast::MetaItem { path, kind, span })
274     }
275
276     crate fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> {
277         Ok(if self.eat(&token::Eq) {
278             ast::MetaItemKind::NameValue(self.parse_unsuffixed_lit()?)
279         } else if self.check(&token::OpenDelim(token::Paren)) {
280             // Matches `meta_seq = ( COMMASEP(meta_item_inner) )`.
281             let (list, _) = self.parse_paren_comma_seq(|p| p.parse_meta_item_inner())?;
282             ast::MetaItemKind::List(list)
283         } else {
284             ast::MetaItemKind::Word
285         })
286     }
287
288     /// Matches `meta_item_inner : (meta_item | UNSUFFIXED_LIT) ;`.
289     fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::NestedMetaItem> {
290         match self.parse_unsuffixed_lit() {
291             Ok(lit) => return Ok(ast::NestedMetaItem::Literal(lit)),
292             Err(ref mut err) => err.cancel(),
293         }
294
295         match self.parse_meta_item() {
296             Ok(mi) => return Ok(ast::NestedMetaItem::MetaItem(mi)),
297             Err(ref mut err) => err.cancel(),
298         }
299
300         let found = pprust::token_to_string(&self.token);
301         let msg = format!("expected unsuffixed literal or identifier, found `{}`", found);
302         Err(self.struct_span_err(self.token.span, &msg))
303     }
304 }