Skip to content

Commit 5f7a9be

Browse files
committed
Possibly finish
1 parent 561352f commit 5f7a9be

File tree

1 file changed

+39
-23
lines changed

1 file changed

+39
-23
lines changed

clippy_lints/src/use_last.rs

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,11 @@
11
//! lint on using `x.get(x.len() - 1)` instead of `x.last()`
22
3-
// use crate::utils::{in_macro, snippet_opt, last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_then};
4-
// use rustc::lint::{in_external_macro, LateContext, LateLintPass, LintArray, LintContext, LintPass};
3+
use crate::utils::{match_type, paths, span_lint};
54
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
65
use rustc::{declare_tool_lint, lint_array};
7-
// use if_chain::if_chain;
8-
// use syntax::ast::*;
9-
use rustc::hir::{PatKind, BindingAnnotation, Expr, ExprKind};
10-
// use crate::utils::{opt_def_id, sugg};
11-
// use crate::utils::{span_lint, snippet};
12-
use crate::utils::span_lint;
6+
use rustc::hir::{Expr, ExprKind};
7+
use syntax::ast::{LitKind};
138
use if_chain::if_chain;
14-
// use rustc::ty::{self, Ty};
15-
// use rustc_errors::Applicability;
16-
// use std::borrow::Cow;
17-
// use syntax::ast;
189

1910
/// **What it does:** Checks for using `x.get(x.len() - 1)` instead of `x.last()`.
2011
///
@@ -54,22 +45,47 @@ impl LintPass for UseLast {
5445
}
5546

5647
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UseLast {
57-
fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
48+
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
5849
if_chain! {
50+
// Is a method call
5951
if let ExprKind::MethodCall(ref path, _, ref args) = expr.node;
60-
// check if vector
61-
// TODO: check if vector
62-
// check if calling 'get' method
63-
if path.ident.name.as_str() == "get";
64-
// if let ExprKind::MethodCall(ref method_name, ref generics, ref args) = init.node;
65-
// if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, _, None) = local.pat.node;
66-
// if name.node.as_str() == "last_element";
52+
53+
// Method name is "get"
54+
if path.ident.name == "get";
55+
56+
// Argument 0 (the struct we're calling the method on) is a vector
57+
if let Some(struct_calling_on) = args.get(0);
58+
let struct_ty = cx.tables.expr_ty(struct_calling_on);
59+
if match_type(cx, struct_ty, &paths::VEC);
60+
61+
// Argument to "get" is a binary operation
62+
if let Some(get_index_arg) = args.get(1);
63+
if let rustc::hir::ExprKind::Binary(ref op, ref lhs, ref rhs) = get_index_arg.node;
64+
65+
// Binary operation is a subtraction
66+
if op.node == rustc::hir::BinOpKind::Sub;
67+
68+
// LHS of subtraction is "x.len()"
69+
if let ExprKind::MethodCall(ref arg_lhs_path, _, ref lhs_args) = lhs.node;
70+
if arg_lhs_path.ident.name == "len";
71+
if let Some(arg_lhs_struct) = lhs_args.get(0);
72+
// TODO: Is this a valid way to check if they reference the same vector?
73+
if arg_lhs_struct.hir_id == struct_calling_on.hir_id;
74+
75+
// RHS of subtraction is 1
76+
if let ExprKind::Lit(ref rhs_lit) = rhs.node;
77+
if let LitKind::Int(rhs_value, ..) = rhs_lit.node;
78+
if rhs_value == 1;
79+
80+
// TODO: Figure out how to get name of variable for lint message
81+
let vec_name = "x";
82+
6783
then {
6884
span_lint(cx,
6985
USE_LAST,
70-
stmt.span,
71-
// Todo: fix this
72-
&format!("Use `x.last()` instead of `x.get(x.len() - 1)`"));
86+
expr.span,
87+
&format!("Use `{}.last()` instead of `{}.get({}.len() - 1)`",
88+
vec_name, vec_name, vec_name));
7389
}
7490
}
7591
}

0 commit comments

Comments
 (0)