|
| 1 | +use crate::utils::{match_type, span_lint_and_sugg}; |
| 2 | +use crate::utils::paths; |
| 3 | +use crate::utils::sugg::Sugg; |
| 4 | +use if_chain::if_chain; |
| 5 | +use rustc_errors::Applicability; |
1 | 6 | use rustc_lint::{LateLintPass, LateContext};
|
2 | 7 | use rustc_session::{declare_lint_pass, declare_tool_lint};
|
3 | 8 | use rustc_hir::*;
|
4 | 9 |
|
5 | 10 | declare_clippy_lint! {
|
6 | 11 | /// **What it does:**
|
| 12 | + /// Detects when people use `Vec::sort_by` and pass in a function |
| 13 | + /// which compares the second argument to the first. |
7 | 14 | ///
|
8 | 15 | /// **Why is this bad?**
|
| 16 | + /// It is more clear to use `Vec::sort_by_key` and `std::cmp::Reverse` |
9 | 17 | ///
|
10 | 18 | /// **Known problems:** None.
|
11 | 19 | ///
|
12 | 20 | /// **Example:**
|
13 | 21 | ///
|
14 | 22 | /// ```rust
|
15 |
| - /// // example code where clippy issues a warning |
| 23 | + /// vec.sort_by(|a, b| b.foo().cmp(&a.foo())); |
16 | 24 | /// ```
|
17 | 25 | /// Use instead:
|
18 | 26 | /// ```rust
|
19 |
| - /// // example code which does not raise clippy warning |
| 27 | + /// vec.sort_by_key(|e| Reverse(e.foo())); |
20 | 28 | /// ```
|
21 | 29 | pub SORT_BY_KEY_REVERSE,
|
22 | 30 | complexity,
|
23 |
| - "default lint description" |
| 31 | + "Use of `Vec::sort_by` when `Vec::sort_by_key` would be clearer" |
24 | 32 | }
|
25 | 33 |
|
26 | 34 | declare_lint_pass!(SortByKeyReverse => [SORT_BY_KEY_REVERSE]);
|
27 | 35 |
|
28 |
| -impl LateLintPass<'_, '_> for SortByKeyReverse {} |
| 36 | +struct LintTrigger { |
| 37 | + vec_name: String, |
| 38 | + closure_arg: String, |
| 39 | + closure_reverse_body: String, |
| 40 | + unstable: bool, |
| 41 | +} |
| 42 | + |
| 43 | +fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option<LintTrigger> { |
| 44 | + if_chain! { |
| 45 | + if let ExprKind::MethodCall(name_ident, _, args) = &expr.kind; |
| 46 | + if let name = name_ident.ident.name.to_ident_string(); |
| 47 | + if name == "sort_by" || name == "sort_unstable_by"; |
| 48 | + if let [vec, Expr { kind: ExprKind::Closure(_, closure_decl, closure_body_id, _, _), .. }] = args; |
| 49 | + if closure_decl.inputs.len() == 2; |
| 50 | + if match_type(cx, &cx.tables.expr_ty(vec), &paths::VEC); |
| 51 | + then { |
| 52 | + let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); |
| 53 | + let unstable = name == "sort_unstable_by"; |
| 54 | + Some(LintTrigger { vec_name, unstable, closure_arg: "e".to_string(), closure_reverse_body: "e".to_string() }) |
| 55 | + } else { |
| 56 | + None |
| 57 | + } |
| 58 | + } |
| 59 | +} |
| 60 | + |
| 61 | +impl LateLintPass<'_, '_> for SortByKeyReverse { |
| 62 | + fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { |
| 63 | + println!("{:?}", expr); |
| 64 | + span_lint_and_sugg( |
| 65 | + cx, |
| 66 | + SORT_BY_KEY_REVERSE, |
| 67 | + expr.span, |
| 68 | + "use Vec::sort_by_key here instead", |
| 69 | + "try", |
| 70 | + String::from("being a better person"), |
| 71 | + Applicability::MachineApplicable, |
| 72 | + ); |
| 73 | + if let Some(trigger) = detect_lint(cx, expr) { |
| 74 | + span_lint_and_sugg( |
| 75 | + cx, |
| 76 | + SORT_BY_KEY_REVERSE, |
| 77 | + expr.span, |
| 78 | + "use Vec::sort_by_key here instead", |
| 79 | + "try", |
| 80 | + format!( |
| 81 | + "{}.sort{}_by_key(|{}| Reverse({}))", |
| 82 | + trigger.vec_name, |
| 83 | + if trigger.unstable { "_unstable" } else { "" }, |
| 84 | + trigger.closure_arg, |
| 85 | + trigger.closure_reverse_body, |
| 86 | + ), |
| 87 | + Applicability::MachineApplicable, |
| 88 | + ); |
| 89 | + } |
| 90 | + } |
| 91 | +} |
0 commit comments