rust/src/libsyntax/parse/mod.rs

1098 lines
42 KiB
Rust
Raw Normal View History

// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
2012-11-29 00:20:41 +00:00
//! The main parser interface
use ast;
use codemap::CodeMap;
use syntax_pos::{self, Span, FileMap};
2015-12-20 21:00:43 +00:00
use errors::{Handler, ColorConfig, DiagnosticBuilder};
use parse::parser::Parser;
use parse::token::InternedString;
2014-09-13 16:06:01 +00:00
use ptr::P;
use str::char_at;
use tokenstream;
use std::cell::RefCell;
use std::iter;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::str;
2015-12-20 21:00:43 +00:00
pub type PResult<'a, T> = Result<T, DiagnosticBuilder<'a>>;
#[macro_use]
pub mod parser;
pub mod lexer;
pub mod token;
pub mod attr;
2012-11-19 01:56:50 +00:00
pub mod common;
pub mod classify;
pub mod obsolete;
2012-11-29 00:20:41 +00:00
2014-06-09 20:12:30 +00:00
/// Info about a parsing session.
pub struct ParseSess {
pub span_diagnostic: Handler, // better be the same as the one in the reader!
/// Used to determine and report recursive mod inclusions
included_mod_stack: RefCell<Vec<PathBuf>>,
code_map: Rc<CodeMap>,
}
2012-11-29 00:20:41 +00:00
impl ParseSess {
pub fn new() -> ParseSess {
let cm = Rc::new(CodeMap::new());
let handler = Handler::with_tty_emitter(ColorConfig::Auto, None, true, false, cm.clone());
ParseSess::with_span_handler(handler, cm)
}
2012-11-29 00:20:41 +00:00
pub fn with_span_handler(handler: Handler, code_map: Rc<CodeMap>) -> ParseSess {
ParseSess {
span_diagnostic: handler,
included_mod_stack: RefCell::new(vec![]),
code_map: code_map
}
}
pub fn codemap(&self) -> &CodeMap {
&self.code_map
}
2012-11-29 00:20:41 +00:00
}
2013-02-11 21:36:24 +00:00
// a bunch of utility functions of the form parse_<thing>_from_<source>
// where <thing> includes crate, expr, item, stmt, tts, and one that
// uses a HOF to parse anything, and <source> includes file and
// source_str.
pub fn parse_crate_from_file<'a>(input: &Path,
cfg: ast::CrateConfig,
sess: &'a ParseSess)
-> PResult<'a, ast::Crate> {
2015-12-20 21:00:43 +00:00
let mut parser = new_parser_from_file(sess, cfg, input);
parser.parse_crate_mod()
2012-11-29 00:20:41 +00:00
}
pub fn parse_crate_attrs_from_file<'a>(input: &Path,
cfg: ast::CrateConfig,
sess: &'a ParseSess)
-> PResult<'a, Vec<ast::Attribute>> {
2015-12-20 21:00:43 +00:00
let mut parser = new_parser_from_file(sess, cfg, input);
parser.parse_inner_attributes()
}
pub fn parse_crate_from_source_str<'a>(name: String,
source: String,
cfg: ast::CrateConfig,
sess: &'a ParseSess)
-> PResult<'a, ast::Crate> {
2013-12-30 22:04:00 +00:00
let mut p = new_parser_from_source_str(sess,
2014-02-05 17:16:44 +00:00
cfg,
2013-12-30 22:04:00 +00:00
name,
source);
p.parse_crate_mod()
2012-11-29 00:20:41 +00:00
}
pub fn parse_crate_attrs_from_source_str<'a>(name: String,
source: String,
cfg: ast::CrateConfig,
sess: &'a ParseSess)
-> PResult<'a, Vec<ast::Attribute>> {
2013-12-30 22:04:00 +00:00
let mut p = new_parser_from_source_str(sess,
2014-02-05 17:16:44 +00:00
cfg,
2013-12-30 22:04:00 +00:00
name,
source);
p.parse_inner_attributes()
}
pub fn parse_expr_from_source_str<'a>(name: String,
source: String,
cfg: ast::CrateConfig,
sess: &'a ParseSess)
-> PResult<'a, P<ast::Expr>> {
2013-12-30 22:04:00 +00:00
let mut p = new_parser_from_source_str(sess, cfg, name, source);
p.parse_expr()
2012-11-29 00:20:41 +00:00
}
2016-02-19 13:43:13 +00:00
/// Parses an item.
///
/// Returns `Ok(Some(item))` when successful, `Ok(None)` when no item was found, and`Err`
/// when a syntax error occurred.
pub fn parse_item_from_source_str<'a>(name: String,
source: String,
cfg: ast::CrateConfig,
sess: &'a ParseSess)
-> PResult<'a, Option<P<ast::Item>>> {
2013-12-30 22:04:00 +00:00
let mut p = new_parser_from_source_str(sess, cfg, name, source);
p.parse_item()
2012-11-29 00:20:41 +00:00
}
pub fn parse_meta_from_source_str<'a>(name: String,
source: String,
cfg: ast::CrateConfig,
sess: &'a ParseSess)
-> PResult<'a, P<ast::MetaItem>> {
2013-12-30 22:04:00 +00:00
let mut p = new_parser_from_source_str(sess, cfg, name, source);
p.parse_meta_item()
}
pub fn parse_stmt_from_source_str<'a>(name: String,
source: String,
cfg: ast::CrateConfig,
sess: &'a ParseSess)
-> PResult<'a, Option<ast::Stmt>> {
2013-12-30 22:04:00 +00:00
let mut p = new_parser_from_source_str(
sess,
cfg,
name,
source
);
p.parse_stmt()
2012-11-29 00:20:41 +00:00
}
// Warning: This parses with quote_depth > 0, which is not the default.
pub fn parse_tts_from_source_str<'a>(name: String,
source: String,
cfg: ast::CrateConfig,
sess: &'a ParseSess)
-> PResult<'a, Vec<tokenstream::TokenTree>> {
2013-12-30 22:04:00 +00:00
let mut p = new_parser_from_source_str(
sess,
cfg,
name,
source
);
p.quote_depth += 1;
2013-04-23 17:57:41 +00:00
// right now this is re-creating the token trees from ... token trees.
p.parse_all_token_trees()
2012-11-29 00:20:41 +00:00
}
2013-04-23 17:57:41 +00:00
// Create a new parser from a source string
2014-03-09 14:54:34 +00:00
pub fn new_parser_from_source_str<'a>(sess: &'a ParseSess,
2014-03-16 18:56:24 +00:00
cfg: ast::CrateConfig,
name: String,
source: String)
2014-03-16 18:56:24 +00:00
-> Parser<'a> {
filemap_to_parser(sess, sess.codemap().new_filemap(name, None, source), cfg)
2012-11-29 00:20:41 +00:00
}
/// Create a new parser, handling errors as appropriate
2012-11-29 00:20:41 +00:00
/// if the file doesn't exist
2014-03-16 18:56:24 +00:00
pub fn new_parser_from_file<'a>(sess: &'a ParseSess,
cfg: ast::CrateConfig,
path: &Path) -> Parser<'a> {
filemap_to_parser(sess, file_to_filemap(sess, path, None), cfg)
2012-11-29 00:20:41 +00:00
}
2013-04-23 17:57:41 +00:00
/// Given a session, a crate config, a path, and a span, add
/// the file at the given path to the codemap, and return a parser.
/// On an error, use the given span as the source of the problem.
2014-03-16 18:56:24 +00:00
pub fn new_sub_parser_from_file<'a>(sess: &'a ParseSess,
cfg: ast::CrateConfig,
path: &Path,
owns_directory: bool,
module_name: Option<String>,
2014-03-16 18:56:24 +00:00
sp: Span) -> Parser<'a> {
let mut p = filemap_to_parser(sess, file_to_filemap(sess, path, Some(sp)), cfg);
p.owns_directory = owns_directory;
p.root_module_name = module_name;
p
2013-04-23 17:57:41 +00:00
}
/// Given a filemap and config, return a parser
2014-03-09 14:54:34 +00:00
pub fn filemap_to_parser<'a>(sess: &'a ParseSess,
2014-03-16 18:56:24 +00:00
filemap: Rc<FileMap>,
2014-03-09 14:54:34 +00:00
cfg: ast::CrateConfig) -> Parser<'a> {
let end_pos = filemap.end_pos;
let mut parser = tts_to_parser(sess, filemap_to_tts(sess, filemap), cfg);
if parser.token == token::Eof && parser.span == syntax_pos::DUMMY_SP {
parser.span = syntax_pos::mk_sp(end_pos, end_pos);
}
parser
2013-04-23 17:57:41 +00:00
}
// must preserve old name for now, because quote! from the *existing*
// compiler expands into it
2014-03-09 14:54:34 +00:00
pub fn new_parser_from_tts<'a>(sess: &'a ParseSess,
cfg: ast::CrateConfig,
tts: Vec<tokenstream::TokenTree>) -> Parser<'a> {
2014-03-16 18:56:24 +00:00
tts_to_parser(sess, tts, cfg)
2013-04-23 17:57:41 +00:00
}
// base abstractions
/// Given a session and a path and an optional span (for error reporting),
/// add the path to the session's codemap and return the new filemap.
fn file_to_filemap(sess: &ParseSess, path: &Path, spanopt: Option<Span>)
-> Rc<FileMap> {
match sess.codemap().load_file(path) {
Ok(filemap) => filemap,
Err(e) => {
let msg = format!("couldn't read {:?}: {}", path.display(), e);
match spanopt {
Some(sp) => panic!(sess.span_diagnostic.span_fatal(sp, &msg)),
None => panic!(sess.span_diagnostic.fatal(&msg))
}
}
2012-11-29 00:20:41 +00:00
}
}
2014-06-09 20:12:30 +00:00
/// Given a filemap, produce a sequence of token-trees
2014-03-16 18:56:24 +00:00
pub fn filemap_to_tts(sess: &ParseSess, filemap: Rc<FileMap>)
-> Vec<tokenstream::TokenTree> {
2013-04-23 17:57:41 +00:00
// it appears to me that the cfg doesn't matter here... indeed,
// parsing tt's probably shouldn't require a parser at all.
let cfg = Vec::new();
2014-05-21 23:57:31 +00:00
let srdr = lexer::StringReader::new(&sess.span_diagnostic, filemap);
let mut p1 = Parser::new(sess, cfg, Box::new(srdr));
panictry!(p1.parse_all_token_trees())
2013-04-23 17:57:41 +00:00
}
2014-06-09 20:12:30 +00:00
/// Given tts and cfg, produce a parser
2014-03-09 14:54:34 +00:00
pub fn tts_to_parser<'a>(sess: &'a ParseSess,
tts: Vec<tokenstream::TokenTree>,
2014-03-09 14:54:34 +00:00
cfg: ast::CrateConfig) -> Parser<'a> {
2014-09-16 01:27:28 +00:00
let trdr = lexer::new_tt_reader(&sess.span_diagnostic, None, None, tts);
let mut p = Parser::new(sess, cfg, Box::new(trdr));
p.check_unknown_macro_variable();
p
2012-11-29 00:20:41 +00:00
}
2013-01-30 17:56:33 +00:00
/// Parse a string representing a character literal into its final form.
/// Rather than just accepting/rejecting a given literal, unescapes it as
/// well. Can take any slice prefixed by a character escape. Returns the
/// character and the number of characters consumed.
2015-01-17 23:33:05 +00:00
pub fn char_lit(lit: &str) -> (char, isize) {
use std::char;
let mut chars = lit.chars();
let c = match (chars.next(), chars.next()) {
(Some(c), None) if c != '\\' => return (c, 1),
(Some('\\'), Some(c)) => match c {
'"' => Some('"'),
'n' => Some('\n'),
'r' => Some('\r'),
't' => Some('\t'),
'\\' => Some('\\'),
'\'' => Some('\''),
'0' => Some('\0'),
_ => { None }
},
_ => panic!("lexer accepted invalid char escape `{}`", lit)
};
match c {
Some(x) => return (x, 2),
None => { }
}
let msg = format!("lexer should have rejected a bad character escape {}", lit);
let msg2 = &msg[..];
2015-01-17 23:33:05 +00:00
fn esc(len: usize, lit: &str) -> Option<(char, isize)> {
u32::from_str_radix(&lit[2..len], 16).ok()
.and_then(char::from_u32)
2015-01-17 23:33:05 +00:00
.map(|x| (x, len as isize))
}
let unicode_escape = || -> Option<(char, isize)> {
if lit.as_bytes()[2] == b'{' {
let idx = lit.find('}').expect(msg2);
2015-01-07 16:58:31 +00:00
let subslice = &lit[3..idx];
u32::from_str_radix(subslice, 16).ok()
.and_then(char::from_u32)
2015-01-17 23:33:05 +00:00
.map(|x| (x, subslice.chars().count() as isize + 4))
} else {
esc(6, lit)
}
};
// Unicode escapes
return match lit.as_bytes()[1] as char {
'x' | 'X' => esc(4, lit),
'u' => unicode_escape(),
'U' => esc(10, lit),
_ => None,
}.expect(msg2);
}
/// Parse a string representing a string literal into its final form. Does
/// unescaping.
pub fn str_lit(lit: &str) -> String {
debug!("parse_str_lit: given {}", lit.escape_default());
let mut res = String::with_capacity(lit.len());
// FIXME #8372: This could be a for-loop if it didn't borrow the iterator
let error = |i| format!("lexer should have rejected {} at {}", lit, i);
/// Eat everything up to a non-whitespace
fn eat<'a>(it: &mut iter::Peekable<str::CharIndices<'a>>) {
loop {
2014-12-09 17:17:24 +00:00
match it.peek().map(|x| x.1) {
Some(' ') | Some('\n') | Some('\r') | Some('\t') => {
it.next();
},
_ => { break; }
}
}
}
let mut chars = lit.char_indices().peekable();
loop {
match chars.next() {
Some((i, c)) => {
match c {
'\\' => {
let ch = chars.peek().unwrap_or_else(|| {
panic!("{}", error(i))
2014-12-09 17:17:24 +00:00
}).1;
if ch == '\n' {
eat(&mut chars);
} else if ch == '\r' {
chars.next();
let ch = chars.peek().unwrap_or_else(|| {
panic!("{}", error(i))
2014-12-09 17:17:24 +00:00
}).1;
if ch != '\n' {
panic!("lexer accepted bare CR");
}
eat(&mut chars);
} else {
// otherwise, a normal escape
2015-01-07 16:58:31 +00:00
let (c, n) = char_lit(&lit[i..]);
for _ in 0..n - 1 { // we don't need to move past the first \
chars.next();
}
res.push(c);
}
},
'\r' => {
let ch = chars.peek().unwrap_or_else(|| {
panic!("{}", error(i))
2014-12-09 17:17:24 +00:00
}).1;
if ch != '\n' {
panic!("lexer accepted bare CR");
}
chars.next();
res.push('\n');
}
c => res.push(c),
}
},
None => break
}
}
res.shrink_to_fit(); // probably not going to do anything, unless there was an escape.
debug!("parse_str_lit: returning {}", res);
res
}
/// Parse a string representing a raw string literal into its final form. The
/// only operation this does is convert embedded CRLF into a single LF.
pub fn raw_str_lit(lit: &str) -> String {
debug!("raw_str_lit: given {}", lit.escape_default());
let mut res = String::with_capacity(lit.len());
// FIXME #8372: This could be a for-loop if it didn't borrow the iterator
let mut chars = lit.chars().peekable();
loop {
match chars.next() {
Some(c) => {
if c == '\r' {
if *chars.peek().unwrap() != '\n' {
panic!("lexer accepted bare CR");
}
chars.next();
res.push('\n');
} else {
res.push(c);
}
},
None => break
}
}
res.shrink_to_fit();
res
}
// check if `s` looks like i32 or u1234 etc.
fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool {
s.len() > 1 &&
first_chars.contains(&char_at(s, 0)) &&
2015-01-07 16:58:31 +00:00
s[1..].chars().all(|c| '0' <= c && c <= '9')
}
fn filtered_float_lit(data: token::InternedString, suffix: Option<&str>,
sd: &Handler, sp: Span) -> ast::LitKind {
debug!("filtered_float_lit: {}, {:?}", data, suffix);
match suffix.as_ref().map(|s| &**s) {
Some("f32") => ast::LitKind::Float(data, ast::FloatTy::F32),
Some("f64") => ast::LitKind::Float(data, ast::FloatTy::F64),
Some(suf) => {
if suf.len() >= 2 && looks_like_width_suffix(&['f'], suf) {
// if it looks like a width, lets try to be helpful.
2015-12-20 21:00:43 +00:00
sd.struct_span_err(sp, &format!("invalid width `{}` for float literal", &suf[1..]))
.help("valid widths are 32 and 64")
2015-12-20 21:00:43 +00:00
.emit();
} else {
2015-12-20 21:00:43 +00:00
sd.struct_span_err(sp, &format!("invalid suffix `{}` for float literal", suf))
.help("valid suffixes are `f32` and `f64`")
2015-12-20 21:00:43 +00:00
.emit();
}
ast::LitKind::FloatUnsuffixed(data)
}
None => ast::LitKind::FloatUnsuffixed(data)
}
}
pub fn float_lit(s: &str, suffix: Option<InternedString>,
sd: &Handler, sp: Span) -> ast::LitKind {
debug!("float_lit: {:?}, {:?}", s, suffix);
// FIXME #2252: bounds checking float literals is deferred until trans
let s = s.chars().filter(|&c| c != '_').collect::<String>();
let data = token::intern_and_get_ident(&s);
filtered_float_lit(data, suffix.as_ref().map(|s| &**s), sd, sp)
}
/// Parse a string representing a byte literal into its final form. Similar to `char_lit`
2015-01-17 23:33:05 +00:00
pub fn byte_lit(lit: &str) -> (u8, usize) {
let err = |i| format!("lexer accepted invalid byte literal {} step {}", lit, i);
if lit.len() == 1 {
(lit.as_bytes()[0], 1)
} else {
2015-01-31 16:23:42 +00:00
assert!(lit.as_bytes()[0] == b'\\', err(0));
let b = match lit.as_bytes()[1] {
b'"' => b'"',
b'n' => b'\n',
b'r' => b'\r',
b't' => b'\t',
b'\\' => b'\\',
b'\'' => b'\'',
b'0' => b'\0',
_ => {
match u64::from_str_radix(&lit[2..4], 16).ok() {
Some(c) =>
if c > 0xFF {
panic!(err(2))
} else {
return (c as u8, 4)
},
None => panic!(err(3))
}
}
};
return (b, 2);
}
}
pub fn byte_str_lit(lit: &str) -> Rc<Vec<u8>> {
let mut res = Vec::with_capacity(lit.len());
// FIXME #8372: This could be a for-loop if it didn't borrow the iterator
let error = |i| format!("lexer should have rejected {} at {}", lit, i);
/// Eat everything up to a non-whitespace
fn eat<'a, I: Iterator<Item=(usize, u8)>>(it: &mut iter::Peekable<I>) {
loop {
2014-12-09 17:17:24 +00:00
match it.peek().map(|x| x.1) {
Some(b' ') | Some(b'\n') | Some(b'\r') | Some(b'\t') => {
it.next();
},
_ => { break; }
}
}
}
// byte string literals *must* be ASCII, but the escapes don't have to be
let mut chars = lit.bytes().enumerate().peekable();
loop {
match chars.next() {
Some((i, b'\\')) => {
let em = error(i);
match chars.peek().expect(&em).1 {
b'\n' => eat(&mut chars),
b'\r' => {
chars.next();
if chars.peek().expect(&em).1 != b'\n' {
panic!("lexer accepted bare CR");
}
eat(&mut chars);
}
_ => {
// otherwise, a normal escape
2015-01-07 16:58:31 +00:00
let (c, n) = byte_lit(&lit[i..]);
// we don't need to move past the first \
for _ in 0..n - 1 {
chars.next();
}
res.push(c);
}
}
},
Some((i, b'\r')) => {
let em = error(i);
if chars.peek().expect(&em).1 != b'\n' {
panic!("lexer accepted bare CR");
}
chars.next();
res.push(b'\n');
}
Some((_, c)) => res.push(c),
None => break,
}
}
Rc::new(res)
}
2013-02-04 21:15:17 +00:00
pub fn integer_lit(s: &str,
suffix: Option<InternedString>,
sd: &Handler,
sp: Span)
-> ast::LitKind {
// s can only be ascii, byte indexing is fine
let s2 = s.chars().filter(|&c| c != '_').collect::<String>();
let mut s = &s2[..];
debug!("integer_lit: {}, {:?}", s, suffix);
let mut base = 10;
let orig = s;
let mut ty = ast::LitIntType::Unsuffixed;
if char_at(s, 0) == '0' && s.len() > 1 {
match char_at(s, 1) {
'x' => base = 16,
'o' => base = 8,
'b' => base = 2,
_ => { }
}
}
// 1f64 and 2f32 etc. are valid float literals.
if let Some(ref suf) = suffix {
if looks_like_width_suffix(&['f'], suf) {
match base {
16 => sd.span_err(sp, "hexadecimal float literal is not supported"),
8 => sd.span_err(sp, "octal float literal is not supported"),
2 => sd.span_err(sp, "binary float literal is not supported"),
_ => ()
}
2016-02-08 22:55:55 +00:00
let ident = token::intern_and_get_ident(&s);
return filtered_float_lit(ident, Some(&suf), sd, sp)
}
}
if base != 10 {
2015-01-07 16:58:31 +00:00
s = &s[2..];
}
if let Some(ref suf) = suffix {
if suf.is_empty() { sd.span_bug(sp, "found empty literal suffix in Some")}
ty = match &**suf {
"isize" => ast::LitIntType::Signed(ast::IntTy::Is),
"i8" => ast::LitIntType::Signed(ast::IntTy::I8),
"i16" => ast::LitIntType::Signed(ast::IntTy::I16),
"i32" => ast::LitIntType::Signed(ast::IntTy::I32),
"i64" => ast::LitIntType::Signed(ast::IntTy::I64),
"usize" => ast::LitIntType::Unsigned(ast::UintTy::Us),
"u8" => ast::LitIntType::Unsigned(ast::UintTy::U8),
"u16" => ast::LitIntType::Unsigned(ast::UintTy::U16),
"u32" => ast::LitIntType::Unsigned(ast::UintTy::U32),
"u64" => ast::LitIntType::Unsigned(ast::UintTy::U64),
_ => {
// i<digits> and u<digits> look like widths, so lets
// give an error message along those lines
if looks_like_width_suffix(&['i', 'u'], suf) {
2015-12-20 21:00:43 +00:00
sd.struct_span_err(sp, &format!("invalid width `{}` for integer literal",
&suf[1..]))
.help("valid widths are 8, 16, 32 and 64")
2015-12-20 21:00:43 +00:00
.emit();
} else {
2015-12-20 21:00:43 +00:00
sd.struct_span_err(sp, &format!("invalid suffix `{}` for numeric literal", suf))
.help("the suffix must be one of the integral types \
(`u32`, `isize`, etc)")
2015-12-20 21:00:43 +00:00
.emit();
}
ty
}
}
}
debug!("integer_lit: the type is {:?}, base {:?}, the new string is {:?}, the original \
string was {:?}, the original suffix was {:?}", ty, base, s, orig, suffix);
match u64::from_str_radix(s, base) {
Ok(r) => ast::LitKind::Int(r, ty),
Err(_) => {
// small bases are lexed as if they were base 10, e.g, the string
// might be `0b10201`. This will cause the conversion above to fail,
// but these cases have errors in the lexer: we don't want to emit
// two errors, and we especially don't want to emit this error since
// it isn't necessarily true.
let already_errored = base < 10 &&
s.chars().any(|c| c.to_digit(10).map_or(false, |d| d >= base));
if !already_errored {
sd.span_err(sp, "int literal is too large");
}
ast::LitKind::Int(0, ty)
}
}
}
2013-02-04 21:15:17 +00:00
#[cfg(test)]
mod tests {
2013-02-04 21:15:17 +00:00
use super::*;
use std::rc::Rc;
2016-06-22 16:50:19 +00:00
use syntax_pos::{Span, BytePos, Pos, NO_EXPANSION};
use codemap::Spanned;
use ast::{self, PatKind};
use abi::Abi;
use attr::{first_attr_value_str_by_name, AttrMetaMethods};
2015-01-31 22:54:43 +00:00
use parse;
2013-04-23 17:57:41 +00:00
use parse::parser::Parser;
2013-06-08 16:21:11 +00:00
use parse::token::{str_to_ident};
use print::pprust::item_to_string;
use ptr::P;
use tokenstream::{self, TokenTree};
2013-09-24 19:31:24 +00:00
use util::parser_testing::{string_to_tts, string_to_parser};
use util::parser_testing::{string_to_expr, string_to_item, string_to_stmt};
use util::ThinVec;
2013-04-23 17:57:41 +00:00
// produce a syntax_pos::span
2013-11-20 16:32:29 +00:00
fn sp(a: u32, b: u32) -> Span {
Span {lo: BytePos(a), hi: BytePos(b), expn_id: NO_EXPANSION}
2013-04-23 17:57:41 +00:00
}
#[test] fn path_exprs_1() {
assert!(string_to_expr("a".to_string()) ==
2014-09-13 16:06:01 +00:00
P(ast::Expr{
id: ast::DUMMY_NODE_ID,
node: ast::ExprKind::Path(None, ast::Path {
span: sp(0, 1),
global: false,
segments: vec!(
ast::PathSegment {
identifier: str_to_ident("a"),
parameters: ast::PathParameters::none(),
}
),
}),
span: sp(0, 1),
attrs: ThinVec::new(),
2014-09-13 16:06:01 +00:00
}))
2013-04-23 17:57:41 +00:00
}
#[test] fn path_exprs_2 () {
assert!(string_to_expr("::a::b".to_string()) ==
2014-09-13 16:06:01 +00:00
P(ast::Expr {
id: ast::DUMMY_NODE_ID,
node: ast::ExprKind::Path(None, ast::Path {
span: sp(0, 6),
global: true,
segments: vec!(
ast::PathSegment {
identifier: str_to_ident("a"),
parameters: ast::PathParameters::none(),
},
ast::PathSegment {
identifier: str_to_ident("b"),
parameters: ast::PathParameters::none(),
}
)
}),
span: sp(0, 6),
attrs: ThinVec::new(),
2014-09-13 16:06:01 +00:00
}))
2013-04-23 17:57:41 +00:00
}
#[should_panic]
2013-04-23 17:57:41 +00:00
#[test] fn bad_path_expr_1() {
string_to_expr("::abc::def::return".to_string());
}
2013-04-23 17:57:41 +00:00
// check the token-tree-ization of macros
#[test]
fn string_to_tts_macro () {
let tts = string_to_tts("macro_rules! zip (($a)=>($a))".to_string());
let tts: &[tokenstream::TokenTree] = &tts[..];
match (tts.len(), tts.get(0), tts.get(1), tts.get(2), tts.get(3)) {
(
4,
2016-04-16 01:12:02 +00:00
Some(&TokenTree::Token(_, token::Ident(name_macro_rules))),
Some(&TokenTree::Token(_, token::Not)),
2016-04-16 01:12:02 +00:00
Some(&TokenTree::Token(_, token::Ident(name_zip))),
Some(&TokenTree::Delimited(_, ref macro_delimed)),
)
if name_macro_rules.name.as_str() == "macro_rules"
&& name_zip.name.as_str() == "zip" => {
let tts = &macro_delimed.tts[..];
match (tts.len(), tts.get(0), tts.get(1), tts.get(2)) {
(
3,
Some(&TokenTree::Delimited(_, ref first_delimed)),
Some(&TokenTree::Token(_, token::FatArrow)),
Some(&TokenTree::Delimited(_, ref second_delimed)),
)
if macro_delimed.delim == token::Paren => {
let tts = &first_delimed.tts[..];
match (tts.len(), tts.get(0), tts.get(1)) {
(
2,
Some(&TokenTree::Token(_, token::Dollar)),
2016-04-16 01:12:02 +00:00
Some(&TokenTree::Token(_, token::Ident(ident))),
)
if first_delimed.delim == token::Paren
&& ident.name.as_str() == "a" => {},
_ => panic!("value 3: {:?}", **first_delimed),
}
let tts = &second_delimed.tts[..];
match (tts.len(), tts.get(0), tts.get(1)) {
(
2,
Some(&TokenTree::Token(_, token::Dollar)),
2016-04-16 01:12:02 +00:00
Some(&TokenTree::Token(_, token::Ident(ident))),
)
if second_delimed.delim == token::Paren
&& ident.name.as_str() == "a" => {},
_ => panic!("value 4: {:?}", **second_delimed),
}
},
_ => panic!("value 2: {:?}", **macro_delimed),
}
},
2015-01-07 00:16:35 +00:00
_ => panic!("value: {:?}",tts),
}
}
#[test]
fn string_to_tts_1() {
let tts = string_to_tts("fn a (b : i32) { b; }".to_string());
let expected = vec![
2016-04-16 01:12:02 +00:00
TokenTree::Token(sp(0, 2), token::Ident(str_to_ident("fn"))),
TokenTree::Token(sp(3, 4), token::Ident(str_to_ident("a"))),
TokenTree::Delimited(
sp(5, 14),
Rc::new(tokenstream::Delimited {
delim: token::DelimToken::Paren,
open_span: sp(5, 6),
tts: vec![
2016-04-16 01:12:02 +00:00
TokenTree::Token(sp(6, 7), token::Ident(str_to_ident("b"))),
TokenTree::Token(sp(8, 9), token::Colon),
TokenTree::Token(sp(10, 13), token::Ident(str_to_ident("i32"))),
],
close_span: sp(13, 14),
})),
TokenTree::Delimited(
sp(15, 21),
Rc::new(tokenstream::Delimited {
delim: token::DelimToken::Brace,
open_span: sp(15, 16),
tts: vec![
2016-04-16 01:12:02 +00:00
TokenTree::Token(sp(17, 18), token::Ident(str_to_ident("b"))),
TokenTree::Token(sp(18, 19), token::Semi),
],
close_span: sp(20, 21),
}))
];
assert_eq!(tts, expected);
2013-04-23 17:57:41 +00:00
}
#[test] fn ret_expr() {
assert!(string_to_expr("return d".to_string()) ==
2014-09-13 16:06:01 +00:00
P(ast::Expr{
id: ast::DUMMY_NODE_ID,
node:ast::ExprKind::Ret(Some(P(ast::Expr{
id: ast::DUMMY_NODE_ID,
node:ast::ExprKind::Path(None, ast::Path{
span: sp(7, 8),
global: false,
segments: vec!(
ast::PathSegment {
identifier: str_to_ident("d"),
parameters: ast::PathParameters::none(),
}
),
}),
span:sp(7,8),
attrs: ThinVec::new(),
2014-09-13 16:06:01 +00:00
}))),
span:sp(0,8),
attrs: ThinVec::new(),
2014-09-13 16:06:01 +00:00
}))
2013-04-23 17:57:41 +00:00
}
#[test] fn parse_stmt_1 () {
assert!(string_to_stmt("b;".to_string()) ==
2016-06-17 02:30:01 +00:00
Some(ast::Stmt {
node: ast::StmtKind::Expr(P(ast::Expr {
id: ast::DUMMY_NODE_ID,
node: ast::ExprKind::Path(None, ast::Path {
span:sp(0,1),
global:false,
segments: vec!(
ast::PathSegment {
identifier: str_to_ident("b"),
parameters: ast::PathParameters::none(),
}
),
}),
span: sp(0,1),
attrs: ThinVec::new()})),
2016-06-17 02:30:01 +00:00
id: ast::DUMMY_NODE_ID,
span: sp(0,1)}))
2013-04-23 17:57:41 +00:00
}
fn parser_done(p: Parser){
2014-10-27 08:22:52 +00:00
assert_eq!(p.token.clone(), token::Eof);
}
2013-04-23 17:57:41 +00:00
#[test] fn parse_ident_pat () {
let sess = ParseSess::new();
let mut parser = string_to_parser(&sess, "b".to_string());
assert!(panictry!(parser.parse_pat())
2014-09-13 16:06:01 +00:00
== P(ast::Pat{
id: ast::DUMMY_NODE_ID,
2016-02-11 18:16:33 +00:00
node: PatKind::Ident(ast::BindingMode::ByValue(ast::Mutability::Immutable),
Spanned{ span:sp(0, 1),
node: str_to_ident("b")
},
None),
2014-09-13 16:06:01 +00:00
span: sp(0,1)}));
parser_done(parser);
2013-04-23 17:57:41 +00:00
}
// check the contents of the tt manually:
#[test] fn parse_fundecl () {
// this test depends on the intern order of "fn" and "i32"
assert_eq!(string_to_item("fn a (b : i32) { b; }".to_string()),
2013-04-23 17:57:41 +00:00
Some(
2014-09-13 16:06:01 +00:00
P(ast::Item{ident:str_to_ident("a"),
attrs:Vec::new(),
id: ast::DUMMY_NODE_ID,
node: ast::ItemKind::Fn(P(ast::FnDecl {
inputs: vec!(ast::Arg{
ty: P(ast::Ty{id: ast::DUMMY_NODE_ID,
node: ast::TyKind::Path(None, ast::Path{
2013-04-23 17:57:41 +00:00
span:sp(10,13),
global:false,
segments: vec!(
ast::PathSegment {
identifier:
str_to_ident("i32"),
parameters: ast::PathParameters::none(),
}
),
}),
span:sp(10,13)
}),
2014-09-13 16:06:01 +00:00
pat: P(ast::Pat {
id: ast::DUMMY_NODE_ID,
2016-02-11 18:16:33 +00:00
node: PatKind::Ident(
ast::BindingMode::ByValue(ast::Mutability::Immutable),
Spanned{
span: sp(6,7),
node: str_to_ident("b")},
None
),
span: sp(6,7)
2014-09-13 16:06:01 +00:00
}),
id: ast::DUMMY_NODE_ID
}),
output: ast::FunctionRetTy::Default(sp(15, 15)),
variadic: false
}),
2014-12-09 15:36:46 +00:00
ast::Unsafety::Normal,
2015-05-22 12:45:05 +00:00
ast::Constness::NotConst,
Abi::Rust,
2013-04-23 17:57:41 +00:00
ast::Generics{ // no idea on either of these:
lifetimes: Vec::new(),
2016-04-22 20:43:14 +00:00
ty_params: P::new(),
where_clause: ast::WhereClause {
id: ast::DUMMY_NODE_ID,
predicates: Vec::new(),
}
2013-04-23 17:57:41 +00:00
},
P(ast::Block {
2016-06-17 02:30:01 +00:00
stmts: vec!(ast::Stmt {
node: ast::StmtKind::Semi(P(ast::Expr{
id: ast::DUMMY_NODE_ID,
node: ast::ExprKind::Path(None,
ast::Path{
span:sp(17,18),
global:false,
segments: vec!(
ast::PathSegment {
identifier:
str_to_ident(
"b"),
parameters:
ast::PathParameters::none(),
}
),
}),
span: sp(17,18),
attrs: ThinVec::new()})),
2016-06-17 02:30:01 +00:00
id: ast::DUMMY_NODE_ID,
span: sp(17,19)}),
id: ast::DUMMY_NODE_ID,
rules: ast::BlockCheckMode::Default, // no idea
2013-04-23 17:57:41 +00:00
span: sp(15,21),
})),
vis: ast::Visibility::Inherited,
2014-09-13 16:06:01 +00:00
span: sp(0,21)})));
2013-04-23 17:57:41 +00:00
}
#[test] fn parse_use() {
let use_s = "use foo::bar::baz;";
let vitem = string_to_item(use_s.to_string()).unwrap();
2016-02-08 22:55:55 +00:00
let vitem_s = item_to_string(&vitem);
assert_eq!(&vitem_s[..], use_s);
let use_s = "use foo::bar as baz;";
let vitem = string_to_item(use_s.to_string()).unwrap();
2016-02-08 22:55:55 +00:00
let vitem_s = item_to_string(&vitem);
assert_eq!(&vitem_s[..], use_s);
}
#[test] fn parse_extern_crate() {
let ex_s = "extern crate foo;";
let vitem = string_to_item(ex_s.to_string()).unwrap();
2016-02-08 22:55:55 +00:00
let vitem_s = item_to_string(&vitem);
assert_eq!(&vitem_s[..], ex_s);
let ex_s = "extern crate foo as bar;";
let vitem = string_to_item(ex_s.to_string()).unwrap();
2016-02-08 22:55:55 +00:00
let vitem_s = item_to_string(&vitem);
assert_eq!(&vitem_s[..], ex_s);
}
fn get_spans_of_pat_idents(src: &str) -> Vec<Span> {
let item = string_to_item(src.to_string()).unwrap();
struct PatIdentVisitor {
spans: Vec<Span>
}
impl ::visit::Visitor for PatIdentVisitor {
fn visit_pat(&mut self, p: &ast::Pat) {
match p.node {
2016-02-11 18:16:33 +00:00
PatKind::Ident(_ , ref spannedident, _) => {
self.spans.push(spannedident.span.clone());
}
_ => {
::visit::walk_pat(self, p);
}
}
}
}
let mut v = PatIdentVisitor { spans: Vec::new() };
2016-02-08 22:55:55 +00:00
::visit::walk_item(&mut v, &item);
return v.spans;
}
#[test] fn span_of_self_arg_pat_idents_are_correct() {
2015-01-17 23:33:05 +00:00
let srcs = ["impl z { fn a (&self, &myarg: i32) {} }",
"impl z { fn a (&mut self, &myarg: i32) {} }",
"impl z { fn a (&'a self, &myarg: i32) {} }",
"impl z { fn a (self, &myarg: i32) {} }",
"impl z { fn a (self: Foo, &myarg: i32) {} }",
];
2015-01-31 17:20:46 +00:00
for &src in &srcs {
let spans = get_spans_of_pat_idents(src);
2015-01-04 04:43:24 +00:00
let Span{ lo, hi, .. } = spans[0];
assert!("self" == &src[lo.to_usize()..hi.to_usize()],
"\"{}\" != \"self\". src=\"{}\"",
&src[lo.to_usize()..hi.to_usize()], src)
}
}
2013-04-23 17:57:41 +00:00
#[test] fn parse_exprs () {
// just make sure that they parse....
string_to_expr("3 + 4".to_string());
2014-09-13 16:06:01 +00:00
string_to_expr("a::z.froob(b,&(987+3))".to_string());
2013-02-04 21:15:17 +00:00
}
#[test] fn attrs_fix_bug () {
2014-04-16 01:17:48 +00:00
string_to_item("pub fn mk_file_writer(path: &Path, flags: &[FileFlag])
2014-09-13 16:06:01 +00:00
-> Result<Box<Writer>, String> {
#[cfg(windows)]
fn wb() -> c_int {
(O_WRONLY | libc::consts::os::extra::O_BINARY) as c_int
}
#[cfg(unix)]
fn wb() -> c_int { O_WRONLY as c_int }
let mut fflags: c_int = wb();
}".to_string());
}
#[test] fn crlf_doc_comments() {
let sess = ParseSess::new();
let name = "<source>".to_string();
let source = "/// doc comment\r\nfn foo() {}".to_string();
let item = parse_item_from_source_str(name.clone(), source, Vec::new(), &sess)
.unwrap().unwrap();
let doc = first_attr_value_str_by_name(&item.attrs, "doc").unwrap();
assert_eq!(&doc[..], "/// doc comment");
let source = "/// doc comment\r\n/// line 2\r\nfn foo() {}".to_string();
let item = parse_item_from_source_str(name.clone(), source, Vec::new(), &sess)
.unwrap().unwrap();
2015-02-21 10:56:57 +00:00
let docs = item.attrs.iter().filter(|a| &*a.name() == "doc")
.map(|a| a.value_str().unwrap().to_string()).collect::<Vec<_>>();
DST coercions and DST structs [breaking-change] 1. The internal layout for traits has changed from (vtable, data) to (data, vtable). If you were relying on this in unsafe transmutes, you might get some very weird and apparently unrelated errors. You should not be doing this! Prefer not to do this at all, but if you must, you should use raw::TraitObject rather than hardcoding rustc's internal representation into your code. 2. The minimal type of reference-to-vec-literals (e.g., `&[1, 2, 3]`) is now a fixed size vec (e.g., `&[int, ..3]`) where it used to be an unsized vec (e.g., `&[int]`). If you want the unszied type, you must explicitly give the type (e.g., `let x: &[_] = &[1, 2, 3]`). Note in particular where multiple blocks must have the same type (e.g., if and else clauses, vec elements), the compiler will not coerce to the unsized type without a hint. E.g., `[&[1], &[1, 2]]` used to be a valid expression of type '[&[int]]'. It no longer type checks since the first element now has type `&[int, ..1]` and the second has type &[int, ..2]` which are incompatible. 3. The type of blocks (including functions) must be coercible to the expected type (used to be a subtype). Mostly this makes things more flexible and not less (in particular, in the case of coercing function bodies to the return type). However, in some rare cases, this is less flexible. TBH, I'm not exactly sure of the exact effects. I think the change causes us to resolve inferred type variables slightly earlier which might make us slightly more restrictive. Possibly it only affects blocks with unreachable code. E.g., `if ... { fail!(); "Hello" }` used to type check, it no longer does. The fix is to add a semicolon after the string.
2014-08-04 12:20:11 +00:00
let b: &[_] = &["/// doc comment".to_string(), "/// line 2".to_string()];
assert_eq!(&docs[..], b);
let source = "/** doc comment\r\n * with CRLF */\r\nfn foo() {}".to_string();
let item = parse_item_from_source_str(name, source, Vec::new(), &sess).unwrap().unwrap();
let doc = first_attr_value_str_by_name(&item.attrs, "doc").unwrap();
assert_eq!(&doc[..], "/** doc comment\n * with CRLF */");
}
2015-01-31 22:54:43 +00:00
#[test]
fn ttdelim_span() {
let sess = ParseSess::new();
2015-01-31 22:54:43 +00:00
let expr = parse::parse_expr_from_source_str("foo".to_string(),
"foo!( fn main() { body } )".to_string(), vec![], &sess).unwrap();
2015-01-31 22:54:43 +00:00
let tts = match expr.node {
ast::ExprKind::Mac(ref mac) => mac.node.tts.clone(),
2015-01-31 22:54:43 +00:00
_ => panic!("not a macro"),
};
let span = tts.iter().rev().next().unwrap().get_span();
match sess.codemap().span_to_snippet(span) {
Ok(s) => assert_eq!(&s[..], "{ body }"),
Err(_) => panic!("could not get snippet"),
2015-01-31 22:54:43 +00:00
}
}
2013-02-04 21:15:17 +00:00
}