Skip to content

Commit 39ffabb

Browse files
author
Yiming Lei
committed
Point at the string inside literal and mention if we need string interpolation
modified: compiler/rustc_passes/src/liveness.rs new file: src/test/ui/type/issue-100584.rs new file: src/test/ui/type/issue-100584.stderr
1 parent a1bea15 commit 39ffabb

File tree

3 files changed

+130
-11
lines changed

3 files changed

+130
-11
lines changed

compiler/rustc_passes/src/liveness.rs

+71-11
Original file line numberDiff line numberDiff line change
@@ -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+
191204
struct IrMaps<'tcx> {
192205
tcx: TyCtxt<'tcx>,
193206
live_node_map: HirIdMap<LiveNode>,
@@ -1342,7 +1355,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
13421355

13431356
impl<'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| {

src/test/ui/type/issue-100584.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#![deny(unused)]
2+
fn foo(xyza: &str) {
3+
//~^ ERROR unused variable: `xyza`
4+
let _ = "{xyza}";
5+
}
6+
7+
fn foo3(xyza: &str) {
8+
//~^ ERROR unused variable: `xyza`
9+
let _ = "aaa{xyza}bbb";
10+
}
11+
12+
fn main() {
13+
foo("x");
14+
foo3("xx");
15+
}

src/test/ui/type/issue-100584.stderr

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
error: unused variable: `xyza`
2+
--> $DIR/issue-100584.rs:2:8
3+
|
4+
LL | fn foo(xyza: &str) {
5+
| ^^^^ unused variable
6+
LL |
7+
LL | let _ = "{xyza}";
8+
| -------- you might have meant to use string interpolation in this string literal
9+
|
10+
note: the lint level is defined here
11+
--> $DIR/issue-100584.rs:1:9
12+
|
13+
LL | #![deny(unused)]
14+
| ^^^^^^
15+
= note: `#[deny(unused_variables)]` implied by `#[deny(unused)]`
16+
help: string interpolation only works in `format!` invocations
17+
|
18+
LL | let _ = format!("{xyza}");
19+
| ++++++++ +
20+
help: if this is intentional, prefix it with an underscore
21+
|
22+
LL | fn foo(_xyza: &str) {
23+
| ~~~~~
24+
25+
error: unused variable: `xyza`
26+
--> $DIR/issue-100584.rs:7:9
27+
|
28+
LL | fn foo3(xyza: &str) {
29+
| ^^^^ unused variable
30+
LL |
31+
LL | let _ = "aaa{xyza}bbb";
32+
| -------------- you might have meant to use string interpolation in this string literal
33+
|
34+
help: string interpolation only works in `format!` invocations
35+
|
36+
LL | let _ = format!("aaa{xyza}bbb");
37+
| ++++++++ +
38+
help: if this is intentional, prefix it with an underscore
39+
|
40+
LL | fn foo3(_xyza: &str) {
41+
| ~~~~~
42+
43+
error: aborting due to 2 previous errors
44+

0 commit comments

Comments
 (0)