]>
Commit | Line | Data |
---|---|---|
1a4d82fc | 1 | // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT |
223e47cc LB |
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 | //! The main parser interface | |
12 | ||
c30ab7b3 | 13 | use ast::{self, CrateConfig}; |
7cac9316 | 14 | use codemap::{CodeMap, FilePathMapping}; |
cc61c64b | 15 | use syntax_pos::{self, Span, FileMap, NO_EXPANSION}; |
9cc50fc6 | 16 | use errors::{Handler, ColorConfig, DiagnosticBuilder}; |
9e0c209e | 17 | use feature_gate::UnstableFeatures; |
223e47cc | 18 | use parse::parser::Parser; |
1a4d82fc | 19 | use ptr::P; |
d9579d0f | 20 | use str::char_at; |
476ff2be | 21 | use symbol::Symbol; |
8bb4bdeb | 22 | use tokenstream::{TokenStream, TokenTree}; |
9346a6ac | 23 | |
62682a34 | 24 | use std::cell::RefCell; |
476ff2be | 25 | use std::collections::HashSet; |
c34b1796 | 26 | use std::iter; |
c34b1796 AL |
27 | use std::path::{Path, PathBuf}; |
28 | use std::rc::Rc; | |
1a4d82fc | 29 | use std::str; |
223e47cc | 30 | |
9cc50fc6 | 31 | pub type PResult<'a, T> = Result<T, DiagnosticBuilder<'a>>; |
9346a6ac | 32 | |
1a4d82fc | 33 | #[macro_use] |
223e47cc | 34 | pub mod parser; |
1a4d82fc JJ |
35 | |
36 | pub mod lexer; | |
223e47cc | 37 | pub mod token; |
223e47cc LB |
38 | pub mod attr; |
39 | ||
223e47cc | 40 | pub mod common; |
223e47cc | 41 | pub mod classify; |
223e47cc LB |
42 | pub mod obsolete; |
43 | ||
1a4d82fc | 44 | /// Info about a parsing session. |
223e47cc | 45 | pub struct ParseSess { |
32a655c1 | 46 | pub span_diagnostic: Handler, |
9e0c209e | 47 | pub unstable_features: UnstableFeatures, |
c30ab7b3 | 48 | pub config: CrateConfig, |
8bb4bdeb | 49 | pub missing_fragment_specifiers: RefCell<HashSet<Span>>, |
1a4d82fc | 50 | /// Used to determine and report recursive mod inclusions |
c34b1796 | 51 | included_mod_stack: RefCell<Vec<PathBuf>>, |
9cc50fc6 | 52 | code_map: Rc<CodeMap>, |
1a4d82fc JJ |
53 | } |
54 | ||
55 | impl ParseSess { | |
7cac9316 XL |
56 | pub fn new(file_path_mapping: FilePathMapping) -> Self { |
57 | let cm = Rc::new(CodeMap::new(file_path_mapping)); | |
5bcae85e SL |
58 | let handler = Handler::with_tty_emitter(ColorConfig::Auto, |
59 | true, | |
60 | false, | |
61 | Some(cm.clone())); | |
9cc50fc6 | 62 | ParseSess::with_span_handler(handler, cm) |
1a4d82fc | 63 | } |
1a4d82fc | 64 | |
9cc50fc6 | 65 | pub fn with_span_handler(handler: Handler, code_map: Rc<CodeMap>) -> ParseSess { |
62682a34 | 66 | ParseSess { |
9cc50fc6 | 67 | span_diagnostic: handler, |
9e0c209e | 68 | unstable_features: UnstableFeatures::from_environment(), |
476ff2be | 69 | config: HashSet::new(), |
8bb4bdeb | 70 | missing_fragment_specifiers: RefCell::new(HashSet::new()), |
9cc50fc6 SL |
71 | included_mod_stack: RefCell::new(vec![]), |
72 | code_map: code_map | |
1a4d82fc | 73 | } |
62682a34 | 74 | } |
1a4d82fc | 75 | |
62682a34 | 76 | pub fn codemap(&self) -> &CodeMap { |
9cc50fc6 | 77 | &self.code_map |
223e47cc LB |
78 | } |
79 | } | |
80 | ||
476ff2be SL |
81 | #[derive(Clone)] |
82 | pub struct Directory { | |
83 | pub path: PathBuf, | |
84 | pub ownership: DirectoryOwnership, | |
85 | } | |
86 | ||
87 | #[derive(Copy, Clone)] | |
88 | pub enum DirectoryOwnership { | |
89 | Owned, | |
90 | UnownedViaBlock, | |
91 | UnownedViaMod(bool /* legacy warnings? */), | |
92 | } | |
93 | ||
223e47cc LB |
94 | // a bunch of utility functions of the form parse_<thing>_from_<source> |
95 | // where <thing> includes crate, expr, item, stmt, tts, and one that | |
96 | // uses a HOF to parse anything, and <source> includes file and | |
97 | // source_str. | |
98 | ||
c30ab7b3 SL |
99 | pub fn parse_crate_from_file<'a>(input: &Path, sess: &'a ParseSess) -> PResult<'a, ast::Crate> { |
100 | let mut parser = new_parser_from_file(sess, input); | |
54a0048b | 101 | parser.parse_crate_mod() |
223e47cc LB |
102 | } |
103 | ||
c30ab7b3 | 104 | pub fn parse_crate_attrs_from_file<'a>(input: &Path, sess: &'a ParseSess) |
54a0048b | 105 | -> PResult<'a, Vec<ast::Attribute>> { |
c30ab7b3 | 106 | let mut parser = new_parser_from_file(sess, input); |
54a0048b | 107 | parser.parse_inner_attributes() |
1a4d82fc JJ |
108 | } |
109 | ||
7cac9316 XL |
110 | pub fn parse_crate_from_source_str(name: String, source: String, sess: &ParseSess) |
111 | -> PResult<ast::Crate> { | |
c30ab7b3 | 112 | new_parser_from_source_str(sess, name, source).parse_crate_mod() |
223e47cc LB |
113 | } |
114 | ||
7cac9316 XL |
115 | pub fn parse_crate_attrs_from_source_str(name: String, source: String, sess: &ParseSess) |
116 | -> PResult<Vec<ast::Attribute>> { | |
c30ab7b3 | 117 | new_parser_from_source_str(sess, name, source).parse_inner_attributes() |
1a4d82fc JJ |
118 | } |
119 | ||
7cac9316 XL |
120 | pub fn parse_expr_from_source_str(name: String, source: String, sess: &ParseSess) |
121 | -> PResult<P<ast::Expr>> { | |
c30ab7b3 | 122 | new_parser_from_source_str(sess, name, source).parse_expr() |
223e47cc LB |
123 | } |
124 | ||
54a0048b SL |
125 | /// Parses an item. |
126 | /// | |
127 | /// Returns `Ok(Some(item))` when successful, `Ok(None)` when no item was found, and`Err` | |
128 | /// when a syntax error occurred. | |
7cac9316 XL |
129 | pub fn parse_item_from_source_str(name: String, source: String, sess: &ParseSess) |
130 | -> PResult<Option<P<ast::Item>>> { | |
c30ab7b3 | 131 | new_parser_from_source_str(sess, name, source).parse_item() |
223e47cc LB |
132 | } |
133 | ||
7cac9316 XL |
134 | pub fn parse_meta_from_source_str(name: String, source: String, sess: &ParseSess) |
135 | -> PResult<ast::MetaItem> { | |
c30ab7b3 | 136 | new_parser_from_source_str(sess, name, source).parse_meta_item() |
223e47cc LB |
137 | } |
138 | ||
7cac9316 XL |
139 | pub fn parse_stmt_from_source_str(name: String, source: String, sess: &ParseSess) |
140 | -> PResult<Option<ast::Stmt>> { | |
c30ab7b3 | 141 | new_parser_from_source_str(sess, name, source).parse_stmt() |
223e47cc LB |
142 | } |
143 | ||
041b39d2 XL |
144 | pub fn parse_stream_from_source_str(name: String, source: String, sess: &ParseSess, |
145 | override_span: Option<Span>) | |
146 | -> TokenStream { | |
147 | filemap_to_stream(sess, sess.codemap().new_filemap(name, source), override_span) | |
223e47cc LB |
148 | } |
149 | ||
970d7e83 | 150 | // Create a new parser from a source string |
7cac9316 XL |
151 | pub fn new_parser_from_source_str(sess: &ParseSess, name: String, source: String) |
152 | -> Parser { | |
153 | let mut parser = filemap_to_parser(sess, sess.codemap().new_filemap(name, source)); | |
154 | parser.recurse_into_file_modules = false; | |
155 | parser | |
223e47cc LB |
156 | } |
157 | ||
158 | /// Create a new parser, handling errors as appropriate | |
159 | /// if the file doesn't exist | |
c30ab7b3 SL |
160 | pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path) -> Parser<'a> { |
161 | filemap_to_parser(sess, file_to_filemap(sess, path, None)) | |
223e47cc LB |
162 | } |
163 | ||
970d7e83 LB |
164 | /// Given a session, a crate config, a path, and a span, add |
165 | /// the file at the given path to the codemap, and return a parser. | |
166 | /// On an error, use the given span as the source of the problem. | |
1a4d82fc | 167 | pub fn new_sub_parser_from_file<'a>(sess: &'a ParseSess, |
1a4d82fc | 168 | path: &Path, |
476ff2be | 169 | directory_ownership: DirectoryOwnership, |
1a4d82fc JJ |
170 | module_name: Option<String>, |
171 | sp: Span) -> Parser<'a> { | |
c30ab7b3 | 172 | let mut p = filemap_to_parser(sess, file_to_filemap(sess, path, Some(sp))); |
476ff2be | 173 | p.directory.ownership = directory_ownership; |
1a4d82fc JJ |
174 | p.root_module_name = module_name; |
175 | p | |
970d7e83 LB |
176 | } |
177 | ||
178 | /// Given a filemap and config, return a parser | |
7cac9316 | 179 | pub fn filemap_to_parser(sess: & ParseSess, filemap: Rc<FileMap>, ) -> Parser { |
c1a9b12d | 180 | let end_pos = filemap.end_pos; |
041b39d2 | 181 | let mut parser = stream_to_parser(sess, filemap_to_stream(sess, filemap, None)); |
c1a9b12d | 182 | |
3157f602 | 183 | if parser.token == token::Eof && parser.span == syntax_pos::DUMMY_SP { |
cc61c64b | 184 | parser.span = Span { lo: end_pos, hi: end_pos, ctxt: NO_EXPANSION }; |
c1a9b12d SL |
185 | } |
186 | ||
187 | parser | |
970d7e83 LB |
188 | } |
189 | ||
190 | // must preserve old name for now, because quote! from the *existing* | |
191 | // compiler expands into it | |
7cac9316 | 192 | pub fn new_parser_from_tts(sess: &ParseSess, tts: Vec<TokenTree>) -> Parser { |
8bb4bdeb | 193 | stream_to_parser(sess, tts.into_iter().collect()) |
5bcae85e SL |
194 | } |
195 | ||
970d7e83 LB |
196 | |
197 | // base abstractions | |
198 | ||
199 | /// Given a session and a path and an optional span (for error reporting), | |
200 | /// add the path to the session's codemap and return the new filemap. | |
62682a34 SL |
201 | fn file_to_filemap(sess: &ParseSess, path: &Path, spanopt: Option<Span>) |
202 | -> Rc<FileMap> { | |
203 | match sess.codemap().load_file(path) { | |
204 | Ok(filemap) => filemap, | |
223e47cc | 205 | Err(e) => { |
62682a34 SL |
206 | let msg = format!("couldn't read {:?}: {}", path.display(), e); |
207 | match spanopt { | |
208 | Some(sp) => panic!(sess.span_diagnostic.span_fatal(sp, &msg)), | |
9cc50fc6 | 209 | None => panic!(sess.span_diagnostic.fatal(&msg)) |
62682a34 | 210 | } |
223e47cc LB |
211 | } |
212 | } | |
213 | } | |
214 | ||
1a4d82fc | 215 | /// Given a filemap, produce a sequence of token-trees |
041b39d2 XL |
216 | pub fn filemap_to_stream(sess: &ParseSess, filemap: Rc<FileMap>, override_span: Option<Span>) |
217 | -> TokenStream { | |
32a655c1 | 218 | let mut srdr = lexer::StringReader::new(sess, filemap); |
041b39d2 | 219 | srdr.override_span = override_span; |
32a655c1 SL |
220 | srdr.real_token(); |
221 | panictry!(srdr.parse_all_token_trees()) | |
970d7e83 LB |
222 | } |
223 | ||
7cac9316 XL |
224 | /// Given stream and the `ParseSess`, produce a parser |
225 | pub fn stream_to_parser(sess: &ParseSess, stream: TokenStream) -> Parser { | |
226 | Parser::new(sess, stream, None, true, false) | |
1a4d82fc JJ |
227 | } |
228 | ||
1a4d82fc JJ |
229 | /// Parse a string representing a character literal into its final form. |
230 | /// Rather than just accepting/rejecting a given literal, unescapes it as | |
231 | /// well. Can take any slice prefixed by a character escape. Returns the | |
232 | /// character and the number of characters consumed. | |
85aaf69f | 233 | pub fn char_lit(lit: &str) -> (char, isize) { |
c34b1796 | 234 | use std::char; |
1a4d82fc | 235 | |
9e0c209e SL |
236 | // Handle non-escaped chars first. |
237 | if lit.as_bytes()[0] != b'\\' { | |
238 | // If the first byte isn't '\\' it might part of a multi-byte char, so | |
239 | // get the char with chars(). | |
240 | let c = lit.chars().next().unwrap(); | |
241 | return (c, 1); | |
1a4d82fc JJ |
242 | } |
243 | ||
9e0c209e SL |
244 | // Handle escaped chars. |
245 | match lit.as_bytes()[1] as char { | |
246 | '"' => ('"', 2), | |
247 | 'n' => ('\n', 2), | |
248 | 'r' => ('\r', 2), | |
249 | 't' => ('\t', 2), | |
250 | '\\' => ('\\', 2), | |
251 | '\'' => ('\'', 2), | |
252 | '0' => ('\0', 2), | |
253 | 'x' => { | |
254 | let v = u32::from_str_radix(&lit[2..4], 16).unwrap(); | |
255 | let c = char::from_u32(v).unwrap(); | |
256 | (c, 4) | |
c34b1796 | 257 | } |
9e0c209e | 258 | 'u' => { |
7cac9316 | 259 | assert_eq!(lit.as_bytes()[2], b'{'); |
9e0c209e SL |
260 | let idx = lit.find('}').unwrap(); |
261 | let v = u32::from_str_radix(&lit[3..idx], 16).unwrap(); | |
262 | let c = char::from_u32(v).unwrap(); | |
263 | (c, (idx + 1) as isize) | |
264 | } | |
265 | _ => panic!("lexer should have rejected a bad character escape {}", lit) | |
266 | } | |
1a4d82fc JJ |
267 | } |
268 | ||
7cac9316 XL |
269 | pub fn escape_default(s: &str) -> String { |
270 | s.chars().map(char::escape_default).flat_map(|x| x).collect() | |
271 | } | |
272 | ||
1a4d82fc JJ |
273 | /// Parse a string representing a string literal into its final form. Does |
274 | /// unescaping. | |
275 | pub fn str_lit(lit: &str) -> String { | |
7cac9316 | 276 | debug!("parse_str_lit: given {}", escape_default(lit)); |
1a4d82fc JJ |
277 | let mut res = String::with_capacity(lit.len()); |
278 | ||
279 | // FIXME #8372: This could be a for-loop if it didn't borrow the iterator | |
85aaf69f | 280 | let error = |i| format!("lexer should have rejected {} at {}", lit, i); |
223e47cc | 281 | |
1a4d82fc | 282 | /// Eat everything up to a non-whitespace |
85aaf69f | 283 | fn eat<'a>(it: &mut iter::Peekable<str::CharIndices<'a>>) { |
1a4d82fc JJ |
284 | loop { |
285 | match it.peek().map(|x| x.1) { | |
286 | Some(' ') | Some('\n') | Some('\r') | Some('\t') => { | |
287 | it.next(); | |
288 | }, | |
289 | _ => { break; } | |
290 | } | |
291 | } | |
292 | } | |
293 | ||
294 | let mut chars = lit.char_indices().peekable(); | |
7cac9316 XL |
295 | while let Some((i, c)) = chars.next() { |
296 | match c { | |
297 | '\\' => { | |
298 | let ch = chars.peek().unwrap_or_else(|| { | |
299 | panic!("{}", error(i)) | |
300 | }).1; | |
301 | ||
302 | if ch == '\n' { | |
303 | eat(&mut chars); | |
304 | } else if ch == '\r' { | |
305 | chars.next(); | |
306 | let ch = chars.peek().unwrap_or_else(|| { | |
307 | panic!("{}", error(i)) | |
308 | }).1; | |
1a4d82fc | 309 | |
7cac9316 XL |
310 | if ch != '\n' { |
311 | panic!("lexer accepted bare CR"); | |
312 | } | |
313 | eat(&mut chars); | |
314 | } else { | |
315 | // otherwise, a normal escape | |
316 | let (c, n) = char_lit(&lit[i..]); | |
317 | for _ in 0..n - 1 { // we don't need to move past the first \ | |
1a4d82fc | 318 | chars.next(); |
1a4d82fc | 319 | } |
7cac9316 | 320 | res.push(c); |
1a4d82fc JJ |
321 | } |
322 | }, | |
7cac9316 XL |
323 | '\r' => { |
324 | let ch = chars.peek().unwrap_or_else(|| { | |
325 | panic!("{}", error(i)) | |
326 | }).1; | |
327 | ||
328 | if ch != '\n' { | |
329 | panic!("lexer accepted bare CR"); | |
330 | } | |
331 | chars.next(); | |
332 | res.push('\n'); | |
333 | } | |
334 | c => res.push(c), | |
1a4d82fc JJ |
335 | } |
336 | } | |
337 | ||
338 | res.shrink_to_fit(); // probably not going to do anything, unless there was an escape. | |
339 | debug!("parse_str_lit: returning {}", res); | |
340 | res | |
341 | } | |
342 | ||
343 | /// Parse a string representing a raw string literal into its final form. The | |
344 | /// only operation this does is convert embedded CRLF into a single LF. | |
345 | pub fn raw_str_lit(lit: &str) -> String { | |
7cac9316 | 346 | debug!("raw_str_lit: given {}", escape_default(lit)); |
1a4d82fc JJ |
347 | let mut res = String::with_capacity(lit.len()); |
348 | ||
1a4d82fc | 349 | let mut chars = lit.chars().peekable(); |
7cac9316 XL |
350 | while let Some(c) = chars.next() { |
351 | if c == '\r' { | |
352 | if *chars.peek().unwrap() != '\n' { | |
353 | panic!("lexer accepted bare CR"); | |
354 | } | |
355 | chars.next(); | |
356 | res.push('\n'); | |
357 | } else { | |
358 | res.push(c); | |
1a4d82fc JJ |
359 | } |
360 | } | |
361 | ||
362 | res.shrink_to_fit(); | |
363 | res | |
364 | } | |
365 | ||
366 | // check if `s` looks like i32 or u1234 etc. | |
367 | fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool { | |
368 | s.len() > 1 && | |
d9579d0f | 369 | first_chars.contains(&char_at(s, 0)) && |
1a4d82fc JJ |
370 | s[1..].chars().all(|c| '0' <= c && c <= '9') |
371 | } | |
372 | ||
cc61c64b XL |
373 | macro_rules! err { |
374 | ($opt_diag:expr, |$span:ident, $diag:ident| $($body:tt)*) => { | |
375 | match $opt_diag { | |
376 | Some(($span, $diag)) => { $($body)* } | |
377 | None => return None, | |
378 | } | |
379 | } | |
380 | } | |
381 | ||
382 | pub fn lit_token(lit: token::Lit, suf: Option<Symbol>, diag: Option<(Span, &Handler)>) | |
383 | -> (bool /* suffix illegal? */, Option<ast::LitKind>) { | |
384 | use ast::LitKind; | |
385 | ||
386 | match lit { | |
387 | token::Byte(i) => (true, Some(LitKind::Byte(byte_lit(&i.as_str()).0))), | |
388 | token::Char(i) => (true, Some(LitKind::Char(char_lit(&i.as_str()).0))), | |
389 | ||
390 | // There are some valid suffixes for integer and float literals, | |
391 | // so all the handling is done internally. | |
392 | token::Integer(s) => (false, integer_lit(&s.as_str(), suf, diag)), | |
393 | token::Float(s) => (false, float_lit(&s.as_str(), suf, diag)), | |
394 | ||
395 | token::Str_(s) => { | |
396 | let s = Symbol::intern(&str_lit(&s.as_str())); | |
397 | (true, Some(LitKind::Str(s, ast::StrStyle::Cooked))) | |
398 | } | |
399 | token::StrRaw(s, n) => { | |
400 | let s = Symbol::intern(&raw_str_lit(&s.as_str())); | |
401 | (true, Some(LitKind::Str(s, ast::StrStyle::Raw(n)))) | |
402 | } | |
403 | token::ByteStr(i) => { | |
404 | (true, Some(LitKind::ByteStr(byte_str_lit(&i.as_str())))) | |
405 | } | |
406 | token::ByteStrRaw(i, _) => { | |
407 | (true, Some(LitKind::ByteStr(Rc::new(i.to_string().into_bytes())))) | |
408 | } | |
409 | } | |
410 | } | |
411 | ||
412 | fn filtered_float_lit(data: Symbol, suffix: Option<Symbol>, diag: Option<(Span, &Handler)>) | |
413 | -> Option<ast::LitKind> { | |
1a4d82fc | 414 | debug!("filtered_float_lit: {}, {:?}", data, suffix); |
476ff2be SL |
415 | let suffix = match suffix { |
416 | Some(suffix) => suffix, | |
cc61c64b | 417 | None => return Some(ast::LitKind::FloatUnsuffixed(data)), |
476ff2be SL |
418 | }; |
419 | ||
cc61c64b | 420 | Some(match &*suffix.as_str() { |
476ff2be SL |
421 | "f32" => ast::LitKind::Float(data, ast::FloatTy::F32), |
422 | "f64" => ast::LitKind::Float(data, ast::FloatTy::F64), | |
423 | suf => { | |
cc61c64b XL |
424 | err!(diag, |span, diag| { |
425 | if suf.len() >= 2 && looks_like_width_suffix(&['f'], suf) { | |
426 | // if it looks like a width, lets try to be helpful. | |
427 | let msg = format!("invalid width `{}` for float literal", &suf[1..]); | |
428 | diag.struct_span_err(span, &msg).help("valid widths are 32 and 64").emit() | |
429 | } else { | |
430 | let msg = format!("invalid suffix `{}` for float literal", suf); | |
431 | diag.struct_span_err(span, &msg) | |
432 | .help("valid suffixes are `f32` and `f64`") | |
433 | .emit(); | |
434 | } | |
435 | }); | |
1a4d82fc | 436 | |
7453a54e | 437 | ast::LitKind::FloatUnsuffixed(data) |
1a4d82fc | 438 | } |
cc61c64b | 439 | }) |
1a4d82fc | 440 | } |
cc61c64b XL |
441 | pub fn float_lit(s: &str, suffix: Option<Symbol>, diag: Option<(Span, &Handler)>) |
442 | -> Option<ast::LitKind> { | |
1a4d82fc | 443 | debug!("float_lit: {:?}, {:?}", s, suffix); |
85aaf69f | 444 | // FIXME #2252: bounds checking float literals is deferred until trans |
1a4d82fc | 445 | let s = s.chars().filter(|&c| c != '_').collect::<String>(); |
cc61c64b | 446 | filtered_float_lit(Symbol::intern(&s), suffix, diag) |
1a4d82fc JJ |
447 | } |
448 | ||
449 | /// Parse a string representing a byte literal into its final form. Similar to `char_lit` | |
85aaf69f SL |
450 | pub fn byte_lit(lit: &str) -> (u8, usize) { |
451 | let err = |i| format!("lexer accepted invalid byte literal {} step {}", lit, i); | |
1a4d82fc JJ |
452 | |
453 | if lit.len() == 1 { | |
454 | (lit.as_bytes()[0], 1) | |
455 | } else { | |
7cac9316 | 456 | assert_eq!(lit.as_bytes()[0], b'\\', "{}", err(0)); |
1a4d82fc JJ |
457 | let b = match lit.as_bytes()[1] { |
458 | b'"' => b'"', | |
459 | b'n' => b'\n', | |
460 | b'r' => b'\r', | |
461 | b't' => b'\t', | |
462 | b'\\' => b'\\', | |
463 | b'\'' => b'\'', | |
464 | b'0' => b'\0', | |
465 | _ => { | |
c34b1796 | 466 | match u64::from_str_radix(&lit[2..4], 16).ok() { |
1a4d82fc JJ |
467 | Some(c) => |
468 | if c > 0xFF { | |
469 | panic!(err(2)) | |
470 | } else { | |
471 | return (c as u8, 4) | |
472 | }, | |
473 | None => panic!(err(3)) | |
474 | } | |
475 | } | |
476 | }; | |
7cac9316 | 477 | (b, 2) |
1a4d82fc JJ |
478 | } |
479 | } | |
480 | ||
e9174d1e | 481 | pub fn byte_str_lit(lit: &str) -> Rc<Vec<u8>> { |
1a4d82fc JJ |
482 | let mut res = Vec::with_capacity(lit.len()); |
483 | ||
484 | // FIXME #8372: This could be a for-loop if it didn't borrow the iterator | |
85aaf69f | 485 | let error = |i| format!("lexer should have rejected {} at {}", lit, i); |
1a4d82fc JJ |
486 | |
487 | /// Eat everything up to a non-whitespace | |
7cac9316 | 488 | fn eat<I: Iterator<Item=(usize, u8)>>(it: &mut iter::Peekable<I>) { |
1a4d82fc JJ |
489 | loop { |
490 | match it.peek().map(|x| x.1) { | |
491 | Some(b' ') | Some(b'\n') | Some(b'\r') | Some(b'\t') => { | |
492 | it.next(); | |
493 | }, | |
494 | _ => { break; } | |
495 | } | |
496 | } | |
497 | } | |
498 | ||
e9174d1e | 499 | // byte string literals *must* be ASCII, but the escapes don't have to be |
1a4d82fc JJ |
500 | let mut chars = lit.bytes().enumerate().peekable(); |
501 | loop { | |
502 | match chars.next() { | |
503 | Some((i, b'\\')) => { | |
504 | let em = error(i); | |
85aaf69f | 505 | match chars.peek().expect(&em).1 { |
1a4d82fc JJ |
506 | b'\n' => eat(&mut chars), |
507 | b'\r' => { | |
508 | chars.next(); | |
85aaf69f | 509 | if chars.peek().expect(&em).1 != b'\n' { |
1a4d82fc JJ |
510 | panic!("lexer accepted bare CR"); |
511 | } | |
512 | eat(&mut chars); | |
513 | } | |
514 | _ => { | |
515 | // otherwise, a normal escape | |
516 | let (c, n) = byte_lit(&lit[i..]); | |
517 | // we don't need to move past the first \ | |
85aaf69f | 518 | for _ in 0..n - 1 { |
1a4d82fc JJ |
519 | chars.next(); |
520 | } | |
521 | res.push(c); | |
522 | } | |
523 | } | |
524 | }, | |
525 | Some((i, b'\r')) => { | |
526 | let em = error(i); | |
85aaf69f | 527 | if chars.peek().expect(&em).1 != b'\n' { |
1a4d82fc JJ |
528 | panic!("lexer accepted bare CR"); |
529 | } | |
530 | chars.next(); | |
531 | res.push(b'\n'); | |
532 | } | |
533 | Some((_, c)) => res.push(c), | |
534 | None => break, | |
535 | } | |
536 | } | |
537 | ||
538 | Rc::new(res) | |
539 | } | |
540 | ||
cc61c64b XL |
541 | pub fn integer_lit(s: &str, suffix: Option<Symbol>, diag: Option<(Span, &Handler)>) |
542 | -> Option<ast::LitKind> { | |
1a4d82fc JJ |
543 | // s can only be ascii, byte indexing is fine |
544 | ||
545 | let s2 = s.chars().filter(|&c| c != '_').collect::<String>(); | |
85aaf69f | 546 | let mut s = &s2[..]; |
1a4d82fc JJ |
547 | |
548 | debug!("integer_lit: {}, {:?}", s, suffix); | |
549 | ||
550 | let mut base = 10; | |
551 | let orig = s; | |
7453a54e | 552 | let mut ty = ast::LitIntType::Unsuffixed; |
1a4d82fc | 553 | |
d9579d0f AL |
554 | if char_at(s, 0) == '0' && s.len() > 1 { |
555 | match char_at(s, 1) { | |
1a4d82fc JJ |
556 | 'x' => base = 16, |
557 | 'o' => base = 8, | |
558 | 'b' => base = 2, | |
559 | _ => { } | |
560 | } | |
561 | } | |
562 | ||
563 | // 1f64 and 2f32 etc. are valid float literals. | |
476ff2be SL |
564 | if let Some(suf) = suffix { |
565 | if looks_like_width_suffix(&['f'], &suf.as_str()) { | |
cc61c64b XL |
566 | let err = match base { |
567 | 16 => Some("hexadecimal float literal is not supported"), | |
568 | 8 => Some("octal float literal is not supported"), | |
569 | 2 => Some("binary float literal is not supported"), | |
570 | _ => None, | |
571 | }; | |
572 | if let Some(err) = err { | |
573 | err!(diag, |span, diag| diag.span_err(span, err)); | |
1a4d82fc | 574 | } |
7cac9316 | 575 | return filtered_float_lit(Symbol::intern(s), Some(suf), diag) |
1a4d82fc | 576 | } |
1a4d82fc JJ |
577 | } |
578 | ||
579 | if base != 10 { | |
580 | s = &s[2..]; | |
581 | } | |
582 | ||
476ff2be | 583 | if let Some(suf) = suffix { |
cc61c64b XL |
584 | if suf.as_str().is_empty() { |
585 | err!(diag, |span, diag| diag.span_bug(span, "found empty literal suffix in Some")); | |
586 | } | |
476ff2be | 587 | ty = match &*suf.as_str() { |
7453a54e SL |
588 | "isize" => ast::LitIntType::Signed(ast::IntTy::Is), |
589 | "i8" => ast::LitIntType::Signed(ast::IntTy::I8), | |
590 | "i16" => ast::LitIntType::Signed(ast::IntTy::I16), | |
591 | "i32" => ast::LitIntType::Signed(ast::IntTy::I32), | |
592 | "i64" => ast::LitIntType::Signed(ast::IntTy::I64), | |
32a655c1 | 593 | "i128" => ast::LitIntType::Signed(ast::IntTy::I128), |
7453a54e SL |
594 | "usize" => ast::LitIntType::Unsigned(ast::UintTy::Us), |
595 | "u8" => ast::LitIntType::Unsigned(ast::UintTy::U8), | |
596 | "u16" => ast::LitIntType::Unsigned(ast::UintTy::U16), | |
597 | "u32" => ast::LitIntType::Unsigned(ast::UintTy::U32), | |
598 | "u64" => ast::LitIntType::Unsigned(ast::UintTy::U64), | |
32a655c1 | 599 | "u128" => ast::LitIntType::Unsigned(ast::UintTy::U128), |
476ff2be | 600 | suf => { |
1a4d82fc JJ |
601 | // i<digits> and u<digits> look like widths, so lets |
602 | // give an error message along those lines | |
cc61c64b XL |
603 | err!(diag, |span, diag| { |
604 | if looks_like_width_suffix(&['i', 'u'], suf) { | |
605 | let msg = format!("invalid width `{}` for integer literal", &suf[1..]); | |
606 | diag.struct_span_err(span, &msg) | |
607 | .help("valid widths are 8, 16, 32, 64 and 128") | |
608 | .emit(); | |
609 | } else { | |
610 | let msg = format!("invalid suffix `{}` for numeric literal", suf); | |
611 | diag.struct_span_err(span, &msg) | |
612 | .help("the suffix must be one of the integral types \ | |
613 | (`u32`, `isize`, etc)") | |
614 | .emit(); | |
615 | } | |
616 | }); | |
1a4d82fc JJ |
617 | |
618 | ty | |
619 | } | |
620 | } | |
621 | } | |
622 | ||
623 | debug!("integer_lit: the type is {:?}, base {:?}, the new string is {:?}, the original \ | |
624 | string was {:?}, the original suffix was {:?}", ty, base, s, orig, suffix); | |
625 | ||
cc61c64b | 626 | Some(match u128::from_str_radix(s, base) { |
7453a54e SL |
627 | Ok(r) => ast::LitKind::Int(r, ty), |
628 | Err(_) => { | |
c34b1796 AL |
629 | // small bases are lexed as if they were base 10, e.g, the string |
630 | // might be `0b10201`. This will cause the conversion above to fail, | |
631 | // but these cases have errors in the lexer: we don't want to emit | |
632 | // two errors, and we especially don't want to emit this error since | |
633 | // it isn't necessarily true. | |
634 | let already_errored = base < 10 && | |
635 | s.chars().any(|c| c.to_digit(10).map_or(false, |d| d >= base)); | |
636 | ||
637 | if !already_errored { | |
cc61c64b | 638 | err!(diag, |span, diag| diag.span_err(span, "int literal is too large")); |
c34b1796 | 639 | } |
7453a54e | 640 | ast::LitKind::Int(0, ty) |
c34b1796 | 641 | } |
cc61c64b | 642 | }) |
1a4d82fc | 643 | } |
223e47cc LB |
644 | |
645 | #[cfg(test)] | |
d9579d0f | 646 | mod tests { |
223e47cc | 647 | use super::*; |
9e0c209e | 648 | use syntax_pos::{self, Span, BytePos, Pos, NO_EXPANSION}; |
3157f602 | 649 | use codemap::Spanned; |
476ff2be | 650 | use ast::{self, Ident, PatKind}; |
7453a54e | 651 | use abi::Abi; |
9e0c209e | 652 | use attr::first_attr_value_str_by_name; |
85aaf69f | 653 | use parse; |
970d7e83 | 654 | use parse::parser::Parser; |
85aaf69f | 655 | use print::pprust::item_to_string; |
1a4d82fc | 656 | use ptr::P; |
3157f602 | 657 | use tokenstream::{self, TokenTree}; |
8bb4bdeb | 658 | use util::parser_testing::{string_to_stream, string_to_parser}; |
85aaf69f | 659 | use util::parser_testing::{string_to_expr, string_to_item, string_to_stmt}; |
3157f602 | 660 | use util::ThinVec; |
970d7e83 | 661 | |
3157f602 | 662 | // produce a syntax_pos::span |
1a4d82fc | 663 | fn sp(a: u32, b: u32) -> Span { |
cc61c64b | 664 | Span {lo: BytePos(a), hi: BytePos(b), ctxt: NO_EXPANSION} |
970d7e83 LB |
665 | } |
666 | ||
8bb4bdeb XL |
667 | fn str2seg(s: &str, lo: u32, hi: u32) -> ast::PathSegment { |
668 | ast::PathSegment::from_ident(Ident::from_str(s), sp(lo, hi)) | |
669 | } | |
670 | ||
1a4d82fc JJ |
671 | #[test] fn path_exprs_1() { |
672 | assert!(string_to_expr("a".to_string()) == | |
673 | P(ast::Expr{ | |
674 | id: ast::DUMMY_NODE_ID, | |
7453a54e | 675 | node: ast::ExprKind::Path(None, ast::Path { |
1a4d82fc | 676 | span: sp(0, 1), |
8bb4bdeb | 677 | segments: vec![str2seg("a", 0, 1)], |
1a4d82fc | 678 | }), |
92a42be0 | 679 | span: sp(0, 1), |
3157f602 | 680 | attrs: ThinVec::new(), |
1a4d82fc JJ |
681 | })) |
682 | } | |
223e47cc | 683 | |
1a4d82fc JJ |
684 | #[test] fn path_exprs_2 () { |
685 | assert!(string_to_expr("::a::b".to_string()) == | |
686 | P(ast::Expr { | |
687 | id: ast::DUMMY_NODE_ID, | |
7453a54e | 688 | node: ast::ExprKind::Path(None, ast::Path { |
32a655c1 | 689 | span: sp(0, 6), |
041b39d2 | 690 | segments: vec![ast::PathSegment::crate_root(sp(0, 2)), |
8bb4bdeb XL |
691 | str2seg("a", 2, 3), |
692 | str2seg("b", 5, 6)] | |
32a655c1 | 693 | }), |
92a42be0 | 694 | span: sp(0, 6), |
3157f602 | 695 | attrs: ThinVec::new(), |
1a4d82fc | 696 | })) |
223e47cc LB |
697 | } |
698 | ||
c34b1796 | 699 | #[should_panic] |
1a4d82fc JJ |
700 | #[test] fn bad_path_expr_1() { |
701 | string_to_expr("::abc::def::return".to_string()); | |
970d7e83 LB |
702 | } |
703 | ||
1a4d82fc JJ |
704 | // check the token-tree-ization of macros |
705 | #[test] | |
706 | fn string_to_tts_macro () { | |
8bb4bdeb XL |
707 | let tts: Vec<_> = |
708 | string_to_stream("macro_rules! zip (($a)=>($a))".to_string()).trees().collect(); | |
709 | let tts: &[TokenTree] = &tts[..]; | |
d9579d0f AL |
710 | |
711 | match (tts.len(), tts.get(0), tts.get(1), tts.get(2), tts.get(3)) { | |
712 | ( | |
713 | 4, | |
a7813a04 | 714 | Some(&TokenTree::Token(_, token::Ident(name_macro_rules))), |
92a42be0 | 715 | Some(&TokenTree::Token(_, token::Not)), |
a7813a04 | 716 | Some(&TokenTree::Token(_, token::Ident(name_zip))), |
92a42be0 | 717 | Some(&TokenTree::Delimited(_, ref macro_delimed)), |
d9579d0f | 718 | ) |
476ff2be SL |
719 | if name_macro_rules.name == "macro_rules" |
720 | && name_zip.name == "zip" => { | |
8bb4bdeb | 721 | let tts = ¯o_delimed.stream().trees().collect::<Vec<_>>(); |
d9579d0f AL |
722 | match (tts.len(), tts.get(0), tts.get(1), tts.get(2)) { |
723 | ( | |
724 | 3, | |
92a42be0 SL |
725 | Some(&TokenTree::Delimited(_, ref first_delimed)), |
726 | Some(&TokenTree::Token(_, token::FatArrow)), | |
727 | Some(&TokenTree::Delimited(_, ref second_delimed)), | |
d9579d0f | 728 | ) |
1a4d82fc | 729 | if macro_delimed.delim == token::Paren => { |
8bb4bdeb | 730 | let tts = &first_delimed.stream().trees().collect::<Vec<_>>(); |
d9579d0f AL |
731 | match (tts.len(), tts.get(0), tts.get(1)) { |
732 | ( | |
733 | 2, | |
92a42be0 | 734 | Some(&TokenTree::Token(_, token::Dollar)), |
a7813a04 | 735 | Some(&TokenTree::Token(_, token::Ident(ident))), |
d9579d0f | 736 | ) |
476ff2be | 737 | if first_delimed.delim == token::Paren && ident.name == "a" => {}, |
8bb4bdeb | 738 | _ => panic!("value 3: {:?}", *first_delimed), |
1a4d82fc | 739 | } |
8bb4bdeb | 740 | let tts = &second_delimed.stream().trees().collect::<Vec<_>>(); |
d9579d0f AL |
741 | match (tts.len(), tts.get(0), tts.get(1)) { |
742 | ( | |
743 | 2, | |
92a42be0 | 744 | Some(&TokenTree::Token(_, token::Dollar)), |
a7813a04 | 745 | Some(&TokenTree::Token(_, token::Ident(ident))), |
d9579d0f | 746 | ) |
1a4d82fc | 747 | if second_delimed.delim == token::Paren |
476ff2be | 748 | && ident.name == "a" => {}, |
8bb4bdeb | 749 | _ => panic!("value 4: {:?}", *second_delimed), |
1a4d82fc JJ |
750 | } |
751 | }, | |
8bb4bdeb | 752 | _ => panic!("value 2: {:?}", *macro_delimed), |
1a4d82fc JJ |
753 | } |
754 | }, | |
755 | _ => panic!("value: {:?}",tts), | |
756 | } | |
970d7e83 LB |
757 | } |
758 | ||
1a4d82fc | 759 | #[test] |
c34b1796 | 760 | fn string_to_tts_1() { |
8bb4bdeb | 761 | let tts = string_to_stream("fn a (b : i32) { b; }".to_string()); |
c34b1796 | 762 | |
8bb4bdeb XL |
763 | let expected = TokenStream::concat(vec![ |
764 | TokenTree::Token(sp(0, 2), token::Ident(Ident::from_str("fn"))).into(), | |
765 | TokenTree::Token(sp(3, 4), token::Ident(Ident::from_str("a"))).into(), | |
92a42be0 | 766 | TokenTree::Delimited( |
c34b1796 | 767 | sp(5, 14), |
8bb4bdeb | 768 | tokenstream::Delimited { |
c34b1796 | 769 | delim: token::DelimToken::Paren, |
8bb4bdeb XL |
770 | tts: TokenStream::concat(vec![ |
771 | TokenTree::Token(sp(6, 7), token::Ident(Ident::from_str("b"))).into(), | |
772 | TokenTree::Token(sp(8, 9), token::Colon).into(), | |
773 | TokenTree::Token(sp(10, 13), token::Ident(Ident::from_str("i32"))).into(), | |
774 | ]).into(), | |
775 | }).into(), | |
92a42be0 | 776 | TokenTree::Delimited( |
c34b1796 | 777 | sp(15, 21), |
8bb4bdeb | 778 | tokenstream::Delimited { |
c34b1796 | 779 | delim: token::DelimToken::Brace, |
8bb4bdeb XL |
780 | tts: TokenStream::concat(vec![ |
781 | TokenTree::Token(sp(17, 18), token::Ident(Ident::from_str("b"))).into(), | |
782 | TokenTree::Token(sp(18, 19), token::Semi).into(), | |
783 | ]).into(), | |
784 | }).into() | |
785 | ]); | |
c34b1796 AL |
786 | |
787 | assert_eq!(tts, expected); | |
223e47cc | 788 | } |
223e47cc | 789 | |
970d7e83 | 790 | #[test] fn ret_expr() { |
1a4d82fc JJ |
791 | assert!(string_to_expr("return d".to_string()) == |
792 | P(ast::Expr{ | |
793 | id: ast::DUMMY_NODE_ID, | |
7453a54e | 794 | node:ast::ExprKind::Ret(Some(P(ast::Expr{ |
1a4d82fc | 795 | id: ast::DUMMY_NODE_ID, |
7453a54e | 796 | node:ast::ExprKind::Path(None, ast::Path{ |
1a4d82fc | 797 | span: sp(7, 8), |
8bb4bdeb | 798 | segments: vec![str2seg("d", 7, 8)], |
1a4d82fc | 799 | }), |
92a42be0 | 800 | span:sp(7,8), |
3157f602 | 801 | attrs: ThinVec::new(), |
1a4d82fc | 802 | }))), |
92a42be0 | 803 | span:sp(0,8), |
3157f602 | 804 | attrs: ThinVec::new(), |
1a4d82fc | 805 | })) |
970d7e83 LB |
806 | } |
807 | ||
808 | #[test] fn parse_stmt_1 () { | |
1a4d82fc | 809 | assert!(string_to_stmt("b;".to_string()) == |
3157f602 | 810 | Some(ast::Stmt { |
7453a54e | 811 | node: ast::StmtKind::Expr(P(ast::Expr { |
1a4d82fc | 812 | id: ast::DUMMY_NODE_ID, |
7453a54e | 813 | node: ast::ExprKind::Path(None, ast::Path { |
1a4d82fc | 814 | span:sp(0,1), |
8bb4bdeb | 815 | segments: vec![str2seg("b", 0, 1)], |
1a4d82fc | 816 | }), |
92a42be0 | 817 | span: sp(0,1), |
3157f602 XL |
818 | attrs: ThinVec::new()})), |
819 | id: ast::DUMMY_NODE_ID, | |
7453a54e | 820 | span: sp(0,1)})) |
970d7e83 LB |
821 | |
822 | } | |
823 | ||
824 | fn parser_done(p: Parser){ | |
1a4d82fc | 825 | assert_eq!(p.token.clone(), token::Eof); |
970d7e83 LB |
826 | } |
827 | ||
828 | #[test] fn parse_ident_pat () { | |
7cac9316 | 829 | let sess = ParseSess::new(FilePathMapping::empty()); |
1a4d82fc | 830 | let mut parser = string_to_parser(&sess, "b".to_string()); |
92a42be0 | 831 | assert!(panictry!(parser.parse_pat()) |
1a4d82fc JJ |
832 | == P(ast::Pat{ |
833 | id: ast::DUMMY_NODE_ID, | |
7453a54e | 834 | node: PatKind::Ident(ast::BindingMode::ByValue(ast::Mutability::Immutable), |
1a4d82fc | 835 | Spanned{ span:sp(0, 1), |
476ff2be | 836 | node: Ident::from_str("b") |
1a4d82fc JJ |
837 | }, |
838 | None), | |
839 | span: sp(0,1)})); | |
970d7e83 LB |
840 | parser_done(parser); |
841 | } | |
842 | ||
970d7e83 LB |
843 | // check the contents of the tt manually: |
844 | #[test] fn parse_fundecl () { | |
85aaf69f SL |
845 | // this test depends on the intern order of "fn" and "i32" |
846 | assert_eq!(string_to_item("fn a (b : i32) { b; }".to_string()), | |
970d7e83 | 847 | Some( |
476ff2be | 848 | P(ast::Item{ident:Ident::from_str("a"), |
1a4d82fc JJ |
849 | attrs:Vec::new(), |
850 | id: ast::DUMMY_NODE_ID, | |
7453a54e | 851 | node: ast::ItemKind::Fn(P(ast::FnDecl { |
c30ab7b3 | 852 | inputs: vec![ast::Arg{ |
1a4d82fc | 853 | ty: P(ast::Ty{id: ast::DUMMY_NODE_ID, |
7453a54e | 854 | node: ast::TyKind::Path(None, ast::Path{ |
970d7e83 | 855 | span:sp(10,13), |
8bb4bdeb | 856 | segments: vec![str2seg("i32", 10, 13)], |
c34b1796 | 857 | }), |
1a4d82fc JJ |
858 | span:sp(10,13) |
859 | }), | |
860 | pat: P(ast::Pat { | |
861 | id: ast::DUMMY_NODE_ID, | |
7453a54e SL |
862 | node: PatKind::Ident( |
863 | ast::BindingMode::ByValue(ast::Mutability::Immutable), | |
1a4d82fc JJ |
864 | Spanned{ |
865 | span: sp(6,7), | |
476ff2be | 866 | node: Ident::from_str("b")}, |
1a4d82fc JJ |
867 | None |
868 | ), | |
869 | span: sp(6,7) | |
870 | }), | |
871 | id: ast::DUMMY_NODE_ID | |
c30ab7b3 | 872 | }], |
7453a54e | 873 | output: ast::FunctionRetTy::Default(sp(15, 15)), |
1a4d82fc JJ |
874 | variadic: false |
875 | }), | |
876 | ast::Unsafety::Normal, | |
9e0c209e SL |
877 | Spanned { |
878 | span: sp(0,2), | |
879 | node: ast::Constness::NotConst, | |
880 | }, | |
7453a54e | 881 | Abi::Rust, |
970d7e83 | 882 | ast::Generics{ // no idea on either of these: |
1a4d82fc | 883 | lifetimes: Vec::new(), |
32a655c1 | 884 | ty_params: Vec::new(), |
1a4d82fc JJ |
885 | where_clause: ast::WhereClause { |
886 | id: ast::DUMMY_NODE_ID, | |
887 | predicates: Vec::new(), | |
9e0c209e SL |
888 | }, |
889 | span: syntax_pos::DUMMY_SP, | |
970d7e83 | 890 | }, |
1a4d82fc | 891 | P(ast::Block { |
c30ab7b3 | 892 | stmts: vec![ast::Stmt { |
7453a54e | 893 | node: ast::StmtKind::Semi(P(ast::Expr{ |
1a4d82fc | 894 | id: ast::DUMMY_NODE_ID, |
7453a54e | 895 | node: ast::ExprKind::Path(None, |
1a4d82fc JJ |
896 | ast::Path{ |
897 | span:sp(17,18), | |
8bb4bdeb | 898 | segments: vec![str2seg("b", 17, 18)], |
1a4d82fc | 899 | }), |
92a42be0 | 900 | span: sp(17,18), |
3157f602 XL |
901 | attrs: ThinVec::new()})), |
902 | id: ast::DUMMY_NODE_ID, | |
c30ab7b3 | 903 | span: sp(17,19)}], |
1a4d82fc | 904 | id: ast::DUMMY_NODE_ID, |
7453a54e | 905 | rules: ast::BlockCheckMode::Default, // no idea |
970d7e83 | 906 | span: sp(15,21), |
1a4d82fc | 907 | })), |
7453a54e | 908 | vis: ast::Visibility::Inherited, |
1a4d82fc JJ |
909 | span: sp(0,21)}))); |
910 | } | |
911 | ||
912 | #[test] fn parse_use() { | |
913 | let use_s = "use foo::bar::baz;"; | |
85aaf69f | 914 | let vitem = string_to_item(use_s.to_string()).unwrap(); |
7453a54e | 915 | let vitem_s = item_to_string(&vitem); |
85aaf69f | 916 | assert_eq!(&vitem_s[..], use_s); |
1a4d82fc JJ |
917 | |
918 | let use_s = "use foo::bar as baz;"; | |
85aaf69f | 919 | let vitem = string_to_item(use_s.to_string()).unwrap(); |
7453a54e | 920 | let vitem_s = item_to_string(&vitem); |
85aaf69f | 921 | assert_eq!(&vitem_s[..], use_s); |
1a4d82fc JJ |
922 | } |
923 | ||
924 | #[test] fn parse_extern_crate() { | |
925 | let ex_s = "extern crate foo;"; | |
85aaf69f | 926 | let vitem = string_to_item(ex_s.to_string()).unwrap(); |
7453a54e | 927 | let vitem_s = item_to_string(&vitem); |
85aaf69f | 928 | assert_eq!(&vitem_s[..], ex_s); |
1a4d82fc | 929 | |
c34b1796 | 930 | let ex_s = "extern crate foo as bar;"; |
85aaf69f | 931 | let vitem = string_to_item(ex_s.to_string()).unwrap(); |
7453a54e | 932 | let vitem_s = item_to_string(&vitem); |
85aaf69f | 933 | assert_eq!(&vitem_s[..], ex_s); |
1a4d82fc JJ |
934 | } |
935 | ||
936 | fn get_spans_of_pat_idents(src: &str) -> Vec<Span> { | |
937 | let item = string_to_item(src.to_string()).unwrap(); | |
938 | ||
939 | struct PatIdentVisitor { | |
940 | spans: Vec<Span> | |
941 | } | |
476ff2be SL |
942 | impl<'a> ::visit::Visitor<'a> for PatIdentVisitor { |
943 | fn visit_pat(&mut self, p: &'a ast::Pat) { | |
1a4d82fc | 944 | match p.node { |
7453a54e | 945 | PatKind::Ident(_ , ref spannedident, _) => { |
1a4d82fc JJ |
946 | self.spans.push(spannedident.span.clone()); |
947 | } | |
948 | _ => { | |
949 | ::visit::walk_pat(self, p); | |
950 | } | |
951 | } | |
952 | } | |
953 | } | |
954 | let mut v = PatIdentVisitor { spans: Vec::new() }; | |
7453a54e | 955 | ::visit::walk_item(&mut v, &item); |
1a4d82fc | 956 | return v.spans; |
970d7e83 LB |
957 | } |
958 | ||
1a4d82fc JJ |
959 | #[test] fn span_of_self_arg_pat_idents_are_correct() { |
960 | ||
85aaf69f SL |
961 | let srcs = ["impl z { fn a (&self, &myarg: i32) {} }", |
962 | "impl z { fn a (&mut self, &myarg: i32) {} }", | |
963 | "impl z { fn a (&'a self, &myarg: i32) {} }", | |
964 | "impl z { fn a (self, &myarg: i32) {} }", | |
965 | "impl z { fn a (self: Foo, &myarg: i32) {} }", | |
1a4d82fc JJ |
966 | ]; |
967 | ||
85aaf69f | 968 | for &src in &srcs { |
1a4d82fc JJ |
969 | let spans = get_spans_of_pat_idents(src); |
970 | let Span{ lo, hi, .. } = spans[0]; | |
85aaf69f | 971 | assert!("self" == &src[lo.to_usize()..hi.to_usize()], |
1a4d82fc | 972 | "\"{}\" != \"self\". src=\"{}\"", |
85aaf69f | 973 | &src[lo.to_usize()..hi.to_usize()], src) |
1a4d82fc JJ |
974 | } |
975 | } | |
970d7e83 LB |
976 | |
977 | #[test] fn parse_exprs () { | |
978 | // just make sure that they parse.... | |
1a4d82fc JJ |
979 | string_to_expr("3 + 4".to_string()); |
980 | string_to_expr("a::z.froob(b,&(987+3))".to_string()); | |
970d7e83 LB |
981 | } |
982 | ||
983 | #[test] fn attrs_fix_bug () { | |
1a4d82fc JJ |
984 | string_to_item("pub fn mk_file_writer(path: &Path, flags: &[FileFlag]) |
985 | -> Result<Box<Writer>, String> { | |
970d7e83 LB |
986 | #[cfg(windows)] |
987 | fn wb() -> c_int { | |
988 | (O_WRONLY | libc::consts::os::extra::O_BINARY) as c_int | |
989 | } | |
990 | ||
991 | #[cfg(unix)] | |
992 | fn wb() -> c_int { O_WRONLY as c_int } | |
993 | ||
994 | let mut fflags: c_int = wb(); | |
1a4d82fc | 995 | }".to_string()); |
970d7e83 LB |
996 | } |
997 | ||
1a4d82fc | 998 | #[test] fn crlf_doc_comments() { |
7cac9316 | 999 | let sess = ParseSess::new(FilePathMapping::empty()); |
1a4d82fc JJ |
1000 | |
1001 | let name = "<source>".to_string(); | |
1002 | let source = "/// doc comment\r\nfn foo() {}".to_string(); | |
c30ab7b3 | 1003 | let item = parse_item_from_source_str(name.clone(), source, &sess) |
54a0048b | 1004 | .unwrap().unwrap(); |
85aaf69f | 1005 | let doc = first_attr_value_str_by_name(&item.attrs, "doc").unwrap(); |
476ff2be | 1006 | assert_eq!(doc, "/// doc comment"); |
1a4d82fc JJ |
1007 | |
1008 | let source = "/// doc comment\r\n/// line 2\r\nfn foo() {}".to_string(); | |
c30ab7b3 | 1009 | let item = parse_item_from_source_str(name.clone(), source, &sess) |
54a0048b | 1010 | .unwrap().unwrap(); |
cc61c64b | 1011 | let docs = item.attrs.iter().filter(|a| a.path == "doc") |
85aaf69f | 1012 | .map(|a| a.value_str().unwrap().to_string()).collect::<Vec<_>>(); |
1a4d82fc | 1013 | let b: &[_] = &["/// doc comment".to_string(), "/// line 2".to_string()]; |
85aaf69f | 1014 | assert_eq!(&docs[..], b); |
1a4d82fc JJ |
1015 | |
1016 | let source = "/** doc comment\r\n * with CRLF */\r\nfn foo() {}".to_string(); | |
c30ab7b3 | 1017 | let item = parse_item_from_source_str(name, source, &sess).unwrap().unwrap(); |
85aaf69f | 1018 | let doc = first_attr_value_str_by_name(&item.attrs, "doc").unwrap(); |
476ff2be | 1019 | assert_eq!(doc, "/** doc comment\n * with CRLF */"); |
85aaf69f SL |
1020 | } |
1021 | ||
1022 | #[test] | |
1023 | fn ttdelim_span() { | |
7cac9316 | 1024 | let sess = ParseSess::new(FilePathMapping::empty()); |
85aaf69f | 1025 | let expr = parse::parse_expr_from_source_str("foo".to_string(), |
c30ab7b3 | 1026 | "foo!( fn main() { body } )".to_string(), &sess).unwrap(); |
85aaf69f | 1027 | |
8bb4bdeb XL |
1028 | let tts: Vec<_> = match expr.node { |
1029 | ast::ExprKind::Mac(ref mac) => mac.node.stream().trees().collect(), | |
85aaf69f SL |
1030 | _ => panic!("not a macro"), |
1031 | }; | |
1032 | ||
8bb4bdeb | 1033 | let span = tts.iter().rev().next().unwrap().span(); |
85aaf69f | 1034 | |
62682a34 | 1035 | match sess.codemap().span_to_snippet(span) { |
85aaf69f SL |
1036 | Ok(s) => assert_eq!(&s[..], "{ body }"), |
1037 | Err(_) => panic!("could not get snippet"), | |
1038 | } | |
1a4d82fc | 1039 | } |
7cac9316 XL |
1040 | |
1041 | // This tests that when parsing a string (rather than a file) we don't try | |
1042 | // and read in a file for a module declaration and just parse a stub. | |
1043 | // See `recurse_into_file_modules` in the parser. | |
1044 | #[test] | |
1045 | fn out_of_line_mod() { | |
1046 | let sess = ParseSess::new(FilePathMapping::empty()); | |
1047 | let item = parse_item_from_source_str( | |
1048 | "foo".to_owned(), | |
1049 | "mod foo { struct S; mod this_does_not_exist; }".to_owned(), | |
1050 | &sess, | |
1051 | ).unwrap().unwrap(); | |
1052 | ||
1053 | if let ast::ItemKind::Mod(ref m) = item.node { | |
1054 | assert!(m.items.len() == 2); | |
1055 | } else { | |
1056 | panic!(); | |
1057 | } | |
1058 | } | |
970d7e83 | 1059 | } |