11use crate :: consts:: { constant, Constant } ;
22use 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 } ;
44use if_chain:: if_chain;
55use rustc:: hir:: * ;
66use rustc:: lint:: { LateContext , LateLintPass , LintArray , LintPass } ;
77use rustc:: { declare_lint_pass, declare_tool_lint} ;
88use syntax:: ast:: LitKind ;
9- use std:: borrow:: Cow ;
109
1110declare_clippy_lint ! {
1211 /// **What it does:** Checks for `assert!(true)` and `assert!(false)` calls.
@@ -34,72 +33,68 @@ declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]);
3433
3534impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for AssertionsOnConstants {
3635 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+ ) ;
5953 } ;
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+
6064 if let Some ( debug_assert_span) = is_expn_of ( e. span , "debug_assert" ) {
6165 if debug_assert_span. from_expansion ( ) {
6266 return ;
6367 }
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+ } ;
6576 } else if let Some ( assert_span) = is_direct_expn_of ( e. span , "assert" ) {
6677 if assert_span. from_expansion ( ) {
6778 return ;
6879 }
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+ } ;
9887 }
9988 }
10089 }
10190}
10291
92+ /// Result of calling `match_assert_with_message`.
93+ enum AssertKind {
94+ WithMessage ( String , bool ) ,
95+ WithoutMessage ( bool ) ,
96+ }
97+
10398/// Check if the expression matches
10499///
105100/// ```rust,ignore
@@ -113,13 +108,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssertionsOnConstants {
113108/// };
114109/// ```
115110///
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 > {
123113 if_chain ! {
124114 if let ExprKind :: Match ( ref expr, ref arms, _) = expr. kind;
125115 // matches { let _t = expr; _t }
@@ -140,12 +130,17 @@ fn assert_with_message<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) -
140130 // function call
141131 if let Some ( args) = match_function_call( cx, begin_panic_call, & paths:: BEGIN_PANIC ) ;
142132 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) ;
145135 // second argument of begin_panic is irrelevant
146136 // as is the second match arm
147137 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) ) ) ;
149144 }
150145 }
151146 None
0 commit comments