Skip to content

Commit 8e37b75

Browse files
committed
Detect NulInCStr error earlier.
By making it an `EscapeError` instead of a `LitError`. This makes it more like the other errors produced during unescaping. One nice thing about this: the old approach had some code in `report_lit_error` to calculate the span of the nul char from a range. This code used a hardwired `+2` to account for the `c"` at the start of a C string literal, but this should have changed to a `+3` for raw C string literals to account for the `cr"`, which meant that the caret in `cr"` nul error messages was one short of where it should have been. The new approach doesn't need any of this and avoids the off-by-one error. XXX: this means they occur earlier; need to delay all lex errors
1 parent 1d031fa commit 8e37b75

File tree

8 files changed

+26
-28
lines changed

8 files changed

+26
-28
lines changed

compiler/rustc_ast/src/util/literal.rs

+2-10
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use rustc_lexer::unescape::{
88
};
99
use rustc_span::symbol::{kw, sym, Symbol};
1010
use rustc_span::Span;
11-
use std::ops::Range;
1211
use std::{ascii, fmt, str};
1312

1413
// Escapes a string, represented as a symbol. Reuses the original symbol,
@@ -39,7 +38,6 @@ pub enum LitError {
3938
InvalidFloatSuffix,
4039
NonDecimalFloat(u32),
4140
IntTooLarge(u32),
42-
NulInCStr(Range<usize>),
4341
}
4442

4543
impl LitKind {
@@ -167,10 +165,7 @@ impl LitKind {
167165
let s = symbol.as_str();
168166
let mut buf = Vec::with_capacity(s.len());
169167
let mut error = Ok(());
170-
unescape_c_string(s, Mode::CStr, &mut |span, c| match c {
171-
Ok(CStrUnit::Byte(0) | CStrUnit::Char('\0')) => {
172-
error = Err(LitError::NulInCStr(span));
173-
}
168+
unescape_c_string(s, Mode::CStr, &mut |_span, c| match c {
174169
Ok(CStrUnit::Byte(b)) => buf.push(b),
175170
Ok(CStrUnit::Char(c)) if c.len_utf8() == 1 => buf.push(c as u8),
176171
Ok(CStrUnit::Char(c)) => {
@@ -190,10 +185,7 @@ impl LitKind {
190185
let s = symbol.as_str();
191186
let mut buf = Vec::with_capacity(s.len());
192187
let mut error = Ok(());
193-
unescape_c_string(s, Mode::RawCStr, &mut |span, c| match c {
194-
Ok(CStrUnit::Byte(0) | CStrUnit::Char('\0')) => {
195-
error = Err(LitError::NulInCStr(span));
196-
}
188+
unescape_c_string(s, Mode::RawCStr, &mut |_span, c| match c {
197189
Ok(CStrUnit::Byte(b)) => buf.push(b),
198190
Ok(CStrUnit::Char(c)) if c.len_utf8() == 1 => buf.push(c as u8),
199191
Ok(CStrUnit::Char(c)) => {

compiler/rustc_lexer/src/unescape.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ pub enum EscapeError {
5959
/// Non-ascii character in byte literal, byte string literal, or raw byte string literal.
6060
NonAsciiCharInByte,
6161

62+
// `\0` in a C string literal.
63+
NulInCStr,
64+
6265
/// After a line ending with '\', the next line contains whitespace
6366
/// characters that are not skipped.
6467
UnskippedWhitespaceWarning,
@@ -121,10 +124,18 @@ where
121124
{
122125
match mode {
123126
CStr => {
124-
unescape_str_common(src, mode, callback);
127+
unescape_str_common(src, mode, &mut |r, mut result| {
128+
if let Ok(CStrUnit::Byte(0) | CStrUnit::Char('\0')) = result {
129+
result = Err(EscapeError::NulInCStr);
130+
}
131+
callback(r, result)
132+
});
125133
}
126134
RawCStr => {
127-
unescape_raw_str_or_raw_byte_str(src, mode, &mut |r, result| {
135+
unescape_raw_str_or_raw_byte_str(src, mode, &mut |r, mut result| {
136+
if let Ok('\0') = result {
137+
result = Err(EscapeError::NulInCStr);
138+
}
128139
callback(r, result.map(CStrUnit::Char))
129140
});
130141
}

compiler/rustc_parse/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,8 @@ parse_note_mut_pattern_usage = `mut` may be followed by `variable` and `variable
616616
617617
parse_note_pattern_alternatives_use_single_vert = alternatives in or-patterns are separated with `|`, not `||`
618618
619+
parse_nul_in_c_str = null characters in C string literals are not supported
620+
619621
parse_or_pattern_not_allowed_in_fn_parameters = top-level or-patterns are not allowed in function parameters
620622
parse_or_pattern_not_allowed_in_let_binding = top-level or-patterns are not allowed in `let` bindings
621623
parse_out_of_range_hex_escape = out of range hex escape

compiler/rustc_parse/src/errors.rs

+5
Original file line numberDiff line numberDiff line change
@@ -2145,6 +2145,11 @@ pub enum UnescapeError {
21452145
#[subdiagnostic]
21462146
suggestion: MoreThanOneCharSugg,
21472147
},
2148+
#[diag(parse_nul_in_c_str)]
2149+
NulInCStr {
2150+
#[primary_span]
2151+
span: Span,
2152+
},
21482153
}
21492154

21502155
#[derive(Subdiagnostic)]

compiler/rustc_parse/src/lexer/unescape_error_reporting.rs

+3
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,9 @@ pub(crate) fn emit_unescape_error(
256256
EscapeError::LoneSlash => {
257257
handler.emit_err(UnescapeError::LoneSlash(span));
258258
}
259+
EscapeError::NulInCStr => {
260+
handler.emit_err(UnescapeError::NulInCStr { span });
261+
}
259262
EscapeError::UnskippedWhitespaceWarning => {
260263
let (c, char_span) = last_char();
261264
handler.emit_warning(UnescapeError::UnskippedWhitespace {

compiler/rustc_session/messages.ftl

-2
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,6 @@ session_not_circumvent_feature = `-Zunleash-the-miri-inside-of-you` may not be u
7070
7171
session_not_supported = not supported
7272
73-
session_nul_in_c_str = null characters in C string literals are not supported
74-
7573
session_octal_float_literal_not_supported = octal float literal is not supported
7674
session_optimization_fuel_exhausted = optimization-fuel-exhausted: {$msg}
7775

compiler/rustc_session/src/errors.rs

+1-14
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_ast::token;
55
use rustc_ast::util::literal::LitError;
66
use rustc_errors::{error_code, DiagnosticMessage, ErrorGuaranteed, IntoDiagnostic, MultiSpan};
77
use rustc_macros::Diagnostic;
8-
use rustc_span::{BytePos, Span, Symbol};
8+
use rustc_span::{Span, Symbol};
99
use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTriple};
1010

1111
pub struct FeatureGateError {
@@ -329,13 +329,6 @@ pub(crate) struct BinaryFloatLiteralNotSupported {
329329
pub span: Span,
330330
}
331331

332-
#[derive(Diagnostic)]
333-
#[diag(session_nul_in_c_str)]
334-
pub(crate) struct NulInCStr {
335-
#[primary_span]
336-
pub span: Span,
337-
}
338-
339332
pub fn report_lit_error(sess: &ParseSess, err: LitError, lit: token::Lit, span: Span) {
340333
// Checks if `s` looks like i32 or u1234 etc.
341334
fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool {
@@ -414,12 +407,6 @@ pub fn report_lit_error(sess: &ParseSess, err: LitError, lit: token::Lit, span:
414407
};
415408
sess.emit_err(IntLiteralTooLarge { span, limit });
416409
}
417-
LitError::NulInCStr(range) => {
418-
let lo = BytePos(span.lo().0 + range.start as u32 + 2);
419-
let hi = BytePos(span.lo().0 + range.end as u32 + 2);
420-
let span = span.with_lo(lo).with_hi(hi);
421-
sess.emit_err(NulInCStr { span });
422-
}
423410
}
424411
}
425412

Binary file not shown.

0 commit comments

Comments
 (0)