@@ -18,82 +18,118 @@ pub(crate) fn check<'tcx>(
1818 right : & ' tcx Expr < ' _ > ,
1919) {
2020 if !is_allowed ( cx, op, left, right) {
21- match op {
22- BinOpKind :: Add | BinOpKind :: BitOr | BinOpKind :: BitXor => {
23- check_op (
24- cx,
25- left,
26- 0 ,
27- expr. span ,
28- peel_hir_expr_refs ( right) . 0 . span ,
29- needs_parenthesis ( cx, expr, right) ,
30- ) ;
31- check_op (
32- cx,
33- right,
34- 0 ,
35- expr. span ,
36- peel_hir_expr_refs ( left) . 0 . span ,
37- Parens :: Unneeded ,
38- ) ;
39- } ,
40- BinOpKind :: Shl | BinOpKind :: Shr | BinOpKind :: Sub => {
41- check_op (
42- cx,
43- right,
44- 0 ,
45- expr. span ,
46- peel_hir_expr_refs ( left) . 0 . span ,
47- Parens :: Unneeded ,
48- ) ;
49- } ,
50- BinOpKind :: Mul => {
51- check_op (
52- cx,
53- left,
54- 1 ,
55- expr. span ,
56- peel_hir_expr_refs ( right) . 0 . span ,
57- needs_parenthesis ( cx, expr, right) ,
58- ) ;
59- check_op (
60- cx,
61- right,
62- 1 ,
63- expr. span ,
64- peel_hir_expr_refs ( left) . 0 . span ,
65- Parens :: Unneeded ,
66- ) ;
67- } ,
68- BinOpKind :: Div => check_op (
21+ return ;
22+ }
23+
24+ // we need to know whether a ref is coerced to a value
25+ // if a ref is coerced, then the suggested lint must deref it
26+ // e.g. `let _: i32 = x+0` with `x: &i32` should be replaced with `let _: i32 = *x`.
27+ // we do this by checking the _kind_ of the type of the expression
28+ // if it's a ref, we then check whether it is erased, and that's it.
29+ let ( peeled_left_span, left_is_coerced_to_value) = {
30+ let expr = peel_hir_expr_refs ( left) . 0 ;
31+ let span = expr. span ;
32+ let is_coerced = expr_is_erased_ref ( cx, expr) ;
33+ ( span, is_coerced)
34+ } ;
35+
36+ let ( peeled_right_span, right_is_coerced_to_value) = {
37+ let expr = peel_hir_expr_refs ( right) . 0 ;
38+ let span = expr. span ;
39+ let is_coerced = expr_is_erased_ref ( cx, expr) ;
40+ ( span, is_coerced)
41+ } ;
42+
43+ match op {
44+ BinOpKind :: Add | BinOpKind :: BitOr | BinOpKind :: BitXor => {
45+ check_op (
46+ cx,
47+ left,
48+ 0 ,
49+ expr. span ,
50+ peeled_right_span,
51+ needs_parenthesis ( cx, expr, right) ,
52+ right_is_coerced_to_value,
53+ ) ;
54+ check_op (
55+ cx,
56+ right,
57+ 0 ,
58+ expr. span ,
59+ peeled_left_span,
60+ Parens :: Unneeded ,
61+ left_is_coerced_to_value,
62+ ) ;
63+ } ,
64+ BinOpKind :: Shl | BinOpKind :: Shr | BinOpKind :: Sub => {
65+ check_op (
66+ cx,
67+ right,
68+ 0 ,
69+ expr. span ,
70+ peeled_left_span,
71+ Parens :: Unneeded ,
72+ left_is_coerced_to_value,
73+ ) ;
74+ } ,
75+ BinOpKind :: Mul => {
76+ check_op (
77+ cx,
78+ left,
79+ 1 ,
80+ expr. span ,
81+ peeled_right_span,
82+ needs_parenthesis ( cx, expr, right) ,
83+ right_is_coerced_to_value,
84+ ) ;
85+ check_op (
6986 cx,
7087 right,
7188 1 ,
7289 expr. span ,
73- peel_hir_expr_refs ( left ) . 0 . span ,
90+ peeled_left_span ,
7491 Parens :: Unneeded ,
75- ) ,
76- BinOpKind :: BitAnd => {
77- check_op (
78- cx,
79- left,
80- -1 ,
81- expr. span ,
82- peel_hir_expr_refs ( right) . 0 . span ,
83- needs_parenthesis ( cx, expr, right) ,
84- ) ;
85- check_op (
86- cx,
87- right,
88- -1 ,
89- expr. span ,
90- peel_hir_expr_refs ( left) . 0 . span ,
91- Parens :: Unneeded ,
92- ) ;
93- } ,
94- BinOpKind :: Rem => check_remainder ( cx, left, right, expr. span , left. span ) ,
95- _ => ( ) ,
96- }
92+ left_is_coerced_to_value,
93+ ) ;
94+ } ,
95+ BinOpKind :: Div => check_op (
96+ cx,
97+ right,
98+ 1 ,
99+ expr. span ,
100+ peeled_left_span,
101+ Parens :: Unneeded ,
102+ left_is_coerced_to_value,
103+ ) ,
104+ BinOpKind :: BitAnd => {
105+ check_op (
106+ cx,
107+ left,
108+ -1 ,
109+ expr. span ,
110+ peeled_right_span,
111+ needs_parenthesis ( cx, expr, right) ,
112+ right_is_coerced_to_value,
113+ ) ;
114+ check_op (
115+ cx,
116+ right,
117+ -1 ,
118+ expr. span ,
119+ peeled_left_span,
120+ Parens :: Unneeded ,
121+ left_is_coerced_to_value,
122+ ) ;
123+ } ,
124+ BinOpKind :: Rem => check_remainder ( cx, left, right, expr. span , left. span ) ,
125+ _ => ( ) ,
126+ }
127+ }
128+
129+ fn expr_is_erased_ref ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
130+ match cx. typeck_results ( ) . expr_ty ( expr) . kind ( ) {
131+ ty:: Ref ( r, ..) => r. is_erased ( ) ,
132+ _ => false ,
97133 }
98134}
99135
@@ -144,11 +180,11 @@ fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>)
144180}
145181
146182fn is_allowed ( cx : & LateContext < ' _ > , cmp : BinOpKind , left : & Expr < ' _ > , right : & Expr < ' _ > ) -> bool {
147- // This lint applies to integers
148- ! cx. typeck_results ( ) . expr_ty ( left) . peel_refs ( ) . is_integral ( )
149- || ! cx. typeck_results ( ) . expr_ty ( right) . peel_refs ( ) . is_integral ( )
183+ // This lint applies to integers and their references
184+ cx. typeck_results ( ) . expr_ty ( left) . peel_refs ( ) . is_integral ( )
185+ && cx. typeck_results ( ) . expr_ty ( right) . peel_refs ( ) . is_integral ( )
150186 // `1 << 0` is a common pattern in bit manipulation code
151- || ( cmp == BinOpKind :: Shl
187+ && ! ( cmp == BinOpKind :: Shl
152188 && constant_simple ( cx, cx. typeck_results ( ) , right) == Some ( Constant :: Int ( 0 ) )
153189 && constant_simple ( cx, cx. typeck_results ( ) , left) == Some ( Constant :: Int ( 1 ) ) )
154190}
@@ -161,11 +197,11 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span
161197 ( Some ( FullInt :: U ( lv) ) , Some ( FullInt :: U ( rv) ) ) => lv < rv,
162198 _ => return ,
163199 } {
164- span_ineffective_operation ( cx, span, arg, Parens :: Unneeded ) ;
200+ span_ineffective_operation ( cx, span, arg, Parens :: Unneeded , false ) ;
165201 }
166202}
167203
168- fn check_op ( cx : & LateContext < ' _ > , e : & Expr < ' _ > , m : i8 , span : Span , arg : Span , parens : Parens ) {
204+ fn check_op ( cx : & LateContext < ' _ > , e : & Expr < ' _ > , m : i8 , span : Span , arg : Span , parens : Parens , is_erased : bool ) {
169205 if let Some ( Constant :: Int ( v) ) = constant_simple ( cx, cx. typeck_results ( ) , e) . map ( Constant :: peel_refs) {
170206 let check = match * cx. typeck_results ( ) . expr_ty ( e) . peel_refs ( ) . kind ( ) {
171207 ty:: Int ( ity) => unsext ( cx. tcx , -1_i128 , ity) ,
@@ -178,18 +214,28 @@ fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, pa
178214 1 => v == 1 ,
179215 _ => unreachable ! ( ) ,
180216 } {
181- span_ineffective_operation ( cx, span, arg, parens) ;
217+ span_ineffective_operation ( cx, span, arg, parens, is_erased ) ;
182218 }
183219 }
184220}
185221
186- fn span_ineffective_operation ( cx : & LateContext < ' _ > , span : Span , arg : Span , parens : Parens ) {
222+ fn span_ineffective_operation (
223+ cx : & LateContext < ' _ > ,
224+ span : Span ,
225+ arg : Span ,
226+ parens : Parens ,
227+ is_ref_coerced_to_val : bool ,
228+ ) {
187229 let mut applicability = Applicability :: MachineApplicable ;
188230 let expr_snippet = snippet_with_applicability ( cx, arg, ".." , & mut applicability) ;
189-
231+ let expr_snippet = if is_ref_coerced_to_val {
232+ format ! ( "*{expr_snippet}" )
233+ } else {
234+ expr_snippet. into_owned ( )
235+ } ;
190236 let suggestion = match parens {
191237 Parens :: Needed => format ! ( "({expr_snippet})" ) ,
192- Parens :: Unneeded => expr_snippet. into_owned ( ) ,
238+ Parens :: Unneeded => expr_snippet,
193239 } ;
194240
195241 span_lint_and_sugg (
0 commit comments