Make most lexer errors non-fatal
Most errors that arise in the lexer can be recovered from. This allows for more than one syntax error to be reported at a time.
This commit is contained in:
parent
c329a1fcdc
commit
4c9dace5d5
9 changed files with 148 additions and 211 deletions
|
@ -291,7 +291,7 @@ fn read_block_comment(rdr: &mut StringReader,
|
||||||
while level > 0 {
|
while level > 0 {
|
||||||
debug!("=== block comment level {}", level);
|
debug!("=== block comment level {}", level);
|
||||||
if is_eof(rdr) {
|
if is_eof(rdr) {
|
||||||
rdr.fatal("unterminated block comment".to_strbuf());
|
rdr.fatal("unterminated block comment");
|
||||||
}
|
}
|
||||||
if rdr.curr_is('\n') {
|
if rdr.curr_is('\n') {
|
||||||
trim_whitespace_prefix_and_push_line(&mut lines,
|
trim_whitespace_prefix_and_push_line(&mut lines,
|
||||||
|
|
|
@ -21,15 +21,16 @@ use std::mem::replace;
|
||||||
use std::num::from_str_radix;
|
use std::num::from_str_radix;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::strbuf::StrBuf;
|
|
||||||
|
|
||||||
pub use ext::tt::transcribe::{TtReader, new_tt_reader};
|
pub use ext::tt::transcribe::{TtReader, new_tt_reader};
|
||||||
|
|
||||||
pub trait Reader {
|
pub trait Reader {
|
||||||
fn is_eof(&self) -> bool;
|
fn is_eof(&self) -> bool;
|
||||||
fn next_token(&mut self) -> TokenAndSpan;
|
fn next_token(&mut self) -> TokenAndSpan;
|
||||||
fn fatal(&self, StrBuf) -> !;
|
/// Report a fatal error with the current span.
|
||||||
fn span_diag<'a>(&'a self) -> &'a SpanHandler;
|
fn fatal(&self, &str) -> !;
|
||||||
|
/// Report a non-fatal error with the current span.
|
||||||
|
fn err(&self, &str);
|
||||||
fn peek(&self) -> TokenAndSpan;
|
fn peek(&self) -> TokenAndSpan;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,15 +102,17 @@ impl<'a> Reader for StringReader<'a> {
|
||||||
string_advance_token(self);
|
string_advance_token(self);
|
||||||
ret_val
|
ret_val
|
||||||
}
|
}
|
||||||
fn fatal(&self, m: StrBuf) -> ! {
|
fn fatal(&self, m: &str) -> ! {
|
||||||
self.span_diagnostic.span_fatal(self.peek_span, m.as_slice())
|
self.span_diagnostic.span_fatal(self.peek_span, m)
|
||||||
|
}
|
||||||
|
fn err(&self, m: &str) {
|
||||||
|
self.span_diagnostic.span_err(self.peek_span, m)
|
||||||
}
|
}
|
||||||
fn span_diag<'a>(&'a self) -> &'a SpanHandler { self.span_diagnostic }
|
|
||||||
fn peek(&self) -> TokenAndSpan {
|
fn peek(&self) -> TokenAndSpan {
|
||||||
// FIXME(pcwalton): Bad copy!
|
// FIXME(pcwalton): Bad copy!
|
||||||
TokenAndSpan {
|
TokenAndSpan {
|
||||||
tok: self.peek_tok.clone(),
|
tok: self.peek_tok.clone(),
|
||||||
sp: self.peek_span.clone(),
|
sp: self.peek_span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,55 +126,58 @@ impl<'a> Reader for TtReader<'a> {
|
||||||
debug!("TtReader: r={:?}", r);
|
debug!("TtReader: r={:?}", r);
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
fn fatal(&self, m: StrBuf) -> ! {
|
fn fatal(&self, m: &str) -> ! {
|
||||||
self.sp_diag.span_fatal(self.cur_span, m.as_slice());
|
self.sp_diag.span_fatal(self.cur_span, m);
|
||||||
|
}
|
||||||
|
fn err(&self, m: &str) {
|
||||||
|
self.sp_diag.span_err(self.cur_span, m);
|
||||||
}
|
}
|
||||||
fn span_diag<'a>(&'a self) -> &'a SpanHandler { self.sp_diag }
|
|
||||||
fn peek(&self) -> TokenAndSpan {
|
fn peek(&self) -> TokenAndSpan {
|
||||||
TokenAndSpan {
|
TokenAndSpan {
|
||||||
tok: self.cur_tok.clone(),
|
tok: self.cur_tok.clone(),
|
||||||
sp: self.cur_span.clone(),
|
sp: self.cur_span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// report a lexical error spanning [`from_pos`, `to_pos`)
|
// report a lexical error spanning [`from_pos`, `to_pos`)
|
||||||
fn fatal_span(rdr: &mut StringReader,
|
fn fatal_span(rdr: &mut StringReader, from_pos: BytePos, to_pos: BytePos, m: &str) -> ! {
|
||||||
from_pos: BytePos,
|
|
||||||
to_pos: BytePos,
|
|
||||||
m: StrBuf)
|
|
||||||
-> ! {
|
|
||||||
rdr.peek_span = codemap::mk_sp(from_pos, to_pos);
|
rdr.peek_span = codemap::mk_sp(from_pos, to_pos);
|
||||||
rdr.fatal(m);
|
rdr.fatal(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn err_span(rdr: &mut StringReader, from_pos: BytePos, to_pos: BytePos, m: &str) {
|
||||||
|
rdr.peek_span = codemap::mk_sp(from_pos, to_pos);
|
||||||
|
rdr.err(m);
|
||||||
|
}
|
||||||
|
|
||||||
// report a lexical error spanning [`from_pos`, `to_pos`), appending an
|
// report a lexical error spanning [`from_pos`, `to_pos`), appending an
|
||||||
// escaped character to the error message
|
// escaped character to the error message
|
||||||
fn fatal_span_char(rdr: &mut StringReader,
|
fn fatal_span_char(rdr: &mut StringReader,
|
||||||
from_pos: BytePos,
|
from_pos: BytePos, to_pos: BytePos,
|
||||||
to_pos: BytePos,
|
m: &str, c: char) -> ! {
|
||||||
m: StrBuf,
|
let mut m = m.to_strbuf();
|
||||||
c: char)
|
|
||||||
-> ! {
|
|
||||||
let mut m = m;
|
|
||||||
m.push_str(": ");
|
m.push_str(": ");
|
||||||
char::escape_default(c, |c| m.push_char(c));
|
char::escape_default(c, |c| m.push_char(c));
|
||||||
fatal_span(rdr, from_pos, to_pos, m.into_strbuf());
|
fatal_span(rdr, from_pos, to_pos, m.as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn err_span_char(rdr: &mut StringReader, from_pos: BytePos, to_pos: BytePos, m: &str, c: char) {
|
||||||
|
let mut m = m.to_strbuf();
|
||||||
|
m.push_str(": ");
|
||||||
|
char::escape_default(c, |c| m.push_char(c));
|
||||||
|
err_span(rdr, from_pos, to_pos, m.as_slice());
|
||||||
}
|
}
|
||||||
|
|
||||||
// report a lexical error spanning [`from_pos`, `to_pos`), appending the
|
// report a lexical error spanning [`from_pos`, `to_pos`), appending the
|
||||||
// offending string to the error message
|
// offending string to the error message
|
||||||
fn fatal_span_verbose(rdr: &mut StringReader,
|
fn fatal_span_verbose(rdr: &mut StringReader, from_pos: BytePos, to_pos: BytePos, m: &str) -> ! {
|
||||||
from_pos: BytePos,
|
let mut m = m.to_strbuf();
|
||||||
to_pos: BytePos,
|
|
||||||
m: StrBuf)
|
|
||||||
-> ! {
|
|
||||||
let mut m = m;
|
|
||||||
m.push_str(": ");
|
m.push_str(": ");
|
||||||
let from = byte_offset(rdr, from_pos).to_uint();
|
let from = byte_offset(rdr, from_pos).to_uint();
|
||||||
let to = byte_offset(rdr, to_pos).to_uint();
|
let to = byte_offset(rdr, to_pos).to_uint();
|
||||||
m.push_str(rdr.filemap.src.as_slice().slice(from, to));
|
m.push_str(rdr.filemap.src.as_slice().slice(from, to));
|
||||||
fatal_span(rdr, from_pos, to_pos, m);
|
fatal_span(rdr, from_pos, to_pos, m.as_slice());
|
||||||
}
|
}
|
||||||
|
|
||||||
// EFFECT: advance peek_tok and peek_span to refer to the next token.
|
// EFFECT: advance peek_tok and peek_span to refer to the next token.
|
||||||
|
@ -283,15 +289,6 @@ pub fn nextnextch_is(rdr: &StringReader, c: char) -> bool {
|
||||||
nextnextch(rdr) == Some(c)
|
nextnextch(rdr) == Some(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hex_digit_val(c: Option<char>) -> int {
|
|
||||||
let d = c.unwrap_or('\x00');
|
|
||||||
|
|
||||||
if in_range(c, '0', '9') { return (d as int) - ('0' as int); }
|
|
||||||
if in_range(c, 'a', 'f') { return (d as int) - ('a' as int) + 10; }
|
|
||||||
if in_range(c, 'A', 'F') { return (d as int) - ('A' as int) + 10; }
|
|
||||||
fail!();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_whitespace(c: Option<char>) -> bool {
|
pub fn is_whitespace(c: Option<char>) -> bool {
|
||||||
match c.unwrap_or('\x00') { // None can be null for now... it's not whitespace
|
match c.unwrap_or('\x00') { // None can be null for now... it's not whitespace
|
||||||
' ' | '\n' | '\t' | '\r' => true,
|
' ' | '\n' | '\t' | '\r' => true,
|
||||||
|
@ -299,20 +296,6 @@ pub fn is_whitespace(c: Option<char>) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn in_range(c: Option<char>, lo: char, hi: char) -> bool {
|
|
||||||
match c {
|
|
||||||
Some(c) => lo <= c && c <= hi,
|
|
||||||
_ => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_dec_digit(c: Option<char>) -> bool { return in_range(c, '0', '9'); }
|
|
||||||
|
|
||||||
fn is_hex_digit(c: Option<char>) -> bool {
|
|
||||||
return in_range(c, '0', '9') || in_range(c, 'a', 'f') ||
|
|
||||||
in_range(c, 'A', 'F');
|
|
||||||
}
|
|
||||||
|
|
||||||
// EFFECT: eats whitespace and comments.
|
// EFFECT: eats whitespace and comments.
|
||||||
// returns a Some(sugared-doc-attr) if one exists, None otherwise.
|
// returns a Some(sugared-doc-attr) if one exists, None otherwise.
|
||||||
fn consume_whitespace_and_comments(rdr: &mut StringReader)
|
fn consume_whitespace_and_comments(rdr: &mut StringReader)
|
||||||
|
@ -403,9 +386,9 @@ fn consume_block_comment(rdr: &mut StringReader) -> Option<TokenAndSpan> {
|
||||||
while level > 0 {
|
while level > 0 {
|
||||||
if is_eof(rdr) {
|
if is_eof(rdr) {
|
||||||
let msg = if is_doc_comment {
|
let msg = if is_doc_comment {
|
||||||
"unterminated block doc-comment".to_strbuf()
|
"unterminated block doc-comment"
|
||||||
} else {
|
} else {
|
||||||
"unterminated block comment".to_strbuf()
|
"unterminated block comment"
|
||||||
};
|
};
|
||||||
fatal_span(rdr, start_bpos, rdr.last_pos, msg);
|
fatal_span(rdr, start_bpos, rdr.last_pos, msg);
|
||||||
} else if rdr.curr_is('/') && nextch_is(rdr, '*') {
|
} else if rdr.curr_is('/') && nextch_is(rdr, '*') {
|
||||||
|
@ -456,13 +439,13 @@ fn scan_exponent(rdr: &mut StringReader, start_bpos: BytePos) -> Option<StrBuf>
|
||||||
let exponent = scan_digits(rdr, 10u);
|
let exponent = scan_digits(rdr, 10u);
|
||||||
if exponent.len() > 0u {
|
if exponent.len() > 0u {
|
||||||
rslt.push_str(exponent.as_slice());
|
rslt.push_str(exponent.as_slice());
|
||||||
return Some(rslt);
|
|
||||||
} else {
|
} else {
|
||||||
fatal_span(rdr, start_bpos, rdr.last_pos,
|
err_span(rdr, start_bpos, rdr.last_pos, "scan_exponent: bad fp literal");
|
||||||
"scan_exponent: bad fp literal".to_strbuf());
|
rslt.push_str("1"); // arbitrary placeholder exponent
|
||||||
}
|
}
|
||||||
|
Some(rslt)
|
||||||
} else {
|
} else {
|
||||||
return None::<StrBuf>;
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,14 +467,9 @@ fn scan_digits(rdr: &mut StringReader, radix: uint) -> StrBuf {
|
||||||
fn check_float_base(rdr: &mut StringReader, start_bpos: BytePos, last_bpos: BytePos,
|
fn check_float_base(rdr: &mut StringReader, start_bpos: BytePos, last_bpos: BytePos,
|
||||||
base: uint) {
|
base: uint) {
|
||||||
match base {
|
match base {
|
||||||
16u => {
|
16u => err_span(rdr, start_bpos, last_bpos, "hexadecimal float literal is not supported"),
|
||||||
fatal_span(rdr, start_bpos, last_bpos,
|
8u => err_span(rdr, start_bpos, last_bpos, "octal float literal is not supported"),
|
||||||
"hexadecimal float literal is not supported".to_strbuf())
|
2u => err_span(rdr, start_bpos, last_bpos, "binary float literal is not supported"),
|
||||||
}
|
|
||||||
8u => fatal_span(rdr, start_bpos, last_bpos,
|
|
||||||
"octal float literal is not supported".to_strbuf()),
|
|
||||||
2u => fatal_span(rdr, start_bpos, last_bpos,
|
|
||||||
"binary float literal is not supported".to_strbuf()),
|
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -517,7 +495,6 @@ fn scan_number(c: char, rdr: &mut StringReader) -> token::Token {
|
||||||
}
|
}
|
||||||
num_str = scan_digits(rdr, base);
|
num_str = scan_digits(rdr, base);
|
||||||
c = rdr.curr.unwrap_or('\x00');
|
c = rdr.curr.unwrap_or('\x00');
|
||||||
nextch(rdr);
|
|
||||||
if c == 'u' || c == 'i' {
|
if c == 'u' || c == 'i' {
|
||||||
enum Result { Signed(ast::IntTy), Unsigned(ast::UintTy) }
|
enum Result { Signed(ast::IntTy), Unsigned(ast::UintTy) }
|
||||||
let signed = c == 'i';
|
let signed = c == 'i';
|
||||||
|
@ -550,14 +527,16 @@ fn scan_number(c: char, rdr: &mut StringReader) -> token::Token {
|
||||||
else { Unsigned(ast::TyU64) };
|
else { Unsigned(ast::TyU64) };
|
||||||
}
|
}
|
||||||
if num_str.len() == 0u {
|
if num_str.len() == 0u {
|
||||||
fatal_span(rdr, start_bpos, rdr.last_pos,
|
err_span(rdr, start_bpos, rdr.last_pos, "no valid digits found for number");
|
||||||
"no valid digits found for number".to_strbuf());
|
num_str = "1".to_strbuf();
|
||||||
}
|
}
|
||||||
let parsed = match from_str_radix::<u64>(num_str.as_slice(),
|
let parsed = match from_str_radix::<u64>(num_str.as_slice(),
|
||||||
base as uint) {
|
base as uint) {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => fatal_span(rdr, start_bpos, rdr.last_pos,
|
None => {
|
||||||
"int literal is too large".to_strbuf())
|
err_span(rdr, start_bpos, rdr.last_pos, "int literal is too large");
|
||||||
|
1
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match tp {
|
match tp {
|
||||||
|
@ -607,22 +586,23 @@ fn scan_number(c: char, rdr: &mut StringReader) -> token::Token {
|
||||||
check_float_base(rdr, start_bpos, rdr.last_pos, base);
|
check_float_base(rdr, start_bpos, rdr.last_pos, base);
|
||||||
return token::LIT_FLOAT(str_to_ident(num_str.as_slice()), ast::TyF128);
|
return token::LIT_FLOAT(str_to_ident(num_str.as_slice()), ast::TyF128);
|
||||||
}
|
}
|
||||||
fatal_span(rdr, start_bpos, rdr.last_pos,
|
err_span(rdr, start_bpos, rdr.last_pos, "expected `f32`, `f64` or `f128` suffix");
|
||||||
"expected `f32`, `f64` or `f128` suffix".to_strbuf());
|
|
||||||
}
|
}
|
||||||
if is_float {
|
if is_float {
|
||||||
check_float_base(rdr, start_bpos, rdr.last_pos, base);
|
check_float_base(rdr, start_bpos, rdr.last_pos, base);
|
||||||
return token::LIT_FLOAT_UNSUFFIXED(str_to_ident(num_str.as_slice()));
|
return token::LIT_FLOAT_UNSUFFIXED(str_to_ident(num_str.as_slice()));
|
||||||
} else {
|
} else {
|
||||||
if num_str.len() == 0u {
|
if num_str.len() == 0u {
|
||||||
fatal_span(rdr, start_bpos, rdr.last_pos,
|
err_span(rdr, start_bpos, rdr.last_pos, "no valid digits found for number");
|
||||||
"no valid digits found for number".to_strbuf());
|
num_str = "1".to_strbuf();
|
||||||
}
|
}
|
||||||
let parsed = match from_str_radix::<u64>(num_str.as_slice(),
|
let parsed = match from_str_radix::<u64>(num_str.as_slice(),
|
||||||
base as uint) {
|
base as uint) {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => fatal_span(rdr, start_bpos, rdr.last_pos,
|
None => {
|
||||||
"int literal is too large".to_strbuf())
|
err_span(rdr, start_bpos, rdr.last_pos, "int literal is too large");
|
||||||
|
1
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("lexing {} as an unsuffixed integer literal",
|
debug!("lexing {} as an unsuffixed integer literal",
|
||||||
|
@ -631,34 +611,33 @@ fn scan_number(c: char, rdr: &mut StringReader) -> token::Token {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scan_numeric_escape(rdr: &mut StringReader, n_hex_digits: uint) -> char {
|
fn scan_numeric_escape(rdr: &mut StringReader, n_hex_digits: uint, delim: char) -> char {
|
||||||
let mut accum_int = 0;
|
let mut accum_int = 0u32;
|
||||||
let mut i = n_hex_digits;
|
|
||||||
let start_bpos = rdr.last_pos;
|
let start_bpos = rdr.last_pos;
|
||||||
while i != 0u && !is_eof(rdr) {
|
for _ in range(0, n_hex_digits) {
|
||||||
let n = rdr.curr;
|
if is_eof(rdr) {
|
||||||
if !is_hex_digit(n) {
|
fatal_span(rdr, start_bpos, rdr.last_pos, "unterminated numeric character escape");
|
||||||
fatal_span_char(
|
|
||||||
rdr,
|
|
||||||
rdr.last_pos,
|
|
||||||
rdr.pos,
|
|
||||||
"illegal character in numeric character escape".to_strbuf(),
|
|
||||||
n.unwrap());
|
|
||||||
}
|
}
|
||||||
bump(rdr);
|
if rdr.curr_is(delim) {
|
||||||
|
err_span(rdr, start_bpos, rdr.last_pos, "numeric character escape is too short");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let c = rdr.curr.unwrap_or('\x00');
|
||||||
accum_int *= 16;
|
accum_int *= 16;
|
||||||
accum_int += hex_digit_val(n);
|
accum_int += c.to_digit(16).unwrap_or_else(|| {
|
||||||
i -= 1u;
|
err_span_char(rdr, rdr.last_pos, rdr.pos,
|
||||||
}
|
"illegal character in numeric character escape", c);
|
||||||
if i != 0 && is_eof(rdr) {
|
0
|
||||||
fatal_span(rdr, start_bpos, rdr.last_pos,
|
}) as u32;
|
||||||
"unterminated numeric character escape".to_strbuf());
|
bump(rdr);
|
||||||
}
|
}
|
||||||
|
|
||||||
match char::from_u32(accum_int as u32) {
|
match char::from_u32(accum_int) {
|
||||||
Some(x) => x,
|
Some(x) => x,
|
||||||
None => fatal_span(rdr, start_bpos, rdr.last_pos,
|
None => {
|
||||||
"illegal numeric character escape".to_strbuf())
|
err_span(rdr, start_bpos, rdr.last_pos, "illegal numeric character escape");
|
||||||
|
'?'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -706,7 +685,7 @@ fn next_token_inner(rdr: &mut StringReader) -> token::Token {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if is_dec_digit(c) {
|
if c.map_or(false, |c| c.is_digit_radix(10)) {
|
||||||
return scan_number(c.unwrap(), rdr);
|
return scan_number(c.unwrap(), rdr);
|
||||||
}
|
}
|
||||||
fn binop(rdr: &mut StringReader, op: token::BinOp) -> token::Token {
|
fn binop(rdr: &mut StringReader, op: token::BinOp) -> token::Token {
|
||||||
|
@ -823,16 +802,13 @@ fn next_token_inner(rdr: &mut StringReader) -> token::Token {
|
||||||
let tok = &token::IDENT(ident, false);
|
let tok = &token::IDENT(ident, false);
|
||||||
|
|
||||||
if token::is_keyword(token::keywords::Self, tok) {
|
if token::is_keyword(token::keywords::Self, tok) {
|
||||||
fatal_span(rdr, start, rdr.last_pos,
|
err_span(rdr, start, rdr.last_pos,
|
||||||
"invalid lifetime name: 'self \
|
"invalid lifetime name: 'self is no longer a special lifetime");
|
||||||
is no longer a special lifetime".to_strbuf());
|
|
||||||
} else if token::is_any_keyword(tok) &&
|
} else if token::is_any_keyword(tok) &&
|
||||||
!token::is_keyword(token::keywords::Static, tok) {
|
!token::is_keyword(token::keywords::Static, tok) {
|
||||||
fatal_span(rdr, start, rdr.last_pos,
|
err_span(rdr, start, rdr.last_pos, "invalid lifetime name");
|
||||||
"invalid lifetime name".to_strbuf());
|
|
||||||
} else {
|
|
||||||
return token::LIFETIME(ident);
|
|
||||||
}
|
}
|
||||||
|
return token::LIFETIME(ident);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise it is a character constant:
|
// Otherwise it is a character constant:
|
||||||
|
@ -853,28 +829,20 @@ fn next_token_inner(rdr: &mut StringReader) -> token::Token {
|
||||||
'\'' => '\'',
|
'\'' => '\'',
|
||||||
'"' => '"',
|
'"' => '"',
|
||||||
'0' => '\x00',
|
'0' => '\x00',
|
||||||
'x' => scan_numeric_escape(rdr, 2u),
|
'x' => scan_numeric_escape(rdr, 2u, '\''),
|
||||||
'u' => scan_numeric_escape(rdr, 4u),
|
'u' => scan_numeric_escape(rdr, 4u, '\''),
|
||||||
'U' => scan_numeric_escape(rdr, 8u),
|
'U' => scan_numeric_escape(rdr, 8u, '\''),
|
||||||
c2 => {
|
c2 => {
|
||||||
fatal_span_char(rdr,
|
err_span_char(rdr, escaped_pos, rdr.last_pos,
|
||||||
escaped_pos,
|
"unknown character escape", c2);
|
||||||
rdr.last_pos,
|
c2
|
||||||
"unknown character \
|
|
||||||
escape".to_strbuf(),
|
|
||||||
c2)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'\t' | '\n' | '\r' | '\'' => {
|
'\t' | '\n' | '\r' | '\'' => {
|
||||||
fatal_span_char(
|
err_span_char(rdr, start, rdr.last_pos, "character constant must be escaped", c2);
|
||||||
rdr,
|
|
||||||
start,
|
|
||||||
rdr.last_pos,
|
|
||||||
"character constant must be escaped".to_strbuf(),
|
|
||||||
c2);
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -883,9 +851,8 @@ fn next_token_inner(rdr: &mut StringReader) -> token::Token {
|
||||||
// Byte offsetting here is okay because the
|
// Byte offsetting here is okay because the
|
||||||
// character before position `start` is an
|
// character before position `start` is an
|
||||||
// ascii single quote.
|
// ascii single quote.
|
||||||
start - BytePos(1),
|
start - BytePos(1), rdr.last_pos,
|
||||||
rdr.last_pos,
|
"unterminated character constant");
|
||||||
"unterminated character constant".to_strbuf());
|
|
||||||
}
|
}
|
||||||
bump(rdr); // advance curr past token
|
bump(rdr); // advance curr past token
|
||||||
return token::LIT_CHAR(c2);
|
return token::LIT_CHAR(c2);
|
||||||
|
@ -896,8 +863,7 @@ fn next_token_inner(rdr: &mut StringReader) -> token::Token {
|
||||||
bump(rdr);
|
bump(rdr);
|
||||||
while !rdr.curr_is('"') {
|
while !rdr.curr_is('"') {
|
||||||
if is_eof(rdr) {
|
if is_eof(rdr) {
|
||||||
fatal_span(rdr, start_bpos, rdr.last_pos,
|
fatal_span(rdr, start_bpos, rdr.last_pos, "unterminated double quote string");
|
||||||
"unterminated double quote string".to_strbuf());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let ch = rdr.curr.unwrap();
|
let ch = rdr.curr.unwrap();
|
||||||
|
@ -905,8 +871,7 @@ fn next_token_inner(rdr: &mut StringReader) -> token::Token {
|
||||||
match ch {
|
match ch {
|
||||||
'\\' => {
|
'\\' => {
|
||||||
if is_eof(rdr) {
|
if is_eof(rdr) {
|
||||||
fatal_span(rdr, start_bpos, rdr.last_pos,
|
fatal_span(rdr, start_bpos, rdr.last_pos, "unterminated double quote string");
|
||||||
"unterminated double quote string".to_strbuf());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let escaped = rdr.curr.unwrap();
|
let escaped = rdr.curr.unwrap();
|
||||||
|
@ -922,17 +887,16 @@ fn next_token_inner(rdr: &mut StringReader) -> token::Token {
|
||||||
'\n' => consume_whitespace(rdr),
|
'\n' => consume_whitespace(rdr),
|
||||||
'0' => accum_str.push_char('\x00'),
|
'0' => accum_str.push_char('\x00'),
|
||||||
'x' => {
|
'x' => {
|
||||||
accum_str.push_char(scan_numeric_escape(rdr, 2u));
|
accum_str.push_char(scan_numeric_escape(rdr, 2u, '"'));
|
||||||
}
|
}
|
||||||
'u' => {
|
'u' => {
|
||||||
accum_str.push_char(scan_numeric_escape(rdr, 4u));
|
accum_str.push_char(scan_numeric_escape(rdr, 4u, '"'));
|
||||||
}
|
}
|
||||||
'U' => {
|
'U' => {
|
||||||
accum_str.push_char(scan_numeric_escape(rdr, 8u));
|
accum_str.push_char(scan_numeric_escape(rdr, 8u, '"'));
|
||||||
}
|
}
|
||||||
c2 => {
|
c2 => {
|
||||||
fatal_span_char(rdr, escaped_pos, rdr.last_pos,
|
err_span_char(rdr, escaped_pos, rdr.last_pos, "unknown string escape", c2);
|
||||||
"unknown string escape".to_strbuf(), c2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -952,12 +916,11 @@ fn next_token_inner(rdr: &mut StringReader) -> token::Token {
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_eof(rdr) {
|
if is_eof(rdr) {
|
||||||
fatal_span(rdr, start_bpos, rdr.last_pos,
|
fatal_span(rdr, start_bpos, rdr.last_pos, "unterminated raw string");
|
||||||
"unterminated raw string".to_strbuf());
|
|
||||||
} else if !rdr.curr_is('"') {
|
} else if !rdr.curr_is('"') {
|
||||||
fatal_span_char(rdr, start_bpos, rdr.last_pos,
|
fatal_span_char(rdr, start_bpos, rdr.last_pos,
|
||||||
"only `#` is allowed in raw string delimitation; \
|
"only `#` is allowed in raw string delimitation; \
|
||||||
found illegal character".to_strbuf(),
|
found illegal character",
|
||||||
rdr.curr.unwrap());
|
rdr.curr.unwrap());
|
||||||
}
|
}
|
||||||
bump(rdr);
|
bump(rdr);
|
||||||
|
@ -965,8 +928,7 @@ fn next_token_inner(rdr: &mut StringReader) -> token::Token {
|
||||||
let mut content_end_bpos;
|
let mut content_end_bpos;
|
||||||
'outer: loop {
|
'outer: loop {
|
||||||
if is_eof(rdr) {
|
if is_eof(rdr) {
|
||||||
fatal_span(rdr, start_bpos, rdr.last_pos,
|
fatal_span(rdr, start_bpos, rdr.last_pos, "unterminated raw string");
|
||||||
"unterminated raw string".to_strbuf());
|
|
||||||
}
|
}
|
||||||
if rdr.curr_is('"') {
|
if rdr.curr_is('"') {
|
||||||
content_end_bpos = rdr.last_pos;
|
content_end_bpos = rdr.last_pos;
|
||||||
|
@ -1013,8 +975,7 @@ fn next_token_inner(rdr: &mut StringReader) -> token::Token {
|
||||||
'^' => { return binop(rdr, token::CARET); }
|
'^' => { return binop(rdr, token::CARET); }
|
||||||
'%' => { return binop(rdr, token::PERCENT); }
|
'%' => { return binop(rdr, token::PERCENT); }
|
||||||
c => {
|
c => {
|
||||||
fatal_span_char(rdr, rdr.last_pos, rdr.pos,
|
fatal_span_char(rdr, rdr.last_pos, rdr.pos, "unknown start of token", c);
|
||||||
"unknown start of token".to_strbuf(), c);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,22 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
// ignore-tidy-cr
|
||||||
|
// ignore-tidy-tab
|
||||||
fn main() {
|
fn main() {
|
||||||
// these literals are just silly.
|
// these literals are just silly.
|
||||||
''';
|
''';
|
||||||
//~^ ERROR: character constant must be escaped
|
//~^ ERROR: character constant must be escaped: \'
|
||||||
|
|
||||||
|
// note that this is a literal "\n" byte
|
||||||
|
'
|
||||||
|
';
|
||||||
|
//~^^ ERROR: character constant must be escaped: \n
|
||||||
|
|
||||||
|
// note that this is a literal "\r" byte
|
||||||
|
'
'; //~ ERROR: character constant must be escaped: \r
|
||||||
|
|
||||||
|
// note that this is a literal tab character here
|
||||||
|
' ';
|
||||||
|
//~^ ERROR: character constant must be escaped: \t
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
// Copyright 2013 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.
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// note that this is a literal "\n" byte
|
|
||||||
'
|
|
||||||
';
|
|
||||||
//~^^ ERROR: character constant must be escaped
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
// Copyright 2013-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.
|
|
||||||
|
|
||||||
// ignore-tidy-cr
|
|
||||||
fn main() {
|
|
||||||
// note that this is a literal "\r" byte
|
|
||||||
'
|
|
||||||
'; //~^ ERROR: character constant must be escaped
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
// Copyright 2013-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.
|
|
||||||
|
|
||||||
// ignore-tidy-tab
|
|
||||||
fn main() {
|
|
||||||
// note that this is a literal tab character here
|
|
||||||
' ';
|
|
||||||
//~^ ERROR: character constant must be escaped
|
|
||||||
}
|
|
|
@ -9,16 +9,17 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
|
||||||
trait Serializable<'self, T> { //~ ERROR: no longer a special lifetime
|
trait Serializable<'self, T> { //~ ERROR no longer a special lifetime
|
||||||
fn serialize(val : &'self T) -> Vec<u8> ;
|
fn serialize(val : &'self T) -> Vec<u8> ; //~ ERROR no longer a special lifetime
|
||||||
fn deserialize(repr : &[u8]) -> &'self T;
|
fn deserialize(repr : &[u8]) -> &'self T; //~ ERROR no longer a special lifetime
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'self> Serializable<str> for &'self str {
|
impl<'self> Serializable<str> for &'self str { //~ ERROR no longer a special lifetime
|
||||||
fn serialize(val : &'self str) -> Vec<u8> {
|
//~^ ERROR no longer a special lifetime
|
||||||
|
fn serialize(val : &'self str) -> Vec<u8> { //~ ERROR no longer a special lifetime
|
||||||
vec!(1)
|
vec!(1)
|
||||||
}
|
}
|
||||||
fn deserialize(repr: &[u8]) -> &'self str {
|
fn deserialize(repr: &[u8]) -> &'self str { //~ ERROR no longer a special lifetime
|
||||||
"hi"
|
"hi"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
// Copyright 2013 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.
|
|
||||||
|
|
||||||
static c: char =
|
|
||||||
'\Uffffffff' //~ ERROR: illegal numeric character escape
|
|
||||||
;
|
|
|
@ -11,3 +11,25 @@
|
||||||
static c: char =
|
static c: char =
|
||||||
'\u539_' //~ ERROR: illegal character in numeric character escape
|
'\u539_' //~ ERROR: illegal character in numeric character escape
|
||||||
;
|
;
|
||||||
|
|
||||||
|
static c2: char =
|
||||||
|
'\Uffffffff' //~ ERROR: illegal numeric character escape
|
||||||
|
;
|
||||||
|
|
||||||
|
static c3: char =
|
||||||
|
'\x1' //~ ERROR: numeric character escape is too short
|
||||||
|
;
|
||||||
|
|
||||||
|
static c4: char =
|
||||||
|
'\u23q' //~ ERROR: illegal character in numeric character escape
|
||||||
|
;
|
||||||
|
//~^^ ERROR: numeric character escape is too short
|
||||||
|
|
||||||
|
static s: &'static str =
|
||||||
|
"\x1" //~ ERROR: numeric character escape is too short
|
||||||
|
;
|
||||||
|
|
||||||
|
static s2: &'static str =
|
||||||
|
"\u23q" //~ ERROR: illegal character in numeric character escape
|
||||||
|
;
|
||||||
|
//~^^ ERROR: numeric character escape is too short
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue