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