Skip to content

Commit 1019177

Browse files
committed
lexer: show correct span on lexical errors
Previously, the lexer calling `rdr.fatal(...)` would report the span of the last complete token, instead of a span within the erroneous token (besides one span fixed in 1ac90bb). This commit adds a wrapper around `rdr.fatal(...)` that sets the span explicilty, so that all fatal errors in `libsyntax/parse/lexer.rs` now report the offending code more precisely. A number of tests try to verify that, though the `compile-fail` testing setup can only check that the spans are on the right lines, and the "unterminated string/block comment" errors can't have the line marked at all, so that's incomplete. Closes #9149.
1 parent d2b0b11 commit 1019177

13 files changed

+215
-19
lines changed

src/libsyntax/parse/lexer.rs

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,16 @@ impl reader for TtReader {
149149
fn dup(@mut self) -> @mut reader { dup_tt_reader(self) as @mut reader }
150150
}
151151

152+
// report a lexical error spanning [`from_pos`, `to_pos`)
153+
fn fatal_span(rdr: @mut StringReader,
154+
from_pos: BytePos,
155+
to_pos: BytePos,
156+
m: ~str)
157+
-> ! {
158+
rdr.peek_span = codemap::mk_sp(from_pos, to_pos);
159+
rdr.fatal(m);
160+
}
161+
152162
// EFFECT: advance peek_tok and peek_span to refer to the next token.
153163
// EFFECT: update the interner, maybe.
154164
fn string_advance_token(r: @mut StringReader) {
@@ -327,7 +337,8 @@ fn consume_block_comment(rdr: @mut StringReader)
327337
bump(rdr);
328338
}
329339
if is_eof(rdr) {
330-
rdr.fatal(~"unterminated block doc-comment");
340+
fatal_span(rdr, start_bpos, rdr.last_pos,
341+
~"unterminated block doc-comment");
331342
} else {
332343
bump(rdr);
333344
bump(rdr);
@@ -344,8 +355,12 @@ fn consume_block_comment(rdr: @mut StringReader)
344355
}
345356
}
346357
} else {
358+
let start_bpos = rdr.last_pos - BytePos(2u);
347359
loop {
348-
if is_eof(rdr) { rdr.fatal(~"unterminated block comment"); }
360+
if is_eof(rdr) {
361+
fatal_span(rdr, start_bpos, rdr.last_pos,
362+
~"unterminated block comment");
363+
}
349364
if rdr.curr == '*' && nextch(rdr) == '/' {
350365
bump(rdr);
351366
bump(rdr);
@@ -362,6 +377,7 @@ fn consume_block_comment(rdr: @mut StringReader)
362377
}
363378

364379
fn scan_exponent(rdr: @mut StringReader) -> Option<~str> {
380+
let start_bpos = rdr.last_pos;
365381
let mut c = rdr.curr;
366382
let mut rslt = ~"";
367383
if c == 'e' || c == 'E' {
@@ -375,7 +391,10 @@ fn scan_exponent(rdr: @mut StringReader) -> Option<~str> {
375391
let exponent = scan_digits(rdr, 10u);
376392
if exponent.len() > 0u {
377393
return Some(rslt + exponent);
378-
} else { rdr.fatal(~"scan_exponent: bad fp literal"); }
394+
} else {
395+
fatal_span(rdr, start_bpos, rdr.last_pos,
396+
~"scan_exponent: bad fp literal");
397+
}
379398
} else { return None::<~str>; }
380399
}
381400

@@ -399,6 +418,7 @@ fn scan_number(c: char, rdr: @mut StringReader) -> token::Token {
399418
let mut base = 10u;
400419
let mut c = c;
401420
let mut n = nextch(rdr);
421+
let start_bpos = rdr.last_pos;
402422
if c == '0' && n == 'x' {
403423
bump(rdr);
404424
bump(rdr);
@@ -442,11 +462,13 @@ fn scan_number(c: char, rdr: @mut StringReader) -> token::Token {
442462
else { either::Right(ast::ty_u64) };
443463
}
444464
if num_str.len() == 0u {
445-
rdr.fatal(~"no valid digits found for number");
465+
fatal_span(rdr, start_bpos, rdr.last_pos,
466+
~"no valid digits found for number");
446467
}
447468
let parsed = match from_str_radix::<u64>(num_str, base as uint) {
448469
Some(p) => p,
449-
None => rdr.fatal(~"int literal is too large")
470+
None => fatal_span(rdr, start_bpos, rdr.last_pos,
471+
~"int literal is too large")
450472
};
451473

452474
match tp {
@@ -464,8 +486,10 @@ fn scan_number(c: char, rdr: @mut StringReader) -> token::Token {
464486
}
465487
if is_float {
466488
match base {
467-
16u => rdr.fatal(~"hexadecimal float literal is not supported"),
468-
2u => rdr.fatal(~"binary float literal is not supported"),
489+
16u => fatal_span(rdr, start_bpos, rdr.last_pos,
490+
~"hexadecimal float literal is not supported"),
491+
2u => fatal_span(rdr, start_bpos, rdr.last_pos,
492+
~"binary float literal is not supported"),
469493
_ => ()
470494
}
471495
}
@@ -507,11 +531,13 @@ fn scan_number(c: char, rdr: @mut StringReader) -> token::Token {
507531
return token::LIT_FLOAT_UNSUFFIXED(str_to_ident(num_str));
508532
} else {
509533
if num_str.len() == 0u {
510-
rdr.fatal(~"no valid digits found for number");
534+
fatal_span(rdr, start_bpos, rdr.last_pos,
535+
~"no valid digits found for number");
511536
}
512537
let parsed = match from_str_radix::<u64>(num_str, base as uint) {
513538
Some(p) => p,
514-
None => rdr.fatal(~"int literal is too large")
539+
None => fatal_span(rdr, start_bpos, rdr.last_pos,
540+
~"int literal is too large")
515541
};
516542

517543
debug!("lexing %s as an unsuffixed integer literal",
@@ -523,19 +549,23 @@ fn scan_number(c: char, rdr: @mut StringReader) -> token::Token {
523549
fn scan_numeric_escape(rdr: @mut StringReader, n_hex_digits: uint) -> char {
524550
let mut accum_int = 0;
525551
let mut i = n_hex_digits;
552+
let start_bpos = rdr.last_pos;
526553
while i != 0u {
527554
let n = rdr.curr;
528-
bump(rdr);
529555
if !is_hex_digit(n) {
530-
rdr.fatal(fmt!("illegal numeric character escape: %d", n as int));
556+
fatal_span(rdr, rdr.last_pos, rdr.pos,
557+
fmt!("illegal numeric character escape: %d",
558+
n as int));
531559
}
560+
bump(rdr);
532561
accum_int *= 16;
533562
accum_int += hex_digit_val(n);
534563
i -= 1u;
535564
}
536565
match char::from_u32(accum_int as u32) {
537566
Some(x) => x,
538-
None => rdr.fatal(fmt!("illegal numeric character escape"))
567+
None => fatal_span(rdr, start_bpos, rdr.last_pos,
568+
fmt!("illegal numeric character escape"))
539569
}
540570
}
541571

@@ -691,6 +721,7 @@ fn next_token_inner(rdr: @mut StringReader) -> token::Token {
691721
if c2 == '\\' {
692722
// '\X' for some X must be a character constant:
693723
let escaped = rdr.curr;
724+
let escaped_pos = rdr.last_pos;
694725
bump(rdr);
695726
match escaped {
696727
'n' => { c2 = '\n'; }
@@ -704,12 +735,18 @@ fn next_token_inner(rdr: @mut StringReader) -> token::Token {
704735
'u' => { c2 = scan_numeric_escape(rdr, 4u); }
705736
'U' => { c2 = scan_numeric_escape(rdr, 8u); }
706737
c2 => {
707-
rdr.fatal(fmt!("unknown character escape: %d", c2 as int));
738+
fatal_span(rdr, escaped_pos, rdr.last_pos,
739+
fmt!("unknown character escape: %d", c2 as int));
708740
}
709741
}
710742
}
711743
if rdr.curr != '\'' {
712-
rdr.fatal(~"unterminated character constant");
744+
fatal_span(rdr,
745+
// Byte offsetting here is okay because the character
746+
// before position `start` is an ascii single quote.
747+
start - BytePos(1u),
748+
rdr.last_pos,
749+
~"unterminated character constant");
713750
}
714751
bump(rdr); // advance curr past token
715752
return token::LIT_CHAR(c2 as u32);
@@ -721,7 +758,9 @@ fn next_token_inner(rdr: @mut StringReader) -> token::Token {
721758
while rdr.curr != '"' {
722759
if is_eof(rdr) {
723760
do with_str_from(rdr, n) |s| {
724-
rdr.fatal(fmt!("unterminated double quote string: %s", s));
761+
fatal_span(rdr, n, rdr.last_pos,
762+
fmt!("unterminated double quote string: %s",
763+
s));
725764
}
726765
}
727766

@@ -730,6 +769,7 @@ fn next_token_inner(rdr: @mut StringReader) -> token::Token {
730769
match ch {
731770
'\\' => {
732771
let escaped = rdr.curr;
772+
let escaped_pos = rdr.last_pos;
733773
bump(rdr);
734774
match escaped {
735775
'n' => accum_str.push_char('\n'),
@@ -750,7 +790,8 @@ fn next_token_inner(rdr: @mut StringReader) -> token::Token {
750790
accum_str.push_char(scan_numeric_escape(rdr, 8u));
751791
}
752792
c2 => {
753-
rdr.fatal(fmt!("unknown string escape: %d", c2 as int));
793+
fatal_span(rdr, escaped_pos, rdr.last_pos,
794+
fmt!("unknown string escape: %d", c2 as int));
754795
}
755796
}
756797
}
@@ -786,11 +827,10 @@ fn next_token_inner(rdr: @mut StringReader) -> token::Token {
786827
'^' => { return binop(rdr, token::CARET); }
787828
'%' => { return binop(rdr, token::PERCENT); }
788829
c => {
789-
// So the error span points to the unrecognized character
790-
rdr.peek_span = codemap::mk_sp(rdr.last_pos, rdr.pos);
791830
let mut cs = ~"";
792831
char::escape_default(c, |c| cs.push_char(c));
793-
rdr.fatal(fmt!("unknown start of token: %s", cs));
832+
fatal_span(rdr, rdr.last_pos, rdr.pos,
833+
fmt!("unknown start of token: %s", cs));
794834
}
795835
}
796836
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2013 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.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
static f: float =
12+
1e+ //~ ERROR: scan_exponent: bad fp literal
13+
;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2013 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.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
static f: float =
12+
0x539.0 //~ ERROR: hexadecimal float literal is not supported
13+
;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2013 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.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
static c: char =
12+
'\Uffffffff' //~ ERROR: illegal numeric character escape
13+
;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2013 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.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
static c: char =
12+
'\u539_' //~ ERROR: illegal numeric character escape
13+
;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2013 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.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
static i: int =
12+
99999999999999999999999999999999u32 //~ ERROR: int literal is too large
13+
;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2013 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.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
static i: int =
12+
99999999999999999999999999999999 //~ ERROR: int literal is too large
13+
;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2013 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.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
static i: int =
12+
0xu32 //~ ERROR: no valid digits
13+
;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2013 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.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
static i: int =
12+
0x //~ ERROR: no valid digits
13+
;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2013 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.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
static c: char =
12+
'\●' //~ ERROR: unknown character escape
13+
;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2013 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.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn main() {
12+
//~ ERROR: unknown start of token
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2013 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.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
static s: &'static str =
12+
"\●" //~ ERROR: unknown string escape
13+
;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2013 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.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
static c: char =
12+
'//~ ERROR: unterminated character constant
13+
;

0 commit comments

Comments
 (0)