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