Skip to content

Commit b2edd42

Browse files
authored
Merge pull request #10603 from robertbastian/octal
Fix false positives and false negatives in `octal_escapes`
2 parents d9c2957 + 67e836d commit b2edd42

File tree

3 files changed

+67
-46
lines changed

3 files changed

+67
-46
lines changed

clippy_lints/src/octal_escapes.rs

+49-45
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) {
7676
if ch == '\\' {
7777
if let Some((_, '0')) = iter.next() {
7878
// collect up to two further octal digits
79-
if let Some((mut to, '0'..='7')) = iter.next() {
80-
if let Some((_, '0'..='7')) = iter.peek() {
79+
if let Some((mut to, _)) = iter.next_if(|(_, ch)| matches!(ch, '0'..='7')) {
80+
if iter.next_if(|(_, ch)| matches!(ch, '0'..='7')).is_some() {
8181
to += 1;
8282
}
8383
found.push((from, to + 1));
@@ -90,32 +90,6 @@ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) {
9090
return;
9191
}
9292

93-
// construct two suggestion strings, one with \x escapes with octal meaning
94-
// as in C, and one with \x00 for null bytes.
95-
let mut suggest_1 = if is_string { "\"" } else { "b\"" }.to_string();
96-
let mut suggest_2 = suggest_1.clone();
97-
let mut index = 0;
98-
for (from, to) in found {
99-
suggest_1.push_str(&contents[index..from]);
100-
suggest_2.push_str(&contents[index..from]);
101-
102-
// construct a replacement escape
103-
// the maximum value is \077, or \x3f, so u8 is sufficient here
104-
if let Ok(n) = u8::from_str_radix(&contents[from + 1..to], 8) {
105-
write!(suggest_1, "\\x{n:02x}").unwrap();
106-
}
107-
108-
// append the null byte as \x00 and the following digits literally
109-
suggest_2.push_str("\\x00");
110-
suggest_2.push_str(&contents[from + 2..to]);
111-
112-
index = to;
113-
}
114-
suggest_1.push_str(&contents[index..]);
115-
suggest_1.push('"');
116-
suggest_2.push_str(&contents[index..]);
117-
suggest_2.push('"');
118-
11993
span_lint_and_then(
12094
cx,
12195
OCTAL_ESCAPES,
@@ -129,23 +103,53 @@ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) {
129103
"octal escapes are not supported, `\\0` is always a null {}",
130104
if is_string { "character" } else { "byte" }
131105
));
132-
// suggestion 1: equivalent hex escape
133-
diag.span_suggestion(
134-
span,
135-
"if an octal escape was intended, use the hexadecimal representation instead",
136-
suggest_1,
137-
Applicability::MaybeIncorrect,
138-
);
139-
// suggestion 2: unambiguous null byte
140-
diag.span_suggestion(
141-
span,
142-
format!(
143-
"if the null {} is intended, disambiguate using",
144-
if is_string { "character" } else { "byte" }
145-
),
146-
suggest_2,
147-
Applicability::MaybeIncorrect,
148-
);
106+
107+
// Generate suggestions if the string is not too long (~ 5 lines)
108+
if contents.len() < 400 {
109+
// construct two suggestion strings, one with \x escapes with octal meaning
110+
// as in C, and one with \x00 for null bytes.
111+
let mut suggest_1 = if is_string { "\"" } else { "b\"" }.to_string();
112+
let mut suggest_2 = suggest_1.clone();
113+
let mut index = 0;
114+
for (from, to) in found {
115+
suggest_1.push_str(&contents[index..from]);
116+
suggest_2.push_str(&contents[index..from]);
117+
118+
// construct a replacement escape
119+
// the maximum value is \077, or \x3f, so u8 is sufficient here
120+
if let Ok(n) = u8::from_str_radix(&contents[from + 1..to], 8) {
121+
write!(suggest_1, "\\x{n:02x}").unwrap();
122+
}
123+
124+
// append the null byte as \x00 and the following digits literally
125+
suggest_2.push_str("\\x00");
126+
suggest_2.push_str(&contents[from + 2..to]);
127+
128+
index = to;
129+
}
130+
suggest_1.push_str(&contents[index..]);
131+
suggest_2.push_str(&contents[index..]);
132+
133+
suggest_1.push('"');
134+
suggest_2.push('"');
135+
// suggestion 1: equivalent hex escape
136+
diag.span_suggestion(
137+
span,
138+
"if an octal escape was intended, use the hexadecimal representation instead",
139+
suggest_1,
140+
Applicability::MaybeIncorrect,
141+
);
142+
// suggestion 2: unambiguous null byte
143+
diag.span_suggestion(
144+
span,
145+
format!(
146+
"if the null {} is intended, disambiguate using",
147+
if is_string { "character" } else { "byte" }
148+
),
149+
suggest_2,
150+
Applicability::MaybeIncorrect,
151+
);
152+
}
149153
},
150154
);
151155
}

tests/ui/octal_escapes.rs

+1
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ fn main() {
1717
let _good3 = "\0\0";
1818
let _good4 = "X\0\0X";
1919
let _good5 = "锈\0锈";
20+
let _good6 = "\0\\01";
2021
}

tests/ui/octal_escapes.stderr

+17-1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,22 @@ help: if the null character is intended, disambiguate using
6363
LL | let _bad4 = "/x001234567";
6464
| ~~~~~~~~~~~~~
6565

66+
error: octal-looking escape in string literal
67+
--> $DIR/octal_escapes.rs:9:17
68+
|
69+
LL | let _bad5 = "/0/03";
70+
| ^^^^^^^
71+
|
72+
= help: octal escapes are not supported, `/0` is always a null character
73+
help: if an octal escape was intended, use the hexadecimal representation instead
74+
|
75+
LL | let _bad5 = "/0/x03";
76+
| ~~~~~~~~
77+
help: if the null character is intended, disambiguate using
78+
|
79+
LL | let _bad5 = "/0/x003";
80+
| ~~~~~~~~~
81+
6682
error: octal-looking escape in string literal
6783
--> $DIR/octal_escapes.rs:10:17
6884
|
@@ -127,5 +143,5 @@ help: if the null character is intended, disambiguate using
127143
LL | let _bad9 = "锈/x0011锈";
128144
| ~~~~~~~~~~~~
129145

130-
error: aborting due to 8 previous errors
146+
error: aborting due to 9 previous errors
131147

0 commit comments

Comments
 (0)