]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/parse/attr.rs
Auto merge of #30641 - tsion:match-range, r=eddyb
[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::{spanned, Spanned, mk_sp, Span};
14 use parse::common::*; //resolve bug?
15 use parse::PResult;
16 use parse::token;
17 use parse::parser::{Parser, TokenType};
18 use ptr::P;
19
20 impl<'a> Parser<'a> {
21     /// Parse attributes that appear before an item
22     pub fn parse_outer_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
23         let mut attrs: Vec<ast::Attribute> = Vec::new();
24         loop {
25             debug!("parse_outer_attributes: self.token={:?}",
26                    self.token);
27             match self.token {
28               token::Pound => {
29                 attrs.push(try!(self.parse_attribute(false)));
30               }
31               token::DocComment(s) => {
32                 let attr = ::attr::mk_sugared_doc_attr(
33                     attr::mk_attr_id(),
34                     self.id_to_interned_str(ast::Ident::with_empty_ctxt(s)),
35                     self.span.lo,
36                     self.span.hi
37                 );
38                 if attr.node.style != ast::AttrStyle::Outer {
39                   return Err(self.fatal("expected outer comment"));
40                 }
41                 attrs.push(attr);
42                 try!(self.bump());
43               }
44               _ => break
45             }
46         }
47         return Ok(attrs);
48     }
49
50     /// Matches `attribute = # ! [ meta_item ]`
51     ///
52     /// If permit_inner is true, then a leading `!` indicates an inner
53     /// attribute
54     pub fn parse_attribute(&mut self, permit_inner: bool) -> PResult<'a, ast::Attribute> {
55         debug!("parse_attributes: permit_inner={:?} self.token={:?}",
56                permit_inner, self.token);
57         let (span, value, mut style) = match self.token {
58             token::Pound => {
59                 let lo = self.span.lo;
60                 try!(self.bump());
61
62                 if permit_inner { self.expected_tokens.push(TokenType::Token(token::Not)); }
63                 let style = if self.token == token::Not {
64                     try!(self.bump());
65                     if !permit_inner {
66                         let span = self.span;
67                         self.diagnostic().struct_span_err(span,
68                                                           "an inner attribute is not permitted in \
69                                                            this context")
70                                          .fileline_help(span,
71                                                         "place inner attribute at the top of \
72                                                          the module or block")
73                                          .emit()
74                     }
75                     ast::AttrStyle::Inner
76                 } else {
77                     ast::AttrStyle::Outer
78                 };
79
80                 try!(self.expect(&token::OpenDelim(token::Bracket)));
81                 let meta_item = try!(self.parse_meta_item());
82                 let hi = self.span.hi;
83                 try!(self.expect(&token::CloseDelim(token::Bracket)));
84
85                 (mk_sp(lo, hi), meta_item, style)
86             }
87             _ => {
88                 let token_str = self.this_token_to_string();
89                 return Err(self.fatal(&format!("expected `#`, found `{}`", token_str)));
90             }
91         };
92
93         if permit_inner && self.token == token::Semi {
94             try!(self.bump());
95             self.span_warn(span, "this inner attribute syntax is deprecated. \
96                            The new syntax is `#![foo]`, with a bang and no semicolon");
97             style = ast::AttrStyle::Inner;
98         }
99
100         Ok(Spanned {
101             span: span,
102             node: ast::Attribute_ {
103                 id: attr::mk_attr_id(),
104                 style: style,
105                 value: value,
106                 is_sugared_doc: false
107             }
108         })
109     }
110
111     /// Parse attributes that appear after the opening of an item. These should
112     /// be preceded by an exclamation mark, but we accept and warn about one
113     /// terminated by a semicolon.
114
115     /// matches inner_attrs*
116     pub fn parse_inner_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
117         let mut attrs: Vec<ast::Attribute> = vec![];
118         loop {
119             match self.token {
120                 token::Pound => {
121                     // Don't even try to parse if it's not an inner attribute.
122                     if !self.look_ahead(1, |t| t == &token::Not) {
123                         break;
124                     }
125
126                     let attr = try!(self.parse_attribute(true));
127                     assert!(attr.node.style == ast::AttrStyle::Inner);
128                     attrs.push(attr);
129                 }
130                 token::DocComment(s) => {
131                     // we need to get the position of this token before we bump.
132                     let Span { lo, hi, .. } = self.span;
133                     let str = self.id_to_interned_str(ast::Ident::with_empty_ctxt(s));
134                     let attr = attr::mk_sugared_doc_attr(attr::mk_attr_id(), str, lo, hi);
135                     if attr.node.style == ast::AttrStyle::Inner {
136                         attrs.push(attr);
137                         try!(self.bump());
138                     } else {
139                         break;
140                     }
141                 }
142                 _ => break
143             }
144         }
145         Ok(attrs)
146     }
147
148     /// matches meta_item = IDENT
149     /// | IDENT = lit
150     /// | IDENT meta_seq
151     pub fn parse_meta_item(&mut self) -> PResult<'a, P<ast::MetaItem>> {
152         let nt_meta = match self.token {
153             token::Interpolated(token::NtMeta(ref e)) => {
154                 Some(e.clone())
155             }
156             _ => None
157         };
158
159         match nt_meta {
160             Some(meta) => {
161                 try!(self.bump());
162                 return Ok(meta);
163             }
164             None => {}
165         }
166
167         let lo = self.span.lo;
168         let ident = try!(self.parse_ident());
169         let name = self.id_to_interned_str(ident);
170         match self.token {
171             token::Eq => {
172                 try!(self.bump());
173                 let lit = try!(self.parse_lit());
174                 // FIXME #623 Non-string meta items are not serialized correctly;
175                 // just forbid them for now
176                 match lit.node {
177                     ast::LitStr(..) => {}
178                     _ => {
179                         self.span_err(
180                             lit.span,
181                             "non-string literals are not allowed in meta-items");
182                     }
183                 }
184                 let hi = self.span.hi;
185                 Ok(P(spanned(lo, hi, ast::MetaNameValue(name, lit))))
186             }
187             token::OpenDelim(token::Paren) => {
188                 let inner_items = try!(self.parse_meta_seq());
189                 let hi = self.span.hi;
190                 Ok(P(spanned(lo, hi, ast::MetaList(name, inner_items))))
191             }
192             _ => {
193                 let hi = self.last_span.hi;
194                 Ok(P(spanned(lo, hi, ast::MetaWord(name))))
195             }
196         }
197     }
198
199     /// matches meta_seq = ( COMMASEP(meta_item) )
200     fn parse_meta_seq(&mut self) -> PResult<'a, Vec<P<ast::MetaItem>>> {
201         self.parse_unspanned_seq(&token::OpenDelim(token::Paren),
202                                  &token::CloseDelim(token::Paren),
203                                  seq_sep_trailing_allowed(token::Comma),
204                                  |p: &mut Parser<'a>| p.parse_meta_item())
205     }
206 }