1- use clippy_utils:: diagnostics:: span_lint_and_sugg ;
1+ use clippy_utils:: diagnostics:: span_lint_and_then ;
22use clippy_utils:: macros:: { find_assert_args, root_macro_call_first_node} ;
33use clippy_utils:: source:: snippet;
44use rustc_errors:: Applicability ;
@@ -22,22 +22,20 @@ declare_clippy_lint! {
2222 /// ```no_run
2323 /// assert_eq!(a, b);
2424 /// assert_ne!(c,d);
25- /// ...
25+ /// /* ... */
2626 /// ```
2727 #[ clippy:: version = "1.95.0" ]
2828 pub ASSERT_MULTIPLE ,
2929 nursery,
30- "Splitting an assert using && into separate asserts makes it clearer which is failing."
30+ "Splitting an assert using '&&' into separate asserts makes it clearer which is failing."
3131}
3232
3333declare_lint_pass ! ( AssertMultiple => [ ASSERT_MULTIPLE ] ) ;
3434
35- // the visitor needs a mutable reference to a vector that lives
36- // only for the duration of a single `check_expr` invocation. we
37- // therefore introduce a separate lifetime `'v` for that borrow.
3835struct AssertVisitor < ' tcx , ' v > {
3936 cx : & ' v LateContext < ' tcx > ,
4037 suggests : Vec < String > ,
38+ assert_string : String ,
4139}
4240
4341impl < ' tcx > Visitor < ' tcx > for AssertVisitor < ' tcx , ' _ > {
@@ -48,15 +46,19 @@ impl<'tcx> Visitor<'tcx> for AssertVisitor<'tcx, '_> {
4846 rustc_hir:: intravisit:: walk_expr ( self , lhs) ;
4947 rustc_hir:: intravisit:: walk_expr ( self , rhs) ;
5048 } ,
49+ BinOpKind :: Or => {
50+ let tmpstr = format ! ( "{}!{};" , self . assert_string, snippet( self . cx, e. span, ".." ) ) ;
51+ self . suggests . push ( tmpstr) ;
52+ } ,
5153 _ => {
52- if let Some ( x) = assert_from_op ( self . cx , op. node , * lhs, * rhs) {
54+ if let Some ( x) = assert_from_op ( self , op. node , * lhs, * rhs) {
5355 self . suggests . push ( x) ;
5456 }
5557 } ,
5658 } ,
5759 ExprKind :: Call ( _call, _args) => {
5860 let tmptxt = snippet ( self . cx , e. span , ".." ) ;
59- let tmpassrt = format ! ( "assert !({tmptxt});" ) ;
61+ let tmpassrt = format ! ( "{} !({tmptxt});" , self . assert_string ) ;
6062 self . suggests . push ( tmpassrt) ;
6163 } ,
6264
@@ -75,44 +77,64 @@ impl<'tcx> Visitor<'tcx> for AssertVisitor<'tcx, '_> {
7577impl < ' tcx > LateLintPass < ' tcx > for AssertMultiple {
7678 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , e : & ' tcx Expr < ' tcx > ) {
7779 if let Some ( macro_call) = root_macro_call_first_node ( cx, e)
78- && matches ! ( cx. tcx. get_diagnostic_name( macro_call. def_id) , Some ( sym:: assert_macro) )
80+ && matches ! (
81+ cx. tcx. get_diagnostic_name( macro_call. def_id) ,
82+ Some ( sym:: assert_macro | sym:: debug_assert_macro)
83+ )
7984 && let Some ( ( condition, _) ) = find_assert_args ( cx, e, macro_call. expn )
8085 && matches ! ( condition. kind, ExprKind :: Binary ( binop, _lhs, _rhs) if binop. node == BinOpKind :: And )
8186 {
8287 let mut am_visitor = AssertVisitor {
8388 cx,
8489 suggests : Vec :: new ( ) ,
90+ assert_string : if matches ! (
91+ cx. tcx. get_diagnostic_name( macro_call. def_id) ,
92+ Some ( sym:: debug_assert_macro)
93+ ) {
94+ "debug_assert" . to_string ( )
95+ } else {
96+ "assert" . to_string ( )
97+ } ,
8598 } ;
8699 rustc_hir:: intravisit:: walk_expr ( & mut am_visitor, condition) ;
87100
88101 if !am_visitor. suggests . is_empty ( ) {
89- // build the suggestion string outside of the closure to avoid
90- // borrowing `suggests` while the diag closure runs
91- let text = am_visitor. suggests . join ( "\n " ) ;
92- let applicability = Applicability :: MaybeIncorrect ;
93- span_lint_and_sugg (
102+ span_lint_and_then (
94103 cx,
95104 ASSERT_MULTIPLE ,
96105 e. span ,
97- "Multiple asserts combined into one" ,
98- "consider writing" ,
99- text. clone ( ) ,
100- applicability,
106+ "multiple asserts combined into one" ,
107+ |diag| {
108+ diag. span_suggestion (
109+ e. span ,
110+ "consider writing" ,
111+ am_visitor. suggests . join ( "\n " ) ,
112+ Applicability :: MaybeIncorrect ,
113+ ) ;
114+ } ,
101115 ) ;
102116 }
103117 }
104118 }
105119}
106120
107- fn assert_from_op ( cx : & LateContext < ' _ > , node : BinOpKind , lhs : Expr < ' _ > , rhs : Expr < ' _ > ) -> Option < String > {
108- let lhs_name = snippet ( cx, lhs. span , ".." ) ;
109- let rhs_name = snippet ( cx, rhs. span , ".." ) ;
121+ fn assert_from_op (
122+ visitor : & mut AssertVisitor < ' _ , ' _ > ,
123+ node : BinOpKind ,
124+ lhs : Expr < ' _ > ,
125+ rhs : Expr < ' _ > ,
126+ ) -> Option < String > {
127+ let cx = visitor. cx ;
128+ let lhs_name = snippet ( cx, lhs. span , "_" ) ;
129+ let rhs_name = snippet ( cx, rhs. span , "_" ) ;
110130 match node {
111- BinOpKind :: Eq => Some ( format ! ( "assert_eq!({lhs_name}, {rhs_name});" ) ) ,
112- BinOpKind :: Ne => Some ( format ! ( "assert_ne!({lhs_name}, {rhs_name});" ) ) ,
113- BinOpKind :: Ge | BinOpKind :: Gt | BinOpKind :: Le | BinOpKind :: Lt => {
114- Some ( format ! ( "assert!({lhs_name} {} {rhs_name})" , node. as_str( ) ) )
115- } ,
131+ BinOpKind :: Eq => Some ( format ! ( "{}_eq!({lhs_name}, {rhs_name});" , visitor. assert_string) ) ,
132+ BinOpKind :: Ne => Some ( format ! ( "{}_ne!({lhs_name}, {rhs_name});" , visitor. assert_string) ) ,
133+ BinOpKind :: Ge | BinOpKind :: Gt | BinOpKind :: Le | BinOpKind :: Lt => Some ( format ! (
134+ "{}!({lhs_name} {} {rhs_name})" ,
135+ visitor. assert_string,
136+ node. as_str( )
137+ ) ) ,
116138 _ => None ,
117139 }
118140}
0 commit comments