|
1 | 1 | use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
2 | 2 | use clippy_utils::ty::implements_trait;
|
3 |
| -use clippy_utils::{is_from_proc_macro, is_res_lang_ctor, last_path_segment, path_res, std_or_core}; |
| 3 | +use clippy_utils::{ |
| 4 | + is_diag_trait_item, is_from_proc_macro, is_res_lang_ctor, last_path_segment, path_res, std_or_core, |
| 5 | +}; |
4 | 6 | use rustc_errors::Applicability;
|
5 | 7 | use rustc_hir::def_id::LocalDefId;
|
6 | 8 | use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, LangItem, Node, UnOp};
|
@@ -98,7 +100,7 @@ declare_clippy_lint! {
|
98 | 100 | ///
|
99 | 101 | /// impl PartialOrd for A {
|
100 | 102 | /// fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
101 |
| - /// Some(self.cmp(other)) |
| 103 | + /// Some(self.cmp(other)) // or self.cmp(other).into() |
102 | 104 | /// }
|
103 | 105 | /// }
|
104 | 106 | /// ```
|
@@ -185,88 +187,96 @@ impl LateLintPass<'_> for NonCanonicalImpls {
|
185 | 187 |
|
186 | 188 | if block.stmts.is_empty()
|
187 | 189 | && let Some(expr) = block.expr
|
188 |
| - && expr_is_cmp(cx, &expr.kind, impl_item, &mut needs_fully_qualified) |
| 190 | + && expr_is_cmp(cx, expr, impl_item, &mut needs_fully_qualified) |
189 | 191 | {
|
| 192 | + return; |
190 | 193 | }
|
191 | 194 | // Fix #12683, allow [`needless_return`] here
|
192 | 195 | else if block.expr.is_none()
|
193 | 196 | && let Some(stmt) = block.stmts.first()
|
194 | 197 | && let rustc_hir::StmtKind::Semi(Expr {
|
195 |
| - kind: ExprKind::Ret(Some(Expr { kind: ret_kind, .. })), |
| 198 | + kind: ExprKind::Ret(Some(ret)), |
196 | 199 | ..
|
197 | 200 | }) = stmt.kind
|
198 |
| - && expr_is_cmp(cx, ret_kind, impl_item, &mut needs_fully_qualified) |
| 201 | + && expr_is_cmp(cx, ret, impl_item, &mut needs_fully_qualified) |
199 | 202 | {
|
200 |
| - } else { |
201 |
| - // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid |
202 |
| - // suggestion tons more complex. |
203 |
| - if let [lhs, rhs, ..] = trait_impl.args.as_slice() |
204 |
| - && lhs != rhs |
205 |
| - { |
206 |
| - return; |
207 |
| - } |
| 203 | + return; |
| 204 | + } |
| 205 | + // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid |
| 206 | + // suggestion tons more complex. |
| 207 | + else if let [lhs, rhs, ..] = trait_impl.args.as_slice() |
| 208 | + && lhs != rhs |
| 209 | + { |
| 210 | + return; |
| 211 | + } |
208 | 212 |
|
209 |
| - span_lint_and_then( |
210 |
| - cx, |
211 |
| - NON_CANONICAL_PARTIAL_ORD_IMPL, |
212 |
| - item.span, |
213 |
| - "non-canonical implementation of `partial_cmp` on an `Ord` type", |
214 |
| - |diag| { |
215 |
| - let [_, other] = body.params else { |
216 |
| - return; |
217 |
| - }; |
218 |
| - let Some(std_or_core) = std_or_core(cx) else { |
219 |
| - return; |
220 |
| - }; |
| 213 | + span_lint_and_then( |
| 214 | + cx, |
| 215 | + NON_CANONICAL_PARTIAL_ORD_IMPL, |
| 216 | + item.span, |
| 217 | + "non-canonical implementation of `partial_cmp` on an `Ord` type", |
| 218 | + |diag| { |
| 219 | + let [_, other] = body.params else { |
| 220 | + return; |
| 221 | + }; |
| 222 | + let Some(std_or_core) = std_or_core(cx) else { |
| 223 | + return; |
| 224 | + }; |
221 | 225 |
|
222 |
| - let suggs = match (other.pat.simple_ident(), needs_fully_qualified) { |
223 |
| - (Some(other_ident), true) => vec![( |
| 226 | + let suggs = match (other.pat.simple_ident(), needs_fully_qualified) { |
| 227 | + (Some(other_ident), true) => vec![( |
| 228 | + block.span, |
| 229 | + format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, {})) }}", other_ident.name), |
| 230 | + )], |
| 231 | + (Some(other_ident), false) => { |
| 232 | + vec![(block.span, format!("{{ Some(self.cmp({})) }}", other_ident.name))] |
| 233 | + }, |
| 234 | + (None, true) => vec![ |
| 235 | + ( |
224 | 236 | block.span,
|
225 |
| - format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, {})) }}", other_ident.name), |
226 |
| - )], |
227 |
| - (Some(other_ident), false) => { |
228 |
| - vec![(block.span, format!("{{ Some(self.cmp({})) }}", other_ident.name))] |
229 |
| - }, |
230 |
| - (None, true) => vec![ |
231 |
| - ( |
232 |
| - block.span, |
233 |
| - format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, other)) }}"), |
234 |
| - ), |
235 |
| - (other.pat.span, "other".to_owned()), |
236 |
| - ], |
237 |
| - (None, false) => vec![ |
238 |
| - (block.span, "{ Some(self.cmp(other)) }".to_owned()), |
239 |
| - (other.pat.span, "other".to_owned()), |
240 |
| - ], |
241 |
| - }; |
| 237 | + format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, other)) }}"), |
| 238 | + ), |
| 239 | + (other.pat.span, "other".to_owned()), |
| 240 | + ], |
| 241 | + (None, false) => vec![ |
| 242 | + (block.span, "{ Some(self.cmp(other)) }".to_owned()), |
| 243 | + (other.pat.span, "other".to_owned()), |
| 244 | + ], |
| 245 | + }; |
242 | 246 |
|
243 |
| - diag.multipart_suggestion("change this to", suggs, Applicability::Unspecified); |
244 |
| - }, |
245 |
| - ); |
246 |
| - } |
| 247 | + diag.multipart_suggestion("change this to", suggs, Applicability::Unspecified); |
| 248 | + }, |
| 249 | + ); |
247 | 250 | }
|
248 | 251 | }
|
249 | 252 | }
|
250 | 253 |
|
251 | 254 | /// Return true if `expr_kind` is a `cmp` call.
|
252 | 255 | fn expr_is_cmp<'tcx>(
|
253 | 256 | cx: &LateContext<'tcx>,
|
254 |
| - expr_kind: &'tcx ExprKind<'tcx>, |
| 257 | + expr: &'tcx Expr<'tcx>, |
255 | 258 | impl_item: &ImplItem<'_>,
|
256 | 259 | needs_fully_qualified: &mut bool,
|
257 | 260 | ) -> bool {
|
| 261 | + let impl_item_did = impl_item.owner_id.def_id; |
258 | 262 | if let ExprKind::Call(
|
259 | 263 | Expr {
|
260 | 264 | kind: ExprKind::Path(some_path),
|
261 | 265 | hir_id: some_hir_id,
|
262 | 266 | ..
|
263 | 267 | },
|
264 | 268 | [cmp_expr],
|
265 |
| - ) = expr_kind |
| 269 | + ) = expr.kind |
266 | 270 | {
|
267 | 271 | is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome)
|
268 | 272 | // Fix #11178, allow `Self::cmp(self, ..)` too
|
269 |
| - && self_cmp_call(cx, cmp_expr, impl_item.owner_id.def_id, needs_fully_qualified) |
| 273 | + && self_cmp_call(cx, cmp_expr, impl_item_did, needs_fully_qualified) |
| 274 | + } else if let ExprKind::MethodCall(_, recv, [], _) = expr.kind { |
| 275 | + cx.tcx |
| 276 | + .typeck(impl_item_did) |
| 277 | + .type_dependent_def_id(expr.hir_id) |
| 278 | + .is_some_and(|def_id| is_diag_trait_item(cx, def_id, sym::Into)) |
| 279 | + && self_cmp_call(cx, recv, impl_item_did, needs_fully_qualified) |
270 | 280 | } else {
|
271 | 281 | false
|
272 | 282 | }
|
|
0 commit comments