Skip to content

Commit 74ae05a

Browse files
committed
syntax: Handle \r\n in byte string literals
This ended up passing through the lexer but dying later on in parsing when it wasn't handled. The strategy taken was to copy the `str_lit` funciton, but adapt it for bytes. Closes #16278
1 parent 6da3889 commit 74ae05a

File tree

3 files changed

+59
-18
lines changed

3 files changed

+59
-18
lines changed

src/libsyntax/parse/mod.rs

+38-18
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use std::gc::Gc;
2121
use std::io::File;
2222
use std::rc::Rc;
2323
use std::str;
24+
use std::iter;
2425

2526
pub mod lexer;
2627
pub mod parser;
@@ -327,7 +328,7 @@ pub fn str_lit(lit: &str) -> String {
327328
let error = |i| format!("lexer should have rejected {} at {}", lit, i);
328329

329330
/// Eat everything up to a non-whitespace
330-
fn eat<'a>(it: &mut ::std::iter::Peekable<(uint, char), ::std::str::CharOffsets<'a>>) {
331+
fn eat<'a>(it: &mut iter::Peekable<(uint, char), str::CharOffsets<'a>>) {
331332
loop {
332333
match it.peek().map(|x| x.val1()) {
333334
Some(' ') | Some('\n') | Some('\r') | Some('\t') => {
@@ -471,35 +472,54 @@ pub fn binary_lit(lit: &str) -> Rc<Vec<u8>> {
471472
// FIXME #8372: This could be a for-loop if it didn't borrow the iterator
472473
let error = |i| format!("lexer should have rejected {} at {}", lit, i);
473474

475+
/// Eat everything up to a non-whitespace
476+
fn eat<'a, I: Iterator<(uint, u8)>>(it: &mut iter::Peekable<(uint, u8), I>) {
477+
loop {
478+
match it.peek().map(|x| x.val1()) {
479+
Some(b' ') | Some(b'\n') | Some(b'\r') | Some(b'\t') => {
480+
it.next();
481+
},
482+
_ => { break; }
483+
}
484+
}
485+
}
486+
474487
// binary literals *must* be ASCII, but the escapes don't have to be
475-
let mut chars = lit.as_bytes().iter().enumerate().peekable();
488+
let mut chars = lit.bytes().enumerate().peekable();
476489
loop {
477490
match chars.next() {
478-
Some((i, &c)) => {
479-
if c == b'\\' {
480-
if *chars.peek().expect(error(i).as_slice()).val1() == b'\n' {
481-
loop {
482-
// eat everything up to a non-whitespace
483-
match chars.peek().map(|x| *x.val1()) {
484-
Some(b' ') | Some(b'\n') | Some(b'\r') | Some(b'\t') => {
485-
chars.next();
486-
},
487-
_ => { break; }
488-
}
491+
Some((i, b'\\')) => {
492+
let em = error(i);
493+
match chars.peek().expect(em.as_slice()).val1() {
494+
b'\n' => eat(&mut chars),
495+
b'\r' => {
496+
chars.next();
497+
if chars.peek().expect(em.as_slice()).val1() != b'\n' {
498+
fail!("lexer accepted bare CR");
489499
}
490-
} else {
500+
eat(&mut chars);
501+
}
502+
_ => {
491503
// otherwise, a normal escape
492504
let (c, n) = byte_lit(lit.slice_from(i));
493-
for _ in range(0, n - 1) { // we don't need to move past the first \
505+
// we don't need to move past the first \
506+
for _ in range(0, n - 1) {
494507
chars.next();
495508
}
496509
res.push(c);
497510
}
498-
} else {
499-
res.push(c);
500511
}
501512
},
502-
None => { break; }
513+
Some((i, b'\r')) => {
514+
let em = error(i);
515+
if chars.peek().expect(em.as_slice()).val1() != b'\n' {
516+
fail!("lexer accepted bare CR");
517+
}
518+
chars.next();
519+
res.push(b'\n');
520+
}
521+
Some((_, c)) => res.push(c),
522+
None => break,
503523
}
504524
}
505525

src/test/run-pass/.gitattributes

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
lexer-crlf-line-endings-string-literal-doc-comment.rs -text
2+
issue-16278.rs -text

src/test/run-pass/issue-16278.rs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 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.
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+
// ignore-tidy-cr
12+
13+
// this file has some special \r\n endings (use xxd to see them)
14+
15+
fn main() {assert_eq!(b"", b"\
16+
");
17+
assert_eq!(b"\n", b"
18+
");
19+
}
20+

0 commit comments

Comments
 (0)