@@ -229,6 +229,19 @@ enum VarKind {
229229 Upvar ( HirId , Symbol ) ,
230230}
231231
232+ struct CollectLitsVisitor < ' tcx > {
233+ lit_exprs : Vec < & ' tcx hir:: Expr < ' tcx > > ,
234+ }
235+
236+ impl < ' tcx > Visitor < ' tcx > for CollectLitsVisitor < ' tcx > {
237+ fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
238+ if let hir:: ExprKind :: Lit ( _) = expr. kind {
239+ self . lit_exprs . push ( expr) ;
240+ }
241+ intravisit:: walk_expr ( self , expr) ;
242+ }
243+ }
244+
232245struct IrMaps < ' tcx > {
233246 tcx : TyCtxt < ' tcx > ,
234247 live_node_map : HirIdMap < LiveNode > ,
@@ -1333,7 +1346,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
13331346
13341347impl < ' a , ' tcx > Visitor < ' tcx > for Liveness < ' a , ' tcx > {
13351348 fn visit_local ( & mut self , local : & ' tcx hir:: Local < ' tcx > ) {
1336- self . check_unused_vars_in_pat ( & local. pat , None , |spans, hir_id, ln, var| {
1349+ self . check_unused_vars_in_pat ( & local. pat , None , None , |spans, hir_id, ln, var| {
13371350 if local. init . is_some ( ) {
13381351 self . warn_about_dead_assign ( spans, hir_id, ln, var) ;
13391352 }
@@ -1348,7 +1361,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> {
13481361 }
13491362
13501363 fn visit_arm ( & mut self , arm : & ' tcx hir:: Arm < ' tcx > ) {
1351- self . check_unused_vars_in_pat ( & arm. pat , None , |_, _, _, _| { } ) ;
1364+ self . check_unused_vars_in_pat ( & arm. pat , None , None , |_, _, _, _| { } ) ;
13521365 intravisit:: walk_arm ( self , arm) ;
13531366 }
13541367}
@@ -1387,7 +1400,7 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) {
13871400 }
13881401
13891402 hir:: ExprKind :: Let ( let_expr) => {
1390- this. check_unused_vars_in_pat ( let_expr. pat , None , |_, _, _, _| { } ) ;
1403+ this. check_unused_vars_in_pat ( let_expr. pat , None , None , |_, _, _, _| { } ) ;
13911404 }
13921405
13931406 // no correctness conditions related to liveness
@@ -1508,20 +1521,26 @@ impl<'tcx> Liveness<'_, 'tcx> {
15081521
15091522 fn warn_about_unused_args ( & self , body : & hir:: Body < ' _ > , entry_ln : LiveNode ) {
15101523 for p in body. params {
1511- self . check_unused_vars_in_pat ( & p. pat , Some ( entry_ln) , |spans, hir_id, ln, var| {
1512- if !self . live_on_entry ( ln, var) {
1513- self . report_unused_assign ( hir_id, spans, var, |name| {
1514- format ! ( "value passed to `{}` is never read" , name)
1515- } ) ;
1516- }
1517- } ) ;
1524+ self . check_unused_vars_in_pat (
1525+ & p. pat ,
1526+ Some ( entry_ln) ,
1527+ Some ( body) ,
1528+ |spans, hir_id, ln, var| {
1529+ if !self . live_on_entry ( ln, var) {
1530+ self . report_unused_assign ( hir_id, spans, var, |name| {
1531+ format ! ( "value passed to `{}` is never read" , name)
1532+ } ) ;
1533+ }
1534+ } ,
1535+ ) ;
15181536 }
15191537 }
15201538
15211539 fn check_unused_vars_in_pat (
15221540 & self ,
15231541 pat : & hir:: Pat < ' _ > ,
15241542 entry_ln : Option < LiveNode > ,
1543+ opt_body : Option < & hir:: Body < ' _ > > ,
15251544 on_used_on_entry : impl Fn ( Vec < Span > , HirId , LiveNode , Variable ) ,
15261545 ) {
15271546 // In an or-pattern, only consider the variable; any later patterns must have the same
@@ -1549,7 +1568,7 @@ impl<'tcx> Liveness<'_, 'tcx> {
15491568 hir_ids_and_spans. into_iter ( ) . map ( |( _, _, ident_span) | ident_span) . collect ( ) ;
15501569 on_used_on_entry ( spans, id, ln, var) ;
15511570 } else {
1552- self . report_unused ( hir_ids_and_spans, ln, var, can_remove) ;
1571+ self . report_unused ( hir_ids_and_spans, ln, var, can_remove, pat , opt_body ) ;
15531572 }
15541573 }
15551574 }
@@ -1561,6 +1580,8 @@ impl<'tcx> Liveness<'_, 'tcx> {
15611580 ln : LiveNode ,
15621581 var : Variable ,
15631582 can_remove : bool ,
1583+ pat : & hir:: Pat < ' _ > ,
1584+ opt_body : Option < & hir:: Body < ' _ > > ,
15641585 ) {
15651586 let first_hir_id = hir_ids_and_spans[ 0 ] . 0 ;
15661587
@@ -1664,6 +1685,9 @@ impl<'tcx> Liveness<'_, 'tcx> {
16641685 . collect :: < Vec < _ > > ( ) ,
16651686 |lint| {
16661687 let mut err = lint. build ( & format ! ( "unused variable: `{}`" , name) ) ;
1688+ if self . has_added_lit_match_name_span ( & name, opt_body, & mut err) {
1689+ err. span_label ( pat. span , "unused variable" ) ;
1690+ }
16671691 err. multipart_suggestion (
16681692 "if this is intentional, prefix it with an underscore" ,
16691693 non_shorthands,
@@ -1677,6 +1701,42 @@ impl<'tcx> Liveness<'_, 'tcx> {
16771701 }
16781702 }
16791703
1704+ fn has_added_lit_match_name_span (
1705+ & self ,
1706+ name : & str ,
1707+ opt_body : Option < & hir:: Body < ' _ > > ,
1708+ err : & mut rustc_errors:: DiagnosticBuilder < ' _ , ( ) > ,
1709+ ) -> bool {
1710+ let mut has_litstring = false ;
1711+ let Some ( opt_body) = opt_body else { return false ; } ;
1712+ let mut visitor = CollectLitsVisitor { lit_exprs : vec ! [ ] } ;
1713+ intravisit:: walk_body ( & mut visitor, opt_body) ;
1714+ for lit_expr in visitor. lit_exprs {
1715+ let hir:: ExprKind :: Lit ( litx) = & lit_expr. kind else { continue } ;
1716+ let rustc_ast:: LitKind :: Str ( syb, _) = litx. node else { continue ; } ;
1717+ let name_str: & str = syb. as_str ( ) ;
1718+ let mut name_pa = String :: from ( "{" ) ;
1719+ name_pa. push_str ( & name) ;
1720+ name_pa. push ( '}' ) ;
1721+ if name_str. contains ( & name_pa) {
1722+ err. span_label (
1723+ lit_expr. span ,
1724+ "you might have meant to use string interpolation in this string literal" ,
1725+ ) ;
1726+ err. multipart_suggestion (
1727+ "string interpolation only works in `format!` invocations" ,
1728+ vec ! [
1729+ ( lit_expr. span. shrink_to_lo( ) , "format!(" . to_string( ) ) ,
1730+ ( lit_expr. span. shrink_to_hi( ) , ")" . to_string( ) ) ,
1731+ ] ,
1732+ Applicability :: MachineApplicable ,
1733+ ) ;
1734+ has_litstring = true ;
1735+ }
1736+ }
1737+ has_litstring
1738+ }
1739+
16801740 fn warn_about_dead_assign ( & self , spans : Vec < Span > , hir_id : HirId , ln : LiveNode , var : Variable ) {
16811741 if !self . live_on_exit ( ln, var) {
16821742 self . report_unused_assign ( hir_id, spans, var, |name| {
0 commit comments