@@ -229,6 +229,19 @@ enum VarKind {
229
229
Upvar ( HirId , Symbol ) ,
230
230
}
231
231
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
+
232
245
struct IrMaps < ' tcx > {
233
246
tcx : TyCtxt < ' tcx > ,
234
247
live_node_map : HirIdMap < LiveNode > ,
@@ -1333,7 +1346,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
1333
1346
1334
1347
impl < ' a , ' tcx > Visitor < ' tcx > for Liveness < ' a , ' tcx > {
1335
1348
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| {
1337
1350
if local. init . is_some ( ) {
1338
1351
self . warn_about_dead_assign ( spans, hir_id, ln, var) ;
1339
1352
}
@@ -1348,7 +1361,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> {
1348
1361
}
1349
1362
1350
1363
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 , |_, _, _, _| { } ) ;
1352
1365
intravisit:: walk_arm ( self , arm) ;
1353
1366
}
1354
1367
}
@@ -1387,7 +1400,7 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) {
1387
1400
}
1388
1401
1389
1402
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 , |_, _, _, _| { } ) ;
1391
1404
}
1392
1405
1393
1406
// no correctness conditions related to liveness
@@ -1508,20 +1521,26 @@ impl<'tcx> Liveness<'_, 'tcx> {
1508
1521
1509
1522
fn warn_about_unused_args ( & self , body : & hir:: Body < ' _ > , entry_ln : LiveNode ) {
1510
1523
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
+ ) ;
1518
1536
}
1519
1537
}
1520
1538
1521
1539
fn check_unused_vars_in_pat (
1522
1540
& self ,
1523
1541
pat : & hir:: Pat < ' _ > ,
1524
1542
entry_ln : Option < LiveNode > ,
1543
+ opt_body : Option < & hir:: Body < ' _ > > ,
1525
1544
on_used_on_entry : impl Fn ( Vec < Span > , HirId , LiveNode , Variable ) ,
1526
1545
) {
1527
1546
// In an or-pattern, only consider the variable; any later patterns must have the same
@@ -1549,7 +1568,7 @@ impl<'tcx> Liveness<'_, 'tcx> {
1549
1568
hir_ids_and_spans. into_iter ( ) . map ( |( _, _, ident_span) | ident_span) . collect ( ) ;
1550
1569
on_used_on_entry ( spans, id, ln, var) ;
1551
1570
} 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 ) ;
1553
1572
}
1554
1573
}
1555
1574
}
@@ -1561,6 +1580,8 @@ impl<'tcx> Liveness<'_, 'tcx> {
1561
1580
ln : LiveNode ,
1562
1581
var : Variable ,
1563
1582
can_remove : bool ,
1583
+ pat : & hir:: Pat < ' _ > ,
1584
+ opt_body : Option < & hir:: Body < ' _ > > ,
1564
1585
) {
1565
1586
let first_hir_id = hir_ids_and_spans[ 0 ] . 0 ;
1566
1587
@@ -1664,6 +1685,9 @@ impl<'tcx> Liveness<'_, 'tcx> {
1664
1685
. collect :: < Vec < _ > > ( ) ,
1665
1686
|lint| {
1666
1687
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
+ }
1667
1691
err. multipart_suggestion (
1668
1692
"if this is intentional, prefix it with an underscore" ,
1669
1693
non_shorthands,
@@ -1677,6 +1701,42 @@ impl<'tcx> Liveness<'_, 'tcx> {
1677
1701
}
1678
1702
}
1679
1703
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
+
1680
1740
fn warn_about_dead_assign ( & self , spans : Vec < Span > , hir_id : HirId , ln : LiveNode , var : Variable ) {
1681
1741
if !self . live_on_exit ( ln, var) {
1682
1742
self . report_unused_assign ( hir_id, spans, var, |name| {
0 commit comments