@@ -2,9 +2,10 @@ use clippy_utils::diagnostics::span_lint_and_then;
2
2
use clippy_utils:: { get_trait_def_id, match_qpath, path_res, ty:: implements_trait} ;
3
3
use rustc_errors:: Applicability ;
4
4
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 } ;
6
6
use rustc_hir_analysis:: hir_ty_to_ty;
7
7
use rustc_lint:: { LateContext , LateLintPass } ;
8
+ use rustc_middle:: ty:: { EarlyBinder , TraitRef } ;
8
9
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
9
10
use rustc_span:: def_id:: DefId ;
10
11
use rustc_span:: sym;
@@ -65,33 +66,46 @@ pub struct ManualPartialOrdAndOrdImpl {
65
66
pub ord_def_id : OnceCell < DefId > ,
66
67
}
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?
71
+
68
72
impl LateLintPass < ' _ > for ManualPartialOrdAndOrdImpl {
69
73
fn check_item ( & mut self , cx : & LateContext < ' _ > , item : & Item < ' _ > ) {
70
74
if_chain ! {
71
75
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;
74
78
if cx. tcx. is_diagnostic_item( sym:: PartialOrd , partial_ord_def_id) ;
75
79
if !cx. tcx. is_automatically_derived( item. owner_id. to_def_id( ) ) ;
76
80
then {
77
- lint_impl_body( self , cx, imp, item) ;
81
+ lint_impl_body( self , cx, imp, item, impl_trait_ref ) ;
78
82
}
79
83
}
80
84
}
81
85
}
82
86
83
87
#[ 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
+ ) {
86
95
for imp_item in imp. items {
87
96
if_chain ! {
88
97
if imp_item. ident. name == sym:: partial_cmp;
89
98
if let ImplItemKind :: Fn ( _, id) = cx. tcx. hir( ) . impl_item( imp_item. id) . kind;
90
99
then {
91
100
let body = cx. tcx. hir( ) . body( id) ;
101
+ // TODO: This can't be changed without causing compilation to fail
92
102
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
+ )
95
109
{
96
110
if_chain! {
97
111
if block. stmts. is_empty( ) ;
@@ -101,22 +115,26 @@ fn lint_impl_body(conf: &mut ManualPartialOrdAndOrdImpl, cx: &LateContext<'_>, i
101
115
if let ExprKind :: MethodCall ( cmp_path, _, [ other_expr] , ..) = cmp_expr. kind;
102
116
if cmp_path. ident. name == sym:: cmp;
103
117
if let Res :: Local ( ..) = path_res( cx, other_expr) ;
118
+ // TODO: Self explanatory
104
119
then { }
105
120
else {
106
121
span_lint_and_then(
107
122
cx,
108
123
MANUAL_PARTIAL_ORD_AND_ORD_IMPL ,
109
124
item. span,
110
125
"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?
111
129
|diag| {
112
130
if let Some ( param) = body. params. get( 1 )
113
131
&& let PatKind :: Binding ( _, _, param_ident, ..) = param. pat. kind
114
132
{
115
133
diag. span_suggestion(
116
134
block. span,
117
135
"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.
120
138
Applicability :: MaybeIncorrect
121
139
) ;
122
140
} else {
0 commit comments