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