mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-10 15:14:29 +00:00
Rework octal_escapes
.
This commit is contained in:
parent
9f5d60f160
commit
4b16e265a7
3 changed files with 176 additions and 194 deletions
|
@ -1,12 +1,12 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use rustc_ast::ast::{Expr, ExprKind};
|
||||
use rustc_ast::token::{Lit, LitKind};
|
||||
use clippy_utils::source::get_source_text;
|
||||
use rustc_ast::token::LitKind;
|
||||
use rustc_ast::{Expr, ExprKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
use std::fmt::Write;
|
||||
use rustc_span::{BytePos, Pos, SpanData};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -52,104 +52,69 @@ declare_lint_pass!(OctalEscapes => [OCTAL_ESCAPES]);
|
|||
|
||||
impl EarlyLintPass for OctalEscapes {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if in_external_macro(cx.sess(), expr.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::Lit(token_lit) = &expr.kind {
|
||||
if matches!(token_lit.kind, LitKind::Str) {
|
||||
check_lit(cx, token_lit, expr.span, true);
|
||||
} else if matches!(token_lit.kind, LitKind::ByteStr) {
|
||||
check_lit(cx, token_lit, expr.span, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) {
|
||||
let contents = lit.symbol.as_str();
|
||||
let mut iter = contents.char_indices().peekable();
|
||||
let mut found = vec![];
|
||||
|
||||
// go through the string, looking for \0[0-7][0-7]?
|
||||
while let Some((from, ch)) = iter.next() {
|
||||
if ch == '\\' {
|
||||
if let Some((_, '0')) = iter.next() {
|
||||
// collect up to two further octal digits
|
||||
if let Some((mut to, _)) = iter.next_if(|(_, ch)| matches!(ch, '0'..='7')) {
|
||||
if iter.next_if(|(_, ch)| matches!(ch, '0'..='7')).is_some() {
|
||||
to += 1;
|
||||
if let ExprKind::Lit(lit) = &expr.kind
|
||||
// The number of bytes from the start of the token to the start of literal's text.
|
||||
&& let start_offset = BytePos::from_u32(match lit.kind {
|
||||
LitKind::Str => 1,
|
||||
LitKind::ByteStr | LitKind::CStr => 2,
|
||||
_ => return,
|
||||
})
|
||||
&& !in_external_macro(cx.sess(), expr.span)
|
||||
{
|
||||
let s = lit.symbol.as_str();
|
||||
let mut iter = s.as_bytes().iter();
|
||||
while let Some(&c) = iter.next() {
|
||||
if c == b'\\'
|
||||
// Always move the iterator to read the escape char.
|
||||
&& let Some(b'0') = iter.next()
|
||||
{
|
||||
// C-style octal escapes read from one to three characters.
|
||||
// The first character (`0`) has already been read.
|
||||
let (tail, len, c_hi, c_lo) = match *iter.as_slice() {
|
||||
[c_hi @ b'0'..=b'7', c_lo @ b'0'..=b'7', ref tail @ ..] => (tail, 4, c_hi, c_lo),
|
||||
[c_lo @ b'0'..=b'7', ref tail @ ..] => (tail, 3, b'0', c_lo),
|
||||
_ => continue,
|
||||
};
|
||||
iter = tail.iter();
|
||||
let offset = start_offset + BytePos::from_usize(s.len() - tail.len());
|
||||
let data = expr.span.data();
|
||||
let span = SpanData {
|
||||
lo: data.lo + offset - BytePos::from_u32(len),
|
||||
hi: data.lo + offset,
|
||||
..data
|
||||
}
|
||||
.span();
|
||||
|
||||
// Last check to make sure the source text matches what we read from the string.
|
||||
// Macros are involved somehow if this doesn't match.
|
||||
if let Some(src) = get_source_text(cx, span)
|
||||
&& let Some(src) = src.as_str()
|
||||
&& match *src.as_bytes() {
|
||||
[b'\\', b'0', lo] => lo == c_lo,
|
||||
[b'\\', b'0', hi, lo] => hi == c_hi && lo == c_lo,
|
||||
_ => false,
|
||||
}
|
||||
{
|
||||
span_lint_and_then(cx, OCTAL_ESCAPES, span, "octal-looking escape in a literal", |diag| {
|
||||
diag.help_once("octal escapes are not supported, `\\0` is always null")
|
||||
.span_suggestion(
|
||||
span,
|
||||
"if an octal escape is intended, use a hex escape instead",
|
||||
format!("\\x{:02x}", (((c_hi - b'0') << 3) | (c_lo - b'0'))),
|
||||
Applicability::MaybeIncorrect,
|
||||
)
|
||||
.span_suggestion(
|
||||
span,
|
||||
"if a null escape is intended, disambiguate using",
|
||||
format!("\\x00{}{}", c_hi as char, c_lo as char),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
});
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
found.push((from, to + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
OCTAL_ESCAPES,
|
||||
span,
|
||||
format!(
|
||||
"octal-looking escape in {} literal",
|
||||
if is_string { "string" } else { "byte string" }
|
||||
),
|
||||
|diag| {
|
||||
diag.help(format!(
|
||||
"octal escapes are not supported, `\\0` is always a null {}",
|
||||
if is_string { "character" } else { "byte" }
|
||||
));
|
||||
|
||||
// Generate suggestions if the string is not too long (~ 5 lines)
|
||||
if contents.len() < 400 {
|
||||
// construct two suggestion strings, one with \x escapes with octal meaning
|
||||
// as in C, and one with \x00 for null bytes.
|
||||
let mut suggest_1 = if is_string { "\"" } else { "b\"" }.to_string();
|
||||
let mut suggest_2 = suggest_1.clone();
|
||||
let mut index = 0;
|
||||
for (from, to) in found {
|
||||
suggest_1.push_str(&contents[index..from]);
|
||||
suggest_2.push_str(&contents[index..from]);
|
||||
|
||||
// construct a replacement escape
|
||||
// the maximum value is \077, or \x3f, so u8 is sufficient here
|
||||
if let Ok(n) = u8::from_str_radix(&contents[from + 1..to], 8) {
|
||||
write!(suggest_1, "\\x{n:02x}").unwrap();
|
||||
}
|
||||
|
||||
// append the null byte as \x00 and the following digits literally
|
||||
suggest_2.push_str("\\x00");
|
||||
suggest_2.push_str(&contents[from + 2..to]);
|
||||
|
||||
index = to;
|
||||
}
|
||||
suggest_1.push_str(&contents[index..]);
|
||||
suggest_2.push_str(&contents[index..]);
|
||||
|
||||
suggest_1.push('"');
|
||||
suggest_2.push('"');
|
||||
// suggestion 1: equivalent hex escape
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
"if an octal escape was intended, use the hexadecimal representation instead",
|
||||
suggest_1,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
// suggestion 2: unambiguous null byte
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
format!(
|
||||
"if the null {} is intended, disambiguate using",
|
||||
if is_string { "character" } else { "byte" }
|
||||
),
|
||||
suggest_2,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,25 +2,20 @@
|
|||
#![warn(clippy::octal_escapes)]
|
||||
|
||||
fn main() {
|
||||
let _bad1 = "\033[0m";
|
||||
//~^ ERROR: octal-looking escape in string literal
|
||||
let _bad2 = b"\033[0m";
|
||||
//~^ ERROR: octal-looking escape in byte string literal
|
||||
let _bad3 = "\\\033[0m";
|
||||
//~^ ERROR: octal-looking escape in string literal
|
||||
let _bad1 = "\033[0m"; //~ octal_escapes
|
||||
let _bad2 = b"\033[0m"; //~ octal_escapes
|
||||
let _bad3 = "\\\033[0m"; //~ octal_escapes
|
||||
// maximum 3 digits (\012 is the escape)
|
||||
let _bad4 = "\01234567";
|
||||
//~^ ERROR: octal-looking escape in string literal
|
||||
let _bad5 = "\0\03";
|
||||
//~^ ERROR: octal-looking escape in string literal
|
||||
let _bad4 = "\01234567"; //~ octal_escapes
|
||||
let _bad5 = "\0\03"; //~ octal_escapes
|
||||
let _bad6 = "Text-\055\077-MoreText";
|
||||
//~^ ERROR: octal-looking escape in string literal
|
||||
//~^ octal_escapes
|
||||
//~| octal_escapes
|
||||
let _bad7 = "EvenMoreText-\01\02-ShortEscapes";
|
||||
//~^ ERROR: octal-looking escape in string literal
|
||||
let _bad8 = "锈\01锈";
|
||||
//~^ ERROR: octal-looking escape in string literal
|
||||
let _bad9 = "锈\011锈";
|
||||
//~^ ERROR: octal-looking escape in string literal
|
||||
//~^ octal_escapes
|
||||
//~| octal_escapes
|
||||
let _bad8 = "锈\01锈"; //~ octal_escapes
|
||||
let _bad9 = "锈\011锈"; //~ octal_escapes
|
||||
|
||||
let _good1 = "\\033[0m";
|
||||
let _good2 = "\0\\0";
|
||||
|
|
|
@ -1,148 +1,170 @@
|
|||
error: octal-looking escape in string literal
|
||||
--> tests/ui/octal_escapes.rs:5:17
|
||||
error: octal-looking escape in a literal
|
||||
--> tests/ui/octal_escapes.rs:5:18
|
||||
|
|
||||
LL | let _bad1 = "\033[0m";
|
||||
| ^^^^^^^^^
|
||||
| ^^^^
|
||||
|
|
||||
= help: octal escapes are not supported, `\0` is always a null character
|
||||
= help: octal escapes are not supported, `\0` is always null
|
||||
= note: `-D clippy::octal-escapes` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::octal_escapes)]`
|
||||
help: if an octal escape was intended, use the hexadecimal representation instead
|
||||
help: if an octal escape is intended, use a hex escape instead
|
||||
|
|
||||
LL | let _bad1 = "\x1b[0m";
|
||||
| ~~~~~~~~~
|
||||
help: if the null character is intended, disambiguate using
|
||||
| ~~~~
|
||||
help: if a null escape is intended, disambiguate using
|
||||
|
|
||||
LL | let _bad1 = "\x0033[0m";
|
||||
| ~~~~~~~~~~~
|
||||
| ~~~~~~
|
||||
|
||||
error: octal-looking escape in byte string literal
|
||||
--> tests/ui/octal_escapes.rs:7:17
|
||||
error: octal-looking escape in a literal
|
||||
--> tests/ui/octal_escapes.rs:6:19
|
||||
|
|
||||
LL | let _bad2 = b"\033[0m";
|
||||
| ^^^^^^^^^^
|
||||
| ^^^^
|
||||
|
|
||||
= help: octal escapes are not supported, `\0` is always a null byte
|
||||
help: if an octal escape was intended, use the hexadecimal representation instead
|
||||
help: if an octal escape is intended, use a hex escape instead
|
||||
|
|
||||
LL | let _bad2 = b"\x1b[0m";
|
||||
| ~~~~~~~~~~
|
||||
help: if the null byte is intended, disambiguate using
|
||||
| ~~~~
|
||||
help: if a null escape is intended, disambiguate using
|
||||
|
|
||||
LL | let _bad2 = b"\x0033[0m";
|
||||
| ~~~~~~~~~~~~
|
||||
| ~~~~~~
|
||||
|
||||
error: octal-looking escape in string literal
|
||||
--> tests/ui/octal_escapes.rs:9:17
|
||||
error: octal-looking escape in a literal
|
||||
--> tests/ui/octal_escapes.rs:7:20
|
||||
|
|
||||
LL | let _bad3 = "\\\033[0m";
|
||||
| ^^^^^^^^^^^
|
||||
| ^^^^
|
||||
|
|
||||
= help: octal escapes are not supported, `\0` is always a null character
|
||||
help: if an octal escape was intended, use the hexadecimal representation instead
|
||||
help: if an octal escape is intended, use a hex escape instead
|
||||
|
|
||||
LL | let _bad3 = "\\\x1b[0m";
|
||||
| ~~~~~~~~~~~
|
||||
help: if the null character is intended, disambiguate using
|
||||
| ~~~~
|
||||
help: if a null escape is intended, disambiguate using
|
||||
|
|
||||
LL | let _bad3 = "\\\x0033[0m";
|
||||
| ~~~~~~~~~~~~~
|
||||
| ~~~~~~
|
||||
|
||||
error: octal-looking escape in string literal
|
||||
--> tests/ui/octal_escapes.rs:12:17
|
||||
error: octal-looking escape in a literal
|
||||
--> tests/ui/octal_escapes.rs:9:18
|
||||
|
|
||||
LL | let _bad4 = "\01234567";
|
||||
| ^^^^^^^^^^^
|
||||
| ^^^^
|
||||
|
|
||||
= help: octal escapes are not supported, `\0` is always a null character
|
||||
help: if an octal escape was intended, use the hexadecimal representation instead
|
||||
help: if an octal escape is intended, use a hex escape instead
|
||||
|
|
||||
LL | let _bad4 = "\x0a34567";
|
||||
| ~~~~~~~~~~~
|
||||
help: if the null character is intended, disambiguate using
|
||||
| ~~~~
|
||||
help: if a null escape is intended, disambiguate using
|
||||
|
|
||||
LL | let _bad4 = "\x001234567";
|
||||
| ~~~~~~~~~~~~~
|
||||
| ~~~~~~
|
||||
|
||||
error: octal-looking escape in string literal
|
||||
--> tests/ui/octal_escapes.rs:14:17
|
||||
error: octal-looking escape in a literal
|
||||
--> tests/ui/octal_escapes.rs:10:20
|
||||
|
|
||||
LL | let _bad5 = "\0\03";
|
||||
| ^^^^^^^
|
||||
| ^^^
|
||||
|
|
||||
= help: octal escapes are not supported, `\0` is always a null character
|
||||
help: if an octal escape was intended, use the hexadecimal representation instead
|
||||
help: if an octal escape is intended, use a hex escape instead
|
||||
|
|
||||
LL | let _bad5 = "\0\x03";
|
||||
| ~~~~~~~~
|
||||
help: if the null character is intended, disambiguate using
|
||||
| ~~~~
|
||||
help: if a null escape is intended, disambiguate using
|
||||
|
|
||||
LL | let _bad5 = "\0\x003";
|
||||
| ~~~~~~~~~
|
||||
LL | let _bad5 = "\0\x0003";
|
||||
| ~~~~~~
|
||||
|
||||
error: octal-looking escape in string literal
|
||||
--> tests/ui/octal_escapes.rs:16:17
|
||||
error: octal-looking escape in a literal
|
||||
--> tests/ui/octal_escapes.rs:11:23
|
||||
|
|
||||
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
|
||||
help: if an octal escape is intended, use a hex escape instead
|
||||
|
|
||||
LL | let _bad6 = "Text-\x2d\x3f-MoreText";
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
help: if the null character is intended, disambiguate using
|
||||
LL | let _bad6 = "Text-\x2d\077-MoreText";
|
||||
| ~~~~
|
||||
help: if a null escape is intended, disambiguate using
|
||||
|
|
||||
LL | let _bad6 = "Text-\x0055\x0077-MoreText";
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
LL | let _bad6 = "Text-\x0055\077-MoreText";
|
||||
| ~~~~~~
|
||||
|
||||
error: octal-looking escape in string literal
|
||||
--> tests/ui/octal_escapes.rs:18:17
|
||||
error: octal-looking escape in a literal
|
||||
--> tests/ui/octal_escapes.rs:11:27
|
||||
|
|
||||
LL | let _bad6 = "Text-\055\077-MoreText";
|
||||
| ^^^^
|
||||
|
|
||||
help: if an octal escape is intended, use a hex escape instead
|
||||
|
|
||||
LL | let _bad6 = "Text-\055\x3f-MoreText";
|
||||
| ~~~~
|
||||
help: if a null escape is intended, disambiguate using
|
||||
|
|
||||
LL | let _bad6 = "Text-\055\x0077-MoreText";
|
||||
| ~~~~~~
|
||||
|
||||
error: octal-looking escape in a literal
|
||||
--> tests/ui/octal_escapes.rs:14:31
|
||||
|
|
||||
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
|
||||
help: if an octal escape is intended, use a hex escape instead
|
||||
|
|
||||
LL | let _bad7 = "EvenMoreText-\x01\x02-ShortEscapes";
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
help: if the null character is intended, disambiguate using
|
||||
LL | let _bad7 = "EvenMoreText-\x01\02-ShortEscapes";
|
||||
| ~~~~
|
||||
help: if a null escape is intended, disambiguate using
|
||||
|
|
||||
LL | let _bad7 = "EvenMoreText-\x001\x002-ShortEscapes";
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
LL | let _bad7 = "EvenMoreText-\x0001\02-ShortEscapes";
|
||||
| ~~~~~~
|
||||
|
||||
error: octal-looking escape in string literal
|
||||
--> tests/ui/octal_escapes.rs:20:17
|
||||
error: octal-looking escape in a literal
|
||||
--> tests/ui/octal_escapes.rs:14:34
|
||||
|
|
||||
LL | let _bad7 = "EvenMoreText-\01\02-ShortEscapes";
|
||||
| ^^^
|
||||
|
|
||||
help: if an octal escape is intended, use a hex escape instead
|
||||
|
|
||||
LL | let _bad7 = "EvenMoreText-\01\x02-ShortEscapes";
|
||||
| ~~~~
|
||||
help: if a null escape is intended, disambiguate using
|
||||
|
|
||||
LL | let _bad7 = "EvenMoreText-\01\x0002-ShortEscapes";
|
||||
| ~~~~~~
|
||||
|
||||
error: octal-looking escape in a literal
|
||||
--> tests/ui/octal_escapes.rs:17:19
|
||||
|
|
||||
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
|
||||
help: if an octal escape is intended, use a hex escape instead
|
||||
|
|
||||
LL | let _bad8 = "锈\x01锈";
|
||||
| ~~~~~~~~~~
|
||||
help: if the null character is intended, disambiguate using
|
||||
| ~~~~
|
||||
help: if a null escape is intended, disambiguate using
|
||||
|
|
||||
LL | let _bad8 = "锈\x001锈";
|
||||
| ~~~~~~~~~~~
|
||||
LL | let _bad8 = "锈\x0001锈";
|
||||
| ~~~~~~
|
||||
|
||||
error: octal-looking escape in string literal
|
||||
--> tests/ui/octal_escapes.rs:22:17
|
||||
error: octal-looking escape in a literal
|
||||
--> tests/ui/octal_escapes.rs:18:19
|
||||
|
|
||||
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
|
||||
help: if an octal escape is intended, use a hex escape instead
|
||||
|
|
||||
LL | let _bad9 = "锈\x09锈";
|
||||
| ~~~~~~~~~~
|
||||
help: if the null character is intended, disambiguate using
|
||||
| ~~~~
|
||||
help: if a null escape is intended, disambiguate using
|
||||
|
|
||||
LL | let _bad9 = "锈\x0011锈";
|
||||
| ~~~~~~~~~~~~
|
||||
| ~~~~~~
|
||||
|
||||
error: aborting due to 9 previous errors
|
||||
error: aborting due to 11 previous errors
|
||||
|
||||
|
|
Loading…
Reference in a new issue