|
1 | 1 | //! lint on using `x.get(x.len() - 1)` instead of `x.last()`
|
2 | 2 |
|
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}; |
5 | 4 | use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
6 | 5 | 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}; |
13 | 8 | 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; |
18 | 9 |
|
19 | 10 | /// **What it does:** Checks for using `x.get(x.len() - 1)` instead of `x.last()`.
|
20 | 11 | ///
|
@@ -54,22 +45,47 @@ impl LintPass for UseLast {
|
54 | 45 | }
|
55 | 46 |
|
56 | 47 | 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) { |
58 | 49 | if_chain! {
|
| 50 | + // Is a method call |
59 | 51 | 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 | + |
67 | 83 | then {
|
68 | 84 | span_lint(cx,
|
69 | 85 | 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)); |
73 | 89 | }
|
74 | 90 | }
|
75 | 91 | }
|
|
0 commit comments