1
- use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
- use clippy_utils:: { ast_utils, is_direct_expn_of} ;
3
- use rustc_ast:: ast:: { Expr , ExprKind , Lit , LitKind } ;
1
+ use clippy_utils:: { diagnostics:: span_lint_and_sugg, higher, is_direct_expn_of, ty:: implements_trait} ;
2
+ use rustc_ast:: ast:: LitKind ;
4
3
use rustc_errors:: Applicability ;
5
- use rustc_lint:: { EarlyContext , EarlyLintPass } ;
4
+ use rustc_hir:: * ;
5
+ use rustc_lint:: { LateContext , LateLintPass } ;
6
+ use rustc_middle:: ty;
6
7
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
8
+ use rustc_span:: symbol:: Ident ;
7
9
8
10
declare_clippy_lint ! {
9
11
/// ### What it does
@@ -28,45 +30,77 @@ declare_clippy_lint! {
28
30
29
31
declare_lint_pass ! ( BoolAssertComparison => [ BOOL_ASSERT_COMPARISON ] ) ;
30
32
31
- fn is_bool_lit ( e : & Expr ) -> bool {
33
+ fn is_bool_lit ( e : & Expr < ' _ > ) -> bool {
32
34
matches ! (
33
35
e. kind,
34
36
ExprKind :: Lit ( Lit {
35
- kind : LitKind :: Bool ( _) ,
37
+ node : LitKind :: Bool ( _) ,
36
38
..
37
39
} )
38
40
) && !e. span . from_expansion ( )
39
41
}
40
42
41
- impl EarlyLintPass for BoolAssertComparison {
42
- fn check_expr ( & mut self , cx : & EarlyContext < ' _ > , e : & Expr ) {
43
+ fn impl_not_trait_with_bool_out ( cx : & LateContext < ' tcx > , e : & ' tcx Expr < ' _ > ) -> bool {
44
+ let ty = cx. typeck_results ( ) . expr_ty ( e) ;
45
+
46
+ cx. tcx
47
+ . lang_items ( )
48
+ . not_trait ( )
49
+ . filter ( |id| implements_trait ( cx, ty, * id, & [ ] ) )
50
+ . and_then ( |id| {
51
+ cx. tcx . associated_items ( id) . find_by_name_and_kind (
52
+ cx. tcx ,
53
+ Ident :: from_str ( "Output" ) ,
54
+ ty:: AssocKind :: Type ,
55
+ id,
56
+ )
57
+ } )
58
+ . map_or ( false , |item| {
59
+ let proj = cx. tcx . mk_projection ( item. def_id , cx. tcx . mk_substs_trait ( ty, & [ ] ) ) ;
60
+ let nty = cx. tcx . normalize_erasing_regions ( cx. param_env , proj) ;
61
+
62
+ nty. is_bool ( )
63
+ } )
64
+ }
65
+
66
+ impl < ' tcx > LateLintPass < ' tcx > for BoolAssertComparison {
67
+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
43
68
let macros = [ "assert_eq" , "debug_assert_eq" ] ;
44
69
let inverted_macros = [ "assert_ne" , "debug_assert_ne" ] ;
45
70
46
71
for mac in macros. iter ( ) . chain ( inverted_macros. iter ( ) ) {
47
- if let Some ( span) = is_direct_expn_of ( e. span , mac) {
48
- if let Some ( [ a, b] ) = ast_utils:: extract_assert_macro_args ( e) {
49
- let nb_bool_args = is_bool_lit ( a) as usize + is_bool_lit ( b) as usize ;
72
+ if let Some ( span) = is_direct_expn_of ( expr. span , mac) {
73
+ if let Some ( args) = higher:: extract_assert_macro_args ( expr) {
74
+ if let [ a, b, ..] = args[ ..] {
75
+ let nb_bool_args = is_bool_lit ( a) as usize + is_bool_lit ( b) as usize ;
76
+
77
+ if nb_bool_args != 1 {
78
+ // If there are two boolean arguments, we definitely don't understand
79
+ // what's going on, so better leave things as is...
80
+ //
81
+ // Or there is simply no boolean and then we can leave things as is!
82
+ return ;
83
+ }
50
84
51
- if nb_bool_args != 1 {
52
- // If there are two boolean arguments, we definitely don't understand
53
- // what's going on, so better leave things as is...
54
- //
55
- // Or there is simply no boolean and then we can leave things as is!
85
+ if !impl_not_trait_with_bool_out ( cx, a) || !impl_not_trait_with_bool_out ( cx, b) {
86
+ // At this point the expression which is not a boolean
87
+ // literal does not implement Not trait with a bool output,
88
+ // so we cannot suggest to rewrite our code
89
+ return ;
90
+ }
91
+
92
+ let non_eq_mac = & mac[ ..mac. len ( ) - 3 ] ;
93
+ span_lint_and_sugg (
94
+ cx,
95
+ BOOL_ASSERT_COMPARISON ,
96
+ span,
97
+ & format ! ( "used `{}!` with a literal bool" , mac) ,
98
+ "replace it with" ,
99
+ format ! ( "{}!(..)" , non_eq_mac) ,
100
+ Applicability :: MaybeIncorrect ,
101
+ ) ;
56
102
return ;
57
103
}
58
-
59
- let non_eq_mac = & mac[ ..mac. len ( ) - 3 ] ;
60
- span_lint_and_sugg (
61
- cx,
62
- BOOL_ASSERT_COMPARISON ,
63
- span,
64
- & format ! ( "used `{}!` with a literal bool" , mac) ,
65
- "replace it with" ,
66
- format ! ( "{}!(..)" , non_eq_mac) ,
67
- Applicability :: MaybeIncorrect ,
68
- ) ;
69
- return ;
70
104
}
71
105
}
72
106
}
0 commit comments