Skip to content

Commit 96e2bc8

Browse files
committed
Added lint match_vec_item
1 parent 1d4dd3d commit 96e2bc8

File tree

6 files changed

+260
-0
lines changed

6 files changed

+260
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1352,6 +1352,7 @@ Released 2018-09-13
13521352
[`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats
13531353
[`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms
13541354
[`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding
1355+
[`match_vec_item`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_vec_item
13551356
[`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm
13561357
[`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter
13571358
[`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum

clippy_lints/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ mod macro_use;
249249
mod main_recursion;
250250
mod map_clone;
251251
mod map_unit_fn;
252+
mod match_vec_item;
252253
mod matches;
253254
mod mem_discriminant;
254255
mod mem_forget;
@@ -629,6 +630,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
629630
&map_clone::MAP_CLONE,
630631
&map_unit_fn::OPTION_MAP_UNIT_FN,
631632
&map_unit_fn::RESULT_MAP_UNIT_FN,
633+
&match_vec_item::MATCH_VEC_ITEM,
632634
&matches::INFALLIBLE_DESTRUCTURING_MATCH,
633635
&matches::MATCH_AS_REF,
634636
&matches::MATCH_BOOL,
@@ -1060,6 +1062,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10601062
store.register_late_pass(|| box future_not_send::FutureNotSend);
10611063
store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls);
10621064
store.register_late_pass(|| box if_let_mutex::IfLetMutex);
1065+
store.register_late_pass(|| box match_vec_item::MatchVecItem);
10631066

10641067
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
10651068
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -1277,6 +1280,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
12771280
LintId::of(&map_clone::MAP_CLONE),
12781281
LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
12791282
LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN),
1283+
LintId::of(&match_vec_item::MATCH_VEC_ITEM),
12801284
LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH),
12811285
LintId::of(&matches::MATCH_AS_REF),
12821286
LintId::of(&matches::MATCH_BOOL),
@@ -1469,6 +1473,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
14691473
LintId::of(&loops::WHILE_LET_ON_ITERATOR),
14701474
LintId::of(&main_recursion::MAIN_RECURSION),
14711475
LintId::of(&map_clone::MAP_CLONE),
1476+
LintId::of(&match_vec_item::MATCH_VEC_ITEM),
14721477
LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH),
14731478
LintId::of(&matches::MATCH_BOOL),
14741479
LintId::of(&matches::MATCH_OVERLAPPING_ARM),

clippy_lints/src/match_vec_item.rs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
use crate::utils::{is_wild, span_lint_and_help};
2+
use if_chain::if_chain;
3+
use rustc_hir::{Arm, Expr, ExprKind, MatchSource};
4+
use rustc_lint::{LateContext, LateLintPass, LintContext};
5+
use rustc_middle::lint::in_external_macro;
6+
use rustc_middle::ty::{self, AdtDef};
7+
use rustc_session::{declare_lint_pass, declare_tool_lint};
8+
9+
declare_clippy_lint! {
10+
/// **What it does:** Checks for `match vec[idx]` or `match vec[n..m]`.
11+
///
12+
/// **Why is this bad?** This can panic at runtime.
13+
///
14+
/// **Known problems:** None.
15+
///
16+
/// **Example:**
17+
/// ```rust, no_run
18+
/// let arr = vec![0, 1, 2, 3];
19+
/// let idx = 1;
20+
///
21+
/// // Bad
22+
/// match arr[idx] {
23+
/// 0 => println!("{}", 0),
24+
/// 1 => println!("{}", 3),
25+
/// _ => {},
26+
/// }
27+
/// ```
28+
/// Use instead:
29+
/// ```rust, no_run
30+
/// let arr = vec![0, 1, 2, 3];
31+
/// let idx = 1;
32+
///
33+
/// // Good
34+
/// match arr.get(idx) {
35+
/// Some(0) => println!("{}", 0),
36+
/// Some(1) => println!("{}", 3),
37+
/// _ => {},
38+
/// }
39+
/// ```
40+
pub MATCH_VEC_ITEM,
41+
style,
42+
"match vector by indexing can panic"
43+
}
44+
45+
declare_lint_pass!(MatchVecItem => [MATCH_VEC_ITEM]);
46+
47+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MatchVecItem {
48+
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) {
49+
if_chain! {
50+
if !in_external_macro(cx.sess(), expr.span);
51+
if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind;
52+
if contains_wild_arm(arms);
53+
if is_vec_indexing(cx, ex);
54+
55+
then {
56+
span_lint_and_help(
57+
cx,
58+
MATCH_VEC_ITEM,
59+
expr.span,
60+
"indexing vector may panic",
61+
None,
62+
"consider using `get(..)` instead.",
63+
);
64+
}
65+
}
66+
}
67+
}
68+
69+
fn contains_wild_arm(arms: &[Arm<'_>]) -> bool {
70+
arms.iter().any(|arm| is_wild(&arm.pat) && is_unit_expr(arm.body))
71+
}
72+
73+
fn is_vec_indexing<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
74+
if_chain! {
75+
if let ExprKind::Index(ref array, _) = expr.kind;
76+
let ty = cx.tables.expr_ty(array);
77+
if let ty::Adt(def, _) = ty.kind;
78+
if is_vec(cx, def);
79+
80+
then {
81+
return true;
82+
}
83+
}
84+
85+
false
86+
}
87+
88+
fn is_vec<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, def: &'a AdtDef) -> bool {
89+
if_chain! {
90+
let def_path = cx.tcx.def_path(def.did);
91+
if def_path.data.len() == 2;
92+
if let Some(module) = def_path.data.get(0);
93+
if module.data.as_symbol() == sym!(vec);
94+
if let Some(name) = def_path.data.get(1);
95+
if name.data.as_symbol() == sym!(Vec);
96+
97+
then {
98+
return true;
99+
}
100+
}
101+
102+
false
103+
}
104+
105+
fn is_unit_expr(expr: &Expr<'_>) -> bool {
106+
match expr.kind {
107+
ExprKind::Tup(ref v) if v.is_empty() => true,
108+
ExprKind::Block(ref b, _) if b.stmts.is_empty() && b.expr.is_none() => true,
109+
_ => false,
110+
}
111+
}

src/lintlist/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,6 +1172,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
11721172
deprecation: None,
11731173
module: "matches",
11741174
},
1175+
Lint {
1176+
name: "match_vec_item",
1177+
group: "style",
1178+
desc: "match vector by indexing can panic",
1179+
deprecation: None,
1180+
module: "match_vec_item",
1181+
},
11751182
Lint {
11761183
name: "match_wild_err_arm",
11771184
group: "style",

tests/ui/match_vec_item.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#![warn(clippy::match_vec_item)]
2+
3+
fn main() {
4+
match_with_wildcard();
5+
match_without_wildcard();
6+
match_wildcard_and_action();
7+
match_with_get();
8+
match_with_array();
9+
}
10+
11+
fn match_with_wildcard() {
12+
let arr = vec![0, 1, 2, 3];
13+
let range = 1..3;
14+
let idx = 1;
15+
16+
// Lint, may panic
17+
match arr[idx] {
18+
0 => println!("0"),
19+
1 => println!("1"),
20+
_ => {},
21+
}
22+
23+
// Lint, may panic
24+
match arr[range] {
25+
[0, 1] => println!("0 1"),
26+
[1, 2] => println!("1 2"),
27+
_ => {},
28+
}
29+
}
30+
31+
fn match_without_wildcard() {
32+
let arr = vec![0, 1, 2, 3];
33+
let range = 1..3;
34+
let idx = 2;
35+
36+
// Ok
37+
match arr[idx] {
38+
0 => println!("0"),
39+
1 => println!("1"),
40+
num => {},
41+
}
42+
43+
// Ok
44+
match arr[range] {
45+
[0, 1] => println!("0 1"),
46+
[1, 2] => println!("1 2"),
47+
[ref sub @ ..] => {},
48+
}
49+
}
50+
51+
fn match_wildcard_and_action() {
52+
let arr = vec![0, 1, 2, 3];
53+
let range = 1..3;
54+
let idx = 3;
55+
56+
// Ok
57+
match arr[idx] {
58+
0 => println!("0"),
59+
1 => println!("1"),
60+
_ => println!("Hello, World!"),
61+
}
62+
63+
// Ok
64+
match arr[range] {
65+
[0, 1] => println!("0 1"),
66+
[1, 2] => println!("1 2"),
67+
_ => println!("Hello, World!"),
68+
}
69+
}
70+
71+
fn match_with_get() {
72+
let arr = vec![0, 1, 2, 3];
73+
let range = 1..3;
74+
let idx = 3;
75+
76+
// Ok
77+
match arr.get(idx) {
78+
Some(0) => println!("0"),
79+
Some(1) => println!("1"),
80+
_ => {},
81+
}
82+
83+
// Ok
84+
match arr.get(range) {
85+
Some(&[0, 1]) => println!("0 1"),
86+
Some(&[1, 2]) => println!("1 2"),
87+
_ => {},
88+
}
89+
}
90+
91+
fn match_with_array() {
92+
let arr = [0, 1, 2, 3];
93+
let range = 1..3;
94+
let idx = 3;
95+
96+
// Ok
97+
match arr[idx] {
98+
0 => println!("0"),
99+
1 => println!("1"),
100+
_ => {},
101+
}
102+
103+
// Ok
104+
match arr[range] {
105+
[0, 1] => println!("0 1"),
106+
[1, 2] => println!("1 2"),
107+
_ => {},
108+
}
109+
}

tests/ui/match_vec_item.stderr

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error: indexing vector may panic
2+
--> $DIR/match_vec_item.rs:17:5
3+
|
4+
LL | / match arr[idx] {
5+
LL | | 0 => println!("0"),
6+
LL | | 1 => println!("1"),
7+
LL | | _ => {},
8+
LL | | }
9+
| |_____^
10+
|
11+
= note: `-D clippy::match-vec-item` implied by `-D warnings`
12+
= help: consider using `get(..)` instead.
13+
14+
error: indexing vector may panic
15+
--> $DIR/match_vec_item.rs:24:5
16+
|
17+
LL | / match arr[range] {
18+
LL | | [0, 1] => println!("0 1"),
19+
LL | | [1, 2] => println!("1 2"),
20+
LL | | _ => {},
21+
LL | | }
22+
| |_____^
23+
|
24+
= help: consider using `get(..)` instead.
25+
26+
error: aborting due to 2 previous errors
27+

0 commit comments

Comments
 (0)