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