Skip to content

Commit 189de55

Browse files
committed
Fix unused_variables typo suggestions for const patterns
When `unused_variables` finds a similarly named const or enum variant, it suggests replacing the unused binding with a pattern matching that item. The item was printed with a trimmed path, which is not necessarily resolvable from the binding site, so the `MachineApplicable` suggestion could fail to compile. Print the item with `with_crate_prefix!`: a `crate::`-rooted, re-export-aware path that resolves from any binding site in the crate. A const declared inside the current body has no such path, so it is printed by its bare in-scope name. Also suppress const typo suggestions for bare `let x = ...` bindings, where a const pattern would be refutable.
1 parent 5b686bc commit 189de55

4 files changed

Lines changed: 334 additions & 5 deletions

File tree

compiler/rustc_mir_transform/src/liveness.rs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc_middle::mir::visit::{
1010
MutatingUseContext, NonMutatingUseContext, NonUseContext, PlaceContext, Visitor,
1111
};
1212
use rustc_middle::mir::*;
13-
use rustc_middle::ty::print::with_no_trimmed_paths;
13+
use rustc_middle::ty::print::with_crate_prefix;
1414
use rustc_middle::ty::{self, Ty, TyCtxt};
1515
use rustc_mir_dataflow::fmt::DebugWithContext;
1616
use rustc_mir_dataflow::{Analysis, Backward, ResultsCursor};
@@ -168,13 +168,15 @@ fn is_capture(place: PlaceRef<'_>) -> bool {
168168
}
169169
}
170170

171-
/// Give a diagnostic when an unused variable may be a typo of a unit variant or a struct.
171+
/// Give a diagnostic when an unused variable may be a typo of a unit variant,
172+
/// unit struct, or const.
172173
fn maybe_suggest_unit_pattern_typo<'tcx>(
173174
tcx: TyCtxt<'tcx>,
174175
body_def_id: DefId,
175176
name: Symbol,
176177
span: Span,
177178
ty: Ty<'tcx>,
179+
allow_consts: bool,
178180
) -> Option<errors::PatternTypo> {
179181
if let ty::Adt(adt_def, _) = ty.peel_refs().kind() {
180182
let variant_names: Vec<_> = adt_def
@@ -191,19 +193,26 @@ fn maybe_suggest_unit_pattern_typo<'tcx>(
191193
{
192194
return Some(errors::PatternTypo {
193195
span,
194-
code: with_no_trimmed_paths!(tcx.def_path_str(variant.def_id)),
196+
code: pattern_item_path(tcx, body_def_id, variant.def_id),
195197
kind: tcx.def_descr(variant.def_id),
196198
item_name: variant.name,
197199
});
198200
}
199201
}
200202

203+
if !allow_consts {
204+
return None;
205+
}
206+
201207
// Look for consts of the same type with similar names as well,
202208
// not just unit structs and variants.
203209
let constants = tcx
204210
.hir_body_owners()
205211
.filter(|&def_id| {
212+
// `const _: T = ...` items have no name reachable from a pattern: writing
213+
// `path::_` is not valid syntax, so they must never appear in a suggestion.
206214
matches!(tcx.def_kind(def_id), DefKind::Const { .. })
215+
&& tcx.item_name(def_id.to_def_id()) != kw::Underscore
207216
&& tcx.type_of(def_id).instantiate_identity().skip_norm_wip() == ty
208217
&& tcx.visibility(def_id).is_accessible_from(body_def_id, tcx)
209218
})
@@ -215,7 +224,7 @@ fn maybe_suggest_unit_pattern_typo<'tcx>(
215224
{
216225
return Some(errors::PatternTypo {
217226
span,
218-
code: with_no_trimmed_paths!(tcx.def_path_str(def_id)),
227+
code: pattern_item_path(tcx, body_def_id, def_id.to_def_id()),
219228
kind: "constant",
220229
item_name,
221230
});
@@ -224,6 +233,16 @@ fn maybe_suggest_unit_pattern_typo<'tcx>(
224233
None
225234
}
226235

236+
fn pattern_item_path(tcx: TyCtxt<'_>, body_def_id: DefId, item_def_id: DefId) -> String {
237+
// A body-local const is in scope only by its bare name; everything else is a
238+
// module-level item that `with_crate_prefix!` prints as a valid `crate::` path.
239+
if tcx.is_descendant_of(item_def_id, body_def_id) {
240+
return tcx.item_name(item_def_id).to_string();
241+
}
242+
243+
with_crate_prefix!(tcx.def_path_str(item_def_id))
244+
}
245+
227246
/// Return whether we should consider the current place as a drop guard and skip reporting.
228247
fn maybe_drop_guard<'tcx>(
229248
tcx: TyCtxt<'tcx>,
@@ -977,15 +996,19 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
977996
&& introductions.iter().any(|intro| intro.span.eq_ctxt(def_span));
978997

979998
let maybe_suggest_typo = || {
980-
if let LocalKind::Arg = self.body.local_kind(local) {
999+
if matches!(self.body.local_kind(local), LocalKind::Arg) {
9811000
None
9821001
} else {
1002+
// In a bare `let x = ...`, replacing `x` with a const path would make the
1003+
// pattern refutable. Keep the existing unit ADT typo behavior unchanged.
1004+
let allow_consts = !matches!(binding.opt_match_place, Some((None, _)));
9831005
maybe_suggest_unit_pattern_typo(
9841006
tcx,
9851007
self.body.source.def_id(),
9861008
name,
9871009
def_span,
9881010
decl.ty,
1011+
allow_consts,
9891012
)
9901013
}
9911014
};
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
error: unused variable: `x`
2+
--> $DIR/unused-variables-const-pattern-typo.rs:17:18
3+
|
4+
LL | let Some(x) = x else { return };
5+
| ^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/unused-variables-const-pattern-typo.rs:9:9
9+
|
10+
LL | #![deny(unused_variables)]
11+
| ^^^^^^^^^^^^^^^^
12+
help: you might have meant to pattern match on the similarly named constant `X`
13+
|
14+
LL - let Some(x) = x else { return };
15+
LL + let Some(enclosed::X) = x else { return };
16+
|
17+
help: if this is intentional, prefix it with an underscore
18+
|
19+
LL | let Some(_x) = x else { return };
20+
| +
21+
22+
error: unused variable: `x`
23+
--> $DIR/unused-variables-const-pattern-typo.rs:27:13
24+
|
25+
LL | let x = system::Y;
26+
| ^ help: if this is intentional, prefix it with an underscore: `_x`
27+
28+
error: unused variable: `good`
29+
--> $DIR/unused-variables-const-pattern-typo.rs:37:18
30+
|
31+
LL | let Some(good) = x else { return };
32+
| ^^^^
33+
|
34+
help: you might have meant to pattern match on the similarly named constant `GOOD`
35+
|
36+
LL - let Some(good) = x else { return };
37+
LL + let Some(same_module::GOOD) = x else { return };
38+
|
39+
help: if this is intentional, prefix it with an underscore
40+
|
41+
LL | let Some(_good) = x else { return };
42+
| +
43+
44+
error: unused variable: `local`
45+
--> $DIR/unused-variables-const-pattern-typo.rs:46:14
46+
|
47+
LL | let Some(local) = x else { return };
48+
| ^^^^^
49+
|
50+
help: you might have meant to pattern match on the similarly named constant `LOCAL`
51+
|
52+
LL - let Some(local) = x else { return };
53+
LL + let Some(LOCAL) = x else { return };
54+
|
55+
help: if this is intentional, prefix it with an underscore
56+
|
57+
LL | let Some(_local) = x else { return };
58+
| +
59+
60+
error: unused variable: `ready`
61+
--> $DIR/unused-variables-const-pattern-typo.rs:60:18
62+
|
63+
LL | let Some(ready) = x else { return };
64+
| ^^^^^
65+
|
66+
help: you might have meant to pattern match on the similarly named variant `Ready`
67+
|
68+
LL - let Some(ready) = x else { return };
69+
LL + let Some(variants::State::Ready) = x else { return };
70+
|
71+
help: if this is intentional, prefix it with an underscore
72+
|
73+
LL | let Some(_ready) = x else { return };
74+
| +
75+
76+
error: unused variable: `x`
77+
--> $DIR/unused-variables-const-pattern-typo.rs:80:14
78+
|
79+
LL | let Some(x) = x else { return };
80+
| ^ help: if this is intentional, prefix it with an underscore: `_x`
81+
82+
error: unused variable: `value`
83+
--> $DIR/unused-variables-const-pattern-typo.rs:96:14
84+
|
85+
LL | let Some(value) = x else { return };
86+
| ^^^^^ help: if this is intentional, prefix it with an underscore: `_value`
87+
88+
error: unused variable: `xyzzy`
89+
--> $DIR/unused-variables-const-pattern-typo.rs:111:14
90+
|
91+
LL | let Some(xyzzy) = x else { return };
92+
| ^^^^^ help: if this is intentional, prefix it with an underscore: `_xyzzy`
93+
94+
error: aborting due to 8 previous errors
95+
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
error: unused variable: `x`
2+
--> $DIR/unused-variables-const-pattern-typo.rs:17:18
3+
|
4+
LL | let Some(x) = x else { return };
5+
| ^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/unused-variables-const-pattern-typo.rs:9:9
9+
|
10+
LL | #![deny(unused_variables)]
11+
| ^^^^^^^^^^^^^^^^
12+
help: you might have meant to pattern match on the similarly named constant `X`
13+
|
14+
LL - let Some(x) = x else { return };
15+
LL + let Some(crate::enclosed::X) = x else { return };
16+
|
17+
help: if this is intentional, prefix it with an underscore
18+
|
19+
LL | let Some(_x) = x else { return };
20+
| +
21+
22+
error: unused variable: `x`
23+
--> $DIR/unused-variables-const-pattern-typo.rs:27:13
24+
|
25+
LL | let x = system::Y;
26+
| ^ help: if this is intentional, prefix it with an underscore: `_x`
27+
28+
error: unused variable: `good`
29+
--> $DIR/unused-variables-const-pattern-typo.rs:37:18
30+
|
31+
LL | let Some(good) = x else { return };
32+
| ^^^^
33+
|
34+
help: you might have meant to pattern match on the similarly named constant `GOOD`
35+
|
36+
LL - let Some(good) = x else { return };
37+
LL + let Some(crate::same_module::GOOD) = x else { return };
38+
|
39+
help: if this is intentional, prefix it with an underscore
40+
|
41+
LL | let Some(_good) = x else { return };
42+
| +
43+
44+
error: unused variable: `local`
45+
--> $DIR/unused-variables-const-pattern-typo.rs:46:14
46+
|
47+
LL | let Some(local) = x else { return };
48+
| ^^^^^
49+
|
50+
help: you might have meant to pattern match on the similarly named constant `LOCAL`
51+
|
52+
LL - let Some(local) = x else { return };
53+
LL + let Some(LOCAL) = x else { return };
54+
|
55+
help: if this is intentional, prefix it with an underscore
56+
|
57+
LL | let Some(_local) = x else { return };
58+
| +
59+
60+
error: unused variable: `ready`
61+
--> $DIR/unused-variables-const-pattern-typo.rs:60:18
62+
|
63+
LL | let Some(ready) = x else { return };
64+
| ^^^^^
65+
|
66+
help: you might have meant to pattern match on the similarly named variant `Ready`
67+
|
68+
LL - let Some(ready) = x else { return };
69+
LL + let Some(crate::variants::State::Ready) = x else { return };
70+
|
71+
help: if this is intentional, prefix it with an underscore
72+
|
73+
LL | let Some(_ready) = x else { return };
74+
| +
75+
76+
error: unused variable: `x`
77+
--> $DIR/unused-variables-const-pattern-typo.rs:80:14
78+
|
79+
LL | let Some(x) = x else { return };
80+
| ^ help: if this is intentional, prefix it with an underscore: `_x`
81+
82+
error: unused variable: `value`
83+
--> $DIR/unused-variables-const-pattern-typo.rs:96:14
84+
|
85+
LL | let Some(value) = x else { return };
86+
| ^^^^^ help: if this is intentional, prefix it with an underscore: `_value`
87+
88+
error: unused variable: `xyzzy`
89+
--> $DIR/unused-variables-const-pattern-typo.rs:111:14
90+
|
91+
LL | let Some(xyzzy) = x else { return };
92+
| ^^^^^ help: if this is intentional, prefix it with an underscore: `_xyzzy`
93+
94+
error: aborting due to 8 previous errors
95+

0 commit comments

Comments
 (0)