@@ -188,6 +188,19 @@ enum VarKind {
188
188
Upvar ( HirId , Symbol ) ,
189
189
}
190
190
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
+
191
204
struct IrMaps < ' tcx > {
192
205
tcx : TyCtxt < ' tcx > ,
193
206
live_node_map : HirIdMap < LiveNode > ,
@@ -1342,7 +1355,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
1342
1355
1343
1356
impl < ' a , ' tcx > Visitor < ' tcx > for Liveness < ' a , ' tcx > {
1344
1357
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| {
1346
1359
if local. init . is_some ( ) {
1347
1360
self . warn_about_dead_assign ( spans, hir_id, ln, var) ;
1348
1361
}
@@ -1357,7 +1370,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> {
1357
1370
}
1358
1371
1359
1372
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 , |_, _, _, _| { } ) ;
1361
1374
intravisit:: walk_arm ( self , arm) ;
1362
1375
}
1363
1376
}
@@ -1396,7 +1409,7 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) {
1396
1409
}
1397
1410
1398
1411
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 , |_, _, _, _| { } ) ;
1400
1413
}
1401
1414
1402
1415
// no correctness conditions related to liveness
@@ -1517,20 +1530,26 @@ impl<'tcx> Liveness<'_, 'tcx> {
1517
1530
1518
1531
fn warn_about_unused_args ( & self , body : & hir:: Body < ' _ > , entry_ln : LiveNode ) {
1519
1532
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
+ ) ;
1527
1545
}
1528
1546
}
1529
1547
1530
1548
fn check_unused_vars_in_pat (
1531
1549
& self ,
1532
1550
pat : & hir:: Pat < ' _ > ,
1533
1551
entry_ln : Option < LiveNode > ,
1552
+ opt_body : Option < & hir:: Body < ' _ > > ,
1534
1553
on_used_on_entry : impl Fn ( Vec < Span > , HirId , LiveNode , Variable ) ,
1535
1554
) {
1536
1555
// In an or-pattern, only consider the variable; any later patterns must have the same
@@ -1558,7 +1577,7 @@ impl<'tcx> Liveness<'_, 'tcx> {
1558
1577
hir_ids_and_spans. into_iter ( ) . map ( |( _, _, ident_span) | ident_span) . collect ( ) ;
1559
1578
on_used_on_entry ( spans, id, ln, var) ;
1560
1579
} 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 ) ;
1562
1581
}
1563
1582
}
1564
1583
}
@@ -1570,6 +1589,8 @@ impl<'tcx> Liveness<'_, 'tcx> {
1570
1589
ln : LiveNode ,
1571
1590
var : Variable ,
1572
1591
can_remove : bool ,
1592
+ pat : & hir:: Pat < ' _ > ,
1593
+ opt_body : Option < & hir:: Body < ' _ > > ,
1573
1594
) {
1574
1595
let first_hir_id = hir_ids_and_spans[ 0 ] . 0 ;
1575
1596
@@ -1673,6 +1694,9 @@ impl<'tcx> Liveness<'_, 'tcx> {
1673
1694
. collect :: < Vec < _ > > ( ) ,
1674
1695
|lint| {
1675
1696
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
+ }
1676
1700
err. multipart_suggestion (
1677
1701
"if this is intentional, prefix it with an underscore" ,
1678
1702
non_shorthands,
@@ -1686,6 +1710,42 @@ impl<'tcx> Liveness<'_, 'tcx> {
1686
1710
}
1687
1711
}
1688
1712
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
+
1689
1749
fn warn_about_dead_assign ( & self , spans : Vec < Span > , hir_id : HirId , ln : LiveNode , var : Variable ) {
1690
1750
if !self . live_on_exit ( ln, var) {
1691
1751
self . report_unused_assign ( hir_id, spans, var, |name| {
0 commit comments