@@ -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