1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
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.
11 //! The main parser interface
14 use codemap
::{Span, CodeMap, FileMap}
;
15 use diagnostic
::{SpanHandler, mk_span_handler, default_handler, Auto}
;
16 use parse
::attr
::ParserAttr
;
17 use parse
::parser
::Parser
;
20 use std
::cell
::{Cell, RefCell}
;
21 use std
::old_io
::File
;
38 /// Info about a parsing session.
39 pub struct ParseSess
{
40 pub span_diagnostic
: SpanHandler
, // better be the same as the one in the reader!
41 /// Used to determine and report recursive mod inclusions
42 included_mod_stack
: RefCell
<Vec
<Path
>>,
43 pub node_id
: Cell
<ast
::NodeId
>,
46 pub fn new_parse_sess() -> ParseSess
{
48 span_diagnostic
: mk_span_handler(default_handler(Auto
, None
, true), CodeMap
::new()),
49 included_mod_stack
: RefCell
::new(Vec
::new()),
50 node_id
: Cell
::new(1),
54 pub fn new_parse_sess_special_handler(sh
: SpanHandler
) -> ParseSess
{
57 included_mod_stack
: RefCell
::new(Vec
::new()),
58 node_id
: Cell
::new(1),
63 pub fn next_node_id(&self) -> ast
::NodeId
{
64 self.reserve_node_ids(1)
66 pub fn reserve_node_ids(&self, count
: ast
::NodeId
) -> ast
::NodeId
{
67 let v
= self.node_id
.get();
69 match v
.checked_add(count
) {
70 Some(next
) => { self.node_id.set(next); }
71 None
=> panic
!("Input too large, ran out of node ids!")
78 // a bunch of utility functions of the form parse_<thing>_from_<source>
79 // where <thing> includes crate, expr, item, stmt, tts, and one that
80 // uses a HOF to parse anything, and <source> includes file and
83 pub fn parse_crate_from_file(
85 cfg
: ast
::CrateConfig
,
88 new_parser_from_file(sess
, cfg
, input
).parse_crate_mod()
89 // why is there no p.abort_if_errors here?
92 pub fn parse_crate_attrs_from_file(
94 cfg
: ast
::CrateConfig
,
96 ) -> Vec
<ast
::Attribute
> {
97 let mut parser
= new_parser_from_file(sess
, cfg
, input
);
98 let (inner
, _
) = parser
.parse_inner_attrs_and_next();
102 pub fn parse_crate_from_source_str(name
: String
,
104 cfg
: ast
::CrateConfig
,
107 let mut p
= new_parser_from_source_str(sess
,
111 maybe_aborted(p
.parse_crate_mod(),p
)
114 pub fn parse_crate_attrs_from_source_str(name
: String
,
116 cfg
: ast
::CrateConfig
,
118 -> Vec
<ast
::Attribute
> {
119 let mut p
= new_parser_from_source_str(sess
,
123 let (inner
, _
) = maybe_aborted(p
.parse_inner_attrs_and_next(),p
);
127 pub fn parse_expr_from_source_str(name
: String
,
129 cfg
: ast
::CrateConfig
,
132 let mut p
= new_parser_from_source_str(sess
, cfg
, name
, source
);
133 maybe_aborted(p
.parse_expr(), p
)
136 pub fn parse_item_from_source_str(name
: String
,
138 cfg
: ast
::CrateConfig
,
140 -> Option
<P
<ast
::Item
>> {
141 let mut p
= new_parser_from_source_str(sess
, cfg
, name
, source
);
142 maybe_aborted(p
.parse_item_with_outer_attributes(),p
)
145 pub fn parse_meta_from_source_str(name
: String
,
147 cfg
: ast
::CrateConfig
,
149 -> P
<ast
::MetaItem
> {
150 let mut p
= new_parser_from_source_str(sess
, cfg
, name
, source
);
151 maybe_aborted(p
.parse_meta_item(),p
)
154 pub fn parse_stmt_from_source_str(name
: String
,
156 cfg
: ast
::CrateConfig
,
157 attrs
: Vec
<ast
::Attribute
> ,
160 let mut p
= new_parser_from_source_str(
166 maybe_aborted(p
.parse_stmt(attrs
),p
)
169 // Note: keep in sync with `with_hygiene::parse_tts_from_source_str`
170 // until #16472 is resolved.
172 // Warning: This parses with quote_depth > 0, which is not the default.
173 pub fn parse_tts_from_source_str(name
: String
,
175 cfg
: ast
::CrateConfig
,
177 -> Vec
<ast
::TokenTree
> {
178 let mut p
= new_parser_from_source_str(
185 // right now this is re-creating the token trees from ... token trees.
186 maybe_aborted(p
.parse_all_token_trees(),p
)
189 // Note: keep in sync with `with_hygiene::new_parser_from_source_str`
190 // until #16472 is resolved.
191 // Create a new parser from a source string
192 pub fn new_parser_from_source_str
<'a
>(sess
: &'a ParseSess
,
193 cfg
: ast
::CrateConfig
,
197 filemap_to_parser(sess
, string_to_filemap(sess
, source
, name
), cfg
)
200 /// Create a new parser, handling errors as appropriate
201 /// if the file doesn't exist
202 pub fn new_parser_from_file
<'a
>(sess
: &'a ParseSess
,
203 cfg
: ast
::CrateConfig
,
204 path
: &Path
) -> Parser
<'a
> {
205 filemap_to_parser(sess
, file_to_filemap(sess
, path
, None
), cfg
)
208 /// Given a session, a crate config, a path, and a span, add
209 /// the file at the given path to the codemap, and return a parser.
210 /// On an error, use the given span as the source of the problem.
211 pub fn new_sub_parser_from_file
<'a
>(sess
: &'a ParseSess
,
212 cfg
: ast
::CrateConfig
,
214 owns_directory
: bool
,
215 module_name
: Option
<String
>,
216 sp
: Span
) -> Parser
<'a
> {
217 let mut p
= filemap_to_parser(sess
, file_to_filemap(sess
, path
, Some(sp
)), cfg
);
218 p
.owns_directory
= owns_directory
;
219 p
.root_module_name
= module_name
;
223 // Note: keep this in sync with `with_hygiene::filemap_to_parser` until
224 // #16472 is resolved.
225 /// Given a filemap and config, return a parser
226 pub fn filemap_to_parser
<'a
>(sess
: &'a ParseSess
,
227 filemap
: Rc
<FileMap
>,
228 cfg
: ast
::CrateConfig
) -> Parser
<'a
> {
229 tts_to_parser(sess
, filemap_to_tts(sess
, filemap
), cfg
)
232 // must preserve old name for now, because quote! from the *existing*
233 // compiler expands into it
234 pub fn new_parser_from_tts
<'a
>(sess
: &'a ParseSess
,
235 cfg
: ast
::CrateConfig
,
236 tts
: Vec
<ast
::TokenTree
>) -> Parser
<'a
> {
237 tts_to_parser(sess
, tts
, cfg
)
243 /// Given a session and a path and an optional span (for error reporting),
244 /// add the path to the session's codemap and return the new filemap.
245 pub fn file_to_filemap(sess
: &ParseSess
, path
: &Path
, spanopt
: Option
<Span
>)
247 let err
= |msg
: &str| {
249 Some(sp
) => sess
.span_diagnostic
.span_fatal(sp
, msg
),
250 None
=> sess
.span_diagnostic
.handler().fatal(msg
),
253 let bytes
= match File
::open(path
).read_to_end() {
256 err(&format
!("couldn't read {:?}: {}",
257 path
.display(), e
)[]);
261 match str::from_utf8(&bytes
[..]).ok() {
263 return string_to_filemap(sess
, s
.to_string(),
264 path
.as_str().unwrap().to_string())
267 err(&format
!("{:?} is not UTF-8 encoded", path
.display())[])
273 /// Given a session and a string, add the string to
274 /// the session's codemap and return the new filemap
275 pub fn string_to_filemap(sess
: &ParseSess
, source
: String
, path
: String
)
277 sess
.span_diagnostic
.cm
.new_filemap(path
, source
)
280 // Note: keep this in sync with `with_hygiene::filemap_to_tts` (apart
281 // from the StringReader constructor), until #16472 is resolved.
282 /// Given a filemap, produce a sequence of token-trees
283 pub fn filemap_to_tts(sess
: &ParseSess
, filemap
: Rc
<FileMap
>)
284 -> Vec
<ast
::TokenTree
> {
285 // it appears to me that the cfg doesn't matter here... indeed,
286 // parsing tt's probably shouldn't require a parser at all.
287 let cfg
= Vec
::new();
288 let srdr
= lexer
::StringReader
::new(&sess
.span_diagnostic
, filemap
);
289 let mut p1
= Parser
::new(sess
, cfg
, box srdr
);
290 p1
.parse_all_token_trees()
293 /// Given tts and cfg, produce a parser
294 pub fn tts_to_parser
<'a
>(sess
: &'a ParseSess
,
295 tts
: Vec
<ast
::TokenTree
>,
296 cfg
: ast
::CrateConfig
) -> Parser
<'a
> {
297 let trdr
= lexer
::new_tt_reader(&sess
.span_diagnostic
, None
, None
, tts
);
298 let mut p
= Parser
::new(sess
, cfg
, box trdr
);
299 p
.check_unknown_macro_variable();
303 // FIXME (Issue #16472): The `with_hygiene` mod should go away after
304 // ToToken impls are revised to go directly to token-trees.
305 pub mod with_hygiene
{
307 use codemap
::FileMap
;
308 use parse
::parser
::Parser
;
310 use super::ParseSess
;
311 use super::{maybe_aborted, string_to_filemap, tts_to_parser}
;
313 // Note: keep this in sync with `super::parse_tts_from_source_str` until
314 // #16472 is resolved.
316 // Warning: This parses with quote_depth > 0, which is not the default.
317 pub fn parse_tts_from_source_str(name
: String
,
319 cfg
: ast
::CrateConfig
,
320 sess
: &ParseSess
) -> Vec
<ast
::TokenTree
> {
321 let mut p
= new_parser_from_source_str(
328 // right now this is re-creating the token trees from ... token trees.
329 maybe_aborted(p
.parse_all_token_trees(),p
)
332 // Note: keep this in sync with `super::new_parser_from_source_str` until
333 // #16472 is resolved.
334 // Create a new parser from a source string
335 fn new_parser_from_source_str
<'a
>(sess
: &'a ParseSess
,
336 cfg
: ast
::CrateConfig
,
338 source
: String
) -> Parser
<'a
> {
339 filemap_to_parser(sess
, string_to_filemap(sess
, source
, name
), cfg
)
342 // Note: keep this in sync with `super::filemap_to_parserr` until
343 // #16472 is resolved.
344 /// Given a filemap and config, return a parser
345 fn filemap_to_parser
<'a
>(sess
: &'a ParseSess
,
346 filemap
: Rc
<FileMap
>,
347 cfg
: ast
::CrateConfig
) -> Parser
<'a
> {
348 tts_to_parser(sess
, filemap_to_tts(sess
, filemap
), cfg
)
351 // Note: keep this in sync with `super::filemap_to_tts` until
352 // #16472 is resolved.
353 /// Given a filemap, produce a sequence of token-trees
354 fn filemap_to_tts(sess
: &ParseSess
, filemap
: Rc
<FileMap
>)
355 -> Vec
<ast
::TokenTree
> {
356 // it appears to me that the cfg doesn't matter here... indeed,
357 // parsing tt's probably shouldn't require a parser at all.
358 use super::lexer
::make_reader_with_embedded_idents
as make_reader
;
359 let cfg
= Vec
::new();
360 let srdr
= make_reader(&sess
.span_diagnostic
, filemap
);
361 let mut p1
= Parser
::new(sess
, cfg
, box srdr
);
362 p1
.parse_all_token_trees()
366 /// Abort if necessary
367 pub fn maybe_aborted
<T
>(result
: T
, p
: Parser
) -> T
{
372 /// Parse a string representing a character literal into its final form.
373 /// Rather than just accepting/rejecting a given literal, unescapes it as
374 /// well. Can take any slice prefixed by a character escape. Returns the
375 /// character and the number of characters consumed.
376 pub fn char_lit(lit
: &str) -> (char, isize) {
377 use std
::{num, char}
;
379 let mut chars
= lit
.chars();
380 let c
= match (chars
.next(), chars
.next()) {
381 (Some(c
), None
) if c
!= '
\\'
=> return (c
, 1),
382 (Some('
\\'
), Some(c
)) => match c
{
392 _
=> panic
!("lexer accepted invalid char escape `{}`", lit
)
396 Some(x
) => return (x
, 2),
400 let msg
= format
!("lexer should have rejected a bad character escape {}", lit
);
403 fn esc(len
: usize, lit
: &str) -> Option
<(char, isize)> {
404 num
::from_str_radix(&lit
[2..len
], 16).ok()
405 .and_then(char::from_u32
)
406 .map(|x
| (x
, len
as isize))
409 let unicode_escape
= || -> Option
<(char, isize)>
410 if lit
.as_bytes()[2] == b'
{'
{
411 let idx
= lit
.find('
}'
).expect(msg2
);
412 let subslice
= &lit
[3..idx
];
413 num
::from_str_radix(subslice
, 16).ok()
414 .and_then(char::from_u32
)
415 .map(|x
| (x
, subslice
.chars().count() as isize + 4))
421 return match lit
.as_bytes()[1] as char {
422 'x'
| 'X'
=> esc(4, lit
),
423 'u'
=> unicode_escape(),
429 /// Parse a string representing a string literal into its final form. Does
431 pub fn str_lit(lit
: &str) -> String
{
432 debug
!("parse_str_lit: given {}", lit
.escape_default());
433 let mut res
= String
::with_capacity(lit
.len());
435 // FIXME #8372: This could be a for-loop if it didn't borrow the iterator
436 let error
= |i
| format
!("lexer should have rejected {} at {}", lit
, i
);
438 /// Eat everything up to a non-whitespace
439 fn eat
<'a
>(it
: &mut iter
::Peekable
<str::CharIndices
<'a
>>) {
441 match it
.peek().map(|x
| x
.1) {
442 Some(' '
) | Some('
\n'
) | Some('
\r'
) | Some('
\t'
) => {
450 let mut chars
= lit
.char_indices().peekable();
456 let ch
= chars
.peek().unwrap_or_else(|| {
457 panic
!("{}", error(i
))
462 } else if ch
== '
\r'
{
464 let ch
= chars
.peek().unwrap_or_else(|| {
465 panic
!("{}", error(i
))
469 panic
!("lexer accepted bare CR");
473 // otherwise, a normal escape
474 let (c
, n
) = char_lit(&lit
[i
..]);
475 for _
in 0..n
- 1 { // we don't need to move past the first \
482 let ch
= chars
.peek().unwrap_or_else(|| {
483 panic
!("{}", error(i
))
487 panic
!("lexer accepted bare CR");
499 res
.shrink_to_fit(); // probably not going to do anything, unless there was an escape.
500 debug
!("parse_str_lit: returning {}", res
);
504 /// Parse a string representing a raw string literal into its final form. The
505 /// only operation this does is convert embedded CRLF into a single LF.
506 pub fn raw_str_lit(lit
: &str) -> String
{
507 debug
!("raw_str_lit: given {}", lit
.escape_default());
508 let mut res
= String
::with_capacity(lit
.len());
510 // FIXME #8372: This could be a for-loop if it didn't borrow the iterator
511 let mut chars
= lit
.chars().peekable();
516 if *chars
.peek().unwrap() != '
\n'
{
517 panic
!("lexer accepted bare CR");
533 // check if `s` looks like i32 or u1234 etc.
534 fn looks_like_width_suffix(first_chars
: &[char], s
: &str) -> bool
{
536 first_chars
.contains(&s
.char_at(0)) &&
537 s
[1..].chars().all(|c
| '
0'
<= c
&& c
<= '
9'
)
540 fn filtered_float_lit(data
: token
::InternedString
, suffix
: Option
<&str>,
541 sd
: &SpanHandler
, sp
: Span
) -> ast
::Lit_
{
542 debug
!("filtered_float_lit: {}, {:?}", data
, suffix
);
544 Some("f32") => ast
::LitFloat(data
, ast
::TyF32
),
545 Some("f64") => ast
::LitFloat(data
, ast
::TyF64
),
547 if suf
.len() >= 2 && looks_like_width_suffix(&['f'
], suf
) {
548 // if it looks like a width, lets try to be helpful.
549 sd
.span_err(sp
, &*format
!("illegal width `{}` for float literal, \
550 valid widths are 32 and 64", &suf
[1..]));
552 sd
.span_err(sp
, &*format
!("illegal suffix `{}` for float literal, \
553 valid suffixes are `f32` and `f64`", suf
));
556 ast
::LitFloatUnsuffixed(data
)
558 None
=> ast
::LitFloatUnsuffixed(data
)
561 pub fn float_lit(s
: &str, suffix
: Option
<&str>, sd
: &SpanHandler
, sp
: Span
) -> ast
::Lit_
{
562 debug
!("float_lit: {:?}, {:?}", s
, suffix
);
563 // FIXME #2252: bounds checking float literals is deferred until trans
564 let s
= s
.chars().filter(|&c
| c
!= '_'
).collect
::<String
>();
565 let data
= token
::intern_and_get_ident(&*s
);
566 filtered_float_lit(data
, suffix
, sd
, sp
)
569 /// Parse a string representing a byte literal into its final form. Similar to `char_lit`
570 pub fn byte_lit(lit
: &str) -> (u8, usize) {
571 let err
= |i
| format
!("lexer accepted invalid byte literal {} step {}", lit
, i
);
574 (lit
.as_bytes()[0], 1)
576 assert
!(lit
.as_bytes()[0] == b'
\\'
, err(0));
577 let b
= match lit
.as_bytes()[1] {
586 match ::std
::num
::from_str_radix
::<u64>(&lit
[2..4], 16).ok() {
593 None
=> panic
!(err(3))
601 pub fn binary_lit(lit
: &str) -> Rc
<Vec
<u8>> {
602 let mut res
= Vec
::with_capacity(lit
.len());
604 // FIXME #8372: This could be a for-loop if it didn't borrow the iterator
605 let error
= |i
| format
!("lexer should have rejected {} at {}", lit
, i
);
607 /// Eat everything up to a non-whitespace
608 fn eat
<'a
, I
: Iterator
<Item
=(usize, u8)>>(it
: &mut iter
::Peekable
<I
>) {
610 match it
.peek().map(|x
| x
.1) {
611 Some(b' '
) | Some(b'
\n'
) | Some(b'
\r'
) | Some(b'
\t'
) => {
619 // binary literals *must* be ASCII, but the escapes don't have to be
620 let mut chars
= lit
.bytes().enumerate().peekable();
623 Some((i
, b'
\\'
)) => {
625 match chars
.peek().expect(&em
).1 {
626 b'
\n'
=> eat(&mut chars
),
629 if chars
.peek().expect(&em
).1 != b'
\n'
{
630 panic
!("lexer accepted bare CR");
635 // otherwise, a normal escape
636 let (c
, n
) = byte_lit(&lit
[i
..]);
637 // we don't need to move past the first \
645 Some((i
, b'
\r'
)) => {
647 if chars
.peek().expect(&em
).1 != b'
\n'
{
648 panic
!("lexer accepted bare CR");
653 Some((_
, c
)) => res
.push(c
),
661 pub fn integer_lit(s
: &str, suffix
: Option
<&str>, sd
: &SpanHandler
, sp
: Span
) -> ast
::Lit_
{
662 // s can only be ascii, byte indexing is fine
664 let s2
= s
.chars().filter(|&c
| c
!= '_'
).collect
::<String
>();
667 debug
!("integer_lit: {}, {:?}", s
, suffix
);
671 let mut ty
= ast
::UnsuffixedIntLit(ast
::Plus
);
673 if s
.char_at(0) == '
0'
&& s
.len() > 1 {
682 // 1f64 and 2f32 etc. are valid float literals.
684 Some(suf
) if looks_like_width_suffix(&['f'
], suf
) => {
686 16 => sd
.span_err(sp
, "hexadecimal float literal is not supported"),
687 8 => sd
.span_err(sp
, "octal float literal is not supported"),
688 2 => sd
.span_err(sp
, "binary float literal is not supported"),
691 let ident
= token
::intern_and_get_ident(&*s
);
692 return filtered_float_lit(ident
, suffix
, sd
, sp
)
701 if let Some(suf
) = suffix
{
702 if suf
.is_empty() { sd.span_bug(sp, "found empty literal suffix in Some")}
704 "isize" => ast
::SignedIntLit(ast
::TyIs(false), ast
::Plus
),
705 "i8" => ast
::SignedIntLit(ast
::TyI8
, ast
::Plus
),
706 "i16" => ast
::SignedIntLit(ast
::TyI16
, ast
::Plus
),
707 "i32" => ast
::SignedIntLit(ast
::TyI32
, ast
::Plus
),
708 "i64" => ast
::SignedIntLit(ast
::TyI64
, ast
::Plus
),
709 "usize" => ast
::UnsignedIntLit(ast
::TyUs(false)),
710 "u8" => ast
::UnsignedIntLit(ast
::TyU8
),
711 "u16" => ast
::UnsignedIntLit(ast
::TyU16
),
712 "u32" => ast
::UnsignedIntLit(ast
::TyU32
),
713 "u64" => ast
::UnsignedIntLit(ast
::TyU64
),
714 "i" | "is" => ast
::SignedIntLit(ast
::TyIs(true), ast
::Plus
),
715 "u" | "us" => ast
::UnsignedIntLit(ast
::TyUs(true)),
717 // i<digits> and u<digits> look like widths, so lets
718 // give an error message along those lines
719 if looks_like_width_suffix(&['i'
, 'u'
], suf
) {
720 sd
.span_err(sp
, &*format
!("illegal width `{}` for integer literal; \
721 valid widths are 8, 16, 32 and 64",
724 sd
.span_err(sp
, &*format
!("illegal suffix `{}` for numeric literal", suf
));
725 sd
.span_help(sp
, "the suffix must be one of the integral types \
726 (`u32`, `isize`, etc)");
734 debug
!("integer_lit: the type is {:?}, base {:?}, the new string is {:?}, the original \
735 string was {:?}, the original suffix was {:?}", ty
, base
, s
, orig
, suffix
);
737 let res
: u64 = match ::std
::num
::from_str_radix(s
, base
).ok() {
739 None
=> { sd.span_err(sp, "int literal is too large"); 0 }
743 let sign
= ast
::Sign
::new(res
);
745 ast
::SignedIntLit(t
, _
) => ast
::LitInt(res
, ast
::SignedIntLit(t
, sign
)),
746 ast
::UnsuffixedIntLit(_
) => ast
::LitInt(res
, ast
::UnsuffixedIntLit(sign
)),
747 us@ast
::UnsignedIntLit(_
) => ast
::LitInt(res
, us
)
755 use codemap
::{Span, BytePos, Pos, Spanned, NO_EXPANSION}
;
756 use owned_slice
::OwnedSlice
;
759 use attr
::{first_attr_value_str_by_name, AttrMetaMethods}
;
761 use parse
::parser
::Parser
;
762 use parse
::token
::{str_to_ident}
;
763 use print
::pprust
::item_to_string
;
765 use util
::parser_testing
::{string_to_tts, string_to_parser}
;
766 use util
::parser_testing
::{string_to_expr, string_to_item, string_to_stmt}
;
768 // produce a codemap::span
769 fn sp(a
: u32, b
: u32) -> Span
{
770 Span {lo: BytePos(a), hi: BytePos(b), expn_id: NO_EXPANSION}
773 #[test] fn path_exprs_1() {
774 assert
!(string_to_expr("a".to_string()) ==
776 id
: ast
::DUMMY_NODE_ID
,
777 node
: ast
::ExprPath(ast
::Path
{
782 identifier
: str_to_ident("a"),
783 parameters
: ast
::PathParameters
::none(),
791 #[test] fn path_exprs_2 () {
792 assert
!(string_to_expr("::a::b".to_string()) ==
794 id
: ast
::DUMMY_NODE_ID
,
795 node
: ast
::ExprPath(ast
::Path
{
800 identifier
: str_to_ident("a"),
801 parameters
: ast
::PathParameters
::none(),
804 identifier
: str_to_ident("b"),
805 parameters
: ast
::PathParameters
::none(),
814 #[test] fn bad_path_expr_1() {
815 string_to_expr("::abc::def::return".to_string());
818 // check the token-tree-ization of macros
820 fn string_to_tts_macro () {
821 let tts
= string_to_tts("macro_rules! zip (($a)=>($a))".to_string());
822 let tts
: &[ast
::TokenTree
] = &tts
[..];
824 [ast
::TtToken(_
, token
::Ident(name_macro_rules
, token
::Plain
)),
825 ast
::TtToken(_
, token
::Not
),
826 ast
::TtToken(_
, token
::Ident(name_zip
, token
::Plain
)),
827 ast
::TtDelimited(_
, ref macro_delimed
)]
828 if name_macro_rules
.as_str() == "macro_rules"
829 && name_zip
.as_str() == "zip" => {
830 match ¯o_delimed
.tts
[] {
831 [ast
::TtDelimited(_
, ref first_delimed
),
832 ast
::TtToken(_
, token
::FatArrow
),
833 ast
::TtDelimited(_
, ref second_delimed
)]
834 if macro_delimed
.delim
== token
::Paren
=> {
835 match &first_delimed
.tts
[] {
836 [ast
::TtToken(_
, token
::Dollar
),
837 ast
::TtToken(_
, token
::Ident(name
, token
::Plain
))]
838 if first_delimed
.delim
== token
::Paren
839 && name
.as_str() == "a" => {}
,
840 _
=> panic
!("value 3: {:?}", **first_delimed
),
842 match &second_delimed
.tts
[] {
843 [ast
::TtToken(_
, token
::Dollar
),
844 ast
::TtToken(_
, token
::Ident(name
, token
::Plain
))]
845 if second_delimed
.delim
== token
::Paren
846 && name
.as_str() == "a" => {}
,
847 _
=> panic
!("value 4: {:?}", **second_delimed
),
850 _
=> panic
!("value 2: {:?}", **macro_delimed
),
853 _
=> panic
!("value: {:?}",tts
),
858 fn string_to_tts_1 () {
859 let tts
= string_to_tts("fn a (b : i32) { b; }".to_string());
860 assert_eq
!(json
::encode(&tts
).unwrap(),
863 \"variant\":\"TtToken\",\
867 \"variant\":\"Ident\",\
876 \"variant\":\"TtToken\",\
880 \"variant\":\"Ident\",\
889 \"variant\":\"TtDelimited\",\
893 \"delim\":\"Paren\",\
897 \"variant\":\"TtToken\",\
901 \"variant\":\"Ident\",\
910 \"variant\":\"TtToken\",\
917 \"variant\":\"TtToken\",\
921 \"variant\":\"Ident\",\
935 \"variant\":\"TtDelimited\",\
939 \"delim\":\"Brace\",\
943 \"variant\":\"TtToken\",\
947 \"variant\":\"Ident\",\
956 \"variant\":\"TtToken\",\
971 #[test] fn ret_expr() {
972 assert
!(string_to_expr("return d".to_string()) ==
974 id
: ast
::DUMMY_NODE_ID
,
975 node
:ast
::ExprRet(Some(P(ast
::Expr
{
976 id
: ast
::DUMMY_NODE_ID
,
977 node
:ast
::ExprPath(ast
::Path
{
982 identifier
: str_to_ident("d"),
983 parameters
: ast
::PathParameters
::none(),
993 #[test] fn parse_stmt_1 () {
994 assert
!(string_to_stmt("b;".to_string()) ==
996 node
: ast
::StmtExpr(P(ast
::Expr
{
997 id
: ast
::DUMMY_NODE_ID
,
998 node
: ast
::ExprPath(ast
::Path
{
1003 identifier
: str_to_ident("b"),
1004 parameters
: ast
::PathParameters
::none(),
1009 ast
::DUMMY_NODE_ID
),
1014 fn parser_done(p
: Parser
){
1015 assert_eq
!(p
.token
.clone(), token
::Eof
);
1018 #[test] fn parse_ident_pat () {
1019 let sess
= new_parse_sess();
1020 let mut parser
= string_to_parser(&sess
, "b".to_string());
1021 assert
!(parser
.parse_pat()
1023 id
: ast
::DUMMY_NODE_ID
,
1024 node
: ast
::PatIdent(ast
::BindByValue(ast
::MutImmutable
),
1025 Spanned
{ span
:sp(0, 1),
1026 node
: str_to_ident("b")
1030 parser_done(parser
);
1033 // check the contents of the tt manually:
1034 #[test] fn parse_fundecl () {
1035 // this test depends on the intern order of "fn" and "i32"
1036 assert_eq
!(string_to_item("fn a (b : i32) { b; }".to_string()),
1038 P(ast
::Item
{ident
:str_to_ident("a"),
1040 id
: ast
::DUMMY_NODE_ID
,
1041 node
: ast
::ItemFn(P(ast
::FnDecl
{
1042 inputs
: vec
!(ast
::Arg
{
1043 ty
: P(ast
::Ty
{id
: ast
::DUMMY_NODE_ID
,
1044 node
: ast
::TyPath(ast
::Path
{
1050 str_to_ident("i32"),
1051 parameters
: ast
::PathParameters
::none(),
1054 }, ast
::DUMMY_NODE_ID
),
1058 id
: ast
::DUMMY_NODE_ID
,
1059 node
: ast
::PatIdent(
1060 ast
::BindByValue(ast
::MutImmutable
),
1063 node
: str_to_ident("b")},
1068 id
: ast
::DUMMY_NODE_ID
1070 output
: ast
::DefaultReturn(sp(15, 15)),
1073 ast
::Unsafety
::Normal
,
1075 ast
::Generics
{ // no idea on either of these:
1076 lifetimes
: Vec
::new(),
1077 ty_params
: OwnedSlice
::empty(),
1078 where_clause
: ast
::WhereClause
{
1079 id
: ast
::DUMMY_NODE_ID
,
1080 predicates
: Vec
::new(),
1084 stmts
: vec
!(P(Spanned
{
1085 node
: ast
::StmtSemi(P(ast
::Expr
{
1086 id
: ast
::DUMMY_NODE_ID
,
1087 node
: ast
::ExprPath(
1097 ast
::PathParameters
::none(),
1102 ast
::DUMMY_NODE_ID
),
1105 id
: ast
::DUMMY_NODE_ID
,
1106 rules
: ast
::DefaultBlock
, // no idea
1109 vis
: ast
::Inherited
,
1113 #[test] fn parse_use() {
1114 let use_s
= "use foo::bar::baz;";
1115 let vitem
= string_to_item(use_s
.to_string()).unwrap();
1116 let vitem_s
= item_to_string(&*vitem
);
1117 assert_eq
!(&vitem_s
[..], use_s
);
1119 let use_s
= "use foo::bar as baz;";
1120 let vitem
= string_to_item(use_s
.to_string()).unwrap();
1121 let vitem_s
= item_to_string(&*vitem
);
1122 assert_eq
!(&vitem_s
[..], use_s
);
1125 #[test] fn parse_extern_crate() {
1126 let ex_s
= "extern crate foo;";
1127 let vitem
= string_to_item(ex_s
.to_string()).unwrap();
1128 let vitem_s
= item_to_string(&*vitem
);
1129 assert_eq
!(&vitem_s
[..], ex_s
);
1131 let ex_s
= "extern crate \"foo\" as bar;";
1132 let vitem
= string_to_item(ex_s
.to_string()).unwrap();
1133 let vitem_s
= item_to_string(&*vitem
);
1134 assert_eq
!(&vitem_s
[..], ex_s
);
1137 fn get_spans_of_pat_idents(src
: &str) -> Vec
<Span
> {
1138 let item
= string_to_item(src
.to_string()).unwrap();
1140 struct PatIdentVisitor
{
1143 impl<'v
> ::visit
::Visitor
<'v
> for PatIdentVisitor
{
1144 fn visit_pat(&mut self, p
: &'v ast
::Pat
) {
1146 ast
::PatIdent(_
, ref spannedident
, _
) => {
1147 self.spans
.push(spannedident
.span
.clone());
1150 ::visit
::walk_pat(self, p
);
1155 let mut v
= PatIdentVisitor { spans: Vec::new() }
;
1156 ::visit
::walk_item(&mut v
, &*item
);
1160 #[test] fn span_of_self_arg_pat_idents_are_correct() {
1162 let srcs
= ["impl z { fn a (&self, &myarg: i32) {} }",
1163 "impl z { fn a (&mut self, &myarg: i32) {} }",
1164 "impl z { fn a (&'a self, &myarg: i32) {} }",
1165 "impl z { fn a (self, &myarg: i32) {} }",
1166 "impl z { fn a (self: Foo, &myarg: i32) {} }",
1170 let spans
= get_spans_of_pat_idents(src
);
1171 let Span{ lo, hi, .. }
= spans
[0];
1172 assert
!("self" == &src
[lo
.to_usize()..hi
.to_usize()],
1173 "\"{}\" != \"self\". src=\"{}\"",
1174 &src
[lo
.to_usize()..hi
.to_usize()], src
)
1178 #[test] fn parse_exprs () {
1179 // just make sure that they parse....
1180 string_to_expr("3 + 4".to_string());
1181 string_to_expr("a::z.froob(b,&(987+3))".to_string());
1184 #[test] fn attrs_fix_bug () {
1185 string_to_item("pub fn mk_file_writer(path: &Path, flags: &[FileFlag])
1186 -> Result<Box<Writer>, String> {
1189 (O_WRONLY | libc::consts::os::extra::O_BINARY) as c_int
1193 fn wb() -> c_int { O_WRONLY as c_int }
1195 let mut fflags: c_int = wb();
1199 #[test] fn crlf_doc_comments() {
1200 let sess
= new_parse_sess();
1202 let name
= "<source>".to_string();
1203 let source
= "/// doc comment\r\nfn foo() {}".to_string();
1204 let item
= parse_item_from_source_str(name
.clone(), source
, Vec
::new(), &sess
).unwrap();
1205 let doc
= first_attr_value_str_by_name(&item
.attrs
, "doc").unwrap();
1206 assert_eq
!(&doc
[..], "/// doc comment");
1208 let source
= "/// doc comment\r\n/// line 2\r\nfn foo() {}".to_string();
1209 let item
= parse_item_from_source_str(name
.clone(), source
, Vec
::new(), &sess
).unwrap();
1210 let docs
= item
.attrs
.iter().filter(|a
| &a
.name()[] == "doc")
1211 .map(|a
| a
.value_str().unwrap().to_string()).collect
::<Vec
<_
>>();
1212 let b
: &[_
] = &["/// doc comment".to_string(), "/// line 2".to_string()];
1213 assert_eq
!(&docs
[..], b
);
1215 let source
= "/** doc comment\r\n * with CRLF */\r\nfn foo() {}".to_string();
1216 let item
= parse_item_from_source_str(name
, source
, Vec
::new(), &sess
).unwrap();
1217 let doc
= first_attr_value_str_by_name(&item
.attrs
, "doc").unwrap();
1218 assert_eq
!(&doc
[..], "/** doc comment\n * with CRLF */");
1223 let sess
= parse
::new_parse_sess();
1224 let expr
= parse
::parse_expr_from_source_str("foo".to_string(),
1225 "foo!( fn main() { body } )".to_string(), vec
![], &sess
);
1227 let tts
= match expr
.node
{
1228 ast
::ExprMac(ref mac
) => {
1229 let ast
::MacInvocTT(_
, ref tts
, _
) = mac
.node
;
1232 _
=> panic
!("not a macro"),
1235 let span
= tts
.iter().rev().next().unwrap().get_span();
1237 match sess
.span_diagnostic
.cm
.span_to_snippet(span
) {
1238 Ok(s
) => assert_eq
!(&s
[..], "{ body }"),
1239 Err(_
) => panic
!("could not get snippet"),