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