1
1
use 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} ;
3
3
use rustc_errors:: Applicability ;
4
4
use rustc_hir:: def:: Res ;
5
5
use rustc_hir:: { Expr , ExprKind , Impl , ImplItemKind , Item , ItemKind , PatKind } ;
6
6
use rustc_hir_analysis:: hir_ty_to_ty;
7
7
use rustc_lint:: { LateContext , LateLintPass } ;
8
8
use 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} ;
11
10
use rustc_span:: sym;
12
- use std:: cell:: OnceCell ;
13
11
14
12
declare_clippy_lint ! {
15
13
/// ### What it does
16
14
/// Checks for manual implementations of both `PartialOrd` and `Ord` when only `Ord` is
17
15
/// necessary.
18
16
///
19
17
/// ### 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.
22
21
///
23
22
/// ### Example
24
23
/// ```rust,ignore
@@ -59,15 +58,7 @@ declare_clippy_lint! {
59
58
correctness,
60
59
"manual implementation of `PartialOrd` when `Ord` is already implemented"
61
60
}
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 ] ) ;
71
62
72
63
impl LateLintPass < ' _ > for ManualPartialOrdAndOrdImpl {
73
64
fn check_item ( & mut self , cx : & LateContext < ' _ > , item : & Item < ' _ > ) {
@@ -78,28 +69,25 @@ impl LateLintPass<'_> for ManualPartialOrdAndOrdImpl {
78
69
if cx. tcx. is_diagnostic_item( sym:: PartialOrd , partial_ord_def_id) ;
79
70
if !cx. tcx. is_automatically_derived( item. owner_id. to_def_id( ) ) ;
80
71
then {
81
- lint_impl_body( self , cx, imp, item, impl_trait_ref) ;
72
+ lint_impl_body( cx, imp, item, impl_trait_ref) ;
82
73
}
83
74
}
84
75
}
85
76
}
86
77
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 > ) {
95
79
for imp_item in imp. items {
96
80
if_chain ! {
97
81
if imp_item. ident. name == sym:: partial_cmp;
98
82
if let ImplItemKind :: Fn ( _, id) = cx. tcx. hir( ) . impl_item( imp_item. id) . kind;
99
83
then {
100
84
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( ) ;
103
91
if let ExprKind :: Block ( block, ..) = body. value. kind && implements_trait(
104
92
cx,
105
93
hir_ty_to_ty( cx. tcx, imp. self_ty) ,
@@ -111,11 +99,12 @@ fn lint_impl_body<'tcx>(
111
99
if block. stmts. is_empty( ) ;
112
100
if let Some ( expr) = block. expr;
113
101
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.
114
104
if match_qpath( some_path, & [ "Some" ] ) ;
115
105
if let ExprKind :: MethodCall ( cmp_path, _, [ other_expr] , ..) = cmp_expr. kind;
116
106
if cmp_path. ident. name == sym:: cmp;
117
107
if let Res :: Local ( ..) = path_res( cx, other_expr) ;
118
- // TODO: Self explanatory
119
108
then { }
120
109
else {
121
110
span_lint_and_then(
@@ -134,8 +123,7 @@ fn lint_impl_body<'tcx>(
134
123
block. span,
135
124
"change this to" ,
136
125
format!( "{{ Some(self.cmp({})) }}" , param_ident. name) ,
137
- // TODO: This is always correct afaik.
138
- Applicability :: MaybeIncorrect
126
+ Applicability :: Unspecified ,
139
127
) ;
140
128
} else {
141
129
diag. help( "return the value of `self.cmp` wrapped in `Some` instead" ) ;
0 commit comments