Skip to content

Commit b87d42c

Browse files
committed
Add a new lint
1 parent e6f3390 commit b87d42c

File tree

4 files changed

+93
-0
lines changed

4 files changed

+93
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5279,6 +5279,7 @@ Released 2018-09-13
52795279
[`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none
52805280
[`mem_replace_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default
52815281
[`mem_replace_with_uninit`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_uninit
5282+
[`might_panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#might_panic
52825283
[`min_ident_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_ident_chars
52835284
[`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max
52845285
[`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
451451
crate::methods::WAKER_CLONE_WAKE_INFO,
452452
crate::methods::WRONG_SELF_CONVENTION_INFO,
453453
crate::methods::ZST_OFFSET_INFO,
454+
crate::might_panic::MIGHT_PANIC_INFO,
454455
crate::min_ident_chars::MIN_IDENT_CHARS_INFO,
455456
crate::minmax::MIN_MAX_INFO,
456457
crate::misc::SHORT_CIRCUIT_STATEMENT_INFO,

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ mod match_result_ok;
204204
mod matches;
205205
mod mem_replace;
206206
mod methods;
207+
mod might_panic;
207208
mod min_ident_chars;
208209
mod minmax;
209210
mod misc;
@@ -1069,6 +1070,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
10691070
store.register_late_pass(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter));
10701071
store.register_late_pass(|_| Box::new(iter_over_hash_type::IterOverHashType));
10711072
store.register_late_pass(|_| Box::new(impl_hash_with_borrow_str_and_bytes::ImplHashWithBorrowStrBytes));
1073+
store.register_late_pass(|_| Box::new(might_panic::MightPanic));
10721074
// add lints here, do not remove this comment, it's used in `new_lint`
10731075
}
10741076

clippy_lints/src/might_panic.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use clippy_utils::source::snippet_opt;
3+
use rustc_ast::ast::LitKind;
4+
use rustc_errors::Applicability;
5+
use rustc_hir as hir;
6+
use rustc_lint::{LateContext, LateLintPass};
7+
use rustc_session::declare_lint_pass;
8+
9+
declare_clippy_lint! {
10+
/// ### What it does
11+
/// replace accessing array with an `idx`` by a `get(idx)`.
12+
///
13+
/// ### Why is this bad?
14+
/// it's easier to locate error where you access array when
15+
/// out of boundary; and you can write your error handle to
16+
/// solve this problem.
17+
///
18+
/// ### Example
19+
/// ```no_run
20+
/// let numbers = &[4, 7, 3];
21+
/// let my_number = numbers[5];
22+
/// ```
23+
/// Use instead:
24+
/// ```no_run
25+
/// let numbers = &[4, 7, 3];
26+
/// let Some(my_number) = numbers.get(5) else {
27+
/// // handle
28+
/// };
29+
/// ```
30+
#[clippy::version = "1.76.0"]
31+
pub MIGHT_PANIC,
32+
style,
33+
"use `get()` function if you're not sure whether index is legal"
34+
}
35+
36+
declare_lint_pass!(MightPanic => [MIGHT_PANIC]);
37+
38+
impl LateLintPass<'_> for MightPanic {
39+
fn check_stmt<'tcx>(&mut self, cx: &LateContext<'tcx>, stmt: &hir::Stmt<'tcx>) {
40+
// pattern matching an `let` stmt satisfy form like `let x: ty = arr[idx];`
41+
if let hir::StmtKind::Local(hir::Local {
42+
pat,
43+
ty,
44+
init,
45+
els: _,
46+
hir_id: _,
47+
span,
48+
source: _,
49+
}) = &stmt.kind
50+
&& let hir::PatKind::Binding(_, _, ident, _) = pat.kind
51+
&& let Some(expr) = init
52+
&& let hir::ExprKind::Index(arr, idx, _) = &expr.kind
53+
&& let hir::ExprKind::Path(_) = arr.kind
54+
&& let hir::ExprKind::Lit(spanned) = idx.kind
55+
&& let LitKind::Int(v, _) = spanned.node
56+
{
57+
// get type info string by stmt.local.ty.span
58+
// may not have an explict type, it doesn't matter
59+
let mut ty_str = String::new();
60+
if let Some(hir::Ty {
61+
hir_id: _,
62+
kind: _,
63+
span: ty_span,
64+
}) = ty
65+
&& let Some(snip) = snippet_opt(cx, *ty_span)
66+
{
67+
ty_str = snip;
68+
}
69+
let span = *span;
70+
span_lint_and_then(
71+
cx,
72+
MIGHT_PANIC,
73+
span,
74+
"use `get()` function if you're not sure whether index is legal",
75+
|diag| {
76+
if let Some(snip) = snippet_opt(cx, arr.span) {
77+
// format sugg string
78+
let sugg = if ty_str.is_empty() {
79+
format!("let Some({ident}) = {snip}.get({v}) else {{ panic!() }};")
80+
} else {
81+
format!("let Some({ident}): Option<&{ty_str}> = {snip}.get({v}) else {{ panic!() }};")
82+
};
83+
diag.span_suggestion(span, "Try", sugg, Applicability::MachineApplicable);
84+
}
85+
},
86+
);
87+
}
88+
}
89+
}

0 commit comments

Comments
 (0)