@@ -188,6 +188,19 @@ enum VarKind {
188188 Upvar ( HirId , Symbol ) ,
189189}
190190
191+ struct CollectLitsVisitor < ' tcx > {
192+ lit_exprs : Vec < & ' tcx hir:: Expr < ' tcx > > ,
193+ }
194+
195+ impl < ' tcx > Visitor < ' tcx > for CollectLitsVisitor < ' tcx > {
196+ fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
197+ if let hir:: ExprKind :: Lit ( _) = expr. kind {
198+ self . lit_exprs . push ( expr) ;
199+ }
200+ intravisit:: walk_expr ( self , expr) ;
201+ }
202+ }
203+
191204struct IrMaps < ' tcx > {
192205 tcx : TyCtxt < ' tcx > ,
193206 live_node_map : HirIdMap < LiveNode > ,
@@ -1342,7 +1355,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
13421355
13431356impl < ' a , ' tcx > Visitor < ' tcx > for Liveness < ' a , ' tcx > {
13441357 fn visit_local ( & mut self , local : & ' tcx hir:: Local < ' tcx > ) {
1345- self . check_unused_vars_in_pat ( & local. pat , None , |spans, hir_id, ln, var| {
1358+ self . check_unused_vars_in_pat ( & local. pat , None , None , |spans, hir_id, ln, var| {
13461359 if local. init . is_some ( ) {
13471360 self . warn_about_dead_assign ( spans, hir_id, ln, var) ;
13481361 }
@@ -1357,7 +1370,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> {
13571370 }
13581371
13591372 fn visit_arm ( & mut self , arm : & ' tcx hir:: Arm < ' tcx > ) {
1360- self . check_unused_vars_in_pat ( & arm. pat , None , |_, _, _, _| { } ) ;
1373+ self . check_unused_vars_in_pat ( & arm. pat , None , None , |_, _, _, _| { } ) ;
13611374 intravisit:: walk_arm ( self , arm) ;
13621375 }
13631376}
@@ -1396,7 +1409,7 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) {
13961409 }
13971410
13981411 hir:: ExprKind :: Let ( let_expr) => {
1399- this. check_unused_vars_in_pat ( let_expr. pat , None , |_, _, _, _| { } ) ;
1412+ this. check_unused_vars_in_pat ( let_expr. pat , None , None , |_, _, _, _| { } ) ;
14001413 }
14011414
14021415 // no correctness conditions related to liveness
@@ -1517,20 +1530,26 @@ impl<'tcx> Liveness<'_, 'tcx> {
15171530
15181531 fn warn_about_unused_args ( & self , body : & hir:: Body < ' _ > , entry_ln : LiveNode ) {
15191532 for p in body. params {
1520- self . check_unused_vars_in_pat ( & p. pat , Some ( entry_ln) , |spans, hir_id, ln, var| {
1521- if !self . live_on_entry ( ln, var) {
1522- self . report_unused_assign ( hir_id, spans, var, |name| {
1523- format ! ( "value passed to `{}` is never read" , name)
1524- } ) ;
1525- }
1526- } ) ;
1533+ self . check_unused_vars_in_pat (
1534+ & p. pat ,
1535+ Some ( entry_ln) ,
1536+ Some ( body) ,
1537+ |spans, hir_id, ln, var| {
1538+ if !self . live_on_entry ( ln, var) {
1539+ self . report_unused_assign ( hir_id, spans, var, |name| {
1540+ format ! ( "value passed to `{}` is never read" , name)
1541+ } ) ;
1542+ }
1543+ } ,
1544+ ) ;
15271545 }
15281546 }
15291547
15301548 fn check_unused_vars_in_pat (
15311549 & self ,
15321550 pat : & hir:: Pat < ' _ > ,
15331551 entry_ln : Option < LiveNode > ,
1552+ opt_body : Option < & hir:: Body < ' _ > > ,
15341553 on_used_on_entry : impl Fn ( Vec < Span > , HirId , LiveNode , Variable ) ,
15351554 ) {
15361555 // In an or-pattern, only consider the variable; any later patterns must have the same
@@ -1558,7 +1577,7 @@ impl<'tcx> Liveness<'_, 'tcx> {
15581577 hir_ids_and_spans. into_iter ( ) . map ( |( _, _, ident_span) | ident_span) . collect ( ) ;
15591578 on_used_on_entry ( spans, id, ln, var) ;
15601579 } else {
1561- self . report_unused ( hir_ids_and_spans, ln, var, can_remove) ;
1580+ self . report_unused ( hir_ids_and_spans, ln, var, can_remove, pat , opt_body ) ;
15621581 }
15631582 }
15641583 }
@@ -1570,6 +1589,8 @@ impl<'tcx> Liveness<'_, 'tcx> {
15701589 ln : LiveNode ,
15711590 var : Variable ,
15721591 can_remove : bool ,
1592+ pat : & hir:: Pat < ' _ > ,
1593+ opt_body : Option < & hir:: Body < ' _ > > ,
15731594 ) {
15741595 let first_hir_id = hir_ids_and_spans[ 0 ] . 0 ;
15751596
@@ -1673,6 +1694,9 @@ impl<'tcx> Liveness<'_, 'tcx> {
16731694 . collect :: < Vec < _ > > ( ) ,
16741695 |lint| {
16751696 let mut err = lint. build ( & format ! ( "unused variable: `{}`" , name) ) ;
1697+ if self . has_added_lit_match_name_span ( & name, opt_body, & mut err) {
1698+ err. span_label ( pat. span , "unused variable" ) ;
1699+ }
16761700 err. multipart_suggestion (
16771701 "if this is intentional, prefix it with an underscore" ,
16781702 non_shorthands,
@@ -1686,6 +1710,42 @@ impl<'tcx> Liveness<'_, 'tcx> {
16861710 }
16871711 }
16881712
1713+ fn has_added_lit_match_name_span (
1714+ & self ,
1715+ name : & str ,
1716+ opt_body : Option < & hir:: Body < ' _ > > ,
1717+ err : & mut rustc_errors:: DiagnosticBuilder < ' _ , ( ) > ,
1718+ ) -> bool {
1719+ let mut has_litstring = false ;
1720+ let Some ( opt_body) = opt_body else { return false ; } ;
1721+ let mut visitor = CollectLitsVisitor { lit_exprs : vec ! [ ] } ;
1722+ intravisit:: walk_body ( & mut visitor, opt_body) ;
1723+ for lit_expr in visitor. lit_exprs {
1724+ let hir:: ExprKind :: Lit ( litx) = & lit_expr. kind else { continue } ;
1725+ let rustc_ast:: LitKind :: Str ( syb, _) = litx. node else { continue ; } ;
1726+ let name_str: & str = syb. as_str ( ) ;
1727+ let mut name_pa = String :: from ( "{" ) ;
1728+ name_pa. push_str ( & name) ;
1729+ name_pa. push ( '}' ) ;
1730+ if name_str. contains ( & name_pa) {
1731+ err. span_label (
1732+ lit_expr. span ,
1733+ "you might have meant to use string interpolation in this string literal" ,
1734+ ) ;
1735+ err. multipart_suggestion (
1736+ "string interpolation only works in `format!` invocations" ,
1737+ vec ! [
1738+ ( lit_expr. span. shrink_to_lo( ) , "format!(" . to_string( ) ) ,
1739+ ( lit_expr. span. shrink_to_hi( ) , ")" . to_string( ) ) ,
1740+ ] ,
1741+ Applicability :: MachineApplicable ,
1742+ ) ;
1743+ has_litstring = true ;
1744+ }
1745+ }
1746+ has_litstring
1747+ }
1748+
16891749 fn warn_about_dead_assign ( & self , spans : Vec < Span > , hir_id : HirId , ln : LiveNode , var : Variable ) {
16901750 if !self . live_on_exit ( ln, var) {
16911751 self . report_unused_assign ( hir_id, spans, var, |name| {
0 commit comments