Skip to content

Commit aa03f1f

Browse files
committed
Improve diagnostic for let A = 0;
where `A` is a constant, not a new variable.
1 parent 084beb8 commit aa03f1f

File tree

7 files changed

+96
-26
lines changed

7 files changed

+96
-26
lines changed

src/librustc/hir/map/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1064,6 +1064,14 @@ impl<'hir> Map<'hir> {
10641064
self.as_local_hir_id(id).map(|id| self.span(id))
10651065
}
10661066

1067+
pub fn res_span(&self, res: Res) -> Option<Span> {
1068+
match res {
1069+
Res::Err => None,
1070+
Res::Local(id) => Some(self.span(id)),
1071+
res => self.span_if_local(res.opt_def_id()?),
1072+
}
1073+
}
1074+
10671075
pub fn node_to_string(&self, id: HirId) -> String {
10681076
hir_id_to_string(self, id, true)
10691077
}

src/librustc_mir/hair/pattern/check_match.rs

+39-8
Original file line numberDiff line numberDiff line change
@@ -270,20 +270,51 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
270270
"refutable pattern in {}: {} not covered",
271271
origin, joined_patterns
272272
);
273-
err.span_label(pat.span, match &pat.kind {
273+
match &pat.kind {
274274
hir::PatKind::Path(hir::QPath::Resolved(None, path))
275-
if path.segments.len() == 1 && path.segments[0].args.is_none() => {
276-
format!("interpreted as {} {} pattern, not new variable",
277-
path.res.article(), path.res.descr())
275+
if path.segments.len() == 1 && path.segments[0].args.is_none() =>
276+
{
277+
const_not_var(&mut err, cx.tcx, pat, path);
278278
}
279-
_ => pattern_not_convered_label(&witnesses, &joined_patterns),
280-
});
279+
_ => {
280+
err.span_label(
281+
pat.span,
282+
pattern_not_covered_label(&witnesses, &joined_patterns),
283+
);
284+
}
285+
}
286+
281287
adt_defined_here(cx, &mut err, pattern_ty, &witnesses);
282288
err.emit();
283289
});
284290
}
285291
}
286292

293+
/// A path pattern was interpreted as a constant, not a new variable.
294+
/// This caused an irrefutable match failure in e.g. `let`.
295+
fn const_not_var(err: &mut DiagnosticBuilder<'_>, tcx: TyCtxt<'_>, pat: &Pat, path: &hir::Path) {
296+
let descr = path.res.descr();
297+
err.span_label(pat.span, format!(
298+
"interpreted as {} {} pattern, not a new variable",
299+
path.res.article(),
300+
descr,
301+
));
302+
303+
err.span_suggestion(
304+
pat.span,
305+
"introduce a variable instead",
306+
format!("{}_var", path.segments[0].ident).to_lowercase(),
307+
// Cannot use `MachineApplicable` as it's not really *always* correct
308+
// because there may be such an identifier in scope or the user maybe
309+
// really wanted to match against the constant. This is quite unlikely however.
310+
Applicability::MaybeIncorrect,
311+
);
312+
313+
if let Some(span) = tcx.hir().res_span(path.res) {
314+
err.span_label(span, format!("{} defined here", descr));
315+
}
316+
}
317+
287318
fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pat) {
288319
pat.walk(|p| {
289320
if let hir::PatKind::Binding(_, _, ident, None) = p.kind {
@@ -449,7 +480,7 @@ fn check_exhaustive<'tcx>(
449480
cx.tcx.sess, sp,
450481
format!("non-exhaustive patterns: {} not covered", joined_patterns),
451482
);
452-
err.span_label(sp, pattern_not_convered_label(&witnesses, &joined_patterns));
483+
err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
453484
adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
454485
err.help(
455486
"ensure that all possible cases are being handled, \
@@ -475,7 +506,7 @@ fn joined_uncovered_patterns(witnesses: &[super::Pat<'_>]) -> String {
475506
}
476507
}
477508

478-
fn pattern_not_convered_label(witnesses: &[super::Pat<'_>], joined_patterns: &str) -> String {
509+
fn pattern_not_covered_label(witnesses: &[super::Pat<'_>], joined_patterns: &str) -> String {
479510
format!("pattern{} {} not covered", rustc_errors::pluralise!(witnesses.len()), joined_patterns)
480511
}
481512

src/librustc_typeck/astconv.rs

+2-5
Original file line numberDiff line numberDiff line change
@@ -1368,11 +1368,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
13681368
span,
13691369
format!("associated type `{}` must be specified", assoc_item.ident),
13701370
);
1371-
if item_def_id.is_local() {
1372-
err.span_label(
1373-
tcx.def_span(*item_def_id),
1374-
format!("`{}` defined here", assoc_item.ident),
1375-
);
1371+
if let Some(sp) = tcx.hir().span_if_local(*item_def_id) {
1372+
err.span_label(sp, format!("`{}` defined here", assoc_item.ident));
13761373
}
13771374
if suggest {
13781375
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(

src/librustc_typeck/check/callee.rs

+1-10
Original file line numberDiff line numberDiff line change
@@ -351,16 +351,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
351351

352352
err.span_label(call_expr.span, "call expression requires function");
353353

354-
let def_span = match def {
355-
Res::Err => None,
356-
Res::Local(id) => {
357-
Some(self.tcx.hir().span(id))
358-
},
359-
_ => def
360-
.opt_def_id()
361-
.and_then(|did| self.tcx.hir().span_if_local(did)),
362-
};
363-
if let Some(span) = def_span {
354+
if let Some(span) = self.tcx.hir().res_span(def) {
364355
let label = match (unit_variant, inner_callee_path) {
365356
(Some(path), _) => format!("`{}` defined here", path),
366357
(_, Some(hir::QPath::Resolved(_, path))) => format!(

src/test/ui/consts/const-pattern-irrefutable.stderr

+21-3
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,38 @@
11
error[E0005]: refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX` not covered
22
--> $DIR/const-pattern-irrefutable.rs:12:9
33
|
4+
LL | const a: u8 = 2;
5+
| ---------------- constant defined here
6+
...
47
LL | let a = 4;
5-
| ^ interpreted as a constant pattern, not new variable
8+
| ^
9+
| |
10+
| interpreted as a constant pattern, not a new variable
11+
| help: introduce a variable instead: `a_var`
612

713
error[E0005]: refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX` not covered
814
--> $DIR/const-pattern-irrefutable.rs:13:9
915
|
16+
LL | pub const b: u8 = 2;
17+
| -------------------- constant defined here
18+
...
1019
LL | let c = 4;
11-
| ^ interpreted as a constant pattern, not new variable
20+
| ^
21+
| |
22+
| interpreted as a constant pattern, not a new variable
23+
| help: introduce a variable instead: `c_var`
1224

1325
error[E0005]: refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX` not covered
1426
--> $DIR/const-pattern-irrefutable.rs:14:9
1527
|
28+
LL | pub const d: u8 = 2;
29+
| -------------------- constant defined here
30+
...
1631
LL | let d = 4;
17-
| ^ interpreted as a constant pattern, not new variable
32+
| ^
33+
| |
34+
| interpreted as a constant pattern, not a new variable
35+
| help: introduce a variable instead: `d_var`
1836

1937
error: aborting due to 3 previous errors
2038

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
fn main() {
2+
let A = 3;
3+
//~^ ERROR refutable pattern in local binding: `std::i32::MIN..=1i32` and
4+
//~| interpreted as a constant pattern, not a new variable
5+
//~| HELP introduce a variable instead
6+
//~| SUGGESTION a_var
7+
8+
const A: i32 = 2;
9+
//~^ constant defined here
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0005]: refutable pattern in local binding: `std::i32::MIN..=1i32` and `3i32..=std::i32::MAX` not covered
2+
--> $DIR/const-pat-non-exaustive-let-new-var.rs:2:9
3+
|
4+
LL | let A = 3;
5+
| ^
6+
| |
7+
| interpreted as a constant pattern, not a new variable
8+
| help: introduce a variable instead: `a_var`
9+
...
10+
LL | const A: i32 = 2;
11+
| ----------------- constant defined here
12+
13+
error: aborting due to previous error
14+
15+
For more information about this error, try `rustc --explain E0005`.

0 commit comments

Comments
 (0)