-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of #5809 - JarredAllen:stable_sort_primitive, r=Manishearth
Stable sort primitive changelog: Implements #5762
- Loading branch information
Showing
11 changed files
with
294 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
use crate::utils::{is_slice_of_primitives, span_lint_and_sugg, sugg::Sugg}; | ||
|
||
use if_chain::if_chain; | ||
|
||
use rustc_errors::Applicability; | ||
use rustc_hir::{Expr, ExprKind}; | ||
use rustc_lint::{LateContext, LateLintPass}; | ||
use rustc_session::{declare_lint_pass, declare_tool_lint}; | ||
|
||
declare_clippy_lint! { | ||
/// **What it does:** | ||
/// When sorting primitive values (integers, bools, chars, as well | ||
/// as arrays, slices, and tuples of such items), it is better to | ||
/// use an unstable sort than a stable sort. | ||
/// | ||
/// **Why is this bad?** | ||
/// Using a stable sort consumes more memory and cpu cycles. Because | ||
/// values which compare equal are identical, preserving their | ||
/// relative order (the guarantee that a stable sort provides) means | ||
/// nothing, while the extra costs still apply. | ||
/// | ||
/// **Known problems:** | ||
/// None | ||
/// | ||
/// **Example:** | ||
/// | ||
/// ```rust | ||
/// let mut vec = vec![2, 1, 3]; | ||
/// vec.sort(); | ||
/// ``` | ||
/// Use instead: | ||
/// ```rust | ||
/// let mut vec = vec![2, 1, 3]; | ||
/// vec.sort_unstable(); | ||
/// ``` | ||
pub STABLE_SORT_PRIMITIVE, | ||
perf, | ||
"use of sort() when sort_unstable() is equivalent" | ||
} | ||
|
||
declare_lint_pass!(StableSortPrimitive => [STABLE_SORT_PRIMITIVE]); | ||
|
||
/// The three "kinds" of sorts | ||
enum SortingKind { | ||
Vanilla, | ||
// The other kinds of lint are currently commented out because they | ||
// can map distinct values to equal ones. If the key function is | ||
// provably one-to-one, or if the Cmp function conserves equality, | ||
// then they could be linted on, but I don't know if we can check | ||
// for that. | ||
|
||
// ByKey, | ||
// ByCmp, | ||
} | ||
impl SortingKind { | ||
/// The name of the stable version of this kind of sort | ||
fn stable_name(&self) -> &str { | ||
match self { | ||
SortingKind::Vanilla => "sort", | ||
// SortingKind::ByKey => "sort_by_key", | ||
// SortingKind::ByCmp => "sort_by", | ||
} | ||
} | ||
/// The name of the unstable version of this kind of sort | ||
fn unstable_name(&self) -> &str { | ||
match self { | ||
SortingKind::Vanilla => "sort_unstable", | ||
// SortingKind::ByKey => "sort_unstable_by_key", | ||
// SortingKind::ByCmp => "sort_unstable_by", | ||
} | ||
} | ||
/// Takes the name of a function call and returns the kind of sort | ||
/// that corresponds to that function name (or None if it isn't) | ||
fn from_stable_name(name: &str) -> Option<SortingKind> { | ||
match name { | ||
"sort" => Some(SortingKind::Vanilla), | ||
// "sort_by" => Some(SortingKind::ByCmp), | ||
// "sort_by_key" => Some(SortingKind::ByKey), | ||
_ => None, | ||
} | ||
} | ||
} | ||
|
||
/// A detected instance of this lint | ||
struct LintDetection { | ||
slice_name: String, | ||
method: SortingKind, | ||
method_args: String, | ||
} | ||
|
||
fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintDetection> { | ||
if_chain! { | ||
if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind; | ||
if let Some(slice) = &args.get(0); | ||
if let Some(method) = SortingKind::from_stable_name(&method_name.ident.name.as_str()); | ||
if is_slice_of_primitives(cx, slice); | ||
then { | ||
let args_str = args.iter().skip(1).map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::<Vec<String>>().join(", "); | ||
Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str }) | ||
} else { | ||
None | ||
} | ||
} | ||
} | ||
|
||
impl LateLintPass<'_> for StableSortPrimitive { | ||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { | ||
if let Some(detection) = detect_stable_sort_primitive(cx, expr) { | ||
span_lint_and_sugg( | ||
cx, | ||
STABLE_SORT_PRIMITIVE, | ||
expr.span, | ||
format!( | ||
"Use {} instead of {}", | ||
detection.method.unstable_name(), | ||
detection.method.stable_name() | ||
) | ||
.as_str(), | ||
"try", | ||
format!( | ||
"{}.{}({})", | ||
detection.slice_name, | ||
detection.method.unstable_name(), | ||
detection.method_args | ||
), | ||
Applicability::MachineApplicable, | ||
); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// run-rustfix | ||
#![warn(clippy::stable_sort_primitive)] | ||
|
||
fn main() { | ||
// positive examples | ||
let mut vec = vec![1, 3, 2]; | ||
vec.sort_unstable(); | ||
let mut vec = vec![false, false, true]; | ||
vec.sort_unstable(); | ||
let mut vec = vec!['a', 'A', 'c']; | ||
vec.sort_unstable(); | ||
let mut vec = vec!["ab", "cd", "ab", "bc"]; | ||
vec.sort_unstable(); | ||
let mut vec = vec![(2, 1), (1, 2), (2, 5)]; | ||
vec.sort_unstable(); | ||
let mut vec = vec![[2, 1], [1, 2], [2, 5]]; | ||
vec.sort_unstable(); | ||
let mut arr = [1, 3, 2]; | ||
arr.sort_unstable(); | ||
// Negative examples: behavior changes if made unstable | ||
let mut vec = vec![1, 3, 2]; | ||
vec.sort_by_key(|i| i / 2); | ||
vec.sort_by(|a, b| (a + b).cmp(&b)); | ||
// negative examples - Not of a primitive type | ||
let mut vec_of_complex = vec![String::from("hello"), String::from("world!")]; | ||
vec_of_complex.sort(); | ||
vec_of_complex.sort_by_key(String::len); | ||
let mut vec = vec![(String::from("hello"), String::from("world"))]; | ||
vec.sort(); | ||
let mut vec = vec![[String::from("hello"), String::from("world")]]; | ||
vec.sort(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// run-rustfix | ||
#![warn(clippy::stable_sort_primitive)] | ||
|
||
fn main() { | ||
// positive examples | ||
let mut vec = vec![1, 3, 2]; | ||
vec.sort(); | ||
let mut vec = vec![false, false, true]; | ||
vec.sort(); | ||
let mut vec = vec!['a', 'A', 'c']; | ||
vec.sort(); | ||
let mut vec = vec!["ab", "cd", "ab", "bc"]; | ||
vec.sort(); | ||
let mut vec = vec![(2, 1), (1, 2), (2, 5)]; | ||
vec.sort(); | ||
let mut vec = vec![[2, 1], [1, 2], [2, 5]]; | ||
vec.sort(); | ||
let mut arr = [1, 3, 2]; | ||
arr.sort(); | ||
// Negative examples: behavior changes if made unstable | ||
let mut vec = vec![1, 3, 2]; | ||
vec.sort_by_key(|i| i / 2); | ||
vec.sort_by(|a, b| (a + b).cmp(&b)); | ||
// negative examples - Not of a primitive type | ||
let mut vec_of_complex = vec![String::from("hello"), String::from("world!")]; | ||
vec_of_complex.sort(); | ||
vec_of_complex.sort_by_key(String::len); | ||
let mut vec = vec![(String::from("hello"), String::from("world"))]; | ||
vec.sort(); | ||
let mut vec = vec![[String::from("hello"), String::from("world")]]; | ||
vec.sort(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
error: Use sort_unstable instead of sort | ||
--> $DIR/stable_sort_primitive.rs:7:5 | ||
| | ||
LL | vec.sort(); | ||
| ^^^^^^^^^^ help: try: `vec.sort_unstable()` | ||
| | ||
= note: `-D clippy::stable-sort-primitive` implied by `-D warnings` | ||
|
||
error: Use sort_unstable instead of sort | ||
--> $DIR/stable_sort_primitive.rs:9:5 | ||
| | ||
LL | vec.sort(); | ||
| ^^^^^^^^^^ help: try: `vec.sort_unstable()` | ||
|
||
error: Use sort_unstable instead of sort | ||
--> $DIR/stable_sort_primitive.rs:11:5 | ||
| | ||
LL | vec.sort(); | ||
| ^^^^^^^^^^ help: try: `vec.sort_unstable()` | ||
|
||
error: Use sort_unstable instead of sort | ||
--> $DIR/stable_sort_primitive.rs:13:5 | ||
| | ||
LL | vec.sort(); | ||
| ^^^^^^^^^^ help: try: `vec.sort_unstable()` | ||
|
||
error: Use sort_unstable instead of sort | ||
--> $DIR/stable_sort_primitive.rs:15:5 | ||
| | ||
LL | vec.sort(); | ||
| ^^^^^^^^^^ help: try: `vec.sort_unstable()` | ||
|
||
error: Use sort_unstable instead of sort | ||
--> $DIR/stable_sort_primitive.rs:17:5 | ||
| | ||
LL | vec.sort(); | ||
| ^^^^^^^^^^ help: try: `vec.sort_unstable()` | ||
|
||
error: Use sort_unstable instead of sort | ||
--> $DIR/stable_sort_primitive.rs:19:5 | ||
| | ||
LL | arr.sort(); | ||
| ^^^^^^^^^^ help: try: `arr.sort_unstable()` | ||
|
||
error: aborting due to 7 previous errors | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
// run-rustfix | ||
|
||
#![allow(clippy::stable_sort_primitive)] | ||
|
||
use std::cmp::Reverse; | ||
|
||
fn unnecessary_sort_by() { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
// run-rustfix | ||
|
||
#![allow(clippy::stable_sort_primitive)] | ||
|
||
use std::cmp::Reverse; | ||
|
||
fn unnecessary_sort_by() { | ||
|
Oops, something went wrong.