Skip to content

Commit e27d824

Browse files
committed
Merge branch 'master' of github.com:HarrisonMc555/rust-clippy
2 parents ff27988 + 98ecce7 commit e27d824

File tree

4 files changed

+120
-0
lines changed

4 files changed

+120
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,6 +1048,7 @@ All notable changes to this project will be documented in this file.
10481048
[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
10491049
[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
10501050
[`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug
1051+
[`use_last`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_last
10511052
[`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self
10521053
[`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding
10531054
[`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref

clippy_lints/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ pub mod unsafe_removed_from_name;
242242
pub mod unused_io_amount;
243243
pub mod unused_label;
244244
pub mod unwrap;
245+
pub mod use_last;
245246
pub mod use_self;
246247
pub mod vec;
247248
pub mod wildcard_dependencies;
@@ -843,6 +844,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
843844
unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
844845
unused_io_amount::UNUSED_IO_AMOUNT,
845846
unused_label::UNUSED_LABEL,
847+
use_last::USE_LAST,
846848
vec::USELESS_VEC,
847849
write::PRINTLN_EMPTY_STRING,
848850
write::PRINT_LITERAL,
@@ -1006,6 +1008,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
10061008
types::UNNECESSARY_CAST,
10071009
types::VEC_BOX,
10081010
unused_label::UNUSED_LABEL,
1011+
use_last::USE_LAST,
10091012
zero_div_zero::ZERO_DIVIDED_BY_ZERO,
10101013
]);
10111014

clippy_lints/src/use_last.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
//! lint on using `x.get(x.len() - 1)` instead of `x.last()`
2+
3+
use crate::utils::{match_type, paths, span_lint};
4+
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
5+
use rustc::{declare_tool_lint, lint_array};
6+
use rustc::hir::{Expr, ExprKind};
7+
use syntax::ast::{LitKind};
8+
use if_chain::if_chain;
9+
10+
/// **What it does:** Checks for using `x.get(x.len() - 1)` instead of `x.last()`.
11+
///
12+
/// **Why is this bad?** Using `x.last()` is easier to read and has the same result.
13+
///
14+
/// Note that using x[x.len() - 1] is semantically different from x.last(),
15+
/// since indexing into the array will panic on out-of-bounds accesses, while
16+
/// x.get() and x.last() will return None.
17+
///
18+
/// **Known problems:** None.
19+
///
20+
/// **Example:**
21+
///
22+
/// ```rust
23+
/// // Bad
24+
/// let x = vec![2, 3, 5];
25+
/// let last_element = x.get(x.len() - 1);
26+
///
27+
/// // Good
28+
/// let x = vec![2, 3, 5];
29+
/// let last_element = x.last();
30+
/// ```
31+
32+
declare_clippy_lint! {
33+
pub USE_LAST,
34+
complexity,
35+
"using `x.get(x.len() - 1)` instead of `x.last()`"
36+
}
37+
38+
#[derive(Copy, Clone, Debug)]
39+
pub struct UseLast;
40+
41+
impl LintPass for UseLast {
42+
fn get_lints(&self) -> LintArray {
43+
lint_array!(USE_LAST)
44+
}
45+
46+
fn name(&self) -> &'static str {
47+
"UseLast"
48+
}
49+
}
50+
51+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UseLast {
52+
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
53+
if_chain! {
54+
// Is a method call
55+
if let ExprKind::MethodCall(ref path, _, ref args) = expr.node;
56+
57+
// Method name is "get"
58+
if path.ident.name == "get";
59+
60+
// Argument 0 (the struct we're calling the method on) is a vector
61+
if let Some(struct_calling_on) = args.get(0);
62+
let struct_ty = cx.tables.expr_ty(struct_calling_on);
63+
if match_type(cx, struct_ty, &paths::VEC);
64+
65+
// Argument to "get" is a binary operation
66+
if let Some(get_index_arg) = args.get(1);
67+
if let rustc::hir::ExprKind::Binary(ref op, ref lhs, ref rhs) = get_index_arg.node;
68+
69+
// Binary operation is a subtraction
70+
if op.node == rustc::hir::BinOpKind::Sub;
71+
72+
// LHS of subtraction is "x.len()"
73+
if let ExprKind::MethodCall(ref arg_lhs_path, _, ref lhs_args) = lhs.node;
74+
if arg_lhs_path.ident.name == "len";
75+
if let Some(arg_lhs_struct) = lhs_args.get(0);
76+
// TODO: Is this a valid way to check if they reference the same vector?
77+
if arg_lhs_struct.hir_id == struct_calling_on.hir_id;
78+
79+
// RHS of subtraction is 1
80+
if let ExprKind::Lit(ref rhs_lit) = rhs.node;
81+
if let LitKind::Int(rhs_value, ..) = rhs_lit.node;
82+
if rhs_value == 1;
83+
84+
// TODO: Figure out how to get name of variable for lint message
85+
let vec_name = "x";
86+
87+
then {
88+
span_lint(cx,
89+
USE_LAST,
90+
expr.span,
91+
&format!("Use `{}.last()` instead of `{}.get({}.len() - 1)`",
92+
vec_name, vec_name, vec_name));
93+
}
94+
}
95+
}
96+
}

tests/ui/use_last.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#![warn(clippy::all)]
2+
// #![warn(clippy::use_last)]
3+
4+
fn dont_use_last() -> Option<i32> {
5+
let x = vec![2, 3, 5];
6+
let last_element = x.get(x.len() - 1); // ~ERROR Use _.last()
7+
last_element.map(|val| val + 1) // To avoid warnings
8+
}
9+
10+
fn index_into_last() -> i32 {
11+
let x = vec![2, 3, 5];
12+
let last_element = x[x.len() - 1];
13+
last_element + 1 // To avoid warnings
14+
}
15+
16+
fn main() {
17+
let expected_value: i32 = 5;
18+
assert_eq!(dont_use_last(), Some(expected_value));
19+
assert_eq!(index_into_last(), 5);
20+
}

0 commit comments

Comments
 (0)