Auto merge of #7878 - rust-lang:string-slice, r=giraffate
new lint: string-slice This is a restriction lint to highlight code that should have tests containing non-ascii characters. See #6623. changelog: new lint: [`string-slice`]
This commit is contained in:
commit
075996efd7
@ -3147,6 +3147,7 @@ Released 2018-09-13
|
|||||||
[`string_extend_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_extend_chars
|
[`string_extend_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_extend_chars
|
||||||
[`string_from_utf8_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_from_utf8_as_bytes
|
[`string_from_utf8_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_from_utf8_as_bytes
|
||||||
[`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes
|
[`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes
|
||||||
|
[`string_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_slice
|
||||||
[`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string
|
[`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string
|
||||||
[`strlen_on_c_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#strlen_on_c_strings
|
[`strlen_on_c_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#strlen_on_c_strings
|
||||||
[`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools
|
[`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools
|
||||||
|
@ -431,6 +431,7 @@
|
|||||||
strings::STRING_ADD_ASSIGN,
|
strings::STRING_ADD_ASSIGN,
|
||||||
strings::STRING_FROM_UTF8_AS_BYTES,
|
strings::STRING_FROM_UTF8_AS_BYTES,
|
||||||
strings::STRING_LIT_AS_BYTES,
|
strings::STRING_LIT_AS_BYTES,
|
||||||
|
strings::STRING_SLICE,
|
||||||
strings::STRING_TO_STRING,
|
strings::STRING_TO_STRING,
|
||||||
strings::STR_TO_STRING,
|
strings::STR_TO_STRING,
|
||||||
strlen_on_c_strings::STRLEN_ON_C_STRINGS,
|
strlen_on_c_strings::STRLEN_ON_C_STRINGS,
|
||||||
|
@ -53,6 +53,7 @@
|
|||||||
LintId::of(shadow::SHADOW_SAME),
|
LintId::of(shadow::SHADOW_SAME),
|
||||||
LintId::of(shadow::SHADOW_UNRELATED),
|
LintId::of(shadow::SHADOW_UNRELATED),
|
||||||
LintId::of(strings::STRING_ADD),
|
LintId::of(strings::STRING_ADD),
|
||||||
|
LintId::of(strings::STRING_SLICE),
|
||||||
LintId::of(strings::STRING_TO_STRING),
|
LintId::of(strings::STRING_TO_STRING),
|
||||||
LintId::of(strings::STR_TO_STRING),
|
LintId::of(strings::STR_TO_STRING),
|
||||||
LintId::of(types::RC_BUFFER),
|
LintId::of(types::RC_BUFFER),
|
||||||
|
@ -107,51 +107,87 @@
|
|||||||
"calling `as_bytes` on a string literal instead of using a byte string literal"
|
"calling `as_bytes` on a string literal instead of using a byte string literal"
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_lint_pass!(StringAdd => [STRING_ADD, STRING_ADD_ASSIGN]);
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Checks for slice operations on strings
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// UTF-8 characters span multiple bytes, and it is easy to inadvertently confuse character
|
||||||
|
/// counts and string indices. This may lead to panics, and should warrant some test cases
|
||||||
|
/// containing wide UTF-8 characters. This lint is most useful in code that should avoid
|
||||||
|
/// panics at all costs.
|
||||||
|
///
|
||||||
|
/// ### Known problems
|
||||||
|
/// Probably lots of false positives. If an index comes from a known valid position (e.g.
|
||||||
|
/// obtained via `char_indices` over the same string), it is totally OK.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```rust,should_panic
|
||||||
|
/// &"Ölkanne"[1..];
|
||||||
|
/// ```
|
||||||
|
pub STRING_SLICE,
|
||||||
|
restriction,
|
||||||
|
"slicing a string"
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_lint_pass!(StringAdd => [STRING_ADD, STRING_ADD_ASSIGN, STRING_SLICE]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for StringAdd {
|
impl<'tcx> LateLintPass<'tcx> for StringAdd {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||||
if in_external_macro(cx.sess(), e.span) {
|
if in_external_macro(cx.sess(), e.span) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
match e.kind {
|
||||||
if let ExprKind::Binary(
|
ExprKind::Binary(
|
||||||
Spanned {
|
Spanned {
|
||||||
node: BinOpKind::Add, ..
|
node: BinOpKind::Add, ..
|
||||||
},
|
},
|
||||||
left,
|
left,
|
||||||
_,
|
_,
|
||||||
) = e.kind
|
) => {
|
||||||
{
|
if is_string(cx, left) {
|
||||||
if is_string(cx, left) {
|
if !is_lint_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) {
|
||||||
if !is_lint_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) {
|
let parent = get_parent_expr(cx, e);
|
||||||
let parent = get_parent_expr(cx, e);
|
if let Some(p) = parent {
|
||||||
if let Some(p) = parent {
|
if let ExprKind::Assign(target, _, _) = p.kind {
|
||||||
if let ExprKind::Assign(target, _, _) = p.kind {
|
// avoid duplicate matches
|
||||||
// avoid duplicate matches
|
if SpanlessEq::new(cx).eq_expr(target, left) {
|
||||||
if SpanlessEq::new(cx).eq_expr(target, left) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
span_lint(
|
||||||
|
cx,
|
||||||
|
STRING_ADD,
|
||||||
|
e.span,
|
||||||
|
"you added something to a string. Consider using `String::push_str()` instead",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
span_lint(
|
},
|
||||||
cx,
|
ExprKind::Assign(target, src, _) => {
|
||||||
STRING_ADD,
|
if is_string(cx, target) && is_add(cx, src, target) {
|
||||||
e.span,
|
span_lint(
|
||||||
"you added something to a string. Consider using `String::push_str()` instead",
|
cx,
|
||||||
);
|
STRING_ADD_ASSIGN,
|
||||||
}
|
e.span,
|
||||||
} else if let ExprKind::Assign(target, src, _) = e.kind {
|
"you assigned the result of adding something to this string. Consider using \
|
||||||
if is_string(cx, target) && is_add(cx, src, target) {
|
`String::push_str()` instead",
|
||||||
span_lint(
|
);
|
||||||
cx,
|
}
|
||||||
STRING_ADD_ASSIGN,
|
},
|
||||||
e.span,
|
ExprKind::Index(target, _idx) => {
|
||||||
"you assigned the result of adding something to this string. Consider using \
|
let e_ty = cx.typeck_results().expr_ty(target).peel_refs();
|
||||||
`String::push_str()` instead",
|
if matches!(e_ty.kind(), ty::Str) || is_type_diagnostic_item(cx, e_ty, sym::String) {
|
||||||
);
|
span_lint(
|
||||||
}
|
cx,
|
||||||
|
STRING_SLICE,
|
||||||
|
e.span,
|
||||||
|
"indexing into a string may panic if the index is within a UTF-8 character",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10
tests/ui/string_slice.rs
Normal file
10
tests/ui/string_slice.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#[warn(clippy::string_slice)]
|
||||||
|
#[allow(clippy::no_effect)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
&"Ölkanne"[1..];
|
||||||
|
let m = "Mötörhead";
|
||||||
|
&m[2..5];
|
||||||
|
let s = String::from(m);
|
||||||
|
&s[0..2];
|
||||||
|
}
|
22
tests/ui/string_slice.stderr
Normal file
22
tests/ui/string_slice.stderr
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
error: indexing into a string may panic if the index is within a UTF-8 character
|
||||||
|
--> $DIR/string_slice.rs:5:6
|
||||||
|
|
|
||||||
|
LL | &"Ölkanne"[1..];
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `-D clippy::string-slice` implied by `-D warnings`
|
||||||
|
|
||||||
|
error: indexing into a string may panic if the index is within a UTF-8 character
|
||||||
|
--> $DIR/string_slice.rs:7:6
|
||||||
|
|
|
||||||
|
LL | &m[2..5];
|
||||||
|
| ^^^^^^^
|
||||||
|
|
||||||
|
error: indexing into a string may panic if the index is within a UTF-8 character
|
||||||
|
--> $DIR/string_slice.rs:9:6
|
||||||
|
|
|
||||||
|
LL | &s[0..2];
|
||||||
|
| ^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
Loading…
Reference in New Issue
Block a user