]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_parse/src/parser/attr.rs
Auto merge of #77616 - jyn514:no-normalize, r=lcnr
[rust.git] / compiler / rustc_parse / src / parser / attr.rs
1 use super::{Parser, PathStyle};
2 use rustc_ast as 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::{sym, Span};
8
9 use tracing::debug;
10
11 // Public for rustfmt usage
12 #[derive(Debug)]
13 pub enum InnerAttrPolicy<'a> {
14     Permitted,
15     Forbidden { reason: &'a str, saw_doc_comment: bool, prev_attr_sp: Option<Span> },
16 }
17
18 const DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG: &str = "an inner attribute is not \
19                                                      permitted in this context";
20
21 pub(super) const DEFAULT_INNER_ATTR_FORBIDDEN: InnerAttrPolicy<'_> = InnerAttrPolicy::Forbidden {
22     reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG,
23     saw_doc_comment: false,
24     prev_attr_sp: None,
25 };
26
27 impl<'a> Parser<'a> {
28     /// Parses attributes that appear before an item.
29     pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
30         let mut attrs: Vec<ast::Attribute> = Vec::new();
31         let mut just_parsed_doc_comment = false;
32         loop {
33             debug!("parse_outer_attributes: self.token={:?}", self.token);
34             let attr = if self.check(&token::Pound) {
35                 let inner_error_reason = if just_parsed_doc_comment {
36                     "an inner attribute is not permitted following an outer doc comment"
37                 } else if !attrs.is_empty() {
38                     "an inner attribute is not permitted following an outer attribute"
39                 } else {
40                     DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG
41                 };
42                 let inner_parse_policy = InnerAttrPolicy::Forbidden {
43                     reason: inner_error_reason,
44                     saw_doc_comment: just_parsed_doc_comment,
45                     prev_attr_sp: attrs.last().map(|a| a.span),
46                 };
47                 just_parsed_doc_comment = false;
48                 Some(self.parse_attribute(inner_parse_policy)?)
49             } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
50                 if attr_style != ast::AttrStyle::Outer {
51                     self.sess
52                         .span_diagnostic
53                         .struct_span_err_with_code(
54                             self.token.span,
55                             "expected outer doc comment",
56                             error_code!(E0753),
57                         )
58                         .note(
59                             "inner doc comments like this (starting with \
60                          `//!` or `/*!`) can only appear before items",
61                         )
62                         .emit();
63                 }
64                 self.bump();
65                 just_parsed_doc_comment = true;
66                 Some(attr::mk_doc_comment(comment_kind, attr_style, data, self.prev_token.span))
67             } else {
68                 None
69             };
70
71             if let Some(attr) = attr {
72                 attrs.push(attr);
73             } else {
74                 break;
75             }
76         }
77         Ok(attrs)
78     }
79
80     /// Matches `attribute = # ! [ meta_item ]`.
81     /// `inner_parse_policy` prescribes how to handle inner attributes.
82     // Public for rustfmt usage.
83     pub fn parse_attribute(
84         &mut self,
85         inner_parse_policy: InnerAttrPolicy<'_>,
86     ) -> PResult<'a, ast::Attribute> {
87         debug!(
88             "parse_attribute: inner_parse_policy={:?} self.token={:?}",
89             inner_parse_policy, self.token
90         );
91         let lo = self.token.span;
92         let ((item, style, span), tokens) = self.collect_tokens(|this| {
93             if this.eat(&token::Pound) {
94                 let style = if this.eat(&token::Not) {
95                     ast::AttrStyle::Inner
96                 } else {
97                     ast::AttrStyle::Outer
98                 };
99
100                 this.expect(&token::OpenDelim(token::Bracket))?;
101                 let item = this.parse_attr_item(false)?;
102                 this.expect(&token::CloseDelim(token::Bracket))?;
103                 let attr_sp = lo.to(this.prev_token.span);
104
105                 // Emit error if inner attribute is encountered and forbidden.
106                 if style == ast::AttrStyle::Inner {
107                     this.error_on_forbidden_inner_attr(attr_sp, inner_parse_policy);
108                 }
109
110                 Ok((item, style, attr_sp))
111             } else {
112                 let token_str = pprust::token_to_string(&this.token);
113                 let msg = &format!("expected `#`, found `{}`", token_str);
114                 Err(this.struct_span_err(this.token.span, msg))
115             }
116         })?;
117
118         Ok(attr::mk_attr_from_item(item, tokens, style, 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, capture_tokens: bool) -> 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 do_parse = |this: &mut Self| {
164                 let path = this.parse_path(PathStyle::Mod)?;
165                 let args = this.parse_attr_args()?;
166                 Ok(ast::AttrItem { path, args, tokens: None })
167             };
168             if capture_tokens {
169                 let (mut item, tokens) = self.collect_tokens(do_parse)?;
170                 item.tokens = tokens;
171                 item
172             } else {
173                 do_parse(self)?
174             }
175         })
176     }
177
178     /// Parses attributes that appear after the opening of an item. These should
179     /// be preceded by an exclamation mark, but we accept and warn about one
180     /// terminated by a semicolon.
181     ///
182     /// Matches `inner_attrs*`.
183     crate fn parse_inner_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
184         let mut attrs: Vec<ast::Attribute> = vec![];
185         loop {
186             // Only try to parse if it is an inner attribute (has `!`).
187             let attr = if self.check(&token::Pound) && self.look_ahead(1, |t| t == &token::Not) {
188                 Some(self.parse_attribute(InnerAttrPolicy::Permitted)?)
189             } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
190                 if attr_style == ast::AttrStyle::Inner {
191                     self.bump();
192                     Some(attr::mk_doc_comment(comment_kind, attr_style, data, self.prev_token.span))
193                 } else {
194                     None
195                 }
196             } else {
197                 None
198             };
199             if let Some(attr) = attr {
200                 attrs.push(attr);
201             } else {
202                 break;
203             }
204         }
205         Ok(attrs)
206     }
207
208     crate fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> {
209         let lit = self.parse_lit()?;
210         debug!("checking if {:?} is unusuffixed", lit);
211
212         if !lit.kind.is_unsuffixed() {
213             self.struct_span_err(lit.span, "suffixed literals are not allowed in attributes")
214                 .help(
215                     "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \
216                     use an unsuffixed version (`1`, `1.0`, etc.)",
217                 )
218                 .emit();
219         }
220
221         Ok(lit)
222     }
223
224     /// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited.
225     pub fn parse_cfg_attr(&mut self) -> PResult<'a, (ast::MetaItem, Vec<(ast::AttrItem, Span)>)> {
226         let cfg_predicate = self.parse_meta_item()?;
227         self.expect(&token::Comma)?;
228
229         // Presumably, the majority of the time there will only be one attr.
230         let mut expanded_attrs = Vec::with_capacity(1);
231         while self.token.kind != token::Eof {
232             let lo = self.token.span;
233             let item = self.parse_attr_item(true)?;
234             expanded_attrs.push((item, lo.to(self.prev_token.span)));
235             if !self.eat(&token::Comma) {
236                 break;
237             }
238         }
239
240         Ok((cfg_predicate, expanded_attrs))
241     }
242
243     /// Matches `COMMASEP(meta_item_inner)`.
244     crate fn parse_meta_seq_top(&mut self) -> PResult<'a, Vec<ast::NestedMetaItem>> {
245         // Presumably, the majority of the time there will only be one attr.
246         let mut nmis = Vec::with_capacity(1);
247         while self.token.kind != token::Eof {
248             nmis.push(self.parse_meta_item_inner()?);
249             if !self.eat(&token::Comma) {
250                 break;
251             }
252         }
253         Ok(nmis)
254     }
255
256     /// Matches the following grammar (per RFC 1559).
257     ///
258     ///     meta_item : PATH ( '=' UNSUFFIXED_LIT | '(' meta_item_inner? ')' )? ;
259     ///     meta_item_inner : (meta_item | UNSUFFIXED_LIT) (',' meta_item_inner)? ;
260     pub fn parse_meta_item(&mut self) -> PResult<'a, ast::MetaItem> {
261         let nt_meta = match self.token.kind {
262             token::Interpolated(ref nt) => match **nt {
263                 token::NtMeta(ref e) => Some(e.clone()),
264                 _ => None,
265             },
266             _ => None,
267         };
268
269         if let Some(item) = nt_meta {
270             return match item.meta(item.path.span) {
271                 Some(meta) => {
272                     self.bump();
273                     Ok(meta)
274                 }
275                 None => self.unexpected(),
276             };
277         }
278
279         let lo = self.token.span;
280         let path = self.parse_path(PathStyle::Mod)?;
281         let kind = self.parse_meta_item_kind()?;
282         let span = lo.to(self.prev_token.span);
283         Ok(ast::MetaItem { path, kind, span })
284     }
285
286     crate fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> {
287         Ok(if self.eat(&token::Eq) {
288             ast::MetaItemKind::NameValue(self.parse_unsuffixed_lit()?)
289         } else if self.check(&token::OpenDelim(token::Paren)) {
290             // Matches `meta_seq = ( COMMASEP(meta_item_inner) )`.
291             let (list, _) = self.parse_paren_comma_seq(|p| p.parse_meta_item_inner())?;
292             ast::MetaItemKind::List(list)
293         } else {
294             ast::MetaItemKind::Word
295         })
296     }
297
298     /// Matches `meta_item_inner : (meta_item | UNSUFFIXED_LIT) ;`.
299     fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::NestedMetaItem> {
300         match self.parse_unsuffixed_lit() {
301             Ok(lit) => return Ok(ast::NestedMetaItem::Literal(lit)),
302             Err(ref mut err) => err.cancel(),
303         }
304
305         match self.parse_meta_item() {
306             Ok(mi) => return Ok(ast::NestedMetaItem::MetaItem(mi)),
307             Err(ref mut err) => err.cancel(),
308         }
309
310         let found = pprust::token_to_string(&self.token);
311         let msg = format!("expected unsuffixed literal or identifier, found `{}`", found);
312         Err(self.struct_span_err(self.token.span, &msg))
313     }
314 }
315
316 pub fn maybe_needs_tokens(attrs: &[ast::Attribute]) -> bool {
317     // One of the attributes may either itself be a macro, or apply derive macros (`derive`),
318     // or expand to macro attributes (`cfg_attr`).
319     attrs.iter().any(|attr| {
320         attr.ident().map_or(true, |ident| {
321             ident.name == sym::derive
322                 || ident.name == sym::cfg_attr
323                 || !rustc_feature::is_builtin_attr_name(ident.name)
324         })
325     })
326 }