Skip to content

Commit cbd63a5

Browse files
committed
check bounds to increase accuracy, and add todos
1 parent beb7d29 commit cbd63a5

File tree

2 files changed

+45
-10
lines changed

2 files changed

+45
-10
lines changed

clippy_lints/src/manual_partial_ord_and_ord_impl.rs

+28-10
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::{get_trait_def_id, match_qpath, path_res, ty::implements_trait};
33
use rustc_errors::Applicability;
44
use rustc_hir::def::Res;
5-
use rustc_hir::{Expr, ExprKind, Impl, ImplItemKind, Item, ItemKind, PatKind, QPath};
5+
use rustc_hir::{Expr, ExprKind, Impl, ImplItemKind, Item, ItemKind, PatKind};
66
use rustc_hir_analysis::hir_ty_to_ty;
77
use rustc_lint::{LateContext, LateLintPass};
8+
use rustc_middle::ty::{EarlyBinder, TraitRef};
89
use rustc_session::{declare_tool_lint, impl_lint_pass};
910
use rustc_span::def_id::DefId;
1011
use rustc_span::sym;
@@ -65,33 +66,46 @@ pub struct ManualPartialOrdAndOrdImpl {
6566
pub ord_def_id: OnceCell<DefId>,
6667
}
6768

69+
// TODO: The number of if_chain! calls makes this is a bit hard to follow, can that be reduced?
70+
// or more generally, can this be done cleaner?
71+
6872
impl LateLintPass<'_> for ManualPartialOrdAndOrdImpl {
6973
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
7074
if_chain! {
7175
if let ItemKind::Impl(imp) = &item.kind;
72-
if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id);
73-
let partial_ord_def_id = impl_trait_ref.skip_binder().def_id;
76+
if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder);
77+
let partial_ord_def_id = impl_trait_ref.def_id;
7478
if cx.tcx.is_diagnostic_item(sym::PartialOrd, partial_ord_def_id);
7579
if !cx.tcx.is_automatically_derived(item.owner_id.to_def_id());
7680
then {
77-
lint_impl_body(self, cx, imp, item);
81+
lint_impl_body(self, cx, imp, item, impl_trait_ref);
7882
}
7983
}
8084
}
8185
}
8286

8387
#[allow(clippy::unnecessary_def_path)] // ???
84-
// ^ line 91, that can't be changed without causing compilation to fail
85-
fn lint_impl_body(conf: &mut ManualPartialOrdAndOrdImpl, cx: &LateContext<'_>, imp: &Impl<'_>, item: &Item<'_>) {
88+
fn lint_impl_body<'tcx>(
89+
conf: &mut ManualPartialOrdAndOrdImpl,
90+
cx: &LateContext<'tcx>,
91+
imp: &Impl<'_>,
92+
item: &Item<'_>,
93+
impl_trait_ref: TraitRef<'tcx>,
94+
) {
8695
for imp_item in imp.items {
8796
if_chain! {
8897
if imp_item.ident.name == sym::partial_cmp;
8998
if let ImplItemKind::Fn(_, id) = cx.tcx.hir().impl_item(imp_item.id).kind;
9099
then {
91100
let body = cx.tcx.hir().body(id);
101+
// TODO: This can't be changed without causing compilation to fail
92102
let ord_def_id = conf.ord_def_id.get_or_init(|| get_trait_def_id(cx, &["core", "cmp", "Ord"]).unwrap());
93-
if let ExprKind::Block(block, ..)
94-
= body.value.kind && implements_trait(cx, hir_ty_to_ty(cx.tcx, imp.self_ty), *ord_def_id, &[])
103+
if let ExprKind::Block(block, ..) = body.value.kind && implements_trait(
104+
cx,
105+
hir_ty_to_ty(cx.tcx, imp.self_ty),
106+
*ord_def_id,
107+
impl_trait_ref.substs,
108+
)
95109
{
96110
if_chain! {
97111
if block.stmts.is_empty();
@@ -101,22 +115,26 @@ fn lint_impl_body(conf: &mut ManualPartialOrdAndOrdImpl, cx: &LateContext<'_>, i
101115
if let ExprKind::MethodCall(cmp_path, _, [other_expr], ..) = cmp_expr.kind;
102116
if cmp_path.ident.name == sym::cmp;
103117
if let Res::Local(..) = path_res(cx, other_expr);
118+
// TODO: Self explanatory
104119
then {}
105120
else {
106121
span_lint_and_then(
107122
cx,
108123
MANUAL_PARTIAL_ORD_AND_ORD_IMPL,
109124
item.span,
110125
"manual implementation of `PartialOrd` when `Ord` is already implemented",
126+
// TODO: Is this how this should be done? Should we instead suggest
127+
// changing the entire block, including the name
128+
// of (by default) other if we can't find it?
111129
|diag| {
112130
if let Some(param) = body.params.get(1)
113131
&& let PatKind::Binding(_, _, param_ident, ..) = param.pat.kind
114132
{
115133
diag.span_suggestion(
116134
block.span,
117135
"change this to",
118-
format!("{{ Some(self.cmp({})) }}",
119-
param_ident.name),
136+
format!("{{ Some(self.cmp({})) }}", param_ident.name),
137+
// TODO: This is always correct afaik.
120138
Applicability::MaybeIncorrect
121139
);
122140
} else {

tests/ui/manual_partial_ord_and_ord_impl.rs

+17
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,20 @@ impl PartialOrd for E {
7070
todo!();
7171
}
7272
}
73+
74+
// do not lint since ord has more restrictive bounds
75+
76+
#[derive(Eq, PartialEq)]
77+
struct Uwu<A>(A);
78+
79+
impl<A: std::fmt::Debug + Ord + PartialOrd> Ord for Uwu<A> {
80+
fn cmp(&self, other: &Self) -> Ordering {
81+
todo!();
82+
}
83+
}
84+
85+
impl<A: Ord + PartialOrd> PartialOrd for Uwu<A> {
86+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
87+
todo!();
88+
}
89+
}

0 commit comments

Comments
 (0)