1
1
use crate :: consts:: { constant, Constant } ;
2
2
use crate :: utils:: paths;
3
- use crate :: utils:: { is_direct_expn_of, is_expn_of, match_def_path, span_help_and_lint , snippet } ;
3
+ use crate :: utils:: { is_direct_expn_of, is_expn_of, match_def_path, snippet_opt , span_help_and_lint } ;
4
4
use if_chain:: if_chain;
5
5
use rustc:: hir:: * ;
6
6
use rustc:: lint:: { LateContext , LateLintPass , LintArray , LintPass } ;
7
7
use rustc:: { declare_lint_pass, declare_tool_lint} ;
8
8
use syntax:: ast:: LitKind ;
9
- use std:: borrow:: Cow ;
10
9
11
10
declare_clippy_lint ! {
12
11
/// **What it does:** Checks for `assert!(true)` and `assert!(false)` calls.
@@ -34,72 +33,68 @@ declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]);
34
33
35
34
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for AssertionsOnConstants {
36
35
fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , e : & ' tcx Expr ) {
37
- let lint_assert_cb = |is_debug_assert : bool | {
38
- if let ExprKind :: Unary ( _, ref lit) = e. kind {
39
- if let Some ( ( Constant :: Bool ( is_true) , _) ) = constant ( cx, cx. tables , lit) {
40
- if is_true {
41
- span_help_and_lint (
42
- cx,
43
- ASSERTIONS_ON_CONSTANTS ,
44
- e. span ,
45
- "`assert!(true)` will be optimized out by the compiler" ,
46
- "remove it" ,
47
- ) ;
48
- } else if !is_debug_assert {
49
- span_help_and_lint (
50
- cx,
51
- ASSERTIONS_ON_CONSTANTS ,
52
- e. span ,
53
- "`assert!(false)` should probably be replaced" ,
54
- "use `panic!()` or `unreachable!()`" ,
55
- ) ;
56
- }
57
- }
58
- }
36
+ let lint_true = || {
37
+ span_help_and_lint (
38
+ cx,
39
+ ASSERTIONS_ON_CONSTANTS ,
40
+ e. span ,
41
+ "`assert!(true)` will be optimized out by the compiler" ,
42
+ "remove it" ,
43
+ ) ;
44
+ } ;
45
+ let lint_false_without_message = || {
46
+ span_help_and_lint (
47
+ cx,
48
+ ASSERTIONS_ON_CONSTANTS ,
49
+ e. span ,
50
+ "`assert!(false)` should probably be replaced" ,
51
+ "use `panic!()` or `unreachable!()`" ,
52
+ ) ;
59
53
} ;
54
+ let lint_false_with_message = |panic_message : String | {
55
+ span_help_and_lint (
56
+ cx,
57
+ ASSERTIONS_ON_CONSTANTS ,
58
+ e. span ,
59
+ & format ! ( "`assert!(false, {})` should probably be replaced" , panic_message) ,
60
+ & format ! ( "use `panic!({})` or `unreachable!({})`" , panic_message, panic_message) ,
61
+ )
62
+ } ;
63
+
60
64
if let Some ( debug_assert_span) = is_expn_of ( e. span , "debug_assert" ) {
61
65
if debug_assert_span. from_expansion ( ) {
62
66
return ;
63
67
}
64
- lint_assert_cb ( true ) ;
68
+ if_chain ! {
69
+ if let ExprKind :: Unary ( _, ref lit) = e. kind;
70
+ if let Some ( ( Constant :: Bool ( is_true) , _) ) = constant( cx, cx. tables, lit) ;
71
+ if is_true;
72
+ then {
73
+ lint_true( ) ;
74
+ }
75
+ } ;
65
76
} else if let Some ( assert_span) = is_direct_expn_of ( e. span , "assert" ) {
66
77
if assert_span. from_expansion ( ) {
67
78
return ;
68
79
}
69
- if let Some ( ( panic_message, is_true) ) = assert_with_message ( & cx, e) {
70
- if is_true {
71
- span_help_and_lint (
72
- cx,
73
- ASSERTIONS_ON_CONSTANTS ,
74
- e. span ,
75
- "`assert!(true)` will be optimized out by the compiler" ,
76
- "remove it" ,
77
- ) ;
78
- } else if panic_message. is_empty ( ) || panic_message. starts_with ( "\" assertion failed: " ) {
79
- span_help_and_lint (
80
- cx,
81
- ASSERTIONS_ON_CONSTANTS ,
82
- e. span ,
83
- "`assert!(false)` should probably be replaced" ,
84
- "use `panic!()` or `unreachable!()`" ,
85
- ) ;
86
- } else {
87
- span_help_and_lint (
88
- cx,
89
- ASSERTIONS_ON_CONSTANTS ,
90
- e. span ,
91
- & format ! ( "`assert!(false, {})` should probably be replaced" , panic_message, ) ,
92
- & format ! (
93
- "use `panic!({})` or `unreachable!({})`" ,
94
- panic_message, panic_message,
95
- ) ,
96
- ) ;
97
- }
80
+ if let Some ( assert_match) = match_assert_with_message ( & cx, e) {
81
+ match assert_match {
82
+ // matched assert but not message
83
+ AssertKind :: WithoutMessage ( false ) => lint_false_without_message ( ) ,
84
+ AssertKind :: WithoutMessage ( true ) | AssertKind :: WithMessage ( _, true ) => lint_true ( ) ,
85
+ AssertKind :: WithMessage ( panic_message, false ) => lint_false_with_message ( panic_message) ,
86
+ } ;
98
87
}
99
88
}
100
89
}
101
90
}
102
91
92
+ /// Result of calling `match_assert_with_message`.
93
+ enum AssertKind {
94
+ WithMessage ( String , bool ) ,
95
+ WithoutMessage ( bool ) ,
96
+ }
97
+
103
98
/// Check if the expression matches
104
99
///
105
100
/// ```rust,ignore
@@ -113,13 +108,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssertionsOnConstants {
113
108
/// };
114
109
/// ```
115
110
///
116
- /// where `message` is a string literal and `c` is a constant bool.
117
- ///
118
- /// TODO extend this to match anything as message not just string literals
119
- ///
120
- /// Returns the `message` argument of `begin_panic` and the value of `c` which is the
121
- /// first argument of `assert!`.
122
- fn assert_with_message < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr ) -> Option < ( Cow < ' a , str > , bool ) > {
111
+ /// where `message` is any expression and `c` is a constant bool.
112
+ fn match_assert_with_message < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr ) -> Option < AssertKind > {
123
113
if_chain ! {
124
114
if let ExprKind :: Match ( ref expr, ref arms, _) = expr. kind;
125
115
// matches { let _t = expr; _t }
@@ -140,12 +130,17 @@ fn assert_with_message<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) -
140
130
// function call
141
131
if let Some ( args) = match_function_call( cx, begin_panic_call, & paths:: BEGIN_PANIC ) ;
142
132
if args. len( ) == 2 ;
143
- // bind the second argument of the `assert!` macro
144
- let panic_message_arg = snippet ( cx, args[ 0 ] . span, ".." ) ;
133
+ // bind the second argument of the `assert!` macro if it exists
134
+ if let panic_message = snippet_opt ( cx, args[ 0 ] . span) ;
145
135
// second argument of begin_panic is irrelevant
146
136
// as is the second match arm
147
137
then {
148
- return Some ( ( panic_message_arg, is_true) ) ;
138
+ // an empty message occurs when it was generated by the macro
139
+ // (and not passed by the user)
140
+ return panic_message
141
+ . filter( |msg| !msg. is_empty( ) )
142
+ . map( |msg| AssertKind :: WithMessage ( msg, is_true) )
143
+ . or( Some ( AssertKind :: WithoutMessage ( is_true) ) ) ;
149
144
}
150
145
}
151
146
None
0 commit comments