Skip to content

Commit 8e559f5

Browse files
committed
Add snippet, suggestion, update stderr, add test case
1 parent ac60941 commit 8e559f5

File tree

4 files changed

+35
-25
lines changed

4 files changed

+35
-25
lines changed

clippy_lints/src/use_last.rs

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
//! lint on using `x.get(x.len() - 1)` instead of `x.last()`
22
3-
use crate::utils::{match_type, paths, span_lint};
3+
use crate::utils::{match_type, paths, span_lint_and_sugg, snippet_with_applicability};
44
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
55
use rustc::{declare_tool_lint, lint_array};
66
use rustc::hir::{Expr, ExprKind};
7+
use rustc_errors::Applicability;
78
use syntax::ast::{LitKind};
89
use if_chain::if_chain;
910

1011
/// **What it does:** Checks for using `x.get(x.len() - 1)` instead of `x.last()`.
1112
///
1213
/// **Why is this bad?** Using `x.last()` is easier to read and has the same result.
1314
///
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.
15+
/// Note that using `x[x.len() - 1]` is semantically different from `x.last()`.
16+
/// Indexing into the array will panic on out-of-bounds accesses, while
17+
/// `x.get()` and `x.last()` will return `None`.
18+
///
19+
/// There is another lint (get_unwrap) that covers the case of using
20+
/// `x.get(index).unwrap()` instead of `x[index]`.
1721
///
1822
/// **Known problems:** None.
1923
///
@@ -79,11 +83,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UseLast {
7983

8084
// LHS of subtraction is "x.len()"
8185
if let ExprKind::MethodCall(ref arg_lhs_path, _, ref lhs_args) = lhs.node;
82-
// let _ = println!("LHS of sub is a method call");
86+
// let _ = println!("LHS of sub is a method call");
8387
if arg_lhs_path.ident.name == "len";
8488
// let _ = println!("LHS of sub was method named len");
85-
// if let Some(arg_lhs_struct) = lhs_args.get(0);
89+
if let Some(_arg_lhs_struct) = lhs_args.get(0);
8690
// let _ = println!("LHS of sub method has an arg");
91+
8792
// TODO: Is this a valid way to check if they reference the same vector?
8893
// if arg_lhs_struct.hir_id == struct_calling_on.hir_id;
8994
// let _ = println!("The vector in .get and .len were the same");
@@ -96,24 +101,22 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UseLast {
96101
if rhs_value == 1;
97102
// let _ = println!("RHS of sub was 1");
98103

99-
// TODO: Figure out how to get name of variable for lint message
100-
// Can't do this (cannot move out of borrowed content (context?))
101-
// if let ExprKind::Struct(ref struct_calling_on_path, _, _) = struct_calling_on.node;
102-
// let _ = println!("It was a struct");
103-
// let vec_name = match struct_calling_on_path.into_inner() {
104-
// rustc::hir::QPath::Resolved(_, path) =>
105-
// path.segments.last().map(|path_seg| path_seg.ident.name.as_str().get()).unwrap_or("x"),
106-
// rustc::hir::QPath::TypeRelative(_, path_seg) => path_seg.ident.name.as_str().get(),
107-
// };
108-
let vec_name = "x";
104+
let mut applicability = Applicability::MachineApplicable;
105+
let vec_name = snippet_with_applicability(
106+
cx, struct_calling_on.span, "x", &mut applicability);
109107
// let _ = println!("About to span_lint on \"{}\"", vec_name);
110108

111109
then {
112-
span_lint(cx,
113-
USE_LAST,
114-
expr.span,
115-
&format!("Use `{}.last()` instead of `{}.get({}.len() - 1)`",
116-
vec_name, vec_name, vec_name));
110+
span_lint_and_sugg(
111+
cx,
112+
USE_LAST,
113+
expr.span,
114+
&format!("Use `{}.last()` instead of `{}.get({}.len() - 1)`",
115+
vec_name, vec_name, vec_name),
116+
"try",
117+
format!("{}.last()", vec_name),
118+
applicability,
119+
);
117120
}
118121
}
119122
}

tests/ui/use_last.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,26 @@
22

33
fn dont_use_last() -> Option<i32> {
44
let x = vec![2, 3, 5];
5-
let last_element = x.get(x.len() - 1); // ~ERROR Use _.last()
5+
let last_element = x.get(x.len() - 1); // ~ERROR Use x.last()
66
last_element.map(|val| val + 1) // To avoid warnings
77
}
88

9+
fn indexing_two_from_end() -> Option<i32> {
10+
let x = vec![2, 3, 5];
11+
let last_element = x.get(x.len() - 2);
12+
last_element.map(|val| val + 3) // To avoid warnings
13+
}
14+
915
fn index_into_last() -> i32 {
1016
let x = vec![2, 3, 5];
1117
let last_element = x[x.len() - 1];
1218
last_element + 1 // To avoid warnings
1319
}
1420

1521
fn main() {
16-
let expected_value: i32 = 5;
22+
let expected_value: i32 = 6;
1723
println!("Working...");
1824
assert_eq!(dont_use_last(), Some(expected_value));
25+
assert_eq!(indexing_two_from_end(), Some(expected_value));
1926
assert_eq!(index_into_last(), expected_value);
2027
}

tests/ui/use_last.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: Use `x.last()` instead of `x.get(x.len() - 1)`
22
--> $DIR/use_last.rs:5:24
33
|
4-
LL | let last_element = x.get(x.len() - 1); // ~ERROR Use _.last()
5-
| ^^^^^^^^^^^^^^^^^^
4+
LL | let last_element = x.get(x.len() - 1); // ~ERROR Use x.last()
5+
| ^^^^^^^^^^^^^^^^^^ help: try: `x.last()`
66
|
77
= note: `-D clippy::use-last` implied by `-D warnings`
88

tests/ui/use_last.stdout

Whitespace-only changes.

0 commit comments

Comments
 (0)