Skip to content

Commit 3cc50a4

Browse files
committed
Auto merge of #8882 - kyoto7250:get_first, r=llogiq
feat(lint): impl lint about use first() instead of get(0) close #8851 This PR adds new lint about considering replacing .get(0) with .first(). Thank you in advance. changelog: adds new lint [`get_first`] to consider replacing .get(0) with .first()
2 parents b97784f + d0f93c1 commit 3cc50a4

20 files changed

+234
-46
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -3435,6 +3435,7 @@ Released 2018-09-13
34353435
[`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into
34363436
[`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10
34373437
[`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send
3438+
[`get_first`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_first
34383439
[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
34393440
[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
34403441
[`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion

clippy_lints/src/get_first.rs

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::source::snippet_with_applicability;
3+
use clippy_utils::{is_slice_of_primitives, match_def_path, paths};
4+
use if_chain::if_chain;
5+
use rustc_ast::LitKind;
6+
use rustc_errors::Applicability;
7+
use rustc_hir as hir;
8+
use rustc_lint::{LateContext, LateLintPass};
9+
use rustc_session::{declare_lint_pass, declare_tool_lint};
10+
use rustc_span::source_map::Spanned;
11+
12+
declare_clippy_lint! {
13+
/// ### What it does
14+
/// Checks for using `x.get(0)` instead of
15+
/// `x.first()`.
16+
///
17+
/// ### Why is this bad?
18+
/// Using `x.first()` is easier to read and has the same
19+
/// result.
20+
///
21+
/// ### Example
22+
/// ```rust
23+
/// // Bad
24+
/// let x = vec![2, 3, 5];
25+
/// let first_element = x.get(0);
26+
/// ```
27+
/// Use instead:
28+
/// ```rust
29+
/// // Good
30+
/// let x = vec![2, 3, 5];
31+
/// let first_element = x.first();
32+
/// ```
33+
#[clippy::version = "1.63.0"]
34+
pub GET_FIRST,
35+
style,
36+
"Using `x.get(0)` when `x.first()` is simpler"
37+
}
38+
declare_lint_pass!(GetFirst => [GET_FIRST]);
39+
40+
impl<'tcx> LateLintPass<'tcx> for GetFirst {
41+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
42+
if_chain! {
43+
if let hir::ExprKind::MethodCall(_, [struct_calling_on, method_arg], _) = &expr.kind;
44+
if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
45+
if match_def_path(cx, expr_def_id, &paths::SLICE_GET);
46+
47+
if let Some(_) = is_slice_of_primitives(cx, struct_calling_on);
48+
if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = method_arg.kind;
49+
50+
then {
51+
let mut applicability = Applicability::MachineApplicable;
52+
let slice_name = snippet_with_applicability(
53+
cx,
54+
struct_calling_on.span, "..",
55+
&mut applicability,
56+
);
57+
span_lint_and_sugg(
58+
cx,
59+
GET_FIRST,
60+
expr.span,
61+
&format!("accessing first element with `{0}.get(0)`", slice_name),
62+
"try",
63+
format!("{}.first()", slice_name),
64+
applicability,
65+
);
66+
}
67+
}
68+
}
69+
}

clippy_lints/src/lib.register_all.rs

+1
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
9191
LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
9292
LintId::of(functions::RESULT_UNIT_ERR),
9393
LintId::of(functions::TOO_MANY_ARGUMENTS),
94+
LintId::of(get_first::GET_FIRST),
9495
LintId::of(identity_op::IDENTITY_OP),
9596
LintId::of(if_let_mutex::IF_LET_MUTEX),
9697
LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),

clippy_lints/src/lib.register_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ store.register_lints(&[
183183
functions::TOO_MANY_ARGUMENTS,
184184
functions::TOO_MANY_LINES,
185185
future_not_send::FUTURE_NOT_SEND,
186+
get_first::GET_FIRST,
186187
identity_op::IDENTITY_OP,
187188
if_let_mutex::IF_LET_MUTEX,
188189
if_not_else::IF_NOT_ELSE,

clippy_lints/src/lib.register_style.rs

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
3131
LintId::of(functions::DOUBLE_MUST_USE),
3232
LintId::of(functions::MUST_USE_UNIT),
3333
LintId::of(functions::RESULT_UNIT_ERR),
34+
LintId::of(get_first::GET_FIRST),
3435
LintId::of(inherent_to_string::INHERENT_TO_STRING),
3536
LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
3637
LintId::of(len_zero::COMPARISON_TO_EMPTY),

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ mod from_over_into;
242242
mod from_str_radix_10;
243243
mod functions;
244244
mod future_not_send;
245+
mod get_first;
245246
mod identity_op;
246247
mod if_let_mutex;
247248
mod if_not_else;
@@ -904,6 +905,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
904905
store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace));
905906
store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
906907
store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default()));
908+
store.register_late_pass(|| Box::new(get_first::GetFirst));
907909
// add lints here, do not remove this comment, it's used in `new_lint`
908910
}
909911

clippy_lints/src/methods/iter_next_slice.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,18 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal
3434
if let ast::LitKind::Int(start_idx, _) = start_lit.node;
3535
then {
3636
let mut applicability = Applicability::MachineApplicable;
37+
let suggest = if start_idx == 0 {
38+
format!("{}.first()", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability))
39+
} else {
40+
format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx)
41+
};
3742
span_lint_and_sugg(
3843
cx,
3944
ITER_NEXT_SLICE,
4045
expr.span,
4146
"using `.iter().next()` on a Slice without end index",
4247
"try calling",
43-
format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx),
48+
suggest,
4449
applicability,
4550
);
4651
}
@@ -55,7 +60,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal
5560
"using `.iter().next()` on an array",
5661
"try calling",
5762
format!(
58-
"{}.get(0)",
63+
"{}.first()",
5964
snippet_with_applicability(cx, caller_expr.span, "..", &mut applicability)
6065
),
6166
applicability,

clippy_utils/src/paths.rs

+1
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
141141
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
142142
pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"];
143143
pub const SLICE_FROM_RAW_PARTS_MUT: [&str; 4] = ["core", "slice", "raw", "from_raw_parts_mut"];
144+
pub const SLICE_GET: [&str; 4] = ["core", "slice", "<impl [T]>", "get"];
144145
pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
145146
pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"];
146147
pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];

tests/ui-toml/unwrap_used/unwrap_used.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// compile-flags: --test
22

3-
#![allow(unused_mut, clippy::from_iter_instead_of_collect)]
3+
#![allow(unused_mut, clippy::get_first, clippy::from_iter_instead_of_collect)]
44
#![warn(clippy::unwrap_used)]
55
#![deny(clippy::get_unwrap)]
66

tests/ui/debug_assert_with_mut_call.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#![feature(custom_inner_attributes)]
22
#![rustfmt::skip]
33
#![warn(clippy::debug_assert_with_mut_call)]
4-
#![allow(clippy::redundant_closure_call)]
4+
#![allow(clippy::redundant_closure_call, clippy::get_first)]
55

66

77
struct S;

tests/ui/get_first.fixed

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// run-rustfix
2+
#![warn(clippy::get_first)]
3+
use std::collections::BTreeMap;
4+
use std::collections::HashMap;
5+
use std::collections::VecDeque;
6+
7+
struct Bar {
8+
arr: [u32; 3],
9+
}
10+
11+
impl Bar {
12+
fn get(&self, pos: usize) -> Option<&u32> {
13+
self.arr.get(pos)
14+
}
15+
}
16+
17+
fn main() {
18+
let x = vec![2, 3, 5];
19+
let _ = x.first(); // Use x.first()
20+
let _ = x.get(1);
21+
let _ = x[0];
22+
23+
let y = [2, 3, 5];
24+
let _ = y.first(); // Use y.first()
25+
let _ = y.get(1);
26+
let _ = y[0];
27+
28+
let z = &[2, 3, 5];
29+
let _ = z.first(); // Use z.first()
30+
let _ = z.get(1);
31+
let _ = z[0];
32+
33+
let vecdeque: VecDeque<_> = x.iter().cloned().collect();
34+
let hashmap: HashMap<u8, char> = HashMap::from_iter(vec![(0, 'a'), (1, 'b')]);
35+
let btreemap: BTreeMap<u8, char> = BTreeMap::from_iter(vec![(0, 'a'), (1, 'b')]);
36+
let _ = vecdeque.get(0); // Do not lint, because VecDeque is not slice.
37+
let _ = hashmap.get(&0); // Do not lint, because HashMap is not slice.
38+
let _ = btreemap.get(&0); // Do not lint, because BTreeMap is not slice.
39+
40+
let bar = Bar { arr: [0, 1, 2] };
41+
let _ = bar.get(0); // Do not lint, because Bar is struct.
42+
}

tests/ui/get_first.rs

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// run-rustfix
2+
#![warn(clippy::get_first)]
3+
use std::collections::BTreeMap;
4+
use std::collections::HashMap;
5+
use std::collections::VecDeque;
6+
7+
struct Bar {
8+
arr: [u32; 3],
9+
}
10+
11+
impl Bar {
12+
fn get(&self, pos: usize) -> Option<&u32> {
13+
self.arr.get(pos)
14+
}
15+
}
16+
17+
fn main() {
18+
let x = vec![2, 3, 5];
19+
let _ = x.get(0); // Use x.first()
20+
let _ = x.get(1);
21+
let _ = x[0];
22+
23+
let y = [2, 3, 5];
24+
let _ = y.get(0); // Use y.first()
25+
let _ = y.get(1);
26+
let _ = y[0];
27+
28+
let z = &[2, 3, 5];
29+
let _ = z.get(0); // Use z.first()
30+
let _ = z.get(1);
31+
let _ = z[0];
32+
33+
let vecdeque: VecDeque<_> = x.iter().cloned().collect();
34+
let hashmap: HashMap<u8, char> = HashMap::from_iter(vec![(0, 'a'), (1, 'b')]);
35+
let btreemap: BTreeMap<u8, char> = BTreeMap::from_iter(vec![(0, 'a'), (1, 'b')]);
36+
let _ = vecdeque.get(0); // Do not lint, because VecDeque is not slice.
37+
let _ = hashmap.get(&0); // Do not lint, because HashMap is not slice.
38+
let _ = btreemap.get(&0); // Do not lint, because BTreeMap is not slice.
39+
40+
let bar = Bar { arr: [0, 1, 2] };
41+
let _ = bar.get(0); // Do not lint, because Bar is struct.
42+
}

tests/ui/get_first.stderr

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error: accessing first element with `x.get(0)`
2+
--> $DIR/get_first.rs:19:13
3+
|
4+
LL | let _ = x.get(0); // Use x.first()
5+
| ^^^^^^^^ help: try: `x.first()`
6+
|
7+
= note: `-D clippy::get-first` implied by `-D warnings`
8+
9+
error: accessing first element with `y.get(0)`
10+
--> $DIR/get_first.rs:24:13
11+
|
12+
LL | let _ = y.get(0); // Use y.first()
13+
| ^^^^^^^^ help: try: `y.first()`
14+
15+
error: accessing first element with `z.get(0)`
16+
--> $DIR/get_first.rs:29:13
17+
|
18+
LL | let _ = z.get(0); // Use z.first()
19+
| ^^^^^^^^ help: try: `z.first()`
20+
21+
error: aborting due to 3 previous errors
22+

tests/ui/get_unwrap.fixed

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// run-rustfix
22

3-
#![allow(unused_mut, clippy::from_iter_instead_of_collect)]
3+
#![allow(unused_mut, clippy::from_iter_instead_of_collect, clippy::get_first)]
44
#![warn(clippy::unwrap_used)]
55
#![deny(clippy::get_unwrap)]
66

tests/ui/get_unwrap.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// run-rustfix
22

3-
#![allow(unused_mut, clippy::from_iter_instead_of_collect)]
3+
#![allow(unused_mut, clippy::from_iter_instead_of_collect, clippy::get_first)]
44
#![warn(clippy::unwrap_used)]
55
#![deny(clippy::get_unwrap)]
66

tests/ui/iter_next_slice.fixed

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@ fn main() {
66
let s = [1, 2, 3];
77
let v = vec![1, 2, 3];
88

9-
let _ = s.get(0);
10-
// Should be replaced by s.get(0)
9+
let _ = s.first();
10+
// Should be replaced by s.first()
1111

1212
let _ = s.get(2);
1313
// Should be replaced by s.get(2)
1414

1515
let _ = v.get(5);
1616
// Should be replaced by v.get(5)
1717

18-
let _ = v.get(0);
19-
// Should be replaced by v.get(0)
18+
let _ = v.first();
19+
// Should be replaced by v.first()
2020

2121
let o = Some(5);
2222
o.iter().next();

tests/ui/iter_next_slice.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ fn main() {
77
let v = vec![1, 2, 3];
88

99
let _ = s.iter().next();
10-
// Should be replaced by s.get(0)
10+
// Should be replaced by s.first()
1111

1212
let _ = s[2..].iter().next();
1313
// Should be replaced by s.get(2)
@@ -16,7 +16,7 @@ fn main() {
1616
// Should be replaced by v.get(5)
1717

1818
let _ = v.iter().next();
19-
// Should be replaced by v.get(0)
19+
// Should be replaced by v.first()
2020

2121
let o = Some(5);
2222
o.iter().next();

tests/ui/iter_next_slice.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: using `.iter().next()` on an array
22
--> $DIR/iter_next_slice.rs:9:13
33
|
44
LL | let _ = s.iter().next();
5-
| ^^^^^^^^^^^^^^^ help: try calling: `s.get(0)`
5+
| ^^^^^^^^^^^^^^^ help: try calling: `s.first()`
66
|
77
= note: `-D clippy::iter-next-slice` implied by `-D warnings`
88

@@ -22,7 +22,7 @@ error: using `.iter().next()` on an array
2222
--> $DIR/iter_next_slice.rs:18:13
2323
|
2424
LL | let _ = v.iter().next();
25-
| ^^^^^^^^^^^^^^^ help: try calling: `v.get(0)`
25+
| ^^^^^^^^^^^^^^^ help: try calling: `v.first()`
2626

2727
error: aborting due to 4 previous errors
2828

tests/ui/needless_lifetimes.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
clippy::boxed_local,
55
clippy::needless_pass_by_value,
66
clippy::unnecessary_wraps,
7-
dyn_drop
7+
dyn_drop,
8+
clippy::get_first
89
)]
910

1011
fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {}

0 commit comments

Comments
 (0)