Skip to content

Commit d8d4a13

Browse files
committed
Move UnnecessarySortBy into Methods lint pass
1 parent bb0584d commit d8d4a13

File tree

6 files changed

+106
-100
lines changed

6 files changed

+106
-100
lines changed

clippy_lints/src/lib.register_all.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
213213
LintId::of(methods::UNNECESSARY_FIND_MAP),
214214
LintId::of(methods::UNNECESSARY_FOLD),
215215
LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
216+
LintId::of(methods::UNNECESSARY_SORT_BY),
216217
LintId::of(methods::UNNECESSARY_TO_OWNED),
217218
LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
218219
LintId::of(methods::USELESS_ASREF),
@@ -334,7 +335,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
334335
LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
335336
LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
336337
LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
337-
LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
338338
LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
339339
LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
340340
LintId::of(unused_unit::UNUSED_UNIT),

clippy_lints/src/lib.register_complexity.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
5757
LintId::of(methods::SKIP_WHILE_NEXT),
5858
LintId::of(methods::UNNECESSARY_FILTER_MAP),
5959
LintId::of(methods::UNNECESSARY_FIND_MAP),
60+
LintId::of(methods::UNNECESSARY_SORT_BY),
6061
LintId::of(methods::USELESS_ASREF),
6162
LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
6263
LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
@@ -99,7 +100,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
99100
LintId::of(types::TYPE_COMPLEXITY),
100101
LintId::of(types::VEC_BOX),
101102
LintId::of(unit_types::UNIT_ARG),
102-
LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
103103
LintId::of(unwrap::UNNECESSARY_UNWRAP),
104104
LintId::of(useless_conversion::USELESS_CONVERSION),
105105
LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),

clippy_lints/src/lib.register_lints.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@ store.register_lints(&[
364364
methods::UNNECESSARY_FOLD,
365365
methods::UNNECESSARY_JOIN,
366366
methods::UNNECESSARY_LAZY_EVALUATIONS,
367+
methods::UNNECESSARY_SORT_BY,
367368
methods::UNNECESSARY_TO_OWNED,
368369
methods::UNWRAP_OR_ELSE_DEFAULT,
369370
methods::UNWRAP_USED,
@@ -567,7 +568,6 @@ store.register_lints(&[
567568
unnamed_address::VTABLE_ADDRESS_COMPARISONS,
568569
unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS,
569570
unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
570-
unnecessary_sort_by::UNNECESSARY_SORT_BY,
571571
unnecessary_wraps::UNNECESSARY_WRAPS,
572572
unnested_or_patterns::UNNESTED_OR_PATTERNS,
573573
unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,

clippy_lints/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,6 @@ mod unit_types;
376376
mod unnamed_address;
377377
mod unnecessary_owned_empty_strings;
378378
mod unnecessary_self_imports;
379-
mod unnecessary_sort_by;
380379
mod unnecessary_wraps;
381380
mod unnested_or_patterns;
382381
mod unsafe_removed_from_name;
@@ -716,7 +715,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
716715
store.register_late_pass(|| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
717716
store.register_late_pass(|| Box::new(redundant_clone::RedundantClone));
718717
store.register_late_pass(|| Box::new(slow_vector_initialization::SlowVectorInit));
719-
store.register_late_pass(|| Box::new(unnecessary_sort_by::UnnecessarySortBy));
720718
store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)));
721719
store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants));
722720
store.register_late_pass(|| Box::new(assertions_on_result_states::AssertionsOnResultStates));

clippy_lints/src/methods/mod.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ mod unnecessary_fold;
8484
mod unnecessary_iter_cloned;
8585
mod unnecessary_join;
8686
mod unnecessary_lazy_eval;
87+
mod unnecessary_sort_by;
8788
mod unnecessary_to_owned;
8889
mod unwrap_or_else_default;
8990
mod unwrap_used;
@@ -2872,6 +2873,40 @@ declare_clippy_lint! {
28722873
"hashing a unit value, which does nothing"
28732874
}
28742875

2876+
declare_clippy_lint! {
2877+
/// ### What it does
2878+
/// Detects uses of `Vec::sort_by` passing in a closure
2879+
/// which compares the two arguments, either directly or indirectly.
2880+
///
2881+
/// ### Why is this bad?
2882+
/// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
2883+
/// possible) than to use `Vec::sort_by` and a more complicated
2884+
/// closure.
2885+
///
2886+
/// ### Known problems
2887+
/// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
2888+
/// imported by a use statement, then it will need to be added manually.
2889+
///
2890+
/// ### Example
2891+
/// ```rust
2892+
/// # struct A;
2893+
/// # impl A { fn foo(&self) {} }
2894+
/// # let mut vec: Vec<A> = Vec::new();
2895+
/// vec.sort_by(|a, b| a.foo().cmp(&b.foo()));
2896+
/// ```
2897+
/// Use instead:
2898+
/// ```rust
2899+
/// # struct A;
2900+
/// # impl A { fn foo(&self) {} }
2901+
/// # let mut vec: Vec<A> = Vec::new();
2902+
/// vec.sort_by_key(|a| a.foo());
2903+
/// ```
2904+
#[clippy::version = "1.46.0"]
2905+
pub UNNECESSARY_SORT_BY,
2906+
complexity,
2907+
"Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
2908+
}
2909+
28752910
pub struct Methods {
28762911
avoid_breaking_exported_api: bool,
28772912
msrv: Option<RustcVersion>,
@@ -2990,6 +3025,7 @@ impl_lint_pass!(Methods => [
29903025
REPEAT_ONCE,
29913026
STABLE_SORT_PRIMITIVE,
29923027
UNIT_HASH,
3028+
UNNECESSARY_SORT_BY,
29933029
]);
29943030

29953031
/// Extracts a method call name, args, and `Span` of the method name.
@@ -3387,6 +3423,12 @@ impl Methods {
33873423
("sort", []) => {
33883424
stable_sort_primitive::check(cx, expr, recv);
33893425
},
3426+
("sort_by", [arg]) => {
3427+
unnecessary_sort_by::check(cx, expr, recv, arg, false);
3428+
},
3429+
("sort_unstable_by", [arg]) => {
3430+
unnecessary_sort_by::check(cx, expr, recv, arg, true);
3431+
},
33903432
("splitn" | "rsplitn", [count_arg, pat_arg]) => {
33913433
if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
33923434
suspicious_splitn::check(cx, name, expr, recv, count);
Lines changed: 61 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,17 @@
11
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::is_trait_method;
23
use clippy_utils::sugg::Sugg;
3-
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
4+
use clippy_utils::ty::implements_trait;
45
use if_chain::if_chain;
56
use rustc_errors::Applicability;
67
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
7-
use rustc_lint::{LateContext, LateLintPass};
8+
use rustc_lint::LateContext;
89
use rustc_middle::ty::{self, subst::GenericArgKind};
9-
use rustc_session::{declare_lint_pass, declare_tool_lint};
1010
use rustc_span::sym;
1111
use rustc_span::symbol::Ident;
1212
use std::iter;
1313

14-
declare_clippy_lint! {
15-
/// ### What it does
16-
/// Detects uses of `Vec::sort_by` passing in a closure
17-
/// which compares the two arguments, either directly or indirectly.
18-
///
19-
/// ### Why is this bad?
20-
/// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
21-
/// possible) than to use `Vec::sort_by` and a more complicated
22-
/// closure.
23-
///
24-
/// ### Known problems
25-
/// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
26-
/// imported by a use statement, then it will need to be added manually.
27-
///
28-
/// ### Example
29-
/// ```rust
30-
/// # struct A;
31-
/// # impl A { fn foo(&self) {} }
32-
/// # let mut vec: Vec<A> = Vec::new();
33-
/// vec.sort_by(|a, b| a.foo().cmp(&b.foo()));
34-
/// ```
35-
/// Use instead:
36-
/// ```rust
37-
/// # struct A;
38-
/// # impl A { fn foo(&self) {} }
39-
/// # let mut vec: Vec<A> = Vec::new();
40-
/// vec.sort_by_key(|a| a.foo());
41-
/// ```
42-
#[clippy::version = "1.46.0"]
43-
pub UNNECESSARY_SORT_BY,
44-
complexity,
45-
"Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
46-
}
47-
48-
declare_lint_pass!(UnnecessarySortBy => [UNNECESSARY_SORT_BY]);
14+
use super::UNNECESSARY_SORT_BY;
4915

5016
enum LintTrigger {
5117
Sort(SortDetection),
@@ -54,15 +20,13 @@ enum LintTrigger {
5420

5521
struct SortDetection {
5622
vec_name: String,
57-
unstable: bool,
5823
}
5924

6025
struct SortByKeyDetection {
6126
vec_name: String,
6227
closure_arg: String,
6328
closure_body: String,
6429
reverse: bool,
65-
unstable: bool,
6630
}
6731

6832
/// Detect if the two expressions are mirrored (identical, except one
@@ -150,20 +114,20 @@ fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident
150114
}
151115
}
152116

153-
fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
117+
fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) -> Option<LintTrigger> {
154118
if_chain! {
155-
if let ExprKind::MethodCall(name_ident, args, _) = &expr.kind;
156-
if let name = name_ident.ident.name.to_ident_string();
157-
if name == "sort_by" || name == "sort_unstable_by";
158-
if let [vec, Expr { kind: ExprKind::Closure(Closure { body: closure_body_id, .. }), .. }] = args;
159-
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym::Vec);
160-
if let closure_body = cx.tcx.hir().body(*closure_body_id);
119+
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
120+
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
121+
if cx.tcx.type_of(impl_id).is_slice();
122+
if let ExprKind::Closure(&Closure { body, .. }) = arg.kind;
123+
if let closure_body = cx.tcx.hir().body(body);
161124
if let &[
162125
Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..},
163126
Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. }
164127
] = &closure_body.params;
165-
if let ExprKind::MethodCall(method_path, [ref left_expr, ref right_expr], _) = &closure_body.value.kind;
128+
if let ExprKind::MethodCall(method_path, [left_expr, right_expr], _) = closure_body.value.kind;
166129
if method_path.ident.name == sym::cmp;
130+
if is_trait_method(cx, &closure_body.value, sym::Ord);
167131
then {
168132
let (closure_body, closure_arg, reverse) = if mirrored_exprs(
169133
left_expr,
@@ -177,19 +141,18 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
177141
} else {
178142
return None;
179143
};
180-
let vec_name = Sugg::hir(cx, &args[0], "..").to_string();
181-
let unstable = name == "sort_unstable_by";
144+
let vec_name = Sugg::hir(cx, recv, "..").to_string();
182145

183146
if_chain! {
184-
if let ExprKind::Path(QPath::Resolved(_, Path {
185-
segments: [PathSegment { ident: left_name, .. }], ..
186-
})) = &left_expr.kind;
187-
if left_name == left_ident;
188-
if cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| {
189-
implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[])
190-
});
147+
if let ExprKind::Path(QPath::Resolved(_, Path {
148+
segments: [PathSegment { ident: left_name, .. }], ..
149+
})) = &left_expr.kind;
150+
if left_name == left_ident;
151+
if cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| {
152+
implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[])
153+
});
191154
then {
192-
return Some(LintTrigger::Sort(SortDetection { vec_name, unstable }));
155+
return Some(LintTrigger::Sort(SortDetection { vec_name }));
193156
}
194157
}
195158

@@ -199,7 +162,6 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
199162
closure_arg,
200163
closure_body,
201164
reverse,
202-
unstable,
203165
}));
204166
}
205167
}
@@ -213,46 +175,50 @@ fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
213175
matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
214176
}
215177

216-
impl LateLintPass<'_> for UnnecessarySortBy {
217-
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
218-
match detect_lint(cx, expr) {
219-
Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg(
220-
cx,
221-
UNNECESSARY_SORT_BY,
222-
expr.span,
223-
"use Vec::sort_by_key here instead",
224-
"try",
225-
format!(
226-
"{}.sort{}_by_key(|{}| {})",
227-
trigger.vec_name,
228-
if trigger.unstable { "_unstable" } else { "" },
229-
trigger.closure_arg,
230-
if trigger.reverse {
231-
format!("std::cmp::Reverse({})", trigger.closure_body)
232-
} else {
233-
trigger.closure_body.to_string()
234-
},
235-
),
178+
pub(super) fn check<'tcx>(
179+
cx: &LateContext<'tcx>,
180+
expr: &'tcx Expr<'_>,
181+
recv: &'tcx Expr<'_>,
182+
arg: &'tcx Expr<'_>,
183+
is_unstable: bool,
184+
) {
185+
match detect_lint(cx, expr, recv, arg) {
186+
Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg(
187+
cx,
188+
UNNECESSARY_SORT_BY,
189+
expr.span,
190+
"use Vec::sort_by_key here instead",
191+
"try",
192+
format!(
193+
"{}.sort{}_by_key(|{}| {})",
194+
trigger.vec_name,
195+
if is_unstable { "_unstable" } else { "" },
196+
trigger.closure_arg,
236197
if trigger.reverse {
237-
Applicability::MaybeIncorrect
198+
format!("std::cmp::Reverse({})", trigger.closure_body)
238199
} else {
239-
Applicability::MachineApplicable
200+
trigger.closure_body.to_string()
240201
},
241202
),
242-
Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg(
243-
cx,
244-
UNNECESSARY_SORT_BY,
245-
expr.span,
246-
"use Vec::sort here instead",
247-
"try",
248-
format!(
249-
"{}.sort{}()",
250-
trigger.vec_name,
251-
if trigger.unstable { "_unstable" } else { "" },
252-
),
253-
Applicability::MachineApplicable,
203+
if trigger.reverse {
204+
Applicability::MaybeIncorrect
205+
} else {
206+
Applicability::MachineApplicable
207+
},
208+
),
209+
Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg(
210+
cx,
211+
UNNECESSARY_SORT_BY,
212+
expr.span,
213+
"use Vec::sort here instead",
214+
"try",
215+
format!(
216+
"{}.sort{}()",
217+
trigger.vec_name,
218+
if is_unstable { "_unstable" } else { "" },
254219
),
255-
None => {},
256-
}
220+
Applicability::MachineApplicable,
221+
),
222+
None => {},
257223
}
258224
}

0 commit comments

Comments
 (0)