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