Skip to content

Commit b9441f2

Browse files
committed
Improve char escaping in lexer messages
Currently ', " and \ are escaped as \', \" and \\ respectively. This leads to confusing messages such as `error: unknown start of token: \\` when encountering a single backslash. Fix by emitting printable ASCII characters directly. This will still escape \r, \n, \t and Unicode characters. Fixes #47902
1 parent 560a2f4 commit b9441f2

File tree

3 files changed

+33
-13
lines changed

3 files changed

+33
-13
lines changed

src/libsyntax/parse/lexer/mod.rs

+19-12
Original file line numberDiff line numberDiff line change
@@ -246,14 +246,27 @@ impl<'a> StringReader<'a> {
246246
self.err_span(self.mk_sp(from_pos, to_pos), m)
247247
}
248248

249+
/// Pushes a character to a message string for error reporting
250+
fn push_escaped_char_for_msg(m: &mut String, c: char) {
251+
match c {
252+
'\u{20}'...'\u{7e}' => {
253+
// Don't escape \, ' or " for user-facing messages
254+
m.push(c);
255+
}
256+
_ => {
257+
for c in c.escape_default() {
258+
m.push(c);
259+
}
260+
}
261+
}
262+
}
263+
249264
/// Report a lexical error spanning [`from_pos`, `to_pos`), appending an
250265
/// escaped character to the error message
251266
fn fatal_span_char(&self, from_pos: BytePos, to_pos: BytePos, m: &str, c: char) -> FatalError {
252267
let mut m = m.to_string();
253268
m.push_str(": ");
254-
for c in c.escape_default() {
255-
m.push(c)
256-
}
269+
Self::push_escaped_char_for_msg(&mut m, c);
257270
self.fatal_span_(from_pos, to_pos, &m[..])
258271
}
259272
fn struct_fatal_span_char(&self,
@@ -264,9 +277,7 @@ impl<'a> StringReader<'a> {
264277
-> DiagnosticBuilder<'a> {
265278
let mut m = m.to_string();
266279
m.push_str(": ");
267-
for c in c.escape_default() {
268-
m.push(c)
269-
}
280+
Self::push_escaped_char_for_msg(&mut m, c);
270281
self.sess.span_diagnostic.struct_span_fatal(self.mk_sp(from_pos, to_pos), &m[..])
271282
}
272283

@@ -275,9 +286,7 @@ impl<'a> StringReader<'a> {
275286
fn err_span_char(&self, from_pos: BytePos, to_pos: BytePos, m: &str, c: char) {
276287
let mut m = m.to_string();
277288
m.push_str(": ");
278-
for c in c.escape_default() {
279-
m.push(c)
280-
}
289+
Self::push_escaped_char_for_msg(&mut m, c);
281290
self.err_span_(from_pos, to_pos, &m[..]);
282291
}
283292
fn struct_err_span_char(&self,
@@ -288,9 +297,7 @@ impl<'a> StringReader<'a> {
288297
-> DiagnosticBuilder<'a> {
289298
let mut m = m.to_string();
290299
m.push_str(": ");
291-
for c in c.escape_default() {
292-
m.push(c)
293-
}
300+
Self::push_escaped_char_for_msg(&mut m, c);
294301
self.sess.span_diagnostic.struct_span_err(self.mk_sp(from_pos, to_pos), &m[..])
295302
}
296303

src/test/parse-fail/bad-char-literals.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
fn main() {
1616
// these literals are just silly.
1717
''';
18-
//~^ ERROR: character constant must be escaped: \'
18+
//~^ ERROR: character constant must be escaped: '
1919

2020
// note that this is a literal "\n" byte
2121
'
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
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+
// compile-flags: -Z parse-only
12+
13+
\ //~ ERROR: unknown start of token: \

0 commit comments

Comments
 (0)