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