Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7136,6 +7136,7 @@ Released 2018-09-13
[`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
[`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take
[`needless_parens_on_range_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literals
[`needless_partial_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_partial_cmp
[`needless_pass_by_ref_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
[`needless_pub_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pub_self
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
crate::needless_late_init::NEEDLESS_LATE_INIT_INFO,
crate::needless_maybe_sized::NEEDLESS_MAYBE_SIZED_INFO,
crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO,
crate::needless_partial_cmp::NEEDLESS_PARTIAL_CMP_INFO,
crate::needless_pass_by_ref_mut::NEEDLESS_PASS_BY_REF_MUT_INFO,
crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO,
crate::needless_question_mark::NEEDLESS_QUESTION_MARK_INFO,
Expand Down
2 changes: 2 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ mod needless_ifs;
mod needless_late_init;
mod needless_maybe_sized;
mod needless_parens_on_range_literals;
mod needless_partial_cmp;
mod needless_pass_by_ref_mut;
mod needless_pass_by_value;
mod needless_question_mark;
Expand Down Expand Up @@ -862,6 +863,7 @@ rustc_lint::late_lint_methods!(
ManualAssertEq: manual_assert_eq::ManualAssertEq = manual_assert_eq::ManualAssertEq,
WithCapacityZero: with_capacity_zero::WithCapacityZero = with_capacity_zero::WithCapacityZero,
RefPatterns: ref_patterns::RefPatterns = ref_patterns::RefPatterns,
NeedlessPartialCmp: needless_partial_cmp::NeedlessPartialCmp = needless_partial_cmp::NeedlessPartialCmp,
// add late passes here, used by `cargo dev new_lint`
]]
);
66 changes: 66 additions & 0 deletions clippy_lints/src/needless_partial_cmp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::res::{MaybeDef, MaybeTypeckRes};
use clippy_utils::source::snippet_with_context;
use clippy_utils::sym;
use clippy_utils::ty::implements_trait;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
declare_clippy_lint! {
/// ### What it does
/// Looks for cases where PartialOrd::partial_cmp is used instead of Ord::cmp, when both exist.
///
/// ### Why is this bad?
/// It removes an unnecessary panic path. It is more concise. It also future-proofs a case where
/// the Ord trait implementation is removed, resulting in a compiler error, instead of silently
/// using the same error handling.
///
/// ### Example
/// ```no_run
/// fn foo(a: &str, b: &str) {
/// a.partial_cmp(b).unwrap();
/// }
/// ```
/// Use instead:
/// ```no_run
/// fn foo(a: &str, b: &str) {
/// a.cmp(b);
/// }
/// ```
#[clippy::version = "1.96.0"]
pub NEEDLESS_PARTIAL_CMP,
nursery,
"checks for uses of PartialOrd trait where Ord trait would be preferable"
}

declare_lint_pass!(NeedlessPartialCmp => [NEEDLESS_PARTIAL_CMP]);

impl<'tcx> LateLintPass<'tcx> for NeedlessPartialCmp {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) {
// Look for partial_cmp method calls.
if let ExprKind::MethodCall(path, parent, _, span) = e.kind
&& path.ident.name == sym::partial_cmp
&& cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::PartialOrd)
{
// does this type implement Ord as well as PartialOrd?
let implements_ord = (cx.tcx.get_diagnostic_item(sym::Ord))
.is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(parent), id, &[]));
if implements_ord {
// if so, suggest using cmp instead
let mut app = Applicability::MaybeIncorrect;
let snip = snippet_with_context(cx, span, e.span.ctxt(), "_", &mut app).0;
let new_snip = snip.to_string().replace("partial_cmp", "cmp");
span_lint_and_sugg(
cx,
NEEDLESS_PARTIAL_CMP,
span,
"partial_cmp called when cmp is implemented",
"consider writing (note the change in return type)",
new_snip,
app,
);
}
}
}
}
24 changes: 24 additions & 0 deletions tests/ui/needless_partial_cmp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#![warn(clippy::needless_partial_cmp)]
#![allow(clippy::needless_ifs)]
//@no-rustfix
use std::cmp::Ordering;

#[derive(PartialOrd, PartialEq, Ord, Eq)]
struct HasOrd(u32);

#[derive(PartialOrd, PartialEq)]
struct NoOrd(i32);
fn main() {
let a = HasOrd(6);
let b = HasOrd(87);
let c = NoOrd(4);
let d = NoOrd(5);

let _ = a.partial_cmp(&b);
//~^ needless_partial_cmp

if a.partial_cmp(&b).is_some() {}
//~^ needless_partial_cmp

if c.partial_cmp(&d).is_some() {}
}
17 changes: 17 additions & 0 deletions tests/ui/needless_partial_cmp.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error: partial_cmp called when cmp is implemented
--> tests/ui/needless_partial_cmp.rs:17:15
|
LL | let _ = a.partial_cmp(&b);
| ^^^^^^^^^^^^^^^ help: consider writing (note the change in return type): `cmp(&b)`
|
= note: `-D clippy::needless-partial-cmp` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::needless_partial_cmp)]`

error: partial_cmp called when cmp is implemented
--> tests/ui/needless_partial_cmp.rs:20:10
|
LL | if a.partial_cmp(&b).is_some() {}
| ^^^^^^^^^^^^^^^ help: consider writing (note the change in return type): `cmp(&b)`

error: aborting due to 2 previous errors