Auto merge of #6226 - Urcra:master, r=flip1995
Add lint for comparing to empty slices instead of using .is_empty() Hey first time making a clippy lint I added the implementation of the lint the `len_zero` since it shared a lot of the code, I would otherwise have to rewrite. Just tell me if the lint should use it's own file instead changelog: Add lint for comparing to empty slices Fixes #6217
This commit is contained in:
commit
e1a2845558
@ -1665,6 +1665,7 @@ Released 2018-09-13
|
|||||||
[`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
|
[`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
|
||||||
[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
|
[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
|
||||||
[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
|
[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
|
||||||
|
[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
|
||||||
[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
|
[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
|
||||||
[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir
|
[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir
|
||||||
[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
|
[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
|
||||||
|
@ -68,7 +68,44 @@
|
|||||||
"traits or impls with a public `len` method but no corresponding `is_empty` method"
|
"traits or impls with a public `len` method but no corresponding `is_empty` method"
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY]);
|
declare_clippy_lint! {
|
||||||
|
/// **What it does:** Checks for comparing to an empty slice such as "" or [],`
|
||||||
|
/// and suggests using `.is_empty()` where applicable.
|
||||||
|
///
|
||||||
|
/// **Why is this bad?** Some structures can answer `.is_empty()` much faster
|
||||||
|
/// than checking for equality. So it is good to get into the habit of using
|
||||||
|
/// `.is_empty()`, and having it is cheap.
|
||||||
|
/// Besides, it makes the intent clearer than a manual comparison in some contexts.
|
||||||
|
///
|
||||||
|
/// **Known problems:** None.
|
||||||
|
///
|
||||||
|
/// **Example:**
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// if s == "" {
|
||||||
|
/// ..
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// if arr == [] {
|
||||||
|
/// ..
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```ignore
|
||||||
|
/// if s.is_empty() {
|
||||||
|
/// ..
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// if arr.is_empty() {
|
||||||
|
/// ..
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub COMPARISON_TO_EMPTY,
|
||||||
|
style,
|
||||||
|
"checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead"
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMPTY]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for LenZero {
|
impl<'tcx> LateLintPass<'tcx> for LenZero {
|
||||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||||
@ -221,6 +258,8 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>
|
|||||||
}
|
}
|
||||||
|
|
||||||
check_len(cx, span, method_path.ident.name, args, &lit.node, op, compare_to)
|
check_len(cx, span, method_path.ident.name, args, &lit.node, op, compare_to)
|
||||||
|
} else {
|
||||||
|
check_empty_expr(cx, span, method, lit, op)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,6 +297,42 @@ fn check_len(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str) {
|
||||||
|
if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) {
|
||||||
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
COMPARISON_TO_EMPTY,
|
||||||
|
span,
|
||||||
|
"comparison to empty slice",
|
||||||
|
&format!("using `{}is_empty` is clearer and more explicit", op),
|
||||||
|
format!(
|
||||||
|
"{}{}.is_empty()",
|
||||||
|
op,
|
||||||
|
snippet_with_applicability(cx, lit1.span, "_", &mut applicability)
|
||||||
|
),
|
||||||
|
applicability,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_empty_string(expr: &Expr<'_>) -> bool {
|
||||||
|
if let ExprKind::Lit(ref lit) = expr.kind {
|
||||||
|
if let LitKind::Str(lit, _) = lit.node {
|
||||||
|
let lit = lit.as_str();
|
||||||
|
return lit == "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_empty_array(expr: &Expr<'_>) -> bool {
|
||||||
|
if let ExprKind::Array(ref arr) = expr.kind {
|
||||||
|
return arr.is_empty();
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks if this type has an `is_empty` method.
|
/// Checks if this type has an `is_empty` method.
|
||||||
fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||||
/// Gets an `AssocItem` and return true if it matches `is_empty(self)`.
|
/// Gets an `AssocItem` and return true if it matches `is_empty(self)`.
|
||||||
|
@ -614,6 +614,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||||||
&large_const_arrays::LARGE_CONST_ARRAYS,
|
&large_const_arrays::LARGE_CONST_ARRAYS,
|
||||||
&large_enum_variant::LARGE_ENUM_VARIANT,
|
&large_enum_variant::LARGE_ENUM_VARIANT,
|
||||||
&large_stack_arrays::LARGE_STACK_ARRAYS,
|
&large_stack_arrays::LARGE_STACK_ARRAYS,
|
||||||
|
&len_zero::COMPARISON_TO_EMPTY,
|
||||||
&len_zero::LEN_WITHOUT_IS_EMPTY,
|
&len_zero::LEN_WITHOUT_IS_EMPTY,
|
||||||
&len_zero::LEN_ZERO,
|
&len_zero::LEN_ZERO,
|
||||||
&let_if_seq::USELESS_LET_IF_SEQ,
|
&let_if_seq::USELESS_LET_IF_SEQ,
|
||||||
@ -1365,6 +1366,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||||||
LintId::of(&int_plus_one::INT_PLUS_ONE),
|
LintId::of(&int_plus_one::INT_PLUS_ONE),
|
||||||
LintId::of(&large_const_arrays::LARGE_CONST_ARRAYS),
|
LintId::of(&large_const_arrays::LARGE_CONST_ARRAYS),
|
||||||
LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT),
|
LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT),
|
||||||
|
LintId::of(&len_zero::COMPARISON_TO_EMPTY),
|
||||||
LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
|
LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
|
||||||
LintId::of(&len_zero::LEN_ZERO),
|
LintId::of(&len_zero::LEN_ZERO),
|
||||||
LintId::of(&let_underscore::LET_UNDERSCORE_LOCK),
|
LintId::of(&let_underscore::LET_UNDERSCORE_LOCK),
|
||||||
@ -1591,6 +1593,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||||||
LintId::of(&functions::RESULT_UNIT_ERR),
|
LintId::of(&functions::RESULT_UNIT_ERR),
|
||||||
LintId::of(&if_let_some_result::IF_LET_SOME_RESULT),
|
LintId::of(&if_let_some_result::IF_LET_SOME_RESULT),
|
||||||
LintId::of(&inherent_to_string::INHERENT_TO_STRING),
|
LintId::of(&inherent_to_string::INHERENT_TO_STRING),
|
||||||
|
LintId::of(&len_zero::COMPARISON_TO_EMPTY),
|
||||||
LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
|
LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
|
||||||
LintId::of(&len_zero::LEN_ZERO),
|
LintId::of(&len_zero::LEN_ZERO),
|
||||||
LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING),
|
LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING),
|
||||||
|
@ -298,6 +298,13 @@
|
|||||||
deprecation: None,
|
deprecation: None,
|
||||||
module: "comparison_chain",
|
module: "comparison_chain",
|
||||||
},
|
},
|
||||||
|
Lint {
|
||||||
|
name: "comparison_to_empty",
|
||||||
|
group: "style",
|
||||||
|
desc: "checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead",
|
||||||
|
deprecation: None,
|
||||||
|
module: "len_zero",
|
||||||
|
},
|
||||||
Lint {
|
Lint {
|
||||||
name: "copy_iterator",
|
name: "copy_iterator",
|
||||||
group: "pedantic",
|
group: "pedantic",
|
||||||
|
23
tests/ui/comparison_to_empty.fixed
Normal file
23
tests/ui/comparison_to_empty.fixed
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// run-rustfix
|
||||||
|
|
||||||
|
#![warn(clippy::comparison_to_empty)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Disallow comparisons to empty
|
||||||
|
let s = String::new();
|
||||||
|
let _ = s.is_empty();
|
||||||
|
let _ = !s.is_empty();
|
||||||
|
|
||||||
|
let v = vec![0];
|
||||||
|
let _ = v.is_empty();
|
||||||
|
let _ = !v.is_empty();
|
||||||
|
|
||||||
|
// Allow comparisons to non-empty
|
||||||
|
let s = String::new();
|
||||||
|
let _ = s == " ";
|
||||||
|
let _ = s != " ";
|
||||||
|
|
||||||
|
let v = vec![0];
|
||||||
|
let _ = v == [0];
|
||||||
|
let _ = v != [0];
|
||||||
|
}
|
23
tests/ui/comparison_to_empty.rs
Normal file
23
tests/ui/comparison_to_empty.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// run-rustfix
|
||||||
|
|
||||||
|
#![warn(clippy::comparison_to_empty)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Disallow comparisons to empty
|
||||||
|
let s = String::new();
|
||||||
|
let _ = s == "";
|
||||||
|
let _ = s != "";
|
||||||
|
|
||||||
|
let v = vec![0];
|
||||||
|
let _ = v == [];
|
||||||
|
let _ = v != [];
|
||||||
|
|
||||||
|
// Allow comparisons to non-empty
|
||||||
|
let s = String::new();
|
||||||
|
let _ = s == " ";
|
||||||
|
let _ = s != " ";
|
||||||
|
|
||||||
|
let v = vec![0];
|
||||||
|
let _ = v == [0];
|
||||||
|
let _ = v != [0];
|
||||||
|
}
|
28
tests/ui/comparison_to_empty.stderr
Normal file
28
tests/ui/comparison_to_empty.stderr
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
error: comparison to empty slice
|
||||||
|
--> $DIR/comparison_to_empty.rs:8:13
|
||||||
|
|
|
||||||
|
LL | let _ = s == "";
|
||||||
|
| ^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()`
|
||||||
|
|
|
||||||
|
= note: `-D clippy::comparison-to-empty` implied by `-D warnings`
|
||||||
|
|
||||||
|
error: comparison to empty slice
|
||||||
|
--> $DIR/comparison_to_empty.rs:9:13
|
||||||
|
|
|
||||||
|
LL | let _ = s != "";
|
||||||
|
| ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!s.is_empty()`
|
||||||
|
|
||||||
|
error: comparison to empty slice
|
||||||
|
--> $DIR/comparison_to_empty.rs:12:13
|
||||||
|
|
|
||||||
|
LL | let _ = v == [];
|
||||||
|
| ^^^^^^^ help: using `is_empty` is clearer and more explicit: `v.is_empty()`
|
||||||
|
|
||||||
|
error: comparison to empty slice
|
||||||
|
--> $DIR/comparison_to_empty.rs:13:13
|
||||||
|
|
|
||||||
|
LL | let _ = v != [];
|
||||||
|
| ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!v.is_empty()`
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
Loading…
Reference in New Issue
Block a user