Skip to content

Commit 16a7e28

Browse files
committed
Detect NulInCStr error earlier.
By making it an `EscapeError` instead of a `LitError`. This makes it like the other errors produced when checking string literals contents, e.g. for invalid escape sequences or bare CR chars. NOTE: this means these errors are issued earlier, before expansion, which changes behaviour. It will be possible to move the check back to the later point if desired. If that happens, it's likely that all the string literal contents checks will be delayed together. 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.
1 parent 62d7ed4 commit 16a7e28

File tree

11 files changed

+32
-28
lines changed

11 files changed

+32
-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 {
@@ -156,10 +154,7 @@ impl LitKind {
156154
let s = symbol.as_str();
157155
let mut buf = Vec::with_capacity(s.len());
158156
let mut error = Ok(());
159-
unescape_c_string(s, Mode::CStr, &mut |span, c| match c {
160-
Ok(CStrUnit::Byte(0) | CStrUnit::Char('\0')) => {
161-
error = Err(LitError::NulInCStr(span));
162-
}
157+
unescape_c_string(s, Mode::CStr, &mut |_span, c| match c {
163158
Ok(CStrUnit::Byte(b)) => buf.push(b),
164159
Ok(CStrUnit::Char(c)) => {
165160
buf.extend_from_slice(c.encode_utf8(&mut [0; 4]).as_bytes())
@@ -179,10 +174,7 @@ impl LitKind {
179174
// can convert the symbol directly to a `Lrc<u8>` on success.
180175
let s = symbol.as_str();
181176
let mut error = Ok(());
182-
unescape_c_string(s, Mode::RawCStr, &mut |span, c| match c {
183-
Ok(CStrUnit::Byte(0) | CStrUnit::Char('\0')) => {
184-
error = Err(LitError::NulInCStr(span));
185-
}
177+
unescape_c_string(s, Mode::RawCStr, &mut |_, c| match c {
186178
Ok(_) => {}
187179
Err(err) => {
188180
if err.is_fatal() {

compiler/rustc_lexer/src/unescape.rs

+15-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,
@@ -122,10 +125,20 @@ where
122125
{
123126
match mode {
124127
CStr => {
125-
unescape_non_raw_common(src, mode, callback);
128+
unescape_non_raw_common(src, mode, &mut |r, mut result| {
129+
if let Ok(CStrUnit::Byte(0) | CStrUnit::Char('\0')) = result {
130+
result = Err(EscapeError::NulInCStr);
131+
}
132+
callback(r, result)
133+
});
126134
}
127135
RawCStr => {
128-
check_raw_common(src, mode, &mut |r, result| callback(r, result.map(CStrUnit::Char)));
136+
check_raw_common(src, mode, &mut |r, mut result| {
137+
if let Ok('\0') = result {
138+
result = Err(EscapeError::NulInCStr);
139+
}
140+
callback(r, result.map(CStrUnit::Char))
141+
});
129142
}
130143
Char | Byte | Str | RawStr | ByteStr | RawByteStr => unreachable!(),
131144
}

compiler/rustc_parse/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,8 @@ parse_note_mut_pattern_usage = `mut` may be followed by `variable` and `variable
613613
614614
parse_note_pattern_alternatives_use_single_vert = alternatives in or-patterns are separated with `|`, not `||`
615615
616+
parse_nul_in_c_str = null characters in C string literals are not supported
617+
616618
parse_or_pattern_not_allowed_in_fn_parameters = top-level or-patterns are not allowed in function parameters
617619
parse_or_pattern_not_allowed_in_let_binding = top-level or-patterns are not allowed in `let` bindings
618620
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
@@ -2144,6 +2144,11 @@ pub enum UnescapeError {
21442144
#[subdiagnostic]
21452145
suggestion: MoreThanOneCharSugg,
21462146
},
2147+
#[diag(parse_nul_in_c_str)]
2148+
NulInCStr {
2149+
#[primary_span]
2150+
span: Span,
2151+
},
21472152
}
21482153

21492154
#[derive(Subdiagnostic)]

compiler/rustc_parse/src/lexer/unescape_error_reporting.rs

+3
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,9 @@ pub(crate) fn emit_unescape_error(
262262
EscapeError::LoneSlash => {
263263
dcx.emit_err(UnescapeError::LoneSlash(err_span));
264264
}
265+
EscapeError::NulInCStr => {
266+
dcx.emit_err(UnescapeError::NulInCStr { span: err_span });
267+
}
265268
EscapeError::UnskippedWhitespaceWarning => {
266269
let (c, char_span) = last_char();
267270
dcx.emit_warn(UnescapeError::UnskippedWhitespace {

compiler/rustc_session/messages.ftl

-2
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,6 @@ session_not_circumvent_feature = `-Zunleash-the-miri-inside-of-you` may not be u
7272
7373
session_not_supported = not supported
7474
75-
session_nul_in_c_str = null characters in C string literals are not supported
76-
7775
session_octal_float_literal_not_supported = octal float literal is not supported
7876
7977
session_optimization_fuel_exhausted = optimization-fuel-exhausted: {$msg}

compiler/rustc_session/src/errors.rs

+1-14
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use rustc_errors::{
77
error_code, DiagCtxt, DiagnosticBuilder, DiagnosticMessage, IntoDiagnostic, Level, MultiSpan,
88
};
99
use rustc_macros::Diagnostic;
10-
use rustc_span::{BytePos, Span, Symbol};
10+
use rustc_span::{Span, Symbol};
1111
use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTriple};
1212

1313
pub struct FeatureGateError {
@@ -327,13 +327,6 @@ pub(crate) struct BinaryFloatLiteralNotSupported {
327327
pub span: Span,
328328
}
329329

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

src/tools/rust-analyzer/crates/parser/src/lexed_str.rs

+1
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ fn error_to_diagnostic_message(error: EscapeError, mode: Mode) -> &'static str {
371371
"non-ASCII character in byte string literal"
372372
}
373373
EscapeError::NonAsciiCharInByte => "non-ASCII character in raw byte string literal",
374+
EscapeError::NulInCStr => "null character in C string literal",
374375
EscapeError::UnskippedWhitespaceWarning => "",
375376
EscapeError::MultipleSkippedLinesWarning => "",
376377
}

src/tools/rust-analyzer/crates/syntax/src/validation.rs

+3
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> (&'static str,
106106
EE::NonAsciiCharInByte => {
107107
"Byte literals must not contain non-ASCII characters"
108108
}
109+
EE::NulInCStr => {
110+
"C strings literals must not contain null characters"
111+
}
109112
EE::UnskippedWhitespaceWarning => "Whitespace after this escape is not skipped",
110113
EE::MultipleSkippedLinesWarning => "Multiple lines are skipped by this escape",
111114

Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)