]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_parse/src/parser/attr.rs
0ed24fe849c079ba0b0d186bb652ab258583363d
[rust.git] / compiler / rustc_parse / src / parser / attr.rs
1 use crate::errors::{InvalidMetaItem, SuffixedLiteralInAttribute};
2
3 use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle};
4 use rustc_ast as ast;
5 use rustc_ast::attr;
6 use rustc_ast::token::{self, Delimiter, Nonterminal};
7 use rustc_errors::{error_code, fluent, Diagnostic, IntoDiagnostic, PResult};
8 use rustc_span::{sym, BytePos, Span};
9 use std::convert::TryInto;
10
11 // Public for rustfmt usage
12 #[derive(Debug)]
13 pub enum InnerAttrPolicy {
14     Permitted,
15     Forbidden(Option<InnerAttrForbiddenReason>),
16 }
17
18 #[derive(Clone, Copy, Debug)]
19 pub enum InnerAttrForbiddenReason {
20     InCodeBlock,
21     AfterOuterDocComment { prev_doc_comment_span: Span },
22     AfterOuterAttribute { prev_outer_attr_sp: Span },
23 }
24
25 enum OuterAttributeType {
26     DocComment,
27     DocBlockComment,
28     Attribute,
29 }
30
31 impl<'a> Parser<'a> {
32     /// Parses attributes that appear before an item.
33     pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, AttrWrapper> {
34         let mut outer_attrs = ast::AttrVec::new();
35         let mut just_parsed_doc_comment = false;
36         let start_pos = self.token_cursor.num_next_calls;
37         loop {
38             let attr = if self.check(&token::Pound) {
39                 let prev_outer_attr_sp = outer_attrs.last().map(|attr| attr.span);
40
41                 let inner_error_reason = if just_parsed_doc_comment {
42                     Some(InnerAttrForbiddenReason::AfterOuterDocComment {
43                         prev_doc_comment_span: prev_outer_attr_sp.unwrap(),
44                     })
45                 } else if let Some(prev_outer_attr_sp) = prev_outer_attr_sp {
46                     Some(InnerAttrForbiddenReason::AfterOuterAttribute { prev_outer_attr_sp })
47                 } else {
48                     None
49                 };
50                 let inner_parse_policy = InnerAttrPolicy::Forbidden(inner_error_reason);
51                 just_parsed_doc_comment = false;
52                 Some(self.parse_attribute(inner_parse_policy)?)
53             } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
54                 if attr_style != ast::AttrStyle::Outer {
55                     let span = self.token.span;
56                     let mut err = self.sess.span_diagnostic.struct_span_err_with_code(
57                         span,
58                         fluent::parse_inner_doc_comment_not_permitted,
59                         error_code!(E0753),
60                     );
61                     if let Some(replacement_span) = self.annotate_following_item_if_applicable(
62                         &mut err,
63                         span,
64                         match comment_kind {
65                             token::CommentKind::Line => OuterAttributeType::DocComment,
66                             token::CommentKind::Block => OuterAttributeType::DocBlockComment,
67                         },
68                     ) {
69                         err.note(fluent::note);
70                         err.span_suggestion_verbose(
71                             replacement_span,
72                             fluent::suggestion,
73                             "",
74                             rustc_errors::Applicability::MachineApplicable,
75                         );
76                     }
77                     err.emit();
78                 }
79                 self.bump();
80                 just_parsed_doc_comment = true;
81                 // Always make an outer attribute - this allows us to recover from a misplaced
82                 // inner attribute.
83                 Some(attr::mk_doc_comment(
84                     &self.sess.attr_id_generator,
85                     comment_kind,
86                     ast::AttrStyle::Outer,
87                     data,
88                     self.prev_token.span,
89                 ))
90             } else {
91                 None
92             };
93
94             if let Some(attr) = attr {
95                 if attr.style == ast::AttrStyle::Outer {
96                     outer_attrs.push(attr);
97                 }
98             } else {
99                 break;
100             }
101         }
102         Ok(AttrWrapper::new(outer_attrs, start_pos))
103     }
104
105     /// Matches `attribute = # ! [ meta_item ]`.
106     /// `inner_parse_policy` prescribes how to handle inner attributes.
107     // Public for rustfmt usage.
108     pub fn parse_attribute(
109         &mut self,
110         inner_parse_policy: InnerAttrPolicy,
111     ) -> PResult<'a, ast::Attribute> {
112         debug!(
113             "parse_attribute: inner_parse_policy={:?} self.token={:?}",
114             inner_parse_policy, self.token
115         );
116         let lo = self.token.span;
117         // Attributes can't have attributes of their own [Editor's note: not with that attitude]
118         self.collect_tokens_no_attrs(|this| {
119             assert!(this.eat(&token::Pound), "parse_attribute called in non-attribute position");
120
121             let style =
122                 if this.eat(&token::Not) { ast::AttrStyle::Inner } else { ast::AttrStyle::Outer };
123
124             this.expect(&token::OpenDelim(Delimiter::Bracket))?;
125             let item = this.parse_attr_item(false)?;
126             this.expect(&token::CloseDelim(Delimiter::Bracket))?;
127             let attr_sp = lo.to(this.prev_token.span);
128
129             // Emit error if inner attribute is encountered and forbidden.
130             if style == ast::AttrStyle::Inner {
131                 this.error_on_forbidden_inner_attr(attr_sp, inner_parse_policy);
132             }
133
134             Ok(attr::mk_attr_from_item(&self.sess.attr_id_generator, item, None, style, attr_sp))
135         })
136     }
137
138     fn annotate_following_item_if_applicable(
139         &self,
140         err: &mut Diagnostic,
141         span: Span,
142         attr_type: OuterAttributeType,
143     ) -> Option<Span> {
144         let mut snapshot = self.create_snapshot_for_diagnostic();
145         let lo = span.lo()
146             + BytePos(match attr_type {
147                 OuterAttributeType::Attribute => 1,
148                 _ => 2,
149             });
150         let hi = lo + BytePos(1);
151         let replacement_span = span.with_lo(lo).with_hi(hi);
152         if let OuterAttributeType::DocBlockComment | OuterAttributeType::DocComment = attr_type {
153             snapshot.bump();
154         }
155         loop {
156             // skip any other attributes, we want the item
157             if snapshot.token.kind == token::Pound {
158                 if let Err(err) = snapshot.parse_attribute(InnerAttrPolicy::Permitted) {
159                     err.cancel();
160                     return Some(replacement_span);
161                 }
162             } else {
163                 break;
164             }
165         }
166         match snapshot.parse_item_common(
167             AttrWrapper::empty(),
168             true,
169             false,
170             FnParseMode { req_name: |_| true, req_body: true },
171             ForceCollect::No,
172         ) {
173             Ok(Some(item)) => {
174                 // FIXME(#100717)
175                 err.set_arg("item", item.kind.descr());
176                 err.span_label(item.span, fluent::label_does_not_annotate_this);
177                 err.span_suggestion_verbose(
178                     replacement_span,
179                     fluent::sugg_change_inner_to_outer,
180                     match attr_type {
181                         OuterAttributeType::Attribute => "",
182                         OuterAttributeType::DocBlockComment => "*",
183                         OuterAttributeType::DocComment => "/",
184                     },
185                     rustc_errors::Applicability::MachineApplicable,
186                 );
187                 return None;
188             }
189             Err(item_err) => {
190                 item_err.cancel();
191             }
192             Ok(None) => {}
193         }
194         Some(replacement_span)
195     }
196
197     pub(super) fn error_on_forbidden_inner_attr(&self, attr_sp: Span, policy: InnerAttrPolicy) {
198         if let InnerAttrPolicy::Forbidden(reason) = policy {
199             let mut diag = match reason.as_ref().copied() {
200                 Some(InnerAttrForbiddenReason::AfterOuterDocComment { prev_doc_comment_span }) => {
201                     let mut diag = self.struct_span_err(
202                         attr_sp,
203                         fluent::parse_inner_attr_not_permitted_after_outer_doc_comment,
204                     );
205                     diag.span_label(attr_sp, fluent::label_attr)
206                         .span_label(prev_doc_comment_span, fluent::label_prev_doc_comment);
207                     diag
208                 }
209                 Some(InnerAttrForbiddenReason::AfterOuterAttribute { prev_outer_attr_sp }) => {
210                     let mut diag = self.struct_span_err(
211                         attr_sp,
212                         fluent::parse_inner_attr_not_permitted_after_outer_attr,
213                     );
214                     diag.span_label(attr_sp, fluent::label_attr)
215                         .span_label(prev_outer_attr_sp, fluent::label_prev_attr);
216                     diag
217                 }
218                 Some(InnerAttrForbiddenReason::InCodeBlock) | None => {
219                     self.struct_span_err(attr_sp, fluent::parse_inner_attr_not_permitted)
220                 }
221             };
222
223             diag.note(fluent::parse_inner_attr_explanation);
224             if self
225                 .annotate_following_item_if_applicable(
226                     &mut diag,
227                     attr_sp,
228                     OuterAttributeType::Attribute,
229                 )
230                 .is_some()
231             {
232                 diag.note(fluent::parse_outer_attr_explanation);
233             };
234             diag.emit();
235         }
236     }
237
238     /// Parses an inner part of an attribute (the path and following tokens).
239     /// The tokens must be either a delimited token stream, or empty token stream,
240     /// or the "legacy" key-value form.
241     ///     PATH `(` TOKEN_STREAM `)`
242     ///     PATH `[` TOKEN_STREAM `]`
243     ///     PATH `{` TOKEN_STREAM `}`
244     ///     PATH
245     ///     PATH `=` UNSUFFIXED_LIT
246     /// The delimiters or `=` are still put into the resulting token stream.
247     pub fn parse_attr_item(&mut self, capture_tokens: bool) -> PResult<'a, ast::AttrItem> {
248         let item = match &self.token.kind {
249             token::Interpolated(nt) => match &**nt {
250                 Nonterminal::NtMeta(item) => Some(item.clone().into_inner()),
251                 _ => None,
252             },
253             _ => None,
254         };
255         Ok(if let Some(item) = item {
256             self.bump();
257             item
258         } else {
259             let do_parse = |this: &mut Self| {
260                 let path = this.parse_path(PathStyle::Mod)?;
261                 let args = this.parse_attr_args()?;
262                 Ok(ast::AttrItem { path, args, tokens: None })
263             };
264             // Attr items don't have attributes
265             if capture_tokens { self.collect_tokens_no_attrs(do_parse) } else { do_parse(self) }?
266         })
267     }
268
269     /// Parses attributes that appear after the opening of an item. These should
270     /// be preceded by an exclamation mark, but we accept and warn about one
271     /// terminated by a semicolon.
272     ///
273     /// Matches `inner_attrs*`.
274     pub(crate) fn parse_inner_attributes(&mut self) -> PResult<'a, ast::AttrVec> {
275         let mut attrs = ast::AttrVec::new();
276         loop {
277             let start_pos: u32 = self.token_cursor.num_next_calls.try_into().unwrap();
278             // Only try to parse if it is an inner attribute (has `!`).
279             let attr = if self.check(&token::Pound) && self.look_ahead(1, |t| t == &token::Not) {
280                 Some(self.parse_attribute(InnerAttrPolicy::Permitted)?)
281             } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
282                 if attr_style == ast::AttrStyle::Inner {
283                     self.bump();
284                     Some(attr::mk_doc_comment(
285                         &self.sess.attr_id_generator,
286                         comment_kind,
287                         attr_style,
288                         data,
289                         self.prev_token.span,
290                     ))
291                 } else {
292                     None
293                 }
294             } else {
295                 None
296             };
297             if let Some(attr) = attr {
298                 let end_pos: u32 = self.token_cursor.num_next_calls.try_into().unwrap();
299                 // If we are currently capturing tokens, mark the location of this inner attribute.
300                 // If capturing ends up creating a `LazyAttrTokenStream`, we will include
301                 // this replace range with it, removing the inner attribute from the final
302                 // `AttrTokenStream`. Inner attributes are stored in the parsed AST note.
303                 // During macro expansion, they are selectively inserted back into the
304                 // token stream (the first inner attribute is removed each time we invoke the
305                 // corresponding macro).
306                 let range = start_pos..end_pos;
307                 if let Capturing::Yes = self.capture_state.capturing {
308                     self.capture_state.inner_attr_ranges.insert(attr.id, (range, vec![]));
309                 }
310                 attrs.push(attr);
311             } else {
312                 break;
313             }
314         }
315         Ok(attrs)
316     }
317
318     pub(crate) fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> {
319         let lit = self.parse_ast_lit()?;
320         debug!("checking if {:?} is unsuffixed", lit);
321
322         if !lit.kind.is_unsuffixed() {
323             self.sess.emit_err(SuffixedLiteralInAttribute { span: lit.span });
324         }
325
326         Ok(lit)
327     }
328
329     /// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited.
330     pub fn parse_cfg_attr(&mut self) -> PResult<'a, (ast::MetaItem, Vec<(ast::AttrItem, Span)>)> {
331         let cfg_predicate = self.parse_meta_item()?;
332         self.expect(&token::Comma)?;
333
334         // Presumably, the majority of the time there will only be one attr.
335         let mut expanded_attrs = Vec::with_capacity(1);
336         while self.token.kind != token::Eof {
337             let lo = self.token.span;
338             let item = self.parse_attr_item(true)?;
339             expanded_attrs.push((item, lo.to(self.prev_token.span)));
340             if !self.eat(&token::Comma) {
341                 break;
342             }
343         }
344
345         Ok((cfg_predicate, expanded_attrs))
346     }
347
348     /// Matches `COMMASEP(meta_item_inner)`.
349     pub(crate) fn parse_meta_seq_top(&mut self) -> PResult<'a, Vec<ast::NestedMetaItem>> {
350         // Presumably, the majority of the time there will only be one attr.
351         let mut nmis = Vec::with_capacity(1);
352         while self.token.kind != token::Eof {
353             nmis.push(self.parse_meta_item_inner()?);
354             if !self.eat(&token::Comma) {
355                 break;
356             }
357         }
358         Ok(nmis)
359     }
360
361     /// Matches the following grammar (per RFC 1559).
362     /// ```ebnf
363     /// meta_item : PATH ( '=' UNSUFFIXED_LIT | '(' meta_item_inner? ')' )? ;
364     /// meta_item_inner : (meta_item | UNSUFFIXED_LIT) (',' meta_item_inner)? ;
365     /// ```
366     pub fn parse_meta_item(&mut self) -> PResult<'a, ast::MetaItem> {
367         let nt_meta = match &self.token.kind {
368             token::Interpolated(nt) => match &**nt {
369                 token::NtMeta(e) => Some(e.clone()),
370                 _ => None,
371             },
372             _ => None,
373         };
374
375         if let Some(item) = nt_meta {
376             return match item.meta(item.path.span) {
377                 Some(meta) => {
378                     self.bump();
379                     Ok(meta)
380                 }
381                 None => self.unexpected(),
382             };
383         }
384
385         let lo = self.token.span;
386         let path = self.parse_path(PathStyle::Mod)?;
387         let kind = self.parse_meta_item_kind()?;
388         let span = lo.to(self.prev_token.span);
389         Ok(ast::MetaItem { path, kind, span })
390     }
391
392     pub(crate) fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> {
393         Ok(if self.eat(&token::Eq) {
394             ast::MetaItemKind::NameValue(self.parse_unsuffixed_lit()?)
395         } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
396             // Matches `meta_seq = ( COMMASEP(meta_item_inner) )`.
397             let (list, _) = self.parse_paren_comma_seq(|p| p.parse_meta_item_inner())?;
398             ast::MetaItemKind::List(list)
399         } else {
400             ast::MetaItemKind::Word
401         })
402     }
403
404     /// Matches `meta_item_inner : (meta_item | UNSUFFIXED_LIT) ;`.
405     fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::NestedMetaItem> {
406         match self.parse_unsuffixed_lit() {
407             Ok(lit) => return Ok(ast::NestedMetaItem::Literal(lit)),
408             Err(err) => err.cancel(),
409         }
410
411         match self.parse_meta_item() {
412             Ok(mi) => return Ok(ast::NestedMetaItem::MetaItem(mi)),
413             Err(err) => err.cancel(),
414         }
415
416         Err(InvalidMetaItem { span: self.token.span, token: self.token.clone() }
417             .into_diagnostic(&self.sess.span_diagnostic))
418     }
419 }
420
421 pub fn maybe_needs_tokens(attrs: &[ast::Attribute]) -> bool {
422     // One of the attributes may either itself be a macro,
423     // or expand to macro attributes (`cfg_attr`).
424     attrs.iter().any(|attr| {
425         if attr.is_doc_comment() {
426             return false;
427         }
428         attr.ident().map_or(true, |ident| {
429             ident.name == sym::cfg_attr || !rustc_feature::is_builtin_attr_name(ident.name)
430         })
431     })
432 }