@@ -8,6 +8,7 @@ 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 } ;
1213use std:: ops:: ControlFlow ;
1314
@@ -142,6 +143,21 @@ pub(super) fn check<'tcx>(
142143 }
143144
144145 if cast_from. kind ( ) == cast_to. kind ( ) && !expr. span . in_external_macro ( cx. sess ( ) . source_map ( ) ) {
146+ enum MaybeParenOrBlock {
147+ Paren ,
148+ Block ,
149+ Nothing ,
150+ }
151+
152+ fn is_borrow_expr ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
153+ matches ! ( expr. kind, ExprKind :: AddrOf ( ..) )
154+ || cx
155+ . typeck_results ( )
156+ . expr_adjustments ( expr)
157+ . iter ( )
158+ . any ( |adj| matches ! ( adj. kind, Adjust :: Borrow ( _) ) )
159+ }
160+
145161 if let Some ( id) = path_to_local ( cast_expr)
146162 && !cx. tcx . hir_span ( id) . eq_ctxt ( cast_expr. span )
147163 {
@@ -150,26 +166,26 @@ pub(super) fn check<'tcx>(
150166 return false ;
151167 }
152168
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 ( .. ) ) ) ;
169+ let surrounding = match cx . tcx . parent_hir_node ( expr . hir_id ) {
170+ // Changing `&( x as i32)` to `&x` would change the meaning of the code because the previous creates
171+ // a reference to the temporary while the latter creates a reference to the original value.
172+ Node :: Expr ( parent ) if !parent . span . from_expansion ( ) && is_borrow_expr ( cx , parent ) => {
173+ MaybeParenOrBlock :: Block
174+ } ,
175+ Node :: Expr ( parent ) if cast_expr . precedence ( ) < parent . precedence ( ) => MaybeParenOrBlock :: Paren ,
176+ _ => MaybeParenOrBlock :: Nothing ,
177+ } ;
162178
163179 span_lint_and_sugg (
164180 cx,
165181 UNNECESSARY_CAST ,
166182 expr. span ,
167183 format ! ( "casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)" ) ,
168184 "try" ,
169- if needs_block {
170- format ! ( "{{ { cast_str} }}" )
171- } else {
172- cast_str
185+ match surrounding {
186+ MaybeParenOrBlock :: Paren => format ! ( "({ cast_str})" ) ,
187+ MaybeParenOrBlock :: Block => format ! ( "{{ {cast_str} }}" ) ,
188+ MaybeParenOrBlock :: Nothing => cast_str,
173189 } ,
174190 Applicability :: MachineApplicable ,
175191 ) ;
0 commit comments