Added lint match_vec_item

This commit is contained in:
CrazyRoka 2020-04-24 00:28:18 +03:00 committed by CrazyRoka
parent 1d4dd3d428
commit 96e2bc80f5
6 changed files with 260 additions and 0 deletions

View file

@ -1352,6 +1352,7 @@ Released 2018-09-13
[`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats [`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats
[`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms [`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms
[`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding [`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding
[`match_vec_item`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_vec_item
[`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm [`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm
[`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter [`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter
[`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum [`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum

View file

@ -249,6 +249,7 @@ mod macro_use;
mod main_recursion; mod main_recursion;
mod map_clone; mod map_clone;
mod map_unit_fn; mod map_unit_fn;
mod match_vec_item;
mod matches; mod matches;
mod mem_discriminant; mod mem_discriminant;
mod mem_forget; mod mem_forget;
@ -629,6 +630,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&map_clone::MAP_CLONE, &map_clone::MAP_CLONE,
&map_unit_fn::OPTION_MAP_UNIT_FN, &map_unit_fn::OPTION_MAP_UNIT_FN,
&map_unit_fn::RESULT_MAP_UNIT_FN, &map_unit_fn::RESULT_MAP_UNIT_FN,
&match_vec_item::MATCH_VEC_ITEM,
&matches::INFALLIBLE_DESTRUCTURING_MATCH, &matches::INFALLIBLE_DESTRUCTURING_MATCH,
&matches::MATCH_AS_REF, &matches::MATCH_AS_REF,
&matches::MATCH_BOOL, &matches::MATCH_BOOL,
@ -1060,6 +1062,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| box future_not_send::FutureNotSend); store.register_late_pass(|| box future_not_send::FutureNotSend);
store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls);
store.register_late_pass(|| box if_let_mutex::IfLetMutex); store.register_late_pass(|| box if_let_mutex::IfLetMutex);
store.register_late_pass(|| box match_vec_item::MatchVecItem);
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(&arithmetic::FLOAT_ARITHMETIC), LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@ -1277,6 +1280,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&map_clone::MAP_CLONE), LintId::of(&map_clone::MAP_CLONE),
LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN),
LintId::of(&match_vec_item::MATCH_VEC_ITEM),
LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH),
LintId::of(&matches::MATCH_AS_REF), LintId::of(&matches::MATCH_AS_REF),
LintId::of(&matches::MATCH_BOOL), LintId::of(&matches::MATCH_BOOL),
@ -1469,6 +1473,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&loops::WHILE_LET_ON_ITERATOR), LintId::of(&loops::WHILE_LET_ON_ITERATOR),
LintId::of(&main_recursion::MAIN_RECURSION), LintId::of(&main_recursion::MAIN_RECURSION),
LintId::of(&map_clone::MAP_CLONE), LintId::of(&map_clone::MAP_CLONE),
LintId::of(&match_vec_item::MATCH_VEC_ITEM),
LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH),
LintId::of(&matches::MATCH_BOOL), LintId::of(&matches::MATCH_BOOL),
LintId::of(&matches::MATCH_OVERLAPPING_ARM), LintId::of(&matches::MATCH_OVERLAPPING_ARM),

View file

@ -0,0 +1,111 @@
use crate::utils::{is_wild, span_lint_and_help};
use if_chain::if_chain;
use rustc_hir::{Arm, Expr, ExprKind, MatchSource};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, AdtDef};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// **What it does:** Checks for `match vec[idx]` or `match vec[n..m]`.
///
/// **Why is this bad?** This can panic at runtime.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust, no_run
/// let arr = vec![0, 1, 2, 3];
/// let idx = 1;
///
/// // Bad
/// match arr[idx] {
/// 0 => println!("{}", 0),
/// 1 => println!("{}", 3),
/// _ => {},
/// }
/// ```
/// Use instead:
/// ```rust, no_run
/// let arr = vec![0, 1, 2, 3];
/// let idx = 1;
///
/// // Good
/// match arr.get(idx) {
/// Some(0) => println!("{}", 0),
/// Some(1) => println!("{}", 3),
/// _ => {},
/// }
/// ```
pub MATCH_VEC_ITEM,
style,
"match vector by indexing can panic"
}
declare_lint_pass!(MatchVecItem => [MATCH_VEC_ITEM]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MatchVecItem {
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) {
if_chain! {
if !in_external_macro(cx.sess(), expr.span);
if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind;
if contains_wild_arm(arms);
if is_vec_indexing(cx, ex);
then {
span_lint_and_help(
cx,
MATCH_VEC_ITEM,
expr.span,
"indexing vector may panic",
None,
"consider using `get(..)` instead.",
);
}
}
}
}
fn contains_wild_arm(arms: &[Arm<'_>]) -> bool {
arms.iter().any(|arm| is_wild(&arm.pat) && is_unit_expr(arm.body))
}
fn is_vec_indexing<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
if_chain! {
if let ExprKind::Index(ref array, _) = expr.kind;
let ty = cx.tables.expr_ty(array);
if let ty::Adt(def, _) = ty.kind;
if is_vec(cx, def);
then {
return true;
}
}
false
}
fn is_vec<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, def: &'a AdtDef) -> bool {
if_chain! {
let def_path = cx.tcx.def_path(def.did);
if def_path.data.len() == 2;
if let Some(module) = def_path.data.get(0);
if module.data.as_symbol() == sym!(vec);
if let Some(name) = def_path.data.get(1);
if name.data.as_symbol() == sym!(Vec);
then {
return true;
}
}
false
}
fn is_unit_expr(expr: &Expr<'_>) -> bool {
match expr.kind {
ExprKind::Tup(ref v) if v.is_empty() => true,
ExprKind::Block(ref b, _) if b.stmts.is_empty() && b.expr.is_none() => true,
_ => false,
}
}

View file

@ -1172,6 +1172,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None, deprecation: None,
module: "matches", module: "matches",
}, },
Lint {
name: "match_vec_item",
group: "style",
desc: "match vector by indexing can panic",
deprecation: None,
module: "match_vec_item",
},
Lint { Lint {
name: "match_wild_err_arm", name: "match_wild_err_arm",
group: "style", group: "style",

109
tests/ui/match_vec_item.rs Normal file
View file

@ -0,0 +1,109 @@
#![warn(clippy::match_vec_item)]
fn main() {
match_with_wildcard();
match_without_wildcard();
match_wildcard_and_action();
match_with_get();
match_with_array();
}
fn match_with_wildcard() {
let arr = vec![0, 1, 2, 3];
let range = 1..3;
let idx = 1;
// Lint, may panic
match arr[idx] {
0 => println!("0"),
1 => println!("1"),
_ => {},
}
// Lint, may panic
match arr[range] {
[0, 1] => println!("0 1"),
[1, 2] => println!("1 2"),
_ => {},
}
}
fn match_without_wildcard() {
let arr = vec![0, 1, 2, 3];
let range = 1..3;
let idx = 2;
// Ok
match arr[idx] {
0 => println!("0"),
1 => println!("1"),
num => {},
}
// Ok
match arr[range] {
[0, 1] => println!("0 1"),
[1, 2] => println!("1 2"),
[ref sub @ ..] => {},
}
}
fn match_wildcard_and_action() {
let arr = vec![0, 1, 2, 3];
let range = 1..3;
let idx = 3;
// Ok
match arr[idx] {
0 => println!("0"),
1 => println!("1"),
_ => println!("Hello, World!"),
}
// Ok
match arr[range] {
[0, 1] => println!("0 1"),
[1, 2] => println!("1 2"),
_ => println!("Hello, World!"),
}
}
fn match_with_get() {
let arr = vec![0, 1, 2, 3];
let range = 1..3;
let idx = 3;
// Ok
match arr.get(idx) {
Some(0) => println!("0"),
Some(1) => println!("1"),
_ => {},
}
// Ok
match arr.get(range) {
Some(&[0, 1]) => println!("0 1"),
Some(&[1, 2]) => println!("1 2"),
_ => {},
}
}
fn match_with_array() {
let arr = [0, 1, 2, 3];
let range = 1..3;
let idx = 3;
// Ok
match arr[idx] {
0 => println!("0"),
1 => println!("1"),
_ => {},
}
// Ok
match arr[range] {
[0, 1] => println!("0 1"),
[1, 2] => println!("1 2"),
_ => {},
}
}

View file

@ -0,0 +1,27 @@
error: indexing vector may panic
--> $DIR/match_vec_item.rs:17:5
|
LL | / match arr[idx] {
LL | | 0 => println!("0"),
LL | | 1 => println!("1"),
LL | | _ => {},
LL | | }
| |_____^
|
= note: `-D clippy::match-vec-item` implied by `-D warnings`
= help: consider using `get(..)` instead.
error: indexing vector may panic
--> $DIR/match_vec_item.rs:24:5
|
LL | / match arr[range] {
LL | | [0, 1] => println!("0 1"),
LL | | [1, 2] => println!("1 2"),
LL | | _ => {},
LL | | }
| |_____^
|
= help: consider using `get(..)` instead.
error: aborting due to 2 previous errors