Skip to content

Commit c0a22a0

Browse files
committed
suggest adding/removing ref for binding patterns
1 parent 3ae03e0 commit c0a22a0

8 files changed

+160
-6
lines changed

compiler/rustc_typeck/src/check/pat.rs

+49-4
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
617617
// If there are multiple arms, make sure they all agree on
618618
// what the type of the binding `x` ought to be.
619619
if var_id != pat.hir_id {
620-
self.check_binding_alt_eq_ty(pat.span, var_id, local_ty, ti);
620+
self.check_binding_alt_eq_ty(ba, pat.span, var_id, local_ty, ti);
621621
}
622622

623623
if let Some(p) = sub {
@@ -627,7 +627,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
627627
local_ty
628628
}
629629

630-
fn check_binding_alt_eq_ty(&self, span: Span, var_id: HirId, ty: Ty<'tcx>, ti: TopInfo<'tcx>) {
630+
fn check_binding_alt_eq_ty(
631+
&self,
632+
ba: hir::BindingAnnotation,
633+
span: Span,
634+
var_id: HirId,
635+
ty: Ty<'tcx>,
636+
ti: TopInfo<'tcx>,
637+
) {
631638
let var_ty = self.local_ty(span, var_id).decl_ty;
632639
if let Some(mut err) = self.demand_eqtype_pat_diag(span, var_ty, ty, ti) {
633640
let hir = self.tcx.hir();
@@ -645,12 +652,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
645652
});
646653
let pre = if in_match { "in the same arm, " } else { "" };
647654
err.note(&format!("{}a binding must have the same type in all alternatives", pre));
648-
// FIXME: check if `var_ty` and `ty` can be made the same type by adding or removing
649-
// `ref` or `&` to the pattern.
655+
self.suggest_adding_missing_ref_or_removing_ref(
656+
&mut err,
657+
span,
658+
var_ty,
659+
self.resolve_vars_with_obligations(ty),
660+
ba,
661+
);
650662
err.emit();
651663
}
652664
}
653665

666+
fn suggest_adding_missing_ref_or_removing_ref(
667+
&self,
668+
err: &mut Diagnostic,
669+
span: Span,
670+
expected: Ty<'tcx>,
671+
actual: Ty<'tcx>,
672+
ba: hir::BindingAnnotation,
673+
) {
674+
match (expected.kind(), actual.kind(), ba) {
675+
(ty::Ref(_, inner_ty, _), _, hir::BindingAnnotation::Unannotated)
676+
if self.can_eq(self.param_env, *inner_ty, actual).is_ok() =>
677+
{
678+
err.span_suggestion_verbose(
679+
span.shrink_to_lo(),
680+
"consider adding `ref`",
681+
"ref ",
682+
Applicability::MaybeIncorrect,
683+
);
684+
}
685+
(_, ty::Ref(_, inner_ty, _), hir::BindingAnnotation::Ref)
686+
if self.can_eq(self.param_env, expected, *inner_ty).is_ok() =>
687+
{
688+
err.span_suggestion_verbose(
689+
span.with_hi(span.lo() + BytePos(4)),
690+
"consider removing `ref`",
691+
"",
692+
Applicability::MaybeIncorrect,
693+
);
694+
}
695+
_ => (),
696+
}
697+
}
698+
654699
// Precondition: pat is a Ref(_) pattern
655700
fn borrow_pat_suggestion(&self, err: &mut Diagnostic, pat: &Pat<'_>) {
656701
let tcx = self.tcx;

src/test/ui/mismatched_types/E0409.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ LL | (0, ref y) | (y, 0) => {}
1717
| first introduced with type `&{integer}` here
1818
|
1919
= note: in the same arm, a binding must have the same type in all alternatives
20+
help: consider adding `ref`
21+
|
22+
LL | (0, ref y) | (ref y, 0) => {}
23+
| +++
2024

2125
error: aborting due to 2 previous errors
2226

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// run-rustfix
2+
#![allow(dead_code, unused_variables)]
3+
4+
fn main() {
5+
enum Blah {
6+
A(isize, isize, usize),
7+
B(isize, usize),
8+
}
9+
10+
match Blah::A(1, 1, 2) {
11+
Blah::A(_, x, ref y) | Blah::B(x, ref y) => {}
12+
//~^ ERROR mismatched types
13+
//~| ERROR variable `y` is bound inconsistently across alternatives separated by `|`
14+
}
15+
16+
match Blah::A(1, 1, 2) {
17+
Blah::A(_, x, y) | Blah::B(x, y) => {}
18+
//~^ ERROR mismatched types
19+
//~| variable `y` is bound inconsistently across alternatives separated by `|`
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// run-rustfix
2+
#![allow(dead_code, unused_variables)]
3+
4+
fn main() {
5+
enum Blah {
6+
A(isize, isize, usize),
7+
B(isize, usize),
8+
}
9+
10+
match Blah::A(1, 1, 2) {
11+
Blah::A(_, x, ref y) | Blah::B(x, y) => {}
12+
//~^ ERROR mismatched types
13+
//~| ERROR variable `y` is bound inconsistently across alternatives separated by `|`
14+
}
15+
16+
match Blah::A(1, 1, 2) {
17+
Blah::A(_, x, y) | Blah::B(x, ref y) => {}
18+
//~^ ERROR mismatched types
19+
//~| variable `y` is bound inconsistently across alternatives separated by `|`
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
error[E0409]: variable `y` is bound inconsistently across alternatives separated by `|`
2+
--> $DIR/suggest-adding-or-removing-ref-for-binding-pattern.rs:11:43
3+
|
4+
LL | Blah::A(_, x, ref y) | Blah::B(x, y) => {}
5+
| - first binding ^ bound in different ways
6+
7+
error[E0409]: variable `y` is bound inconsistently across alternatives separated by `|`
8+
--> $DIR/suggest-adding-or-removing-ref-for-binding-pattern.rs:17:43
9+
|
10+
LL | Blah::A(_, x, y) | Blah::B(x, ref y) => {}
11+
| - first binding ^ bound in different ways
12+
13+
error[E0308]: mismatched types
14+
--> $DIR/suggest-adding-or-removing-ref-for-binding-pattern.rs:11:43
15+
|
16+
LL | match Blah::A(1, 1, 2) {
17+
| ---------------- this expression has type `Blah`
18+
LL | Blah::A(_, x, ref y) | Blah::B(x, y) => {}
19+
| ----- ^ expected `&usize`, found `usize`
20+
| |
21+
| first introduced with type `&usize` here
22+
|
23+
= note: in the same arm, a binding must have the same type in all alternatives
24+
help: consider adding `ref`
25+
|
26+
LL | Blah::A(_, x, ref y) | Blah::B(x, ref y) => {}
27+
| +++
28+
29+
error[E0308]: mismatched types
30+
--> $DIR/suggest-adding-or-removing-ref-for-binding-pattern.rs:17:39
31+
|
32+
LL | match Blah::A(1, 1, 2) {
33+
| ---------------- this expression has type `Blah`
34+
LL | Blah::A(_, x, y) | Blah::B(x, ref y) => {}
35+
| - ^^^^^ expected `usize`, found `&usize`
36+
| |
37+
| first introduced with type `usize` here
38+
|
39+
= note: in the same arm, a binding must have the same type in all alternatives
40+
help: consider removing `ref`
41+
|
42+
LL - Blah::A(_, x, y) | Blah::B(x, ref y) => {}
43+
LL + Blah::A(_, x, y) | Blah::B(x, y) => {}
44+
|
45+
46+
error: aborting due to 4 previous errors
47+
48+
Some errors have detailed explanations: E0308, E0409.
49+
For more information about an error, try `rustc --explain E0308`.

src/test/ui/resolve/resolve-inconsistent-binding-mode.stderr

+8
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ LL | Opts::A(ref i) | Opts::B(i) => {}
3131
| first introduced with type `&isize` here
3232
|
3333
= note: in the same arm, a binding must have the same type in all alternatives
34+
help: consider adding `ref`
35+
|
36+
LL | Opts::A(ref i) | Opts::B(ref i) => {}
37+
| +++
3438

3539
error[E0308]: mismatched types
3640
--> $DIR/resolve-inconsistent-binding-mode.rs:18:34
@@ -43,6 +47,10 @@ LL | Opts::A(ref i) | Opts::B(i) => {}
4347
| first introduced with type `&isize` here
4448
|
4549
= note: in the same arm, a binding must have the same type in all alternatives
50+
help: consider adding `ref`
51+
|
52+
LL | Opts::A(ref i) | Opts::B(ref i) => {}
53+
| +++
4654

4755
error[E0308]: mismatched types
4856
--> $DIR/resolve-inconsistent-binding-mode.rs:27:38

src/test/ui/resolve/resolve-inconsistent-names.rs

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ fn main() {
2323
//~| ERROR mismatched types
2424
//~| ERROR variable `c` is not bound in all patterns
2525
//~| HELP if you meant to match on unit variant `E::A`, use the full path in the pattern
26+
//~| HELP consider removing `ref`
2627
}
2728

2829
let z = (10, 20);

src/test/ui/resolve/resolve-inconsistent-names.stderr

+7-2
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ LL | (A, B) | (ref B, c) | (c, A) => ()
5555
| first binding
5656

5757
error[E0408]: variable `CONST1` is not bound in all patterns
58-
--> $DIR/resolve-inconsistent-names.rs:30:23
58+
--> $DIR/resolve-inconsistent-names.rs:31:23
5959
|
6060
LL | (CONST1, _) | (_, Const2) => ()
6161
| ------ ^^^^^^^^^^^ pattern doesn't bind `CONST1`
@@ -69,7 +69,7 @@ LL | const CONST1: usize = 10;
6969
| ^^^^^^^^^^^^^^^^^^^^^^^^^ not accessible
7070

7171
error[E0408]: variable `Const2` is not bound in all patterns
72-
--> $DIR/resolve-inconsistent-names.rs:30:9
72+
--> $DIR/resolve-inconsistent-names.rs:31:9
7373
|
7474
LL | (CONST1, _) | (_, Const2) => ()
7575
| ^^^^^^^^^^^ ------ variable not in all patterns
@@ -92,6 +92,11 @@ LL | (A, B) | (ref B, c) | (c, A) => ()
9292
| first introduced with type `E` here
9393
|
9494
= note: in the same arm, a binding must have the same type in all alternatives
95+
help: consider removing `ref`
96+
|
97+
LL - (A, B) | (ref B, c) | (c, A) => ()
98+
LL + (A, B) | (B, c) | (c, A) => ()
99+
|
95100

96101
error: aborting due to 9 previous errors
97102

0 commit comments

Comments
 (0)