Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 07d1088

Browse files
committedDec 21, 2023
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 57ad505 commit 07d1088

File tree

9 files changed

+28
-28
lines changed

9 files changed

+28
-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
@@ -612,6 +612,8 @@ parse_note_mut_pattern_usage = `mut` may be followed by `variable` and `variable
612612
613613
parse_note_pattern_alternatives_use_single_vert = alternatives in or-patterns are separated with `|`, not `||`
614614
615+
parse_nul_in_c_str = null characters in C string literals are not supported
616+
615617
parse_or_pattern_not_allowed_in_fn_parameters = top-level or-patterns are not allowed in function parameters
616618
parse_or_pattern_not_allowed_in_let_binding = top-level or-patterns are not allowed in `let` bindings
617619
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
@@ -2138,6 +2138,11 @@ pub enum UnescapeError {
21382138
#[subdiagnostic]
21392139
suggestion: MoreThanOneCharSugg,
21402140
},
2141+
#[diag(parse_nul_in_c_str)]
2142+
NulInCStr {
2143+
#[primary_span]
2144+
span: Span,
2145+
},
21412146
}
21422147

21432148
#[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_warning(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
@@ -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.
Binary file not shown.

0 commit comments

Comments
 (0)
Please sign in to comment.