@@ -8,7 +8,9 @@ use rustc_errors::Applicability;
88use rustc_hir:: def:: { DefKind , Res } ;
99use rustc_hir:: { Expr , ExprKind , Lit , Node , Path , QPath , TyKind , UnOp } ;
1010use rustc_lint:: { LateContext , LintContext } ;
11+ use rustc_middle:: ty:: adjustment:: Adjust ;
1112use rustc_middle:: ty:: { self , FloatTy , InferTy , Ty } ;
13+ use rustc_span:: { Symbol , sym} ;
1214use std:: ops:: ControlFlow ;
1315
1416use super :: UNNECESSARY_CAST ;
@@ -142,6 +144,33 @@ pub(super) fn check<'tcx>(
142144 }
143145
144146 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+ . first ( )
159+ . is_some_and ( |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+ cx. tcx. get_diagnostic_name( def_id) . is_some_and( |sym| ALLOWED_MACROS . contains( & sym) ) )
172+ }
173+
145174 if let Some ( id) = path_to_local ( cast_expr)
146175 && !cx. tcx . hir_span ( id) . eq_ctxt ( cast_expr. span )
147176 {
@@ -150,26 +179,26 @@ pub(super) fn check<'tcx>(
150179 return false ;
151180 }
152181
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_borrow_expr ( cx , parent ) && ! is_in_allowed_macro ( cx , parent ) => {
186+ MaybeParenOrBlock :: Block
187+ } ,
188+ Node :: Expr ( parent ) if cast_expr . precedence ( ) < parent . precedence ( ) => MaybeParenOrBlock :: Paren ,
189+ _ => MaybeParenOrBlock :: Nothing ,
190+ } ;
162191
163192 span_lint_and_sugg (
164193 cx,
165194 UNNECESSARY_CAST ,
166195 expr. span ,
167196 format ! ( "casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)" ) ,
168197 "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,
173202 } ,
174203 Applicability :: MachineApplicable ,
175204 ) ;
0 commit comments