]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/parse/attr.rs
Use `Ident` instead of `Name` in `MetaItem`
[rust.git] / src / libsyntax / parse / attr.rs
1 // Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use attr;
12 use ast;
13 use codemap::respan;
14 use parse::common::SeqSep;
15 use parse::PResult;
16 use parse::token::{self, Nonterminal};
17 use parse::parser::{Parser, TokenType, PathStyle};
18 use tokenstream::TokenStream;
19
20 #[derive(PartialEq, Eq, Debug)]
21 enum InnerAttributeParsePolicy<'a> {
22     Permitted,
23     NotPermitted { reason: &'a str },
24 }
25
26 const DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG: &'static str = "an inner attribute is not \
27                                                              permitted in this context";
28
29 impl<'a> Parser<'a> {
30     /// Parse attributes that appear before an item
31     pub fn parse_outer_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
32         let mut attrs: Vec<ast::Attribute> = Vec::new();
33         let mut just_parsed_doc_comment = false;
34         loop {
35             debug!("parse_outer_attributes: self.token={:?}", self.token);
36             match self.token {
37                 token::Pound => {
38                     let inner_error_reason = if just_parsed_doc_comment {
39                         "an inner attribute is not permitted following an outer doc comment"
40                     } else if !attrs.is_empty() {
41                         "an inner attribute is not permitted following an outer attribute"
42                     } else {
43                         DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG
44                     };
45                     let inner_parse_policy =
46                         InnerAttributeParsePolicy::NotPermitted { reason: inner_error_reason };
47                     attrs.push(self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?);
48                     just_parsed_doc_comment = false;
49                 }
50                 token::DocComment(s) => {
51                     let attr = attr::mk_sugared_doc_attr(attr::mk_attr_id(), s, self.span);
52                     if attr.style != ast::AttrStyle::Outer {
53                         let mut err = self.fatal("expected outer doc comment");
54                         err.note("inner doc comments like this (starting with \
55                                   `//!` or `/*!`) can only appear before items");
56                         return Err(err);
57                     }
58                     attrs.push(attr);
59                     self.bump();
60                     just_parsed_doc_comment = true;
61                 }
62                 _ => break,
63             }
64         }
65         Ok(attrs)
66     }
67
68     /// Matches `attribute = # ! [ meta_item ]`
69     ///
70     /// If permit_inner is true, then a leading `!` indicates an inner
71     /// attribute
72     pub fn parse_attribute(&mut self, permit_inner: bool) -> PResult<'a, ast::Attribute> {
73         debug!("parse_attribute: permit_inner={:?} self.token={:?}",
74                permit_inner,
75                self.token);
76         let inner_parse_policy = if permit_inner {
77             InnerAttributeParsePolicy::Permitted
78         } else {
79             InnerAttributeParsePolicy::NotPermitted
80                 { reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG }
81         };
82         self.parse_attribute_with_inner_parse_policy(inner_parse_policy)
83     }
84
85     /// The same as `parse_attribute`, except it takes in an `InnerAttributeParsePolicy`
86     /// that prescribes how to handle inner attributes.
87     fn parse_attribute_with_inner_parse_policy(&mut self,
88                                                inner_parse_policy: InnerAttributeParsePolicy)
89                                                -> PResult<'a, ast::Attribute> {
90         debug!("parse_attribute_with_inner_parse_policy: inner_parse_policy={:?} self.token={:?}",
91                inner_parse_policy,
92                self.token);
93         let (span, path, tokens, style) = match self.token {
94             token::Pound => {
95                 let lo = self.span;
96                 self.bump();
97
98                 if inner_parse_policy == InnerAttributeParsePolicy::Permitted {
99                     self.expected_tokens.push(TokenType::Token(token::Not));
100                 }
101                 let style = if self.token == token::Not {
102                     self.bump();
103                     if let InnerAttributeParsePolicy::NotPermitted { reason } = inner_parse_policy
104                     {
105                         let span = self.span;
106                         self.diagnostic()
107                             .struct_span_err(span, reason)
108                             .note("inner attributes, like `#![no_std]`, annotate the item \
109                                    enclosing them, and are usually found at the beginning of \
110                                    source files. Outer attributes, like `#[test]`, annotate the \
111                                    item following them.")
112                             .emit()
113                     }
114                     ast::AttrStyle::Inner
115                 } else {
116                     ast::AttrStyle::Outer
117                 };
118
119                 self.expect(&token::OpenDelim(token::Bracket))?;
120                 let (path, tokens) = self.parse_path_and_tokens()?;
121                 self.expect(&token::CloseDelim(token::Bracket))?;
122                 let hi = self.prev_span;
123
124                 (lo.to(hi), path, tokens, style)
125             }
126             _ => {
127                 let token_str = self.this_token_to_string();
128                 return Err(self.fatal(&format!("expected `#`, found `{}`", token_str)));
129             }
130         };
131
132         Ok(ast::Attribute {
133             id: attr::mk_attr_id(),
134             style,
135             path,
136             tokens,
137             is_sugared_doc: false,
138             span,
139         })
140     }
141
142     pub fn parse_path_and_tokens(&mut self) -> PResult<'a, (ast::Path, TokenStream)> {
143         let meta = match self.token {
144             token::Interpolated(ref nt) => match nt.0 {
145                 Nonterminal::NtMeta(ref meta) => Some(meta.clone()),
146                 _ => None,
147             },
148             _ => None,
149         };
150         Ok(if let Some(meta) = meta {
151             self.bump();
152             (ast::Path::from_ident(meta.ident), meta.node.tokens(meta.span))
153         } else {
154             (self.parse_path(PathStyle::Mod)?, self.parse_tokens())
155         })
156     }
157
158     /// Parse attributes that appear after the opening of an item. These should
159     /// be preceded by an exclamation mark, but we accept and warn about one
160     /// terminated by a semicolon.
161
162     /// matches inner_attrs*
163     pub fn parse_inner_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
164         let mut attrs: Vec<ast::Attribute> = vec![];
165         loop {
166             match self.token {
167                 token::Pound => {
168                     // Don't even try to parse if it's not an inner attribute.
169                     if !self.look_ahead(1, |t| t == &token::Not) {
170                         break;
171                     }
172
173                     let attr = self.parse_attribute(true)?;
174                     assert_eq!(attr.style, ast::AttrStyle::Inner);
175                     attrs.push(attr);
176                 }
177                 token::DocComment(s) => {
178                     // we need to get the position of this token before we bump.
179                     let attr = attr::mk_sugared_doc_attr(attr::mk_attr_id(), s, self.span);
180                     if attr.style == ast::AttrStyle::Inner {
181                         attrs.push(attr);
182                         self.bump();
183                     } else {
184                         break;
185                     }
186                 }
187                 _ => break,
188             }
189         }
190         Ok(attrs)
191     }
192
193     fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> {
194         let lit = self.parse_lit()?;
195         debug!("Checking if {:?} is unusuffixed.", lit);
196
197         if !lit.node.is_unsuffixed() {
198             let msg = "suffixed literals are not allowed in attributes";
199             self.diagnostic().struct_span_err(lit.span, msg)
200                              .help("instead of using a suffixed literal \
201                                     (1u8, 1.0f32, etc.), use an unsuffixed version \
202                                     (1, 1.0, etc.).")
203                              .emit()
204         }
205
206         Ok(lit)
207     }
208
209     /// Per RFC#1559, matches the following grammar:
210     ///
211     /// meta_item : IDENT ( '=' UNSUFFIXED_LIT | '(' meta_item_inner? ')' )? ;
212     /// meta_item_inner : (meta_item | UNSUFFIXED_LIT) (',' meta_item_inner)? ;
213     pub fn parse_meta_item(&mut self) -> PResult<'a, ast::MetaItem> {
214         let nt_meta = match self.token {
215             token::Interpolated(ref nt) => match nt.0 {
216                 token::NtMeta(ref e) => Some(e.clone()),
217                 _ => None,
218             },
219             _ => None,
220         };
221
222         if let Some(meta) = nt_meta {
223             self.bump();
224             return Ok(meta);
225         }
226
227         let lo = self.span;
228         let ident = self.parse_ident()?;
229         let node = self.parse_meta_item_kind()?;
230         Ok(ast::MetaItem { ident, node: node, span: lo.to(self.prev_span) })
231     }
232
233     pub fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> {
234         Ok(if self.eat(&token::Eq) {
235             ast::MetaItemKind::NameValue(self.parse_unsuffixed_lit()?)
236         } else if self.eat(&token::OpenDelim(token::Paren)) {
237             ast::MetaItemKind::List(self.parse_meta_seq()?)
238         } else {
239             ast::MetaItemKind::Word
240         })
241     }
242
243     /// matches meta_item_inner : (meta_item | UNSUFFIXED_LIT) ;
244     fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::NestedMetaItem> {
245         let lo = self.span;
246
247         match self.parse_unsuffixed_lit() {
248             Ok(lit) => {
249                 return Ok(respan(lo.to(self.prev_span), ast::NestedMetaItemKind::Literal(lit)))
250             }
251             Err(ref mut err) => self.diagnostic().cancel(err)
252         }
253
254         match self.parse_meta_item() {
255             Ok(mi) => {
256                 return Ok(respan(lo.to(self.prev_span), ast::NestedMetaItemKind::MetaItem(mi)))
257             }
258             Err(ref mut err) => self.diagnostic().cancel(err)
259         }
260
261         let found = self.this_token_to_string();
262         let msg = format!("expected unsuffixed literal or identifier, found {}", found);
263         Err(self.diagnostic().struct_span_err(lo, &msg))
264     }
265
266     /// matches meta_seq = ( COMMASEP(meta_item_inner) )
267     fn parse_meta_seq(&mut self) -> PResult<'a, Vec<ast::NestedMetaItem>> {
268         self.parse_seq_to_end(&token::CloseDelim(token::Paren),
269                               SeqSep::trailing_allowed(token::Comma),
270                               |p: &mut Parser<'a>| p.parse_meta_item_inner())
271     }
272 }