diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs index 9ae269bc7d9..b3a4ab121e1 100644 --- a/clippy_lints/src/octal_escapes.rs +++ b/clippy_lints/src/octal_escapes.rs @@ -9,8 +9,8 @@ use rustc_span::Span; declare_clippy_lint! { /// ### What it does - /// Checks for `\0` escapes in string and byte literals that look like octal character - /// escapes in C + /// Checks for `\0` escapes in string and byte literals that look like octal + /// character escapes in C. /// /// ### Why is this bad? /// Rust does not support octal notation for character escapes. `\0` is always a @@ -57,20 +57,18 @@ impl EarlyLintPass for OctalEscapes { fn check_lit(cx: &EarlyContext<'tcx>, lit: &Lit, span: Span, is_string: bool) { let contents = lit.symbol.as_str(); - let mut iter = contents.char_indices(); + let mut iter = contents.char_indices().peekable(); // go through the string, looking for \0[0-7] while let Some((from, ch)) = iter.next() { if ch == '\\' { - if let Some((mut to, '0')) = iter.next() { - // collect all further potentially octal digits - while let Some((j, '0'..='7')) = iter.next() { - to = j + 1; - } - // if it's more than just `\0` we have a match - if to > from + 2 { - emit(cx, &contents, from, to, span, is_string); - return; + if let Some((_, '0')) = iter.next() { + // collect up to two further octal digits + if let Some((mut to, '0'..='7')) = iter.next() { + if let Some((_, '0'..='7')) = iter.peek() { + to += 1; + } + emit(cx, &contents, from, to + 1, span, is_string); } } } @@ -80,19 +78,9 @@ fn check_lit(cx: &EarlyContext<'tcx>, lit: &Lit, span: Span, is_string: bool) { fn emit(cx: &EarlyContext<'tcx>, contents: &str, from: usize, to: usize, span: Span, is_string: bool) { // construct a replacement escape for that case that octal was intended let escape = &contents[from + 1..to]; - let literal_suggestion = if is_string { - u32::from_str_radix(escape, 8).ok().and_then(|n| { - if n < 256 { - Some(format!("\\x{:02x}", n)) - } else if n <= std::char::MAX as u32 { - Some(format!("\\u{{{:x}}}", n)) - } else { - None - } - }) - } else { - u8::from_str_radix(escape, 8).ok().map(|n| format!("\\x{:02x}", n)) - }; + // the maximum value is \077, or \x3f + let literal_suggestion = u8::from_str_radix(escape, 8).ok().map(|n| format!("\\x{:02x}", n)); + let prefix = if is_string { "" } else { "b" }; span_lint_and_then( cx, @@ -111,8 +99,8 @@ fn emit(cx: &EarlyContext<'tcx>, contents: &str, from: usize, to: usize, span: S if let Some(sugg) = literal_suggestion { diag.span_suggestion( span, - "if an octal escape is intended, use", - format!("\"{}{}{}\"", &contents[..from], sugg, &contents[to..]), + "if an octal escape was intended, use the hexadecimal representation instead", + format!("{}\"{}{}{}\"", prefix, &contents[..from], sugg, &contents[to..]), Applicability::MaybeIncorrect, ); } @@ -123,7 +111,7 @@ fn emit(cx: &EarlyContext<'tcx>, contents: &str, from: usize, to: usize, span: S "if the null {} is intended, disambiguate using", if is_string { "character" } else { "byte" } ), - format!("\"{}\\x00{}\"", &contents[..from], &contents[from + 2..]), + format!("{}\"{}\\x00{}\"", prefix, &contents[..from], &contents[from + 2..]), Applicability::MaybeIncorrect, ); }, diff --git a/tests/ui/octal_escapes.rs b/tests/ui/octal_escapes.rs index 5ddee73c020..53145ef0fd2 100644 --- a/tests/ui/octal_escapes.rs +++ b/tests/ui/octal_escapes.rs @@ -4,9 +4,17 @@ fn main() { let _bad1 = "\033[0m"; let _bad2 = b"\033[0m"; let _bad3 = "\\\033[0m"; + // maximum 3 digits (\012 is the escape) let _bad4 = "\01234567"; let _bad5 = "\0\03"; + let _bad6 = "Text-\055\077-MoreText"; + let _bad7 = "EvenMoreText-\01\02-ShortEscapes"; + let _bad8 = "锈\01锈"; + let _bad9 = "锈\011锈"; let _good1 = "\\033[0m"; let _good2 = "\0\\0"; + let _good3 = "\0\0"; + let _good4 = "X\0\0X"; + let _good5 = "锈\0锈"; } diff --git a/tests/ui/octal_escapes.stderr b/tests/ui/octal_escapes.stderr index 8a93e781bed..8c27dc0cf05 100644 --- a/tests/ui/octal_escapes.stderr +++ b/tests/ui/octal_escapes.stderr @@ -6,7 +6,7 @@ LL | let _bad1 = "/033[0m"; | = note: `-D clippy::octal-escapes` implied by `-D warnings` = help: octal escapes are not supported, `/0` is always a null character -help: if an octal escape is intended, use +help: if an octal escape was intended, use the hexadecimal representation instead | LL | let _bad1 = "/x1b[0m"; | ~~~~~~~~~ @@ -22,14 +22,14 @@ LL | let _bad2 = b"/033[0m"; | ^^^^^^^^^^ | = help: octal escapes are not supported, `/0` is always a null byte -help: if an octal escape is intended, use +help: if an octal escape was intended, use the hexadecimal representation instead | -LL | let _bad2 = "/x1b[0m"; - | ~~~~~~~~~ +LL | let _bad2 = b"/x1b[0m"; + | ~~~~~~~~~~ help: if the null byte is intended, disambiguate using | -LL | let _bad2 = "/x0033[0m"; - | ~~~~~~~~~~~ +LL | let _bad2 = b"/x0033[0m"; + | ~~~~~~~~~~~~ error: octal-looking escape in string literal --> $DIR/octal_escapes.rs:6:17 @@ -38,7 +38,7 @@ LL | let _bad3 = "//033[0m"; | ^^^^^^^^^^^ | = help: octal escapes are not supported, `/0` is always a null character -help: if an octal escape is intended, use +help: if an octal escape was intended, use the hexadecimal representation instead | LL | let _bad3 = "//x1b[0m"; | ~~~~~~~~~~~ @@ -48,20 +48,116 @@ LL | let _bad3 = "//x0033[0m"; | ~~~~~~~~~~~~~ error: octal-looking escape in string literal - --> $DIR/octal_escapes.rs:7:17 + --> $DIR/octal_escapes.rs:8:17 | LL | let _bad4 = "/01234567"; | ^^^^^^^^^^^ | = help: octal escapes are not supported, `/0` is always a null character -help: if an octal escape is intended, use +help: if an octal escape was intended, use the hexadecimal representation instead | -LL | let _bad4 = "/u{53977}"; +LL | let _bad4 = "/x0a34567"; | ~~~~~~~~~~~ help: if the null character is intended, disambiguate using | LL | let _bad4 = "/x001234567"; | ~~~~~~~~~~~~~ -error: aborting due to 4 previous errors +error: octal-looking escape in string literal + --> $DIR/octal_escapes.rs:10:17 + | +LL | let _bad6 = "Text-/055/077-MoreText"; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: octal escapes are not supported, `/0` is always a null character +help: if an octal escape was intended, use the hexadecimal representation instead + | +LL | let _bad6 = "Text-/x2d/077-MoreText"; + | ~~~~~~~~~~~~~~~~~~~~~~~~ +help: if the null character is intended, disambiguate using + | +LL | let _bad6 = "Text-/x0055/077-MoreText"; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: octal-looking escape in string literal + --> $DIR/octal_escapes.rs:10:17 + | +LL | let _bad6 = "Text-/055/077-MoreText"; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: octal escapes are not supported, `/0` is always a null character +help: if an octal escape was intended, use the hexadecimal representation instead + | +LL | let _bad6 = "Text-/055/x3f-MoreText"; + | ~~~~~~~~~~~~~~~~~~~~~~~~ +help: if the null character is intended, disambiguate using + | +LL | let _bad6 = "Text-/055/x0077-MoreText"; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: octal-looking escape in string literal + --> $DIR/octal_escapes.rs:11:17 + | +LL | let _bad7 = "EvenMoreText-/01/02-ShortEscapes"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: octal escapes are not supported, `/0` is always a null character +help: if an octal escape was intended, use the hexadecimal representation instead + | +LL | let _bad7 = "EvenMoreText-/x01/02-ShortEscapes"; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: if the null character is intended, disambiguate using + | +LL | let _bad7 = "EvenMoreText-/x001/02-ShortEscapes"; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: octal-looking escape in string literal + --> $DIR/octal_escapes.rs:11:17 + | +LL | let _bad7 = "EvenMoreText-/01/02-ShortEscapes"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: octal escapes are not supported, `/0` is always a null character +help: if an octal escape was intended, use the hexadecimal representation instead + | +LL | let _bad7 = "EvenMoreText-/01/x02-ShortEscapes"; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: if the null character is intended, disambiguate using + | +LL | let _bad7 = "EvenMoreText-/01/x002-ShortEscapes"; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: octal-looking escape in string literal + --> $DIR/octal_escapes.rs:12:17 + | +LL | let _bad8 = "锈/01锈"; + | ^^^^^^^^^ + | + = help: octal escapes are not supported, `/0` is always a null character +help: if an octal escape was intended, use the hexadecimal representation instead + | +LL | let _bad8 = "锈/x01锈"; + | ~~~~~~~~~~ +help: if the null character is intended, disambiguate using + | +LL | let _bad8 = "锈/x001锈"; + | ~~~~~~~~~~~ + +error: octal-looking escape in string literal + --> $DIR/octal_escapes.rs:13:17 + | +LL | let _bad9 = "锈/011锈"; + | ^^^^^^^^^^ + | + = help: octal escapes are not supported, `/0` is always a null character +help: if an octal escape was intended, use the hexadecimal representation instead + | +LL | let _bad9 = "锈/x09锈"; + | ~~~~~~~~~~ +help: if the null character is intended, disambiguate using + | +LL | let _bad9 = "锈/x0011锈"; + | ~~~~~~~~~~~~ + +error: aborting due to 10 previous errors