1 use super::{AttrWrapper, Capturing, Parser, PathStyle};
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;
12 // Public for rustfmt usage
14 pub enum InnerAttrPolicy<'a> {
16 Forbidden { reason: &'a str, saw_doc_comment: bool, prev_attr_sp: Option<Span> },
19 const DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG: &str = "an inner attribute is not \
20 permitted in this context";
22 pub(super) const DEFAULT_INNER_ATTR_FORBIDDEN: InnerAttrPolicy<'_> = InnerAttrPolicy::Forbidden {
23 reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG,
24 saw_doc_comment: false,
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;
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"
41 DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG
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),
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 {
54 .struct_span_err_with_code(
56 "expected outer doc comment",
60 "inner doc comments like this (starting with \
61 `//!` or `/*!`) can only appear before items",
66 just_parsed_doc_comment = true;
67 Some(attr::mk_doc_comment(comment_kind, attr_style, data, self.prev_token.span))
72 if let Some(attr) = attr {
78 Ok(AttrWrapper::new(attrs.into(), start_pos))
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(
86 inner_parse_policy: InnerAttrPolicy<'_>,
87 ) -> PResult<'a, ast::Attribute> {
89 "parse_attribute: inner_parse_policy={:?} self.token={:?}",
90 inner_parse_policy, self.token
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) {
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);
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);
112 Ok(attr::mk_attr_from_item(item, None, style, attr_sp))
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))
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 {
124 if saw_doc_comment { "previous doc comment" } else { "previous outer attribute" };
126 let mut diag = self.struct_span_err(attr_sp, reason);
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);
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.",
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 `}`
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()),
159 Ok(if let Some(item) = item {
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 })
168 // Attr items don't have attributes
169 if capture_tokens { self.collect_tokens_no_attrs(do_parse) } else { do_parse(self) }?
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.
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![];
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 {
188 Some(attr::mk_doc_comment(comment_kind, attr_style, data, self.prev_token.span))
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![]));
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);
220 if !lit.kind.is_unsuffixed() {
221 self.struct_span_err(lit.span, "suffixed literals are not allowed in attributes")
223 "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \
224 use an unsuffixed version (`1`, `1.0`, etc.)",
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)?;
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) {
248 Ok((cfg_predicate, expanded_attrs))
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) {
264 /// Matches the following grammar (per RFC 1559).
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()),
277 if let Some(item) = nt_meta {
278 return match item.meta(item.path.span) {
283 None => self.unexpected(),
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 })
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)
302 ast::MetaItemKind::Word
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(),
313 match self.parse_meta_item() {
314 Ok(mi) => return Ok(ast::NestedMetaItem::MetaItem(mi)),
315 Err(ref mut err) => err.cancel(),
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))
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() {
331 attr.ident().map_or(true, |ident| {
332 ident.name == sym::cfg_attr || !rustc_feature::is_builtin_attr_name(ident.name)