@@ -8,7 +8,9 @@ use rustc_errors::Applicability;
8
8
use rustc_hir:: def:: { DefKind , Res } ;
9
9
use rustc_hir:: { Expr , ExprKind , Lit , Node , Path , QPath , TyKind , UnOp } ;
10
10
use rustc_lint:: { LateContext , LintContext } ;
11
+ use rustc_middle:: ty:: adjustment:: Adjust ;
11
12
use rustc_middle:: ty:: { self , FloatTy , InferTy , Ty } ;
13
+ use rustc_span:: { Symbol , sym} ;
12
14
use std:: ops:: ControlFlow ;
13
15
14
16
use super :: UNNECESSARY_CAST ;
@@ -142,6 +144,33 @@ pub(super) fn check<'tcx>(
142
144
}
143
145
144
146
if cast_from. kind ( ) == cast_to. kind ( ) && !expr. span . in_external_macro ( cx. sess ( ) . source_map ( ) ) {
147
+ enum MaybeParenOrBlock {
148
+ Paren ,
149
+ Block ,
150
+ Nothing ,
151
+ }
152
+
153
+ fn is_borrow_expr ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
154
+ matches ! ( expr. kind, ExprKind :: AddrOf ( ..) )
155
+ || cx
156
+ . typeck_results ( )
157
+ . expr_adjustments ( expr)
158
+ . iter ( )
159
+ . any ( |adj| matches ! ( adj. kind, Adjust :: Borrow ( _) ) )
160
+ }
161
+
162
+ fn is_in_allowed_macro ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
163
+ const ALLOWED_MACROS : & [ Symbol ] = & [
164
+ sym:: format_args_macro,
165
+ sym:: assert_eq_macro,
166
+ sym:: debug_assert_eq_macro,
167
+ sym:: assert_ne_macro,
168
+ sym:: debug_assert_ne_macro,
169
+ ] ;
170
+ matches ! ( expr. span. ctxt( ) . outer_expn_data( ) . macro_def_id, Some ( def_id) if
171
+ ALLOWED_MACROS . iter( ) . any( |& sym| cx. tcx. is_diagnostic_item( sym, def_id) ) )
172
+ }
173
+
145
174
if let Some ( id) = path_to_local ( cast_expr)
146
175
&& !cx. tcx . hir_span ( id) . eq_ctxt ( cast_expr. span )
147
176
{
@@ -150,26 +179,26 @@ pub(super) fn check<'tcx>(
150
179
return false ;
151
180
}
152
181
153
- // If the whole cast expression is a unary expression (`(*x as T)`) or an addressof
154
- // expression (`(&x as T)`), then not surrounding the suggestion into a block risks us
155
- // changing the precedence of operators if the cast expression is followed by an operation
156
- // with higher precedence than the unary operator (`(*x as T).foo()` would become
157
- // `*x.foo()`, which changes what the `*` applies on).
158
- // The same is true if the expression encompassing the cast expression is a unary
159
- // expression or an addressof expression.
160
- let needs_block = matches ! ( cast_expr . kind , ExprKind :: Unary ( .. ) | ExprKind :: AddrOf ( .. ) )
161
- || get_parent_expr ( cx , expr ) . is_some_and ( |e| matches ! ( e . kind , ExprKind :: Unary ( .. ) | ExprKind :: AddrOf ( .. ) ) ) ;
182
+ // Changing `&(x as i32)` to `&x` would change the meaning of the code because the previous creates
183
+ // a reference to the temporary while the latter creates a reference to the original value.
184
+ let surrounding = match cx . tcx . parent_hir_node ( expr . hir_id ) {
185
+ Node :: Expr ( parent ) if ! is_in_allowed_macro ( cx , parent ) && is_borrow_expr ( cx , parent ) => {
186
+ MaybeParenOrBlock :: Block
187
+ } ,
188
+ Node :: Expr ( parent ) if cast_expr . precedence ( ) < parent . precedence ( ) => MaybeParenOrBlock :: Paren ,
189
+ _ => MaybeParenOrBlock :: Nothing ,
190
+ } ;
162
191
163
192
span_lint_and_sugg (
164
193
cx,
165
194
UNNECESSARY_CAST ,
166
195
expr. span ,
167
196
format ! ( "casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)" ) ,
168
197
"try" ,
169
- if needs_block {
170
- format ! ( "{{ { cast_str} }}" )
171
- } else {
172
- cast_str
198
+ match surrounding {
199
+ MaybeParenOrBlock :: Paren => format ! ( "({ cast_str})" ) ,
200
+ MaybeParenOrBlock :: Block => format ! ( "{{ {cast_str} }}" ) ,
201
+ MaybeParenOrBlock :: Nothing => cast_str,
173
202
} ,
174
203
Applicability :: MachineApplicable ,
175
204
) ;
0 commit comments