Skip to content

Commit

Permalink
Create initial lint code
Browse files Browse the repository at this point in the history
  • Loading branch information
JarredAllen committed Jul 16, 2020
1 parent 5a2a9e0 commit e9ee068
Showing 1 changed file with 101 additions and 5 deletions.
106 changes: 101 additions & 5 deletions clippy_lints/src/stable_sort_primitive.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,36 @@
use rustc_lint::{LateLintPass, LateContext};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use crate::utils::{span_lint_and_sugg, span_lint, sugg::Sugg};

use if_chain::if_chain;

use rustc_errors::Applicability;
use rustc_hir::*;
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, floats, 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
/// // example code where clippy issues a warning
/// let mut vec = vec![2, 1, 3];
/// mec.sort();
/// ```
/// Use instead:
/// ```rust
/// // example code which does not raise clippy warning
/// let mut vec = vec![2, 1, 3];
/// mec.sort_unstable();
/// ```
pub STABLE_SORT_PRIMITIVE,
perf,
Expand All @@ -25,4 +39,86 @@ declare_clippy_lint! {

declare_lint_pass!(StableSortPrimitive => [STABLE_SORT_PRIMITIVE]);

impl LateLintPass<'_, '_> for StableSortPrimitive {}
enum SortingKind {
Vanilla,
ByKey,
ByCmp,
}
impl SortingKind {
fn stable_name(&self) -> &str {
match self {
SortingKind::Vanilla => "sort",
SortingKind::ByKey => "sort_by_key",
SortingKind::ByCmp => "sort_by",
}
}
fn unstable_name(&self) -> &str {
match self {
SortingKind::Vanilla => "sort_unstable",
SortingKind::ByKey => "sort_unstable_by_key",
SortingKind::ByCmp => "sort_unstable_by",
}
}

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,
}
}
}

struct LintDetection {
vec_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 args.len() <= 2;
if let Some(method) = SortingKind::from_stable_name(&method_name.ident.name.as_str());
if let Some(vec) = &args.get(0);
then {
let args_str = String::new(); // args.iter().skip(1).map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::<Vec<String>>().join(", ");
Some(LintDetection { vec_name: Sugg::hir(cx, vec, "..").to_string(), method, method_args: args_str })
} else {
None
}
}
}

impl LateLintPass<'_> for StableSortPrimitive {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
span_lint(
cx,
STABLE_SORT_PRIMITIVE,
expr.span,
"foo error",
);
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.vec_name,
detection.method.unstable_name(),
detection.method_args
),
Applicability::MachineApplicable,
);
}
}
}

0 comments on commit e9ee068

Please sign in to comment.