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