|
1 | 1 | use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
| 2 | +use clippy_utils::paths::ORD_CMP; |
2 | 3 | use clippy_utils::ty::implements_trait;
|
3 |
| -use clippy_utils::{get_parent_node, is_res_lang_ctor, last_path_segment, path_res}; |
| 4 | +use clippy_utils::{get_parent_node, is_res_lang_ctor, last_path_segment, match_def_path, path_res}; |
4 | 5 | use rustc_errors::Applicability;
|
5 |
| -use rustc_hir::def::Res; |
| 6 | +use rustc_hir::def_id::LocalDefId; |
6 | 7 | use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, ItemKind, LangItem, Node, UnOp};
|
7 | 8 | use rustc_hir_analysis::hir_ty_to_ty;
|
8 | 9 | use rustc_lint::{LateContext, LateLintPass};
|
@@ -60,6 +61,10 @@ declare_clippy_lint! {
|
60 | 61 | /// wrapping the result of `cmp` in `Some` for `partial_cmp`. Not doing this may silently
|
61 | 62 | /// introduce an error upon refactoring.
|
62 | 63 | ///
|
| 64 | + /// ### Known issues |
| 65 | + /// Code like `self.cmp(other).into()` will be flagged as incorrect, despite `.into()` wrapping |
| 66 | + /// it in `Some`. |
| 67 | + /// |
63 | 68 | /// ### Limitations
|
64 | 69 | /// Will not lint if `Self` and `Rhs` do not have the same type.
|
65 | 70 | ///
|
@@ -202,9 +207,8 @@ impl LateLintPass<'_> for IncorrectImpls {
|
202 | 207 | [cmp_expr],
|
203 | 208 | ) = expr.kind
|
204 | 209 | && is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome)
|
205 |
| - && let ExprKind::MethodCall(cmp_path, _, [other_expr], ..) = cmp_expr.kind |
206 |
| - && cmp_path.ident.name == sym::cmp |
207 |
| - && let Res::Local(..) = path_res(cx, other_expr) |
| 210 | + // Fix #11178, allow `Self::cmp(self, ..)` too |
| 211 | + && self_cmp_call(cx, cmp_expr, impl_item.owner_id.def_id) |
208 | 212 | {} else {
|
209 | 213 | // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid
|
210 | 214 | // suggestion tons more complex.
|
@@ -242,3 +246,21 @@ impl LateLintPass<'_> for IncorrectImpls {
|
242 | 246 | }
|
243 | 247 | }
|
244 | 248 | }
|
| 249 | + |
| 250 | +/// Returns whether this is `self.cmp(..)`, `Self::cmp(self, ..)` or `Ord::cmp(self, ..)`. |
| 251 | +fn self_cmp_call<'tcx>(cx: &LateContext<'tcx>, cmp_expr: &'tcx Expr<'tcx>, def_id: LocalDefId) -> bool { |
| 252 | + match cmp_expr.kind { |
| 253 | + ExprKind::Call(path, [_self, _other]) => path_res(cx, path) |
| 254 | + .opt_def_id() |
| 255 | + .is_some_and(|def_id| match_def_path(cx, def_id, &ORD_CMP)), |
| 256 | + ExprKind::MethodCall(_, _, [_other], ..) => { |
| 257 | + // It's a bit annoying but `typeck_results` only gives us the CURRENT body, which we have none, not |
| 258 | + // of any `LocalDefId` we want, so we must call the query itself to avoid an immediate ICE |
| 259 | + cx.tcx |
| 260 | + .typeck(def_id) |
| 261 | + .type_dependent_def_id(cmp_expr.hir_id) |
| 262 | + .is_some_and(|def_id| match_def_path(cx, def_id, &ORD_CMP)) |
| 263 | + }, |
| 264 | + _ => false, |
| 265 | + } |
| 266 | +} |
0 commit comments