11use clippy_utils:: diagnostics:: span_lint_and_then;
2- use clippy_utils:: { get_trait_def_id , match_qpath, path_res, ty:: implements_trait} ;
2+ use clippy_utils:: { match_qpath, path_res, ty:: implements_trait} ;
33use rustc_errors:: Applicability ;
44use rustc_hir:: def:: Res ;
55use rustc_hir:: { Expr , ExprKind , Impl , ImplItemKind , Item , ItemKind , PatKind } ;
66use rustc_hir_analysis:: hir_ty_to_ty;
77use rustc_lint:: { LateContext , LateLintPass } ;
88use rustc_middle:: ty:: { EarlyBinder , TraitRef } ;
9- use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
10- use rustc_span:: def_id:: DefId ;
9+ use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
1110use rustc_span:: sym;
12- use std:: cell:: OnceCell ;
1311
1412declare_clippy_lint ! {
1513 /// ### What it does
1614 /// Checks for manual implementations of both `PartialOrd` and `Ord` when only `Ord` is
1715 /// necessary.
1816 ///
1917 /// ### Why is this bad?
20- /// If both `PartialOrd` and `Ord` are implemented, `PartialOrd` will wrap the returned value of
21- /// `Ord::cmp` in `Some`. Not doing this may silently introduce an error upon refactoring.
18+ /// If both `PartialOrd` and `Ord` are implemented, they must agree. This is commonly done by
19+ /// wrapping the result of `cmp` in `Some` for `partial_cmp`. Not doing this may silently
20+ /// introduce an error upon refactoring.
2221 ///
2322 /// ### Example
2423 /// ```rust,ignore
@@ -59,15 +58,7 @@ declare_clippy_lint! {
5958 correctness,
6059 "manual implementation of `PartialOrd` when `Ord` is already implemented"
6160}
62- impl_lint_pass ! ( ManualPartialOrdAndOrdImpl => [ MANUAL_PARTIAL_ORD_AND_ORD_IMPL ] ) ;
63-
64- #[ derive( Clone ) ]
65- pub struct ManualPartialOrdAndOrdImpl {
66- pub ord_def_id : OnceCell < DefId > ,
67- }
68-
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?
61+ declare_lint_pass ! ( ManualPartialOrdAndOrdImpl => [ MANUAL_PARTIAL_ORD_AND_ORD_IMPL ] ) ;
7162
7263impl LateLintPass < ' _ > for ManualPartialOrdAndOrdImpl {
7364 fn check_item ( & mut self , cx : & LateContext < ' _ > , item : & Item < ' _ > ) {
@@ -78,28 +69,25 @@ impl LateLintPass<'_> for ManualPartialOrdAndOrdImpl {
7869 if cx. tcx. is_diagnostic_item( sym:: PartialOrd , partial_ord_def_id) ;
7970 if !cx. tcx. is_automatically_derived( item. owner_id. to_def_id( ) ) ;
8071 then {
81- lint_impl_body( self , cx, imp, item, impl_trait_ref) ;
72+ lint_impl_body( cx, imp, item, impl_trait_ref) ;
8273 }
8374 }
8475 }
8576}
8677
87- #[ allow( clippy:: unnecessary_def_path) ] // ???
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- ) {
78+ fn lint_impl_body < ' tcx > ( cx : & LateContext < ' tcx > , imp : & Impl < ' _ > , item : & Item < ' _ > , impl_trait_ref : TraitRef < ' tcx > ) {
9579 for imp_item in imp. items {
9680 if_chain ! {
9781 if imp_item. ident. name == sym:: partial_cmp;
9882 if let ImplItemKind :: Fn ( _, id) = cx. tcx. hir( ) . impl_item( imp_item. id) . kind;
9983 then {
10084 let body = cx. tcx. hir( ) . body( id) ;
101- // TODO: This can't be changed without causing compilation to fail
102- let ord_def_id = conf. ord_def_id. get_or_init( || get_trait_def_id( cx, & [ "core" , "cmp" , "Ord" ] ) . unwrap( ) ) ;
85+ let ord_def_id = cx
86+ . tcx
87+ . diagnostic_items( impl_trait_ref. def_id. krate)
88+ . name_to_id
89+ . get( & sym:: Ord )
90+ . unwrap( ) ;
10391 if let ExprKind :: Block ( block, ..) = body. value. kind && implements_trait(
10492 cx,
10593 hir_ty_to_ty( cx. tcx, imp. self_ty) ,
@@ -111,11 +99,12 @@ fn lint_impl_body<'tcx>(
11199 if block. stmts. is_empty( ) ;
112100 if let Some ( expr) = block. expr;
113101 if let ExprKind :: Call ( Expr { kind: ExprKind :: Path ( some_path) , ..} , [ cmp_expr] ) = expr. kind;
102+ // TODO: We can likely use the `option_some_variant` lang item instead, but this may be tricky
103+ // due to the fact that `expr` is the constructor, not `option_some_variant` itself.
114104 if match_qpath( some_path, & [ "Some" ] ) ;
115105 if let ExprKind :: MethodCall ( cmp_path, _, [ other_expr] , ..) = cmp_expr. kind;
116106 if cmp_path. ident. name == sym:: cmp;
117107 if let Res :: Local ( ..) = path_res( cx, other_expr) ;
118- // TODO: Self explanatory
119108 then { }
120109 else {
121110 span_lint_and_then(
@@ -134,8 +123,7 @@ fn lint_impl_body<'tcx>(
134123 block. span,
135124 "change this to" ,
136125 format!( "{{ Some(self.cmp({})) }}" , param_ident. name) ,
137- // TODO: This is always correct afaik.
138- Applicability :: MaybeIncorrect
126+ Applicability :: Unspecified ,
139127 ) ;
140128 } else {
141129 diag. help( "return the value of `self.cmp` wrapped in `Some` instead" ) ;
0 commit comments