-use crate::utils::{is_slice_of_primitives, span_lint_and_sugg, sugg::Sugg};
-
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::{is_slice_of_primitives, 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:**
+ /// ### What it does
/// When sorting primitive values (integers, bools, chars, as well
- /// as arrays, slices, and tuples of such items), it is better to
+ /// as arrays, slices, and tuples of such items), it is typically 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
+ /// ### Why is this bad?
+ /// Typically, 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
+ /// ### Known problems
///
- /// **Example:**
+ /// As pointed out in
+ /// [issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241),
+ /// a stable sort can instead be significantly faster for certain scenarios
+ /// (eg. when a sorted vector is extended with new data and resorted).
///
+ /// For more information and benchmarking results, please refer to the
+ /// issue linked above.
+ ///
+ /// ### Example
/// ```rust
/// let mut vec = vec![2, 1, 3];
/// vec.sort();
/// let mut vec = vec![2, 1, 3];
/// vec.sort_unstable();
/// ```
+ #[clippy::version = "1.47.0"]
pub STABLE_SORT_PRIMITIVE,
- perf,
+ pedantic,
"use of sort() when sort_unstable() is equivalent"
}
slice_name: String,
method: SortingKind,
method_args: String,
+ slice_type: 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 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);
+ if let Some(method) = SortingKind::from_stable_name(method_name.ident.name.as_str());
+ if let Some(slice_type) = 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 })
+ Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str, slice_type })
} 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(
+ span_lint_and_then(
cx,
STABLE_SORT_PRIMITIVE,
expr.span,
format!(
- "used {} instead of {}",
+ "used `{}` on primitive type `{}`",
detection.method.stable_name(),
- detection.method.unstable_name()
+ detection.slice_type,
)
.as_str(),
- "try",
- format!(
- "{}.{}({})",
- detection.slice_name,
- detection.method.unstable_name(),
- detection.method_args
- ),
- Applicability::MachineApplicable,
+ |diag| {
+ diag.span_suggestion(
+ expr.span,
+ "try",
+ format!(
+ "{}.{}({})",
+ detection.slice_name,
+ detection.method.unstable_name(),
+ detection.method_args,
+ ),
+ Applicability::MachineApplicable,
+ );
+ diag.note(
+ "an unstable sort typically performs faster without any observable difference for this data type",
+ );
+ },
);
}
}